Стратегия Asimmetric Stoch NR
Стратегия основана на асимметричном стохастическом осцилляторе. Реагирует на пересечения линий %K и %D и допускает защиту позиции.
Метод переключает периоды расчёта %K для адаптации к рыночному шуму. Стоп-лосс и тейк-профит задаются в абсолютных ценовых единицах.
Подробности
- Условия входа:
- Лонг:
%Kпересекает%Dснизу вверх - Шорт:
%Kпересекает%Dсверху вниз
- Лонг:
- Длинные/короткие: обе стороны
- Условия выхода:
- Лонг:
%Kпересекает%Dсверху вниз - Шорт:
%Kпересекает%Dснизу вверх
- Лонг:
- Стопы: абсолютные
StopLossиTakeProfit - Значения по умолчанию:
KPeriodShort= 5KPeriodLong= 12DPeriod= 7Slowing= 3Overbought= 80mOversold= 20mStopLoss= 1000mTakeProfit= 2000mCandleType= TimeSpan.FromHours(4).TimeFrame()
- Фильтры:
- Категория: Осциллятор
- Направление: Оба
- Индикаторы: Стохастик
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Долгосрочный
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on stochastic oscillator crossover with asymmetric periods.
/// The strategy opens or closes positions when %K crosses %D.
/// </summary>
public class AsimmetricStochNrStrategy : Strategy
{
private readonly StrategyParam<int> _kPeriodShort;
private readonly StrategyParam<int> _kPeriodLong;
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<int> _slowing;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<decimal> _oversold;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<bool> _buyOpen;
private readonly StrategyParam<bool> _sellOpen;
private readonly StrategyParam<bool> _buyClose;
private readonly StrategyParam<bool> _sellClose;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevK;
private decimal _prevD;
private bool _isInitialized;
/// <summary>
/// Constructor.
/// </summary>
public AsimmetricStochNrStrategy()
{
_kPeriodShort = Param(nameof(KPeriodShort), 5)
.SetGreaterThanZero()
.SetDisplay("Short %K period", "Fast %K period for asymmetric calculation", "Indicator");
_kPeriodLong = Param(nameof(KPeriodLong), 12)
.SetGreaterThanZero()
.SetDisplay("Long %K period", "Slow %K period for asymmetric calculation", "Indicator");
_dPeriod = Param(nameof(DPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("%D period", "Smoothing period for signal line", "Indicator");
_slowing = Param(nameof(Slowing), 3)
.SetGreaterThanZero()
.SetDisplay("Slowing", "Smoothing of %K line", "Indicator");
_overbought = Param(nameof(Overbought), 80m)
.SetDisplay("Overbought", "Overbought level", "Indicator");
_oversold = Param(nameof(Oversold), 20m)
.SetDisplay("Oversold", "Oversold level", "Indicator");
_stopLoss = Param(nameof(StopLoss), 1000m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk");
_takeProfit = Param(nameof(TakeProfit), 2000m)
.SetNotNegative()
.SetDisplay("Take Profit", "Take profit in price units", "Risk");
_buyOpen = Param(nameof(BuyOpen), true)
.SetDisplay("Allow Buy", "Allow opening long positions", "General");
_sellOpen = Param(nameof(SellOpen), true)
.SetDisplay("Allow Sell", "Allow opening short positions", "General");
_buyClose = Param(nameof(BuyClose), true)
.SetDisplay("Close Long", "Allow closing long positions", "General");
_sellClose = Param(nameof(SellClose), true)
.SetDisplay("Close Short", "Allow closing short positions", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for indicator calculation", "General");
}
/// <summary>
/// Short %K period.
/// </summary>
public int KPeriodShort
{
get => _kPeriodShort.Value;
set => _kPeriodShort.Value = value;
}
/// <summary>
/// Long %K period.
/// </summary>
public int KPeriodLong
{
get => _kPeriodLong.Value;
set => _kPeriodLong.Value = value;
}
/// <summary>
/// %D smoothing period.
/// </summary>
public int DPeriod
{
get => _dPeriod.Value;
set => _dPeriod.Value = value;
}
/// <summary>
/// Slowing value for %K.
/// </summary>
public int Slowing
{
get => _slowing.Value;
set => _slowing.Value = value;
}
/// <summary>
/// Overbought level.
/// </summary>
public decimal Overbought
{
get => _overbought.Value;
set => _overbought.Value = value;
}
/// <summary>
/// Oversold level.
/// </summary>
public decimal Oversold
{
get => _oversold.Value;
set => _oversold.Value = value;
}
/// <summary>
/// Stop loss in price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Permission to open long positions.
/// </summary>
public bool BuyOpen
{
get => _buyOpen.Value;
set => _buyOpen.Value = value;
}
/// <summary>
/// Permission to open short positions.
/// </summary>
public bool SellOpen
{
get => _sellOpen.Value;
set => _sellOpen.Value = value;
}
/// <summary>
/// Permission to close long positions.
/// </summary>
public bool BuyClose
{
get => _buyClose.Value;
set => _buyClose.Value = value;
}
/// <summary>
/// Permission to close short positions.
/// </summary>
public bool SellClose
{
get => _sellClose.Value;
set => _sellClose.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevK = 0m;
_prevD = 0m;
_isInitialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var stochastic = new StochasticOscillator();
stochastic.K.Length = KPeriodLong;
stochastic.D.Length = DPeriod;
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(stochastic, ProcessStochastic)
.Start();
StartProtection(
new Unit(TakeProfit, UnitTypes.Absolute),
new Unit(StopLoss, UnitTypes.Absolute));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, stochastic);
DrawOwnTrades(area);
}
}
private void ProcessStochastic(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var value = (StochasticOscillatorValue)stochValue;
if (value.K is not decimal k || value.D is not decimal d)
return;
if (!_isInitialized)
{
_prevK = k;
_prevD = d;
_isInitialized = true;
return;
}
var crossUp = _prevK < _prevD && k > d;
var crossDown = _prevK > _prevD && k < d;
var isOversold = k <= Oversold || d <= Oversold;
var isOverbought = k >= Overbought || d >= Overbought;
if (crossUp && isOversold)
{
if (BuyOpen && Position <= 0)
BuyMarket(Position < 0 ? Volume + Math.Abs(Position) : Volume);
else if (SellClose && Position < 0)
BuyMarket(Math.Abs(Position));
}
else if (crossDown && isOverbought)
{
if (SellOpen && Position >= 0)
SellMarket(Position > 0 ? Volume + Position : Volume);
else if (BuyClose && Position > 0)
SellMarket(Math.Abs(Position));
}
_prevK = k;
_prevD = d;
}
}
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, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class asimmetric_stoch_nr_strategy(Strategy):
def __init__(self):
super(asimmetric_stoch_nr_strategy, self).__init__()
self._k_period_short = self.Param("KPeriodShort", 5) \
.SetDisplay("Short %K period", "Fast %K period for asymmetric calculation", "Indicator")
self._k_period_long = self.Param("KPeriodLong", 12) \
.SetDisplay("Long %K period", "Slow %K period for asymmetric calculation", "Indicator")
self._d_period = self.Param("DPeriod", 7) \
.SetDisplay("%D period", "Smoothing period for signal line", "Indicator")
self._slowing = self.Param("Slowing", 3) \
.SetDisplay("Slowing", "Smoothing of %K line", "Indicator")
self._overbought = self.Param("Overbought", 80.0) \
.SetDisplay("Overbought", "Overbought level", "Indicator")
self._oversold = self.Param("Oversold", 20.0) \
.SetDisplay("Oversold", "Oversold level", "Indicator")
self._stop_loss = self.Param("StopLoss", 1000.0) \
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk")
self._take_profit = self.Param("TakeProfit", 2000.0) \
.SetDisplay("Take Profit", "Take profit in price units", "Risk")
self._buy_open = self.Param("BuyOpen", True) \
.SetDisplay("Allow Buy", "Allow opening long positions", "General")
self._sell_open = self.Param("SellOpen", True) \
.SetDisplay("Allow Sell", "Allow opening short positions", "General")
self._buy_close = self.Param("BuyClose", True) \
.SetDisplay("Close Long", "Allow closing long positions", "General")
self._sell_close = self.Param("SellClose", True) \
.SetDisplay("Close Short", "Allow closing short positions", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for indicator calculation", "General")
self._prev_k = 0.0
self._prev_d = 0.0
self._is_initialized = False
@property
def KPeriodShort(self):
return self._k_period_short.Value
@KPeriodShort.setter
def KPeriodShort(self, value):
self._k_period_short.Value = value
@property
def KPeriodLong(self):
return self._k_period_long.Value
@KPeriodLong.setter
def KPeriodLong(self, value):
self._k_period_long.Value = value
@property
def DPeriod(self):
return self._d_period.Value
@DPeriod.setter
def DPeriod(self, value):
self._d_period.Value = value
@property
def Slowing(self):
return self._slowing.Value
@Slowing.setter
def Slowing(self, value):
self._slowing.Value = value
@property
def Overbought(self):
return self._overbought.Value
@Overbought.setter
def Overbought(self, value):
self._overbought.Value = value
@property
def Oversold(self):
return self._oversold.Value
@Oversold.setter
def Oversold(self, value):
self._oversold.Value = 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
@property
def BuyOpen(self):
return self._buy_open.Value
@BuyOpen.setter
def BuyOpen(self, value):
self._buy_open.Value = value
@property
def SellOpen(self):
return self._sell_open.Value
@SellOpen.setter
def SellOpen(self, value):
self._sell_open.Value = value
@property
def BuyClose(self):
return self._buy_close.Value
@BuyClose.setter
def BuyClose(self, value):
self._buy_close.Value = value
@property
def SellClose(self):
return self._sell_close.Value
@SellClose.setter
def SellClose(self, value):
self._sell_close.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(asimmetric_stoch_nr_strategy, self).OnStarted2(time)
stochastic = StochasticOscillator()
stochastic.K.Length = self.KPeriodLong
stochastic.D.Length = self.DPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.BindEx(stochastic, self.ProcessCandle) \
.Start()
self.StartProtection(
Unit(self.TakeProfit, UnitTypes.Absolute),
Unit(self.StopLoss, UnitTypes.Absolute))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, stochastic)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, stoch_value):
if candle.State != CandleStates.Finished:
return
k_raw = stoch_value.K
d_raw = stoch_value.D
if k_raw is None or d_raw is None:
return
k = float(k_raw)
d = float(d_raw)
if not self._is_initialized:
self._prev_k = k
self._prev_d = d
self._is_initialized = True
return
cross_up = self._prev_k < self._prev_d and k > d
cross_down = self._prev_k > self._prev_d and k < d
is_oversold = k <= float(self.Oversold) or d <= float(self.Oversold)
is_overbought = k >= float(self.Overbought) or d >= float(self.Overbought)
if cross_up and is_oversold:
if self.BuyOpen and self.Position <= 0:
volume = self.Volume + abs(self.Position) if self.Position < 0 else self.Volume
self.BuyMarket(volume)
elif self.SellClose and self.Position < 0:
self.BuyMarket(abs(self.Position))
elif cross_down and is_overbought:
if self.SellOpen and self.Position >= 0:
volume = self.Volume + self.Position if self.Position > 0 else self.Volume
self.SellMarket(volume)
elif self.BuyClose and self.Position > 0:
self.SellMarket(abs(self.Position))
self._prev_k = k
self._prev_d = d
def OnReseted(self):
super(asimmetric_stoch_nr_strategy, self).OnReseted()
self._prev_k = 0.0
self._prev_d = 0.0
self._is_initialized = False
def CreateClone(self):
return asimmetric_stoch_nr_strategy()