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
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// MACD Long Strategy.
/// Uses MACD crossover with RSI oversold/overbought lookback for timing.
/// Buys when RSI was recently oversold and MACD turns positive.
/// Sells when RSI was recently overbought and MACD turns negative.
/// </summary>
public class MacdLongStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleTypeParam;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _rsiOversold;
private readonly StrategyParam<int> _rsiOverbought;
private readonly StrategyParam<int> _lookbackBars;
private readonly StrategyParam<int> _cooldownBars;
private RelativeStrengthIndex _rsi;
private MovingAverageConvergenceDivergence _macd;
private int _barsSinceOversold;
private int _barsSinceOverbought;
private decimal _prevMacd;
private int _cooldownRemaining;
public MacdLongStrategy()
{
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_rsiOversold = Param(nameof(RsiOversold), 40)
.SetDisplay("RSI Oversold", "Oversold level", "RSI");
_rsiOverbought = Param(nameof(RsiOverbought), 60)
.SetDisplay("RSI Overbought", "Overbought level", "RSI");
_lookbackBars = Param(nameof(LookbackBars), 20)
.SetDisplay("Lookback Bars", "Bars to look back for RSI conditions", "Strategy");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public int RsiOversold
{
get => _rsiOversold.Value;
set => _rsiOversold.Value = value;
}
public int RsiOverbought
{
get => _rsiOverbought.Value;
set => _rsiOverbought.Value = value;
}
public int LookbackBars
{
get => _lookbackBars.Value;
set => _lookbackBars.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_rsi = null;
_macd = null;
_barsSinceOversold = int.MaxValue;
_barsSinceOverbought = int.MaxValue;
_prevMacd = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_rsi = new RelativeStrengthIndex { Length = RsiLength };
_macd = new MovingAverageConvergenceDivergence();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_rsi, _macd, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal rsi, decimal macdVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!_rsi.IsFormed || !_macd.IsFormed)
{
_prevMacd = macdVal;
return;
}
// Track RSI oversold/overbought
if (rsi <= RsiOversold)
_barsSinceOversold = 0;
else
_barsSinceOversold = Math.Min(_barsSinceOversold + 1, int.MaxValue - 1);
if (rsi >= RsiOverbought)
_barsSinceOverbought = 0;
else
_barsSinceOverbought = Math.Min(_barsSinceOverbought + 1, int.MaxValue - 1);
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevMacd = macdVal;
return;
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevMacd = macdVal;
return;
}
var wasOversold = _barsSinceOversold <= LookbackBars;
var wasOverbought = _barsSinceOverbought <= LookbackBars;
// MACD zero cross
var macdCrossUp = macdVal > 0 && _prevMacd <= 0 && _prevMacd != 0;
var macdCrossDown = macdVal < 0 && _prevMacd >= 0 && _prevMacd != 0;
// Buy: RSI was recently oversold + MACD crosses above zero
if (wasOversold && macdCrossUp && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Sell: RSI was recently overbought + MACD crosses below zero
else if (wasOverbought && macdCrossDown && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit long on MACD cross down
else if (Position > 0 && macdCrossDown)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short on MACD cross up
else if (Position < 0 && macdCrossUp)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
_prevMacd = macdVal;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex, MovingAverageConvergenceDivergence
from StockSharp.Algo.Strategies import Strategy
import sys
class macd_long_strategy(Strategy):
"""MACD Long Strategy. RSI lookback + MACD zero crossover."""
def __init__(self):
super(macd_long_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._rsi_oversold = self.Param("RsiOversold", 40) \
.SetDisplay("RSI Oversold", "Oversold level", "RSI")
self._rsi_overbought = self.Param("RsiOverbought", 60) \
.SetDisplay("RSI Overbought", "Overbought level", "RSI")
self._lookback_bars = self.Param("LookbackBars", 20) \
.SetDisplay("Lookback Bars", "Bars to look back for RSI conditions", "Strategy")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._rsi = None
self._macd = None
self._bars_since_oversold = sys.maxsize
self._bars_since_overbought = sys.maxsize
self._prev_macd = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(macd_long_strategy, self).OnReseted()
self._rsi = None
self._macd = None
self._bars_since_oversold = sys.maxsize
self._bars_since_overbought = sys.maxsize
self._prev_macd = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(macd_long_strategy, self).OnStarted2(time)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = int(self._rsi_length.Value)
self._macd = MovingAverageConvergenceDivergence()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._rsi, self._macd, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_val, macd_val):
if candle.State != CandleStates.Finished:
return
if not self._rsi.IsFormed or not self._macd.IsFormed:
self._prev_macd = float(macd_val)
return
rsi = float(rsi_val)
macd = float(macd_val)
rsi_os = int(self._rsi_oversold.Value)
rsi_ob = int(self._rsi_overbought.Value)
# Track RSI oversold/overbought
if rsi <= rsi_os:
self._bars_since_oversold = 0
else:
self._bars_since_oversold = min(self._bars_since_oversold + 1, sys.maxsize - 1)
if rsi >= rsi_ob:
self._bars_since_overbought = 0
else:
self._bars_since_overbought = min(self._bars_since_overbought + 1, sys.maxsize - 1)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_macd = macd
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_macd = macd
return
cooldown = int(self._cooldown_bars.Value)
lookback = int(self._lookback_bars.Value)
was_oversold = self._bars_since_oversold <= lookback
was_overbought = self._bars_since_overbought <= lookback
macd_cross_up = macd > 0 and self._prev_macd <= 0 and self._prev_macd != 0
macd_cross_down = macd < 0 and self._prev_macd >= 0 and self._prev_macd != 0
if was_oversold and macd_cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif was_overbought and macd_cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
elif self.Position > 0 and macd_cross_down:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and macd_cross_up:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._prev_macd = macd
def CreateClone(self):
return macd_long_strategy()