Os candles Heikin-Ashi suavizam o ruído e destacam a direção da tendência. Uma mudança de uma série de candles HA de baixa para um de alta, ou vice-versa, pode indicar uma mudança de momentum. Esta estratégia opera essas mudanças de cor e utiliza um stop percentual para proteção.
Os testes indicam um retorno anual médio de aproximadamente 145%. Funciona melhor no mercado de criptomoedas.
A lógica calcula os valores Heikin-Ashi a partir dos candles regulares. Quando o fechamento HA cruza acima da abertura HA após uma sequência de baixa, uma posição comprada é assumida. Um cruzamento abaixo após uma sequência de alta abre uma posição vendida. O stop é colocado a uma porcentagem fixa a partir da entrada.
O método é simples mas eficaz durante oscilações instáveis quando os gráficos de candles tradicionais são ruidosos.
Detalhes
Critérios de entrada: O candle Heikin-Ashi muda de cor.
Comprado/Vendido: Ambos.
Critérios de saída: Stop-loss.
Stops: Sim, baseado em percentual.
Valores padrão:
CandleType = 15 minute
StopLossPercent = 2
Filtros:
Categoria: Reversão
Direção: Ambos
Indicadores: Heikin-Ashi
Stops: Sim
Complexidade: Básico
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>
/// Heikin Ashi Reversal strategy.
/// Computes Heikin-Ashi candles from regular candles. A bullish Heikin-Ashi candle after bearish ones turns the position long,
/// a bearish one after bullish ones turns it short; a percent stop limits the loss.
/// </summary>
public class HeikinAshiReversalStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal? _haOpen;
private decimal _haClose;
private bool? _prevBullish;
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HeikinAshiReversalStrategy()
{
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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();
_haOpen = null;
_haClose = default;
_prevBullish = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_haOpen = null;
_haClose = default;
_prevBullish = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4;
var haOpen = _haOpen is decimal prevOpen
? (prevOpen + _haClose) / 2
: (candle.OpenPrice + candle.ClosePrice) / 2;
_haOpen = haOpen;
_haClose = haClose;
// A Heikin-Ashi candle without a body keeps the previous color.
if (haClose == haOpen)
return;
var isBullish = haClose > haOpen;
var wasBullish = _prevBullish;
_prevBullish = isBullish;
if (wasBullish is not bool previous || previous == isBullish || !IsFormedAndOnlineAndAllowTrading())
return;
if (isBullish && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (!isBullish && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Math, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
class heikin_ashi_reversal_strategy(Strategy):
"""
Heikin Ashi Reversal strategy.
Computes Heikin-Ashi candles from regular candles. A bullish Heikin-Ashi candle after bearish ones turns the position long,
a bearish one after bullish ones turns it short; a percent stop limits the loss.
"""
def __init__(self):
super(heikin_ashi_reversal_strategy, self).__init__()
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._ha_open = None
self._ha_close = Decimal(0)
self._prev_bullish = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(heikin_ashi_reversal_strategy, self).OnReseted()
self._ha_open = None
self._ha_close = Decimal(0)
self._prev_bullish = None
def OnStarted2(self, time):
super(heikin_ashi_reversal_strategy, self).OnStarted2(time)
self._ha_open = None
self._ha_close = Decimal(0)
self._prev_bullish = None
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
ha_close = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / Decimal(4)
if self._ha_open is None:
ha_open = (candle.OpenPrice + candle.ClosePrice) / Decimal(2)
else:
ha_open = (self._ha_open + self._ha_close) / Decimal(2)
self._ha_open = ha_open
self._ha_close = ha_close
# A Heikin-Ashi candle without a body keeps the previous color.
if ha_close == ha_open:
return
is_bullish = ha_close > ha_open
was_bullish = self._prev_bullish
self._prev_bullish = is_bullish
if was_bullish is None or was_bullish == is_bullish or not self.IsFormedAndOnlineAndAllowTrading():
return
if is_bullish and self.Position <= 0:
self.BuyMarket(self.Volume + Math.Abs(self.Position))
elif not is_bullish and self.Position >= 0:
self.SellMarket(self.Volume + Math.Abs(self.Position))
def CreateClone(self):
return heikin_ashi_reversal_strategy()