Die OpenPendingorderAfterPositionGetStopLoss-Strategie portiert die MetaTrader 5 gleichnamigen Expertenberater in die StockSharp-Hochebene API. Es wertet kontinuierlich die Steigung der Stochastic %K-Linie im ausgewählten Zeitrahmen aus. Wenn %K nach unten geht, wird eine Verkaufsstopp-Order unter dem Markt platziert, und wenn %K steigt, wird eine Kauf-Stopp-Order über dem Markt platziert. Jeder ausgefüllte Eintrag erhält sofort eine schützende Stop-Loss- und Take-Profit-Order. Wenn ein Stop-Loss die Position schließt, installiert die Strategie automatisch die entsprechende ausstehende Order neu, sodass das Raster der Breakout-Trades wiederhergestellt wird, ohne auf die nächste Kerze warten zu müssen.
Handelsregeln
Abonnieren Sie fertige Kerzen des konfigurierten Zeitrahmens und berechnen Sie einen klassischen Stochastic-Oszillator (KPeriod, DPeriod, Slowing).
Vergleichen Sie den aktuellen %K-Wert mit dem Wert vor zwei Balken:
%K(current) < %K(two bars ago) → Erteilen Sie einen Verkaufsstopp unterhalb des besten Gebots.
%K(current) > %K(two bars ago) → Setzen Sie einen Kaufstopp über dem besten Briefkurs.
Ausstehende Aufträge werden vom Markt um den aktuellen Spread plus den benutzerdefinierten MinStopDistancePoints-Puffer ausgeglichen, der der ursprünglichen MQL-Logik entspricht.
Sobald eine ausstehende Order ausgeführt wird, sendet die Strategie einen schützenden Stop-Loss (Stop-Order) und einen optionalen Take-Profit (Limit-Order).
Wenn der schützende Stop-Loss ausgelöst wird, wird die entsprechende ausstehende Order sofort unter Verwendung der neuesten Marktpreise neu erstellt.
Schutzaufträge werden automatisch gelöscht, wenn die Position durch den Take-Profit geschlossen wird oder wenn die Strategie stoppt.
Parameter
Name
Beschreibung
OrderVolume
Handelsvolumen in Lots für jede ausstehende Order.
StopLossPoints
Stop-Loss-Distanz in Symbolpunkten. Zum Deaktivieren auf 0 setzen.
TakeProfitPoints
Take-Profit-Distanz in Symbolpunkten. Zum Deaktivieren auf 0 setzen.
MinStopDistancePoints
Minimaler Preispuffer (in Punkten), der dem Spread hinzugefügt wird, bevor eine ausstehende Order platziert wird.
MaxPositions
Maximale Anzahl gleichzeitiger Positionen pro Richtung (Netting-Konten verwenden effektiv 0 oder 1).
KPeriod
Anzahl der Balken, die für die %K-Berechnung verwendet werden.
DPeriod
Länge der Glättungslinie %D.
Slowing
Zusätzlicher Glättungsfaktor, der vor dem Vergleich auf %K angewendet wird.
PendingExpiry
Optionale Lebensdauer ausstehender Stop-Orders. Abgelaufene Aufträge werden bei der nächsten Kerze storniert.
CandleType
Zeitrahmen für Kerzenabonnements und Indikatorberechnungen.
Hinweise zur Implementierung
Die gesamte Auftragsverwaltung basiert auf hochrangigen Hilfsprogrammen wie BuyStop, SellStop, SellLimit und BuyLimit, wie von AGENTS.md gefordert.
Indikatorwerte werden direkt im SubscribeCandles().BindEx(...)-Rückruf verbraucht, wodurch jegliche GetValue-Aufrufe vermieden werden.
Die Strategie überwacht MyTrade-Ereignisse, um Schutzanordnungen zu installieren und zu entfernen, und emuliert dabei die OnTradeTransaction-Logik des ursprünglichen Expert Advisors.
Kommentare im Code werden in Englisch verfasst und die Einrückung erfolgt mit Tabulatoren, entsprechend den Repository-Richtlinien.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Trades based on the slope of the Stochastic %K line.
/// Buys when %K is rising, sells when %K is falling.
/// Uses take-profit and stop-loss protection.
/// </summary>
public class OpenPendingorderAfterPositionGetStopLossStrategy : Strategy
{
private readonly StrategyParam<int> _kPeriod;
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<int> _slowing;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<int> _signalCooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal? _lastK;
private decimal? _prevK;
private decimal _entryPrice;
private int _candlesSinceTrade;
public int KPeriod
{
get => _kPeriod.Value;
set => _kPeriod.Value = value;
}
public int DPeriod
{
get => _dPeriod.Value;
set => _dPeriod.Value = value;
}
public int Slowing
{
get => _slowing.Value;
set => _slowing.Value = value;
}
public decimal StopLossPct
{
get => _stopLossPct.Value;
set => _stopLossPct.Value = value;
}
public decimal TakeProfitPct
{
get => _takeProfitPct.Value;
set => _takeProfitPct.Value = value;
}
public int SignalCooldownCandles
{
get => _signalCooldownCandles.Value;
set => _signalCooldownCandles.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public OpenPendingorderAfterPositionGetStopLossStrategy()
{
_kPeriod = Param(nameof(KPeriod), 22)
.SetDisplay("%K Period", "Number of bars for %K", "Indicators");
_dPeriod = Param(nameof(DPeriod), 7)
.SetDisplay("%D Period", "Smoothing period for %K", "Indicators");
_slowing = Param(nameof(Slowing), 2)
.SetDisplay("Slowing", "Additional smoothing factor", "Indicators");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop-loss as percentage of entry price", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take-profit as percentage of entry price", "Risk");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between entries", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for indicator", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_lastK = null;
_prevK = null;
_entryPrice = 0;
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_lastK = null;
_prevK = null;
_entryPrice = 0;
_candlesSinceTrade = SignalCooldownCandles;
var rsi = new RelativeStrengthIndex { Length = KPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal currentK)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
// Check stop-loss / take-profit on existing position
if (Position != 0 && _entryPrice > 0)
{
if (Position > 0)
{
var pnlPct = (close - _entryPrice) / _entryPrice * 100m;
if (pnlPct <= -StopLossPct || pnlPct >= TakeProfitPct)
{
SellMarket();
_entryPrice = 0;
_prevK = currentK;
_lastK = currentK;
return;
}
}
else if (Position < 0)
{
var pnlPct = (_entryPrice - close) / _entryPrice * 100m;
if (pnlPct <= -StopLossPct || pnlPct >= TakeProfitPct)
{
BuyMarket();
_entryPrice = 0;
_prevK = currentK;
_lastK = currentK;
return;
}
}
}
// Need at least 2 values to determine signal transition.
if (_lastK is not decimal prevK)
{
_lastK = currentK;
return;
}
_prevK = _lastK;
_lastK = currentK;
var crossedUp = prevK <= 45m && currentK > 45m;
var crossedDown = prevK >= 55m && currentK < 55m;
if (crossedUp && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_entryPrice = close;
_candlesSinceTrade = 0;
}
else if (crossedDown && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_entryPrice = close;
_candlesSinceTrade = 0;
}
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class open_pendingorder_after_position_get_stop_loss_strategy(Strategy):
def __init__(self):
super(open_pendingorder_after_position_get_stop_loss_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._k_period = self.Param("KPeriod", 22)
self._stop_loss_pct = self.Param("StopLossPct", 2.0)
self._take_profit_pct = self.Param("TakeProfitPct", 3.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 4)
self._last_k = None
self._entry_price = 0.0
self._candles_since_trade = 4
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def KPeriod(self):
return self._k_period.Value
@KPeriod.setter
def KPeriod(self, value):
self._k_period.Value = value
@property
def StopLossPct(self):
return self._stop_loss_pct.Value
@StopLossPct.setter
def StopLossPct(self, value):
self._stop_loss_pct.Value = value
@property
def TakeProfitPct(self):
return self._take_profit_pct.Value
@TakeProfitPct.setter
def TakeProfitPct(self, value):
self._take_profit_pct.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(open_pendingorder_after_position_get_stop_loss_strategy, self).OnReseted()
self._last_k = None
self._entry_price = 0.0
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(open_pendingorder_after_position_get_stop_loss_strategy, self).OnStarted2(time)
self._last_k = None
self._entry_price = 0.0
self._candles_since_trade = self.SignalCooldownCandles
rsi = RelativeStrengthIndex()
rsi.Length = self.KPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._process_candle).Start()
def _process_candle(self, candle, current_k):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
k_val = float(current_k)
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
# Check stop-loss / take-profit on existing position
if self.Position != 0 and self._entry_price > 0:
if self.Position > 0:
pnl_pct = (close - self._entry_price) / self._entry_price * 100.0
if pnl_pct <= -float(self.StopLossPct) or pnl_pct >= float(self.TakeProfitPct):
self.SellMarket()
self._entry_price = 0.0
self._last_k = k_val
return
elif self.Position < 0:
pnl_pct = (self._entry_price - close) / self._entry_price * 100.0
if pnl_pct <= -float(self.StopLossPct) or pnl_pct >= float(self.TakeProfitPct):
self.BuyMarket()
self._entry_price = 0.0
self._last_k = k_val
return
if self._last_k is None:
self._last_k = k_val
return
prev_k = self._last_k
self._last_k = k_val
crossed_up = prev_k <= 45.0 and k_val > 45.0
crossed_down = prev_k >= 55.0 and k_val < 55.0
if crossed_up and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._entry_price = close
self._candles_since_trade = 0
elif crossed_down and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._entry_price = close
self._candles_since_trade = 0
def CreateClone(self):
return open_pendingorder_after_position_get_stop_loss_strategy()