Estrategia Heikin Ashi V2
Esta segunda versión del sistema Heikin Ashi añade un filtro EMA. Las operaciones ocurren solo cuando la dirección de la vela Heikin Ashi concuerda con la tendencia definida por la EMA. El filtro ayuda a evitar señales en contra de la tendencia que el enfoque HA puro podría generar.
Detalles
- Criterios de entrada:
- Largo:
HA_Close > HA_OpenyClose > EMA - Corto:
HA_Close < HA_OpenyClose < EMA
- Largo:
- Largo/Corto: Ambos
- Criterios de salida:
- Señal opuesta
- Stops: Ninguno
- Valores predeterminados:
EmaLength= 20
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Ambos
- Indicadores: Heikin Ashi, EMA
- Stops: No
- Complejidad: Bajo
- Marco temporal: Corto plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Heikin Ashi Strategy V2.
/// Uses fast and slow EMA crossover with Heikin-Ashi color confirmation.
/// Buys on bullish EMA cross when HA candle is green.
/// Sells on bearish EMA cross when HA candle is red.
/// </summary>
public class HeikinAshiV2Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleTypeParam;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _cooldownBars;
private ExponentialMovingAverage _fastEma;
private ExponentialMovingAverage _slowEma;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _prevHaOpen;
private decimal _prevHaClose;
private int _cooldownRemaining;
public HeikinAshiV2Strategy()
{
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
_fastPeriod = Param(nameof(FastPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Moving Averages");
_slowPeriod = Param(nameof(SlowPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Moving Averages");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fastEma = null;
_slowEma = null;
_prevFast = 0;
_prevSlow = 0;
_prevHaOpen = 0;
_prevHaClose = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fastEma = new ExponentialMovingAverage { Length = FastPeriod };
_slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_fastEma, _slowEma, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _fastEma);
DrawIndicator(area, _slowEma);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
// Calculate Heikin-Ashi
decimal haOpen, haClose;
if (_prevHaOpen == 0)
{
haOpen = (candle.OpenPrice + candle.ClosePrice) / 2;
haClose = (candle.OpenPrice + candle.ClosePrice + candle.HighPrice + candle.LowPrice) / 4;
}
else
{
haOpen = (_prevHaOpen + _prevHaClose) / 2;
haClose = (candle.OpenPrice + candle.ClosePrice + candle.HighPrice + candle.LowPrice) / 4;
}
_prevHaOpen = haOpen;
_prevHaClose = haClose;
if (!_fastEma.IsFormed || !_slowEma.IsFormed)
{
_prevFast = fast;
_prevSlow = slow;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevFast == 0)
{
_prevFast = fast;
_prevSlow = slow;
return;
}
var haGreen = haClose > haOpen;
var haRed = haClose < haOpen;
// Bullish: fast crosses above slow + HA is green
var bullishCross = fast > slow && _prevFast <= _prevSlow && haGreen;
// Bearish: fast crosses below slow + HA is red
var bearishCross = fast < slow && _prevFast >= _prevSlow && haRed;
if (bullishCross && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
else if (bearishCross && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
_prevFast = fast;
_prevSlow = slow;
}
}
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.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class heikin_ashi_v2_strategy(Strategy):
"""Heikin Ashi V2 Strategy. Fast/slow EMA crossover with HA color confirmation."""
def __init__(self):
super(heikin_ashi_v2_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General")
self._fast_period = self.Param("FastPeriod", 5) \
.SetDisplay("Fast EMA", "Fast EMA period", "Moving Averages")
self._slow_period = self.Param("SlowPeriod", 20) \
.SetDisplay("Slow EMA", "Slow EMA period", "Moving Averages")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._fast_ema = None
self._slow_ema = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._prev_ha_open = 0.0
self._prev_ha_close = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(heikin_ashi_v2_strategy, self).OnReseted()
self._fast_ema = None
self._slow_ema = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._prev_ha_open = 0.0
self._prev_ha_close = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(heikin_ashi_v2_strategy, self).OnStarted2(time)
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = int(self._fast_period.Value)
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = int(self._slow_period.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._fast_ema, self._slow_ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._fast_ema)
self.DrawIndicator(area, self._slow_ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
# Calculate Heikin-Ashi
if self._prev_ha_open == 0.0:
ha_open = (float(candle.OpenPrice) + float(candle.ClosePrice)) / 2.0
ha_close = (float(candle.OpenPrice) + float(candle.ClosePrice) + float(candle.HighPrice) + float(candle.LowPrice)) / 4.0
else:
ha_open = (self._prev_ha_open + self._prev_ha_close) / 2.0
ha_close = (float(candle.OpenPrice) + float(candle.ClosePrice) + float(candle.HighPrice) + float(candle.LowPrice)) / 4.0
self._prev_ha_open = ha_open
self._prev_ha_close = ha_close
if not self._fast_ema.IsFormed or not self._slow_ema.IsFormed:
self._prev_fast = float(fast)
self._prev_slow = float(slow)
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_fast = float(fast)
self._prev_slow = float(slow)
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_fast = float(fast)
self._prev_slow = float(slow)
return
f = float(fast)
s = float(slow)
cooldown = int(self._cooldown_bars.Value)
if self._prev_fast == 0.0:
self._prev_fast = f
self._prev_slow = s
return
ha_green = ha_close > ha_open
ha_red = ha_close < ha_open
bullish_cross = f > s and self._prev_fast <= self._prev_slow and ha_green
bearish_cross = f < s and self._prev_fast >= self._prev_slow and ha_red
if bullish_cross and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif bearish_cross and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
self._prev_fast = f
self._prev_slow = s
def CreateClone(self):
return heikin_ashi_v2_strategy()