Стохастик с динамическими зонами
Стратегия Stochastic Dynamic Zones основана на стохастическом осцилляторе с динамическими зонами перекупленности и перепроданности.
Тестирование показывает среднегодичную доходность около 52%. Стратегию лучше запускать на крипторынке.
Сигналы формируются, когда стохастик подтверждает изменения тренда на внутридневных данных (5м). Такой метод подходит активным трейдерам.
Стопы рассчитываются на основе кратных ATR и параметров StochPeriod, StochKPeriod. Настройте эти значения для баланса риска и прибыли.
Подробности
- Критерии входа: см. реализацию условий индикаторов.
- Длинные/короткие: обе стороны.
- Критерии выхода: противоположный сигнал или логика стопов.
- Стопы: да, расчёт на основе индикаторов.
- Значения по умолчанию:
StochPeriod = 14StochKPeriod = 3StochDPeriod = 3LookbackPeriod = 20StandardDeviationFactor = 2.0mCandleType = TimeSpan.FromMinutes(5).TimeFrame()
- Фильтры:
- Категория: Следование за трендом
- Направление: Оба
- Индикаторы: Stochastic
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Внутридневной (5м)
- Сезонность: Нет
- Нейронные сети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Stochastic Oscillator with dynamic overbought and oversold zones.
/// The zones are the average of %K over <see cref="LookbackPeriod"/> bars plus/minus
/// <see cref="StandardDeviationFactor"/> standard deviations. Buys when %K crosses above %D inside the oversold zone
/// and sells when %K crosses below %D inside the overbought zone; the opposite signal reverses the position.
/// </summary>
public class StochasticWithDynamicZonesStrategy : Strategy
{
private readonly StrategyParam<int> _stochPeriod;
private readonly StrategyParam<int> _stochKPeriod;
private readonly StrategyParam<int> _stochDPeriod;
private readonly StrategyParam<int> _lookbackPeriod;
private readonly StrategyParam<decimal> _standardDeviationFactor;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _kSmoothing;
private SimpleMovingAverage _dLine;
private SimpleMovingAverage _zoneAverage;
private StandardDeviation _zoneStdDev;
private decimal? _prevK;
private decimal? _prevD;
/// <summary>
/// Stochastic lookback period.
/// </summary>
public int StochPeriod
{
get => _stochPeriod.Value;
set => _stochPeriod.Value = value;
}
/// <summary>
/// Smoothing period of %K.
/// </summary>
public int StochKPeriod
{
get => _stochKPeriod.Value;
set => _stochKPeriod.Value = value;
}
/// <summary>
/// Period of %D.
/// </summary>
public int StochDPeriod
{
get => _stochDPeriod.Value;
set => _stochDPeriod.Value = value;
}
/// <summary>
/// Lookback period for the dynamic zones.
/// </summary>
public int LookbackPeriod
{
get => _lookbackPeriod.Value;
set => _lookbackPeriod.Value = value;
}
/// <summary>
/// Standard deviation factor for the dynamic zones.
/// </summary>
public decimal StandardDeviationFactor
{
get => _standardDeviationFactor.Value;
set => _standardDeviationFactor.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="StochasticWithDynamicZonesStrategy"/>.
/// </summary>
public StochasticWithDynamicZonesStrategy()
{
_stochPeriod = Param(nameof(StochPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Stochastic Period", "Stochastic lookback period", "Indicators");
_stochKPeriod = Param(nameof(StochKPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Stoch %K Period", "Smoothing period of %K", "Indicators");
_stochDPeriod = Param(nameof(StochDPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Stoch %D Period", "Period of %D", "Indicators");
_lookbackPeriod = Param(nameof(LookbackPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Lookback Period", "Period for dynamic zones", "Indicators");
_standardDeviationFactor = Param(nameof(StandardDeviationFactor), 2.0m)
.SetGreaterThanZero()
.SetDisplay("StdDev Factor", "Standard deviation factor for dynamic zones", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_kSmoothing = null;
_dLine = null;
_zoneAverage = null;
_zoneStdDev = null;
_prevK = null;
_prevD = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rawK = new StochasticK { Length = StochPeriod };
_kSmoothing = new SimpleMovingAverage { Length = StochKPeriod };
_dLine = new SimpleMovingAverage { Length = StochDPeriod };
_zoneAverage = new SimpleMovingAverage { Length = LookbackPeriod };
_zoneStdDev = new StandardDeviation { Length = LookbackPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rawK, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rawKValue)
{
if (candle.State != CandleStates.Finished)
return;
var k = _kSmoothing.Process(rawKValue, candle.ServerTime, true).ToDecimal();
if (!_kSmoothing.IsFormed)
return;
var d = _dLine.Process(k, candle.ServerTime, true).ToDecimal();
var average = _zoneAverage.Process(k, candle.ServerTime, true).ToDecimal();
var stdDev = _zoneStdDev.Process(k, candle.ServerTime, true).ToDecimal();
var prevK = _prevK;
var prevD = _prevD;
_prevK = k;
_prevD = d;
if (!_dLine.IsFormed || !_zoneAverage.IsFormed || !_zoneStdDev.IsFormed)
return;
if (prevK is not decimal pk || prevD is not decimal pd)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var oversold = average - StandardDeviationFactor * stdDev;
var overbought = average + StandardDeviationFactor * stdDev;
var crossUp = pk <= pd && k > d;
var crossDown = pk >= pd && k < d;
if (crossUp && k < oversold && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (crossDown && k > overbought && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
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 StochasticK, SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class stochastic_with_dynamic_zones_strategy(Strategy):
"""
Stochastic Oscillator with dynamic overbought and oversold zones.
The zones are the average of %K over LookbackPeriod bars plus/minus StandardDeviationFactor
standard deviations. Buys when %K crosses above %D inside the oversold zone and sells when %K
crosses below %D inside the overbought zone; the opposite signal reverses the position.
"""
def __init__(self):
super(stochastic_with_dynamic_zones_strategy, self).__init__()
self._stoch_period = self.Param("StochPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("Stochastic Period", "Stochastic lookback period", "Indicators")
self._stoch_k_period = self.Param("StochKPeriod", 3) \
.SetGreaterThanZero() \
.SetDisplay("Stoch %K Period", "Smoothing period of %K", "Indicators")
self._stoch_d_period = self.Param("StochDPeriod", 3) \
.SetGreaterThanZero() \
.SetDisplay("Stoch %D Period", "Period of %D", "Indicators")
self._lookback_period = self.Param("LookbackPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Lookback Period", "Period for dynamic zones", "Indicators")
self._standard_deviation_factor = self.Param("StandardDeviationFactor", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("StdDev Factor", "Standard deviation factor for dynamic zones", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._k_smoothing = None
self._d_line = None
self._zone_average = None
self._zone_std_dev = None
self._prev_k = None
self._prev_d = None
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(stochastic_with_dynamic_zones_strategy, self).OnReseted()
self._k_smoothing = None
self._d_line = None
self._zone_average = None
self._zone_std_dev = None
self._prev_k = None
self._prev_d = None
def OnStarted2(self, time):
super(stochastic_with_dynamic_zones_strategy, self).OnStarted2(time)
raw_k = StochasticK()
raw_k.Length = self._stoch_period.Value
self._k_smoothing = SimpleMovingAverage()
self._k_smoothing.Length = self._stoch_k_period.Value
self._d_line = SimpleMovingAverage()
self._d_line.Length = self._stoch_d_period.Value
self._zone_average = SimpleMovingAverage()
self._zone_average.Length = self._lookback_period.Value
self._zone_std_dev = StandardDeviation()
self._zone_std_dev.Length = self._lookback_period.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(raw_k, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle, raw_k_value):
if candle.State != CandleStates.Finished:
return
t = candle.ServerTime
k = float(process_float(self._k_smoothing, raw_k_value, t, True))
if not self._k_smoothing.IsFormed:
return
d = float(process_float(self._d_line, k, t, True))
average = float(process_float(self._zone_average, k, t, True))
std_dev = float(process_float(self._zone_std_dev, k, t, True))
prev_k = self._prev_k
prev_d = self._prev_d
self._prev_k = k
self._prev_d = d
if not self._d_line.IsFormed or not self._zone_average.IsFormed or not self._zone_std_dev.IsFormed:
return
if prev_k is None or prev_d is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
factor = float(self._standard_deviation_factor.Value)
oversold = average - factor * std_dev
overbought = average + factor * std_dev
cross_up = prev_k <= prev_d and k > d
cross_down = prev_k >= prev_d and k < d
if cross_up and k < oversold and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif cross_down and k > overbought and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return stochastic_with_dynamic_zones_strategy()