Multifiltro NTOqF
Descripción general
La estrategia NTOqF Multi-Filter traslada el asesor experto MetaTrader 4 "NTOqF" (versiones V1 a V3) al API de alto nivel de StockSharp. El robot original combina múltiples osciladores y filtros de seguimiento de tendencias, cada uno de los cuales se puede habilitar o deshabilitar de forma independiente. Esta versión de C# conserva la misma capacidad de configuración, admite períodos de tiempo separados para cada indicador y aplica la gestión comercial a través de paradas fijas, objetivos de obtención de ganancias y una parada móvil opcional expresada en pips.
Lógica estratégica
Filtros de indicador
- Filtro RSI: genera una señal larga cuando el valor RSI (en el turno configurado) está por debajo de
RSI Lower y una señal corta cuando el valor está por encima de RSI Upper. Las lecturas neutrales cancelan las entradas.
- Filtro Stochastic: compara %K y %D. Cuando
Use Stochastic High/Low está habilitado, la línea principal también debe estar por encima de Stoch High para largos o por debajo de Stoch Low para cortos; de lo contrario, se utilizan cruces %K/%D simples.
- Filtro ADX: utiliza +DI frente a –DI para determinar la dirección. Cuando la opción
Use ADX Main está habilitada, la línea principal ADX debe exceder ADX Main antes de que se acepte cualquier entrada.
- Parabolic SAR filtro: interpreta el valor SAR relativo al cierre de la barra seleccionada. Los valores por encima del precio favorecen las posiciones largas (reflejando el comportamiento en el código MQL), los valores por debajo favorecen las posiciones cortas.
- Filtro de media móvil: compara la media móvil seleccionada (con cambio positivo opcional) con el precio de cierre en el cambio base. El precio por encima del MA favorece las posiciones largas; El precio por debajo favorece los pantalones cortos.
Todos los filtros habilitados deben coincidir en la misma dirección. Si algún filtro devuelve un estado neutral (por ejemplo, RSI permanece entre sus umbrales), no se abre ninguna posición.
Reglas de entrada
- Las señales se evalúan en el marco temporal de negociación principal (
Candle Type).
- Sólo se permite una posición a la vez; la estrategia espera a que se cierre la posición anterior antes de entrar en una nueva.
- El volumen del pedido se toma de
Trade Volume (lotes).
reglas de salida
- Stop de pérdidas/toma de ganancias fijo: expresado en pips y convertido en compensaciones de precios utilizando el tamaño de paso del instrumento. Establezca un parámetro en
0 para desactivar el nivel correspondiente.
- Trailing stop: cuando está activado, el stop se sigue una vez que el beneficio no realizado supera la distancia de seguimiento y el stop actual se retrasa en más de esa distancia. Las posiciones largas mueven el stop hacia arriba, las posiciones cortas lo mueven hacia abajo.
Comportamiento en múltiples períodos de tiempo
Cada indicador puede suscribirse a su propio marco temporal. Un valor de período de tiempo de 0 reutiliza el período de tiempo de negociación principal, mientras que los valores positivos representan suscripciones de TimeFrameCandle basadas en minutos. Los valores del indicador se evalúan únicamente en velas completadas y respetan el parámetro Shift para que la estrategia pueda reflejar el comportamiento de "revisión" del experto original MetaTrader.
Parámetros
- Tipo de vela: plazo de negociación utilizado para impulsar las ejecuciones.
- Volumen – volumen de órdenes de mercado (lotes).
- Take Profit (pips) – objetivo de ganancias;
0 se desactiva.
- Stop Loss (pips) – parada de protección;
0 se desactiva.
- Usar Trailing / Trailing Stop (pips): habilita y dimensiona el trailing stop.
- Shift – número de velas completadas hacia atrás al leer los valores y el precio del indicador.
- RSI parámetros: alternancia, período, umbrales superior/inferior y período de tiempo.
- Stochastic parámetros: alternar, %K/%D/duraciones de desaceleración, niveles de confirmación alto/bajo opcionales y período de tiempo.
- ADX parámetros: alternancia, período, período de tiempo DI, umbral de línea principal opcional y período de tiempo principal.
- Parabolic SAR parámetros: alternar, paso de aceleración, aceleración máxima y período de tiempo.
- Parámetros de media móvil: alternancia, período, desplazamiento adicional aplicado al búfer MA, método de promediación (SMA/EMA/SMMA/LWMA), precio aplicado y período de tiempo.
Notas
- Las colas de indicadores respetan el
Shift configurado, lo que garantiza que las señales se basen en valores históricos de la misma manera que el experto MQL.
- La lógica de seguimiento solo se activa una vez que la operación ya tiene ganancias por más de la distancia de seguimiento y la parada está a más de esa distancia del precio, coincidiendo con el comportamiento del EA original.
- No se proporciona ninguna versión de Python para este paquete de estrategia.
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()