Esta estrategia es una adaptación StockSharp del experto MetaTrader 4 AG.mq4. El robot opera con dos cálculos de convergencia y divergencia de media móvil (MACD) que utilizan diferentes conjuntos de parámetros. El MACD primario produce activadores de entrada, mientras que el MACD secundario (escalado) actúa como un filtro direccional para evitar operaciones contratendencias y controlar las salidas. La lógica refleja al experto MQL4 original al evaluar solo velas cerradas y reutilizar las comprobaciones de señales de la línea de señal que activaron las órdenes originales.
Lógica de trading
Indicadores
Primario MACD: EMA rápida = FastEmaLength, EMA lenta = SlowEmaLength, señal SMA = SignalSmaLength.
Secundario MACD: EMA rápida = SlowEmaLength * 2, EMA lenta = FastEmaLength * 2, señal SMA = SignalSmaLength * 2.
Entrada larga
La línea principal primaria MACD está por encima de su línea de señal.
La línea de señal primaria MACD es negativa (debajo de la línea de flotación).
La línea principal secundaria MACD está por encima de su línea de señal.
La línea de señal secundaria MACD es negativa.
Entrada corta
La línea principal primaria MACD está debajo de su línea de señal.
La línea de señal primaria MACD es positiva.
La línea principal secundaria MACD está debajo de su línea de señal.
La línea de señal secundaria MACD es positiva.
Reglas de salida
Cierre posiciones largas cuando el MACD secundario se vuelva bajista mientras la línea de señal primaria se mantiene por encima de cero.
Cierre posiciones cortas cuando el MACD secundario se vuelva alcista mientras la línea de señal primaria permanece por debajo de cero.
La estrategia sólo reacciona a las velas terminadas e ignora las barras sin terminar para evitar volver a pintarlas.
Gestión de Puestos
Todas las órdenes son órdenes de mercado con el volumen fijo definido por OrderVolume.
MaxOpenOrders refleja la entrada original ORDER y limita el número total de órdenes activas más posiciones abiertas. Configúrelo en 0 para quitar la tapa.
StartProtection() se habilita una vez que comienza la estrategia para que el administrador de riesgos StockSharp pueda monitorear la exposición abierta.
Parámetros
Nombre
Descripción
OrderVolume
Tamaño de lote base para nuevas operaciones.
FastEmaLength
Período EMA rápida del MACD primario.
SlowEmaLength
Período EMA lenta del MACD primario.
SignalSmaLength
Período de suavizado de señal para ambos MACD.
MaxOpenOrders
Número máximo de órdenes activas y posiciones abiertas combinadas. Establece 0 para ilimitado.
CandleType
Marco de tiempo utilizado para construir velas para ambos indicadores.
Notas
El MACD secundario mantiene el mismo orden rápido/lento que en el EA original, incluso si el período rápido se vuelve más grande que el lento, para preservar los cálculos del autor.
La estrategia no coloca órdenes pendientes; se abre o cierra en el mercado tan pronto como aparecen las condiciones.
No se agregan niveles adicionales de stop-loss o take-profit porque el experto original se basó exclusivamente en las reversiones de señales.
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>
/// AG MACD Dual strategy - MACD histogram crossover with EMA trend filter.
/// Buys when MACD histogram crosses above zero while price is above EMA.
/// Sells when MACD histogram crosses below zero while price is below EMA.
/// </summary>
public class AgMacdDualStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevMacd;
private decimal _prevSignal;
private bool _hasPrev;
private decimal _currentEma;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public AgMacdDualStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 50)
.SetDisplay("EMA Period", "EMA trend filter", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevMacd = 0m; _prevSignal = 0m; _hasPrev = false; _currentEma = 0m; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var macd = new MovingAverageConvergenceDivergenceSignal();
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, ProcessMacd)
.Bind(ema, ProcessEma)
.Start();
}
private void ProcessEma(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished)
return;
_currentEma = ema;
}
private void ProcessMacd(ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished)
return;
if (!value.IsFinal || value.IsEmpty)
return;
var macdVal = value as MovingAverageConvergenceDivergenceSignalValue;
if (macdVal == null)
return;
var macdLine = macdVal.Macd;
var signalLine = macdVal.Signal;
if (macdLine == null || signalLine == null)
return;
var histogram = macdLine.Value - signalLine.Value;
if (!_hasPrev)
{
_prevMacd = macdLine.Value;
_prevSignal = signalLine.Value;
_hasPrev = true;
return;
}
var prevHist = _prevMacd - _prevSignal;
var close = candle.ClosePrice;
if (prevHist <= 0 && histogram > 0 && close > _currentEma && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (prevHist >= 0 && histogram < 0 && close < _currentEma && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevMacd = macdLine.Value;
_prevSignal = signalLine.Value;
}
}
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, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ag_macd_dual_strategy(Strategy):
def __init__(self):
super(ag_macd_dual_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 50).SetDisplay("EMA Period", "EMA trend filter", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_macd = 0.0; self._prev_signal = 0.0; self._has_prev = False; self._current_ema = 0.0
@property
def ema_period(self): return self._ema_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(ag_macd_dual_strategy, self).OnReseted()
self._prev_macd = 0.0; self._prev_signal = 0.0; self._has_prev = False; self._current_ema = 0.0
def OnStarted2(self, time):
super(ag_macd_dual_strategy, self).OnStarted2(time)
self._has_prev = False
macd = MovingAverageConvergenceDivergenceSignal()
ema = ExponentialMovingAverage(); ema.Length = self.ema_period
sub = self.SubscribeCandles(self.candle_type)
sub.BindEx(macd, self.process_macd).Bind(ema, self.process_ema).Start()
def process_ema(self, candle, ema):
if candle.State != CandleStates.Finished: return
self._current_ema = float(ema)
def process_macd(self, candle, value):
if candle.State != CandleStates.Finished: return
if not value.IsFinal or value.IsEmpty: return
if value.Macd is None or value.Signal is None: return
ml = float(value.Macd); sl = float(value.Signal); hist = ml - sl
if not self._has_prev: self._prev_macd = ml; self._prev_signal = sl; self._has_prev = True; return
prev_hist = self._prev_macd - self._prev_signal; close = float(candle.ClosePrice)
if prev_hist <= 0 and hist > 0 and close > self._current_ema and self.Position <= 0:
if self.Position < 0: self.BuyMarket()
self.BuyMarket()
elif prev_hist >= 0 and hist < 0 and close < self._current_ema and self.Position >= 0:
if self.Position > 0: self.SellMarket()
self.SellMarket()
self._prev_macd = ml; self._prev_signal = sl
def CreateClone(self): return ag_macd_dual_strategy()