GitHub で見る

あらゆるポジションをヘッジする戦略

概要

あらゆるポジションをヘッジする戦略は、元のMQL5エキスパート*Hedge any positions (barabashkakvn's edition)*の直接変換です。StockSharpバージョンは中心的なアイデアをそのまま保持します:戦略によって作成された各オープンレッグを監視し、レッグが定義された数のpipsを失うと、すぐに増幅されたロットサイズで反対ポジションを開きます。実装はStockSharpの高レベルAPIに依存しているため、ヘッジ注文は成行注文を通じて配置され、ポジション追跡はカスタム注文ルーティングコードなしに内部で処理されます。

戦略は開始時にオプションで最初のトレードを配置できます。その後は単に不利な価格変動に反応し、ヘッジングトレードのはしごを構築し、各レッグをヘッジ済みとしてマークして同じポジションが複数の反対エントリーを引き起こせないようにします。

ヘッジングワークフロー

  1. ローソク足フィード – 設定可能なCandleTypeが戦略を駆動します。完了したローソク足のみが処理されます。
  2. 損失計算 – 各ローソク足のクローズ時に、戦略はクローズ価格が計算されたpipサイズを掛けたLosingPips以上だけオープンレッグに不利に動いたかどうかを確認します。
  3. ヘッジ実行 – 損失レッグが見つかった場合、反対方向に成行注文が送られます。注文ボリュームは元のレッグボリュームをLotCoefficientで掛けたものに等しく、銘柄ボリュームステップに丸められ、許可された最小/最大ボリュームに制限されます。
  4. 状態更新 – 反対の注文が送られると、元のレッグはヘッジ済みとしてフラグが立てられ、新しく開かれたトレードは新鮮なレッグとして保存されます。そのレッグ自体も後で価格が再び反転した場合にヘッジされる可能性があります。

パラメーター

パラメーター 説明 デフォルト
CandleType 価格変動を評価してヘッジを引き起こすために使用される時間軸。 1分ローソク足
LosingPips ヘッジが開かれる前に価格がレッグに不利に動く必要があるpips数。 5
LotCoefficient ヘッジ注文を送信する際に元のボリュームに適用される乗数。 2.0
AutoPlaceInitialTrade 有効にすると、戦略は開始時に最初のトレードを自動的に送ります。 無効
InitialVolume オプションの最初のトレードで使用される注文サイズ。銘柄ボリュームステップに丸められます。 0.10
InitialDirection オプションの最初のトレードに使用されるサイド(買いまたは売り)。 買い

注: Strategy.Volumeプロパティを戦略が使用する基本注文サイズに設定します。上記のパラメーターはヘッジング固有の動作のみを制御します。

使用ガイドライン

  1. 戦略を開始する前にSecurityPortfolio、希望する基本Volumeを割り当てます。
  2. 選択した銘柄のボラティリティとリスク許容度を反映するようLosingPipsLotCoefficientを調整します。
  3. StockSharpバージョンが最初のポジションを自動的に作成するようにしたい場合はAutoPlaceInitialTradeを有効にします;そうでない場合は手動で最初のレッグを開くか、別のコンポーネントが行います。
  4. StockSharpの高レベルAPIはネットポジションで動作するため、内部レッグリストはヘッジ構造をエミュレートするために使用されます。ネッティングアカウントで実行する場合はアカウントエクスポージャーを監視します。
  5. 実行レポートを確認します:各ヘッジは成行注文(BuyMarketまたはSellMarket)で配置されます。

元のエキスパートとの違い

  • マージン検証、スリッページチェック、詳細な結果ログは削除されました;StockSharpはすでに戦略イベントを通じて実行の問題を報告します。
  • 変換はtick-by-tickデータの代わりに完了したローソク足を使用します。より速い反応時間が必要な場合は十分に小さな時間軸を選択します。
  • ロット丸めはSecurity.VolumeStepSecurity.MinVolumeSecurity.MaxVolumeに基づき、銘柄のトレードルールに準拠します。
  • アラート、通知、MQLバージョンのテスター専用のランダム初期トレードは意図的に省略されました。オプションの自動エントリーパラメーターがその動作を置き換えます。

推奨される機能拡張

  • 最初のポジションをいつ作成するかを定義する個別のエントリー戦略でヘッジングモジュールを組み合わせます。
  • 無制限のヘッジングチェーンを防ぐために、エクイティベースのシャットダウンルールまたは最大深度制限を追加します。
  • マージン要件が許容可能な限度内に収まるようにポートフォリオレベルの監視を統合します。
using System;
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>
/// Hedge Any Positions strategy using ATR-based mean reversion.
/// Enters long when price drops below EMA by ATR threshold, enters short on rally above.
/// Uses stop-loss and take-profit for risk management.
/// </summary>
public class HedgeAnyPositionsStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _atrMultiplier;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _ema;
	private AverageTrueRange _atr;

	private decimal _entryPrice;
	private int _cooldown;

	/// <summary>
	/// EMA period.
	/// </summary>
	public int EmaPeriod
	{
		get => _emaPeriod.Value;
		set => _emaPeriod.Value = value;
	}

	/// <summary>
	/// ATR period.
	/// </summary>
	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

	/// <summary>
	/// Multiplier applied to ATR for entry threshold.
	/// </summary>
	public decimal AtrMultiplier
	{
		get => _atrMultiplier.Value;
		set => _atrMultiplier.Value = value;
	}

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

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

	/// <summary>
	/// Initializes a new instance of the <see cref="HedgeAnyPositionsStrategy"/> class.
	/// </summary>
	public HedgeAnyPositionsStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 100)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA period for mean price", "Indicator");

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR calculation period", "Indicator");

		_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
			.SetGreaterThanZero()
			.SetDisplay("ATR Multiplier", "Multiplier for entry distance", "Indicator");

		_stopLossPoints = Param(nameof(StopLossPoints), 200)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 300)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

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

		_ema = null;
		_atr = null;
		_entryPrice = 0;
		_cooldown = 0;
	}

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

		_ema = new ExponentialMovingAverage { Length = EmaPeriod };
		_atr = new AverageTrueRange { Length = AtrPeriod };

		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_ema, _atr, ProcessCandle);
		subscription.Start();
	}

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

		if (!_ema.IsFormed || !_atr.IsFormed)
			return;

		if (_cooldown > 0)
		{
			_cooldown--;
			return;
		}

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;
		var threshold = atrValue * AtrMultiplier;

		// Check SL/TP
		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 80;
				return;
			}

			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 80;
				return;
			}
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 80;
				return;
			}

			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 80;
				return;
			}
		}

		// Mean reversion: buy when price drops below EMA by threshold
		if (close < emaValue - threshold && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
			_entryPrice = close;
			_cooldown = 80;
		}
		// Mean reversion: sell when price rallies above EMA by threshold
		else if (close > emaValue + threshold && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
			_entryPrice = close;
			_cooldown = 80;
		}
	}
}