Kauf- und Verkaufs-Strategie mit Bullish Engulfing-Muster
Diese Strategie eröffnet eine Long-Position, wenn eine bullische Kerze den vorherigen bärischen Balken vollständig umschließt und optionale Trendbedingungen erfüllt sind. Die Positionsgröße ist ein Prozentsatz des aktuellen Eigenkapitals, während Take-Profit und Stop-Loss Trades automatisch schließen.
Details
- Einstiegskriterien: Bullish Engulfing-Muster mit optionalem SMA-Trendfilter.
- Long/Short: Nur Long.
- Ausstiegskriterien: Take-Profit oder Stop-Loss.
- Stops: Ja, sowohl Take-Profit als auch Stop-Loss.
- Standardwerte:
CandleType= 15 minuteTakeProfitPercent= 2StopLossPercent= 2OrderPercent= 30TrendMode= SMA50
- Filter:
- Kategorie: Muster
- Richtung: Nur Long
- Indikatoren: Candlestick, SMA
- Stops: Ja
- Komplexität: Niedrig
- Zeitrahmen: Intraday
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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 enum BullishEngulfingTrendMode
{
None,
SMA50,
}
/// <summary>
/// Buys bullish body engulfings, optionally following a below-SMA50 bearish bar.
/// Allocates a percentage of current equity and closes longs through local SL/TP.
/// </summary>
public class BuySellBullishEngulfingStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<decimal> _orderPercent;
private readonly StrategyParam<BullishEngulfingTrendMode> _trendMode;
private (decimal Open, decimal Close, decimal Sma, bool SmaReady)? _previousBar;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public decimal TakeProfitPercent { get => _takeProfitPercent.Value; set => _takeProfitPercent.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
public decimal OrderPercent { get => _orderPercent.Value; set => _orderPercent.Value = value; }
public BullishEngulfingTrendMode TrendMode { get => _trendMode.Value; set => _trendMode.Value = value; }
public BuySellBullishEngulfingStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame());
_takeProfitPercent = Param(nameof(TakeProfitPercent), 2m).SetRange(0m, 100m);
_stopLossPercent = Param(nameof(StopLossPercent), 2m).SetRange(0m, 100m);
_orderPercent = Param(nameof(OrderPercent), 30m).SetRange(0m, 100m).SetGreaterThanZero();
_trendMode = Param(nameof(TrendMode), BullishEngulfingTrendMode.SMA50);
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType), (Security, DataType.Level1)];
protected override void OnReseted()
{
base.OnReseted();
_previousBar = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (TrendMode is not (BullishEngulfingTrendMode.None or BullishEngulfingTrendMode.SMA50))
throw new ArgumentOutOfRangeException(nameof(TrendMode));
_previousBar = null;
StartProtection(new Unit(TakeProfitPercent, UnitTypes.Percent), new Unit(StopLossPercent, UnitTypes.Percent),
useMarketOrders: true, isLocalStop: true);
var sma = new SimpleMovingAverage { Length = TrendMode == BullishEngulfingTrendMode.SMA50 ? 50 : 1 };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(sma, (candle, value) => ProcessCandle(candle, value, sma.IsFormed)).Start();
// Feed real bid updates to the native long-position protection between candle closes.
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = Level1Fields.BestBidPrice;
SubscribeLevel1(quotes).Bind(_ => { }).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
if (TrendMode == BullishEngulfingTrendMode.SMA50)
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue, bool smaReady)
{
if (candle.State != CandleStates.Finished)
return;
var previous = _previousBar;
_previousBar = (candle.OpenPrice, candle.ClosePrice, smaValue, smaReady);
if (previous is not { } bar || Position != 0m || !IsFormedAndOnlineAndAllowTrading())
return;
var engulfing = bar.Open > bar.Close && candle.ClosePrice > candle.OpenPrice
&& candle.OpenPrice <= bar.Close && candle.ClosePrice >= bar.Open;
var trend = TrendMode == BullishEngulfingTrendMode.None || bar.SmaReady && bar.Close < bar.Sma;
if (!engulfing || !trend || candle.ClosePrice <= 0m)
return;
var equity = Portfolio.CurrentValue ?? Portfolio.BeginValue ?? 0m;
var step = Security.VolumeStep ?? 1m;
if (equity <= 0m || step <= 0m)
return;
var volume = equity * OrderPercent / 100m / candle.ClosePrice;
volume = Math.Floor(Math.Min(volume, Security.MaxVolume ?? decimal.MaxValue) / step) * step;
if (volume > 0m && volume >= (Security.MinVolume ?? step))
BuyMarket(volume);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.BusinessEntities")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal, Convert, Math
from StockSharp.Messages import DataType, CandleStates, Level1Fields, Unit, UnitTypes
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class buy_sell_bullish_engulfing_strategy(Strategy):
"""Long-only body engulfing, optional prior-bar SMA50 trend, equity sizing and local SL/TP."""
def __init__(self):
super(buy_sell_bullish_engulfing_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15)))
self._take_profit_percent = self.Param("TakeProfitPercent", 2.0).SetRange(0.0, 100.0)
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetRange(0.0, 100.0)
self._order_percent = self.Param("OrderPercent", 30.0).SetRange(0.0, 100.0).SetGreaterThanZero()
self._trend_mode = self.Param("TrendMode", "SMA50")
self._previous_bar = None
def GetWorkingSecurities(self):
return [(self.Security, self._candle_type.Value), (self.Security, DataType.Level1)]
def OnReseted(self):
super(buy_sell_bullish_engulfing_strategy, self).OnReseted()
self._previous_bar = None
def OnStarted2(self, time):
super(buy_sell_bullish_engulfing_strategy, self).OnStarted2(time)
if self._trend_mode.Value not in ("None", "SMA50"):
raise ValueError("TrendMode must be None or SMA50.")
self._previous_bar = None
self.StartProtection(Unit(float(self._take_profit_percent.Value), UnitTypes.Percent),
Unit(float(self._stop_loss_percent.Value), UnitTypes.Percent),
useMarketOrders=True, isLocalStop=True)
sma = SimpleMovingAverage()
sma.Length = 50 if self._trend_mode.Value == "SMA50" else 1
def process(candle, value):
self._process_candle(candle, value, sma.IsFormed)
subscription = self.SubscribeCandles(self._candle_type.Value)
subscription.Bind(sma, process).Start()
# Bid updates drive native long-position protection between finished candles.
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = Level1Fields.BestBidPrice
self.SubscribeLevel1(quotes).Bind(lambda message: None).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
if self._trend_mode.Value == "SMA50":
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_value, sma_ready):
if candle.State != CandleStates.Finished:
return
previous = self._previous_bar
self._previous_bar = (candle.OpenPrice, candle.ClosePrice, sma_value, sma_ready)
if previous is None or self.Position != 0 or not self.IsFormedAndOnlineAndAllowTrading():
return
engulfing = (previous[0] > previous[1] and candle.ClosePrice > candle.OpenPrice and
candle.OpenPrice <= previous[1] and candle.ClosePrice >= previous[0])
trend = (self._trend_mode.Value == "None" or
previous[3] and previous[1] < previous[2])
if not engulfing or not trend or candle.ClosePrice <= 0:
return
equity = self.Portfolio.CurrentValue
if equity is None:
equity = self.Portfolio.BeginValue
step = self.Security.VolumeStep
if step is None:
step = Decimal.One
if equity is None or equity <= 0 or step <= 0:
return
volume = equity * Convert.ToDecimal(self._order_percent.Value) / Decimal(100) / candle.ClosePrice
maximum = self.Security.MaxVolume
if maximum is not None:
volume = Math.Min(volume, maximum)
volume = Math.Floor(volume / step) * step
minimum = self.Security.MinVolume
if minimum is None:
minimum = step
if volume > 0 and volume >= minimum:
self.BuyMarket(volume)
def CreateClone(self):
return buy_sell_bullish_engulfing_strategy()