Последовательные свечи Heikin Ashi
Стратегия основана на серии подряд идущих свечей Heikin Ashi.
Тестирование показывает среднегодичную доходность около 73%. Стратегию лучше запускать на крипторынке.
Heikin Ashi Consecutive ожидает несколько свечей одного цвета для подтверждения импульса. После серии бычьих или медвежьих баров стратегия присоединяется к движению и выходит при первой противоположной свече либо по стопу ATR.
Так как графики Heikin Ashi сглаживают ценовые данные, последовательность свечей одинакового цвета подчёркивает сильное направленное движение. Скользящий стоп ATR помогает зафиксировать прибыль при резком развороте последовательности.
Подробности
- Критерии входа: сигналы на основе Heikin.
- Длинные/короткие: оба направления.
- Критерии выхода: противоположный сигнал или стоп.
- Стопы: да.
- Параметры по умолчанию:
ConsecutiveCandles= 3StopLossPercent= 2мCandleType= TimeSpan.FromMinutes(5)
- Фильтры:
- Категория: Тренд
- Направление: Оба
- Индикаторы: Heikin
- Стопы: Да
- Сложность: Базовая
- Таймфрейм: Внутридневной (5м)
- Сезонность: Нет
- Нейронные сети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on consecutive Heikin Ashi candles.
/// It enters long position after a sequence of bullish Heikin Ashi candles and
/// short position after a sequence of bearish Heikin Ashi candles.
/// </summary>
public class HeikinAshiConsecutiveStrategy : Strategy
{
private readonly StrategyParam<int> _consecutiveCandles;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
// State tracking
private int _bullishCount;
private int _bearishCount;
private decimal _prevHaOpen;
private decimal _prevHaClose;
private decimal _prevHaHigh;
private decimal _prevHaLow;
/// <summary>
/// Number of consecutive candles required for signal.
/// </summary>
public int ConsecutiveCandles
{
get => _consecutiveCandles.Value;
set => _consecutiveCandles.Value = value;
}
/// <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>
/// Initialize the Heikin Ashi Consecutive strategy.
/// </summary>
public HeikinAshiConsecutiveStrategy()
{
_consecutiveCandles = Param(nameof(ConsecutiveCandles), 7)
.SetDisplay("Consecutive Candles", "Number of consecutive candles required for signal", "Trading parameters")
.SetOptimize(5, 10, 1);
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetDisplay("Stop Loss (%)", "Stop loss as a percentage of entry price", "Risk parameters")
.SetOptimize(1, 3, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).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();
_bullishCount = default;
_bearishCount = default;
_prevHaOpen = default;
_prevHaClose = default;
_prevHaHigh = default;
_prevHaLow = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create subscription
var subscription = SubscribeCandles(CandleType);
// We need to calculate Heikin-Ashi candles in the ProcessCandle handler
subscription
.Bind(ProcessCandle)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
// Start protection with stop loss
StartProtection(
takeProfit: null,
stopLoss: new Unit(StopLossPercent, UnitTypes.Percent)
);
}
private void ProcessCandle(ICandleMessage candle)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Check if strategy is ready to trade
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Calculate Heikin-Ashi values
decimal haOpen, haClose, haHigh, haLow;
if (_prevHaOpen == 0)
{
// First candle - initialize Heikin-Ashi values
haOpen = (candle.OpenPrice + candle.ClosePrice) / 2;
haClose = (candle.OpenPrice + candle.ClosePrice + candle.HighPrice + candle.LowPrice) / 4;
haHigh = candle.HighPrice;
haLow = candle.LowPrice;
}
else
{
// Calculate Heikin-Ashi values based on previous HA candle
haOpen = (_prevHaOpen + _prevHaClose) / 2;
haClose = (candle.OpenPrice + candle.ClosePrice + candle.HighPrice + candle.LowPrice) / 4;
haHigh = Math.Max(Math.Max(candle.HighPrice, haOpen), haClose);
haLow = Math.Min(Math.Min(candle.LowPrice, haOpen), haClose);
}
// Determine if Heikin-Ashi candle is bullish or bearish
var isBullish = haClose > haOpen;
var isBearish = haClose < haOpen;
// Update consecutive counts
if (isBullish)
{
_bullishCount++;
_bearishCount = 0;
}
else if (isBearish)
{
_bearishCount++;
_bullishCount = 0;
}
else
{
// Neutral candle (rare case) - reset both counts
_bullishCount = 0;
_bearishCount = 0;
}
// Trading logic - enter/reverse on consecutive candles
if (_bullishCount >= ConsecutiveCandles && Position <= 0)
{
// Enough consecutive bullish candles - Buy signal
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
}
else if (_bearishCount >= ConsecutiveCandles && Position >= 0)
{
// Enough consecutive bearish candles - Sell signal
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
}
// Store current Heikin-Ashi values for next candle
_prevHaOpen = haOpen;
_prevHaClose = haClose;
_prevHaHigh = haHigh;
_prevHaLow = haLow;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class heikin_ashi_consecutive_strategy(Strategy):
def __init__(self):
super(heikin_ashi_consecutive_strategy, self).__init__()
self._consecutive_candles = self.Param("ConsecutiveCandles", 7) \
.SetDisplay("Consecutive Candles", "Number of consecutive candles required for signal", "Trading parameters")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetDisplay("Stop Loss (%)", "Stop loss as a percentage of entry price", "Risk parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._bullish_count = 0
self._bearish_count = 0
self._prev_ha_open = 0.0
self._prev_ha_close = 0.0
self._prev_ha_high = 0.0
self._prev_ha_low = 0.0
@property
def ConsecutiveCandles(self):
return self._consecutive_candles.Value
@ConsecutiveCandles.setter
def ConsecutiveCandles(self, value):
self._consecutive_candles.Value = value
@property
def StopLossPercent(self):
return self._stop_loss_percent.Value
@StopLossPercent.setter
def StopLossPercent(self, value):
self._stop_loss_percent.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(heikin_ashi_consecutive_strategy, self).OnStarted2(time)
self._bullish_count = 0
self._bearish_count = 0
self._prev_ha_open = 0.0
self._prev_ha_close = 0.0
self._prev_ha_high = 0.0
self._prev_ha_low = 0.0
self.SubscribeCandles(self.CandleType) \
.Bind(self.ProcessCandle) \
.Start()
self.StartProtection(
takeProfit=None,
stopLoss=Unit(float(self.StopLossPercent), UnitTypes.Percent)
)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
o = float(candle.OpenPrice)
c = float(candle.ClosePrice)
h = float(candle.HighPrice)
l = float(candle.LowPrice)
if self._prev_ha_open == 0:
ha_open = (o + c) / 2.0
ha_close = (o + c + h + l) / 4.0
ha_high = h
ha_low = l
else:
ha_open = (self._prev_ha_open + self._prev_ha_close) / 2.0
ha_close = (o + c + h + l) / 4.0
ha_high = max(max(h, ha_open), ha_close)
ha_low = min(min(l, ha_open), ha_close)
is_bullish = ha_close > ha_open
is_bearish = ha_close < ha_open
if is_bullish:
self._bullish_count += 1
self._bearish_count = 0
elif is_bearish:
self._bearish_count += 1
self._bullish_count = 0
else:
self._bullish_count = 0
self._bearish_count = 0
consec = int(self.ConsecutiveCandles)
if self._bullish_count >= consec and self.Position <= 0:
volume = self.Volume + Math.Abs(self.Position)
self.BuyMarket(volume)
elif self._bearish_count >= consec and self.Position >= 0:
volume = self.Volume + Math.Abs(self.Position)
self.SellMarket(volume)
self._prev_ha_open = ha_open
self._prev_ha_close = ha_close
self._prev_ha_high = ha_high
self._prev_ha_low = ha_low
def OnReseted(self):
super(heikin_ashi_consecutive_strategy, self).OnReseted()
self._bullish_count = 0
self._bearish_count = 0
self._prev_ha_open = 0.0
self._prev_ha_close = 0.0
self._prev_ha_high = 0.0
self._prev_ha_low = 0.0
def CreateClone(self):
return heikin_ashi_consecutive_strategy()