GitHub で見る

ミーン・リバース戦略

概要

Mean Reversion Strategy は、「MeanReversionTrendEA」エキスパート アドバイザーを複製します。移動平均クロスオーバー トレンド モジュールと、平均トゥルー レンジ (ATR) ボラティリティ バンドによって駆動される平均回帰オーバーレイを組み合わせます。その考え方は、価格が強気または弱気のトレンド転換を確認した場合、またはボラティリティ調整後の距離だけ、より遅い移動平均から離れすぎた場合にポジションをオープンすることです。

取引ロジック

  • トレンド コンポーネント: 速い単純移動平均 (SMA) が遅い SMA を超えると、長いセットアップが表示されます。高速の SMA が低速の SMA を下回ると、短いセットアップがトリガーされます。
  • 平均回帰コンポーネント: 終値が遅い SMA を ATR × Multiplier を超えて下回るたびに、ロングセットアップが有効になります。価格が遅い SMA を同じ距離以上上回ると、短い設定が表示されます。
  • シグナルの組み合わせ: ポジションがオープンしていないときに、トレンド モジュールまたは平均回帰モジュールのいずれかがロング (ショート) をシグナルした場合、ストラテジーは設定されたボリュームでロング (ショート) ポジションにエントリーします。

貿易管理

  • ストップロス: エントリー直後、戦略は価格レベルをロング ポジションの場合は entry − StopLossPoints × Step、ショート ポジションの場合は entry + StopLossPoints × Step に設定します。ローソク足の極値がこのレベルに達すると、ポジションは閉じられます。
  • 利益確定: 利益目標はロングトレードの場合は entry + TakeProfitPoints × Step、ショートトレードの場合は entry − TakeProfitPoints × Step に設定されます。それぞれのローソク足の高値または安値に触れるとポジションがクローズされます。
  • 単一ポジション制約: アルゴリズムは最大でも 1 つのオープン ポジションを保持します。現在の取引が終了するまで、新しいシグナルは無視されます。
  • 安全モジュール: 組み込みの StartProtection() 呼び出しは、元のエキスパートアドバイザーからの安全貿易検証レイヤーをミラーリングし、予期しない位置状態を防ぎます。

インジケーター

  • 単純移動平均 (SMA)、期間は FastMaPeriod
  • 単純移動平均 (SMA)、期間は SlowMaPeriod
  • 期間 AtrPeriod の平均 True Range (ATR)

すべてのインジケーターは、CandleType によって定義された同じローソク足サブスクリプションから更新されます。

パラメーター

名前 説明 デフォルト
FastMaPeriod トレンド検出と平均回帰バンドの両方で使用される高速 SMA のルックバック。 20
SlowMaPeriod 均衡平均を表す遅い SMA のルックバック。 50
AtrPeriod ATR のボラティリティ計算用のローソク足の数。 14
AtrMultiplier 距離チェックのために ATR に適用される乗数。 2.0
StopLossPoints ストップロス距離は Security.Step 単位で測定されます。 500
TakeProfitPoints Security.Step 単位で測定されるテイクプロフィット距離。 1000
TradeVolume 各成行注文で送信されるボリューム。 1
CandleType インジケーターにフィードするローソク足のデータ型。 1時間枠

注意事項

  • デフォルトのローソク足のサイズは、MetaTrader バージョンの「現在のタイムフレーム」ロジックを反映するために 1 時間です。元のチャート期間に一致するように調整します。
  • ATR ベースのエンベロープは、基準価格としてローソク足の終値を使用し、買値と売値の間の元の中間点を反映します。
  • パラメーターに付加された最適化フラグを使用して、さまざまな市場に合わせてシステムを調整します。
namespace StockSharp.Samples.Strategies;

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;

/// <summary>
/// Mean reversion and trend following hybrid that reacts to moving average crossovers and ATR deviations.
/// </summary>
public class MeanReverseStrategy : Strategy
{
	private readonly StrategyParam<int> _fastMaPeriod;
	private readonly StrategyParam<int> _slowMaPeriod;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _atrMultiplier;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<DataType> _candleType;

	private SimpleMovingAverage _fastMa = null!;
	private SimpleMovingAverage _slowMa = null!;
	private AverageTrueRange _atr = null!;

	private decimal? _prevFastMa;
	private decimal? _prevSlowMa;
	private decimal _stopLossPrice;
	private decimal _takeProfitPrice;

	/// <summary>
	/// Fast simple moving average period.
	/// </summary>
	public int FastMaPeriod
	{
		get => _fastMaPeriod.Value;
		set => _fastMaPeriod.Value = value;
	}

	/// <summary>
	/// Slow simple moving average period.
	/// </summary>
	public int SlowMaPeriod
	{
		get => _slowMaPeriod.Value;
		set => _slowMaPeriod.Value = value;
	}

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

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

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

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

	/// <summary>
	/// Volume used for a new position.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

