Esta estratégia transporta o MetaTrader Expert Advisor Expert_ABE_BE_Stoch para o StockSharp API de alto nível. Ele combina análise de velas japonesas com confirmação de impulso para reversões de tempo em torno de zonas de sobrevenda e sobrecompra. O sinal primário procura uma vela envolvente de alta apoiada por um oscilador estocástico profundamente sobrevendido, ou uma vela envolvente de baixa confirmada por uma leitura do oscilador sobrecomprado. Uma vez aberta uma posição, a estratégia depende de cruzamentos de limites estocásticos para gerenciar as saídas, replicando a mecânica de “voto” do especialista original.
A tática é projetada para participação longa e curta. Ele avalia apenas velas concluídas e, portanto, permanece imune a ruídos intrabarras. O dimensionamento da negociação permanece sob o controle da propriedade Volume da estrutura, enquanto as proteções opcionais de stop-loss e take-profit convertem as configurações de risco originais baseadas em pontos em objetos StockSharp Unit.
Como funciona
Assinatura de dados – A estratégia assina o tipo de vela configurado e constrói um StochasticOscillator com três parâmetros ajustáveis (%K, %D e o fator de lentidão).
Detecção de padrão – Em cada vela finalizada, o algoritmo verifica se a última barra engole o corpo da anterior. Dois métodos auxiliares reproduzem definições envolventes de alta e baixa usadas em MetaTrader.
Confirmação de impulso – A linha %D do estocástico serve como filtro de confirmação. Valores abaixo do limite de sobrevenda (padrão 30) são necessários para negociações envolventes de alta, enquanto valores acima do limite de sobrecompra (padrão 70) são necessários para sinais de baixa.
Gerenciamento de posição – O valor %D anterior é armazenado em cache. Se a nova leitura ultrapassar 20 ou 80, qualquer exposição curta será fechada. Por outro lado, cruzamentos descendentes através de 80 ou 20 liquidam a exposição longa. Esses limites refletem os votos de "fechamento" adicionais produzidos pela lógica MQL.
Tratamento de risco – Quando distâncias positivas de stop-loss ou take-profit (expressas em etapas de preço) são fornecidas, a estratégia as converte em UnitTypes.Price e habilita StartProtection. Caso contrário, a proteção padrão StockSharp será ativada com StartProtection().
Regras de negociação
Entrada longa: a vela anterior é de baixa, a vela atual é de alta e o corpo da vela atual engole o corpo anterior. O valor estocástico %D deve estar abaixo de EntryOversoldLevel (padrão 30). Qualquer posição curta existente é fechada e uma nova posição longa é aberta via BuyMarket.
Entrada curta: A vela anterior é de alta, a vela atual é de baixa e o corpo da vela atual engole o corpo anterior. O valor estocástico %D deve exceder EntryOverboughtLevel (padrão 70). Qualquer posição longa existente é fechada e uma nova posição curta é aberta via SellMarket.
Saída longa: Com uma posição longa aberta, se %D cruzar para baixo através de ExitUpperLevel (padrão 80) ou ExitLowerLevel (padrão 20), a posição será fechada com SellMarket.
Saída curta: Com uma posição curta aberta, se %D cruzar para cima através de ExitLowerLevel ou ExitUpperLevel, a posição será coberta usando BuyMarket.
Paradas/metas: StopLossPoints e TakeProfitPoints opcionais convertem distâncias baseadas em pontos em compensações de preço absoluto quando o instrumento expõe um PriceStep diferente de zero.
Parâmetros
Nome
Tipo
Padrão
Descrição
CandleType
DataType
TimeSpan.FromHours(1).TimeFrame()
Fonte de vela usada para detecção de padrões.
StochasticPeriodK
int
47
Período de lookback para o cálculo rápido de %K.
StochasticPeriodD
int
9
Período de suavização para a linha de sinal %D.
StochasticPeriodSlow
int
13
Suavização adicional aplicada a %K antes de se tornar %D.
EntryOversoldLevel
decimal
30
Limite superior para %D que permite negociações envolventes de alta.
EntryOverboughtLevel
decimal
70
Limite inferior para %D que permite negociações envolventes de baixa.
ExitLowerLevel
decimal
20
Nível que, quando cruzado para cima, força saídas curtas; quando cruzado para baixo, fecha posições compradas.
ExitUpperLevel
decimal
80
Limite superior usado da mesma forma que o nível inferior, mas para território de sobrecompra.
TakeProfitPoints
decimal
0
Distância nas etapas de preço para a ordem take-profit (0 a desativa).
StopLossPoints
decimal
0
Distância em etapas de preço para a ordem stop-loss (0 a desativa).
Notas
Funciona em qualquer instrumento que forneça OHLC velas; os padrões assumem barras horárias.
Todos os cálculos dependem de velas fechadas para permanecerem alinhados com a lógica de prazo do especialista MQL.
O tamanho da posição deve ser configurado por meio da propriedade da estratégia base Volume ou do gerenciamento de portfólio de nível 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()