GitHub で見る

ロッカーヘッジグリッド戦略

この戦略は、MetaTrader 4 エキスパート アドバイザー Locker.mq4 を複製します。すべてのサイクルは市場の買いから始まり、ヘッジされた買い注文と売り注文のグリッドを管理します。すべてのオープン取引の未実現利益の合計が口座資本の一定割合に達すると、すべてのポジションがクローズされ、新しいサイクルが始まります。変動損失がマイナス方向に同じ割合を超えた場合、この戦略は固定点間隔で徐々にレスキュー注文を追加し、ロングエントリーとショートエントリーを交互に行うことで価格変動を固定します。

パラメーター

パラメータ 説明 デフォルト
NeedProfitRatio 注文をクローズまたは追加する前に獲得(または損失)する必要があるポートフォリオの資本の割合。 0.001 はアカウントの 0.1% に相当します。 0.001
InitialVolume 各サイクルの開始時の最初の成行買い注文のボリューム。 0.5
StepVolume 戦略がドローダウンフェーズにある間に追加される各救助命令のボリューム。 0.2
StepPoints 救助命令間の距離 (MetaTrader ポイント)。 Security.PriceStep (pip) 情報を使用して内部で価格に変換されます。 50
EnableRescue 浮動損失が負のしきい値を超えたときに平均グリッドを有効にします。無効にすると、戦略は最初の取引のみを実行し、利益を待ちます。 true

取引ロジック

  1. サイクルスタート

    • 最初に受信した取引見積で、成行買いが InitialVolume で送信されます。
    • エントリー価格が参照チェックポイントとなり、最高値買いトラッカーと最低売りトラッカーの両方がその価格にリセットされます。
  2. 利益のロック

    • この戦略はティックごとに、すべてのロングレッグとショートレッグからの未実現損益を合計します。長い脚は (price - averageBuyPrice) * longVolume を追加し、短い脚は (averageSellPrice - price) * shortVolume を追加します。
    • 変動利益が NeedProfitRatio * equity に達すると、すべてのポジションは反対成行注文によってフラット化されます。充填が確認された後、新しいサイクルが開始されます。
  3. レスキューグリッド

    • 未実現利益が -NeedProfitRatio * equity を下回り、EnableRescue が true の場合、システムは価格が StepPoints (価格距離に変換) するまで待機します。最後のチェックポイントを超えるそれぞれの新たな高値は市場での買いを発行し、新たな安値はそれぞれ市場での売りをスケジュールします。ボリュームは常に StepVolume と等しくなります。
    • チェックポイントと方向の極値は救助命令が出るたびに更新されるため、次の追加にはさらに完全な価格が必要になります。
  4. サイクルリセット

    • ロングとショートの両方の在庫がゼロに下がった後(独自の取引通知で確認)、チェックポイントとエクストリームは最新の取引価格にリセットされ、戦略は最初の購入で新しいサイクルをシードする準備が整います。

実装メモ

  • SubscribeTrades().Bind(ProcessTrade) を使用してティックごとの価格を操作し、現在の入札/売値に反応した元の MQL EA を反映します。
  • Security.PriceStep から導出されたピップ サイズを介して、MetaTrader の「ポイント」を StockSharp の価格に変換します。小数点以下 3 桁または 5 桁で引用されたシンボルは、標準の x10 調整を受けます。
  • OnOwnTradeReceived 内で長期在庫と短期在庫を個別に追跡し、MT4 バージョンとまったく同じようにエクスポージャをヘッジできます (買いポジションと売りポジションが共存可能)。
  • ポートフォリオの資本は、Portfolio.CurrentValue から推定され、フォールバックは CurrentBalance または BeginValue になります。最初の正の読み取り値はキャッシュされるため、プロバイダーが値の報告を停止しても利益しきい値は安定したままになります。
  • すべての成行注文量は、Security.VolumeStepVolumeMin、および VolumeMax の制限を遵守する AlignVolume ヘルパーを通過します。

使用のヒント

  • 楽器のメタデータが正しい PriceStep を提供していることを確認してください。そうしないと、ポイントから価格への変換が不正確になり、グリッド距離が MetaTrader の動作と一致しなくなります。
  • レスキュー ロジックはマーチンゲール スタイルの平均を反映しているため、StepVolume を慎重に選択し、リスクを監視してください。 StepPointsStepVolume の両方を増やすと、オープン取引の数は減りますが、エクスポージャーは増大します。
  • EnableRescuefalse に設定すると、平均を下がらずに最初のポジションが利益目標に達するまで待機する保守的なバリアントが複製されます。
  • 外国為替シンボルのバックテストは、EA の元の粒度に一致するティック データを使用して実行する必要があります。

MQL エキスパートとの違い

  • 元のスクリプトは、8 つを超える取引がアクティブな場合に、完全にオフセットする注文ペアをクローズしようとしました。このブロックはチケット フィルターのバグのため実行されず、省略されました。
  • 初期化時の既存の注文に基づく StepLot の再計算は複製されません。ボリュームは、StockSharp で公開されるパラメータを通じて完全に制御されます。
  • EA の注文コメント、アラート ポップアップ、手動停止フラグは存在しません。StockSharp バージョンは純粋に自律取引ロジックに焦点を当てています。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Grid strategy that opens positions at regular price intervals.
/// Uses ATR to determine grid spacing and reverses direction on profit targets.
/// </summary>
public class LockerHedgingGridStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _gridMultiplier;

	private decimal _gridLevel;
	private decimal _entryPrice;
	private bool _initialized;

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

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "Period for ATR calculation.", "Indicators");

		_gridMultiplier = Param(nameof(GridMultiplier), 1.5m)
			.SetDisplay("Grid Multiplier", "ATR multiplier for grid spacing.", "Grid");
	}

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

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

	public decimal GridMultiplier
	{
		get => _gridMultiplier.Value;
		set => _gridMultiplier.Value = value;
	}

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

		_gridLevel = 0;
		_entryPrice = 0;
		_initialized = false;
	}

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

		_gridLevel = 0;
		_entryPrice = 0;
		_initialized = false;

		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		if (atrValue <= 0)
			return;

		var close = candle.ClosePrice;
		var gridStep = atrValue * GridMultiplier;

		if (!_initialized)
		{
			_gridLevel = close;
			_initialized = true;
			return;
		}

		// Grid logic: trade when price moves a full grid step
		if (Position == 0)
		{
			if (close >= _gridLevel + gridStep)
			{
				// Price moved up a grid step - buy
				_entryPrice = close;
				_gridLevel = close;
				BuyMarket();
			}
			else if (close <= _gridLevel - gridStep)
			{
				// Price moved down a grid step - sell
				_entryPrice = close;
				_gridLevel = close;
				SellMarket();
			}
		}
		else if (Position > 0)
		{
			if (close >= _entryPrice + gridStep)
			{
				// Take profit
				SellMarket();
				_gridLevel = close;
			}
			else if (close <= _entryPrice - gridStep * 2)
			{
				// Stop-loss at 2x grid step
				SellMarket();
				_gridLevel = close;
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - gridStep)
			{
				// Take profit
				BuyMarket();
				_gridLevel = close;
			}
			else if (close >= _entryPrice + gridStep * 2)
			{
				// Stop-loss at 2x grid step
				BuyMarket();
				_gridLevel = close;
			}
		}
	}
}