GitHub で見る

NTOqFマルチフィルター

概要

NTOqF マルチフィルター戦略は、MetaTrader 4 エキスパート アドバイザ「NTOqF」(バージョン V1 ~ V3)を StockSharp の高レベル API に移植します。オリジナルのロボットは複数のオシレーターとトレンドフォローフィルターを組み合わせており、それぞれを個別に有効または無効にすることができます。この C# バージョンは、同じ構成機能を維持し、すべてのインジケーターに対して個別のタイムフレームをサポートし、固定ストップ、テイクプロフィット ターゲット、およびピップで表現されるオプションのトレーリング ストップを通じて取引管理を適用します。

戦略ロジック

インジケーターフィルター

  • RSI フィルター – RSI 値 (構成されたシフトで) が RSI Lower を下回る場合に長い信号を生成し、値が RSI Upper を上回る場合に短い信号を生成します。中立の読み取り値はエントリをキャンセルします。
  • Stochastic フィルター – %K と %D を比較します。 Use Stochastic High/Low が有効な場合、メインラインはロングの場合は Stoch High より上、ショートの場合は Stoch Low より下にある必要があります。それ以外の場合は、単純な %K/%D クロスオーバーが使用されます。
  • ADX フィルター – +DI と -DI を使用して方向を決定します。 Use ADX Main オプションが有効な場合、エントリが受け入れられる前に、ADX のメインラインが ADX Main を超える必要があります。
  • Parabolic SAR フィルター – 選択したバーの終値を基準にして SAR 値を解釈します。価格を超える値はロングを優先し(MQL コードの動作を反映)、以下の値はショートを優先します。
  • 移動平均フィルター – 選択した移動平均 (オプションの正のシフトあり) を基準シフトの終値と比較します。 MAを上回る価格はロングを有利にします。以下の価格ではショートパンツが有利です。

すべての有効なフィルターは同じ方向に一致する必要があります。いずれかのフィルターが中立状態を返した場合 (たとえば、RSI がしきい値の間に残っている場合)、ポジションはオープンされません。

エントリールール

  • シグナルは主要な取引時間枠 (Candle Type) で評価されます。
  • 一度に許可されるポジションは 1 つだけです。この戦略は、新しいポジションに入る前に、前のポジションが閉じるのを待ちます。
  • 注文量は Trade Volume (ロット) から取得されます。

終了ルール

  • 固定ストップロス/テイクプロフィット – ピップ単位で表され、商品のステップサイズを使用して価格オフセットに変換されます。対応するレベルを無効にするには、パラメータを 0 に設定します。
  • トレーリングストップ – 有効にすると、含み益がトレーリング距離を超え、現在のストップが価格よりその距離以上遅れた場合にストップがトレーリングされます。ロングポジションではストップが上に移動し、ショートポジションではストップが下に移動します。

マルチタイムフレームの動作

各インジケーターは独自の時間枠をサブスクライブできます。タイムフレーム値 0 はプライマリ取引タイムフレームを再利用し、正の値は分ベースの TimeFrameCandle サブスクリプションを表します。インジケーター値は完了したローソク足でのみ評価され、Shift パラメーターを尊重するため、戦略は元の MetaTrader エキスパートからの「ルックバック」動作を反映できます。

パラメーター

  • キャンドルタイプ – 実行を促進するために使用される取引時間枠。
  • 出来高 – 成行注文の量(ロット)。
  • 利益確定 (pips) – 利益目標。 0 は無効になります。
  • ストップロス (pips) – 保護ストップ。 0 は無効になります。
  • トレーリングを使用 / トレーリング ストップ (pips) – トレーリング ストップを有効にしてサイズ設定します。
  • シフト – インジケーターの値と価格を読み取るときに遡って完了したローソク足の数。
  • RSI パラメータ – トグル、期間、上限/下限しきい値、および時間枠。
  • Stochastic パラメータ – トグル、%K/%D/遅延の長さ、オプションの高/低確認レベル、および時間枠。
  • ADX パラメータ – トグル、期間、DI タイムフレーム、オプションのメインラインしきい値、およびメイン タイムフレーム。
  • Parabolic SAR パラメータ – トグル、加速ステップ、最大加速、およびタイムフレーム。
  • 移動平均パラメータ – トグル、期間、MA バッファに適用される追加シフト、平均化方法 (SMA/EMA/SMMA/LWMA)、適用される価格、および時間枠。

注意事項

  • インジケーター キューは設定された Shift を尊重し、MQL エキスパートと同じ方法でシグナルが履歴値に基づいていることを保証します。
  • トレーリング ロジックは、トレーリング距離を超えて取引がすでに利益を上げており、ストップが価格からその距離以上離れている場合にのみアクティブになり、元の EA の動作と一致します。
  • この戦略パッケージには Python バージョンは提供されていません。
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>
/// NtoQf: RSI + EMA multi-filter strategy with ATR trailing.
/// Combines RSI overbought/oversold with EMA trend confirmation.
/// </summary>
public class NtoQfStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _rsiUpper;
	private readonly StrategyParam<decimal> _rsiLower;

	private decimal _prevRsi;

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

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");

		_emaLength = Param(nameof(EmaLength), 50)
			.SetDisplay("EMA Length", "EMA trend filter.", "Indicators");

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

		_rsiUpper = Param(nameof(RsiUpper), 70m)
			.SetDisplay("RSI Upper", "Overbought level.", "Signals");

		_rsiLower = Param(nameof(RsiLower), 30m)
			.SetDisplay("RSI Lower", "Oversold level.", "Signals");
	}

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

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

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

	public decimal RsiUpper
	{
		get => _rsiUpper.Value;
		set => _rsiUpper.Value = value;
	}

	public decimal RsiLower
	{
		get => _rsiLower.Value;
		set => _rsiLower.Value = value;
	}

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

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

		_prevRsi = 0;
	}

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

		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

		StartProtection(
			takeProfit: new Unit(3, UnitTypes.Percent),
			stopLoss: new Unit(2, UnitTypes.Percent));

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

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

		if (_prevRsi == 0 || atrVal <= 0)
		{
			_prevRsi = rsiVal;
			return;
		}

		var close = candle.ClosePrice;

		// Entry: RSI exits extreme zone, confirmed by EMA trend
		if (Position == 0)
		{
			if (_prevRsi < RsiLower && rsiVal >= RsiLower && close > emaVal)
			{
				BuyMarket();
			}
			else if (_prevRsi > RsiUpper && rsiVal <= RsiUpper && close < emaVal)
			{
				SellMarket();
			}
		}

		_prevRsi = rsiVal;
	}
}