Стратегия Heikin Ashi V2
Вторая версия системы Heikin Ashi добавляет фильтр EMA. Сделки совершаются только тогда, когда направление свечи Heikin Ashi совпадает с трендом, определяемым EMA. Такой фильтр помогает избегать контртрендовых сигналов, которые может давать чистый HA.
Детали
- Критерии входа:
- Длинная позиция:
HA_Close > HA_OpenиClose > EMA - Короткая позиция:
HA_Close < HA_OpenиClose < EMA
- Длинная позиция:
- Длинные/короткие: обе стороны
- Критерии выхода:
- Противоположный сигнал
- Стопы: нет
- Значения по умолчанию:
EmaLength= 20
- Фильтры:
- Категория: Следование тренду
- Направление: Оба
- Индикаторы: Heikin Ashi, EMA
- Стопы: Нет
- Сложность: Низкая
- Таймфрейм: Краткосрочный
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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 V2 strategy.
/// Goes long when the Heikin Ashi candle is bullish and the close is above the EMA, and short when the Heikin Ashi
/// candle is bearish and the close is below the EMA. The opposite signal reverses the position.
/// </summary>
public class HeikinAshiV2Strategy : Strategy
{
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevHaOpen;
private decimal? _prevHaClose;
/// <summary>
/// EMA period.
/// </summary>
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HeikinAshiV2Strategy()
{
_emaLength = Param(nameof(EmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA trend filter period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHaOpen = null;
_prevHaClose = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHaOpen = null;
_prevHaClose = null;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue emaValue)
{
if (candle.State != CandleStates.Finished)
return;
var haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4m;
var haOpen = _prevHaOpen is decimal prevOpen && _prevHaClose is decimal prevClose
? (prevOpen + prevClose) / 2m
: (candle.OpenPrice + candle.ClosePrice) / 2m;
_prevHaOpen = haOpen;
_prevHaClose = haClose;
if (!emaValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var ema = emaValue.ToDecimal();
var close = candle.ClosePrice;
if (haClose > haOpen && close > ema && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (haClose < haOpen && close < ema && 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")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class heikin_ashi_v2_strategy(Strategy):
"""
Heikin Ashi V2 strategy.
Goes long when the Heikin Ashi candle is bullish and the close is above the EMA, and short when the Heikin Ashi
candle is bearish and the close is below the EMA. The opposite signal reverses the position.
"""
def __init__(self):
super(heikin_ashi_v2_strategy, self).__init__()
self._ema_length = self.Param("EmaLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("EMA Length", "EMA trend filter period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle type", "Candle type for strategy calculation", "General")
self._prev_ha_open = None
self._prev_ha_close = None
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(heikin_ashi_v2_strategy, self).OnReseted()
self._prev_ha_open = None
self._prev_ha_close = None
def OnStarted2(self, time):
super(heikin_ashi_v2_strategy, self).OnStarted2(time)
self._prev_ha_open = None
self._prev_ha_close = None
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(ema, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
open_price = float(candle.OpenPrice)
close = float(candle.ClosePrice)
ha_close = (open_price + float(candle.HighPrice) + float(candle.LowPrice) + close) / 4.0
if self._prev_ha_open is not None and self._prev_ha_close is not None:
ha_open = (self._prev_ha_open + self._prev_ha_close) / 2.0
else:
ha_open = (open_price + close) / 2.0
self._prev_ha_open = ha_open
self._prev_ha_close = ha_close
if not ema_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
ema = float(ema_value.GetValue[Decimal](None))
if ha_close > ha_open and close > ema and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif ha_close < ha_open and close < ema and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return heikin_ashi_v2_strategy()