GitHub で見る

NRTR Revers戦略

概要

NRTR Revers戦略は、MetaTrader 5のオリジナルエキスパートアドバイザーNRTR_Revers.mq5のC#変換です。システムはNick Rypock Trailing Reverse(NRTR)アプローチを使用して、価格がATRで投影されたサポートおよびレジスタンスバンドとどのように相互作用するかに応じて、ロングとショートのバイアスを切り替えます。トレードの決定は、単一の時間軸サブスクリプションから来る各完了ローソク足のクローズ時に評価されます。

トレードロジック

  1. ATR投影 – 戦略は設定可能な期間でAverage True Range(ATR)を計算します。ATR値はVolatilityMultiplierで乗算され、バンドオフセットを取得します。
  2. 動的バンド – 現在のトレンド方向に対して、戦略は以下を見つけます:
    • 元のMQLウィンドウ設定に合致するローソク足の中で最低安値(または最高高値)。
    • 歴史のより深いところにシフトされた二次的な極値。一次バンドとこの二次極値の間の距離がReversePips閾値と組み合わせて強い反転を確認するために使用されます。
  3. トレンド転換 – 前のクローズがATRバンドの外に動くか、二次極値の差が反転距離を超えると、バイアスが切り替わります(ロングからショートまたはその逆)。反対のポジションが存在する場合は先にそれを決済し、そうでない場合は新しい方向のポジションをすぐに開きます。
  4. フラット待機 – 既存ポジションを決済するために反対の成行注文を発行した後、戦略はポートフォリオがフラットになるまで待ってから新しいエントリー注文を送信します。この動作は元のエキスパートアドバイザーを反映しています。
  5. リスク管理 – ストップロス、テイクプロフィット、トレーリングストップのレベルはpipsで定義され、調整されたポイント値(3桁と5桁のForexシンボルに対応)を使用して絶対価格に変換されます。トレーリングの更新にはTrailingStopPips + TrailingStepPipsより大きい価格進行が必要で、MT5のロジックに一致します。

パラメーター

  • CandleType – 価格データのサブスクリプションに使用する主要な時間軸。
  • AtrPeriod – バンド計算に使用するATRの平均化期間。
  • VolatilityMultiplier – 極値からのオフセットを決定するためにATR値に適用される乗数。
  • ReversePips – バイアスが切り替わる前に二次極値が超える必要がある追加のpipベース距離。
  • StopLossPips – エントリー価格からのpipsでの保護ストップ距離(無効にするにはゼロに設定)。
  • TakeProfitPips – エントリー価格からのpipsでの利益目標距離(無効にするにはゼロに設定)。
  • TrailingStopPips – pipsで測定したトレーリングストップ有効化距離(トレーリングを無効にするにはゼロに設定)。
  • TrailingStepPips – トレーリング更新が発生する前に必要な追加pip距離;トレーリングがアクティブな場合は正である必要があります。
  • TradeVolume – 新規エントリーに使用する注文ボリューム(証券設定に応じてロット/コントラクト単位)。

注意事項

  • インジケーターの計算と反転チェックは完了したローソク足のみを使用します;不完全なローソク足は無視されます。
  • バインディングによって提供されるATR値は、ローソク足完了後に計算が行われるため、ソースEAで使用される前のバーのATRと等価です。
  • 調整されたポイント計算は、3桁と5桁のForex相場を自動的に処理し、pipベースのパラメーターを元のスクリプトと互換性を保ちます。
  • リクエストによりPythonポートは提供されていません。フォルダーは現在C#の実装とドキュメントのみを含んでいます。
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// NRTR reversal strategy using ATR channel around EMA.
/// Goes long when price breaks above EMA + ATR*multiplier.
/// Goes short when price breaks below EMA - ATR*multiplier.
/// </summary>
public class NrtrReversStrategy : Strategy
{
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _volatilityMultiplier;
	private readonly StrategyParam<int> _emaPeriod;

	private AverageTrueRange _atr;
	private ExponentialMovingAverage _ema;

	private decimal _entryPrice;
	private int _cooldown;

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

	/// <summary>
	/// ATR multiplier for band calculation.
	/// </summary>
	public decimal VolatilityMultiplier
	{
		get => _volatilityMultiplier.Value;
		set => _volatilityMultiplier.Value = value;
	}

	/// <summary>
	/// EMA period for trend center.
	/// </summary>
	public int EmaPeriod
	{
		get => _emaPeriod.Value;
		set => _emaPeriod.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public NrtrReversStrategy()
	{
		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR averaging period", "Indicator");

		_volatilityMultiplier = Param(nameof(VolatilityMultiplier), 5m)
			.SetGreaterThanZero()
			.SetDisplay("Multiplier", "ATR multiplier for bands", "Indicator");

		_emaPeriod = Param(nameof(EmaPeriod), 100)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA trend center period", "Indicator");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

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

		_atr = null;
		_ema = null;
		_entryPrice = 0;
		_cooldown = 0;
	}

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

		_atr = new AverageTrueRange { Length = AtrPeriod };
		_ema = new ExponentialMovingAverage { Length = EmaPeriod };

		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_atr, _ema, ProcessCandle);
		subscription.Start();
	}

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

		if (!_atr.IsFormed || !_ema.IsFormed)
			return;

		if (_cooldown > 0)
		{
			_cooldown--;
			return;
		}

		var close = candle.ClosePrice;
		var bandOffset = atrValue * VolatilityMultiplier;

		var upperBand = emaValue + bandOffset;
		var lowerBand = emaValue - bandOffset;

		// Buy: price breaks above upper band
		if (close > upperBand && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
			_entryPrice = close;
			_cooldown = 50;
		}
		// Sell: price breaks below lower band
		else if (close < lowerBand && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
			_entryPrice = close;
			_cooldown = 50;
		}
		// Exit long: price returns to EMA
		else if (Position > 0 && close < emaValue)
		{
			SellMarket();
			_entryPrice = 0;
			_cooldown = 50;
		}
		// Exit short: price returns to EMA
		else if (Position < 0 && close > emaValue)
		{
			BuyMarket();
			_entryPrice = 0;
			_cooldown = 50;
		}
	}
}