ColorJMomentum Strategy
The ColorJMomentum Strategy trades based on the direction changes of a Jurik smoothed momentum indicator. The approach is derived from the original MQL5 expert advisor Exp_ColorJMomentum and reproduced using the StockSharp high level API.
Concept
- Calculate the standard Momentum of the selected price series.
- Smooth the momentum values with the Jurik Moving Average (JMA).
- Monitor the last two values of the smoothed momentum:
- If the indicator was declining and turns upward, a long position is opened.
- If the indicator was rising and turns downward, a short position is opened.
- Position protection is handled by optional stop loss and take profit in percentage terms.
The strategy never reads historical indicator values directly. Instead it reacts only to new candle completions and stores previous values internally.
Parameters
- Momentum Length – period for the momentum calculation.
- JMA Length – smoothing period of the Jurik moving average applied to momentum.
- Candle Type – timeframe used for candle subscriptions.
- Stop Loss % – percentage for optional stop loss.
- Enable Stop Loss – whether to activate stop loss.
- Take Profit % – percentage for take profit.
- Enable Long – allow opening long positions.
- Enable Short – allow opening short positions.
All parameters are created with StrategyParam so they can be optimized in Designer.
Usage
- Attach the strategy to the desired security.
- Configure parameters or leave defaults (8-period momentum and 8-period JMA on 8‑hour candles).
- Run the strategy. Orders will be issued via
BuyMarket and SellMarket when momentum direction reverses.
Notes
- The strategy processes only finished candles.
- No explicit colors are set for indicators – Designer chooses them automatically.
- The algorithm avoids any LINQ or custom collections, following project guidelines.
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>
/// Strategy based on smoothed momentum direction changes.
/// Opens long when momentum turns up, short when momentum turns down.
/// </summary>
public class ColorJMomentumStrategy : Strategy
{
private readonly StrategyParam<int> _momentumLength;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<bool> _enableLong;
private readonly StrategyParam<bool> _enableShort;
private decimal _prevMom;
private decimal _prevPrevMom;
private int _count;
public int MomentumLength { get => _momentumLength.Value; set => _momentumLength.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
public decimal TakeProfitPercent { get => _takeProfitPercent.Value; set => _takeProfitPercent.Value = value; }
public bool EnableLong { get => _enableLong.Value; set => _enableLong.Value = value; }
public bool EnableShort { get => _enableShort.Value; set => _enableShort.Value = value; }
public ColorJMomentumStrategy()
{
_momentumLength = Param(nameof(MomentumLength), 8)
.SetGreaterThanZero()
.SetDisplay("Momentum Length", "Period for momentum", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candles", "Parameters");
_stopLossPercent = Param(nameof(StopLossPercent), 1m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk management");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 2m)
.SetGreaterThanZero()
.SetDisplay("Take Profit %", "Take profit percentage", "Risk management");
_enableLong = Param(nameof(EnableLong), true)
.SetDisplay("Enable Long", "Allow long entries", "General");
_enableShort = Param(nameof(EnableShort), true)
.SetDisplay("Enable Short", "Allow short entries", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMom = 0;
_prevPrevMom = 0;
_count = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var momentum = new Momentum { Length = MomentumLength };
SubscribeCandles(CandleType)
.Bind(momentum, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(TakeProfitPercent, UnitTypes.Percent),
stopLoss: new Unit(StopLossPercent, UnitTypes.Percent)
);
}
private void ProcessCandle(ICandleMessage candle, decimal momValue)
{
if (candle.State != CandleStates.Finished)
return;
_count++;
if (_count < 3)
{
_prevPrevMom = _prevMom;
_prevMom = momValue;
return;
}
var wasDecreasing = _prevMom < _prevPrevMom;
var nowIncreasing = momValue > _prevMom;
var wasIncreasing = _prevMom > _prevPrevMom;
var nowDecreasing = momValue < _prevMom;
// Momentum turns up - go long
if (wasDecreasing && nowIncreasing && EnableLong && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
// Momentum turns down - go short
else if (wasIncreasing && nowDecreasing && EnableShort && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevPrevMom = _prevMom;
_prevMom = momValue;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import Momentum
from StockSharp.Algo.Strategies import Strategy
class color_j_momentum_strategy(Strategy):
"""
Strategy based on smoothed momentum direction changes.
Opens long when momentum turns up, short when momentum turns down.
"""
def __init__(self):
super(color_j_momentum_strategy, self).__init__()
self._momentum_length = self.Param("MomentumLength", 8) \
.SetDisplay("Momentum Length", "Period for momentum", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for candles", "Parameters")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk management")
self._take_profit_percent = self.Param("TakeProfitPercent", 2.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk management")
self._enable_long = self.Param("EnableLong", True) \
.SetDisplay("Enable Long", "Allow long entries", "General")
self._enable_short = self.Param("EnableShort", True) \
.SetDisplay("Enable Short", "Allow short entries", "General")
self._prev_mom = 0.0
self._prev_prev_mom = 0.0
self._count = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(color_j_momentum_strategy, self).OnReseted()
self._prev_mom = 0.0
self._prev_prev_mom = 0.0
self._count = 0
def OnStarted2(self, time):
super(color_j_momentum_strategy, self).OnStarted2(time)
momentum = Momentum()
momentum.Length = self._momentum_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(momentum, self.on_process).Start()
self.StartProtection(
takeProfit=Unit(self._take_profit_percent.Value, UnitTypes.Percent),
stopLoss=Unit(self._stop_loss_percent.Value, UnitTypes.Percent)
)
def on_process(self, candle, mom_val):
if candle.State != CandleStates.Finished:
return
self._count += 1
if self._count < 3:
self._prev_prev_mom = self._prev_mom
self._prev_mom = mom_val
return
was_decreasing = self._prev_mom < self._prev_prev_mom
now_increasing = mom_val > self._prev_mom
was_increasing = self._prev_mom > self._prev_prev_mom
now_decreasing = mom_val < self._prev_mom
if was_decreasing and now_increasing and self._enable_long.Value and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif was_increasing and now_decreasing and self._enable_short.Value and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_prev_mom = self._prev_mom
self._prev_mom = mom_val
def CreateClone(self):
return color_j_momentum_strategy()