Стратегия Heiken Ashi Simplified EA
Паттерновая система на основе свечей Heikin Ashi. Стратегия отслеживает последовательность предыдущих открытий и закрытий Heikin Ashi. Когда три подряд закрытия выше (или ниже) соответствующих открытий, а сами открытия образуют замедляющуюся последовательность, следующая свеча может дать вход по пробою, если цена отходит от последнего открытия Heikin Ashi на заданное расстояние. Алгоритм наращивает позицию до установленного лимита.
Подробности
- Условия входа:
- Лонг: три предыдущих закрытия HA выше прошлых открытий и открытия формируют убывающую серию с уменьшающимся шагом.
- Шорт: три предыдущих закрытия HA ниже прошлых открытий и открытия формируют возрастающую серию с расширяющимся шагом.
- Лонг/Шорт: обе стороны
- Условия выхода:
- Противоположный сигнал
- Стопы: отсутствуют
- Значения по умолчанию:
CandleType= 1 часMaxPositions= 3DistancePoints= 300Volume= 1
- Фильтры:
- Категория: Паттерн-пробой
- Направление: обе стороны
- Индикаторы: Heikin Ashi
- Стопы: нет
- Сложность: средняя
- Таймфрейм: часовой
- Сезонность: нет
- Нейросети: нет
- Дивергенция: нет
- Уровень риска: средний
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on simplified Heikin Ashi pattern analysis.
/// </summary>
public class HeikenAshiSimplifiedEaStrategy : Strategy
{
private readonly StrategyParam<int> _maxPositions;
private readonly StrategyParam<int> _distancePoints;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _haOpen1;
private decimal _haOpen2;
private decimal _haOpen3;
private decimal _haOpen4;
private decimal _haClose1;
private decimal _haClose2;
private decimal _haClose3;
private decimal _priceDistance;
private int _positionCount;
private int _cooldownRemaining;
/// <summary>
/// Initializes a new instance of the <see cref="HeikenAshiSimplifiedEaStrategy"/> class.
/// </summary>
public HeikenAshiSimplifiedEaStrategy()
{
_maxPositions = Param(nameof(MaxPositions), 1)
.SetDisplay("Max Positions", "Maximum number of positions in direction", "General")
.SetOptimize(1, 5, 1);
_distancePoints = Param(nameof(DistancePoints), 400)
.SetDisplay("Distance Points", "Minimum distance in price steps from last HA open", "General")
.SetOptimize(50, 500, 50);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for Heikin Ashi calculation", "General");
_cooldownBars = Param(nameof(CooldownBars), 6)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
}
/// <summary>
/// Maximum number of positions allowed in one direction.
/// </summary>
public int MaxPositions
{
get => _maxPositions.Value;
set => _maxPositions.Value = value;
}
/// <summary>
/// Minimum distance in price steps from last Heikin Ashi open.
/// </summary>
public int DistancePoints
{
get => _distancePoints.Value;
set => _distancePoints.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Number of completed candles to wait after a position change.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_haOpen1 = 0m;
_haOpen2 = 0m;
_haOpen3 = 0m;
_haOpen4 = 0m;
_haClose1 = 0m;
_haClose2 = 0m;
_haClose3 = 0m;
_priceDistance = 0m;
_positionCount = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_priceDistance = DistancePoints * (Security.PriceStep ?? 1m);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
if (Position == 0)
_positionCount = 0;
if (_haOpen4 != 0m)
{
var direction = 0;
if (_haClose1 > _haOpen1 && _haClose2 > _haOpen2 && _haClose3 > _haOpen3)
direction = 1;
else if (_haClose1 < _haOpen1 && _haClose2 < _haOpen2 && _haClose3 < _haOpen3)
direction = -1;
if (direction != 0)
{
if (Position * direction < 0)
{
if (Position > 0)
SellMarket();
else if (Position < 0)
BuyMarket();
_positionCount = 0;
_cooldownRemaining = CooldownBars;
}
var distanceFromAnchor = candle.ClosePrice - _haOpen1;
if (_cooldownRemaining == 0 && distanceFromAnchor * direction > 0m && Math.Abs(distanceFromAnchor) >= _priceDistance)
{
if (direction > 0 && _positionCount < MaxPositions && Position <= 0)
{
BuyMarket();
_positionCount = 1;
_cooldownRemaining = CooldownBars;
}
else if (direction < 0 && _positionCount > -MaxPositions && Position >= 0)
{
SellMarket();
_positionCount = -1;
_cooldownRemaining = CooldownBars;
}
}
}
}
var haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4m;
var haOpen = _haOpen1 == 0m && _haClose1 == 0m
? (candle.OpenPrice + candle.ClosePrice) / 2m
: (_haOpen1 + _haClose1) / 2m;
_haOpen4 = _haOpen3;
_haOpen3 = _haOpen2;
_haOpen2 = _haOpen1;
_haOpen1 = haOpen;
_haClose3 = _haClose2;
_haClose2 = _haClose1;
_haClose1 = haClose;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class heiken_ashi_simplified_ea_strategy(Strategy):
def __init__(self):
super(heiken_ashi_simplified_ea_strategy, self).__init__()
self._max_positions = self.Param("MaxPositions", 1) \
.SetDisplay("Max Positions", "Maximum number of positions in direction", "General")
self._distance_points = self.Param("DistancePoints", 400) \
.SetDisplay("Distance Points", "Minimum distance in price steps from last HA open", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for Heikin Ashi calculation", "General")
self._cooldown_bars = self.Param("CooldownBars", 6) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading")
self._ha_open1 = 0.0
self._ha_open2 = 0.0
self._ha_open3 = 0.0
self._ha_open4 = 0.0
self._ha_close1 = 0.0
self._ha_close2 = 0.0
self._ha_close3 = 0.0
self._price_distance = 0.0
self._position_count = 0
self._cooldown_remaining = 0
@property
def max_positions(self):
return self._max_positions.Value
@property
def distance_points(self):
return self._distance_points.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(heiken_ashi_simplified_ea_strategy, self).OnReseted()
self._ha_open1 = 0.0
self._ha_open2 = 0.0
self._ha_open3 = 0.0
self._ha_open4 = 0.0
self._ha_close1 = 0.0
self._ha_close2 = 0.0
self._ha_close3 = 0.0
self._price_distance = 0.0
self._position_count = 0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(heiken_ashi_simplified_ea_strategy, self).OnStarted2(time)
step = self.Security.PriceStep if self.Security.PriceStep is not None else 1.0
self._price_distance = self.distance_points * float(step)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
if self.Position == 0:
self._position_count = 0
if self._ha_open4 != 0:
direction = 0
if self._ha_close1 > self._ha_open1 and self._ha_close2 > self._ha_open2 and self._ha_close3 > self._ha_open3:
direction = 1
elif self._ha_close1 < self._ha_open1 and self._ha_close2 < self._ha_open2 and self._ha_close3 < self._ha_open3:
direction = -1
if direction != 0:
if self.Position * direction < 0:
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._position_count = 0
self._cooldown_remaining = self.cooldown_bars
distance_from_anchor = float(candle.ClosePrice) - self._ha_open1
if self._cooldown_remaining == 0 and distance_from_anchor * direction > 0 and abs(distance_from_anchor) >= self._price_distance:
if direction > 0 and self._position_count < self.max_positions and self.Position <= 0:
self.BuyMarket()
self._position_count = 1
self._cooldown_remaining = self.cooldown_bars
elif direction < 0 and self._position_count > -self.max_positions and self.Position >= 0:
self.SellMarket()
self._position_count = -1
self._cooldown_remaining = self.cooldown_bars
o = float(candle.OpenPrice)
h = float(candle.HighPrice)
l = float(candle.LowPrice)
c = float(candle.ClosePrice)
ha_close = (o + h + l + c) / 4.0
if self._ha_open1 == 0 and self._ha_close1 == 0:
ha_open = (o + c) / 2.0
else:
ha_open = (self._ha_open1 + self._ha_close1) / 2.0
self._ha_open4 = self._ha_open3
self._ha_open3 = self._ha_open2
self._ha_open2 = self._ha_open1
self._ha_open1 = ha_open
self._ha_close3 = self._ha_close2
self._ha_close2 = self._ha_close1
self._ha_close1 = ha_close
def CreateClone(self):
return heiken_ashi_simplified_ea_strategy()