NTOqF-Multifilter
Überblick
Die NTOqF Multi-Filter-Strategie portiert den MetaTrader 4 Expertenberater „NTOqF“ (Versionen V1–V3) auf den High-Level API von StockSharp. Der ursprüngliche Roboter kombiniert mehrere Oszillatoren und Trendfolgefilter, die jeweils unabhängig aktiviert oder deaktiviert werden können. Diese C#-Version behält die gleiche Konfigurierbarkeit bei, unterstützt separate Zeitrahmen für jeden Indikator und wendet Handelsmanagement durch feste Stopps, Take-Profit-Ziele und einen optionalen Trailing Stop, ausgedrückt in Pips, an.
Strategielogik
Indikatorfilter
- RSI-Filter – erzeugt ein langes Signal, wenn der Wert RSI (an der konfigurierten Verschiebung) unter
RSI Lower liegt, und ein kurzes Signal, wenn der Wert über RSI Upper liegt. Neutrale Messwerte streichen Einträge.
- Stochastic-Filter – vergleicht %K und %D. Wenn
Use Stochastic High/Low aktiviert ist, muss die Hauptlinie auch über Stoch High für Long-Positionen oder unter Stoch Low für Short-Positionen liegen; andernfalls werden einfache %K/%D-Kreuzungen verwendet.
- ADX-Filter – verwendet +DI gegenüber –DI, um die Richtung zu bestimmen. Wenn die Option
Use ADX Main aktiviert ist, muss die Hauptzeile ADX größer als ADX Main sein, bevor Einträge akzeptiert werden.
- Parabolic SAR-Filter – interpretiert den SAR-Wert relativ zum Schlusskurs des ausgewählten Balkens. Werte über dem Preis begünstigen Long-Positionen (was das Verhalten im MQL-Code widerspiegelt), Werte unter dem Preis begünstigen Short-Positionen.
- Filter für gleitenden Durchschnitt – vergleicht den ausgewählten gleitenden Durchschnitt (mit optionaler positiver Verschiebung) mit dem Schlusskurs bei der Basisverschiebung. Ein Preis über dem MA begünstigt Long-Positionen; Der Preis unten begünstigt Shorts.
Alle aktivierten Filter müssen sich auf die gleiche Richtung einigen. Wenn ein Filter einen neutralen Zustand zurückgibt (z. B. RSI bleibt zwischen seinen Schwellenwerten), wird keine Position geöffnet.
Einreisebestimmungen
- Signale werden im primären Handelszeitraum (
Candle Type) ausgewertet.
- Es ist jeweils nur eine Position zulässig; Die Strategie wartet darauf, dass die vorherige Position geschlossen wird, bevor sie eine neue eingibt.
- Das Bestellvolumen wird von
Trade Volume (Lots) übernommen.
Ausgangsregeln
- Fester Stop-Loss / Take-Profit – ausgedrückt in Pips und unter Verwendung der Schrittgröße des Instruments in Preis-Offsets umgewandelt. Setzen Sie einen Parameter auf
0, um die entsprechende Ebene zu deaktivieren.
- Trailing Stop – wenn aktiviert, wird der Stop nachgezogen, sobald der nicht realisierte Gewinn die Trailing-Distanz überschreitet und der aktuelle Stop dem Preis um mehr als diese Distanz hinterherhinkt. Long-Positionen bewegen den Stop nach oben, Short-Positionen nach unten.
Verhalten in mehreren Zeitrahmen
Jeder Indikator kann seinen eigenen Zeitrahmen abonnieren. Ein Zeitrahmenwert von 0 verwendet den primären Handelszeitrahmen wieder, während positive Werte minutenbasierte TimeFrameCandle-Abonnements darstellen. Indikatorwerte werden nur für abgeschlossene Kerzen ausgewertet und respektieren den Parameter Shift, sodass die Strategie das „Rückblick“-Verhalten des ursprünglichen MetaTrader-Experten widerspiegeln kann.
Parameter
- Kerzentyp – Handelszeitrahmen, der zur Steuerung der Ausführungen verwendet wird.
- Volumen – Market-Order-Volumen (Lots).
- Take Profit (Pips) – Gewinnziel;
0 deaktiviert.
- Stop-Loss (Pips) – Schutzstopp;
0 deaktiviert.
- Trailing verwenden / Trailing Stop (Pips) – Trailing Stop aktivieren und dimensionieren.
- Verschiebung – Anzahl der abgeschlossenen Kerzen nach hinten beim Lesen der Indikatorwerte und des Preises.
- RSI Parameter – Umschalten, Zeitraum, obere/untere Schwellenwerte und Zeitrahmen.
- Stochastic-Parameter – Umschalten, %K/%D/Verlangsamungslängen, optionale hohe/niedrige Bestätigungsstufen und Zeitrahmen.
- ADX-Parameter – Umschalten, Zeitraum, DI-Zeitrahmen, optionaler Hauptlinienschwellenwert und Hauptzeitrahmen.
- Parabolic SAR Parameter – Umschalten, Beschleunigungsschritt, maximale Beschleunigung und Zeitrahmen.
- Parameter für den gleitenden Durchschnitt – Umschalten, Zeitraum, zusätzliche auf den MA-Puffer angewendete Verschiebung, Mittelungsmethode (SMA/EMA/SMMA/LWMA), angewendeter Preis und Zeitrahmen.
Notizen
- Indikatorwarteschlangen berücksichtigen den konfigurierten
Shift und stellen sicher, dass Signale auf die gleiche Weise wie der MQL-Experte auf historischen Werten basieren.
- Die Trailing-Logik wird erst aktiviert, wenn der Trade bereits mehr als die Trailing-Distanz im Gewinn ist und der Stop mehr als diese Distanz vom Preis entfernt ist, was dem ursprünglichen Verhalten von EA entspricht.
- Für dieses Strategiepaket wird keine Python-Version bereitgestellt.
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import RelativeStrengthIndex, ExponentialMovingAverage, AverageTrueRange
class nto_qf_strategy(Strategy):
def __init__(self):
super(nto_qf_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "EMA trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period for trailing.", "Indicators")
self._rsi_upper = self.Param("RsiUpper", 70.0) \
.SetDisplay("RSI Upper", "Overbought level.", "Signals")
self._rsi_lower = self.Param("RsiLower", 30.0) \
.SetDisplay("RSI Lower", "Oversold level.", "Signals")
self._prev_rsi = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
@property
def RsiUpper(self):
return self._rsi_upper.Value
@property
def RsiLower(self):
return self._rsi_lower.Value
def OnStarted2(self, time):
super(nto_qf_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._rsi, self._ema, self._atr, self.ProcessCandle).Start()
self.StartProtection(
takeProfit=Unit(3, UnitTypes.Percent),
stopLoss=Unit(2, UnitTypes.Percent)
)
def ProcessCandle(self, candle, rsi_val, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_val)
ev = float(ema_val)
av = float(atr_val)
if self._prev_rsi == 0 or av <= 0:
self._prev_rsi = rv
return
close = float(candle.ClosePrice)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rv
return
# Entry: RSI exits extreme zone, confirmed by EMA trend
if self.Position == 0:
if self._prev_rsi < float(self.RsiLower) and rv >= float(self.RsiLower) and close > ev:
self.BuyMarket()
elif self._prev_rsi > float(self.RsiUpper) and rv <= float(self.RsiUpper) and close < ev:
self.SellMarket()
self._prev_rsi = rv
def OnReseted(self):
super(nto_qf_strategy, self).OnReseted()
self._prev_rsi = 0.0
def CreateClone(self):
return nto_qf_strategy()