Color J Variation Strategy
Strategy replicating the ColorJVariation Expert Advisor using Jurik Moving Average. It tracks the JMA slope and enters when the direction changes. The strategy supports absolute stop loss and take profit levels.
Details
- Entry Criteria:
- Long:
PrevSlopeDown && JMA turns up - Short:
PrevSlopeUp && JMA turns down
- Long:
- Long/Short: Both
- Exit Criteria:
- Opposite reversal signal
- Stops: Absolute via
StopLossandTakeProfit - Default Values:
JmaPeriod= 12JmaPhase= 100StopLoss= 1000TakeProfit= 2000CandleType= TimeSpan.FromHours(1).TimeFrame()
- Filters:
- Category: Trend reversal
- Direction: Both
- Indicators: Jurik Moving Average
- Stops: Yes
- Complexity: Basic
- Timeframe: Mid-term
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
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 Jurik moving average slope reversals, with fixed absolute protection.
/// Buys when JMA turns up, sells when JMA turns down.
/// </summary>
public class ColorJVariationStrategy : Strategy
{
private readonly StrategyParam<int> _jmaPeriod;
private readonly StrategyParam<int> _jmaPhase;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private decimal _prevJma;
private decimal _prevPrevJma;
private int _count;
public int JmaPeriod { get => _jmaPeriod.Value; set => _jmaPeriod.Value = value; }
public int JmaPhase { get => _jmaPhase.Value; set => _jmaPhase.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
public ColorJVariationStrategy()
{
_jmaPeriod = Param(nameof(JmaPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("JMA Period", "JMA averaging period", "Indicator");
_jmaPhase = Param(nameof(JmaPhase), 100)
.SetDisplay("JMA Phase", "Phase for JMA", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for analysis", "General");
_stopLoss = Param(nameof(StopLoss), 1000m)
.SetDisplay("Stop Loss", "Absolute stop distance from entry", "Risk");
_takeProfit = Param(nameof(TakeProfit), 2000m)
.SetDisplay("Take Profit", "Absolute take-profit distance from entry", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevJma = 0;
_prevPrevJma = 0;
_count = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var jma = new JurikMovingAverage
{
Length = JmaPeriod,
Phase = JmaPhase
};
SubscribeCandles(CandleType)
.Bind(jma, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(TakeProfit, UnitTypes.Absolute),
stopLoss: new Unit(StopLoss, UnitTypes.Absolute));
}
private void ProcessCandle(ICandleMessage candle, decimal jmaValue)
{
if (candle.State != CandleStates.Finished)
return;
_count++;
if (_count < 3)
{
_prevPrevJma = _prevJma;
_prevJma = jmaValue;
return;
}
var turnUp = _prevJma < _prevPrevJma && jmaValue > _prevJma;
var turnDown = _prevJma > _prevPrevJma && jmaValue < _prevJma;
if (turnUp && Position <= 0)
{
if (Position < 0)
BuyMarket(-Position);
BuyMarket(Volume);
}
else if (turnDown && Position >= 0)
{
if (Position > 0)
SellMarket(Position);
SellMarket(Volume);
}
_prevPrevJma = _prevJma;
_prevJma = jmaValue;
}
}
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 JurikMovingAverage
from StockSharp.Algo.Strategies import Strategy
class color_j_variation_strategy(Strategy):
"""
Strategy based on Jurik moving average slope reversals.
Buys when JMA turns up, sells when JMA turns down.
"""
def __init__(self):
super(color_j_variation_strategy, self).__init__()
self._jma_period = self.Param("JmaPeriod", 12) \
.SetDisplay("JMA Period", "JMA averaging period", "Indicator")
self._jma_phase = self.Param("JmaPhase", 100) \
.SetDisplay("JMA Phase", "Phase for JMA", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe for analysis", "General")
self._stop_loss = self.Param("StopLoss", 1000.0) \
.SetDisplay("Stop Loss", "Absolute stop distance from entry", "Risk")
self._take_profit = self.Param("TakeProfit", 2000.0) \
.SetDisplay("Take Profit", "Absolute take-profit distance from entry", "Risk")
self._prev_jma = 0.0
self._prev_prev_jma = 0.0
self._count = 0
@property
def candle_type(self):
return self._candle_type.Value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
def OnReseted(self):
super(color_j_variation_strategy, self).OnReseted()
self._prev_jma = 0.0
self._prev_prev_jma = 0.0
self._count = 0
def OnStarted2(self, time):
super(color_j_variation_strategy, self).OnStarted2(time)
jma = JurikMovingAverage()
jma.Length = self._jma_period.Value
jma.Phase = self._jma_phase.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(jma, self.on_process).Start()
self.StartProtection(
takeProfit=Unit(self.TakeProfit, UnitTypes.Absolute),
stopLoss=Unit(self.StopLoss, UnitTypes.Absolute)
)
def on_process(self, candle, jma_val):
if candle.State != CandleStates.Finished:
return
self._count += 1
if self._count < 3:
self._prev_prev_jma = self._prev_jma
self._prev_jma = jma_val
return
turn_up = self._prev_jma < self._prev_prev_jma and jma_val > self._prev_jma
turn_down = self._prev_jma > self._prev_prev_jma and jma_val < self._prev_jma
if turn_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(-self.Position)
self.BuyMarket(self.Volume)
elif turn_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket(self.Position)
self.SellMarket(self.Volume)
self._prev_prev_jma = self._prev_jma
self._prev_jma = jma_val
def CreateClone(self):
return color_j_variation_strategy()