VirtualProfitLossTrailStrategy reproduziert das Verhalten des MetaTrader Expert Advisors "Virtual Profit Loss Trail" in StockSharp. Die Strategie eröffnet selbst niemals neue Positionen. Stattdessen überwacht sie kontinuierlich die aktuelle Position des ausgewählten Wertpapiers und wendet Schutzlogik an:
Eine konfigurierbare Take-Profit-Distanz in Pips.
Eine konfigurierbare Stop-Loss-Distanz in Pips.
Einen virtuellen Trailing Stop, der nach Erreichen eines Mindestgewinns aktiviert wird und nur dann mit dem Markt gleitet, wenn der Preis um den angegebenen Trailing-Schritt vorankommt.
Da die Schutzlevel virtuell sind, werden keine tatsächlichen Stop- oder Limit-Orders an die Börse gesendet. Die Strategie überwacht beste Bid-/Ask-Aktualisierungen und schließt die offene Position mit einer Marktorder, wenn eines der virtuellen Niveaus berührt wird.
Parameter
Parameter
Beschreibung
Take-profit (pips)
Distanz zwischen Einstiegspreis und Gewinnziel. Auf 0 setzen, um den Take-Profit-Ausstieg zu deaktivieren.
Stop-loss (pips)
Distanz zwischen Einstiegspreis und Schutz-Stop. Auf 0 setzen, um den Stop-Loss-Ausstieg zu deaktivieren.
Trailing stop (pips)
Distanz zur Berechnung des Trailing Stops. Bei 0 ist die Trailing-Logik vollständig deaktiviert.
Trailing step (pips)
Zusätzlicher Gewinn, der erzielt werden muss, bevor der Trailing Stop weiter verschoben wird. 0 verwenden, um den Trail bei jedem neuen Hoch/Tief zu verschieben.
Trailing activation (pips)
Mindestgewinn, der gesichert werden muss, bevor der Trailing Stop aktiv wird. Bei 0 startet Trailing sofort nach dem Positionseinstieg.
Alle Distanzen werden in Pip-Einheiten gemessen. Die Strategie leitet die Pip-Größe automatisch aus dem Preisschritt des Wertpapiers ab: Bei Symbolen mit drei oder fünf Dezimalstellen ist ein Pip als zehn Preisschritte definiert, ansonsten als ein Schritt.
Logik
Marktdatensubscription - die Strategie abonniert Level1-Daten, um beste Bid- und Ask-Aktualisierungen zu erhalten. Nur fertige Aktualisierungen werden verarbeitet, sodass der Algorithmus sowohl in Echtzeit als auch bei historischen Replays funktioniert.
Long-Positionsverwaltung - wenn die Nettoposition long ist, berechnet die Strategie virtuelle Stop-Loss-, Take-Profit- und Trailing-Stop-Niveaus auf Basis des durchschnittlichen Einstiegspreises. Berührt der beste Bid Stop-Loss oder Take-Profit, wird die Position sofort geschlossen. Sobald der Aktivierungsgewinn erreicht ist, folgt der Trailing Stop dem Preis nach oben. Der Stop wird nur weitergezogen, wenn die Trailing-Schritt-Anforderung erfüllt ist.
Short-Positionsverwaltung - dieselbe Logik wird symmetrisch mit dem besten Ask für Ausstiege aus Short-Positionen angewendet.
Reset-Verhalten - wenn die Position vollständig geschlossen ist, werden interne Trailing-Referenzen zurückgesetzt, um versehentliche Wiedereinstiegssignale zu verhindern.
Nutzungstipps
Binden Sie die Strategie an einen Connector und ein Wertpapier, das bereits eine offene Position hat oder Orders von anderen Strategien oder manuellem Handel erhalten wird. Der Manager steuert die aggregierte Positionsgröße.
Stellen Sie sicher, dass Level1-Daten verfügbar sind; ohne aktuelle Bid-/Ask-Werte können die virtuellen Niveaus nicht bewertet werden.
Die Strategie kann mit jeder einstiegserzeugenden Strategie kombiniert werden, indem beide unter demselben Portfolio und Wertpapier laufen. Nur eine Instanz sollte die Schutzlogik verwalten, um Konflikte zu vermeiden.
Unterschiede zum MQL-Expert
Die StockSharp-Version arbeitet mit aggregierten Positionen statt mit einzelnen Ordertickets. Sie berechnet automatisch den von der Plattform bereitgestellten durchschnittlichen Einstiegspreis.
Visuelles Linienzeichnen und Tonalarme des ursprünglichen Experts werden durch Logging in StockSharp ersetzt. Schutzaktionen sind im Strategiejournal sichtbar.
Die gleiche pipbasierte Konfiguration bleibt erhalten, einschließlich Trailing-Aktivierungsschwelle und inkrementellem Trailing-Schritt.
Dateien
CS/VirtualProfitLossTrailStrategy.cs - C#-Implementierung der Strategie.
README.md - diese Dokumentation.
README_zh.md - Übersetzung ins vereinfachte Chinesisch.
README_ru.md - russische Übersetzung.
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 class VirtualProfitLossTrailStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public VirtualProfitLossTrailStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class virtual_profit_loss_trail_strategy(Strategy):
def __init__(self):
super(virtual_profit_loss_trail_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(virtual_profit_loss_trail_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(virtual_profit_loss_trail_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return virtual_profit_loss_trail_strategy()