Universelle Heikin Ashi-Strategie
Diese universelle Vorlage konvertiert Standardkerzen in Heikin Ashi-Kerzen und handelt in Richtung ihres Körpers. Die Methode glättet Preisrauschen, sodass Trends klarer sichtbar werden. Sie ist leichtgewichtig und kann als Basis für benutzerdefinierte Filter oder Ausstiege dienen.
Das System steigt long ein, wenn der Heikin Ashi-Schlusskurs über seinem Eröffnungskurs liegt, und wechselt short, wenn der Schlusskurs unter den Eröffnungskurs fällt.
Details
- Einstiegskriterien:
- Long:
HA_Close > HA_Open - Short:
HA_Close < HA_Open
- Long:
- Long/Short: Beide
- Ausstiegskriterien:
- Entgegengesetztes Signal
- Stops: Keine
- Standardwerte:
CandleType= 1 minute
- Filter:
- Kategorie: Trendfolge
- Richtung: Beide
- Indikatoren: Heikin Ashi
- Stops: Nein
- Komplexität: Niedrig
- Zeitrahmen: Kurzfristig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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 Universal Strategy.
/// Uses fast and slow EMAs for trend detection (simulating HA smoothed signals).
/// Buys on bullish EMA crossover, sells on bearish EMA crossover.
/// </summary>
public class HaUniversalStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleTypeParam;
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _cooldownBars;
private ExponentialMovingAverage _fastEma;
private ExponentialMovingAverage _slowEma;
private decimal _prevFast;
private decimal _prevSlow;
private int _cooldownRemaining;
public HaUniversalStrategy()
{
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
_fastLength = Param(nameof(FastLength), 5)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Strategy");
_slowLength = Param(nameof(SlowLength), 20)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Strategy");
_cooldownBars = Param(nameof(CooldownBars), 15)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.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;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fastEma = new ExponentialMovingAverage { Length = FastLength };
_slowEma = new ExponentialMovingAverage { Length = SlowLength };
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;
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;
}
// Bullish crossover
var bullishCross = fast > slow && _prevFast <= _prevSlow;
// Bearish crossover
var bearishCross = fast < slow && _prevFast >= _prevSlow;
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 ha_universal_strategy(Strategy):
"""Heikin Ashi Universal Strategy. Fast/slow EMA crossover."""
def __init__(self):
super(ha_universal_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_length = self.Param("FastLength", 5) \
.SetDisplay("Fast EMA", "Fast EMA period", "Strategy")
self._slow_length = self.Param("SlowLength", 20) \
.SetDisplay("Slow EMA", "Slow EMA period", "Strategy")
self._cooldown_bars = self.Param("CooldownBars", 15) \
.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._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ha_universal_strategy, self).OnReseted()
self._fast_ema = None
self._slow_ema = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(ha_universal_strategy, self).OnStarted2(time)
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = int(self._fast_length.Value)
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = int(self._slow_length.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
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
bullish_cross = f > s and self._prev_fast <= self._prev_slow
bearish_cross = f < s and self._prev_fast >= self._prev_slow
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 ha_universal_strategy()