Estrategia MACD Zero Cross
Este sistema opera cambios de momentum cuando el histograma del Moving Average Convergence Divergence (MACD) se aproxima a la línea cero. Un MACD ascendente por debajo de cero o un MACD descendente por encima de cero señala una posible reversión.
Las pruebas indican un rendimiento anual promedio de aproximadamente 136%. Funciona mejor en el mercado de acciones.
La estrategia espera que la línea MACD tienda hacia cero mientras permanece en el lado opuesto. Una vez que el momentum se desvanece, entra anticipando un giro en el precio.
Las operaciones salen cuando el MACD cruza su línea de señal o se activa un stop-loss.
Detalles
- Criterios de entrada: MACD tendiendo hacia cero desde cualquier lado.
- Largo/Corto: Ambas direcciones.
- Criterios de salida: MACD cruza la línea de señal o stop.
- Stops: Sí.
- Valores predeterminados:
FastPeriod= 12SlowPeriod= 26SignalPeriod= 9StopLossPercent= 2.0mCandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoría: Momentum
- Dirección: Ambos
- Indicadores: MACD
- Stops: Sí
- Complejidad: Básico
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that trades MACD crossings of the zero line.
/// Buys when MACD crosses above zero, sells when MACD crosses below zero.
/// </summary>
public class MacdZeroStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _signalPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevMacd;
private bool _hasPrev;
private int _cooldown;
/// <summary>
/// Fast EMA period for MACD calculation.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow EMA period for MACD calculation.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Signal line period for MACD calculation.
/// </summary>
public int SignalPeriod
{
get => _signalPeriod.Value;
set => _signalPeriod.Value = value;
}
/// <summary>
/// Type of candles used for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initialize the MACD Zero strategy.
/// </summary>
public MacdZeroStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 8)
.SetDisplay("Fast EMA", "Fast EMA period for MACD", "MACD")
.SetOptimize(8, 16, 2);
_slowPeriod = Param(nameof(SlowPeriod), 17)
.SetDisplay("Slow EMA", "Slow EMA period for MACD", "MACD")
.SetOptimize(15, 30, 2);
_signalPeriod = Param(nameof(SignalPeriod), 9)
.SetDisplay("Signal", "Signal line period for MACD", "MACD")
.SetOptimize(7, 12, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 700)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMacd = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMacd = 0;
_hasPrev = false;
_cooldown = 0;
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = FastPeriod },
LongMa = { Length = SlowPeriod },
},
SignalMa = { Length = SignalPeriod }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, macd);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!macdValue.IsFormed)
return;
var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
if (macdTyped.Macd is not decimal macdLine || macdTyped.Signal is not decimal signal)
return;
if (!_hasPrev)
{
_prevMacd = macdLine;
_hasPrev = true;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevMacd = macdLine;
return;
}
// Zero line crossover signals
var prevBelow = _prevMacd < 0;
var currAbove = macdLine >= 0;
var prevAbove = _prevMacd >= 0;
var currBelow = macdLine < 0;
if (Position == 0 && prevBelow && currAbove)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && prevAbove && currBelow)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && prevAbove && currBelow)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && prevBelow && currAbove)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevMacd = macdLine;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class macd_zero_strategy(Strategy):
"""
MACD Zero line crossover strategy.
Buys when MACD crosses above zero, sells when crosses below.
"""
def __init__(self):
super(macd_zero_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 8).SetDisplay("Fast EMA", "Fast EMA period for MACD", "MACD")
self._slow_period = self.Param("SlowPeriod", 17).SetDisplay("Slow EMA", "Slow EMA period for MACD", "MACD")
self._signal_period = self.Param("SignalPeriod", 9).SetDisplay("Signal", "Signal line period for MACD", "MACD")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 700).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._prev_macd = 0.0
self._has_prev = False
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(macd_zero_strategy, self).OnReseted()
self._prev_macd = 0.0
self._has_prev = False
self._cooldown = 0
def OnStarted2(self, time):
super(macd_zero_strategy, self).OnStarted2(time)
self._prev_macd = 0.0
self._has_prev = False
self._cooldown = 0
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._fast_period.Value
macd.Macd.LongMa.Length = self._slow_period.Value
macd.SignalMa.Length = self._signal_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(macd, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, macd)
self.DrawOwnTrades(area)
def _process_candle(self, candle, macd_val):
if candle.State != CandleStates.Finished:
return
if not macd_val.IsFormed:
return
if macd_val.Macd is None or macd_val.Signal is None:
return
macd_line = float(macd_val.Macd)
if not self._has_prev:
self._prev_macd = macd_line
self._has_prev = True
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_macd = macd_line
return
cd = self._cooldown_bars.Value
prev_below = self._prev_macd < 0
curr_above = macd_line >= 0
prev_above = self._prev_macd >= 0
curr_below = macd_line < 0
if self.Position == 0 and prev_below and curr_above:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and prev_above and curr_below:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and prev_above and curr_below:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and prev_below and curr_above:
self.BuyMarket()
self._cooldown = cd
self._prev_macd = macd_line
def CreateClone(self):
return macd_zero_strategy()