Unterbrechen Sie den Handel mit der Strategie für aufeinanderfolgende Verluste
Die Pause Trading On Consecutive Loss-Strategie reproduziert die Risikokontrolllogik des MetaTrader 4-Expertenberaters "Pause Trading On Consecutive Loss". Das ursprüngliche Skript überwachte die letzten abgeschlossenen Geschäfte, zählte, wie viele davon mit einem negativen Gewinn endeten, und setzte neue Aufträge aus, wenn die Verluststrähne innerhalb eines kurzen Zeitfensters ein benutzerdefiniertes Limit überschritt. Der StockSharp-Port behält dieses Verhalten bei und umschließt gleichzeitig ein Einstiegsmodell mit minimalem Momentum, sodass der Pausenmechanismus innerhalb der eigenständigen Strategie ausgewertet werden kann.
Wie es funktioniert
Die Strategie abonniert Zeitrahmenkerzen, die durch CandleType angegeben werden. Immer wenn eine fertige Kerze eintrifft, wird der Schlusskurs mit dem vorherigen Schlusskurs verglichen. Bei einem Anstieg versucht die Strategie einen Long-Einstieg; ist er gesunken, wird ein Short-Einstieg in Betracht gezogen. Positionen werden immer dann geschlossen, wenn eine bullische Position einer bärischen Kerze gegenübersteht (Schluss unter der Eröffnung) oder eine bärische Position einer zinsbullischen Kerze gegenübersteht (Schluss über der Eröffnung).
Nach jeder geschlossenen Position wird der realisierte Gewinn der Strategie überprüft. Verliererergebnisse stellen ihren Abschlusszeitstempel in eine interne FIFO-Liste, die nur aufeinanderfolgende Verluste speichert. Gewinnbringende oder ausgeglichene Exits löschen die Liste, genau wie die MQL-Schleife abgebrochen wurde, als sie auf ein Geschäft ohne Verluste stieß.
Wenn die Liste ConsecutiveLosses Elemente erreicht, prüft die Strategie, ob der Zeitunterschied zwischen dem ältesten und dem neuesten Verlust innerhalb von WithinMinutes liegt. Wenn dies der Fall ist, wird der Handel unterbrochen, bis PauseMinutes seit dem letzten Handelsschluss verstrichen ist. Während der Pause werden keine neuen Marktaufträge übermittelt, aber das bestehende Positionsmanagement läuft weiter, sodass sich das Buch auf natürliche Weise verflachen kann.
Sobald die Pause abgelaufen ist, wird die Liste der Verluste gelöscht und der Handel wird automatisch fortgesetzt. Das Verhalten ahmt die ursprünglichen Funktionen CheckLastNLossDifference und lastOrderCloseTime nach, ohne auf einen dauerhaften Scan des Bestellverlaufs angewiesen zu sein.
Die Implementierung nutzt die High-Level-Kerzenabonnements (SubscribeCandles) von StockSharp und den integrierten PnL-Manager, um realisierte Gewinne zu überwachen. Eine einfache Warteschlange (Queue<DateTimeOffset>) erfasst die Zeitstempel der Verlustserie und respektiert dabei das Verbot der redundanten manuellen Verlaufsdurchquerung.
Parameter
Parameter
Standard
Beschreibung
CandleType
Zeitrahmen von 5 Minuten
Kerzenaggregation, die für die einfachen Momentum-Einträge verwendet wird.
OrderVolume
0.1
Volumen (in Lots/Kontrakten), das mit jedem Ein- und Ausstiegsauftrag gesendet wird.
ConsecutiveLosses
3
Anzahl der aufeinanderfolgenden Verlustpositionen, die erforderlich sind, bevor neue Trades pausiert werden.
WithinMinutes
20
Maximal zulässige Anzahl von Minuten zwischen der ersten und der letzten Niederlage im Streak. Ein Wert von Null deaktiviert die Fensterprüfung.
PauseMinutes
20
Dauer der Handelsaussetzung nach Feststellung der Verlustserie.
Notizen
Die Warteschlange der Verlustzeitstempel wird nur dann gefüllt, wenn die Strategie flach ist und gerade einen Verlust realisiert hat. Teilabschlüsse oder profitable Trades verlängern den Streak nicht und verhindern so Fehlalarme.
Der Pausentimer wird für jede fertige Kerze ausgewertet. Wenn PauseMinutes vergehen, während die Strategie inaktiv ist, wird der Handel mit der nächsten Kerze sofort freigeschaltet.
Da die StockSharp-Version auf einer Netting-Position arbeitet, wird die realisierte PnL-Differenz aus PnLManager.RealizedPnL abgeleitet, wodurch die MetaTrader-Verlaufssuche getreu widergespiegelt wird, ohne dass das gesamte Auftragsprotokoll erneut verarbeitet werden muss.
using System;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simplified from "Pause Trading On Consecutive Loss" MetaTrader expert.
/// Uses simple momentum entries (close vs previous close) with a pause mechanism
/// that halts trading after consecutive losing trades within a time window.
/// </summary>
public class PauseTradingOnConsecutiveLossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _consecutiveLosses;
private readonly StrategyParam<int> _pauseBars;
private decimal? _previousClose;
private int _lossStreak;
private int _pauseCountdown;
private decimal _entryPrice;
private Sides? _entryDirection;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int ConsecutiveLosses
{
get => _consecutiveLosses.Value;
set => _consecutiveLosses.Value = value;
}
public int PauseBars
{
get => _pauseBars.Value;
set => _pauseBars.Value = value;
}
public PauseTradingOnConsecutiveLossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for momentum entries", "General");
_consecutiveLosses = Param(nameof(ConsecutiveLosses), 3)
.SetGreaterThanZero()
.SetDisplay("Consecutive Losses", "Losses before pausing", "Risk");
_pauseBars = Param(nameof(PauseBars), 8)
.SetGreaterThanZero()
.SetDisplay("Pause Bars", "Number of bars to pause after loss streak", "Risk");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_previousClose = null;
_lossStreak = 0;
_pauseCountdown = 0;
_entryPrice = 0;
_entryDirection = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
if (_previousClose is null)
{
_previousClose = close;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
var momentumThreshold = _previousClose.Value * 0.003m;
// Check if we should pause
if (_pauseCountdown > 0)
{
_pauseCountdown--;
_previousClose = close;
return;
}
// Check for exit and track wins/losses
if (Position != 0)
{
var shouldExit = false;
if (Position > 0 && close < _previousClose.Value - momentumThreshold)
shouldExit = true;
else if (Position < 0 && close > _previousClose.Value + momentumThreshold)
shouldExit = true;
if (shouldExit)
{
// Determine if this was a winning or losing trade
var isLoss = false;
if (_entryDirection == Sides.Buy && close < _entryPrice)
isLoss = true;
else if (_entryDirection == Sides.Sell && close > _entryPrice)
isLoss = true;
if (isLoss)
{
_lossStreak++;
if (_lossStreak >= ConsecutiveLosses)
{
_pauseCountdown = PauseBars;
_lossStreak = 0;
}
}
else
{
_lossStreak = 0;
}
// Close position
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(Math.Abs(Position));
_entryDirection = null;
}
}
// New entry: momentum - close > prev close -> buy, close < prev close -> sell
if (Position == 0 && _entryDirection is null)
{
if (close > _previousClose.Value + momentumThreshold)
{
BuyMarket(volume);
_entryPrice = close;
_entryDirection = Sides.Buy;
}
else if (close < _previousClose.Value - momentumThreshold)
{
SellMarket(volume);
_entryPrice = close;
_entryDirection = Sides.Sell;
}
}
_previousClose = close;
}
/// <inheritdoc />
protected override void OnReseted()
{
_previousClose = null;
_lossStreak = 0;
_pauseCountdown = 0;
_entryPrice = 0;
_entryDirection = null;
base.OnReseted();
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class pause_trading_on_consecutive_loss_strategy(Strategy):
def __init__(self):
super(pause_trading_on_consecutive_loss_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60))).SetDisplay("Candle Type", "Timeframe for momentum entries", "General")
self._consecutive_losses = self.Param("ConsecutiveLosses", 3).SetGreaterThanZero().SetDisplay("Consecutive Losses", "Losses before pausing", "Risk")
self._pause_bars = self.Param("PauseBars", 8).SetGreaterThanZero().SetDisplay("Pause Bars", "Number of bars to pause after loss streak", "Risk")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(pause_trading_on_consecutive_loss_strategy, self).OnReseted()
self._previous_close = None
self._loss_streak = 0
self._pause_countdown = 0
self._entry_price = 0
self._entry_direction = None
def OnStarted2(self, time):
super(pause_trading_on_consecutive_loss_strategy, self).OnStarted2(time)
self._previous_close = None
self._loss_streak = 0
self._pause_countdown = 0
self._entry_price = 0
self._entry_direction = None
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawOwnTrades(area)
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
if self._previous_close is None:
self._previous_close = close
return
momentum_threshold = float(self._previous_close) * 0.003
if self._pause_countdown > 0:
self._pause_countdown -= 1
self._previous_close = close
return
if self.Position != 0:
should_exit = False
if self.Position > 0 and close < self._previous_close - momentum_threshold:
should_exit = True
elif self.Position < 0 and close > self._previous_close + momentum_threshold:
should_exit = True
if should_exit:
is_loss = False
if self._entry_direction == "buy" and close < self._entry_price:
is_loss = True
elif self._entry_direction == "sell" and close > self._entry_price:
is_loss = True
if is_loss:
self._loss_streak += 1
if self._loss_streak >= self._consecutive_losses.Value:
self._pause_countdown = self._pause_bars.Value
self._loss_streak = 0
else:
self._loss_streak = 0
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._entry_direction = None
if self.Position == 0 and self._entry_direction is None:
if close > self._previous_close + momentum_threshold:
self.BuyMarket()
self._entry_price = close
self._entry_direction = "buy"
elif close < self._previous_close - momentum_threshold:
self.SellMarket()
self._entry_price = close
self._entry_direction = "sell"
self._previous_close = close
def CreateClone(self):
return pause_trading_on_consecutive_loss_strategy()