Ver en GitHub

Estrategia JFatl Candle MMRec

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

  1. Construir los valores OHLC filtrados y determinar el color de la vela en cada barra terminada.
  2. 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).
  3. 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 true y C0 != 2.
  4. Si C1 == 0 (bajista):
    • cerrar cualquier posición larga cuando EnableBuyExits es true;
    • abrir una posición corta cuando EnableSellEntries es true y C0 != 0.
  5. 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(); }
	}
}