Esta estratégia é um port direto do clássico consultor especialista MACD Sample do MetaTrader 5. Ela usa cruzamentos de MACD filtrados por um detector de tendência EMA. As ordens são dimensionadas com a propriedade padrão Volume, enquanto o gerenciamento de risco baseia-se em limites de pips configuráveis para o histograma MACD, take profit e trailing stop.
Lógica principal
Indicadores
MovingAverageConvergenceDivergenceSignal com períodos (12, 26, 9) fornece linhas MACD e de sinal.
ExponentialMovingAverage com período 26 atua como filtro de tendência.
Critérios de entrada
Comprado: MACD está abaixo de zero, cruza acima da linha de sinal, tem magnitude acima do Nível de Abertura MACD, e a EMA está subindo.
Vendido: MACD está acima de zero, cruza abaixo da linha de sinal, tem magnitude acima do Nível de Abertura MACD, e a EMA está caindo.
Critérios de saída
MACD cruza contra a posição com magnitude acima do Nível de Fechamento MACD.
Take profit atinge a distância em pips configurada a partir do preço de entrada.
Trailing stop (se ativado por lucro > distância de trailing) é atingido.
Mecânica do trailing stop
Posições compradas ativam o trailing stop quando o preço máximo ultrapassa o preço de entrada pela distância de trailing. O stop é então mantido em máximo − distância de trailing.
Posições vendidas ativam o trailing stop quando o preço mínimo se move abaixo do preço de entrada pela distância de trailing. O stop é mantido em mínimo + distância de trailing.
Parâmetros
Parâmetro
Valor padrão
Descrição
FastPeriod
12
Período EMA rápida dentro do MACD.
SlowPeriod
26
Período EMA lenta dentro do MACD.
SignalPeriod
9
Período EMA de sinal dentro do MACD.
TrendPeriod
26
Comprimento do filtro de tendência EMA.
MacdOpenLevelPips
3
Magnitude mínima do MACD (em pips) necessária para abrir uma operação.
MacdCloseLevelPips
2
Magnitude mínima do MACD (em pips) necessária para fechar uma operação no cruzamento.
TakeProfitPips
50
Distância de take profit expressa em pips.
TrailingStopPips
30
Distância de trailing stop expressa em pips. Definir como 0 para desabilitar o trailing.
CandleType
Período de 15 minutos
Tipo de candle usado para cálculos.
Conversão de pips
O consultor especialista original usava a normalização de pips do MetaTrader (multiplicando por 10 para símbolos de 3/5 dígitos). A conversão segue a mesma ideia inspecionando Security.PriceStep:
Se o passo de preço tem 3 ou 5 casas decimais, o tamanho do pip é PriceStep * 10.
Caso contrário, o tamanho do pip equivale a PriceStep.
Quando o passo de preço não está disponível, os limites baseados em pips recorrem a valores brutos.
Notas de comportamento
Posições são fechadas antes que novos sinais sejam avaliados, espelhando a implementação MT5.
Instruções LogInfo relatam entradas, saídas e atualizações de trailing stop para depuração mais fácil.
Ordens de proteção não são colocadas automaticamente; as saídas são gerenciadas dentro de ProcessCandle para imitar a lógica do EA.
Use Volume para definir o tamanho base da operação. As reversões compensam automaticamente a exposição atual adicionando Math.Abs(Position) ao volume da ordem.
Diferenças da versão MQL5
O processamento ocorre em candles finalizados em vez de a cada tick. Isso evita sinais repetidos enquanto mantém comportamento determinístico.
As verificações de trailing stop e take profit usam máximas e mínimas de candles para aproximar os hits de bid/ask do EA original.
Quando Security.PriceStep está ausente, os parâmetros de pips atuam como distâncias absolutas de preço e devem ser ajustados manualmente.
Ajuste os limites de pips e o tipo de candle para se adequar ao instrumento negociado, especialmente ao portar para mercados com diferentes tamanhos de tick.
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>
/// MACD strategy with EMA trend filter.
/// Buys when MACD histogram is positive and price is above trend EMA.
/// Sells when MACD histogram is negative and price is below trend EMA.
/// </summary>
public class MacdSampleTrendFilterStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _trendPeriod;
private int _prevSignal;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public int TrendPeriod
{
get => _trendPeriod.Value;
set => _trendPeriod.Value = value;
}
public MacdSampleTrendFilterStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA for MACD", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA for MACD", "Indicators");
_trendPeriod = Param(nameof(TrendPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("Trend Period", "Trend EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSignal = 0;
var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
var trendEma = new ExponentialMovingAverage { Length = TrendPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, trendEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawIndicator(area, trendEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal trend)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var macdLine = fast - slow;
// Signal: MACD positive + price above trend = bullish; MACD negative + price below trend = bearish
var signal = 0;
if (macdLine > 0 && close > trend)
signal = 1;
else if (macdLine < 0 && close < trend)
signal = -1;
if (signal == _prevSignal)
return;
var oldSignal = _prevSignal;
_prevSignal = signal;
if (signal == 1 && oldSignal <= 0)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (signal == -1 && oldSignal >= 0)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
}
}
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 macd_sample_trend_filter_strategy(Strategy):
def __init__(self):
super(macd_sample_trend_filter_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", 12) \
.SetDisplay("Fast Period", "Fast EMA for MACD", "Indicators")
self._slow_period = self.Param("SlowPeriod", 26) \
.SetDisplay("Slow Period", "Slow EMA for MACD", "Indicators")
self._trend_period = self.Param("TrendPeriod", 100) \
.SetDisplay("Trend Period", "Trend EMA period", "Indicators")
self._prev_signal = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
@property
def TrendPeriod(self):
return self._trend_period.Value
def OnReseted(self):
super(macd_sample_trend_filter_strategy, self).OnReseted()
self._prev_signal = 0
def OnStarted2(self, time):
super(macd_sample_trend_filter_strategy, self).OnStarted2(time)
self._prev_signal = 0
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowPeriod
trend_ema = ExponentialMovingAverage()
trend_ema.Length = self.TrendPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, slow_ema, trend_ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawIndicator(area, trend_ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value, trend_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
fv = float(fast_value)
sv = float(slow_value)
tv = float(trend_value)
macd_line = fv - sv
signal = 0
if macd_line > 0 and close > tv:
signal = 1
elif macd_line < 0 and close < tv:
signal = -1
if signal == self._prev_signal:
return
old_signal = self._prev_signal
self._prev_signal = signal
if signal == 1 and old_signal <= 0:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif signal == -1 and old_signal >= 0:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return macd_sample_trend_filter_strategy()