Diese Strategie portiert den MetaTrader Expert Advisor Expert_ABE_BE_Stoch auf den StockSharp High-Level API. Es kombiniert die japanische Candlestick-Analyse mit der Bestätigung des Momentums, um Zeitumkehrungen in der Nähe von überverkauften und überkauften Zonen zu ermöglichen. Das primäre Signal sucht nach einer zinsbullischen Engulfing-Kerze, die durch einen stark überverkauften stochastischen Oszillator unterstützt wird, oder nach einer bärischen Engulfing-Kerze, die durch einen überkauften Oszillatorwert bestätigt wird. Sobald eine Position offen ist, basiert die Strategie auf stochastischen Schwellenwertüberschreitungen, um Ausstiege zu verwalten, und reproduziert dabei die „Abstimmungs“-Mechanik des ursprünglichen Experten.
Die Taktik ist sowohl für lange als auch für kurze Teilnahmen ausgelegt. Es wertet nur abgeschlossene Kerzen aus und bleibt daher immun gegen Intrabar-Rauschen. Die Handelsgröße bleibt unter der Kontrolle der Volume-Eigenschaft des Frameworks, während optionale Stop-Loss- und Take-Profit-Schutzmaßnahmen die ursprünglichen punktbasierten Risikoeinstellungen in StockSharp Unit-Objekte umwandeln.
Wie es funktioniert
Datenabonnement – Die Strategie abonniert den konfigurierten Kerzentyp und erstellt ein StochasticOscillator mit drei einstellbaren Parametern (%K, %D und dem Verlangsamungsfaktor).
Mustererkennung – Bei jeder fertigen Kerze prüft der Algorithmus, ob der neueste Balken den Körper des vorherigen überdeckt. Zwei Hilfsmethoden reproduzieren die in MetaTrader verwendeten bullischen und bärischen Engulfing-Definitionen.
Momentum-Bestätigung – Die %D-Linie der Stochastik dient als Bestätigungsfilter. Für bullische Engulfing-Trades sind Werte unter dem Überverkaufsschwellenwert (Standard 30) erforderlich, während Werte über dem Überkaufsschwellenwert (Standard 70) für bärische Signale erforderlich sind.
Positionsverwaltung – Der vorherige %D-Wert wird zwischengespeichert. Wenn der neue Wert entweder 20 oder 80 nach oben überschreitet, wird jede Short-Position geschlossen. Umgekehrt liquidieren Abwärtskreuzungen von 80 oder 20 das Long-Engagement. Diese Schwellenwerte spiegeln die zusätzlichen „nahen“ Stimmen wider, die durch die MQL-Logik erzeugt werden.
Risikomanagement – Wenn positive Stop-Loss- oder Take-Profit-Abstände (ausgedrückt in Preisschritten) angegeben werden, wandelt die Strategie diese in UnitTypes.Price um und aktiviert StartProtection. Andernfalls wird der standardmäßige StockSharp-Schutz mit StartProtection() aktiviert.
Handelsregeln
Long-Einstieg: Die vorherige Kerze ist bärisch, die aktuelle Kerze ist bullisch und der Körper der aktuellen Kerze umhüllt den vorherigen Körper. Der stochastische %D-Wert muss unter dem EntryOversoldLevel liegen (Standard 30). Über BuyMarket wird ein eventuell bestehender Short geschlossen und ein neuer Long eröffnet.
Kurzer Eintrag: Die vorherige Kerze ist bullisch, die aktuelle Kerze ist bärisch und der Körper der aktuellen Kerze umhüllt den vorherigen Körper. Der stochastische %D-Wert muss den EntryOverboughtLevel (Standard 70) überschreiten. Über SellMarket wird ein eventuell bestehender Long geschlossen und ein neuer Short eröffnet.
Long-Ausstieg: Bei einem offenen Long-Kurs wird die Position mit SellMarket geschlossen, wenn %D nach unten durch entweder ExitUpperLevel (Standard 80) oder ExitLowerLevel (Standard 20) kreuzt.
Short-Ausstieg: Wenn bei einem offenen Short %D entweder ExitLowerLevel oder ExitUpperLevel nach oben kreuzt, wird die Position durch BuyMarket gedeckt.
Stopps/Ziele: Optional StopLossPoints und TakeProfitPoints wandeln punktbasierte Entfernungen in absolute Preisversätze um, wenn das Instrument einen PriceStep ungleich Null anzeigt.
Parameter
Name
Typ
Standard
Beschreibung
CandleType
DataType
TimeSpan.FromHours(1).TimeFrame()
Kerzenquelle zur Mustererkennung.
StochasticPeriodK
int
47
Lookback-Zeitraum für die schnelle %K-Berechnung.
StochasticPeriodD
int
9
Glättungszeitraum für die Signalleitung %D.
StochasticPeriodSlow
int
13
Auf %K wird eine zusätzliche Glättung angewendet, bevor es zu %D wird.
EntryOversoldLevel
decimal
30
Obergrenze für %D, die bullische Engulfing-Trades ermöglicht.
EntryOverboughtLevel
decimal
70
Untergrenze für %D, die bärische Engulfing-Trades ermöglicht.
ExitLowerLevel
decimal
20
Ebene, die bei Überschreitung nach oben kurze Ausstiege erzwingt; Wenn es nach unten gekreuzt wird, schließt es Long-Positionen.
ExitUpperLevel
decimal
80
Die obere Grenze wird auf die gleiche Weise wie die untere Ebene verwendet, jedoch für überkauftes Gebiet.
TakeProfitPoints
decimal
0
Abstand in Preisschritten für die Take-Profit-Order (0 deaktiviert sie).
StopLossPoints
decimal
0
Abstand in Preisschritten für die Stop-Loss-Order (0 deaktiviert sie).
Notizen
Funktioniert auf jedem Instrument, das OHLC Kerzen liefert; Die Standardwerte gehen von stündlichen Balken aus.
Alle Berechnungen basieren auf geschlossenen Kerzen, um mit der Zeitrahmenlogik des MQL-Experten in Einklang zu bleiben.
Die Positionsgröße sollte über die Basisstrategie Volume Property oder ein Portfoliomanagement auf höherer Ebene konfiguriert werden.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// ABE BE Stoch strategy: Engulfing pattern with Stochastic confirmation.
/// Bullish engulfing + oversold stochastic for long, bearish engulfing + overbought for short.
/// </summary>
public class AbeBeStochStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _stochPeriod;
private readonly StrategyParam<decimal> _oversold;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<int> _signalCooldownCandles;
private readonly List<ICandleMessage> _candles = new();
private decimal _prevK;
private bool _hasPrevK;
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int StochPeriod { get => _stochPeriod.Value; set => _stochPeriod.Value = value; }
public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public AbeBeStochStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_stochPeriod = Param(nameof(StochPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Stoch Period", "Stochastic K period", "Indicators");
_oversold = Param(nameof(Oversold), 30m)
.SetDisplay("Oversold", "Stochastic oversold level", "Signals");
_overbought = Param(nameof(Overbought), 70m)
.SetDisplay("Overbought", "Stochastic overbought level", "Signals");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_candles.Clear();
_prevK = 0m;
_hasPrevK = false;
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_candles.Clear();
_hasPrevK = false;
_candlesSinceTrade = SignalCooldownCandles;
var stoch = new StochasticOscillator { K = { Length = StochPeriod }, D = { Length = 3 } };
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(stoch, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
var stochTyped = stochValue as StochasticOscillatorValue;
if (stochTyped?.K is not decimal kValue) return;
_candles.Add(candle);
if (_candles.Count > 5)
_candles.RemoveAt(0);
if (_candles.Count >= 2)
{
var curr = _candles[^1];
var prev = _candles[^2];
// Bullish engulfing: prev bearish, curr bullish, curr body engulfs prev body
var bullishEngulfing = prev.OpenPrice > prev.ClosePrice
&& curr.ClosePrice > curr.OpenPrice
&& curr.OpenPrice <= prev.ClosePrice
&& curr.ClosePrice >= prev.OpenPrice;
// Bearish engulfing: prev bullish, curr bearish, curr body engulfs prev body
var bearishEngulfing = prev.ClosePrice > prev.OpenPrice
&& curr.OpenPrice > curr.ClosePrice
&& curr.OpenPrice >= prev.ClosePrice
&& curr.ClosePrice <= prev.OpenPrice;
if (bullishEngulfing && kValue < Oversold && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (bearishEngulfing && kValue > Overbought && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
// Exit on stochastic cross
if (_hasPrevK)
{
if (Position > 0 && _prevK >= Overbought && kValue < Overbought && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
else if (Position < 0 && _prevK <= Oversold && kValue > Oversold && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
}
_prevK = kValue;
_hasPrevK = true;
}
}
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 StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class abe_be_stoch_strategy(Strategy):
def __init__(self):
super(abe_be_stoch_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._stoch_period = self.Param("StochPeriod", 14)
self._oversold = self.Param("Oversold", 30.0)
self._overbought = self.Param("Overbought", 70.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 6)
self._candles = []
self._prev_k = 0.0
self._has_prev_k = False
self._candles_since_trade = 6
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def StochPeriod(self):
return self._stoch_period.Value
@StochPeriod.setter
def StochPeriod(self, value):
self._stoch_period.Value = value
@property
def Oversold(self):
return self._oversold.Value
@Oversold.setter
def Oversold(self, value):
self._oversold.Value = value
@property
def Overbought(self):
return self._overbought.Value
@Overbought.setter
def Overbought(self, value):
self._overbought.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(abe_be_stoch_strategy, self).OnReseted()
self._candles.clear()
self._prev_k = 0.0
self._has_prev_k = False
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(abe_be_stoch_strategy, self).OnStarted2(time)
self._candles.clear()
self._has_prev_k = False
self._candles_since_trade = self.SignalCooldownCandles
stoch = StochasticOscillator()
stoch.K.Length = self.StochPeriod
stoch.D.Length = 3
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(stoch, self._process_candle).Start()
def _process_candle(self, candle, stoch_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
k_val = stoch_value.K
if k_val is None:
return
k_value = float(k_val)
self._candles.append(candle)
if len(self._candles) > 5:
self._candles.pop(0)
if len(self._candles) >= 2:
curr = self._candles[-1]
prev = self._candles[-2]
bullish_engulfing = (float(prev.OpenPrice) > float(prev.ClosePrice)
and float(curr.ClosePrice) > float(curr.OpenPrice)
and float(curr.OpenPrice) <= float(prev.ClosePrice)
and float(curr.ClosePrice) >= float(prev.OpenPrice))
bearish_engulfing = (float(prev.ClosePrice) > float(prev.OpenPrice)
and float(curr.OpenPrice) > float(curr.ClosePrice)
and float(curr.OpenPrice) >= float(prev.ClosePrice)
and float(curr.ClosePrice) <= float(prev.OpenPrice))
if bullish_engulfing and k_value < self.Oversold and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif bearish_engulfing and k_value > self.Overbought and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
if self._has_prev_k:
if self.Position > 0 and self._prev_k >= self.Overbought and k_value < self.Overbought and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
elif self.Position < 0 and self._prev_k <= self.Oversold and k_value > self.Oversold and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
self._prev_k = k_value
self._has_prev_k = True
def CreateClone(self):
return abe_be_stoch_strategy()