GitHub で見る

ストラドル トレイル v2.40 戦略

Straddle Trail v2.40 戦略 は、MetaTrader 4 エキスパート アドバイザー「Straddle&Trail」(バージョン 2.40)の StockSharp 移植です。このアルゴリズムは、影響の大きいイベントに先立って対称的なストップ注文のペアを準備し、損益分岐点ロジックとトレーリングストップロジックでトリガーされたポジションを自動的に管理し、アカウントにすでに存在する手動取引に反応できます。

コアワークフロー

  1. 準備
    • この戦略は、オーダーブックの更新をサブスクライブして、最良の買値/売値を追跡し、スケジュール決定のために分足のローソク足 (構成可能) を追跡します。
    • ピップは、ピップで定義されたすべての距離が価格に適切に変換されるように、機器の設定から計算されます。
  2. ストラドルの配置
    • イベント (PreEventEntryMinutes) の前に設定されたリードタイムで、または PlaceStraddleImmediately が有効な場合は直ちに、買いストップと売りストップ注文が市場の上下 DistanceFromPrice ピップで発注されます。
    • AdjustPendingOrders が有効な場合、イベントの前に未決注文を 1 分ごとに再中心化できます。調整はイベント前に StopAdjustMinutes で停止します。
  3. 注文管理
    • 一方がトリガーされると、オプションで反対側の未決注文 (RemoveOppositeOrder) が削除され、二重露光が防止されます。
    • ShutdownNowShutdownOption を併用すると、オープンポジションを平準化したり、オンデマンドで未決注文をキャンセルしたりすることができます。
  4. ポジション保護
    • 初期のストップロスとテイクプロフィットのレベルは、pip ベースのパラメーターから導出されます。
    • 価格が損益分岐点トリガーに達すると、ストップが移動して BreakevenLockPips の利益を確定します。
    • トレーリングはすぐに開始されるか、損益分岐点の後に開始されます (TrailAfterBreakeven に応じて)。
    • ManageManualTrades が true の場合、戦略によって検出された手動ポジションは同じルールを使用して保護されます。

パラメーター

パラメータ 説明
ShutdownNow 次のローソク足の終値でシャットダウン ロジックを強制的に実行します。
ShutdownOption クローズする対象を選択します: すべて、トリガーされたポジションのみ、ロングのみ、ショートのみ、すべての未決注文、買いストップのみ、または売りストップのみ。
DistanceFromPrice 現在の価格と保留中のストップ注文の間の距離 (ピップ)。
StopLossPips 初期のストップロス距離 (pips)。
TakeProfitPips 初期のテイクプロフィット距離 (pips)。テイクプロフィットレベルを無効にするには、0 に設定します。
TrailPips トレーリングストップの距離 (pips)。末尾を無効にするには 0 に設定します。
TrailAfterBreakeven true の場合、トレーリングは損益分岐点トリガーに達した後にのみ開始されます。
BreakevenLockPips 損益分岐点トリガーが発動すると、利益 (ピップ単位) が確定します。
BreakevenTriggerPips 損益分岐点の動きを有効にする利益のしきい値 (ピップ単位)。
EventHour / EventMinute 予定されているニュース イベント時間 (ブローカー時間)。スケジュールを無効にし、手動/即時モードを使用するには、両方を 0 に設定します。
PreEventEntryMinutes 跨ぎが置かれたイベントの数分前。
StopAdjustMinutes イベントの数分前に注文の調整が停止します。最小値は 1 分です。
RemoveOppositeOrder ストラドルの片側が約定された後、反対側の未決注文を削除します。
AdjustPendingOrders ストップ調整ウィンドウに達するまで、未決注文を毎分中心に戻します。
PlaceStraddleImmediately イベントスケジュールを無視して、戦略が開始されるとすぐに跨ぎを配置します。
ManageManualTrades 損益分岐点ロジックとトレーリングロジックを手動ポジションに拡張します。
CandleType タイミングおよびスケジューリング ロジックに使用されるキャンドル シリーズ (デフォルトは 1 分の時間枠)。

