GitHub で見る

クローズオーダーのリスク管理戦略

概要

Close Orders Strategy は、元の MQL エキスパート アドバイザー CloseOrders.mq4 の動作を反映するリスク管理ユーティリティです。オープンポジションの変動損益を継続的に監視し、利益目標またはロスカットしきい値に達すると、一致する注文を自動的に清算します。これにより、ポートフォリオを保護したり、複数の戦略間でエグジットを同期したりするのに適しています。

仕組み

  1. この戦略は、設定可能なローソク足シリーズ (デフォルトでは 1 分) をサブスクライブし、ローソク足が閉じるたびに現在の変動損益を評価します。
  2. 変動損益は、アクティブなポートフォリオのポジションに対して計算されます。マジックナンバーが指定されている場合、内部の StrategyId が構成された値と一致するポジションのみが含まれます。
  3. 変動利益が目標金額以上の場合、一致する注文とポジションはすべてクローズされます。
  4. 変動利益が設定されたカットロス (負の数値) を下回った場合、さらなる損失を最小限に抑えるために同じ清算ルーチンがトリガーされます。
  5. マジックナンバーフィルターを満たすアクティブな注文は、清算中に新たなエクスポージャーがオープンされないように、ポジションをフラット化する前にキャンセルされます。

清算ルーチンは、一致するすべてのポジションがフラットになるまで実行を継続し、部分的な約定が適切に処理されることを保証します。

パラメーター

パラメータ 説明
目標利益金額 一致する注文の清算を引き起こす変動利益 (口座通貨)。ゼロより大きくなければなりません。
カットロスマネー 強制的に清算を行うマイナスの変動損益(口座通貨単位)。値 0 は損失ベースの終了を無効にします。
マジックナンバー オプションの戦略識別子。すべてのオープンポジションを管理するには、空のままにしてください。それ以外の場合は、StrategyId が指定された値と等しい位置のみが影響を受けます。
キャンドルタイプ 定期的な利益チェックをトリガーするために使用されるローソク足シリーズ。より高い頻度の監視が必要な場合は、時間枠を調整します。

実装メモ

  • MQL マジック ナンバーの概念は、StockSharp の UserOrderId/StrategyId フィールドにマッピングされます。管理する必要がある戦略が同じ識別子を使用していることを確認してください。
  • インデントにはタブが使用され、ファイルは変換された戦略に要求される共通の構造に従います。
  • この戦略は、エクスポージャーを平準化するために成行注文を送信する前に保留中の注文をキャンセルし、即時の再エントリーを防ぎます。
  • 緊急対応が必要なライブ取引コンポーネントと戦略を組み合わせる場合、開始保護を追加できます。

使い方のヒント

  • カスタム StrategyId を設定して出口ロジックを一元化する取引戦略と並行して戦略を展開します。
  • Candle Type パラメータを調整して、応答性とリソース使用量のバランスをとります。タイムフレームが短いほど、損益の変化に対する反応が速くなります。
  • このユーティリティとアラートを組み合わせて、自動清算が実行されるたびに通知を受け取ります。
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Close Orders Risk Control strategy: CCI crossover with risk management.
/// Buys when CCI crosses above zero, sells when crosses below zero.
/// </summary>
public class CloseOrdersRiskControlStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<decimal> _cciLevel;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private decimal _prevCci;
	private int _candlesSinceTrade;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
	public decimal CciLevel { get => _cciLevel.Value; set => _cciLevel.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public CloseOrdersRiskControlStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_cciPeriod = Param(nameof(CciPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "CCI period", "Indicators");
		_cciLevel = Param(nameof(CciLevel), 100m)
			.SetDisplay("CCI Level", "CCI threshold for crossover", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevCci = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevCci = 0;
		_candlesSinceTrade = SignalCooldownCandles;
		_hasPrev = false;
		var cci = new CommodityChannelIndex { Length = CciPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(cci, ProcessCandle).Start();
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		if (_hasPrev)
		{
			if (_prevCci < -CciLevel && cciValue >= -CciLevel && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (_prevCci > CciLevel && cciValue <= CciLevel && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevCci = cciValue;
		_hasPrev = true;
	}
}