Стратегия представляет собой перенос советника MetaTrader "Exp_ColorXDerivative" на платформу StockSharp. Она работает на настраиваемом таймфрейме свечей (по умолчанию 12 часов) и анализирует гистограмму импульса ColorXDerivative. Индикатор измеряет скорость изменения выбранной цены за фиксированный сдвиг, сглаживает результат скользящей средней и классифицирует каждый бар в один из пяти цветовых состояний. Сделки повторяют оригинальную логику: робот покупает при ускорении бычьего импульса или при ослаблении нисходящего движения, и продаёт при усилении медвежьего давления или затухании бычьей ноги.
Логика индикатора
Преобразовать свечу в выбранный тип цены AppliedPrice (close, open, weighted close, Demark и т.д.).
Сгладить производную выбранным методом (SMA, EMA, SMMA, LWMA или Jurik). По умолчанию используется Jurik Moving Average как аналог JJMA из библиотеки MQL.
Назначить цветовое состояние:
0 – производная > 0 и растёт (сильное бычье ускорение).
1 – производная > 0, но падает (ослабление бычьего импульса).
2 – производная ≈ 0 (нейтральное состояние).
3 – производная < 0, но растёт (сжатие медвежьего движения).
4 – производная < 0 и падает (усиление медвежьего импульса).
Параметр SignalShift определяет, какой завершённый бар используется для сигналов (1 = последний закрытый бар, 2 = предыдущий и т.д.).
Правила торговли
Вход в лонг (если EnableLongEntry = true):
текущий цвет 0, а предыдущий отличался от 0 (резкое ускорение вверх); или
текущий цвет 3, а предыдущий был 4 либо 2 (нисходящее движение ослабевает).
Вход в шорт (если EnableShortEntry = true):
текущий цвет 4, а предыдущий отличался от 4 (ускорение вниз); или
текущий цвет 1, а предыдущий был 0 либо 2 (затухание бычьего импульса).
Выход из лонга: текущий цвет 1 либо 4 и включён EnableLongExit.
Выход из шорта: текущий цвет 0 либо 3 и включён EnableShortExit.
Заявки отправляются по рынку с объёмом OrderVolume. Перед открытием новой позиции выполняется закрытие текущей, что повторяет последовательную работу оригинального советника.
Управление рисками
Параметры StopLossTicks и TakeProfitTicks задают необязательные защитные дистанции в шагах цены. Если любое значение больше нуля, метод StartProtection конвертирует тики в шаги инструмента (Security.Step) и активирует стоп/тейк-профит один раз. Механизм работает как в автоторговле, так и в тестировании.
Параметры
OrderVolume – объём рыночных заявок.
CandleType – таймфрейм для расчёта индикатора (по умолчанию 12 часов).
DerivativePeriod – сдвиг в барах для вычисления производной.
AppliedPrice – источник цены (close, median, weighted, Demark и т.д.).
SmoothingMethod – метод сглаживания производной (SMA, EMA, SMMA, LWMA, Jurik).
SmoothingLength – период сглаживающего фильтра.
SignalShift – сколько закрытых баров назад считывать цвета (1 = последний бар).
StopLossTicks / TakeProfitTicks – необязательные защитные дистанции в шагах цены.
Реализация полностью повторяет индикаторную логику MetaTrader без дополнительных блоков мани-менеджмента.
Сглаживание Jurik является ближайшим аналогом фильтра JJMA из библиотеки SmoothAlgorithms, остальные методы сопоставлены стандартным скользящим средним StockSharp.
История цветов хранится внутри индикатора, поэтому оптимизация по SignalShift даёт такие же результаты, как в оригинальной платформе.
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>
/// Color X Derivative strategy (simplified). Uses Momentum to detect
/// acceleration/deceleration and generate reversal signals.
/// </summary>
public class ColorXDerivativeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _momentumLength;
private readonly StrategyParam<int> _emaLength;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MomentumLength
{
get => _momentumLength.Value;
set => _momentumLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public ColorXDerivativeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_momentumLength = Param(nameof(MomentumLength), 34)
.SetGreaterThanZero()
.SetDisplay("Momentum Length", "Derivative lookback", "Indicators");
_emaLength = Param(nameof(EmaLength), 7)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "Smoothing period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var momentum = new Momentum { Length = MomentumLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
decimal prevMom = 0;
var hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(momentum, ema, (ICandleMessage candle, decimal momValue, decimal emaValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevMom = momValue;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevMom = momValue;
return;
}
var close = candle.ClosePrice;
// Momentum turning positive with price above EMA
if (prevMom <= 0 && momValue > 0 && close > emaValue && Position <= 0)
BuyMarket();
// Momentum turning negative with price below EMA
else if (prevMom >= 0 && momValue < 0 && close < emaValue && Position >= 0)
SellMarket();
prevMom = momValue;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
}
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 Momentum, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class color_x_derivative_strategy(Strategy):
def __init__(self):
super(color_x_derivative_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candles", "General")
self._momentum_length = self.Param("MomentumLength", 34) \
.SetDisplay("Momentum Length", "Derivative lookback", "Indicators")
self._ema_length = self.Param("EmaLength", 7) \
.SetDisplay("EMA Length", "Smoothing period", "Indicators")
self._prev_mom = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def MomentumLength(self):
return self._momentum_length.Value
@property
def EmaLength(self):
return self._ema_length.Value
def OnReseted(self):
super(color_x_derivative_strategy, self).OnReseted()
self._prev_mom = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(color_x_derivative_strategy, self).OnStarted2(time)
self._prev_mom = 0.0
self._has_prev = False
momentum = Momentum()
momentum.Length = self.MomentumLength
ema = ExponentialMovingAverage()
ema.Length = self.EmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(momentum, ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, mom_value, ema_value):
if candle.State != CandleStates.Finished:
return
mv = float(mom_value)
if not self._has_prev:
self._prev_mom = mv
self._has_prev = True
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_mom = mv
return
close = float(candle.ClosePrice)
ev = float(ema_value)
if self._prev_mom <= 0 and mv > 0 and close > ev and self.Position <= 0:
self.BuyMarket()
elif self._prev_mom >= 0 and mv < 0 and close < ev and self.Position >= 0:
self.SellMarket()
self._prev_mom = mv
def CreateClone(self):
return color_x_derivative_strategy()