Стратегия Triple Stochastic MTF
Эта стратегия использует три стохастических осциллятора на разных таймфреймах и открывает сделки, когда на младшем таймфрейме происходит пересечение %K и %D в направлении, подтверждённом старшими таймфреймами. Цель — поймать краткосрочные откаты в рамках общего тренда.
Основной таймфрейм (по умолчанию 30 минут) и вспомогательный таймфрейм (15 минут) задают направление рынка. Входной таймфрейм (5 минут) ждёт обратного пересечения %K и %D относительно предыдущей свечи, что считается сигналом отката. Позиции закрываются, когда любой из отслеживаемых таймфреймов показывает смену тренда против открытой позиции.
Детали
- Критерии входа:
- Длинная позиция: Предыдущее значение %K > %D на 5-минутном графике, текущее %K ≤ %D и оба старших таймфрейма показывают %K > %D.
- Короткая позиция: Предыдущее значение %K < %D на 5-минутном графике, текущее %K ≥ %D и оба старших таймфрейма показывают %K < %D.
- Длинные/короткие: обе стороны.
- Критерии выхода:
- Длинная позиция: Любой таймфрейм переходит в нисходящий тренд (%K < %D).
- Короткая позиция: Любой таймфрейм переходит в восходящий тренд (%K > %D).
- Стопы: По умолчанию не используются.
- Значения по умолчанию:
Timeframe 1= 30 минут.Timeframe 2= 15 минут.Timeframe 3= 5 минут.%K Period= 5.%D Period= 3.Slowing= 3.
- Фильтры:
- Категория: Следование тренду / Откат
- Направление: Оба
- Индикаторы: Stochastic Oscillator
- Стопы: Нет
- Сложность: Средняя
- Таймфрейм: Краткосрочный
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// Triple Stochastic strategy operating on three timeframes.
/// Opens positions when the fast stochastic crosses the signal line on the smallest timeframe while higher timeframes
/// confirm the trend.
/// </summary>
public class Exp3StoStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType1;
private readonly StrategyParam<DataType> _candleType2;
private readonly StrategyParam<DataType> _candleType3;
private readonly StrategyParam<int> _kPeriod;
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<int> _slowing;
private readonly StrategyParam<bool> _buyPosOpen;
private readonly StrategyParam<bool> _sellPosOpen;
private readonly StrategyParam<bool> _buyPosClose1;
private readonly StrategyParam<bool> _sellPosClose1;
private readonly StrategyParam<bool> _buyPosClose2;
private readonly StrategyParam<bool> _sellPosClose2;
private readonly StrategyParam<bool> _buyPosClose3;
private readonly StrategyParam<bool> _sellPosClose3;
private StochasticOscillator _stoch1 = null!;
private StochasticOscillator _stoch2 = null!;
private StochasticOscillator _stoch3 = null!;
private int _trend1;
private int _trend2;
private int _trend3;
private decimal? _prevK3;
private decimal? _prevD3;
private bool _buyOpenSignal;
private bool _sellOpenSignal;
private bool _buyCloseSignal;
private bool _sellCloseSignal;
/// <summary>
/// First timeframe for stochastic indicator.
/// </summary>
public DataType CandleType1
{
get => _candleType1.Value;
set => _candleType1.Value = value;
}
/// <summary>
/// Second timeframe for stochastic indicator.
/// </summary>
public DataType CandleType2
{
get => _candleType2.Value;
set => _candleType2.Value = value;
}
/// <summary>
/// Third timeframe for stochastic indicator.
/// </summary>
public DataType CandleType3
{
get => _candleType3.Value;
set => _candleType3.Value = value;
}
/// <summary>
/// %K period.
/// </summary>
public int KPeriod
{
get => _kPeriod.Value;
set => _kPeriod.Value = value;
}
/// <summary>
/// %D period.
/// </summary>
public int DPeriod
{
get => _dPeriod.Value;
set => _dPeriod.Value = value;
}
/// <summary>
/// Smoothing factor for %K line.
/// </summary>
public int Slowing
{
get => _slowing.Value;
set => _slowing.Value = value;
}
/// <summary>
/// Allow opening long positions.
/// </summary>
public bool BuyPosOpen
{
get => _buyPosOpen.Value;
set => _buyPosOpen.Value = value;
}
/// <summary>
/// Allow opening short positions.
/// </summary>
public bool SellPosOpen
{
get => _sellPosOpen.Value;
set => _sellPosOpen.Value = value;
}
/// <summary>
/// Close shorts when first timeframe indicates uptrend.
/// </summary>
public bool SellPosClose1
{
get => _sellPosClose1.Value;
set => _sellPosClose1.Value = value;
}
/// <summary>
/// Close longs when first timeframe indicates downtrend.
/// </summary>
public bool BuyPosClose1
{
get => _buyPosClose1.Value;
set => _buyPosClose1.Value = value;
}
/// <summary>
/// Close shorts when second timeframe indicates uptrend.
/// </summary>
public bool SellPosClose2
{
get => _sellPosClose2.Value;
set => _sellPosClose2.Value = value;
}
/// <summary>
/// Close longs when second timeframe indicates downtrend.
/// </summary>
public bool BuyPosClose2
{
get => _buyPosClose2.Value;
set => _buyPosClose2.Value = value;
}
/// <summary>
/// Close shorts when third timeframe indicates uptrend.
/// </summary>
public bool SellPosClose3
{
get => _sellPosClose3.Value;
set => _sellPosClose3.Value = value;
}
/// <summary>
/// Close longs when third timeframe indicates downtrend.
/// </summary>
public bool BuyPosClose3
{
get => _buyPosClose3.Value;
set => _buyPosClose3.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public Exp3StoStrategy()
{
_candleType1 = Param(nameof(CandleType1), TimeSpan.FromDays(1).TimeFrame())
.SetDisplay("Timeframe 1", "Higher timeframe", "General");
_candleType2 = Param(nameof(CandleType2), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Timeframe 2", "Middle timeframe", "General");
_candleType3 = Param(nameof(CandleType3), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Timeframe 3", "Lower timeframe", "General");
_kPeriod = Param(nameof(KPeriod), 5).SetGreaterThanZero().SetDisplay("%K Period", "Length of %K", "Stochastic");
_dPeriod = Param(nameof(DPeriod), 3).SetGreaterThanZero().SetDisplay("%D Period", "Length of %D", "Stochastic");
_slowing = Param(nameof(Slowing), 3).SetGreaterThanZero().SetDisplay("Slowing", "%K smoothing", "Stochastic");
_buyPosOpen =
Param(nameof(BuyPosOpen), true).SetDisplay("Enable Long", "Allow opening long positions", "Signals");
_sellPosOpen =
Param(nameof(SellPosOpen), true).SetDisplay("Enable Short", "Allow opening short positions", "Signals");
_buyPosClose1 =
Param(nameof(BuyPosClose1), false).SetDisplay("Close Long TF1", "Close longs if TF1 down", "Signals");
_sellPosClose1 =
Param(nameof(SellPosClose1), false).SetDisplay("Close Short TF1", "Close shorts if TF1 up", "Signals");
_buyPosClose2 =
Param(nameof(BuyPosClose2), false).SetDisplay("Close Long TF2", "Close longs if TF2 down", "Signals");
_sellPosClose2 =
Param(nameof(SellPosClose2), false).SetDisplay("Close Short TF2", "Close shorts if TF2 up", "Signals");
_buyPosClose3 =
Param(nameof(BuyPosClose3), false).SetDisplay("Close Long TF3", "Close longs if TF3 down", "Signals");
_sellPosClose3 =
Param(nameof(SellPosClose3), false).SetDisplay("Close Short TF3", "Close shorts if TF3 up", "Signals");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType1), (Security, CandleType2), (Security, CandleType3)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_stoch1 = null!;
_stoch2 = null!;
_stoch3 = null!;
_trend1 = _trend2 = _trend3 = 0;
_prevK3 = _prevD3 = null;
_buyOpenSignal = _sellOpenSignal = false;
_buyCloseSignal = _sellCloseSignal = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_stoch1 = new StochasticOscillator();
_stoch1.K.Length = KPeriod;
_stoch1.D.Length = DPeriod;
_stoch2 = new StochasticOscillator();
_stoch2.K.Length = KPeriod;
_stoch2.D.Length = DPeriod;
_stoch3 = new StochasticOscillator();
_stoch3.K.Length = KPeriod;
_stoch3.D.Length = DPeriod;
var sub1 = SubscribeCandles(CandleType1);
sub1.BindEx(_stoch1, ProcessTf1).Start();
var sub2 = SubscribeCandles(CandleType2);
sub2.BindEx(_stoch2, ProcessTf2).Start();
var sub3 = SubscribeCandles(CandleType3);
sub3.BindEx(_stoch3, ProcessTf3).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, sub3);
DrawIndicator(area, _stoch3);
DrawOwnTrades(area);
}
}
private void ProcessTf1(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished)
return;
var stoch = (StochasticOscillatorValue)stochValue;
if (stoch.K is not decimal k || stoch.D is not decimal d)
return;
_trend1 = 0;
if (k > d && BuyPosOpen)
_trend1 = 1;
else if (k < d && SellPosOpen)
_trend1 = -1;
UpdateSignals();
}
private void ProcessTf2(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished)
return;
var stoch = (StochasticOscillatorValue)stochValue;
if (stoch.K is not decimal k || stoch.D is not decimal d)
return;
_trend2 = 0;
if (k > d && BuyPosOpen)
_trend2 = 1;
else if (k < d && SellPosOpen)
_trend2 = -1;
UpdateSignals();
}
private void ProcessTf3(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished)
return;
var stoch = (StochasticOscillatorValue)stochValue;
if (stoch.K is not decimal k || stoch.D is not decimal d)
return;
if (_prevK3 is not decimal pk || _prevD3 is not decimal pd)
{
_prevK3 = k;
_prevD3 = d;
return;
}
_trend3 = 0;
if (pk > pd)
{
_trend3 = 1;
if (BuyPosOpen && k <= d && _trend1 > 0 && _trend2 > 0)
_buyOpenSignal = true;
}
else if (pk < pd)
{
_trend3 = -1;
if (SellPosOpen && k >= d && _trend1 < 0 && _trend2 < 0)
_sellOpenSignal = true;
}
_prevK3 = k;
_prevD3 = d;
UpdateSignals();
}
private void UpdateSignals()
{
_buyCloseSignal = false;
_sellCloseSignal = false;
if (_trend1 > 0 && SellPosClose1)
_sellCloseSignal = true;
if (_trend1 < 0 && BuyPosClose1)
_buyCloseSignal = true;
if (_trend2 > 0 && SellPosClose2)
_sellCloseSignal = true;
if (_trend2 < 0 && BuyPosClose2)
_buyCloseSignal = true;
if (_trend3 > 0 && SellPosClose3)
_sellCloseSignal = true;
if (_trend3 < 0 && BuyPosClose3)
_buyCloseSignal = true;
ExecuteTrades();
}
private void ExecuteTrades()
{
if (_buyOpenSignal && Position <= 0)
{
BuyMarket();
_buyOpenSignal = false;
}
else if (_sellOpenSignal && Position >= 0)
{
SellMarket();
_sellOpenSignal = false;
}
else if (_buyCloseSignal && Position > 0)
{
SellMarket();
}
else if (_sellCloseSignal && Position < 0)
{
BuyMarket();
}
_buyCloseSignal = false;
_sellCloseSignal = false;
}
}
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 StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class exp3_sto_strategy(Strategy):
def __init__(self):
super(exp3_sto_strategy, self).__init__()
self._candle_type1 = self.Param("CandleType1", DataType.TimeFrame(TimeSpan.FromDays(1)))
self._candle_type2 = self.Param("CandleType2", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._candle_type3 = self.Param("CandleType3", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._k_period = self.Param("KPeriod", 5)
self._d_period = self.Param("DPeriod", 3)
self._slowing = self.Param("Slowing", 3)
self._buy_pos_open = self.Param("BuyPosOpen", True)
self._sell_pos_open = self.Param("SellPosOpen", True)
self._buy_pos_close1 = self.Param("BuyPosClose1", False)
self._sell_pos_close1 = self.Param("SellPosClose1", False)
self._buy_pos_close2 = self.Param("BuyPosClose2", False)
self._sell_pos_close2 = self.Param("SellPosClose2", False)
self._buy_pos_close3 = self.Param("BuyPosClose3", False)
self._sell_pos_close3 = self.Param("SellPosClose3", False)
self._trend1 = 0
self._trend2 = 0
self._trend3 = 0
self._prev_k3 = None
self._prev_d3 = None
self._buy_open_signal = False
self._sell_open_signal = False
self._buy_close_signal = False
self._sell_close_signal = False
@property
def CandleType1(self): return self._candle_type1.Value
@CandleType1.setter
def CandleType1(self, v): self._candle_type1.Value = v
@property
def CandleType2(self): return self._candle_type2.Value
@CandleType2.setter
def CandleType2(self, v): self._candle_type2.Value = v
@property
def CandleType3(self): return self._candle_type3.Value
@CandleType3.setter
def CandleType3(self, v): self._candle_type3.Value = v
@property
def KPeriod(self): return self._k_period.Value
@KPeriod.setter
def KPeriod(self, v): self._k_period.Value = v
@property
def DPeriod(self): return self._d_period.Value
@DPeriod.setter
def DPeriod(self, v): self._d_period.Value = v
@property
def Slowing(self): return self._slowing.Value
@Slowing.setter
def Slowing(self, v): self._slowing.Value = v
@property
def BuyPosOpen(self): return self._buy_pos_open.Value
@BuyPosOpen.setter
def BuyPosOpen(self, v): self._buy_pos_open.Value = v
@property
def SellPosOpen(self): return self._sell_pos_open.Value
@SellPosOpen.setter
def SellPosOpen(self, v): self._sell_pos_open.Value = v
@property
def BuyPosClose1(self): return self._buy_pos_close1.Value
@BuyPosClose1.setter
def BuyPosClose1(self, v): self._buy_pos_close1.Value = v
@property
def SellPosClose1(self): return self._sell_pos_close1.Value
@SellPosClose1.setter
def SellPosClose1(self, v): self._sell_pos_close1.Value = v
@property
def BuyPosClose2(self): return self._buy_pos_close2.Value
@BuyPosClose2.setter
def BuyPosClose2(self, v): self._buy_pos_close2.Value = v
@property
def SellPosClose2(self): return self._sell_pos_close2.Value
@SellPosClose2.setter
def SellPosClose2(self, v): self._sell_pos_close2.Value = v
@property
def BuyPosClose3(self): return self._buy_pos_close3.Value
@BuyPosClose3.setter
def BuyPosClose3(self, v): self._buy_pos_close3.Value = v
@property
def SellPosClose3(self): return self._sell_pos_close3.Value
@SellPosClose3.setter
def SellPosClose3(self, v): self._sell_pos_close3.Value = v
def OnStarted2(self, time):
super(exp3_sto_strategy, self).OnStarted2(time)
self._stoch1 = StochasticOscillator()
self._stoch1.K.Length = self.KPeriod
self._stoch1.D.Length = self.DPeriod
self._stoch2 = StochasticOscillator()
self._stoch2.K.Length = self.KPeriod
self._stoch2.D.Length = self.DPeriod
self._stoch3 = StochasticOscillator()
self._stoch3.K.Length = self.KPeriod
self._stoch3.D.Length = self.DPeriod
sub1 = self.SubscribeCandles(self.CandleType1)
sub1.BindEx(self._stoch1, self.ProcessTf1).Start()
sub2 = self.SubscribeCandles(self.CandleType2)
sub2.BindEx(self._stoch2, self.ProcessTf2).Start()
sub3 = self.SubscribeCandles(self.CandleType3)
sub3.BindEx(self._stoch3, self.ProcessTf3).Start()
def ProcessTf1(self, candle, sv):
if candle.State != CandleStates.Finished: return
if sv.K is None or sv.D is None: return
k, d = float(sv.K), float(sv.D)
self._trend1 = 0
if k > d and self.BuyPosOpen: self._trend1 = 1
elif k < d and self.SellPosOpen: self._trend1 = -1
self._update_signals()
def ProcessTf2(self, candle, sv):
if candle.State != CandleStates.Finished: return
if sv.K is None or sv.D is None: return
k, d = float(sv.K), float(sv.D)
self._trend2 = 0
if k > d and self.BuyPosOpen: self._trend2 = 1
elif k < d and self.SellPosOpen: self._trend2 = -1
self._update_signals()
def ProcessTf3(self, candle, sv):
if candle.State != CandleStates.Finished: return
if sv.K is None or sv.D is None: return
k, d = float(sv.K), float(sv.D)
if self._prev_k3 is None or self._prev_d3 is None:
self._prev_k3 = k
self._prev_d3 = d
return
pk, pdv = self._prev_k3, self._prev_d3
self._trend3 = 0
if pk > pdv:
self._trend3 = 1
if self.BuyPosOpen and k <= d and self._trend1 > 0 and self._trend2 > 0:
self._buy_open_signal = True
elif pk < pdv:
self._trend3 = -1
if self.SellPosOpen and k >= d and self._trend1 < 0 and self._trend2 < 0:
self._sell_open_signal = True
self._prev_k3 = k
self._prev_d3 = d
self._update_signals()
def _update_signals(self):
self._buy_close_signal = False
self._sell_close_signal = False
if self._trend1 > 0 and self.SellPosClose1: self._sell_close_signal = True
if self._trend1 < 0 and self.BuyPosClose1: self._buy_close_signal = True
if self._trend2 > 0 and self.SellPosClose2: self._sell_close_signal = True
if self._trend2 < 0 and self.BuyPosClose2: self._buy_close_signal = True
if self._trend3 > 0 and self.SellPosClose3: self._sell_close_signal = True
if self._trend3 < 0 and self.BuyPosClose3: self._buy_close_signal = True
self._execute_trades()
def _execute_trades(self):
if self._buy_open_signal and self.Position <= 0:
self.BuyMarket()
self._buy_open_signal = False
elif self._sell_open_signal and self.Position >= 0:
self.SellMarket()
self._sell_open_signal = False
elif self._buy_close_signal and self.Position > 0:
self.SellMarket()
elif self._sell_close_signal and self.Position < 0:
self.BuyMarket()
self._buy_close_signal = False
self._sell_close_signal = False
def OnReseted(self):
super(exp3_sto_strategy, self).OnReseted()
self._trend1 = 0
self._trend2 = 0
self._trend3 = 0
self._prev_k3 = None
self._prev_d3 = None
self._buy_open_signal = False
self._sell_open_signal = False
self._buy_close_signal = False
self._sell_close_signal = False
def CreateClone(self):
return exp3_sto_strategy()