Esta estrategia traslada el Expert Advisor de MetaTrader Expert_ABE_BE_Stoch al API de alto nivel de StockSharp. Combina el análisis de velas japonesas con la confirmación del impulso para cronometrar las reversiones alrededor de las zonas de sobreventa y sobrecompra. La señal principal busca una vela envolvente alcista respaldada por un oscilador estocástico profundamente sobrevendido, o una vela envolvente bajista confirmada por una lectura de oscilador de sobrecompra. Una vez abierta una posición, la estrategia se basa en cruces de umbrales estocásticos para gestionar las salidas, replicando la mecánica de "voto" del experto original.
La táctica está diseñada para una participación tanto larga como corta. Evalúa sólo velas completas y, por lo tanto, permanece inmune al ruido intrabar. El tamaño de las operaciones permanece bajo el control de la propiedad Volume del marco, mientras que las protecciones opcionales de limitación de pérdidas y toma de ganancias convierten la configuración de riesgo original basada en puntos en objetos StockSharp Unit.
como funciona
Suscripción de datos: la estrategia se suscribe al tipo de vela configurado y crea un StochasticOscillator con tres parámetros ajustables (%K, %D y el factor de desaceleración).
Detección de patrones: en cada vela terminada, el algoritmo comprueba si la última barra envuelve el cuerpo de la anterior. Dos métodos auxiliares reproducen las definiciones envolventes alcistas y bajistas utilizadas en MetaTrader.
Confirmación de impulso: la línea %D del estocástico sirve como filtro de confirmación. Se requieren valores por debajo del umbral de sobreventa (predeterminado 30) para operaciones envolventes alcistas, mientras que valores por encima del umbral de sobrecompra (predeterminado 70) se requieren para señales bajistas.
Gestión de posición: el valor %D anterior se almacena en caché. Si la nueva lectura cruza hacia arriba a través de 20 u 80, cualquier exposición corta se cierra. Por el contrario, los cruces a la baja a través de 80 o 20 liquidan la exposición larga. Estos umbrales reflejan los votos "cerrados" adicionales producidos por la lógica MQL.
Manejo de riesgos: cuando se proporcionan distancias positivas de stop-loss o take-profit (expresadas en incrementos de precio), la estrategia las convierte a UnitTypes.Price y habilita StartProtection. De lo contrario, la protección StockSharp predeterminada se activa con StartProtection().
Reglas comerciales
Entrada larga: la vela anterior es bajista, la vela actual es alcista y el cuerpo de la vela actual envuelve al cuerpo anterior. El valor estocástico %D debe estar por debajo del EntryOversoldLevel (predeterminado 30). Cualquier corto existente se cierra y se abre un nuevo largo a través de BuyMarket.
Entrada breve: La vela anterior es alcista, la vela actual es bajista y el cuerpo de la vela actual envuelve al cuerpo anterior. El valor estocástico %D debe exceder el EntryOverboughtLevel (predeterminado 70). Cualquier posición larga existente se cierra y se abre una nueva posición corta a través de SellMarket.
Salida larga: Con una apertura larga, si %D cruza hacia abajo a través de ExitUpperLevel (80 predeterminado) o ExitLowerLevel (20 predeterminado), la posición se cierra con SellMarket.
Salida corta: Con una apertura corta, si %D cruza hacia arriba a través de ExitLowerLevel o ExitUpperLevel, la posición se cubre usando BuyMarket.
Paradas/objetivos: StopLossPoints y TakeProfitPoints opcionales convierten distancias basadas en puntos en compensaciones de precios absolutos cuando el instrumento expone un valor distinto de cero PriceStep.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
CandleType
DataType
TimeSpan.FromHours(1).TimeFrame()
Fuente de vela utilizada para la detección de patrones.
StochasticPeriodK
int
47
Período retrospectivo para el cálculo rápido %K.
StochasticPeriodD
int
9
Período de suavizado para la línea de señal %D.
StochasticPeriodSlow
int
13
Se aplicó un suavizado adicional a %K antes de que se convierta en %D.
EntryOversoldLevel
decimal
30
Límite superior de %D que permite operaciones envolventes alcistas.
EntryOverboughtLevel
decimal
70
Límite inferior de %D que permite operaciones envolventes bajistas.
ExitLowerLevel
decimal
20
Nivel que, al cruzarse hacia arriba, obliga a salidas cortas; cuando se cruza hacia abajo, cierra largos.
ExitUpperLevel
decimal
80
El límite superior se utiliza de la misma manera que el nivel inferior pero para territorio de sobrecompra.
TakeProfitPoints
decimal
0
Distancia en pasos de precio para la orden de toma de ganancias (0 la desactiva).
StopLossPoints
decimal
0
Distancia en pasos de precio para la orden stop-loss (0 la desactiva).
Notas
Funciona con cualquier instrumento que suministre OHLC velas; los valores predeterminados asumen barras horarias.
Todos los cálculos se basan en velas cerradas para mantenerse alineados con la lógica de marco temporal del experto MQL.
El tamaño de la posición debe configurarse a través de la estrategia base Volume propiedad o gestión de cartera de nivel superior.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// ABE BE Stoch strategy: Engulfing pattern with Stochastic confirmation.
/// Bullish engulfing + oversold stochastic for long, bearish engulfing + overbought for short.
/// </summary>
public class AbeBeStochStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _stochPeriod;
private readonly StrategyParam<decimal> _oversold;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<int> _signalCooldownCandles;
private readonly List<ICandleMessage> _candles = new();
private decimal _prevK;
private bool _hasPrevK;
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int StochPeriod { get => _stochPeriod.Value; set => _stochPeriod.Value = value; }
public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public AbeBeStochStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_stochPeriod = Param(nameof(StochPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Stoch Period", "Stochastic K period", "Indicators");
_oversold = Param(nameof(Oversold), 30m)
.SetDisplay("Oversold", "Stochastic oversold level", "Signals");
_overbought = Param(nameof(Overbought), 70m)
.SetDisplay("Overbought", "Stochastic overbought level", "Signals");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_candles.Clear();
_prevK = 0m;
_hasPrevK = false;
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_candles.Clear();
_hasPrevK = false;
_candlesSinceTrade = SignalCooldownCandles;
var stoch = new StochasticOscillator { K = { Length = StochPeriod }, D = { Length = 3 } };
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(stoch, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
var stochTyped = stochValue as StochasticOscillatorValue;
if (stochTyped?.K is not decimal kValue) return;
_candles.Add(candle);
if (_candles.Count > 5)
_candles.RemoveAt(0);
if (_candles.Count >= 2)
{
var curr = _candles[^1];
var prev = _candles[^2];
// Bullish engulfing: prev bearish, curr bullish, curr body engulfs prev body
var bullishEngulfing = prev.OpenPrice > prev.ClosePrice
&& curr.ClosePrice > curr.OpenPrice
&& curr.OpenPrice <= prev.ClosePrice
&& curr.ClosePrice >= prev.OpenPrice;
// Bearish engulfing: prev bullish, curr bearish, curr body engulfs prev body
var bearishEngulfing = prev.ClosePrice > prev.OpenPrice
&& curr.OpenPrice > curr.ClosePrice
&& curr.OpenPrice >= prev.ClosePrice
&& curr.ClosePrice <= prev.OpenPrice;
if (bullishEngulfing && kValue < Oversold && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (bearishEngulfing && kValue > Overbought && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
// Exit on stochastic cross
if (_hasPrevK)
{
if (Position > 0 && _prevK >= Overbought && kValue < Overbought && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
else if (Position < 0 && _prevK <= Oversold && kValue > Oversold && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
}
_prevK = kValue;
_hasPrevK = true;
}
}
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 abe_be_stoch_strategy(Strategy):
def __init__(self):
super(abe_be_stoch_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._stoch_period = self.Param("StochPeriod", 14)
self._oversold = self.Param("Oversold", 30.0)
self._overbought = self.Param("Overbought", 70.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 6)
self._candles = []
self._prev_k = 0.0
self._has_prev_k = False
self._candles_since_trade = 6
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def StochPeriod(self):
return self._stoch_period.Value
@StochPeriod.setter
def StochPeriod(self, value):
self._stoch_period.Value = value
@property
def Oversold(self):
return self._oversold.Value
@Oversold.setter
def Oversold(self, value):
self._oversold.Value = value
@property
def Overbought(self):
return self._overbought.Value
@Overbought.setter
def Overbought(self, value):
self._overbought.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(abe_be_stoch_strategy, self).OnReseted()
self._candles.clear()
self._prev_k = 0.0
self._has_prev_k = False
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(abe_be_stoch_strategy, self).OnStarted2(time)
self._candles.clear()
self._has_prev_k = False
self._candles_since_trade = self.SignalCooldownCandles
stoch = StochasticOscillator()
stoch.K.Length = self.StochPeriod
stoch.D.Length = 3
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(stoch, self._process_candle).Start()
def _process_candle(self, candle, stoch_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
k_val = stoch_value.K
if k_val is None:
return
k_value = float(k_val)
self._candles.append(candle)
if len(self._candles) > 5:
self._candles.pop(0)
if len(self._candles) >= 2:
curr = self._candles[-1]
prev = self._candles[-2]
bullish_engulfing = (float(prev.OpenPrice) > float(prev.ClosePrice)
and float(curr.ClosePrice) > float(curr.OpenPrice)
and float(curr.OpenPrice) <= float(prev.ClosePrice)
and float(curr.ClosePrice) >= float(prev.OpenPrice))
bearish_engulfing = (float(prev.ClosePrice) > float(prev.OpenPrice)
and float(curr.OpenPrice) > float(curr.ClosePrice)
and float(curr.OpenPrice) >= float(prev.ClosePrice)
and float(curr.ClosePrice) <= float(prev.OpenPrice))
if bullish_engulfing and k_value < self.Oversold and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif bearish_engulfing and k_value > self.Overbought and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
if self._has_prev_k:
if self.Position > 0 and self._prev_k >= self.Overbought and k_value < self.Overbought and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
elif self.Position < 0 and self._prev_k <= self.Oversold and k_value > self.Oversold and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
self._prev_k = k_value
self._has_prev_k = True
def CreateClone(self):
return abe_be_stoch_strategy()