使用上の注意

  • ピップベースの距離が価格に正確に変換されるように、セキュリティ設定を通じて常に機器の正しいピップサイズを設定してください。
  • この戦略は、ストップロスまたはテイクプロフィット条件が満たされたときに成行注文を使用してポジションをクローズします。これは、元の EA が手動でストップ調整を実行した方法を反映しています。
  • PlaceStraddleImmediately が無効でスケジュールがアクティブな場合、ストラドルは取引日ごとに 1 回だけ配置されます。同じ日に別のイベントに備えるために戦略をリセットします。
  • シャットダウン制御は、エクスポージャーを迅速に平坦化し、シナリオ全体で未決注文を削除するための緊急ブレーキとして使用できます。

変換の詳細

  • コード内のすべてのコメントは英語に翻訳され、わかりやすくするために追加の説明が追加されています。
  • 高レベルの StockSharp API メソッド (BuyStopSellStopClosePosition) は、実装をフレームワークのベスト プラクティスに近づけるために使用されます。
  • このアルゴリズムは、インジケーターの直接検索を回避し、代わりに、プロジェクト ガイドラインで要求されているように、バインドされたローソク足とオーダーブックのサブスクリプションに依存します。
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Straddle breakout strategy converted from the MetaTrader 4 Straddle and Trail EA (v2.40).
/// Uses price channel breakouts with trailing stop management.
/// </summary>
public class StraddleTrailV240Strategy : Strategy
{
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<DataType> _candleType;

	private readonly List<decimal> _highs = new();
	private readonly List<decimal> _lows = new();

	/// <summary>
	/// Channel lookback period for breakout detection.
	/// </summary>
	public int ChannelPeriod
	{
		get => _channelPeriod.Value;
		set => _channelPeriod.Value = value;
	}

	/// <summary>
	/// Stop loss distance in absolute price units.
	/// </summary>
	public decimal StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

	/// <summary>
	/// Take profit distance in absolute price units.
	/// </summary>
	public decimal TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

	/// <summary>
	/// Candle type used by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance.
	/// </summary>
	public StraddleTrailV240Strategy()
	{
		_channelPeriod = Param(nameof(ChannelPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Channel Period", "Lookback period for breakout levels", "Parameters");

		_stopLoss = Param(nameof(StopLoss), 500m)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop loss distance", "Risk");

		_takeProfit = Param(nameof(TakeProfit), 500m)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take profit distance", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
			.SetDisplay("Candle Type", "Candle subscription used", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_highs.Clear();
		_lows.Clear();
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		var ema = new EMA { Length = 10 };

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

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

		var tp = TakeProfit > 0 ? new Unit(TakeProfit, UnitTypes.Absolute) : null;
		var sl = StopLoss > 0 ? new Unit(StopLoss, UnitTypes.Absolute) : null;
		StartProtection(tp, sl);

		base.OnStarted2(time);
	}

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

		_highs.Add(candle.HighPrice);
		_lows.Add(candle.LowPrice);

		while (_highs.Count > ChannelPeriod)
			_highs.RemoveAt(0);
		while (_lows.Count > ChannelPeriod)
			_lows.RemoveAt(0);

		if (_highs.Count < ChannelPeriod)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var upper = _highs.Take(_highs.Count - 1).Max();
		var lower = _lows.Take(_lows.Count - 1).Min();

		// Breakout above channel -> buy
		if (candle.ClosePrice > upper && Position <= 0)
		{
			if (Position < 0)
				BuyMarket(Math.Abs(Position));
			BuyMarket(Volume);
		}
		// Breakout below channel -> sell
		else if (candle.ClosePrice < lower && Position >= 0)
		{
			if (Position > 0)
				SellMarket(Position);
			SellMarket(Volume);
		}
	}

	/// <summary>
	/// Shutdown modes enum (preserved from original).
	/// </summary>
	public enum ShutdownModes
	{
		All = 0,
		TriggeredPositions = 1,
		TriggeredLong = 2,
		TriggeredShort = 3,
		PendingOrders = 4,
		PendingLong = 5,
		PendingShort = 6
	}
}