Esta estrategia es un port directo del clásico asesor experto MACD Sample de MetaTrader 5. Utiliza cruces de MACD filtrados por un detector de tendencia EMA. Las órdenes se dimensionan con la propiedad estándar Volume, mientras que la gestión de riesgos se basa en umbrales de pips configurables para el histograma MACD, take profit y trailing stop.
Lógica principal
Indicadores
MovingAverageConvergenceDivergenceSignal con períodos (12, 26, 9) proporciona líneas MACD y de señal.
ExponentialMovingAverage con período 26 actúa como filtro de tendencia.
Criterios de entrada
Largo: MACD está por debajo de cero, cruza por encima de la línea de señal, tiene magnitud por encima del Nivel de Apertura MACD, y la EMA está subiendo.
Corto: MACD está por encima de cero, cruza por debajo de la línea de señal, tiene magnitud por encima del Nivel de Apertura MACD, y la EMA está bajando.
Criterios de salida
MACD cruza en contra de la posición con magnitud por encima del Nivel de Cierre MACD.
El take profit alcanza la distancia en pips configurada desde el precio de entrada.
Se activa el trailing stop (si fue activado por beneficio > distancia de trailing).
Mecánica del trailing stop
Las posiciones largas activan el trailing stop una vez que el precio alto supera el precio de entrada por la distancia de trailing. El stop se mantiene en máximo − distancia de trailing.
Las posiciones cortas activan el trailing stop una vez que el precio bajo se mueve por debajo del precio de entrada por la distancia de trailing. El stop se mantiene en mínimo + distancia de trailing.
Parámetros
Parámetro
Valor predeterminado
Descripción
FastPeriod
12
Período EMA rápida dentro del MACD.
SlowPeriod
26
Período EMA lenta dentro del MACD.
SignalPeriod
9
Período EMA de señal dentro del MACD.
TrendPeriod
26
Longitud del filtro de tendencia EMA.
MacdOpenLevelPips
3
Magnitud mínima del MACD (en pips) requerida para abrir una operación.
MacdCloseLevelPips
2
Magnitud mínima del MACD (en pips) requerida para cerrar una operación en cruce.
TakeProfitPips
50
Distancia de take profit expresada en pips.
TrailingStopPips
30
Distancia de trailing stop expresada en pips. Establecer en 0 para deshabilitar el trailing.
CandleType
Marco temporal de 15 minutos
Tipo de vela utilizado para los cálculos.
Conversión de pips
El asesor experto original usaba la normalización de pips de MetaTrader (multiplicando por 10 para símbolos de 3/5 dígitos). La conversión sigue la misma idea inspeccionando Security.PriceStep:
Si el paso de precio tiene 3 o 5 decimales, el tamaño del pip es PriceStep * 10.
De lo contrario, el tamaño del pip es igual a PriceStep.
Cuando el paso de precio no está disponible, los umbrales basados en pips caen de vuelta a valores brutos.
Notas de comportamiento
Las posiciones se cierran antes de que se evalúen las nuevas señales, reflejando la implementación MT5.
Las instrucciones LogInfo reportan entradas, salidas y actualizaciones de trailing stop para facilitar la depuración.
Las órdenes de protección no se colocan automáticamente; las salidas se gestionan dentro de ProcessCandle para imitar la lógica del EA.
Use Volume para definir el tamaño base de la operación. Las reversiones compensan automáticamente la exposición actual añadiendo Math.Abs(Position) al volumen de la orden.
Diferencias con la versión MQL5
El procesamiento ocurre en velas finalizadas en lugar de en cada tick. Esto evita señales repetidas mientras mantiene un comportamiento determinista.
Las comprobaciones de trailing stop y take profit usan máximos y mínimos de las velas para aproximar los hits de bid/ask del EA original.
Cuando falta Security.PriceStep, los parámetros de pips actúan como distancias absolutas de precio y deben ajustarse manualmente.
Ajuste los umbrales de pips y el tipo de vela para adaptarse al instrumento negociado, especialmente cuando se porta a mercados con diferentes tamaños 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()