GitHub で見る

Twenty 200 Pips 戦略

概要

この戦略は元のMQL5エキスパート20/200 pipsを複製します。1時間足のローソク足を調べ、2つの歴史的な始値(Open[t1]Open[t2])を比較します。特定の時間帯にこれらの始値の差が設定可能なデルタを超えると、戦略はセッションで1回の取引に入り、固定のテイクプロフィットとストップロスレベルに依存します。

取引ロジック

  1. 時間足のローソク足(設定可能)を購読し、必要なインデックスの始値を取得するために2つのShiftインジケーターに始値を供給します。
  2. 完成した各ローソク足の間、現在の時間が設定された取引時間より大きくなったら「取引可能」フラグをリセットします。これは元のエキスパートアドバイザーの毎日のリセットを反映しています。
  3. 時間が設定された取引時間と一致し、ポジションがオープンでない場合、保存された始値を比較します:
    • Open[t1] > Open[t2] + deltaの場合、市場売り注文を送信します。
    • Open[t1] + delta < Open[t2]の場合、市場買い注文を送信します。
  4. 注文を送信した後、戦略は次の毎日のリセットまで新しいエントリーを禁止します。保護テイクプロフィットとストップロス注文はStartProtectionを通じて管理されます。

パラメーター

  • TakeProfit – テイクプロフィット注文の価格ポイント単位の距離(デフォルト200ポイント)。
  • StopLoss – ストップロス注文の価格ポイント単位の距離(デフォルト2000ポイント)。
  • TradeHour – エントリーチェックが行われる時刻(デフォルト18)。
  • FirstOffset – より古い始値のインデックス(MQLスクリプトのOpen[t1]にマップ、デフォルト7)。
  • SecondOffset – より最近の始値のインデックス(Open[t2]、デフォルト2)。
  • DeltaPoints – 取引をトリガーするための2つの始値の間の最小ポイント差(デフォルト70)。
  • Volume – 成行エントリーに使用する注文サイズ(デフォルト0.1)。
  • CandleType – 計算に使用する時間軸(デフォルト1時間足)。

実装上の注意

  • Shiftインジケーターはカスタムコレクションを維持せずに歴史的な始値にアクセスするために手動で処理されます。
  • 戦略はMQLエキスパートで定義されたストップロス/テイクプロフィットレベルを模倣するためにOnStarted中にStartProtectionを1回呼び出します。
  • 英語のコメントはメンテナンスとレビューを容易にするためにコード内に直接含まれています。
  • _canTradeは注文が配置された直後にクリアされ、設定された取引時間が経過した後にのみ復元されるため、1日1回の取引のみが許可されます。

使用方法

  1. 戦略を銘柄に割り当て、対象銘柄に応じてパラメーターを設定します。
  2. 銘柄に有効なPriceStepがあることを確認してください;ポイントベースのパラメーターを絶対的な価格距離に変換するために使用されます。
  3. 戦略を起動します。設定された時間まで待機し、始値の条件が満たされれば次の完成したローソク足で動作します。
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Daily breakout strategy derived from the "20/200 pips" MQL5 expert.
/// Compares open prices from different bar offsets and trades the breakout.
/// </summary>
public class Twenty200PipsStrategy : Strategy
{
	private readonly StrategyParam<int> _takeProfit;
	private readonly StrategyParam<int> _stopLoss;
	private readonly StrategyParam<int> _firstOffset;
	private readonly StrategyParam<int> _secondOffset;
	private readonly StrategyParam<int> _deltaPoints;
	private readonly StrategyParam<DataType> _candleType;

	private readonly List<decimal> _opens = new();
	private decimal _pointValue;

	public int TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

	public int StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

	public int FirstOffset
	{
		get => _firstOffset.Value;
		set => _firstOffset.Value = value;
	}

	public int SecondOffset
	{
		get => _secondOffset.Value;
		set => _secondOffset.Value = value;
	}

	public int DeltaPoints
	{
		get => _deltaPoints.Value;
		set => _deltaPoints.Value = value;
	}

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

	public Twenty200PipsStrategy()
	{
		_takeProfit = Param(nameof(TakeProfit), 200)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit (points)", "Take profit distance in points", "Risk")
			.SetOptimize(50, 500, 50);

		_stopLoss = Param(nameof(StopLoss), 2000)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss (points)", "Stop loss distance in points", "Risk")
			.SetOptimize(200, 4000, 100);

		_firstOffset = Param(nameof(FirstOffset), 7)
			.SetGreaterThanZero()
			.SetDisplay("First Offset", "Older bar index", "Signal")
			.SetOptimize(1, 12, 1);

		_secondOffset = Param(nameof(SecondOffset), 2)
			.SetGreaterThanZero()
			.SetDisplay("Second Offset", "Newer bar index", "Signal")
			.SetOptimize(1, 6, 1);

		_deltaPoints = Param(nameof(DeltaPoints), 1)
			.SetGreaterThanZero()
			.SetDisplay("Delta (points)", "Minimum difference between opens", "Signal")
			.SetOptimize(10, 200, 10);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_opens.Clear();
		_pointValue = 0m;
	}

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

		_pointValue = Security?.PriceStep ?? 1m;
		if (_pointValue <= 0m)
			_pointValue = 1m;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ProcessCandle)
			.Start();

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

		StartProtection(
			takeProfit: new Unit(TakeProfit * _pointValue, UnitTypes.Absolute),
			stopLoss: new Unit(StopLoss * _pointValue, UnitTypes.Absolute),
			useMarketOrders: true);
	}

	private void ProcessCandle(ICandleMessage candle)
	{
		if (candle.State != CandleStates.Finished)
			return;

		_opens.Add(candle.OpenPrice);

		var maxOffset = Math.Max(FirstOffset, SecondOffset);
		if (_opens.Count <= maxOffset)
			return;

		// Keep buffer limited
		if (_opens.Count > maxOffset + 100)
			_opens.RemoveRange(0, _opens.Count - maxOffset - 50);

		if (Position != 0)
			return;

		var openFirst = _opens[_opens.Count - 1 - FirstOffset];
		var openSecond = _opens[_opens.Count - 1 - SecondOffset];
		var threshold = DeltaPoints * _pointValue;

		if (openFirst > openSecond + threshold)
		{
			SellMarket();
		}
		else if (openFirst + threshold < openSecond)
		{
			BuyMarket();
		}
	}
}