GitHub で見る

逆反応戦略

概要

逆反応戦略は、オリジナルのMetaTraderエキスパートアドバイザー「IREA」に触発された平均回帰システムです。異常に大きな単一バーの動きに反応し、次のバーで逆反応を予測します。戦略は最近のローソク足の範囲から動的信頼レベルを計算し、価格変動がそのレベルを超えるがユーザー定義の範囲内に収まる場合にのみ取引します。一度に開けるポジションは1つだけです。

トレーディングロジック

  1. 逆反応インジケーター – 完成したローソク足ごとに戦略は始値/終値の変化を測定し、その絶対値を長さMaPeriodの単純移動平均に入力します。平均された変化はCoefficientで乗算され、オリジナルインジケーターのDynamic Confidence Level(DCL)に似た動的閾値を形成します。
  2. シグナル検証 – 最新のローソク足の絶対始値/終値変化は動的閾値より大きく、MinCriteriaPoints * PriceStepより大きく、MaxCriteriaPoints * PriceStepより小さくなければなりません。前のローソク足がすでに同じ条件を満たしていた場合、シグナルは無視されます。これはオリジナルのエキスパートアドバイザーを反映しています。
  3. 方向 – 負の変化(弱気のローソク足)は上昇への反発を示唆するため、ロングポジションが開かれます。正の変化は弱気の反転の期待を意味し、ショートポジションをトリガーします。既存のポジションがない場合にのみ新しい取引が送信されます。
  4. リスク管理 – エントリー後、戦略は後続のローソク足を監視します。価格が事前定義されたストップロスまたはテイクプロフィットレベル(インストゥルメントのPriceStepを使用してポイントから絶対価格に変換)に触れた場合、成行注文を使用してすぐにオープンポジションを閉じます。StartProtection()もStockSharpの組み込み保護をサポートするために有効になります。

パラメーター

パラメーター 説明
StopLossPoints ポイント単位のストップロス距離(PriceStepで乗算)。
TakeProfitPoints ポイント単位のテイクプロフィット距離。
TradeVolume すべての成行注文に使用するボリューム。
SlippagePoints MQLバージョンを反映する情報設定。現在は注文に適用されていません。
MinCriteriaPoints 有効なシグナルに必要な最小始値/終値距離(ポイント)。
MaxCriteriaPoints 許容される最大始値/終値距離(ポイント)。
Coefficient 動的信頼閾値を構築するために使用する乗数。
MaPeriod インジケーター内で使用する移動平均の長さ。少なくとも3でなければなりません。
CandleType 処理されるローソク足のタイムフレーム(デフォルト: 1時間)。

使用ガイドライン

  • 選択したインストゥルメントに有効なPriceStepがあることを確認してください。利用できない場合、戦略はステップ1.0にフォールバックし、閾値を歪める可能性があります。
  • 選択したタイムフレームのボラティリティに合わせてMinCriteriaPointsMaxCriteriaPointsを調整してください。ウィンドウが狭すぎるとほとんどのシグナルがフィルタリングされ、広すぎると反転しない可能性のある極端に大きな動きが許可されます。
  • デフォルトのCoefficient 1.618はオリジナルインジケーターの黄金比スケーリングを再現します。値が高いほど、取引前により大きな外れ値ローソク足が必要になります。
  • ポジションはストップまたはターゲットレベルに違反する次のローソク足のクローズ時に成行注文でクローズされるため、実際の執行は正確なリミットレベルと異なる場合があります。必要に応じてより精密な制御のためにイントラデイデータでのテストを検討してください。
  • 一度に保持されるポジションは1つのみです。戦略は現在の取引がクローズするのを待ってから新しいシグナルに反応します。

注意事項

  • ライブで使用する前に履歴データで設定をバックテストしてください。オリジナルのEAはFX市場向けに設計されており、他のアセットではパラメーター調整が必要な場合があります。
  • SlippagePointsパラメーターは完全性のために保持されていますが、StockSharpはMetaTraderとは異なる方法でスリッページを処理するため、意図的に使用されていません。
  • MaPeriodは3以上を維持してください。小さい値はオリジナルの実装で禁止されており、不安定な閾値につながる可能性があります。
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy that reacts to large single-bar moves expecting a mean reversion.
/// </summary>
public class InverseReactionStrategy : Strategy
{
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<decimal> _coefficient;
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private readonly List<decimal> _absChanges = new();

	public decimal StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public decimal TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
	public decimal Coefficient { get => _coefficient.Value; set => _coefficient.Value = value; }
	public int MaPeriod { get => _maPeriod.Value; set => _maPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public InverseReactionStrategy()
	{
		_stopLossPoints = Param(nameof(StopLossPoints), 1000m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss", "Stop-loss distance in points", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 250m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit", "Take-profit distance in points", "Risk");

		_coefficient = Param(nameof(Coefficient), 1.618m)
			.SetGreaterThanZero()
			.SetDisplay("Coefficient", "Confidence coefficient", "Signal");

		_maPeriod = Param(nameof(MaPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "Moving average length", "Signal");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe", "General");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_absChanges.Clear();
	}

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

		var step = Security?.PriceStep ?? 1m;
		if (step <= 0m) step = 1m;

		StartProtection(
			takeProfit: new Unit(TakeProfitPoints * step, UnitTypes.Absolute),
			stopLoss: new Unit(StopLossPoints * step, UnitTypes.Absolute),
			useMarketOrders: true);

		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ProcessCandle).Start();
	}

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

		var change = candle.ClosePrice - candle.OpenPrice;
		var absChange = Math.Abs(change);

		_absChanges.Add(absChange);
		if (_absChanges.Count > MaPeriod)
			_absChanges.RemoveAt(0);

		if (_absChanges.Count < MaPeriod)
			return;

		var avg = 0m;
		for (int i = 0; i < _absChanges.Count; i++)
			avg += _absChanges[i];
		avg /= _absChanges.Count;

		var threshold = avg * Coefficient;

		if (Position != 0)
			return;

		if (absChange > threshold && absChange > 0m)
		{
			if (change < 0m)
			{
				// Large bearish bar => expect reversion upward
				BuyMarket();
			}
			else
			{
				// Large bullish bar => expect reversion downward
				SellMarket();
			}
		}
	}
}