	/// <summary>
	/// Candle type processed by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public MeanReverseStrategy()
	{
		_fastMaPeriod = Param(nameof(FastMaPeriod), 20)
			.SetRange(5, 200)
			.SetDisplay("Fast MA Period", "Length of the fast simple moving average", "Indicators")
			;

		_slowMaPeriod = Param(nameof(SlowMaPeriod), 50)
			.SetRange(5, 300)
			.SetDisplay("Slow MA Period", "Length of the slow simple moving average", "Indicators")
			;

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetRange(5, 100)
			.SetDisplay("ATR Period", "Number of candles used for ATR calculation", "Indicators")
			;

		_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
			.SetRange(0.5m, 5m)
			.SetDisplay("ATR Multiplier", "Multiplier applied to ATR for deviation bands", "Indicators")
			;

		_stopLossPoints = Param(nameof(StopLossPoints), 500)
			.SetRange(10, 5000)
			.SetDisplay("Stop Loss Points", "Stop-loss distance in security steps", "Risk Management")
			;

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 1000)
			.SetRange(10, 10000)
			.SetDisplay("Take Profit Points", "Take-profit distance in security steps", "Risk Management")
			;

		_tradeVolume = Param(nameof(TradeVolume), 1m)
			.SetRange(0.01m, 100m)
			.SetDisplay("Trade Volume", "Volume opened with a new signal", "Execution")
			;

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of market data processed by the strategy", "General");
	}

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

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

		_prevFastMa = null;
		_prevSlowMa = null;
		_stopLossPrice = default;
		_takeProfitPrice = default;
	}

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

		_fastMa = new SimpleMovingAverage { Length = FastMaPeriod };
		_slowMa = new SimpleMovingAverage { Length = SlowMaPeriod };
		_atr = new AverageTrueRange { Length = AtrPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_fastMa, _slowMa, _atr, ProcessCandle)
			.Start();

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

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

		// indicators checked via Bind

		var step = Security?.PriceStep ?? 1m;

		if (_prevFastMa is null || _prevSlowMa is null)
		{
			_prevFastMa = fastMa;
			_prevSlowMa = slowMa;
			return;
		}

		var prevFast = _prevFastMa.Value;
		var prevSlow = _prevSlowMa.Value;

		var trendBuySignal = prevFast <= prevSlow && fastMa > slowMa;
		var trendSellSignal = prevFast >= prevSlow && fastMa < slowMa;

		var deviation = atr * AtrMultiplier;
		var reversionBuySignal = candle.ClosePrice < slowMa - deviation;
		var reversionSellSignal = candle.ClosePrice > slowMa + deviation;

		var buySignal = trendBuySignal || reversionBuySignal;
		var sellSignal = trendSellSignal || reversionSellSignal;

		if (Position == 0)
		{
			if (buySignal)
			{
				EnterLong(candle.ClosePrice, step);
			}
			else if (sellSignal)
			{
				EnterShort(candle.ClosePrice, step);
			}
		}
		else if (Position > 0)
		{
			ManageLongPosition(candle);
		}
		else
		{
			ManageShortPosition(candle);
		}

		_prevFastMa = fastMa;
		_prevSlowMa = slowMa;
	}

	private void EnterLong(decimal entryPrice, decimal step)
	{
		// Open a new long position and pre-calculate risk levels.
		BuyMarket(TradeVolume);

		_stopLossPrice = entryPrice - StopLossPoints * step;
		_takeProfitPrice = entryPrice + TakeProfitPoints * step;
	}

	private void EnterShort(decimal entryPrice, decimal step)
	{
		// Open a new short position and pre-calculate risk levels.
		SellMarket(TradeVolume);

		_stopLossPrice = entryPrice + StopLossPoints * step;
		_takeProfitPrice = entryPrice - TakeProfitPoints * step;
	}

	private void ManageLongPosition(ICandleMessage candle)
	{
		// Exit logic for the long position.
		if (candle.LowPrice <= _stopLossPrice)
		{
			SellMarket(Position);
			ClearRiskLevels();
			return;
		}

		if (_takeProfitPrice != default && candle.HighPrice >= _takeProfitPrice)
		{
			SellMarket(Position);
			ClearRiskLevels();
		}
	}

	private void ManageShortPosition(ICandleMessage candle)
	{
		// Exit logic for the short position.
		if (candle.HighPrice >= _stopLossPrice)
		{
			BuyMarket(Math.Abs(Position));
			ClearRiskLevels();
			return;
		}

		if (_takeProfitPrice != default && candle.LowPrice <= _takeProfitPrice)
		{
			BuyMarket(Math.Abs(Position));
			ClearRiskLevels();
		}
	}

	private void ClearRiskLevels()
	{
		// Reset stored risk levels after an exit.
		_stopLossPrice = default;
		_takeProfitPrice = default;
	}
}