Стратегия KST Skyrexio
Стратегия открывает длинную позицию, когда индикатор Know Sure Thing (KST) пересекает сверху свою сигнальную линию, а цена находится выше выбранного скользящего среднего и линии челюсти Аллигатора. Фильтр индекса задерганности помогает избежать входов в боковике. Закрытие позиции происходит по уровням стоп‑лосса и тейк‑профита на основе ATR.
- Условие входа: пересечение KST выше сигнала, цена выше фильтрующего MA и челюсти Аллигатора, значение индекса задерганности ниже порога.
- Условие выхода: достижение уровней стоп‑лосса или тейк‑профита по ATR.
- Индикаторы: KST, ATR, скользящее среднее, челюсть Аллигатора, индекс задерганности.
Параметры
CandleType– таймфрейм свечей.AtrStopLoss– множитель ATR для стоп‑лосса.AtrTakeProfit– множитель ATR для тейк‑профита.FilterMaType– тип фильтрующего скользящего среднего.FilterMaLength– период фильтрующего скользящего среднего.EnableChopFilter– включить фильтр задерганности.ChopThreshold– порог индекса задерганности.ChopLength– период индекса задерганности.RocLen1..4– периоды ROC для KST.SmaLen1..4– периоды SMA для KST.SignalLength– период сигнальной линии KST.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// KST strategy with ATR-based exits and choppiness filter.
/// Goes long when KST crosses above its signal and price is above filter MA and Alligator jaw.
/// </summary>
public class KstSkyrexioStrategy : Strategy
{
private readonly StrategyParam<decimal> _atrStopLoss;
private readonly StrategyParam<decimal> _atrTakeProfit;
private readonly StrategyParam<MaTypes> _filterMaType;
private readonly StrategyParam<int> _filterMaLength;
private readonly StrategyParam<bool> _enableChopFilter;
private readonly StrategyParam<decimal> _chopThreshold;
private readonly StrategyParam<int> _chopLength;
private readonly StrategyParam<int> _rocLen1;
private readonly StrategyParam<int> _rocLen2;
private readonly StrategyParam<int> _rocLen3;
private readonly StrategyParam<int> _rocLen4;
private readonly StrategyParam<int> _smaLen1;
private readonly StrategyParam<int> _smaLen2;
private readonly StrategyParam<int> _smaLen3;
private readonly StrategyParam<int> _smaLen4;
private readonly StrategyParam<int> _signalLength;
private readonly StrategyParam<int> _maxEntries;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevKst;
private decimal _prevSig;
private decimal _stopLoss;
private decimal _takeProfit;
private int _entriesExecuted;
private int _barsSinceSignal;
/// <summary>
/// Moving average type for filter.
/// </summary>
public enum MaTypes
{
SMA,
EMA,
WMA,
HMA,
SMMA,
ALMA,
LSMA,
VWMA
}
/// <summary>
/// ATR stop-loss multiplier.
/// </summary>
public decimal AtrStopLoss { get => _atrStopLoss.Value; set => _atrStopLoss.Value = value; }
/// <summary>
/// ATR take-profit multiplier.
/// </summary>
public decimal AtrTakeProfit { get => _atrTakeProfit.Value; set => _atrTakeProfit.Value = value; }
/// <summary>
/// Filter moving average type.
/// </summary>
public MaTypes FilterMaType { get => _filterMaType.Value; set => _filterMaType.Value = value; }
/// <summary>
/// Filter moving average length.
/// </summary>
public int FilterMaLength { get => _filterMaLength.Value; set => _filterMaLength.Value = value; }
/// <summary>
/// Enable choppiness filter.
/// </summary>
public bool EnableChopFilter { get => _enableChopFilter.Value; set => _enableChopFilter.Value = value; }
/// <summary>
/// Choppiness threshold.
/// </summary>
public decimal ChopThreshold { get => _chopThreshold.Value; set => _chopThreshold.Value = value; }
/// <summary>
/// Choppiness period.
/// </summary>
public int ChopLength { get => _chopLength.Value; set => _chopLength.Value = value; }
/// <summary>
/// ROC length #1.
/// </summary>
public int RocLen1 { get => _rocLen1.Value; set => _rocLen1.Value = value; }
/// <summary>
/// ROC length #2.
/// </summary>
public int RocLen2 { get => _rocLen2.Value; set => _rocLen2.Value = value; }
/// <summary>
/// ROC length #3.
/// </summary>
public int RocLen3 { get => _rocLen3.Value; set => _rocLen3.Value = value; }
/// <summary>
/// ROC length #4.
/// </summary>
public int RocLen4 { get => _rocLen4.Value; set => _rocLen4.Value = value; }
/// <summary>
/// SMA length #1.
/// </summary>
public int SmaLen1 { get => _smaLen1.Value; set => _smaLen1.Value = value; }
/// <summary>
/// SMA length #2.
/// </summary>
public int SmaLen2 { get => _smaLen2.Value; set => _smaLen2.Value = value; }
/// <summary>
/// SMA length #3.
/// </summary>
public int SmaLen3 { get => _smaLen3.Value; set => _smaLen3.Value = value; }
/// <summary>
/// SMA length #4.
/// </summary>
public int SmaLen4 { get => _smaLen4.Value; set => _smaLen4.Value = value; }
/// <summary>
/// KST signal length.
/// </summary>
public int SignalLength { get => _signalLength.Value; set => _signalLength.Value = value; }
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Maximum entries per run.
/// </summary>
public int MaxEntries { get => _maxEntries.Value; set => _maxEntries.Value = value; }
/// <summary>
/// Minimum bars between entries.
/// </summary>
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public KstSkyrexioStrategy()
{
_atrStopLoss = Param(nameof(AtrStopLoss), 1.5m)
.SetGreaterThanZero()
.SetDisplay("ATR Stop Loss", "ATR stop-loss multiplier", "Stops");
_atrTakeProfit = Param(nameof(AtrTakeProfit), 3.5m)
.SetGreaterThanZero()
.SetDisplay("ATR Take Profit", "ATR take-profit multiplier", "Stops");
_filterMaType = Param(nameof(FilterMaType), MaTypes.LSMA)
.SetDisplay("Filter MA Type", "Type of trend filter", "Filter");
_filterMaLength = Param(nameof(FilterMaLength), 200)
.SetGreaterThanZero()
.SetDisplay("Filter MA Length", "Length of trend filter", "Filter");
_enableChopFilter = Param(nameof(EnableChopFilter), true)
.SetDisplay("Enable Choppiness", "Use choppiness filter", "Choppiness");
_chopThreshold = Param(nameof(ChopThreshold), 50m)
.SetDisplay("Choppiness Threshold", "Threshold for choppiness index", "Choppiness");
_chopLength = Param(nameof(ChopLength), 14)
.SetGreaterThanZero()
.SetDisplay("Choppiness Length", "Choppiness index period", "Choppiness");
_rocLen1 = Param(nameof(RocLen1), 10)
.SetGreaterThanZero()
.SetDisplay("ROC Length #1", "First ROC length", "KST");
_rocLen2 = Param(nameof(RocLen2), 15)
.SetGreaterThanZero()
.SetDisplay("ROC Length #2", "Second ROC length", "KST");
_rocLen3 = Param(nameof(RocLen3), 20)
.SetGreaterThanZero()
.SetDisplay("ROC Length #3", "Third ROC length", "KST");
_rocLen4 = Param(nameof(RocLen4), 30)
.SetGreaterThanZero()
.SetDisplay("ROC Length #4", "Fourth ROC length", "KST");
_smaLen1 = Param(nameof(SmaLen1), 10)
.SetGreaterThanZero()
.SetDisplay("SMA Length #1", "First SMA length", "KST");
_smaLen2 = Param(nameof(SmaLen2), 10)
.SetGreaterThanZero()
.SetDisplay("SMA Length #2", "Second SMA length", "KST");
_smaLen3 = Param(nameof(SmaLen3), 10)
.SetGreaterThanZero()
.SetDisplay("SMA Length #3", "Third SMA length", "KST");
_smaLen4 = Param(nameof(SmaLen4), 15)
.SetGreaterThanZero()
.SetDisplay("SMA Length #4", "Fourth SMA length", "KST");
_signalLength = Param(nameof(SignalLength), 9)
.SetGreaterThanZero()
.SetDisplay("Signal Length", "KST signal SMA length", "KST");
_maxEntries = Param(nameof(MaxEntries), 45)
.SetGreaterThanZero()
.SetDisplay("Max Entries", "Maximum entries per run", "Risk");
_cooldownBars = Param(nameof(CooldownBars), 120)
.SetGreaterThanZero()
.SetDisplay("Cooldown Bars", "Minimum bars between entries", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevKst = 0m;
_prevSig = 0m;
_stopLoss = 0m;
_takeProfit = 0m;
_entriesExecuted = 0;
_barsSinceSignal = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entriesExecuted = 0;
_barsSinceSignal = CooldownBars;
var roc1 = new RateOfChange { Length = RocLen1 };
var sma1 = new SMA { Length = SmaLen1 };
var roc2 = new RateOfChange { Length = RocLen2 };
var sma2 = new SMA { Length = SmaLen2 };
var roc3 = new RateOfChange { Length = RocLen3 };
var sma3 = new SMA { Length = SmaLen3 };
var roc4 = new RateOfChange { Length = RocLen4 };
var sma4 = new SMA { Length = SmaLen4 };
var signal = new SMA { Length = SignalLength };
var atr = new AverageTrueRange { Length = 14 };
var choppiness = new ChoppinessIndex { Length = ChopLength };
var jaw = new SmoothedMovingAverage { Length = 13 };
var jawShift = new Shift { Length = 8 };
var filter = CreateFilterMa();
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, filter);
DrawIndicator(area, jaw);
DrawOwnTrades(area);
}
void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_barsSinceSignal++;
var close = candle.ClosePrice;
var median = (candle.HighPrice + candle.LowPrice) / 2m;
var t = candle.OpenTime;
var r1 = sma1.Process(roc1.Process(new DecimalIndicatorValue(roc1, close, t) { IsFinal = true }));
var r2 = sma2.Process(roc2.Process(new DecimalIndicatorValue(roc2, close, t) { IsFinal = true }));
var r3 = sma3.Process(roc3.Process(new DecimalIndicatorValue(roc3, close, t) { IsFinal = true }));
var r4 = sma4.Process(roc4.Process(new DecimalIndicatorValue(roc4, close, t) { IsFinal = true }));
var jawVal = jawShift.Process(jaw.Process(new DecimalIndicatorValue(jaw, median, t) { IsFinal = true }));
var filterVal = filter.Process(new DecimalIndicatorValue(filter, close, t) { IsFinal = true });
var atrVal = atr.Process(candle);
var chopVal = choppiness.Process(candle);
if (!sma1.IsFormed || !sma2.IsFormed || !sma3.IsFormed || !sma4.IsFormed || !jaw.IsFormed || !filter.IsFormed || !atr.IsFormed || !choppiness.IsFormed)
return;
var kst = r1.ToDecimal() + 2m * r2.ToDecimal() + 3m * r3.ToDecimal() + 4m * r4.ToDecimal();
var sigVal = signal.Process(new DecimalIndicatorValue(signal, kst, t) { IsFinal = true });
if (!signal.IsFormed)
return;
var sig = sigVal.ToDecimal();
var jawValue = jawVal.ToDecimal();
var filterValue = filterVal.ToDecimal();
var atrValue = atrVal.ToDecimal();
var chop = chopVal.ToDecimal();
var chopCond = !EnableChopFilter || chop < ChopThreshold;
var crossUp = _prevKst <= _prevSig && kst > sig;
if (_entriesExecuted < MaxEntries && _barsSinceSignal >= CooldownBars && crossUp && close > filterValue && close > jawValue && chopCond && Position == 0)
{
_stopLoss = candle.LowPrice - AtrStopLoss * atrValue;
_takeProfit = close + AtrTakeProfit * atrValue;
BuyMarket();
_entriesExecuted++;
_barsSinceSignal = 0;
}
if (Position > 0 && _stopLoss > 0m && _takeProfit > 0m && (candle.LowPrice <= _stopLoss || candle.HighPrice >= _takeProfit))
{
SellMarket(Math.Abs(Position));
_stopLoss = 0m;
_takeProfit = 0m;
_barsSinceSignal = 0;
}
_prevKst = kst;
_prevSig = sig;
}
}
private IIndicator CreateFilterMa()
{
return FilterMaType switch
{
MaTypes.SMA => new SMA { Length = FilterMaLength },
MaTypes.EMA => new EMA { Length = FilterMaLength },
MaTypes.WMA => new WeightedMovingAverage { Length = FilterMaLength },
MaTypes.HMA => new HullMovingAverage { Length = FilterMaLength },
MaTypes.SMMA => new SmoothedMovingAverage { Length = FilterMaLength },
MaTypes.ALMA => new ArnaudLegouxMovingAverage { Length = FilterMaLength },
MaTypes.LSMA => new LinearRegressionForecast { Length = FilterMaLength },
MaTypes.VWMA => new VolumeWeightedMovingAverage { Length = FilterMaLength },
_ => new SMA { Length = FilterMaLength }
};
}
}
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 (RateOfChange, SimpleMovingAverage, AverageTrueRange,
ChoppinessIndex, SmoothedMovingAverage, LinearRegressionForecast,
Shift)
from StockSharp.Algo.Strategies import Strategy
from System import Decimal
from indicator_extensions import *
class kst_skyrexio_strategy(Strategy):
def __init__(self):
super(kst_skyrexio_strategy, self).__init__()
self._atr_stop_loss = self.Param("AtrStopLoss", 1.5) \
.SetGreaterThanZero() \
.SetDisplay("ATR Stop Loss", "ATR stop-loss multiplier", "Stops")
self._atr_take_profit = self.Param("AtrTakeProfit", 3.5) \
.SetGreaterThanZero() \
.SetDisplay("ATR Take Profit", "ATR take-profit multiplier", "Stops")
self._filter_ma_length = self.Param("FilterMaLength", 200) \
.SetGreaterThanZero() \
.SetDisplay("Filter MA Length", "Length of trend filter", "Filter")
self._chop_threshold = self.Param("ChopThreshold", 50.0) \
.SetDisplay("Choppiness Threshold", "Threshold for choppiness index", "Choppiness")
self._chop_length = self.Param("ChopLength", 14) \
.SetGreaterThanZero() \
.SetDisplay("Choppiness Length", "Choppiness index period", "Choppiness")
self._roc_len1 = self.Param("RocLen1", 10) \
.SetGreaterThanZero() \
.SetDisplay("ROC Length 1", "First ROC length", "KST")
self._roc_len2 = self.Param("RocLen2", 15) \
.SetGreaterThanZero() \
.SetDisplay("ROC Length 2", "Second ROC length", "KST")
self._roc_len3 = self.Param("RocLen3", 20) \
.SetGreaterThanZero() \
.SetDisplay("ROC Length 3", "Third ROC length", "KST")
self._roc_len4 = self.Param("RocLen4", 30) \
.SetGreaterThanZero() \
.SetDisplay("ROC Length 4", "Fourth ROC length", "KST")
self._sma_len1 = self.Param("SmaLen1", 10) \
.SetGreaterThanZero() \
.SetDisplay("SMA Length 1", "First SMA length", "KST")
self._sma_len2 = self.Param("SmaLen2", 10) \
.SetGreaterThanZero() \
.SetDisplay("SMA Length 2", "Second SMA length", "KST")
self._sma_len3 = self.Param("SmaLen3", 10) \
.SetGreaterThanZero() \
.SetDisplay("SMA Length 3", "Third SMA length", "KST")
self._sma_len4 = self.Param("SmaLen4", 15) \
.SetGreaterThanZero() \
.SetDisplay("SMA Length 4", "Fourth SMA length", "KST")
self._signal_length = self.Param("SignalLength", 9) \
.SetGreaterThanZero() \
.SetDisplay("Signal Length", "KST signal SMA length", "KST")
self._max_entries = self.Param("MaxEntries", 45) \
.SetGreaterThanZero() \
.SetDisplay("Max Entries", "Maximum entries per run", "Risk")
self._cooldown_bars = self.Param("CooldownBars", 120) \
.SetGreaterThanZero() \
.SetDisplay("Cooldown Bars", "Minimum bars between entries", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_kst = 0.0
self._prev_sig = 0.0
self._stop_loss = 0.0
self._take_profit = 0.0
self._entries_executed = 0
self._bars_since_signal = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(kst_skyrexio_strategy, self).OnReseted()
self._prev_kst = 0.0
self._prev_sig = 0.0
self._stop_loss = 0.0
self._take_profit = 0.0
self._entries_executed = 0
self._bars_since_signal = 0
def OnStarted2(self, time):
super(kst_skyrexio_strategy, self).OnStarted2(time)
self._entries_executed = 0
self._bars_since_signal = self._cooldown_bars.Value
self._roc1 = RateOfChange()
self._roc1.Length = self._roc_len1.Value
self._sma1 = SimpleMovingAverage()
self._sma1.Length = self._sma_len1.Value
self._roc2 = RateOfChange()
self._roc2.Length = self._roc_len2.Value
self._sma2 = SimpleMovingAverage()
self._sma2.Length = self._sma_len2.Value
self._roc3 = RateOfChange()
self._roc3.Length = self._roc_len3.Value
self._sma3 = SimpleMovingAverage()
self._sma3.Length = self._sma_len3.Value
self._roc4 = RateOfChange()
self._roc4.Length = self._roc_len4.Value
self._sma4 = SimpleMovingAverage()
self._sma4.Length = self._sma_len4.Value
self._signal_sma = SimpleMovingAverage()
self._signal_sma.Length = self._signal_length.Value
self._atr = AverageTrueRange()
self._atr.Length = 14
self._chop = ChoppinessIndex()
self._chop.Length = self._chop_length.Value
self._jaw = SmoothedMovingAverage()
self._jaw.Length = 13
self._jaw_shift = Shift()
self._jaw_shift.Length = 8
self._filter_ma = LinearRegressionForecast()
self._filter_ma.Length = self._filter_ma_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._atr, self._chop, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._filter_ma)
self.DrawOwnTrades(area)
def OnProcess(self, candle, atr_val, chop_val):
if candle.State != CandleStates.Finished:
return
self._bars_since_signal += 1
close = float(candle.ClosePrice)
if atr_val.IsEmpty or chop_val.IsEmpty:
return
# Process jaw manually
median = Decimal((float(candle.HighPrice) + float(candle.LowPrice)) / 2.0)
t = candle.OpenTime
jaw_result = self._jaw_shift.Process(process_float(self._jaw, median, t, True))
# Process filter manually
filter_result = process_float(self._filter_ma, candle.ClosePrice, t, True)
if not self._atr.IsFormed or not self._chop.IsFormed or not self._filter_ma.IsFormed or not self._jaw.IsFormed:
return
atr_v = float(atr_val)
chop_v = float(chop_val)
fv = float(filter_result)
jaw_v = float(jaw_result)
# KST calculation
r1 = float(self._sma1.Process(process_float(self._roc1, candle.ClosePrice, t, True)))
r2 = float(self._sma2.Process(process_float(self._roc2, candle.ClosePrice, t, True)))
r3 = float(self._sma3.Process(process_float(self._roc3, candle.ClosePrice, t, True)))
r4 = float(self._sma4.Process(process_float(self._roc4, candle.ClosePrice, t, True)))
if not self._sma1.IsFormed or not self._sma2.IsFormed or not self._sma3.IsFormed or not self._sma4.IsFormed:
return
kst = r1 + 2.0 * r2 + 3.0 * r3 + 4.0 * r4
sig_result = process_float(self._signal_sma, Decimal(kst), t, True)
if not self._signal_sma.IsFormed:
self._prev_kst = kst
return
sig = float(sig_result)
chop_cond = chop_v < float(self._chop_threshold.Value)
cross_up = self._prev_kst <= self._prev_sig and kst > sig
sl_mult = float(self._atr_stop_loss.Value)
tp_mult = float(self._atr_take_profit.Value)
if self._entries_executed < self._max_entries.Value and self._bars_since_signal >= self._cooldown_bars.Value:
if cross_up and close > fv and close > jaw_v and chop_cond and self.Position == 0:
self._stop_loss = float(candle.LowPrice) - sl_mult * atr_v
self._take_profit = close + tp_mult * atr_v
self.BuyMarket()
self._entries_executed += 1
self._bars_since_signal = 0
if self.Position > 0 and self._stop_loss > 0.0 and self._take_profit > 0.0:
if float(candle.LowPrice) <= self._stop_loss or float(candle.HighPrice) >= self._take_profit:
self.SellMarket()
self._stop_loss = 0.0
self._take_profit = 0.0
self._bars_since_signal = 0
self._prev_kst = kst
self._prev_sig = sig
def CreateClone(self):
return kst_skyrexio_strategy()