GitHub で見る

OverHedge V2 グリッド戦略

OverHedge V2 は、ロング ポジションとショート ポジションを交互に実行しながら、約定するたびに取引サイズを増加させるヘッジ グリッド システムです。この戦略は、高速指数移動平均と低速指数移動平均 (EMA) の関係を分析して、次のサイクルの支配的な方向を決定します。サイクルが開始されると、価格が開始気配付近の事前定義されたトンネル レベルに達するたびに、アルゴリズムによって成行注文が発行されます。グリッドは対称的に拡張するため、すべての新しいレッグが前のレッグのフローティング損失を相殺します。このサイクルは、オープン利益の合計が設定可能な目標を超えたとき、またはトレーダーが手動でシャットダウンを要求したときに終了します。

この実装では、長期エクスポージャーと短期エクスポージャーに対して別々の集計が保持され、ライブレベル 1 価格を使用して新しいヘッジがトリガーされます。取引量は選択した乗数に応じて幾何学的に増加し、元の MetaTrader エキスパート アドバイザーからのマーチンゲール スタイルのリスクが再現されます。注文は市場で実行されるため、システムはポイントで表現されるグリッド間隔を維持しながら、流動性条件に自動的に適応します。

仕組み

  1. 方向フィルター – この戦略は、完了したローソク足で 2 つの EMA を計算します。高速の EMA が低速の EMA を上回ると、次のサイクルはロング バイアスから始まります。それ以外の場合は、ショートバイアスから始まります。
  2. サイクルの初期化 – サイクルの開始時に、アルゴリズムは現在の入札価格を記録し、設定された幅とライブ スプレッドによって分離された 2 つのトンネル境界を導出します。最初のオーダーは EMA バイアスに従い、反対側のレッグはトンネル距離でステージングされます。
  3. グリッド拡張 – 価格が最新のエントリーに対して継続する場合、追加の成行注文が交互にトリガーされます (買い、売り、買い…)。新しいレッグごとに前のボリュームにヘッジ乗数が乗算され、反転時に全体のポジションをより早く回復できるようになります。
  4. 利益の収穫 – このサイクルは、最良の買値/売値を使用して未実現利益を常に監視します。目標値に到達するか、オペレーターがシャットダウン フラグを切り替えると、開いているすべてのレッグが清算され、サイクルがリセットされます。
  5. エクスポージャトラッキング – この戦略は、オープン利益を正確に計算し、既存の注文が保留中の間に重複注文が送信されることを回避するために、ロングヘッジとショートヘッジの両方の平均価格と出来高を維持します。

デフォルトパラメータ

  • Base Volume = 0.1 ロット – 最初のグリッド レッグの初期取引サイズ。
  • Hedge Multiplier = 2.0 – 後続の各レッグに適用されるボリューム乗数。
  • Tunnel Width (points) = 20 – 現在のスプレッドを超える交互注文間の追加距離。
  • Profit Target = 100 – グリッド全体を閉じるアカウント通貨での未実現利益。
  • Short EMA = 8 – 方向検出に使用される高速 EMA の周期。
  • Long EMA = 21 – 方向検出に使用される低速 EMA の期間。
  • Candle Type = 1 分 – EMA フィルターにフィードする時間枠。
  • Shutdown Grid = false – true の場合、ストラテジーはすべてのレッグを直ちに終了し、取引を停止します。

注意事項

  • グリッドは、レベル 1 相場 (最良の買い値/売り値) を提供するあらゆる金融商品で機能します。スプレッドが広いと、トンネルのサイズが自動的に大きくなります。
  • 取引量は、注文の拒否を避けるために、セキュリティ量ステップを使用して正規化されます。
  • このシステムではマーチンゲール サイジング スキームが使用されているため、利益目標に達しないまま価格傾向が続く場合には、大幅なドローダウンが発生する可能性があります。
  • シャットダウン後に取引を再開するには、パラメータを false に戻すか、戦略を再開します。
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// OverHedge V2 Grid strategy using RSI mean reversion with grid averaging.
/// Buy when RSI is oversold, sell when RSI is overbought.
/// </summary>
public class OverHedgeV2GridStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<DataType> _candleType;

	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public OverHedgeV2GridStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI period", "Indicators");

		_oversold = Param(nameof(Oversold), 30m)
			.SetDisplay("Oversold", "RSI oversold level", "Indicators");

		_overbought = Param(nameof(Overbought), 70m)
			.SetDisplay("Overbought", "RSI overbought level", "Indicators");

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

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

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

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

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

		if (rsiValue <= Oversold && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (rsiValue >= Overbought && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
	}
}