Стратегия JSatl Candle
Стратегия использует сглаживающую среднюю Jurik (JMA) для построения тренда. Длинная позиция открывается, когда JMA разворачивается вверх после бокового или нисходящего движения. Короткая позиция открывается, когда JMA разворачивается вниз после бокового или восходящего движения. Опционально применяются стоп‑лосс и тейк‑профит в процентах от цены входа.
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 changes.
/// Opens long when JMA turns upward and short when it turns downward.
/// Includes optional stop loss and take profit protection.
/// </summary>
public class JSatlCandleStrategy : Strategy
{
private readonly StrategyParam<int> _jmaLength;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<bool> _enableStopLoss;
private readonly StrategyParam<decimal> _takeProfitPercent;
private decimal? _prevJma;
private int _prevDirection;
/// <summary>
/// JMA period length.
/// </summary>
public int JmaLength
{
get => _jmaLength.Value;
set => _jmaLength.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Stop loss percent.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Enable stop loss.
/// </summary>
public bool EnableStopLoss
{
get => _enableStopLoss.Value;
set => _enableStopLoss.Value = value;
}
/// <summary>
/// Take profit percent.
/// </summary>
public decimal TakeProfitPercent
{
get => _takeProfitPercent.Value;
set => _takeProfitPercent.Value = value;
}
public JSatlCandleStrategy()
{
_jmaLength = Param(nameof(JmaLength), 5)
.SetGreaterThanZero()
.SetDisplay("JMA Length", "Period for Jurik Moving Average", "Parameters")
.SetOptimize(2, 20, 1);
_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 percent", "Risk Management")
.SetOptimize(0.5m, 3m, 0.5m);
_enableStopLoss = Param(nameof(EnableStopLoss), true)
.SetDisplay("Enable Stop Loss", "Use stop loss", "Risk Management");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 2m)
.SetGreaterThanZero()
.SetDisplay("Take Profit %", "Take profit percent", "Risk Management")
.SetOptimize(1m, 5m, 1m);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevJma = null;
_prevDirection = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevJma = null;
_prevDirection = 0;
var jma = new JurikMovingAverage { Length = JmaLength };
SubscribeCandles(CandleType)
.Bind(jma, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(TakeProfitPercent * 100m, UnitTypes.Percent),
stopLoss: EnableStopLoss ? new Unit(StopLossPercent * 100m, UnitTypes.Percent) : null);
}
private void ProcessCandle(ICandleMessage candle, decimal jmaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevDirection = _prevJma is decimal prevValue ? Math.Sign(jmaValue - prevValue) : 0;
_prevJma = jmaValue;
return;
}
var direction = _prevJma is decimal prev ? Math.Sign(jmaValue - prev) : 0;
if (_prevDirection <= 0 && direction > 0 && Position <= 0)
BuyMarket();
else if (_prevDirection >= 0 && direction < 0 && Position >= 0)
SellMarket();
_prevDirection = direction;
_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, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import JurikMovingAverage
from StockSharp.Algo.Strategies import Strategy
class j_satl_candle_strategy(Strategy):
def __init__(self):
super(j_satl_candle_strategy, self).__init__()
self._jma_length = self.Param("JmaLength", 5) \
.SetDisplay("JMA Length", "Period for Jurik Moving Average", "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 percent", "Risk Management")
self._enable_stop_loss = self.Param("EnableStopLoss", True) \
.SetDisplay("Enable Stop Loss", "Use stop loss", "Risk Management")
self._take_profit_percent = self.Param("TakeProfitPercent", 2.0) \
.SetDisplay("Take Profit %", "Take profit percent", "Risk Management")
self._prev_jma = None
self._prev_direction = 0
@property
def jma_length(self):
return self._jma_length.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def stop_loss_percent(self):
return self._stop_loss_percent.Value
@property
def enable_stop_loss(self):
return self._enable_stop_loss.Value
@property
def take_profit_percent(self):
return self._take_profit_percent.Value
def OnReseted(self):
super(j_satl_candle_strategy, self).OnReseted()
self._prev_jma = None
self._prev_direction = 0
def OnStarted2(self, time):
super(j_satl_candle_strategy, self).OnStarted2(time)
self._prev_jma = None
self._prev_direction = 0
jma = JurikMovingAverage()
jma.Length = int(self.jma_length)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(jma, self.process_candle).Start()
tp_val = float(self.take_profit_percent) * 100.0
sl = Unit(float(self.stop_loss_percent) * 100.0, UnitTypes.Percent) if self.enable_stop_loss else None
self.StartProtection(
takeProfit=Unit(tp_val, UnitTypes.Percent),
stopLoss=sl)
def process_candle(self, candle, jma_value):
if candle.State != CandleStates.Finished:
return
jma_value = float(jma_value)
if self._prev_jma is not None:
diff = jma_value - self._prev_jma
if diff > 0:
direction = 1
elif diff < 0:
direction = -1
else:
direction = 0
else:
direction = 0
if self._prev_direction <= 0 and direction > 0 and self.Position <= 0:
self.BuyMarket()
elif self._prev_direction >= 0 and direction < 0 and self.Position >= 0:
self.SellMarket()
self._prev_direction = direction
self._prev_jma = jma_value
def CreateClone(self):
return j_satl_candle_strategy()