JMA Slope Strategy
This strategy monitors the slope of the Jurik Moving Average (JMA). A position is opened when the slope crosses zero or when its direction changes depending on the selected mode.
Details
- Entry Criteria:
- Long: Slope crosses below zero or turns upward (mode dependent).
- Short: Slope crosses above zero or turns downward.
- Long/Short: Long and short.
- Exit Criteria:
- Opposite signal reverses the position.
- Stops: None.
- Default Values:
JMA Length= 14JMA Phase= 0Mode= BreakdownCandle Type= 4h timeframe
- Filters:
- Category: Trend following
- Direction: Long & Short
- Indicators: JMA
- Stops: No
- Complexity: Basic
- Timeframe: 4h
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// JMA slope based strategy detecting zero breakouts or slope twists.
/// </summary>
public class JmaSlopeStrategy : Strategy
{
/// <summary>
/// JMA slope entry modes.
/// </summary>
public enum JmaSlopeModes
{
/// <summary>
/// Signals when slope crosses zero.
/// </summary>
Breakdown,
/// <summary>
/// Signals when slope changes direction.
/// </summary>
Twist
}
private readonly StrategyParam<int> _jmaLength;
private readonly StrategyParam<int> _jmaPhase;
private readonly StrategyParam<JmaSlopeModes> _mode;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevJma;
private decimal? _prevSlope1;
private decimal? _prevSlope2;
private decimal? _prevSlope3;
/// <summary>
/// JMA smoothing length.
/// </summary>
public int JmaLength { get => _jmaLength.Value; set => _jmaLength.Value = value; }
/// <summary>
/// JMA phase parameter from -100 to 100.
/// </summary>
public int JmaPhase { get => _jmaPhase.Value; set => _jmaPhase.Value = value; }
/// <summary>
/// Entry mode algorithm.
/// </summary>
public JmaSlopeModes Mode { get => _mode.Value; set => _mode.Value = value; }
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initializes a new instance of <see cref="JmaSlopeStrategy"/>.
/// </summary>
public JmaSlopeStrategy()
{
_jmaLength = Param(nameof(JmaLength), 14)
.SetGreaterThanZero()
.SetDisplay("JMA Length", "Period for Jurik Moving Average", "Indicators")
;
_jmaPhase = Param(nameof(JmaPhase), 0)
.SetDisplay("JMA Phase", "Phase parameter", "Indicators");
_mode = Param(nameof(Mode), JmaSlopeModes.Breakdown)
.SetDisplay("Mode", "Entry algorithm", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevJma = null;
_prevSlope1 = null;
_prevSlope2 = null;
_prevSlope3 = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var jma = new JurikMovingAverage { Length = JmaLength, Phase = JmaPhase };
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;
var slope = _prevJma is decimal prev ? jmaValue - prev : (decimal?)null;
if (IsFormedAndOnlineAndAllowTrading()
&& _prevSlope2 is decimal s2 && _prevSlope1 is decimal s1 && _prevSlope3 is decimal s3)
{
var buy = false;
var sell = false;
switch (Mode)
{
case JmaSlopeModes.Breakdown:
buy = s2 > 0m && s1 <= 0m;
sell = s2 < 0m && s1 >= 0m;
break;
case JmaSlopeModes.Twist:
buy = s2 < s3 && s1 > s2;
sell = s2 > s3 && s1 < s2;
break;
}
if (buy && Position <= 0)
BuyMarket();
else if (sell && Position >= 0)
SellMarket();
}
_prevSlope3 = _prevSlope2;
_prevSlope2 = _prevSlope1;
_prevSlope1 = slope;
_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
from StockSharp.Algo.Indicators import JurikMovingAverage
from StockSharp.Algo.Strategies import Strategy
class jma_slope_strategy(Strategy):
def __init__(self):
super(jma_slope_strategy, self).__init__()
self._jma_length = self.Param("JmaLength", 14) \
.SetDisplay("JMA Length", "Period for Jurik Moving Average", "Indicators")
self._jma_phase = self.Param("JmaPhase", 0) \
.SetDisplay("JMA Phase", "Phase parameter", "Indicators")
self._mode = self.Param("Mode", 0) \
.SetDisplay("Mode", "Entry algorithm (0=Breakdown, 1=Twist)", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._prev_jma = None
self._prev_slope1 = None
self._prev_slope2 = None
self._prev_slope3 = None
@property
def jma_length(self):
return self._jma_length.Value
@property
def jma_phase(self):
return self._jma_phase.Value
@property
def mode(self):
return self._mode.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(jma_slope_strategy, self).OnReseted()
self._prev_jma = None
self._prev_slope1 = None
self._prev_slope2 = None
self._prev_slope3 = None
def OnStarted2(self, time):
super(jma_slope_strategy, self).OnStarted2(time)
jma = JurikMovingAverage()
jma.Length = self.jma_length
jma.Phase = self.jma_phase
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(jma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, jma)
self.DrawOwnTrades(area)
def process_candle(self, candle, jma_value):
if candle.State != CandleStates.Finished:
return
jma_value = float(jma_value)
slope = jma_value - self._prev_jma if self._prev_jma is not None else None
if (self._prev_slope2 is not None and self._prev_slope1 is not None
and self._prev_slope3 is not None):
s1 = self._prev_slope1
s2 = self._prev_slope2
s3 = self._prev_slope3
buy = False
sell = False
if self.mode == 0:
buy = s2 > 0 and s1 <= 0
sell = s2 < 0 and s1 >= 0
else:
buy = s2 < s3 and s1 > s2
sell = s2 > s3 and s1 < s2
if buy and self.Position <= 0:
self.BuyMarket()
elif sell and self.Position >= 0:
self.SellMarket()
self._prev_slope3 = self._prev_slope2
self._prev_slope2 = self._prev_slope1
self._prev_slope1 = slope
self._prev_jma = jma_value
def CreateClone(self):
return jma_slope_strategy()