HaramiStrategy konvertiert den MetaTrader-Experten "Harami" in die High-Level-API von StockSharp. Die Strategie kombiniert ein bullisches/bärisches Harami-Muster, das auf einem höheren Zeitrahmen erkannt wird, mit einer Momentum-Expansion und einem langfristigen MACD-Filter. Nur abgeschlossene Kerzen werden verarbeitet und das gesamte Handelsmanagement wird über die integrierte Schutz-Engine von StockSharp abgewickelt.
Daten und Indikatoren
Basiszeitrahmen: konfigurierbar (standardmäßig 15-Minuten-Kerzen) zur Trenderfassung mittels gleitender Durchschnitte.
Höherer Zeitrahmen: konfigurierbar (standardmäßig eine Stunde) zur Mustererkennung und Momentum-Bestätigung.
MACD-Zeitrahmen: konfigurierbar (standardmäßig 30-Tage-Kerzen) zur Emulation des ursprünglichen monatlichen MACD-Filters.
Indikatoren:
Linear gewichteter gleitender Durchschnitt (FastMaLength) auf dem Basiszeitrahmen.
Exponentieller gleitender Durchschnitt (SlowMaLength) auf dem Basiszeitrahmen.
Momentum (MomentumPeriod) auf dem höheren Zeitrahmen. Die Strategie verwendet den absoluten Abstand vom Neutralwert (100) für die letzten drei Balken des höheren Zeitrahmens.
Moving Average Convergence Divergence (12/26/9) auf dem MACD-Zeitrahmen.
Long-Setup
Der langsame EMA liegt über dem schnellen LWMA auf dem Basiszeitrahmen und signalisiert einen Aufwärtstrend.
Der höhere Zeitrahmen bildet eine bullische Harami-Sequenz: Vor zwei Kerzen war die Kerze bärisch, die vorherige Kerze war bullisch und ihr Körper ist kleiner als der frühere bärische Körper.
Jede der letzten drei Momentum-Abweichungen des höheren Zeitrahmens überschreitet MomentumBuyThreshold.
Die MACD-Hauptlinie liegt auf dem MACD-Zeitrahmen über der Signallinie.
Es ist keine Long-Position offen (Position <= 0).
Die Strategie sendet eine Market-Kauforder, die groß genug ist, um Short-Exposure umzukehren und Volume Lots hinzuzufügen.
Short-Setup
Der langsame EMA liegt unter dem schnellen LWMA auf dem Basiszeitrahmen.
Der höhere Zeitrahmen bildet ein bärisches Harami: Vor zwei Kerzen war bullisch, die vorherige Kerze war bärisch und der letzte Körper ist kleiner.
Jede der letzten drei Momentum-Abweichungen des höheren Zeitrahmens überschreitet MomentumSellThreshold.
Die MACD-Hauptlinie liegt unter der Signallinie.
Es ist keine Short-Exposition offen (Position >= 0).
Die Strategie sendet eine Market-Verkaufsorder, die groß genug ist, um Long-Positionen zu schließen und eine neue Short-Position der Größe Volume zu eröffnen.
Risikomanagement
StartProtection installiert Stop-Loss- und Take-Profit-Levels (ausgedrückt in Punkten). Zusätzliche Trailing-, Break-Even- und Geldmanagement-Funktionen des ursprünglichen EA werden absichtlich weggelassen, um die StockSharp-Version kompakt zu halten. Handelsrichtungsänderungen glätten automatisch die entgegengesetzte Exposition.
Parameter
Name
Beschreibung
Standardwert
CandleType
Primärer Zeitrahmen für gleitende Durchschnitte und Signalausführung.
15-Minuten-Kerzen
HigherCandleType
Zeitrahmen für Harami- und Momentum-Bestätigung.
1-Stunden-Kerzen
MacdCandleType
Zeitrahmen für den MACD-Trendfilter.
30-Tage-Kerzen
FastMaLength
Länge des schnellen linear gewichteten MA.
6
SlowMaLength
Länge des langsamen exponentiellen MA.
85
MomentumPeriod
Momentum-Rückblick auf dem höheren Zeitrahmen.
14
MomentumBuyThreshold
Minimale Momentum-Abweichung für Long-Bestätigung.
0.3
MomentumSellThreshold
Minimale Momentum-Abweichung für Short-Bestätigung.
0.3
StopLossPoints
Stop-Loss-Abstand in Punkten.
40
TakeProfitPoints
Take-Profit-Abstand in Punkten.
100
Verwendungshinweise
CandleType, HigherCandleType und MacdCandleType mit verfügbaren historischen Daten abstimmen; sicherstellen, dass der höhere Zeitrahmen länger als der Basiszeitrahmen ist.
Momentum-Schwellenwerte an die Volatilität des gehandelten Instruments anpassen.
Den StockSharp-Optimierer über die bereitgestellten Parameterbereiche verwenden, um MA-Längen und Momentum-Schwellenwerte zu optimieren.
Immer mit realistischen Provisions-/Latenzeinstellungen backtesten, bevor live eingesetzt wird.
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 HaramiStrategy : 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 HaramiStrategy()
{
_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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class harami_strategy(Strategy):
def __init__(self):
super(harami_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(harami_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(harami_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return harami_strategy()