Esta estratégia reproduz o comportamento do consultor especialista "MACDSimple" de Yury Reshetov do MetaTrader dentro do framework StockSharp. Trabalha com um único instrumento e avalia sinais MACD clássicos que são modificados por dois parâmetros de offset. O algoritmo processa apenas velas completas, garantindo que todas as decisões de trading sejam tomadas sobre dados confirmados e evitando o ruído intrabarra.
Indicadores e cálculos
MACD (Moving Average Convergence Divergence) – a linha MACD e a linha de sinal são calculadas com períodos personalizados:
Período da EMA rápida = SignalPeriod + DF
Período da EMA lenta = SignalPeriod + DS + DF
Período da linha de sinal = SignalPeriod
Os offsets DF e DS seguem as entradas originais do especialista e permitem ao trader esticar ou comprimir os componentes MACD mantendo sua relação intacta.
Parâmetros
Nome
Descrição
Padrão
Volume
Tamanho de ordem usado para cada entrada de mercado.
2
DF
Offset adicionado ao comprimento da EMA rápida do MACD. Deve ser zero ou positivo.
1
DS
Offset adicional aplicado ao comprimento da EMA lenta do MACD. Deve ser zero ou positivo.
2
SignalPeriod
Período base do qual os comprimentos de EMA rápida e lenta são derivados.
10
CandleType
Período das velas usadas para análise e trading.
Período de 30 minutos
Lógica de trading
Gerenciamento de posição
Em cada vela concluída, a estratégia atualiza o indicador MACD e ignora a barra se o indicador ainda não estiver totalmente formado.
Se uma posição comprada estiver aberta e a linha MACD cair abaixo de zero, a estratégia fecha toda a posição comprada ao preço de mercado.
Se uma posição vendida estiver aberta e a linha MACD subir acima de zero, a estratégia fecha toda a posição vendida ao preço de mercado.
Após fechar uma posição em uma determinada barra, o algoritmo para de processar essa barra, refletindo o comportamento do consultor especialista original.
Regras de entrada
O algoritmo verifica que tanto a linha MACD quanto a linha de sinal compartilham o mesmo sinal (ambas positivas ou ambas negativas). Sinais mistos não produzem operações.
Quando ambas as linhas são positivas, uma posição comprada é aberta se a linha MACD estiver acima da linha de sinal.
Quando ambas as linhas são negativas, uma posição vendida é aberta se a linha MACD estiver abaixo da linha de sinal.
As ordens de mercado têm o tamanho configurado com o parâmetro Volume. Apenas uma posição pode existir por vez.
Regras de saída
As saídas são impulsionadas exclusivamente pela linha MACD cruzando o nível zero contra a posição aberta, conforme descrito na seção de gerenciamento de posição. Nenhuma saída parcial, stop loss ou take profit são implementados por padrão.
Notas adicionais
A estratégia opera apenas quando IsFormedAndOnlineAndAllowTrading() é satisfeito, garantindo que dados ao vivo estejam disponíveis e o trading esteja habilitado antes de entrar em novas posições.
Não há gerenciamento de risco automático embutido. Os usuários podem adicionar proteções personalizadas como StartProtection() ou combinar a estratégia com controles de risco em nível de portfólio, se desejado.
Como os parâmetros do MACD são derivados de um único período base mais offsets, ajustar SignalPeriod, DF ou DS afeta todos os componentes simultaneamente, preservando o espaçamento relativo pretendido pelo consultor especialista original.
Detalhes de implementação
A ligação de indicadores usa a API SubscribeCandles().Bind() de alto nível do StockSharp, mantendo a implementação concisa e orientada a eventos.
A conversão segue o conjunto de regras descrito em AGENTS.md: tabulações são usadas para indentação, valores de indicadores são consumidos diretamente do callback de ligação, e funções de trading BuyMarket/SellMarket gerenciam entradas e saídas.
A estrutura da estratégia está pronta para extensão (por exemplo, adicionando filtros ou lógica de risco) enquanto permanece fiel à lógica do especialista MetaTrader original.
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>
/// MACD-based strategy adapted from Yury Reshetov's MACDSimple expert advisor.
/// </summary>
public class MacdSimpleReshetovStrategy : Strategy
{
private readonly StrategyParam<int> _df;
private readonly StrategyParam<int> _ds;
private readonly StrategyParam<int> _signalPeriod;
private readonly StrategyParam<DataType> _candleType;
private MovingAverageConvergenceDivergenceSignal _macd;
public int Df { get => _df.Value; set => _df.Value = value; }
public int Ds { get => _ds.Value; set => _ds.Value = value; }
public int SignalPeriod { get => _signalPeriod.Value; set => _signalPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MacdSimpleReshetovStrategy()
{
_df = Param(nameof(Df), 1)
.SetNotNegative()
.SetDisplay("DF", "Offset for the fast EMA", "Indicators");
_ds = Param(nameof(Ds), 2)
.SetNotNegative()
.SetDisplay("DS", "Offset for the slow EMA", "Indicators");
_signalPeriod = Param(nameof(SignalPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Signal Period", "Signal line period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// The MQL version derives MACD periods from the signal period with DF and DS offsets.
var fastPeriod = SignalPeriod + Df;
var slowPeriod = SignalPeriod + Ds + Df;
_macd = new MovingAverageConvergenceDivergenceSignal
{
Macd = { ShortMa = { Length = fastPeriod }, LongMa = { Length = slowPeriod } },
SignalMa = { Length = SignalPeriod }
};
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var result = _macd.Process(candle);
if (!_macd.IsFormed)
return;
var macdValue = result as MovingAverageConvergenceDivergenceSignalValue;
if (macdValue == null)
return;
var macdLine = macdValue.Macd ?? 0m;
var signalLine = macdValue.Signal ?? 0m;
// Manage existing positions before evaluating new signals.
if (Position > 0)
{
if (macdLine < 0m)
SellMarket(Position);
return;
}
if (Position < 0)
{
if (macdLine > 0m)
BuyMarket(-Position);
return;
}
// Enter only when MACD and signal lines share the same sign.
if (macdLine * signalLine <= 0m)
return;
if (macdLine > 0m && macdLine > signalLine)
{
BuyMarket(Volume);
}
else if (macdLine < 0m && macdLine < signalLine)
{
SellMarket(Volume);
}
}
}
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 MovingAverageConvergenceDivergenceSignal, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class macd_simple_reshetov_strategy(Strategy):
def __init__(self):
super(macd_simple_reshetov_strategy, self).__init__()
self._df = self.Param("Df", 1)
self._ds = self.Param("Ds", 2)
self._signal_period = self.Param("SignalPeriod", 10)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._macd = None
@property
def Df(self):
return self._df.Value
@property
def Ds(self):
return self._ds.Value
@property
def SignalPeriod(self):
return self._signal_period.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(macd_simple_reshetov_strategy, self).OnStarted2(time)
fast_period = self.SignalPeriod + self.Df
slow_period = self.SignalPeriod + self.Ds + self.Df
self._macd = MovingAverageConvergenceDivergenceSignal()
self._macd.Macd.ShortMa.Length = fast_period
self._macd.Macd.LongMa.Length = slow_period
self._macd.SignalMa.Length = self.SignalPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
civ = CandleIndicatorValue(self._macd, candle)
civ.IsFinal = True
result = self._macd.Process(civ)
if not self._macd.IsFormed:
return
try:
macd_line = float(result.Macd) if result.Macd is not None else 0.0
signal_line = float(result.Signal) if result.Signal is not None else 0.0
except:
return
pos = float(self.Position)
if pos > 0:
if macd_line < 0:
self.SellMarket(pos)
return
if pos < 0:
if macd_line > 0:
self.BuyMarket(abs(pos))
return
if macd_line * signal_line <= 0:
return
if macd_line > 0 and macd_line > signal_line:
self.BuyMarket(float(self.Volume))
elif macd_line < 0 and macd_line < signal_line:
self.SellMarket(float(self.Volume))
def OnReseted(self):
super(macd_simple_reshetov_strategy, self).OnReseted()
self._macd = None
def CreateClone(self):
return macd_simple_reshetov_strategy()