Esta estrategia es un port de alto nivel de StockSharp del experto de MetaTrader 5 Exp_ColorJFatl_Digit_NN3_MMRec. El robot original usaba un indicador ColorJFatl_Digit personalizado junto con reglas de recuperación de gestión monetaria. La versión de StockSharp se enfoca en el motor de señales central y lo expresa a través de tres módulos independientes que trabajan en diferentes marcos temporales.
Cada módulo aplica una Media Móvil Jurik (JMA) a la fuente de precio seleccionada y monitorea la pendiente de ese promedio. Cuando la pendiente se vuelve positiva, el módulo lo trata como un régimen alcista, cierra la exposición corta y opcionalmente abre una nueva posición larga. Cuando la pendiente se vuelve negativa, el módulo realiza la lógica espejo para operaciones cortas. Todos los módulos comparten el mismo portafolio y por lo tanto siempre trabajan con la posición neta de la estrategia.
Lógica de trading
Suscribirse a velas en tres marcos temporales (por defecto: 1 día, 8 horas, 3 horas).
Para cada vela completada:
Convertir la vela al precio aplicado configurado (cierre, apertura, precio típico, precio DeMark, etc.).
Procesar el valor a través de una Media Móvil Jurik para obtener una serie suavizada.
Comparar el valor JMA actual con el anterior para determinar la dirección de la pendiente. Una pendiente positiva produce un estado "arriba", una pendiente negativa produce un estado "abajo", una pendiente plana mantiene el estado anterior.
Almacenar en buffer los estados según el retraso SignalBar para que la estrategia pueda actuar en barras históricas si se desea (el experto original admitía señales retrasadas).
Cada vez que un módulo detecta una transición:
Transición hacia arriba: opcionalmente cerrar cualquier posición corta y abrir una posición larga con el volumen del módulo.
Transición hacia abajo: opcionalmente cerrar cualquier posición larga y abrir una posición corta con el volumen del módulo.
Las señales opuestas de otro módulo pueden liquidar o revertir la posición dependiendo de las banderas de habilitación.
Los stops y beneficios no están codificados; en cambio, la estrategia se basa en señales opuestas y las protecciones integradas de StockSharp (StartProtection()) para la seguridad.
Parámetros
Los parámetros se agrupan por módulo (A, B, C) para reflejar la plantilla MT5. Cada grupo expone los siguientes valores:
CandleType – marco temporal para las velas entrantes.
JmaLength – período de la Media Móvil Jurik.
JmaPhase – almacenado para documentación; el JMA de StockSharp no expone ajuste de fase.
SignalBar – número de barras completadas a esperar antes de actuar en una señal (0 = inmediato).
AppliedPrices – transformación de precio usada como entrada para JMA (cierre, apertura, mediana, típico, ponderado, simple, cuarto, seguimiento de tendencia, DeMark).
AllowBuyOpen / AllowSellOpen – permiso para abrir posiciones en la dirección correspondiente.
AllowBuyClose / AllowSellClose – permiso para cerrar posiciones existentes cuando el módulo emite una señal opuesta.
Volume – tamaño de la orden que el módulo usa al abrir una nueva operación.
Como los módulos comparten una única posición de cuenta, solo puede existir una posición neta larga o neta corta a la vez. Si un módulo intenta abrir una operación mientras el portafolio ya tiene exposición en la misma dirección, la orden se omite; si hay una dirección opuesta abierta, se cierra antes de colocar la nueva operación (sujeto a las banderas de permiso).
Notas de uso
La estrategia suscribe todos los marcos temporales configurados a través de GetWorkingSecurities(), asegurando que el entorno de simulación o en vivo cargue las series de velas requeridas.
Las señales operan estrictamente en velas completadas para prevenir el redibujado intra-barra.
El enum AppliedPrices reproduce las opciones del indicador original, incluyendo dos variantes de precio de seguimiento de tendencia y el precio DeMark.
La lógica de recuperación de gestión monetaria de la versión MQL no está reproducida. En cambio, el riesgo se puede gestionar mediante las protecciones de StockSharp o ajustando los volúmenes de los módulos.
Los comentarios en inglés dentro del código explican cada paso de la conversión para facilitar el mantenimiento y el futuro port a Python.
Extendiendo la estrategia
Para agregar reglas de stop-loss o take-profit, reemplazar la llamada predeterminada StartProtection() con la configuración deseada.
Se pueden crear módulos adicionales clonando el patrón de configuración SignalModule.
Para la gestión avanzada de posiciones (por ejemplo, rastrear la exposición por módulo), se pueden agregar estrategias hijas o portafolios virtuales de StockSharp sobre esta base.
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>
/// Multi-timeframe JMA slope strategy.
/// Three modules monitor different timeframes and react to slope changes of a Jurik MA.
/// </summary>
public class ColorJFatlDigitNn3MmRecStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _jmaLength;
private JurikMovingAverage _jma;
private decimal? _prevJma;
private int _prevSignal; // -1 down, 0 neutral, 1 up
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int JmaLength
{
get => _jmaLength.Value;
set => _jmaLength.Value = value;
}
public ColorJFatlDigitNn3MmRecStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_jmaLength = Param(nameof(JmaLength), 5)
.SetGreaterThanZero()
.SetDisplay("JMA Length", "Jurik MA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_jma = null;
_prevJma = null;
_prevSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_jma = new JurikMovingAverage { Length = JmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_jma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _jma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal jmaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevJma = jmaValue;
return;
}
if (_prevJma == null)
{
_prevJma = jmaValue;
return;
}
var diff = jmaValue - _prevJma.Value;
var signal = diff > 0 ? 1 : diff < 0 ? -1 : _prevSignal;
_prevJma = jmaValue;
if (signal == _prevSignal)
return;
var oldSignal = _prevSignal;
_prevSignal = signal;
if (signal == 1 && oldSignal == -1)
{
// Slope turned up -- close short, open long
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (signal == -1 && oldSignal == 1)
{
// Slope turned down -- close long, open short
if (Position > 0)
SellMarket();
if (Position >= 0)
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 JurikMovingAverage
from StockSharp.Algo.Strategies import Strategy
class color_j_fatl_digit_nn3_mm_rec_strategy(Strategy):
def __init__(self):
super(color_j_fatl_digit_nn3_mm_rec_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._jma_length = self.Param("JmaLength", 5) \
.SetDisplay("JMA Length", "Jurik MA period", "Indicators")
self._prev_jma = None
self._prev_signal = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def JmaLength(self):
return self._jma_length.Value
def OnReseted(self):
super(color_j_fatl_digit_nn3_mm_rec_strategy, self).OnReseted()
self._prev_jma = None
self._prev_signal = 0
def OnStarted2(self, time):
super(color_j_fatl_digit_nn3_mm_rec_strategy, self).OnStarted2(time)
self._prev_jma = None
self._prev_signal = 0
jma = JurikMovingAverage()
jma.Length = self.JmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(jma, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, jma)
self.DrawOwnTrades(area)
def _on_process(self, candle, jma_value):
if candle.State != CandleStates.Finished:
return
jv = float(jma_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_jma = jv
return
if self._prev_jma is None:
self._prev_jma = jv
return
diff = jv - self._prev_jma
if diff > 0:
signal = 1
elif diff < 0:
signal = -1
else:
signal = self._prev_signal
self._prev_jma = jv
if signal == self._prev_signal:
return
old_signal = self._prev_signal
self._prev_signal = signal
if signal == 1 and old_signal == -1:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif signal == -1 and old_signal == 1:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return color_j_fatl_digit_nn3_mm_rec_strategy()