GitHub で見る

自分自身を鍛える戦略

この戦略は、MetaTrader 4 エキスパート アドバイザー TrainYourself-V1_1-1 の StockSharp 移植です。 MT4 スクリプトのグラフィカル ボタンを明示的なメソッド呼び出しに置き換えながら、チャネル構築とブレイクアウト ロジックを再作成します。このアルゴリズムは継続的に Donchian スタイルの価格チャネルを再構築し、価格が最初にチャネル内で統合された後にチャネルから抜け出すと取引を開始します。

取引ロジック

  1. チャネル構造

    • ChannelLength 期間の DonchianChannels インジケーターは、選択した CandleType の終了したローソク足ごとに評価されます。
    • 生の上部バンドと下部バンドは、追加の MetaTrader のようなバッファー、つまり BufferPoints にインストゥルメント PriceStep を掛け合わせたもの)で拡張されます。これは、最近の高値と安値の上にトレンドラインをスライドさせる前に、最初に現在の買値/売値から 50 ポイント離れた位置にトレンドラインを配置した元のスクリプトを再現します。
    • 結果の UpperBand/LowerBand 値は読み取り専用プロパティとして公開されるため、カスタム ダッシュボードに表示できます。
  2. アーミング状態

    • ポジションがオープンしている間、または EnableTrendTrade が false の場合、ブレイクアウト エンジンは解除されたままになります。
    • ポジションがない場合、価格は両方の境界から ActivationPoints * PriceStep の追加マージンを持ってチャネル内で閉じる必要があります。その場合のみ、_isArmedtrue になり、価格がチャネルに戻ったときに設定された MetaTrader フラグ q=1 を模倣します。
  3. ブレイクアウト実行

    • 準備が完了すると、UpperBand 以上の終値で成行買い注文が発行されます (AllowBuyOpen が有効な場合)。 LowerBand 以下で終了すると、成行売り注文が出されます (AllowSellOpen に関して)。
    • 注文が出されると、価格がオープンポジションなしでチャネルに再び入るまで、戦略は自動的に解除されます。
  4. リスク管理

    • StartProtection は自動保護命令を設定します。距離は、TakeProfitPointsStopLossPoints に現在の PriceStep を乗算して計算されます。ブローカーがステップを報告しない場合は、MetaTrader Point の動作と一致する、0.0001 のフォールバックが使用されます。
  5. 手動制御

    • MT4 ラベル (BUY_TRIANGLESELL_TRIANGLECLOSE_ORDER) は、3 つのパブリック メソッド TriggerManualBuy()TriggerManualSell()、および ClosePositionManually() に置き換えられます。これらは、AllowBuyOpen/AllowSellOpen を尊重し、IsFormedAndOnlineAndAllowTrading() 経由で接続ステータスをチェックします。また、手動取引がすぐに自動エントリーをトリガーしないようにブレイクアウト ロジックを解除します。

パラメーター

パラメータ デフォルト 説明
CandleType 30m 時間枠 すべての計算に使用されるプライマリ キャンドル サブスクリプション。
ChannelLength 20 Donchian チャネルによって分析されたローソク足の数。
BufferPoints 50 チャンネルを終了する前の最後の終了付近で、追加の MetaTrader ポイントが追加されました。
ActivationPoints 2 ブレイクアウトが準備される前に、価格がチャネルエッジから遠ざけなければならないマージン(ポイント単位)。
StopLossPoints 100 ポイント単位のストップロス距離。 PriceStepを乗算して絶対価格に変換します。
TakeProfitPoints 100 テイクプロフィット距離(ポイント単位)。 PriceStep を使用して絶対価格に変換されます。
EnableTrendTrade true 自動ブレイクアウト取引を可能にします。 false の場合、手動ヘルパー メソッドのみがポジションをオープン/クローズできます。
Volume 1 自動取引と手動取引の両方の注文サイズ。

使用上の注意

  • 元のエキスパートアドバイザーでは、トレンドラインを(再)構築するためにチャート上のアイコンをドラッグする必要がありました。 StockSharp では、チャネルはローソク足ごとに自動的に再構築されるため、手動で更新する必要はありません。
  • この戦略は UpperBandLowerBand、および IsArmed を公開するため、ダッシュボードまたは UI ウィジェットは、チャート オブジェクトに依存せずに元の視覚的なフィードバックを複製できます。
  • ストップロスとテイクプロフィットのレベルはオプションです。対応するパラメータを 0 に設定すると、保護命令が無効になり、extern 値が 0 のときに変更ルーチンがスキップされる MetaTrader の動作が反映されます。
  • 手動入力では同じ Volume パラメータが尊重され、設定された保護距離の恩恵が自動的に受けられます。
  • ブレイクアウト状態を手動でリセットするには、ClosePositionManually() を呼び出すか (これにより、IsArmed もクリアされます)、価格がチャネルに再入してアーミング条件が再び満たされるのを待ちます。
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// TrainYourself: Donchian channel breakout strategy.
/// Uses Highest/Lowest as channel, arms after price is inside channel,
/// then trades breakouts.
/// </summary>
public class TrainYourselfStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _channelLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevHighest;
	private decimal _prevLowest;
	private bool _isArmed;
	private decimal _entryPrice;

	public TrainYourselfStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_channelLength = Param(nameof(ChannelLength), 20)
			.SetDisplay("Channel Length", "Highest/Lowest period.", "Channel");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR for stop distance.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int ChannelLength
	{
		get => _channelLength.Value;
		set => _channelLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevHighest = 0;
		_prevLowest = 0;
		_isArmed = false;
		_entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevHighest = 0;
		_prevLowest = 0;
		_isArmed = false;
		_entryPrice = 0;

		var highest = new Highest { Length = ChannelLength };
		var lowest = new Lowest { Length = ChannelLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(highest, lowest, atr, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal highVal, decimal lowVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevHighest == 0 || _prevLowest == 0 || atrVal <= 0)
		{
			_prevHighest = highVal;
			_prevLowest = lowVal;
			return;
		}

		var close = candle.ClosePrice;
		var upper = _prevHighest;
		var lower = _prevLowest;

		// Manage position: exit on channel re-entry
		if (Position > 0)
		{
			if (close < (upper + lower) / 2m)
			{
				SellMarket();
				_entryPrice = 0;
				_isArmed = false;
			}
		}
		else if (Position < 0)
		{
			if (close > (upper + lower) / 2m)
			{
				BuyMarket();
				_entryPrice = 0;
				_isArmed = false;
			}
		}

		// Arm when price is inside channel
		if (Position == 0)
		{
			if (!_isArmed)
			{
				var margin = atrVal * 0.2m;
				if (close > lower + margin && close < upper - margin)
					_isArmed = true;
			}
			else
			{
				// Breakout entry
				if (close > upper)
				{
					_entryPrice = close;
					BuyMarket();
					_isArmed = false;
				}
				else if (close < lower)
				{
					_entryPrice = close;
					SellMarket();
					_isArmed = false;
				}
			}
		}

		_prevHighest = highVal;
		_prevLowest = lowVal;
	}
}