Estrategia MACD CCI
Implementación de la estrategia MACD + CCI. Comprar cuando el MACD está por encima de la línea de señal y el CCI está por debajo de -100 (sobrevendido). Vender cuando el MACD está por debajo de la línea de señal y el CCI está por encima de 100 (sobrecomprado).
Las pruebas indican un retorno anual promedio de aproximadamente el 70%. Funciona mejor en el mercado de acciones.
Las oscilaciones del MACD resaltan los cambios de momentum; el CCI ayuda a cronometrar las entradas en retrocesos en esa dirección. Son posibles tanto operaciones largas como cortas.
Los traders que combinan momentum con osciladores pueden encontrar útil esta técnica. El control de riesgo utiliza un stop basado en ATR.
Detalles
- Criterios de entrada:
- Largo:
MACD > Signal && CCI < CciOversold - Corto:
MACD < Signal && CCI > CciOverbought
- Largo:
- Largo/Corto: Ambos
- Criterios de salida: Cruce del MACD en dirección opuesta
- Stops: Basados en porcentaje usando
StopLoss - Valores predeterminados:
FastPeriod= 12SlowPeriod= 26SignalPeriod= 9CciPeriod= 20CciOversold= -100mCciOverbought= 100mStopLoss= new Unit(2, UnitTypes.Percent)CandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoría: Reversión a la media
- Dirección: Ambos
- Indicadores: MACD, CCI
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Medio plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;
using StockSharp.Algo.Candles;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Implementation of strategy - MACD + CCI.
/// Buy when MACD is above Signal line and CCI is below -100 (oversold).
/// Sell when MACD is below Signal line and CCI is above 100 (overbought).
/// </summary>
public class MacdCciStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _signalPeriod;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _cciOversold;
private readonly StrategyParam<decimal> _cciOverbought;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<Unit> _stopLoss;
private readonly StrategyParam<DataType> _candleType;
private int _cooldown;
private bool _hasPrevMacdState;
private bool _prevMacdAboveSignal;
/// <summary>
/// MACD fast period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// MACD slow period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// MACD signal period.
/// </summary>
public int SignalPeriod
{
get => _signalPeriod.Value;
set => _signalPeriod.Value = value;
}
/// <summary>
/// CCI period.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// CCI oversold level.
/// </summary>
public decimal CciOversold
{
get => _cciOversold.Value;
set => _cciOversold.Value = value;
}
/// <summary>
/// CCI overbought level.
/// </summary>
public decimal CciOverbought
{
get => _cciOverbought.Value;
set => _cciOverbought.Value = value;
}
/// <summary>
/// Bars to wait between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Stop-loss value.
/// </summary>
public Unit StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Candle type used for strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="MacdCciStrategy"/>.
/// </summary>
public MacdCciStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA period for MACD", "MACD Parameters");
_slowPeriod = Param(nameof(SlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA period for MACD", "MACD Parameters");
_signalPeriod = Param(nameof(SignalPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Signal Period", "Signal line period for MACD", "MACD Parameters");
_cciPeriod = Param(nameof(CciPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "Period for Commodity Channel Index", "CCI Parameters");
_cciOversold = Param(nameof(CciOversold), -100m)
.SetDisplay("CCI Oversold", "CCI level to consider market oversold", "CCI Parameters");
_cciOverbought = Param(nameof(CciOverbought), 100m)
.SetDisplay("CCI Overbought", "CCI level to consider market overbought", "CCI Parameters");
_cooldownBars = Param(nameof(CooldownBars), 320)
.SetRange(5, 1000)
.SetDisplay("Cooldown Bars", "Bars between trades", "General");
_stopLoss = Param(nameof(StopLoss), new Unit(2, UnitTypes.Percent))
.SetDisplay("Stop Loss", "Stop loss percent or value", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle type for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cooldown = 0;
_hasPrevMacdState = false;
_prevMacdAboveSignal = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = FastPeriod },
LongMa = { Length = SlowPeriod },
},
SignalMa = { Length = SignalPeriod }
};
var cci = new CommodityChannelIndex { Length = CciPeriod };
// Setup candle subscription
var subscription = SubscribeCandles(CandleType);
// Bind indicators to candles
subscription
.BindEx(macd, cci, ProcessCandle)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, macd);
// Create separate area for CCI
var cciArea = CreateChartArea();
if (cciArea != null)
{
DrawIndicator(cciArea, cci);
}
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue, IIndicatorValue cciValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!macdValue.IsFormed || !cciValue.IsFormed)
return;
// Note: In this implementation, the MACD and signal values are obtained separately.
// We need to extract both MACD and signal values to determine crossovers.
// For demonstration, we'll access these values through a direct call to the indicator.
// In a proper implementation, we should find a way to get these values through Bind parameter values.
// Get MACD line and Signal line values
// This approach is not ideal - in a proper implementation, these values should come from the Bind parameters
var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
var macdLine = macdTyped.Macd; // The main MACD line
var signalLine = macdTyped.Signal; // Signal line
// Determine if MACD is above or below signal line
var isMacdAboveSignal = macdLine > signalLine;
var cciDec = cciValue.ToDecimal();
if (!_hasPrevMacdState)
{
_hasPrevMacdState = true;
_prevMacdAboveSignal = isMacdAboveSignal;
return;
}
LogInfo($"Candle: {candle.OpenTime}, Close: {candle.ClosePrice}, " +
$"MACD: {macdLine}, Signal: {signalLine}, " +
$"MACD > Signal: {isMacdAboveSignal}, CCI: {cciDec}");
if (_cooldown > 0)
{
_cooldown--;
_prevMacdAboveSignal = isMacdAboveSignal;
return;
}
var crossedUp = !_prevMacdAboveSignal && isMacdAboveSignal;
var crossedDown = _prevMacdAboveSignal && !isMacdAboveSignal;
// Trading rules
if (crossedUp && cciDec < CciOversold && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
LogInfo($"Buy signal: MACD crossed above Signal and CCI oversold ({cciDec} < {CciOversold}).");
}
else if (crossedDown && cciDec > CciOverbought && Position == 0)
{
SellMarket();
_cooldown = CooldownBars;
LogInfo($"Sell signal: MACD crossed below Signal and CCI overbought ({cciDec} > {CciOverbought}).");
}
// Exit conditions based on MACD crossovers
else if (crossedDown && Position > 0)
{
// Exit long position when MACD crosses below signal
SellMarket();
_cooldown = CooldownBars;
LogInfo($"Exit long: MACD crossed below Signal. Position: {Position}");
}
else if (crossedUp && Position < 0)
{
// Exit short position when MACD crosses above signal
BuyMarket();
_cooldown = CooldownBars;
LogInfo($"Exit short: MACD crossed above Signal. Position: {Position}");
}
_prevMacdAboveSignal = isMacdAboveSignal;
}
}
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, CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class macd_cci_strategy(Strategy):
"""
MACD + CCI strategy.
Buy when MACD crosses above Signal and CCI is oversold.
Sell when MACD crosses below Signal and CCI is overbought.
"""
def __init__(self):
super(macd_cci_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12) \
.SetDisplay("Fast Period", "Fast EMA period for MACD", "MACD Parameters")
self._slow_period = self.Param("SlowPeriod", 26) \
.SetDisplay("Slow Period", "Slow EMA period for MACD", "MACD Parameters")
self._signal_period = self.Param("SignalPeriod", 9) \
.SetDisplay("Signal Period", "Signal line period for MACD", "MACD Parameters")
self._cci_period = self.Param("CciPeriod", 20) \
.SetDisplay("CCI Period", "Period for Commodity Channel Index", "CCI Parameters")
self._cci_oversold = self.Param("CciOversold", -100.0) \
.SetDisplay("CCI Oversold", "CCI level to consider market oversold", "CCI Parameters")
self._cci_overbought = self.Param("CciOverbought", 100.0) \
.SetDisplay("CCI Overbought", "CCI level to consider market overbought", "CCI Parameters")
self._cooldown_bars = self.Param("CooldownBars", 320) \
.SetRange(5, 1000) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Candle type for strategy", "General")
self._cooldown = 0
self._has_prev_macd_state = False
self._prev_macd_above_signal = False
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(macd_cci_strategy, self).OnStarted2(time)
self._cooldown = 0
self._has_prev_macd_state = False
self._prev_macd_above_signal = False
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
cci = CommodityChannelIndex()
cci.Length = self._cci_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(macd, cci, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, macd)
cci_area = self.CreateChartArea()
if cci_area is not None:
self.DrawIndicator(cci_area, cci)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, macd_value, cci_value):
if candle.State != CandleStates.Finished:
return
if not macd_value.IsFormed or not cci_value.IsFormed:
return
if macd_value.Macd is None or macd_value.Signal is None:
return
macd_line = float(macd_value.Macd)
signal_line = float(macd_value.Signal)
cci_dec = float(cci_value)
is_macd_above_signal = macd_line > signal_line
if not self._has_prev_macd_state:
self._has_prev_macd_state = True
self._prev_macd_above_signal = is_macd_above_signal
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_macd_above_signal = is_macd_above_signal
return
crossed_up = not self._prev_macd_above_signal and is_macd_above_signal
crossed_down = self._prev_macd_above_signal and not is_macd_above_signal
cd = self._cooldown_bars.Value
if crossed_up and cci_dec < self._cci_oversold.Value and self.Position == 0:
self.BuyMarket()
self._cooldown = cd
elif crossed_down and cci_dec > self._cci_overbought.Value and self.Position == 0:
self.SellMarket()
self._cooldown = cd
elif crossed_down and self.Position > 0:
self.SellMarket()
self._cooldown = cd
elif crossed_up and self.Position < 0:
self.BuyMarket()
self._cooldown = cd
self._prev_macd_above_signal = is_macd_above_signal
def OnReseted(self):
super(macd_cci_strategy, self).OnReseted()
self._cooldown = 0
self._has_prev_macd_state = False
self._prev_macd_above_signal = False
def CreateClone(self):
return macd_cci_strategy()