GitHub で見る

EES ヘッジャー (上級)

概要

この戦略は、古典的な MetaTrader「EES ヘッジャー」エキスパート アドバイザーの動作を反映しています。外部トレーダー、裁量オペレーター、または別の自動化システムが同じ口座でポジションをオープンするたびに、この戦略は設定可能なボリュームを使用して反対側のヘッジを即座に作成します。次に、ストップロス、テイクプロフィット、ブレークイーブン、トレーリングストップのルールでヘッジを管理し、ヘッジの利益を保護しながらエクスポージャーを中和します。

従来のシグナル駆動戦略とは異なり、このモジュールはエントリーが他の場所で生成されることを前提としています。その唯一の責任は、口座取引を観察し、一致するチケットに対応し、保護命令または手動でクローズされるまでヘッジポジションを保護することです。

取引ロジック

  1. 外部取引の検出 – アカウント取引のコネクタ ストリームが監視されます。コメントが OriginalOrderComment と一致する取引 (フィールドが空の場合はすべての取引) は、ヘッジする必要があるソースとして扱われます。戦略自体によって生成された取引は、トランザクション識別子を保存することによってフィルターされます。
  2. 注文のミラーリング – 適格な取引が受信されると、ストラテジーは反対方向にボリューム HedgeVolume で即時成行注文を送信します。オプションの HedgerOrderComment は、バックオフィス ツールがヘッジ注文を他のアクティビティから分離するのに役立ちます。
  3. リスク管理 – ヘッジが約定された後、戦略はピップパラメーターで定義された距離にストップロス注文とテイクプロフィット注文を配置します。損益分岐点条件が満たされると、ストップはエントリー価格に 1 ピップを加えた値に移動します。トレーリングを有効にすると、市場がヘッジに有利に動き続けるため、ストップはさらに進みます。
  4. 状態のクリーンアップ – ポジションがゼロになると (たとえば、手動で決済した後)、すべての保護注文がキャンセルされ、内部フラグがリセットされ、次の外部取引を最初からヘッジできるようになります。

パラメーター

パラメータ 説明
HedgeVolume 反対側のヘッジポジションをオープンするために使用されるボリューム。
StopLossPips エントリー価格から保護的なストップロス注文までの距離。
TakeProfitPips エントリー価格から利益確定注文までの距離。
TrailingStopPips アクティブ化のしきい値を超えた後にトレーリングストップによって維持される距離。末尾を無効にするには、ゼロに設定します。
TrailingActivationPips トレーリングストップが動き始める前に必要な最小利益(ピップ単位)。
BreakEvenPips ストップロスがエントリー価格に 1 ピップを加えた値に移動されるまでの利益のしきい値 (ピップ単位)。
OriginalOrderComment どの外部取引をヘッジするかを選択するオプションのコメント フィルター。商品のすべての取引をヘッジするには、空のままにします。
HedgerOrderComment 戦略によって生成されたヘッジ注文およびプロテクティブ ストップに添付されるコメント。

実践メモ

  • 外部トレーダーと同じポートフォリオ/口座を戦略に割り当てます。そのアカウントで作成されたすべてのポジションはコネクターに表示されるため、ヘッジすることができます。
  • MetaTrader ブリッジで使用する場合は、フィルタリングが期待どおりに機能するように、元の注文コメントをコピーするようにエキスパート アドバイザまたはブリッジを構成します。
  • ピップサイズは商品の価格ステップから導出されます。 5 桁の FX シンボルの場合、距離は指定されたピップ値を正しい価格オフセットに自動的に変換します。
  • 損益分岐点とトレーリングのロジックにより、ストップがエントリー価格からさらに遠ざかることはありません。改善のみが適用され、一度損益分岐点に達するとストップが赤字レベルに戻らないようにします。
  • 戦略は元のポジションを管理しません。閉鎖または変更は、引き続き主要な取引システムの責任となります。

使用ワークフロー

  1. コメントフィルターとヘッジのボリュームに特に注意して、戦略パラメータを設定します。
  2. ストラテジーを開始し、ブローカー フィードに接続されていることを確認します。外部取引が到着するまでアイドル状態になります。
  3. 適格な取引が表示されたらすぐに、ヘッジ注文がどのように作成され、DOM で保護注文がどのように発注されるかを観察します。
  4. 損益分岐点とトレーリングの動作を監視して、設定されたピップ距離がブローカーの契約仕様と一致していることを確認します。
  5. ヘッジが必要なくなったら戦略を停止します。稼働中のすべての保護命令はシャットダウン中にキャンセルされます。

制限事項

  • このモジュールは、アカウントの取引ストリームへのアクセスを想定しています。コネクターから完全に見えない取引をヘッジすることはできません。
  • 出来高丸めルールはブローカー固有です。設定された HedgeVolume が機器のロットステップと互換性があることを確認してください。
  • この戦略は成行注文を即座に発行するため、速い市場でのスリッページによりヘッジが不完全になる可能性があります。必要に応じて、これを考慮してストップロス距離を増やしてください。
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>
/// Adapted from the MetaTrader "EES Hedger" expert advisor.
/// Uses EMA crossover signals with break-even and trailing stop risk management.
/// </summary>
public class EesHedgerAdvancedStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPips;
	private readonly StrategyParam<int> _takeProfitPips;
	private readonly StrategyParam<DataType> _candleType;

	/// <summary>
	/// Fast EMA period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPips
	{
		get => _stopLossPips.Value;
		set => _stopLossPips.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPips
	{
		get => _takeProfitPips.Value;
		set => _takeProfitPips.Value = value;
	}

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

	/// <summary>
	/// Initializes default parameters.
	/// </summary>
	public EesHedgerAdvancedStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

		_stopLossPips = Param(nameof(StopLossPips), 500)
			.SetDisplay("Stop Loss", "Stop-loss distance", "Risk Management");

		_takeProfitPips = Param(nameof(TakeProfitPips), 500)
			.SetDisplay("Take Profit", "Take-profit distance", "Risk Management");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candles used for calculations", "Market Data");
	}

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		var fastEma = new EMA { Length = FastPeriod };
		var slowEma = new EMA { Length = SlowPeriod };

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

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

		// Use StartProtection for SL/TP
		var tp = TakeProfitPips > 0 ? new Unit(TakeProfitPips, UnitTypes.Absolute) : null;
		var sl = StopLossPips > 0 ? new Unit(StopLossPips, UnitTypes.Absolute) : null;
		StartProtection(tp, sl);

		base.OnStarted2(time);
	}

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
	}

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

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevFast = fastValue;
			_prevSlow = slowValue;
			_hasPrev = true;
			return;
		}

		if (!_hasPrev)
		{
			_prevFast = fastValue;
			_prevSlow = slowValue;
			_hasPrev = true;
			return;
		}

		// EMA crossover detection
		var crossedUp = _prevFast <= _prevSlow && fastValue > slowValue;
		var crossedDown = _prevFast >= _prevSlow && fastValue < slowValue;

		if (crossedUp)
		{
			// Close short if any, then go long
			if (Position < 0)
				BuyMarket(Math.Abs(Position));
			if (Position <= 0)
				BuyMarket(Volume);
		}
		else if (crossedDown)
		{
			// Close long if any, then go short
			if (Position > 0)
				SellMarket(Position);
			if (Position >= 0)
				SellMarket(Volume);
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}