Esta estrategia es un puerto StockSharp del experto MQL original Exp_Stochastic_Histogram.
Utiliza el oscilador Stochastic para producir señales de trading contrarias en dos modos:
Levels – aparece una señal cuando %K sale de las áreas de sobrecompra o sobreventa definidas por HighLevel y LowLevel.
Cross – aparece una señal cuando %K cruza la línea %D. La operación se abre en la dirección opuesta al cruce.
Siempre que se recibe una nueva señal, la estrategia cierra la posición existente y abre una nueva en la dirección requerida.
Parámetros
KPeriod – período principal de %K.
DPeriod – período de suavizado de %D.
Slowing – suavizado adicional de %K.
HighLevel – umbral superior para el modo Levels.
LowLevel – umbral inferior para el modo Levels.
Mode – Levels o Cross.
CandleType – marco temporal de velas utilizado para los cálculos.
Cómo funciona
Para cada vela completada, el oscilador Stochastic se actualiza y evalúa. En el modo Levels se abre una operación larga cuando %K regresa por debajo del nivel alto, y una operación corta cuando %K sube por encima del nivel bajo. En el modo Cross se abre una operación larga en cruces descendentes de %K por debajo de %D, mientras que los cruces ascendentes desencadenan operaciones cortas. La estrategia tiene como máximo una posición abierta en todo momento.
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>
/// Stochastic-based strategy with crossover mode.
/// Generates contrarian signals when K crosses D.
/// </summary>
public class StochasticHistogramStrategy : Strategy
{
private readonly StrategyParam<int> _kPeriod;
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<decimal> _highLevel;
private readonly StrategyParam<decimal> _lowLevel;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevK;
private decimal? _prevD;
public int KPeriod { get => _kPeriod.Value; set => _kPeriod.Value = value; }
public int DPeriod { get => _dPeriod.Value; set => _dPeriod.Value = value; }
public decimal HighLevel { get => _highLevel.Value; set => _highLevel.Value = value; }
public decimal LowLevel { get => _lowLevel.Value; set => _lowLevel.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public StochasticHistogramStrategy()
{
_kPeriod = Param(nameof(KPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("K Period", "Stochastic %K period", "Stochastic");
_dPeriod = Param(nameof(DPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("D Period", "Stochastic %D period", "Stochastic");
_highLevel = Param(nameof(HighLevel), 80m)
.SetDisplay("High Level", "Upper threshold", "Stochastic");
_lowLevel = Param(nameof(LowLevel), 20m)
.SetDisplay("Low Level", "Lower threshold", "Stochastic");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for calculation", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevK = null;
_prevD = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var stoch = new StochasticOscillator();
stoch.K.Length = KPeriod;
stoch.D.Length = DPeriod;
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(stoch, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, stoch);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished)
return;
var sv = stochValue as IStochasticOscillatorValue;
if (sv?.K is not decimal k || sv?.D is not decimal d)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevK = k;
_prevD = d;
return;
}
if (_prevK is decimal pk && _prevD is decimal pd)
{
// K crosses above D in oversold zone - buy
if (pk <= pd && k > d && k < LowLevel && Position <= 0)
BuyMarket();
// K crosses below D in overbought zone - sell
else if (pk >= pd && k < d && k > HighLevel && Position >= 0)
SellMarket();
}
_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
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class stochastic_histogram_strategy(Strategy):
def __init__(self):
super(stochastic_histogram_strategy, self).__init__()
self._k_period = self.Param("KPeriod", 5) \
.SetDisplay("K Period", "Stochastic %K period", "Stochastic")
self._d_period = self.Param("DPeriod", 3) \
.SetDisplay("D Period", "Stochastic %D period", "Stochastic")
self._high_level = self.Param("HighLevel", 80.0) \
.SetDisplay("High Level", "Upper threshold", "Stochastic")
self._low_level = self.Param("LowLevel", 20.0) \
.SetDisplay("Low Level", "Lower threshold", "Stochastic")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for calculation", "General")
self._prev_k = None
self._prev_d = None
@property
def k_period(self):
return self._k_period.Value
@property
def d_period(self):
return self._d_period.Value
@property
def high_level(self):
return self._high_level.Value
@property
def low_level(self):
return self._low_level.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(stochastic_histogram_strategy, self).OnReseted()
self._prev_k = None
self._prev_d = None
def OnStarted2(self, time):
super(stochastic_histogram_strategy, self).OnStarted2(time)
stoch = StochasticOscillator()
stoch.K.Length = self.k_period
stoch.D.Length = self.d_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(stoch, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, stoch)
self.DrawOwnTrades(area)
def process_candle(self, candle, stoch_value):
if candle.State != CandleStates.Finished:
return
if not stoch_value.IsFormed:
return
k = stoch_value.K
d = stoch_value.D
if k is None or d is None:
return
k = float(k)
d = float(d)
if self._prev_k is not None and self._prev_d is not None:
pk = self._prev_k
pd = self._prev_d
if pk <= pd and k > d and k < float(self.low_level) and self.Position <= 0:
self.BuyMarket()
elif pk >= pd and k < d and k > float(self.high_level) and self.Position >= 0:
self.SellMarket()
self._prev_k = k
self._prev_d = d
def CreateClone(self):
return stochastic_histogram_strategy()