Auf GitHub ansehen

Heikin Ashi Trader-Strategie

Diese Strategie portiert den MetaTrader 4 Experten "Heikin Ashi Trader" nach StockSharp. Sie behält die Multi-Indikator-Bestätigungslogik des ursprünglichen Roboters und implementiert sie mit der High-Level-Kerzensubskriptions-API, sodass jede Entscheidung ausschließlich auf abgeschlossenen Bars basiert.

Details

  • Indikatoren:
    • Heikin-Ashi-Kerzen, berechnet aus dem Arbeitszeitrahmen.
    • Zwei linear gewichtete gleitende Durchschnitte (LWMA) mit dem typischen Kerzenpreis ((high + low + close) / 3).
    • Ein stochastischer Oszillator (%K/%D/Smooth-Perioden sind benutzerkonfigurierbar).
    • Momentum (Abstand vom neutralen 100-Level).
    • Moving Average Convergence Divergence (MACD).
  • Einstiegskriterien:
    • Long: Die letzte Heikin-Ashi-Kerze muss bullisch sein, mindestens einer der letzten drei stochastischen Werte muss über dem Überkauft-Level liegen, der schnelle LWMA muss über dem langsamen LWMA liegen, der Momentum-Abstand von 100 muss den Kaufschwellenwert überschreiten, und die MACD-Linie muss über ihrem Signal liegen.
    • Short: Spiegelbedingungen — bärische Heikin-Ashi-Kerze, Stochastik unter dem Überverkauft-Level, schneller LWMA unter langsamem LWMA, Momentum-Abstand über dem Verkaufsschwellenwert, und MACD-Linie unter ihrem Signal.
    • Optionales Glätten der entgegengesetzten Exposition vor dem neuen Trade (CloseOppositePositions).
  • Positionsmanagement:
    • Fester Stop-Loss und Take-Profit in Pips (abgeleitet aus dem Wertpapier-Preisschritt).
    • Optionaler Trailing-Stop, der dem Schlusskurs folgt, sobald der Trade um TrailingStopPips vorrückt.
    • Break-Even-Logik, die den Stop nach BreakEvenTriggerPips Preisfortschritt auf Entry ± BreakEvenOffsetPips verschiebt.
    • Manueller Kill-Switch (ForceExit) zum Glätten aller Positionen bei der nächsten Kerze.
  • Unterschiede zur MT4-Version:
    • Der ursprüngliche EA bewertete Momentum auf einem höheren Zeitrahmen. Dieser Port behält dieselben Indikatorperioden bei, liest sie aber aus dem primären Kerzenstream, um innerhalb der StockSharp High-Level-API zu bleiben. Parameter ermöglichen die Anpassung der Schwellenwerte für die ursprüngliche Sensitivität.
    • Geldbasierte Stop-Regeln aus dem MT4-Code sind nicht enthalten. Risiko wird durch preisbasierte Stops und das Break-Even-Modul verwaltet.

Parameter

Name Beschreibung
CandleType Zeitrahmen (oder ein anderer Kerzentyp) für alle Indikatoren und Handelsentscheidungen.
FastMaPeriod, SlowMaPeriod Perioden des schnellen und langsamen linear gewichteten gleitenden Durchschnitts (typischer Preis).
StochasticKPeriod, StochasticDPeriod, StochasticSlowing %K/%D-Längen und Glättungsfaktor des stochastischen Oszillators.
StochasticOverbought, StochasticOversold Stochastische Schwellenwerte, die während der letzten drei abgeschlossenen Werte gekreuzt werden müssen.
MomentumPeriod Länge des Momentum-Indikators.
MomentumBuyThreshold, MomentumSellThreshold Minimaler absoluter Abstand von der 100-Linie für Long-/Short-Trades.
MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod MACD-Konfiguration.
CloseOppositePositions Entgegengesetzte Seite vor einem neuen Trade schließen.
MaxPositions Maximale Nettoexposition pro Richtung (0 = unbegrenzt).
TradeVolume Volumen jeder neuen Order; auch dem Strategie-Volume zugewiesen.
UseStopLoss, StopLossPips Schutzenden Stop in Pips aktivieren und dimensionieren.
UseTakeProfit, TakeProfitPips Take-Profit in Pips aktivieren und dimensionieren.
UseTrailingStop, TrailingStopPips Trailing-Stop-Logik aktivieren und Distanz definieren.
UseBreakEven, BreakEvenTriggerPips, BreakEvenOffsetPips Break-Even-Aktivierungsdistanz und gesperrter Offset.
ForceExit Wenn true, werden alle Positionen bei der nächsten verarbeiteten Kerze geschlossen.

Implementierungshinweise

  • Die Strategie abonniert Kerzen über SubscribeCandles().BindEx(...), sodass Indikatoren abgeschlossene Werte erhalten und der Code GetValue() nie direkt aufruft.
  • Pip-Konvertierung verwendet den Instrument-PriceStep; wenn der Markt fraktionale Pips notiert, Wertpapier-Schritt entsprechend konfigurieren.
  • Trailing- und Break-Even-Updates verschieben den Stop nur in die günstige Richtung. Reset-Logik löscht zwischengespeicherte Stop-/Zielwerte bei jedem geschlossenen Trade, damit neue Positionen mit frischen Risikoeinstellungen starten.
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;

public class HeikinAshiTraderStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public HeikinAshiTraderStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}