Estrategia CCI Hook Reversal
La estrategia CCI Hook Reversal utiliza el Commodity Channel Index como disparador cuando engancha alejándose de una lectura extrema. Después de que el indicador supera +100 o cae por debajo de -100, frecuentemente se retrae rápidamente a medida que el impulso se detiene.
Las pruebas indican un rendimiento anual promedio de aproximadamente 169%. Funciona mejor en el mercado de criptomonedas.
Las operaciones largas ocurren cuando el CCI gira al alza desde la sobreventa mientras el precio aún imprime un nuevo mínimo marginal. Los cortos se inician cuando el CCI se vuelve desde la sobrecompra con el precio alcanzando nuevos máximos.
Cada operación lleva un pequeño stop fijo y se cierra cuando el CCI engancha de vuelta en la dirección opuesta o se alcanza el stop.
Detalles
- Criterios de entrada: señal del indicador
- Largo/Corto: Ambos
- Criterios de salida: stop-loss o señal opuesta
- Stops: Sí, basado en porcentaje
- Valores predeterminados:
CandleType= 15 minutosStopLoss= 2%
- Filtros:
- Categoría: Reversión
- Dirección: Ambos
- Indicadores: CCI
- Stops: Sí
- Complejidad: Intermedio
- 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>
/// CCI Hook Reversal strategy.
/// Enters long when CCI hooks up from oversold zone.
/// Enters short when CCI hooks down from overbought zone.
/// Exits when CCI crosses zero.
/// Uses cooldown to control trade frequency.
/// </summary>
public class CciHookReversalStrategy : Strategy
{
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<int> _oversoldLevel;
private readonly StrategyParam<int> _overboughtLevel;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal? _prevCci;
private int _cooldown;
/// <summary>
/// CCI period.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// Oversold level.
/// </summary>
public int OversoldLevel
{
get => _oversoldLevel.Value;
set => _oversoldLevel.Value = value;
}
/// <summary>
/// Overbought level.
/// </summary>
public int OverboughtLevel
{
get => _overboughtLevel.Value;
set => _overboughtLevel.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public CciHookReversalStrategy()
{
_cciPeriod = Param(nameof(CciPeriod), 20)
.SetRange(14, 30)
.SetDisplay("CCI Period", "Period for CCI", "CCI");
_oversoldLevel = Param(nameof(OversoldLevel), -100)
.SetRange(-150, -50)
.SetDisplay("Oversold", "Oversold level", "CCI");
_overboughtLevel = Param(nameof(OverboughtLevel), 100)
.SetRange(50, 150)
.SetDisplay("Overbought", "Overbought level", "CCI");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.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();
_prevCci = null;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCci = null;
_cooldown = 0;
var cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(cci, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, cci);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal cciValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_prevCci == null)
{
_prevCci = cciValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevCci = cciValue;
return;
}
// Hook up from oversold
var oversoldHookUp = _prevCci < OversoldLevel && cciValue > _prevCci;
// Hook down from overbought
var overboughtHookDown = _prevCci > OverboughtLevel && cciValue < _prevCci;
if (Position == 0 && oversoldHookUp)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && overboughtHookDown)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && cciValue < 0)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && cciValue > 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevCci = cciValue;
}
}
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 CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class cci_hook_reversal_strategy(Strategy):
"""
CCI Hook Reversal strategy.
Enters long when CCI hooks up from oversold zone.
Enters short when CCI hooks down from overbought zone.
Exits when CCI crosses zero.
"""
def __init__(self):
super(cci_hook_reversal_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 20).SetDisplay("CCI Period", "Period for CCI", "CCI")
self._oversold_level = self.Param("OversoldLevel", -100).SetDisplay("Oversold", "Oversold level", "CCI")
self._overbought_level = self.Param("OverboughtLevel", 100).SetDisplay("Overbought", "Overbought level", "CCI")
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", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._prev_cci = None
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(cci_hook_reversal_strategy, self).OnReseted()
self._prev_cci = None
self._cooldown = 0
def OnStarted2(self, time):
super(cci_hook_reversal_strategy, self).OnStarted2(time)
self._prev_cci = None
self._cooldown = 0
cci = CommodityChannelIndex()
cci.Length = self._cci_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(cci, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, cci)
self.DrawOwnTrades(area)
def _process_candle(self, candle, cci_val):
if candle.State != CandleStates.Finished:
return
cv = float(cci_val)
if self._prev_cci is None:
self._prev_cci = cv
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_cci = cv
return
cd = self._cooldown_bars.Value
oversold = self._oversold_level.Value
overbought = self._overbought_level.Value
# Hook up from oversold
oversold_hook_up = self._prev_cci < oversold and cv > self._prev_cci
# Hook down from overbought
overbought_hook_down = self._prev_cci > overbought and cv < self._prev_cci
if self.Position == 0 and oversold_hook_up:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and overbought_hook_down:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and cv < 0:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and cv > 0:
self.BuyMarket()
self._cooldown = cd
self._prev_cci = cv
def CreateClone(self):
return cci_hook_reversal_strategy()