La Estrategia Flat Trend reproduce las ideas centrales del asesor experto Flat Trend original al combinar filtros de tendencias de varias velocidades, confirmación ADX y un filtro de ruptura de "jugo" de desviación estándar. La estrategia se centra en detectar el momento en que el precio abandona una fase de rango y el impulso se expande, permitiéndole unir movimientos direccionales con protección de posición dinámica.
Lógica de trading
Filtros de tendencia: tres promedios móviles exponenciales (EMA) con longitudes configurables representan el activador, el primer filtro y el segundo filtro. Su pendiente y la posición del precio relativa a cada EMA se traducen a estados:
Fuerte alcista (precio por encima de EMA y EMA subiendo).
Alcista moderado (precio por encima de EMA pero pendiente neutral).
Fuerte bajista (precio por debajo de EMA y EMA cayendo).
Bajista moderado (precio por debajo de EMA pero pendiente neutral).
Reglas de entrada
Las operaciones largas requieren estados alcistas en el disparador y filtro EMA. Opcionalmente, el segundo filtro se puede ignorar. El modo estricto obliga a utilizar únicamente estados alcistas fuertes.
Las operaciones cortas reflejan las condiciones de los estados bajistas.
La confirmación opcional ADX garantiza que el índice direccional promedio supere un umbral y, cuando está habilitado, los componentes +DI y –DI concuerdan con la dirección comercial.
El filtro "jugo" verifica que la desviación estándar de los precios esté por encima de un nivel de ruptura definido por el usuario, evitando operaciones durante las fases de volatilidad plana.
La negociación se puede restringir a una ventana intradiaria seleccionada.
Reglas de salida
Los estados de tendencia opuestos en el activador EMA inician una salida. En modo estricto, la estrategia espera la contraseñal más fuerte.
Las paradas dinámicas salen de posiciones cada vez que el precio toca el nivel de parada calculado.
Gestión del riesgo
Parada inicial: calculada a partir de una distancia de pip estática o del valor del rango verdadero promedio (ATR), emulando la lógica basada en ADR del EA original.
Trailing stop: movimientos con el precio más alto (o más bajo) desde la entrada utilizando el ATR multiplicado por un divisor.
Equipo: una vez que el precio avanza la distancia configurada, el stop se mueve más allá del precio de entrada en un pequeño valor de bloqueo.
Parámetros
Nombre
Descripción
TriggerLength
EMA longitud para el filtro de activación.
FilterLength1
EMA longitud para el primer filtro de confirmación.
FilterLength2
EMA longitud para el segundo filtro de confirmación.
UseOnlyPrimaryIndicators
Utilice únicamente el activador y el primer filtro para las entradas.
IgnoreModerateForEntry
Requerir estados de tendencia fuertes para nuevas operaciones.
IgnoreModerateForExit
Requiere fuertes contraseñales para cerrar operaciones.
UseTradingHours
Habilite la ventana de negociación intradía.
TradingHourBegin / TradingHourEnd
Hora de inicio y finalización de la ventana de negociación.
UseJuiceFilter, JuicePeriod, JuiceThreshold
Parámetros del filtro de ruptura de desviación estándar.
multiplicador ATR para el cálculo del trailing stop.
BreakEvenPips, BreakEvenLockPips
Activación del punto de equilibrio y distancia de bloqueo.
AtrPeriod
ATR retrospectiva utilizada para la estimación de la volatilidad.
CandleType
Periodo de tiempo de la vela principal.
Resumen de indicadores
Promedio móvil exponencial (EMA): tres instancias para evaluación de tendencias de múltiples velocidades.
Desviación estándar: modela el filtro de ruptura de volatilidad "jugo".
Rango verdadero promedio (ATR): mide la volatilidad de las paradas y el seguimiento.
Índice direccional promedio (ADX): confirma la fuerza y dirección de la tendencia.
Notas de uso
Asegúrese de que la seguridad de la estrategia tenga un PriceStep definido; de lo contrario, se utiliza el paso predeterminado de 0,0001 para distancias basadas en pips.
La estrategia utiliza órdenes de mercado (BuyMarket, SellMarket) y escala automáticamente el volumen al revertir posiciones.
Las paradas dinámicas se simulan internamente cerrando posiciones cuando se toca el nivel de parada virtual.
Combine la ventana de negociación y opciones de entrada estrictas para centrarse en sesiones de alta liquidez y evitar períodos agitados.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Flat Trend strategy - breakout from low volatility using ATR and EMA.
/// Buys when ATR expands and price is above EMA.
/// Sells when ATR expands and price is below EMA.
/// </summary>
public class FlatTrendStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevAtr;
private decimal _prevClose;
private decimal _prevEma;
private bool _hasPrev;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FlatTrendStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20)
.SetDisplay("EMA Period", "EMA trend filter", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "ATR volatility period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevAtr = 0m; _prevClose = 0m; _prevEma = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, atr, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (!_hasPrev)
{
_prevAtr = atr;
_prevClose = close;
_prevEma = ema;
_hasPrev = true;
return;
}
// Volatility expansion: ATR increasing
var atrExpanding = atr > _prevAtr;
// Breakout above EMA with expanding volatility
if (atrExpanding && _prevClose <= _prevEma && close > ema && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Breakout below EMA with expanding volatility
else if (atrExpanding && _prevClose >= _prevEma && close < ema && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevAtr = atr;
_prevClose = close;
_prevEma = ema;
}
}
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
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class flat_trend_strategy(Strategy):
def __init__(self):
super(flat_trend_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20) \
.SetDisplay("EMA Period", "EMA trend filter", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR volatility period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_atr = 0.0
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
@property
def ema_period(self):
return self._ema_period.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(flat_trend_strategy, self).OnReseted()
self._prev_atr = 0.0
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(flat_trend_strategy, self).OnStarted2(time)
self._has_prev = False
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
atr = AverageTrueRange()
atr.Length = self.atr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, atr, self.process_candle).Start()
def process_candle(self, candle, ema, atr):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema)
atr_val = float(atr)
if not self._has_prev:
self._prev_atr = atr_val
self._prev_close = close
self._prev_ema = ema_val
self._has_prev = True
return
atr_expanding = atr_val > self._prev_atr
if atr_expanding and self._prev_close <= self._prev_ema and close > ema_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif atr_expanding and self._prev_close >= self._prev_ema and close < ema_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_atr = atr_val
self._prev_close = close
self._prev_ema = ema_val
def CreateClone(self):
return flat_trend_strategy()