Esta estratégia é um port StockSharp do consultor especialista MetaTrader 5 "ssb5_123". O código original vem da coleção SSB (Step by Step) de Yury V. Reshetov e combina vários osciladores clássicos para confirmar rompimentos direcionais. A versão StockSharp mantém a mesma lógica usando a API de assinatura de velas de alto nível e implementações de indicadores nativos.
O algoritmo trabalha exclusivamente com velas concluídas. Compara o preço de abertura da barra atual com a barra anterior, verifica o momentum e a direção do Awesome Oscillator, MACD e histograma OsMA, e verifica se o preço está sendo negociado acima ou abaixo de uma média móvel suavizada. Confirmação adicional é obtida do oscilador estocástico exigindo que tanto %K quanto %D estejam acima ou abaixo do nível 50.
Indicadores e Sinais
Os seguintes indicadores são empregados exatamente como na versão MetaTrader:
Média Móvel Suavizada (SMMA): média móvel suavizada de 45 períodos calculada a partir das aberturas de velas. A direção de entrada requer que o preço de abertura esteja no lado correto da média.
MACD (rápido 47, lento 95, sinal 74): a linha principal deve ser positiva para trades longos (negativa para trades curtos) e deve estar subindo (caindo) em comparação com a vela anterior.
Histograma OsMA: calculado como MACD menos sua linha de sinal. O histograma deve diminuir para trades longos e aumentar para trades curtos, espelhando a função original fosma1().
Awesome Oscillator: usa as médias móveis suavizadas padrão 5/34 do preço mediano. O valor do oscilador deve ser positivo para longos (negativo para curtos) e seu momentum entre as últimas duas barras deve apontar na direção do trade.
Oscilador Estocástico (K=25, D=12, Slowing=56): tanto as linhas %K quanto %D devem estar acima de 50 para trades longos e abaixo de 50 para trades curtos, fornecendo um filtro de regime.
Lógica de Negociação
Aguardar uma nova vela concluída.
Avaliar a configuração longa. Todas as seguintes condições devem ser verdadeiras:
A abertura da vela atual é menor ou igual à abertura da vela anterior.
O Awesome Oscillator é positivo e está caindo em relação ao valor anterior.
A linha principal MACD é positiva e está subindo em relação ao valor anterior.
O histograma OsMA não está aumentando (histograma atual menos histograma anterior é menor ou igual a zero).
A abertura da vela atual está acima da média móvel suavizada.
As linhas estocásticas %K e %D estão em ou acima de 50.
Avaliar a configuração curta. Todas as seguintes condições devem ser verdadeiras:
A abertura da vela atual é maior ou igual à abertura da vela anterior.
O Awesome Oscillator é negativo e está subindo em relação ao valor anterior.
A linha principal MACD é negativa e está caindo em relação ao valor anterior.
O histograma OsMA não está diminuindo (histograma atual menos histograma anterior é maior ou igual a zero).
A abertura da vela atual está abaixo da média móvel suavizada.
As linhas estocásticas %K e %D estão em ou abaixo de 50.
Se uma posição já existir, um sinal oposto a fecha imediatamente, replicando o gerenciamento de ordens original do MetaTrader.
Quando plano, uma entrada longa tem prioridade: se ambos os sinais forem verdadeiros (possível quando todos os indicadores são exatamente zero), a estratégia abre uma posição longa. Caso contrário, abre uma posição curta quando apenas as condições curtas são satisfeitas.
Parâmetros
SMMA Period – comprimento do filtro de média móvel suavizada (padrão 45).
MACD Fast / Slow / Signal – períodos EMA para o indicador MACD (47 / 95 / 74).
Stochastic %K / %D / Slowing – período principal, período de suavização e suavização adicional para o oscilador estocástico (25 / 12 / 56).
Order Volume – quantidade usada para ordens de mercado (padrão 1).
Candle Type – período das velas de entrada (padrão 1 hora). Ajuste para corresponder ao período usado no MetaTrader.
Notas de Uso
A estratégia opera apenas em velas terminadas; atualizações intrabarra são ignoradas.
Os valores de indicadores da vela anterior são armazenados em cache para que as comparações de momentum correspondam ao comportamento exato das funções auxiliares originais fao1, fmacd1 e fosma1.
Não há regras integradas de stop-loss ou take-profit no consultor especialista original. O gerenciamento de risco deve ser adicionado externamente se necessário.
As configurações padrão de indicadores correspondem aos parâmetros MQL fornecidos, mas todos os valores são expostos como objetos StrategyParam e podem ser otimizados através do otimizador StockSharp.
Considerações de Conversão
A versão MetaTrader usa um número mágico e validação manual de volume; essas partes não são necessárias no StockSharp e foram omitidas.
A lógica de fechamento de ordens segue a mesma precedência que o script MQL: as posições são fechadas primeiro, e novas entradas só são feitas quando a estratégia está plana.
As implementações do Awesome Oscillator e MACD vêm da biblioteca de indicadores StockSharp, eliminando a necessidade de manipulação manual de buffer presente no código original.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// SSB5 123 strategy. Uses triple EMA alignment for 1-2-3 pattern detection.
/// </summary>
public class Ssb5123Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _midPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast;
private decimal? _prevMid;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int MidPeriod
{
get => _midPeriod.Value;
set => _midPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public Ssb5123Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_midPeriod = Param(nameof(MidPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Mid EMA", "Middle EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevMid = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevMid = null;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var mid = new ExponentialMovingAverage { Length = MidPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, mid, slow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, mid);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal midVal, decimal slowVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFast = fastVal;
_prevMid = midVal;
return;
}
if (_prevFast == null || _prevMid == null)
{
_prevFast = fastVal;
_prevMid = midVal;
return;
}
// Fast crosses above mid while both above slow
if (_prevFast.Value <= _prevMid.Value && fastVal > midVal && midVal > slowVal && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Fast crosses below mid while both below slow
else if (_prevFast.Value >= _prevMid.Value && fastVal < midVal && midVal < slowVal && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevFast = fastVal;
_prevMid = midVal;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ssb5123_strategy(Strategy):
def __init__(self):
super(ssb5123_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 5) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._mid_period = self.Param("MidPeriod", 12) \
.SetDisplay("Mid EMA", "Middle EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 26) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._prev_fast = None
self._prev_mid = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def MidPeriod(self):
return self._mid_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(ssb5123_strategy, self).OnReseted()
self._prev_fast = None
self._prev_mid = None
def OnStarted2(self, time):
super(ssb5123_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_mid = None
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
mid = ExponentialMovingAverage()
mid.Length = self.MidPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, mid, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, mid)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, mid_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
mv = float(mid_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_mid is None:
self._prev_fast = fv
self._prev_mid = mv
return
# Fast crosses above mid while both above slow
if self._prev_fast <= self._prev_mid and fv > mv and mv > sv and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Fast crosses below mid while both below slow
elif self._prev_fast >= self._prev_mid and fv < mv and mv < sv and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_fast = fv
self._prev_mid = mv
def CreateClone(self):
return ssb5123_strategy()