Данная стратегия — адаптация советника MetaTrader 5 Exp_ColorJFatl_Digit_NN3_MMRec под высокоуровневый API StockSharp. В оригинале использовался пользовательский индикатор ColorJFatl_Digit и сложные алгоритмы манименеджмента. Версия на StockSharp переносит логику сигналов и организует её в три независимых модуля, работающих на разных таймфреймах.
Каждый модуль применяет скользящую Jurik (JMA) к выбранной цене свечи и отслеживает знак наклона индикатора. Положительный наклон воспринимается как бычий режим: модуль закрывает шорт и, при разрешении, открывает новую длинную позицию. Отрицательный наклон приводит к зеркальным действиям для коротких сделок. Все модули используют общий торговый счёт стратегии, поэтому всегда работает суммарная позиция.
Логика торговли
Подписаться на свечи трёх таймфреймов (по умолчанию: 1 день, 8 часов, 3 часа).
Для каждой завершённой свечи:
Преобразовать цену свечи в выбранный тип (Close, Open, Typical, DeMark и т. д.).
Пропустить значение через Jurik MA для сглаживания.
Сравнить текущее значение JMA с предыдущим и определить направление наклона. Положительный наклон даёт состояние «up», отрицательный — «down», при нулевом наклоне состояние сохраняется.
Сохранить последовательность состояний с учётом задержки SignalBar, чтобы можно было реагировать на сигналы прошлых баров (как в оригинале).
При смене состояния:
Переход вверх — по необходимости закрыть шорт и открыть лонг указанным объёмом.
Переход вниз — закрыть лонг и открыть шорт.
Сигналы других модулей могут перевернуть или закрыть позицию в зависимости от разрешающих флагов.
Жёсткие стопы и тейки не задаются; вместо этого стратегия полагается на противоположные сигналы и защиту StartProtection().
Параметры
Параметры сгруппированы по модулям (A, B, C), что повторяет MT5-шаблон. В каждой группе доступны:
CandleType — таймфрейм свечей.
JmaLength — период Jurik MA.
JmaPhase — хранится для документации; JMA в StockSharp не поддерживает фазу.
SignalBar — число закрытых баров, через которое исполняется сигнал (0 — без задержки).
AllowBuyOpen / AllowSellOpen — разрешение открывать сделки соответствующего направления.
AllowBuyClose / AllowSellClose — разрешение закрывать позицию при обратном сигнале.
Volume — объём ордера при открытии новой сделки.
Так как все модули работают с общей позицией, одновременно может существовать только один чистый лонг или шорт. Если модуль пытается открыть позицию в ту же сторону, где уже есть экспозиция, операция пропускается; при противоположной позиции она сначала закрывается (при включённых флагах), затем открывается новая.
Рекомендации по использованию
Метод GetWorkingSecurities() добавляет все используемые таймфреймы, чтобы торговая среда получила нужные серии свечей.
Обработка выполняется только по завершённым свечам, что исключает перерисовку.
Перечисление AppliedPrices повторяет варианты исходного индикатора, включая два трендовых режима и цену DeMark.
Логика восстановления объёма из MQL-версии не переносилась; управление рисками можно реализовать через StartProtection() или настройку объёмов.
Комментарии внутри кода на английском языке описывают этапы конверсии и упрощают дальнейшее сопровождение или портирование.
Расширение
Для подключения стопов или тейков замените вызов StartProtection() на нужную конфигурацию.
При необходимости можно добавить новые модули, скопировав шаблон SignalModule.
Для отслеживания позиций каждого модуля отдельно можно использовать дочерние стратегии или виртуальные портфели StockSharp.
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()