GitHub で見る

回避平均復帰戦略

概要

エスケープ戦略は、MetaTrader 4 エキスパート アドバイザー escape.mq4 の StockSharp 移植です。オリジナルのロボットは 5 分足チャートを取引し、平均回帰の機会に反応します。価格が短期移動平均を下回ったときに買い、価格が別の高速平均を上回ったときに売ります。各ポジションは、MetaTrader ポイントで表される固定距離のテイクプロフィットとストップロスによって保護されます。 C# 実装では、同じ最小限のロジックを維持しながら、すべての調整可能な距離を戦略パラメーターとして公開します。

取引ロジック

  1. 初期化

    • 構成可能な CandleType シリーズ (デフォルトでは 5 分間のローソク足) を購読します。
    • ローソク足の始値を入力する長さ 5 と 4 の 2 つの SimpleMovingAverage インジケーターを作成します。
    • Security.PriceStep から MetaTrader Point に相当する値を計算します。この値は、pip スタイルの距離を絶対価格に変換するために再利用されます。
  2. キャンドルごとの処理

    • 完成したキャンドルのみが SubscribeCandles(...).WhenCandlesFinished(ProcessCandle) 経由で処理されます。
    • この戦略はまず、ローソク足の高値/安値と記録された出口レベルを比較することによって、既存のポジションがストップロスまたはテイクプロフィットに達しているかどうかをチェックします。レベルを突破すると、ポジションは成行注文でクローズされ、重複した決済注文は内部フラグによって防止されます。
    • 口座がフラットで、2 つの SMA の以前の値が利用可能で、取引が許可され、ポートフォリオに十分な資本 (Portfolio.CurrentValue >= MinimumMarginPerLot * TradeVolume) が保持されている場合、ストラテジーはエントリーを評価します。
      • ロングエントリー – 現在の終値は過去 5 期間の始値 SMA を下回っています。
      • ショートエントリー – 現在の終値は過去 4 期間の始値 SMA を上回っています。
    • シグナルがトリガーされると、設定されたポイント距離を使用してローソク足の終値からストップロスとテイクプロフィットのレベルが計算され、後の監視のために保存されます。
  3. リスク管理

    • TradeVolume は、すべての成行注文のロットサイズを定義します。
    • MinimumMarginPerLot は、MetaTrader からの AccountFreeMargin 小切手に近似します。使用可能なポートフォリオ値が小さすぎる場合、エントリはスキップされ、診断メッセージが記録されます。

パラメーター

名前 デフォルト 説明
LongTakeProfitPoints 10 ロングポジションのテイクプロフィット距離(MetaTrader ポイント)。ターゲットを無効にするには、0 に設定します。
ShortTakeProfitPoints 10 ショートポジションのテイクプロフィット距離(MetaTraderポイント)。ターゲットを無効にするには、0 に設定します。
LongStopLossPoints 1000 ロング ポジションのストップロス距離 (MetaTrader ポイント)。保護停止を無効にするには、0 に設定します。
ShortStopLossPoints 1000 MetaTrader ポイントのショート ポジションのストップロス距離。保護停止を無効にするには、0 に設定します。
TradeVolume 0.2 成行注文を送信するときに使用されるロットサイズ。
MinimumMarginPerLot 500 新しい取引を開始する前のロットごとのおおよその資本要件。
CandleType 5分間の時間枠 インジケーターの更新とシグナル生成を推進する Candle シリーズ。

実装メモ

  • インジケーターはローソクの始値を使用して ProcessCandle 内で手動で更新されるため、保存された値は常に前の足を表します (iMA で使用される shift=1 引数を反映しています)。
  • 終了レベルは、追加のコレクションを作成するのではなく、10 進フィールドで追跡され、高レベルの API ガイドラインを満たします。
  • ストップとターゲットはローソク足の極値に対して評価されます。 OHLC データのみが利用可能なため、MetaTrader の注文優先順位を可能な限り厳密にエミュレートするために、テイクプロフィットの前にストップチェックが実行されます。
  • この戦略では、チャート領域が利用可能な場合、移動平均と独自の取引の両方とともにローソク足を描画し、視覚的な検証を簡素化します。

MetaTrader バージョンとの違い

  • MetaTrader は、ストップロス注文と利食い注文をチケットに直接添付します。 StockSharp ポートは、ローソク足の高値と安値を監視し、市場出口を送信することでそれらを再現します。同じバー内で両方のレベルに触れた場合、バー内実行順序は保証されません。
  • エントリー価格は、MetaTrader で使用される正確な買値/売値ではなく、シグナルをトリガーしたローソク足の終値から導出されるため、スリッページとスプレッドの処理はコネクタ レベルで設定する必要があります。
  • AccountFreeMargin() ガードは Portfolio.CurrentValue を通じて近似されます。より詳細なマージン モデルを持つユーザーは、必要に応じて HasSufficientMargin を拡張できます。
  • 色、音、滑りなどの装飾的な MQL の設定は省略されています。 StockSharp バージョンは、コア取引ロジックに重点を置いています。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Escape Mean Reversion: SMA crossover with ATR stops.
/// </summary>
public class EscapeMeanReversionStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _smaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevClose;
	private decimal _entryPrice;

	public EscapeMeanReversionStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_smaLength = Param(nameof(SmaLength), 5)
			.SetDisplay("SMA Length", "SMA period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int SmaLength
	{
		get => _smaLength.Value;
		set => _smaLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

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

		_prevClose = 0;
		_entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevClose = 0;
		_entryPrice = 0;

		var sma = new SimpleMovingAverage { Length = SmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(sma, atr, ProcessCandle)
			.Start();

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

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

		var close = candle.ClosePrice;

		if (_prevClose == 0 || atrVal <= 0)
		{
			_prevClose = close;
			return;
		}

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 1.5m || close > smaVal)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 1.5m || close < smaVal)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (close < smaVal && _prevClose >= smaVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (close > smaVal && _prevClose <= smaVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevClose = close;
	}
}