Esta estrategia recrea el comportamiento del Expert Advisor original Exp_JFatlCandle_MMRec.mq5 dentro del framework StockSharp.
Analiza los cambios de color producidos por el filtro de velas JFatl y los combina con un bloque adaptativo de gestión de dinero
que reduce el tamaño de la posición tras un número configurable de pérdidas recientes.
Idea de trading
Construye velas sintéticas filtrando los valores OHLC clásicos con el kernel de la Fast Adaptive Trend Line (FATL).
La implementación usa la tabla de coeficientes original de 39 taps seguida de una etapa de suavizado exponencial para
aproximar la media móvil Jurik utilizada en MetaTrader.
Detecta transiciones de color del cuerpo de la vela sintética:
color 2 (alcista) significa que el cierre filtrado está por encima de la apertura filtrada;
color 0 (bajista) significa que el cierre filtrado está por debajo de la apertura filtrada;
color 1 marca un cuerpo neutral.
Un color alcista en la barra con SignalBar + 1 períodos de antigüedad obliga a la estrategia a cerrar cualquier corto y prepararse
para una nueva entrada larga cuando la barra con SignalBar períodos de antigüedad ya no es alcista.
Un color bajista observado de la misma manera cierra los largos y habilita una entrada corta cuando la barra más reciente ya no es bajista.
Las posiciones largas y cortas se dimensionan mediante la lógica de MMRecounter. Cuando las últimas TotalTrigger operaciones de la
dirección correspondiente incluyen al menos LossTrigger resultados negativos, la estrategia cambia al tamaño de posición reducido.
Parámetros
Parámetro
Descripción
CandleType
Marco temporal de las velas que se alimentan al filtro FATL (por defecto: 12 horas).
SignalBar
Número de barras completadas para mirar hacia atrás al leer el buffer de colores. 0 significa usar la barra terminada actual, 1 reproduce los valores predeterminados de MT5.
SmoothingLength
Longitud del suavizado exponencial aplicado después del kernel FATL para emular el suavizado Jurik.
NormalVolume
Tamaño de posición predeterminado cuando el historial reciente es saludable.
ReducedVolume
Tamaño de posición aplicado después de que el MMRecounter detecta demasiadas pérdidas.
BuyTotalTrigger / SellTotalTrigger
Cantidad de operaciones históricas (por dirección) inspeccionadas por el MMRecounter.
BuyLossTrigger / SellLossTrigger
Número mínimo de pérdidas dentro de la ventana inspeccionada que fuerza el tamaño de posición reducido.
EnableBuyEntries / EnableSellEntries
Permitir la apertura de posiciones largas/cortas.
EnableBuyExits / EnableSellExits
Permitir el cierre de posiciones largas/cortas cuando aparece la señal opuesta.
StopLossPoints
Stop de protección opcional para ambas direcciones expresado en pasos de precio del instrumento. Establecer en 0 para desactivar.
TakeProfitPoints
Objetivo de beneficio opcional en pasos de precio. Establecer en 0 para desactivar.
Reglas de trading
Construir los valores OHLC filtrados y determinar el color de la vela en cada barra terminada.
Sea C1 el color de la barra de SignalBar + 1 períodos atrás y C0 el color de la barra de SignalBar períodos atrás
(para SignalBar = 0 la barra actual se usa como C0 y la anterior como C1).
Si C1 == 2 (alcista):
cerrar cualquier posición corta cuando EnableSellExits es true;
abrir una posición larga con el tamaño calculado cuando EnableBuyEntries es trueyC0 != 2.
Si C1 == 0 (bajista):
cerrar cualquier posición larga cuando EnableBuyExits es true;
abrir una posición corta cuando EnableSellEntries es trueyC0 != 0.
Las posiciones también pueden cerrarse por límites de stop-loss o take-profit cuando el rango de la vela toca el nivel configurado.
Gestión de dinero
La estrategia almacena el beneficio de cada operación larga y corta completada por separado. Cuando se considera una nueva entrada, escanea
hasta TotalTrigger operaciones anteriores de esa dirección. Si al menos LossTrigger operaciones dentro de esa ventana terminaron con un resultado
negativo, se usa el volumen reducido; en caso contrario, se opera el volumen normal.
Notas
La lógica de stop-loss y take-profit basada en pasos de precio depende del valor Security.PriceStep. Si el instrumento no lo proporciona,
se asume un paso de 1.
El filtro FATL necesita al menos 39 velas históricas antes de ser operativo. No se generan operaciones hasta que se acumulen suficientes datos.
La estrategia mantiene un historial compacto de operaciones para el bloque MMRecounter; una vez que el historial supera los 100 elementos, los registros más antiguos
se descartan automáticamente.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class JfatlCandleMmRecStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast, _prevSlow;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public JfatlCandleMmRecStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 7).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 21).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null; _prevSlow = null;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevFast = fast; _prevSlow = slow; return; }
if (_prevFast == null || _prevSlow == null) { _prevFast = fast; _prevSlow = slow; return; }
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast; _prevSlow = slow;
if (!prevAbove && currAbove && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (prevAbove && !currAbove && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
}
}
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
from StockSharp.Algo.Strategies import Strategy
class jfatl_candle_mm_rec_strategy(Strategy):
def __init__(self):
super(jfatl_candle_mm_rec_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 7) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 21) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(jfatl_candle_mm_rec_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(jfatl_candle_mm_rec_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_above and not curr_above and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return jfatl_candle_mm_rec_strategy()