This strategy is a StockSharp high-level port of the MetaTrader 5 expert Exp_ColorJFatl_Digit_NN3_MMRec. The original robot used a custom ColorJFatl_Digit indicator together with money-management recovery rules. The StockSharp version focuses on the core signal engine and expresses it through three independent modules working on different timeframes.
Each module applies a Jurik Moving Average (JMA) to the selected price source and monitors the slope of that average. When the slope turns positive the module treats it as a bullish regime, closes short exposure and optionally opens a new long position. When the slope turns negative the module performs the mirror logic for short trades. All modules share the same portfolio and therefore always work with the net position of the strategy.
Trading Logic
Subscribe to candles on three timeframes (defaults: 1 day, 8 hours, 3 hours).
For every finished candle:
Convert the candle to the configured applied price (close, open, typical price, DeMark price, etc.).
Process the value through a Jurik Moving Average to obtain a smoothed series.
Compare the current JMA value with the previous one to determine the slope direction. A positive slope yields an "up" state, a negative slope yields a "down" state, a flat slope keeps the previous state.
Buffer the states according to the SignalBar delay so that the strategy can act on historical bars if desired (the original expert supported delayed signals).
Whenever a module detects a transition:
Up transition: optionally close any short position and open a long position with the module volume.
Down transition: optionally close any long position and open a short position with the module volume.
Opposite signals from another module can flatten or reverse the position depending on the enable flags.
Stops and profits are not hard-coded; instead, the strategy relies on opposite signals and the built-in StockSharp protections (StartProtection()) for safety.
Parameters
The parameters are grouped per module (A, B, C) to mirror the MT5 template. Each group exposes the following values:
CandleType – timeframe for incoming candles.
JmaLength – period of the Jurik Moving Average.
JmaPhase – stored for documentation; StockSharp's JMA does not expose phase adjustment.
SignalBar – number of finished bars to wait before acting on a signal (0 = immediate).
AppliedPrices – price transformation used as input for JMA (close, open, median, typical, weighted, simple, quarter, trend-follow, DeMark).
AllowBuyOpen / AllowSellOpen – permission to open positions in the corresponding direction.
AllowBuyClose / AllowSellClose – permission to close existing positions when the module issues an opposite signal.
Volume – order size the module uses when opening a new trade.
Because modules share a single account position, only one net long or net short position can exist at a time. If a module attempts to open a trade while the portfolio already carries exposure in the same direction, the order is skipped; if an opposite direction is open, it is closed before the new trade is placed (subject to the permission flags).
Usage Notes
The strategy subscribes to all configured timeframes through GetWorkingSecurities(), ensuring the simulation or live environment loads the required candle series.
Signals operate strictly on finished candles to prevent intrabar repainting.
The AppliedPrices enum reproduces the options from the original indicator, including two trend-follow price variants and the DeMark price.
Money-management recovery logic from the MQL version is not reproduced. Instead, risk can be managed via StockSharp protections or by adjusting module volumes.
English comments inside the code explain each step of the conversion for easier maintenance and future Python porting.
Extending the Strategy
To add stop loss or take profit rules, replace the default StartProtection() call with the desired configuration.
Additional modules can be created by cloning the SignalModule configuration pattern.
For advanced position management (e.g., tracking per-module exposure), StockSharp child strategies or virtual portfolios can be added on top of this foundation.
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()