Estratégia de Reversão com Hull MA Confirmada por Duplo CCI
Esta estratégia entra comprado quando o preço cruza acima da Hull Moving Average com confirmação dos indicadores CCI rápido e lento. Uma EMA de rastreamento gerencia o lucro após uma ativação baseada em ATR.
Os testes mostram retorno anual moderado. Funciona melhor em mercados mistos.
Detalhes
- Critérios de entrada:
- Comprado: Preço cruza acima do HMA, fechamento acima do HMA, CCI rápido > 0, CCI lento > 0
- Comprado/Vendido: Somente comprado.
- Critérios de saída:
- Comprado: Fechamento abaixo da EMA de rastreamento após ativação ou mínima atinge o stop ATR
- Stops: Sim.
- Valores padrão:
StopLossAtrMultiplier= 1.75TrailingActivationMultiplier= 2.25FastCciPeriod= 25SlowCciPeriod= 50HullMaLength= 34TrailingEmaLength= 20AtrPeriod= 14CandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoria: Reversão
- Direção: Somente comprado
- Indicadores: CCI, HMA, EMA, ATR
- Stops: Sim
- Complexidade: Intermediário
- Período: Intradiário
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
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>
/// Double CCI Confirmed Hull MA Reversal strategy.
/// Long only: buys when the close crosses above the Hull Moving Average while both the fast and the slow CCI are above zero.
/// At entry the stop is set StopLossAtrMultiplier ATRs below the close and the trailing activation level TrailingActivationMultiplier
/// ATRs above it. The long closes when a candle low reaches the stop, or, once a high has reached the activation level, when the
/// close falls below the trailing EMA.
/// </summary>
public class DoubleCciConfirmedHullMovingAverageReversalStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossAtrMultiplier;
private readonly StrategyParam<decimal> _trailingActivationMultiplier;
private readonly StrategyParam<int> _fastCciPeriod;
private readonly StrategyParam<int> _slowCciPeriod;
private readonly StrategyParam<int> _hullMaLength;
private readonly StrategyParam<int> _trailingEmaLength;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevClose;
private decimal? _prevHma;
private decimal _stopPrice;
private decimal _activationPrice;
private bool _trailingActive;
/// <summary>
/// Stop distance in ATRs below the entry close.
/// </summary>
public decimal StopLossAtrMultiplier
{
get => _stopLossAtrMultiplier.Value;
set => _stopLossAtrMultiplier.Value = value;
}
/// <summary>
/// Profit in ATRs that activates the trailing EMA exit.
/// </summary>
public decimal TrailingActivationMultiplier
{
get => _trailingActivationMultiplier.Value;
set => _trailingActivationMultiplier.Value = value;
}
/// <summary>
/// Fast CCI period.
/// </summary>
public int FastCciPeriod
{
get => _fastCciPeriod.Value;
set => _fastCciPeriod.Value = value;
}
/// <summary>
/// Slow CCI period.
/// </summary>
public int SlowCciPeriod
{
get => _slowCciPeriod.Value;
set => _slowCciPeriod.Value = value;
}
/// <summary>
/// Hull Moving Average period.
/// </summary>
public int HullMaLength
{
get => _hullMaLength.Value;
set => _hullMaLength.Value = value;
}
/// <summary>
/// Trailing EMA period.
/// </summary>
public int TrailingEmaLength
{
get => _trailingEmaLength.Value;
set => _trailingEmaLength.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public DoubleCciConfirmedHullMovingAverageReversalStrategy()
{
_stopLossAtrMultiplier = Param(nameof(StopLossAtrMultiplier), 1.75m)
.SetGreaterThanZero()
.SetDisplay("Stop ATR Mult", "Stop distance in ATRs below the entry close", "Risk");
_trailingActivationMultiplier = Param(nameof(TrailingActivationMultiplier), 2.25m)
.SetGreaterThanZero()
.SetDisplay("Trailing Activation Mult", "Profit in ATRs that activates the trailing EMA exit", "Risk");
_fastCciPeriod = Param(nameof(FastCciPeriod), 25)
.SetGreaterThanZero()
.SetDisplay("Fast CCI", "Fast CCI period", "Indicators");
_slowCciPeriod = Param(nameof(SlowCciPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Slow CCI", "Slow CCI period", "Indicators");
_hullMaLength = Param(nameof(HullMaLength), 34)
.SetGreaterThanZero()
.SetDisplay("Hull MA Length", "Hull Moving Average period", "Indicators");
_trailingEmaLength = Param(nameof(TrailingEmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("Trailing EMA", "Trailing EMA period", "Risk");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_prevClose = null;
_prevHma = null;
_stopPrice = 0m;
_activationPrice = 0m;
_trailingActive = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var hma = new HullMovingAverage { Length = HullMaLength };
var fastCci = new CommodityChannelIndex { Length = FastCciPeriod };
var slowCci = new CommodityChannelIndex { Length = SlowCciPeriod };
var trailingEma = new ExponentialMovingAverage { Length = TrailingEmaLength };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(hma, fastCci, slowCci, trailingEma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, hma);
DrawIndicator(area, trailingEma);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, fastCci);
DrawIndicator(oscillators, slowCci);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue hmaValue, IIndicatorValue fastCciValue, IIndicatorValue slowCciValue, IIndicatorValue emaValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!hmaValue.IsFormed || !fastCciValue.IsFormed || !slowCciValue.IsFormed || !emaValue.IsFormed || !atrValue.IsFormed)
return;
var hma = hmaValue.GetValue<decimal>();
var fastCci = fastCciValue.GetValue<decimal>();
var slowCci = slowCciValue.GetValue<decimal>();
var trailingEma = emaValue.GetValue<decimal>();
var atr = atrValue.GetValue<decimal>();
var close = candle.ClosePrice;
var prevClose = _prevClose;
var prevHma = _prevHma;
_prevClose = close;
_prevHma = hma;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0)
{
if (candle.LowPrice <= _stopPrice)
{
SellMarket(Position);
return;
}
if (candle.HighPrice >= _activationPrice)
_trailingActive = true;
if (_trailingActive && close < trailingEma)
SellMarket(Position);
return;
}
if (prevClose is not decimal pc || prevHma is not decimal ph)
return;
var crossUp = pc <= ph && close > hma;
if (Position == 0 && crossUp && fastCci > 0 && slowCci > 0)
{
_stopPrice = close - atr * StopLossAtrMultiplier;
_activationPrice = close + atr * TrailingActivationMultiplier;
_trailingActive = false;
BuyMarket(Volume);
}
}
}
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 HullMovingAverage, CommodityChannelIndex, ExponentialMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class double_cci_confirmed_hull_moving_average_reversal_strategy(Strategy):
"""
Double CCI Confirmed Hull MA Reversal strategy.
Long only: buys when the close crosses above the Hull Moving Average while both the fast and the slow CCI are above zero.
At entry the stop is set StopLossAtrMultiplier ATRs below the close and the trailing activation level TrailingActivationMultiplier
ATRs above it. The long closes when a candle low reaches the stop, or, once a high has reached the activation level, when the
close falls below the trailing EMA.
"""
def __init__(self):
super(double_cci_confirmed_hull_moving_average_reversal_strategy, self).__init__()
self._stop_loss_atr_multiplier = self.Param("StopLossAtrMultiplier", 1.75).SetGreaterThanZero().SetDisplay("Stop ATR Mult", "Stop distance in ATRs below the entry close", "Risk")
self._trailing_activation_multiplier = self.Param("TrailingActivationMultiplier", 2.25).SetGreaterThanZero().SetDisplay("Trailing Activation Mult", "Profit in ATRs that activates the trailing EMA exit", "Risk")
self._fast_cci_period = self.Param("FastCciPeriod", 25).SetGreaterThanZero().SetDisplay("Fast CCI", "Fast CCI period", "Indicators")
self._slow_cci_period = self.Param("SlowCciPeriod", 50).SetGreaterThanZero().SetDisplay("Slow CCI", "Slow CCI period", "Indicators")
self._hull_ma_length = self.Param("HullMaLength", 34).SetGreaterThanZero().SetDisplay("Hull MA Length", "Hull Moving Average period", "Indicators")
self._trailing_ema_length = self.Param("TrailingEmaLength", 20).SetGreaterThanZero().SetDisplay("Trailing EMA", "Trailing EMA period", "Risk")
self._atr_period = self.Param("AtrPeriod", 14).SetGreaterThanZero().SetDisplay("ATR Period", "ATR period", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_close = None
self._prev_hma = None
self._stop_price = Decimal(0)
self._activation_price = Decimal(0)
self._trailing_active = False
def OnReseted(self):
super(double_cci_confirmed_hull_moving_average_reversal_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(double_cci_confirmed_hull_moving_average_reversal_strategy, self).OnStarted2(time)
self._reset_state()
hma = HullMovingAverage()
hma.Length = self._hull_ma_length.Value
fast_cci = CommodityChannelIndex()
fast_cci.Length = self._fast_cci_period.Value
slow_cci = CommodityChannelIndex()
slow_cci.Length = self._slow_cci_period.Value
trailing_ema = ExponentialMovingAverage()
trailing_ema.Length = self._trailing_ema_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(hma, fast_cci, slow_cci, trailing_ema, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, hma)
self.DrawIndicator(area, trailing_ema)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, fast_cci)
self.DrawIndicator(oscillators, slow_cci)
def _process_candle(self, candle, hma_value, fast_cci_value, slow_cci_value, ema_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not hma_value.IsFormed or not fast_cci_value.IsFormed or not slow_cci_value.IsFormed or not ema_value.IsFormed or not atr_value.IsFormed:
return
hma = hma_value.GetValue[Decimal](None)
fast_cci = fast_cci_value.GetValue[Decimal](None)
slow_cci = slow_cci_value.GetValue[Decimal](None)
trailing_ema = ema_value.GetValue[Decimal](None)
atr = atr_value.GetValue[Decimal](None)
close = candle.ClosePrice
prev_close = self._prev_close
prev_hma = self._prev_hma
self._prev_close = close
self._prev_hma = hma
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position > 0:
if candle.LowPrice <= self._stop_price:
self.SellMarket(self.Position)
return
if candle.HighPrice >= self._activation_price:
self._trailing_active = True
if self._trailing_active and close < trailing_ema:
self.SellMarket(self.Position)
return
if prev_close is None or prev_hma is None:
return
cross_up = prev_close <= prev_hma and close > hma
if self.Position == 0 and cross_up and fast_cci > 0 and slow_cci > 0:
self._stop_price = close - atr * Decimal(self._stop_loss_atr_multiplier.Value)
self._activation_price = close + atr * Decimal(self._trailing_activation_multiplier.Value)
self._trailing_active = False
self.BuyMarket(self.Volume)
def CreateClone(self):
return double_cci_confirmed_hull_moving_average_reversal_strategy()