Estrategia de MACD Long
Combina los extremos del Relative Strength Index con los cruces de MACD para capturar retrocesos dentro de una tendencia. Después de que el RSI alcanza una lectura extrema, el sistema espera un cruce de MACD confirmatorio antes de entrar. Este enfoque filtra los cambios de momentum ruidosos y se centra en reversiones de alta probabilidad.
La estrategia opera en ambas direcciones y puede cambiar rápidamente cuando aparecen señales opuestas. MACD proporciona confirmación de momentum mientras RSI destaca zonas de sobrecompra y sobreventa. Se pueden agregar stops protectores a través de los controles de riesgo del motor.
Detalles
- Criterios de entrada:
- Largo: RSI cae por debajo de la sobreventa, luego la línea MACD cruza por encima de la señal.
- Corto: RSI sube por encima de la sobrecompra, luego la línea MACD cruza por debajo de la señal.
- Criterios de salida:
- Cruce opuesto o stop activado.
- Indicadores:
- RSI (longitud 14, sobreventa 30, sobrecompra 70)
- MACD (rápida 12, lenta 26, señal 9)
- Stops: Implementar mediante StartProtection o gestión de capital externa.
- Valores predeterminados:
RsiLength= 14Oversold= 30Overbought= 70MacdFast= 12MacdSlow= 26MacdSignal= 9
- Filtros:
- Reversión de momentum
- Funciona en varios marcos temporales
- Indicadores: RSI, MACD
- Stops: Opcional
- Complejidad: Básico
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>
/// MACD Long strategy.
/// An RSI reading below Oversold arms a long and a reading above Overbought arms a short. The next MACD crossover
/// confirms or cancels the setup: a bullish MACD/signal cross opens an armed long, a bearish cross opens an armed short.
/// Every opposite crossover closes the current position, or reverses it when the opposite side is armed.
/// </summary>
public class MacdLongStrategy : Strategy
{
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _oversold;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<int> _macdFast;
private readonly StrategyParam<int> _macdSlow;
private readonly StrategyParam<int> _macdSignal;
private readonly StrategyParam<DataType> _candleType;
private bool _longArmed;
private bool _shortArmed;
private decimal? _prevMacd;
private decimal? _prevSignal;
/// <summary>
/// RSI period.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// RSI oversold level.
/// </summary>
public decimal Oversold
{
get => _oversold.Value;
set => _oversold.Value = value;
}
/// <summary>
/// RSI overbought level.
/// </summary>
public decimal Overbought
{
get => _overbought.Value;
set => _overbought.Value = value;
}
/// <summary>
/// MACD fast EMA period.
/// </summary>
public int MacdFast
{
get => _macdFast.Value;
set => _macdFast.Value = value;
}
/// <summary>
/// MACD slow EMA period.
/// </summary>
public int MacdSlow
{
get => _macdSlow.Value;
set => _macdSlow.Value = value;
}
/// <summary>
/// MACD signal line period.
/// </summary>
public int MacdSignal
{
get => _macdSignal.Value;
set => _macdSignal.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MacdLongStrategy()
{
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_oversold = Param(nameof(Oversold), 30m)
.SetDisplay("Oversold", "RSI oversold level", "RSI");
_overbought = Param(nameof(Overbought), 70m)
.SetDisplay("Overbought", "RSI overbought level", "RSI");
_macdFast = Param(nameof(MacdFast), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "MACD fast EMA period", "MACD");
_macdSlow = Param(nameof(MacdSlow), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "MACD slow EMA period", "MACD");
_macdSignal = Param(nameof(MacdSignal), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "MACD signal line period", "MACD");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_longArmed = false;
_shortArmed = false;
_prevMacd = null;
_prevSignal = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_longArmed = false;
_shortArmed = false;
_prevMacd = null;
_prevSignal = null;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFast },
LongMa = { Length = MacdSlow },
},
SignalMa = { Length = MacdSignal },
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(rsi, macd, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, rsi);
DrawIndicator(oscillators, macd);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue rsiValue, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!rsiValue.IsFormed || !macdValue.IsFormed)
return;
var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
if (macdTyped.Macd is not decimal macd || macdTyped.Signal is not decimal signal)
return;
var rsi = rsiValue.ToDecimal();
if (rsi < Oversold)
_longArmed = true;
if (rsi > Overbought)
_shortArmed = true;
var prevMacd = _prevMacd;
var prevSignal = _prevSignal;
_prevMacd = macd;
_prevSignal = signal;
if (prevMacd is not decimal lastMacd || prevSignal is not decimal lastSignal)
return;
var crossUp = lastMacd <= lastSignal && macd > signal;
var crossDown = lastMacd >= lastSignal && macd < signal;
var longSignal = crossUp && _longArmed;
var shortSignal = crossDown && _shortArmed;
// The first crossover after the RSI extreme decides the setup either way.
if (crossUp)
_longArmed = false;
if (crossDown)
_shortArmed = false;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (longSignal && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (shortSignal && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && crossDown)
SellMarket(Position);
else if (Position < 0 && crossUp)
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex, MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
class macd_long_strategy(Strategy):
"""
MACD Long strategy.
An RSI reading below Oversold arms a long and a reading above Overbought arms a short. The next MACD crossover
confirms or cancels the setup: a bullish MACD/signal cross opens an armed long, a bearish cross opens an armed short.
Every opposite crossover closes the current position, or reverses it when the opposite side is armed.
"""
def __init__(self):
super(macd_long_strategy, self).__init__()
self._rsi_length = self.Param("RsiLength", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._oversold = self.Param("Oversold", 30.0) \
.SetDisplay("Oversold", "RSI oversold level", "RSI")
self._overbought = self.Param("Overbought", 70.0) \
.SetDisplay("Overbought", "RSI overbought level", "RSI")
self._macd_fast = self.Param("MacdFast", 12) \
.SetGreaterThanZero() \
.SetDisplay("MACD Fast", "MACD fast EMA period", "MACD")
self._macd_slow = self.Param("MacdSlow", 26) \
.SetGreaterThanZero() \
.SetDisplay("MACD Slow", "MACD slow EMA period", "MACD")
self._macd_signal = self.Param("MacdSignal", 9) \
.SetGreaterThanZero() \
.SetDisplay("MACD Signal", "MACD signal line period", "MACD")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle type", "Candle type for strategy calculation", "General")
self._reset_state()
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def _reset_state(self):
self._long_armed = False
self._short_armed = False
self._prev_macd = None
self._prev_signal = None
def OnReseted(self):
super(macd_long_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(macd_long_strategy, self).OnStarted2(time)
self._reset_state()
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._macd_fast.Value
macd.Macd.LongMa.Length = self._macd_slow.Value
macd.SignalMa.Length = self._macd_signal.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(rsi, macd, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi)
self.DrawIndicator(oscillators, macd)
def _process_candle(self, candle, rsi_value, macd_value):
if candle.State != CandleStates.Finished:
return
if not rsi_value.IsFormed or not macd_value.IsFormed:
return
if macd_value.Macd is None or macd_value.Signal is None:
return
macd = float(macd_value.Macd)
signal = float(macd_value.Signal)
rsi = float(rsi_value.GetValue[Decimal](None))
if rsi < float(self._oversold.Value):
self._long_armed = True
if rsi > float(self._overbought.Value):
self._short_armed = True
prev_macd = self._prev_macd
prev_signal = self._prev_signal
self._prev_macd = macd
self._prev_signal = signal
if prev_macd is None or prev_signal is None:
return
cross_up = prev_macd <= prev_signal and macd > signal
cross_down = prev_macd >= prev_signal and macd < signal
long_signal = cross_up and self._long_armed
short_signal = cross_down and self._short_armed
# The first crossover after the RSI extreme decides the setup either way.
if cross_up:
self._long_armed = False
if cross_down:
self._short_armed = False
if not self.IsFormedAndOnlineAndAllowTrading():
return
if long_signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif short_signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and cross_down:
self.SellMarket(self.Position)
elif self.Position < 0 and cross_up:
self.BuyMarket(-self.Position)
def CreateClone(self):
return macd_long_strategy()