GitHub で見る

マスター出口計画戦略

概要

MasterExitPlanStrategy は、StockSharp の高レベルの API を使用して、MetaTrader エキスパート アドバイザー「マスター出口計画」のリスク管理ロジックを再現します。この戦略は新しい取引を開始するものではありません。代わりに、既存のエクスポージャーを監視し、非表示と表示のストップ ルールの組み合わせを適用し、保留中の注文を追跡し、株式が設定された利益目標に達したらすべてをクローズします。

実装は 1 分間のキャンドルをサブスクライブして、元のスクリプトからの iOpen(symbol, PERIOD_M1, 1) 呼び出しをエミュレートします。すべてのタイマーは戦略スケジューラによって駆動され、MetaTrader EventSetTimer(1) ループの動作と一致して毎秒評価されます。

特長

  • 株式ターゲットのエグジット – ポートフォリオの株式利益が設定されたパーセンテージに達すると、すべてのポジションをクローズします。
  • 静的および動的ストップレベル – エントリー価格からのストップ距離と分ベースの動的アンカーの両方を監視します。
  • 隠れストップ処理 – 交換命令に依存するのではなく、内部で保護出口を実行します。
  • トレーリングストップモジュール – 最小限のマネーゲイン後にアクティブになり、スプレッド補償付きでストップを追跡します。
  • 未決注文のトレーリング – 買いストップ注文と売りストップ注文が市場に追従するように自動的に再登録されます。

パラメーター

名前 説明 デフォルト
EnableTargetEquity 株式目標の清算を有効にします。 false
TargetEquityPercent 目標として使用される現在の残高の割合。 1
EnableStopLoss 静的なブローカースタイルのストップロスを有効にします。 false
StopLossPoints 静的停止距離 (MetaTrader ポイント)。 2000
EnableDynamicStopLoss ハードストップを直前のオープンに結び付けます。 false
DynamicStopLossPoints 動的停止距離 (ポイント)。 2000
EnableHiddenStopLoss 非表示の静的ストップロスを有効にします。 false
HiddenStopLossPoints 非表示の静的停止距離 (ポイント)。 800
EnableHiddenDynamicStopLoss 開始分に基づいて非表示の動的停止を有効にします。 false
HiddenDynamicStopLossPoints 非表示の動的停止距離 (ポイント)。 800
EnableTrailingStop トレーリングストップモジュールを有効にします。 false
TrailingStopPoints 価格(ポイント)よりも遅れて維持される追続距離。 5
TrailingTargetPercent トレーリングがアクティブになる前の残高の%で表した最小利益。 0.2
SureProfitPoints トレーリングストップを作動させる前に確保しなければならない追加ポイント。 30
EnableTrailPendingOrders アクティブなストップ注文 (エントリー) のトレーリングを有効にします。 false
TrailPendingOrderPoints 後続の保留中のストップ注文のポイント単位のオフセット。 10

使用上の注意

  1. 別のエントリーモジュールまたは手動注文によってすでに管理されている証券にストラテジーを添付します。フラット化時に閉じる必要がある契約に応じて、Volume を設定します。
  2. Portfolio.CurrentValue をレポートするポートフォリオを提供します。この戦略では、この値を使用して、MetaTrader から AccountBalanceAccountEquity を近似します。値が欠落している場合、資本目標ロジックはアイドル状態のままになります。
  3. この戦略は、ストップ条件をチェックするときに最良の買値/売値を評価します。スプレッドを考慮した計算が意味のあるものとなるように、レベル 1 データが利用可能であることを確認してください。
  4. 隠れストップとトレーリングエグジットは、ソフトウェア管理の成行注文として実装されます。ブローカー側のストップ注文は作成されません。この動作は、元の EA の「隠された」性質を反映しています。

MQL バージョンとの違い

  • ストップレベルは、しきい値を超えたときに成行注文を発行することによって強制されます。元の EA は OrderStopLoss フィールドを変更しました。 StockSharp は代わりにアクティブ モニタリングを使用します。
  • 動的ストップの計算は、SubscribeCandles を通じて配信された、最後に完了した 1 分間のローソク足に依存します。このサブスクリプションが存在しない場合、動的ルールは無効のままになります。
  • 未決注文トレーリングは、MasterExitPlanStrategy 自体がそれらを登録しないため、他の戦略によって作成された保護逆指値注文を無視します。
  • 株式チェックでは、AccountBalance/AccountEquity の代わりに Portfolio.CurrentValue (Portfolio.BeginValue へのフォールバック) が使用されます。

テスト

この戦略には自動テストは含まれていません。 StockSharp のテスターと履歴データを使用して、実稼働環境にデプロイする前に機器の動作を検証します。

namespace StockSharp.Samples.Strategies;

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

/// <summary>
/// Master Exit Plan strategy: EMA trend following with ATR-based trailing stop exit.
/// Enters on EMA crossover, exits when price retraces by ATR multiple.
/// </summary>
public class MasterExitPlanStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _atrMultiplier;

	private decimal _entryPrice;
	private decimal _trailStop;
	private bool _wasBullish;
	private bool _hasTrendState;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public decimal AtrMultiplier { get => _atrMultiplier.Value; set => _atrMultiplier.Value = value; }

	public MasterExitPlanStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 60)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR period for trailing stop", "Risk");
		_atrMultiplier = Param(nameof(AtrMultiplier), 3m)
			.SetDisplay("ATR Multiplier", "ATR multiplier for trailing distance", "Risk");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_entryPrice = 0;
		_trailStop = 0;
		_wasBullish = false;
		_hasTrendState = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_entryPrice = 0;
		_trailStop = 0;
		_wasBullish = false;
		_hasTrendState = false;
		var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
		var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fastEma, slowEma, ProcessCandle).Start();
	}

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

		var close = candle.ClosePrice;
		var range = candle.HighPrice - candle.LowPrice;
		if (range <= 0) return;

		var trailDist = range * AtrMultiplier;
		var isBullish = fastValue > slowValue;
		var crossedUp = _hasTrendState && !_wasBullish && isBullish;
		var crossedDown = _hasTrendState && _wasBullish && !isBullish;

		if (Position > 0)
		{
			var newStop = close - trailDist;
			if (newStop > _trailStop)
				_trailStop = newStop;
			if (close < _trailStop)
			{
				SellMarket();
				_trailStop = 0;
				_entryPrice = 0;
				_wasBullish = isBullish;
				_hasTrendState = true;
				return;
			}
			else if (crossedDown)
			{
				SellMarket();
				_trailStop = 0;
				_entryPrice = 0;
				_wasBullish = isBullish;
				_hasTrendState = true;
				return;
			}
		}
		else if (Position < 0)
		{
			var newStop = close + trailDist;
			if (_trailStop == 0 || newStop < _trailStop)
				_trailStop = newStop;
			if (close > _trailStop)
			{
				BuyMarket();
				_trailStop = 0;
				_entryPrice = 0;
				_wasBullish = isBullish;
				_hasTrendState = true;
				return;
			}
			else if (crossedUp)
			{
				BuyMarket();
				_trailStop = 0;
				_entryPrice = 0;
				_wasBullish = isBullish;
				_hasTrendState = true;
				return;
			}
		}

		if (Position == 0)
		{
			if (crossedUp)
			{
				BuyMarket();
				_entryPrice = close;
				_trailStop = close - trailDist;
			}
			else if (crossedDown)
			{
				SellMarket();
				_entryPrice = close;
				_trailStop = close + trailDist;
			}
		}

		_wasBullish = isBullish;
		_hasTrendState = true;
	}
}