GitHub で見る

毎週のリバウンドコリドー戦略

概要

毎週のリバウンド コリドー戦略は、MetaTrader 4 エキスパート アドバイザー 2_Otkat_Sys_v1_1 の動作を再現します。システムは、前のセッションの終値と 24 キャンドル前に発生した始値との間の大きなギャップを検索します。検出されたギャップが設定可能なコリドーしきい値を超え、その日が指定された取引曜日である場合、ストラテジーは新しい取引日の最初の数分間に市場に参入します。保護的なストップロスおよびテイクプロフィットレベルが適用され、すべてのオープンポジションは取引セッションが終了する直前に強制的にクローズされます。

取引ロジック

  1. データの準備
    • デフォルトでは分単位のキャンドルを使用します。キャンドル タイプは、他のバー サイズに合わせて構成できます。
    • 前回のローソク足の終値を追跡し、24 個前に観察されたローソク足の始値を返す循環バッファーを維持します。
  2. 信号生成
    • 指定された取引曜日 (MetaTrader 形式: 0 = Sunday6 = Saturday) に、ストラテジーは現地時間が 00:00 から 00:03 の間にある終了したローソク足を評価します。
    • 過去の始値 (24 ローソク前) と最新の終値ローソクの差を計算します。差が構成されたコリドーしきい値を超える場合、成行注文が送信されます。
      • 長期設定: 過去の始値から前回の終値を引いた値がコリドーしきい値を超えています。
      • 短い設定: 前回の終値から過去の始値を引いた値がコリドーしきい値を超えています。
    • 各取引日は最大で 1 つのエントリーをトリガーできます。
  3. 貿易管理
    • ストップロスとテイクプロフィットのレベルはポイントで表されます。商品のティック サイズにより、ポイント値が実際の価格オフセットに変換されます。
    • ロングトレードでは、元の MT4 オフセットである 3 ポイントが利益確定距離に追加されます。
    • この戦略は、ローソク足の高値と安値を継続的に監視してストップロスまたはテイクプロフィットのヒットを検出し、トリガーされると成行注文でオープンポジションをクローズします。
    • 残りのオープンポジションは、元の Expert Advisor の 1 日の終わりのフラットルールをエミュレートするために、現地為替時間の 22:45 後にクローズされます。

パラメーター

名前 説明 デフォルト
TakeProfitPoints テイクプロフィット距離(ポイント単位)。 MT4 スクリプトで定義されているように、ロング取引では 3 つの追加ポイントが追加されます。 5
StopLossPoints ポイント単位のストップロス距離。 49
TradeVolume 成行注文で発注された数量。値は楽器の音量ステップに自動的に調整されます。 1
CorridorPoints 過去の始値と最新の終値の間に必要な最小ギャップ。 10
TradeDayOfWeek MetaTrader 番号の取引日 (0 = Sunday6 = Saturday)。 5 (金曜日)
CandleType 分析に使用されるローソク足のデータ型。 1 minute

注意事項

  • この戦略は、プロジェクトのガイドラインに沿って完成したキャンドルに対してのみ機能します。
  • エントリーを期待する前に、選択した商品が 24 キャンドルのバッファーを構築するのに十分な履歴データを提供していることを確認してください。
  • 出来高およびポイントベースのパラメーターは、商品の仕様 (ティックサイズ、ロットステップ、取引スケジュール) に一致するように調整する必要があります。
using System;

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

namespace StockSharp.Samples.Strategies;

public class WeeklyReboundCorridorStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<int> _cooldownCandles;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevRsi;
	private bool _hasPrev;
	private int _cooldownRemaining;

	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
	public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public WeeklyReboundCorridorStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14).SetDisplay("RSI Period", "RSI lookback", "Indicators");
		_emaPeriod = Param(nameof(EmaPeriod), 50).SetDisplay("EMA Period", "EMA filter", "Indicators");
		_oversold = Param(nameof(Oversold), 25m).SetDisplay("Oversold", "RSI oversold level", "Levels");
		_overbought = Param(nameof(Overbought), 75m).SetDisplay("Overbought", "RSI overbought level", "Levels");
		_cooldownCandles = Param(nameof(CooldownCandles), 50).SetDisplay("Cooldown", "Candles between signals", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevRsi = default;
		_hasPrev = default;
		_cooldownRemaining = default;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevRsi = 0;
		_hasPrev = false;
		_cooldownRemaining = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ema, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal rsi, decimal ema)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		if (!_hasPrev) { _prevRsi = rsi; _hasPrev = true; return; }

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevRsi = rsi;
			return;
		}

		var oversold = Oversold;
		var overbought = Overbought;

		if (_prevRsi >= oversold && rsi < oversold && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_cooldownRemaining = CooldownCandles;
		}
		else if (_prevRsi <= overbought && rsi > overbought && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_cooldownRemaining = CooldownCandles;
		}
		_prevRsi = rsi;
	}
}