Diese Strategie repliziert das klassische bullische und bärische Engulfing-Kerzenmuster, das ursprünglich in MetaTrader für den Expert Advisor "Bullish and Bearish Engulfing" implementiert wurde. Der StockSharp-Port bewertet abgeschlossene Kerzen in einem konfigurierbaren Zeitrahmen, überspringt optional eine Anzahl aktueller Bars und reagiert, wenn ein Engulfing-Muster einen minimalen Abstandsfilter erfüllt. Die Logik ist für diskretionäre Trader konzipiert, die ein bewährtes Price-Action-Muster automatisieren möchten, während sie die Kontrolle über Richtung, Volumen und die Behandlung bestehender Positionen behalten.
Musterdefinition
Ein Engulfing-Signal wird bestätigt, wenn zwei aufeinanderfolgende abgeschlossene Kerzen die folgenden Regeln erfüllen (nach Anwendung des konfigurierten Versatzes):
Bullisches Engulfing
Die zuletzt bewertete Kerze schließt über ihrem Eröffnungskurs (bullischer Körper).
Die vorhergehende Kerze schließt unter ihrem Eröffnungskurs (bärischer Körper).
Die bullische Kerze hat ein höheres Hoch und ein niedrigeres Tief als die vorherige Kerze, und zwar mindestens um den Abstandsfilter.
Der bullische Schluss liegt über dem vorherigen Eröffnungskurs und sein Eröffnungskurs liegt unter dem vorherigen Schluss, wiederum unter Einhaltung des Abstandsfilters.
Bärisches Engulfing
Die bewertete Kerze schließt unter ihrem Eröffnungskurs (bärischer Körper).
Die vorhergehende Kerze schließt über ihrem Eröffnungskurs (bullischer Körper).
Die bärische Kerze druckt noch ein höheres Hoch, schließt aber deutlich unter dem vorherigen Eröffnungskurs, und ihr Eröffnungskurs übersteigt den vorherigen Schluss, jeweils um den Abstandsfilter.
Das Tief des bärischen Bars liegt unter dem vorherigen Tief um den Abstandsfilter.
Diese Bedingungen reproduzieren die ursprüngliche MetaTrader-Implementierung, die forderte, dass die Engulfing-Kerze den vorherigen Körper vollständig abdeckt und über beide Extreme hinausgeht. Der Abstandsfilter wird in Pips gemessen und mit dem Preisschritt und den Dezimalstellen des Instruments in einen Preis umgerechnet (5-stellige und 3-stellige Forex-Kurse werden automatisch auf 10-Punkt-Pips skaliert).
Handelslogik
Den ausgewählten Kerzentyp über die High-Level-API abonnieren und nur abgeschlossene Kerzen verarbeiten.
Einen kleinen rollierenden Puffer pflegen, der die OHLC-Werte speichert, die für den aktuellen Versatzzwert erforderlich sind.
Wenn mindestens zwei historische Kerzen zur Auswertung verfügbar sind, die oben beschriebenen bullischen und bärischen Engulfing-Bedingungen prüfen.
Bei einem bullischen Signal eine Marktorder auf der durch BullishSide definierten Seite senden. Bei einem bärischen Signal die über BearishSide konfigurierte Seite verwenden.
Wenn CloseOppositePositions aktiviert ist und ein gegenläufiges Exposure besteht, erhöht die Strategie das Ordervolumen um die absolute aktuelle Position, sodass der resultierende Trade das gegenläufige Bein schließt und ein neues in der gewünschten Richtung eröffnet. Wenn das Flag deaktiviert ist, werden Signale ignoriert, solange eine gegenläufige Position offen ist.
Die Positionsgröße wird durch den Strategieparameter Volume gesteuert (Standard: 1 Kontrakt/Lot). Standardmäßig wird kein automatischer Stop-Loss oder Take-Profit angehängt; das Risikomanagement bleibt dem Endbenutzer oder Schutzmodulen überlassen (kann mit den integrierten Schutzfunktionen von StockSharp kombiniert werden).
Parameter
Parameter
Beschreibung
Standard
Hinweise
CandleType
Zeitrahmen (StockSharp DataType) zur Signalentdeckung.
1-Stunden-Zeitrahmen
Anpassbar an jeden unterstützten Kerzentyp.
Shift
Anzahl der abgeschlossenen Kerzen, die vor der Musterbewertung übersprungen werden sollen.
1
Wert 1 analysiert den zuletzt geschlossenen Bar; höhere Werte schauen weiter zurück.
DistanceInPips
Mindestabstand in Pips, den die Engulfing-Kerze gegenüber der vorherigen überschreiten muss.
0
In Preis umgerechnet anhand des Instruments-Preisschritts; nützlich zum Herausfiltern von Kerzen mit kleinen Körpern.
CloseOppositePositions
Ob eine bestehende gegenläufige Position geschlossen werden soll, wenn ein neues Signal ausgelöst wird.
true
Wenn deaktiviert, wird der Trade übersprungen, wenn das aktuelle Exposure mit dem Signal in Konflikt steht.
BullishSide
Orderseite bei einem bullischen Engulfing-Signal.
Buy
Kann für konträres Verhalten auf Sell umgestellt werden.
BearishSide
Orderseite bei einem bärischen Engulfing-Signal.
Sell
Kann auf Buy umgestellt werden, um gegen den Trend zu handeln.
Volume
Basisordergröße.
1
Das Ordervolumen wird beim Schließen der Gegenseite um abs(Position) erhöht.
Positionsmanagement und Risiko
Da Orders ohne Schutz-Stops zum Marktpreis gesendet werden, die Strategie mit zusätzlichen Modulen (z. B. StartProtection) kombinieren oder externe Risikokontrollen konfigurieren.
Der originale MetaTrader-Code dimensionierte Trades über einen risikobasierten Money Manager. In diesem Port wird die Dimensionierung auf einen direkten Volumensparameter vereinfacht, damit das Verhalten innerhalb von StockSharp deterministisch ist; einen benutzerdefinierten Money-Management-Block integrieren, wenn dynamisches Sizing erforderlich ist.
Wenn CloseOppositePositionstrue ist, erfolgen Umkehrungen sofort: Das Tradevolumen entspricht dem Basisvolumen plus der absoluten offenen Position, was einen direkten Übergang von flach zur neuen Richtung garantiert.
Dateien
CS/BullishBearishEngulfingStrategy.cs – Haupt-C#-Implementierung, basierend auf der High-Level-StockSharp-Strategie-API.
Hinweis: Für diese ID ist keine Python-Implementierung vorgesehen; nur die C#-Version ist wie angefordert enthalten.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Engulfing pattern strategy that reacts to bullish and bearish engulfing candles.
/// </summary>
public class BullishBearishEngulfingStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _shift;
private readonly StrategyParam<decimal> _distanceInPips;
private readonly StrategyParam<bool> _closeOpposite;
private readonly StrategyParam<Sides> _bullishSide;
private readonly StrategyParam<Sides> _bearishSide;
private readonly List<CandleSnapshot> _candles = new();
/// <summary>
/// Initializes a new instance of <see cref="BullishBearishEngulfingStrategy"/>.
/// </summary>
public BullishBearishEngulfingStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Time frame for analysis", "General");
_shift = Param(nameof(Shift), 1)
.SetGreaterThanZero()
.SetDisplay("Shift", "Number of completed candles to skip", "Pattern")
.SetOptimize(1, 5, 1);
_distanceInPips = Param(nameof(DistanceInPips), 0m)
.SetNotNegative()
.SetDisplay("Distance (pips)", "Additional filter expressed in pips", "Pattern")
.SetOptimize(0m, 10m, 1m);
_closeOpposite = Param(nameof(CloseOppositePositions), true)
.SetDisplay("Close Opposite", "Close opposite position before entering", "Risk");
_bullishSide = Param(nameof(BullishSide), Sides.Buy)
.SetDisplay("Bullish Action", "Order side for bullish engulfing", "Pattern");
_bearishSide = Param(nameof(BearishSide), Sides.Sell)
.SetDisplay("Bearish Action", "Order side for bearish engulfing", "Pattern");
}
/// <summary>
/// Candle type used for pattern detection.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Number of fully completed candles to skip before pattern evaluation.
/// </summary>
public int Shift
{
get => _shift.Value;
set => _shift.Value = value;
}
/// <summary>
/// Additional price filter expressed in pips.
/// </summary>
public decimal DistanceInPips
{
get => _distanceInPips.Value;
set => _distanceInPips.Value = value;
}
/// <summary>
/// Indicates whether opposite positions should be closed before entering a new trade.
/// </summary>
public bool CloseOppositePositions
{
get => _closeOpposite.Value;
set => _closeOpposite.Value = value;
}
/// <summary>
/// Side used when a bullish engulfing pattern appears.
/// </summary>
public Sides BullishSide
{
get => _bullishSide.Value;
set => _bullishSide.Value = value;
}
/// <summary>
/// Side used when a bearish engulfing pattern appears.
/// </summary>
public Sides BearishSide
{
get => _bearishSide.Value;
set => _bearishSide.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_candles.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_candles.Clear();
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
// no protection needed
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var snapshot = new CandleSnapshot
{
Open = candle.OpenPrice,
High = candle.HighPrice,
Low = candle.LowPrice,
Close = candle.ClosePrice
};
_candles.Add(snapshot);
var maxCount = Math.Max(Shift + 2, 3);
while (_candles.Count > maxCount)
try { _candles.RemoveAt(0); } catch { break; }
if (_candles.Count < Shift + 1)
return;
var candles = _candles.ToArray();
var currentIndex = candles.Length - Shift;
if (currentIndex <= 0)
return;
var previousIndex = currentIndex - 1;
if (previousIndex < 0)
return;
var current = candles[currentIndex];
var previous = candles[previousIndex];
var distance = CalculateDistanceInPrice();
var isBullishEngulfing = current.Close > current.Open && previous.Open > previous.Close &&
current.High > previous.High + distance &&
current.Close > previous.Open + distance &&
current.Open < previous.Close - distance &&
current.Low < previous.Low - distance;
if (isBullishEngulfing)
{
HandleSignal(BullishSide);
return;
}
var isBearishEngulfing = current.Open > current.Close && previous.Open < previous.Close &&
current.High > previous.High + distance &&
current.Open > previous.Close + distance &&
current.Close < previous.Open - distance &&
current.Low < previous.Low - distance;
if (isBearishEngulfing)
HandleSignal(BearishSide);
}
private void HandleSignal(Sides side)
{
switch (side)
{
case Sides.Buy:
EnterLong();
break;
case Sides.Sell:
EnterShort();
break;
}
}
private void EnterLong()
{
if (Position > 0)
return;
if (Position < 0)
{
if (!CloseOppositePositions)
return;
// Close short first
BuyMarket();
}
BuyMarket();
}
private void EnterShort()
{
if (Position < 0)
return;
if (Position > 0)
{
if (!CloseOppositePositions)
return;
// Close long first
SellMarket();
}
SellMarket();
}
private decimal CalculateDistanceInPrice()
{
var priceStep = Security?.PriceStep;
if (priceStep == null)
return 0m;
var decimals = Security?.Decimals ?? 0;
var multiplier = decimals is 3 or 5 ? 10m : 1m;
return DistanceInPips * priceStep.Value * multiplier;
}
private struct CandleSnapshot
{
public decimal Open;
public decimal High;
public decimal Low;
public decimal Close;
}
}
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, Sides
from StockSharp.Algo.Strategies import Strategy
class bullish_bearish_engulfing_strategy(Strategy):
"""Engulfing pattern strategy that reacts to bullish and bearish engulfing candles."""
def __init__(self):
super(bullish_bearish_engulfing_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Time frame for analysis", "General")
self._shift = self.Param("Shift", 1) \
.SetGreaterThanZero() \
.SetDisplay("Shift", "Number of completed candles to skip", "Pattern")
self._distance_pips = self.Param("DistanceInPips", 0.0) \
.SetDisplay("Distance (pips)", "Additional filter in pips", "Pattern")
self._close_opposite = self.Param("CloseOpposite", True) \
.SetDisplay("Close Opposite", "Close opposite position before entering", "Risk")
self._candles = []
@property
def CandleType(self):
return self._candle_type.Value
@property
def Shift(self):
return self._shift.Value
@property
def DistanceInPips(self):
return self._distance_pips.Value
@property
def CloseOpposite(self):
return self._close_opposite.Value
def OnStarted2(self, time):
super(bullish_bearish_engulfing_strategy, self).OnStarted2(time)
self._candles = []
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.process_candle).Start()
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
o = float(candle.OpenPrice)
h = float(candle.HighPrice)
lo = float(candle.LowPrice)
c = float(candle.ClosePrice)
self._candles.append((o, h, lo, c))
max_count = max(self.Shift + 2, 3)
while len(self._candles) > max_count:
self._candles.pop(0)
if len(self._candles) < self.Shift + 1:
return
idx = len(self._candles) - self.Shift
if idx <= 0:
return
prev_idx = idx - 1
if prev_idx < 0:
return
cur = self._candles[idx]
prev = self._candles[prev_idx]
dist = self._calc_distance()
# Bullish engulfing
is_bullish = (cur[3] > cur[0] and prev[0] > prev[3] and
cur[1] > prev[1] + dist and
cur[3] > prev[0] + dist and
cur[0] < prev[3] - dist and
cur[2] < prev[2] - dist)
if is_bullish:
self._enter_long()
return
# Bearish engulfing
is_bearish = (cur[0] > cur[3] and prev[0] < prev[3] and
cur[1] > prev[1] + dist and
cur[0] > prev[3] + dist and
cur[3] < prev[0] - dist and
cur[2] < prev[2] - dist)
if is_bearish:
self._enter_short()
def _enter_long(self):
if self.Position > 0:
return
if self.Position < 0:
if not self.CloseOpposite:
return
self.BuyMarket()
self.BuyMarket()
def _enter_short(self):
if self.Position < 0:
return
if self.Position > 0:
if not self.CloseOpposite:
return
self.SellMarket()
self.SellMarket()
def _calc_distance(self):
sec = self.Security
if sec is None or sec.PriceStep is None:
return 0.0
step = float(sec.PriceStep)
decimals = sec.Decimals if sec.Decimals is not None else 0
mult = 10.0 if decimals == 3 or decimals == 5 else 1.0
return float(self.DistanceInPips) * step * mult
def OnReseted(self):
super(bullish_bearish_engulfing_strategy, self).OnReseted()
self._candles = []
def CreateClone(self):
return bullish_bearish_engulfing_strategy()