Die Backtesting Trade Assistant Panel-Strategie ist ein manueller Helfer, der aus dem MetaTrader 4-Expertenberater Backtesting Trade Assistant Panel V1.10 konvertiert wurde. Das ursprüngliche Skript erstellte ein grafisches Bedienfeld im Tester, mit dem der Bediener die Losgröße, Stop-Loss- und Take-Profit-Abstände ändern und sofort KAUF- oder VERKAUF-Marktaufträge erteilen konnte. Der StockSharp-Port bietet den gleichen Workflow innerhalb einer Strategiekomponente, indem er stark typisierte Parameter und öffentliche Hilfsmethoden anstelle von Widgets auf dem Diagramm verfügbar macht.
Hauptfunktionen:
Behalten Sie ein konfigurierbares Auftragsvolumen zusammen mit Stop-Loss- und Take-Profit-Abständen im MetaTrader-Stil bei (gemessen in „Punkten“).
Erteilen Sie bei Bedarf Long- oder Short-Market-Orders über die Helfer ManualBuy() und ManualSell().
Fügen Sie nach jeder manuellen Eingabe mithilfe der konvertierten Punktwerte automatisch Stop-Loss- und Take-Profit-Offsets hinzu.
Stellen Sie Hilfsmethoden bereit, die das Handelsvolumen und die Risikodistanzen zur Laufzeit aktualisieren und dabei die bearbeitbaren Textfelder des MT4-Panels nachahmen.
Parameter
Name
Beschreibung
Standard
OrderVolume
Volumen in Lots, das auf manuelle Marktaufträge angewendet wird. Durch Ändern des Werts wird auch die Basis Strategy.Volume aktualisiert.
0.1
StopLossPips
Abstand vom Füllpreis bis zum Schutzstopp, ausgedrückt in MetaTrader Punkten. Auf 0 setzen, um die automatische Stop-Loss-Platzierung zu deaktivieren.
50
TakeProfitPips
Abstand vom Füllpreis zum Gewinnziel, ausgedrückt in MetaTrader Punkten. Auf 0 setzen, um die automatische Take-Profit-Platzierung zu deaktivieren.
100
MagicNumber
Vom ursprünglichen EA für die Buchhaltung beibehaltener Bezeichner. Es wird nicht direkt von der StockSharp-Ausführungslogik verwendet, kann aber in benutzerdefinierten Erweiterungen referenziert werden.
99
Manuelle Operationen
Das Original EA basierte auf anklickbaren Schaltflächen. In StockSharp stehen dieselben Aktionen als öffentliche Methoden zur Verfügung:
SetOrderVolume(decimal volume) – synchronisiert den Parameter OrderVolume und den internen Wert Strategy.Volume.
SetStopLoss(decimal pips) / SetTakeProfit(decimal pips) – Passen Sie die Schutzabstände an, während die Strategie ausgeführt wird. Werte werden in MetaTrader Punkten interpretiert, genau wie die MT4-Textfelder.
ManualBuy() – sendet eine Marktkauforder mit dem aktuellen Volumen. Nach der Ausführung wandelt die Strategie die konfigurierten Stop-Loss- und Take-Profit-Punkte in Preisversätze um (unter Verwendung von Symbolmetadaten) und ruft SetStopLoss/SetTakeProfit auf, um Schutzaufträge für die resultierende Nettoposition zu registrieren.
ManualSell() – symmetrischer Helfer für Marktverkaufsaufträge.
CloseAllPositions() – schließt das gesamte Engagement zum Marktpreis. Dies spiegelt den Arbeitsablauf wider, bei dem der Tester Positionen manuell reduzieren konnte.
Alle Schutzabstände werden mit der gleichen Pip-Größen-Heuristik wie in MT4 umgerechnet: Fünf- und dreistellige Symbole multiplizieren PriceStep mit zehn, um einen einzelnen „Punkt“ zu erhalten, während andere Symbole auf dem rohen PriceStep basieren. Wenn Marktdaten nicht die erforderlichen Metadaten bereitstellen, wird eine Fallback-Größe von 0.0001 verwendet, um ein konsistentes Verhalten aufrechtzuerhalten.
Verhaltensnotizen
Die Strategie abonniert Level1-Updates, um den besten Geld-/Briefkurs im Auge zu behalten. Wenn diese Preise nicht verfügbar sind, wird auf den letzten Handelspreis zurückgegriffen, bevor Schutzausgleiche vorgenommen werden.
Es werden keine automatischen Handelssignale generiert – dieses Modul fungiert genau wie das MT4-Panel ausschließlich als manueller Ausführungsassistent.
Da StockSharp Schutzanordnungen nativ verwaltet, ist keine explizite magische Zahl erforderlich. Das Feld ist lediglich aus Gründen der Parität mit dem Quell-Expert Advisor enthalten.
Stop-Loss- und Take-Profit-Abstände können jederzeit vor dem Auslösen von ManualBuy()/ManualSell() angepasst werden, um die Bearbeitung der MT4-Textfelder vor dem Drücken der Tasten zu emulieren.
Unterschiede zum Original EA
Die MetaTrader-Benutzeroberfläche wird durch Strategieparameter und Methodenaufrufe ersetzt. Alle Funktionen stehen programmgesteuert zur Verfügung, ohne dass Diagrammsteuerelemente gerendert werden müssen.
Die Slippage-Behandlung aus dem MT4-Aufruf OrderSend (festgelegt auf 50 Punkte) wird nicht reproduziert, da die BuyMarket/SellMarket-Helfer von StockSharp kein direktes Slippage-Argument offenlegen. Die Umgebung sollte bei Bedarf die Ausführungstoleranz verwalten.
Schutzanordnungen werden mit den übergeordneten SetStopLoss/SetTakeProfit-Helfern von StockSharp statt mit direkten OrderSend-Aufrufen erstellt, wodurch die Implementierung im Einklang mit den StockSharp-Konventionen bleibt.
Anwendungstipps
Konfigurieren Sie wie gewohnt das gewünschte Symbol, Portfolio und Connector in StockSharp und starten Sie dann die Strategie.
Passen Sie OrderVolume, StopLossPips und TakeProfitPips über das Parameterraster oder die bereitgestellten Setter-Methoden an.
Rufen Sie ManualBuy() oder ManualSell() an, wenn eine diskretionäre Eingabe erforderlich ist. Der Helfer fügt die angeforderten Schutzanordnungen automatisch an.
Verwenden Sie CloseAllPositions(), um das Risiko während Backtests oder diskretionären Live-Handelssitzungen sofort zu reduzieren.
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>
/// Trade assistant strategy with configurable stop-loss and take-profit.
/// Simplified from the backtesting trade assistant panel.
/// </summary>
public class BacktestingTradeAssistantPanelStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossPips;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _sma;
private decimal _pipSize;
private decimal _entryPrice;
private decimal? _stopPrice;
private decimal? _takePrice;
/// <summary>
/// Stop loss distance in pips.
/// </summary>
public decimal StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take profit distance in pips.
/// </summary>
public decimal TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Candle type for signals.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public BacktestingTradeAssistantPanelStrategy()
{
_stopLossPips = Param(nameof(StopLossPips), 50m)
.SetNotNegative()
.SetDisplay("Stop Loss (pips)", "Stop-loss distance in pips", "Risk");
_takeProfitPips = Param(nameof(TakeProfitPips), 100m)
.SetNotNegative()
.SetDisplay("Take Profit (pips)", "Take-profit distance in pips", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle series for trading signals", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sma = null;
_pipSize = 0m;
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pipSize = CalculatePipSize();
_sma = new SimpleMovingAverage { Length = 20 };
SubscribeCandles(CandleType)
.Bind(_sma, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormed)
return;
var price = candle.ClosePrice;
// Check stop-loss and take-profit
if (Position != 0 && _entryPrice > 0m)
{
if (Position > 0)
{
if (_stopPrice.HasValue && price <= _stopPrice.Value)
{
SellMarket(Math.Abs(Position));
ResetPosition();
return;
}
if (_takePrice.HasValue && price >= _takePrice.Value)
{
SellMarket(Math.Abs(Position));
ResetPosition();
return;
}
}
else if (Position < 0)
{
if (_stopPrice.HasValue && price >= _stopPrice.Value)
{
BuyMarket(Math.Abs(Position));
ResetPosition();
return;
}
if (_takePrice.HasValue && price <= _takePrice.Value)
{
BuyMarket(Math.Abs(Position));
ResetPosition();
return;
}
}
}
// Entry: SMA crossover
if (Position == 0)
{
var pip = _pipSize > 0m ? _pipSize : 1m;
if (price > smaValue)
{
BuyMarket();
_entryPrice = price;
_stopPrice = StopLossPips > 0m ? price - StopLossPips * pip : null;
_takePrice = TakeProfitPips > 0m ? price + TakeProfitPips * pip : null;
}
else if (price < smaValue)
{
SellMarket();
_entryPrice = price;
_stopPrice = StopLossPips > 0m ? price + StopLossPips * pip : null;
_takePrice = TakeProfitPips > 0m ? price - TakeProfitPips * pip : null;
}
}
}
private void ResetPosition()
{
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
}
private decimal CalculatePipSize()
{
var step = Security?.PriceStep ?? 0m;
if (step <= 0m)
return 0.0001m;
var decimals = Security?.Decimals ?? 0;
return decimals is 5 or 3 ? step * 10m : step;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import Math, TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class backtesting_trade_assistant_panel_strategy(Strategy):
def __init__(self):
super(backtesting_trade_assistant_panel_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._stop_loss_pips = self.Param("StopLossPips", 50.0)
self._take_profit_pips = self.Param("TakeProfitPips", 100.0)
self._sma = None
self._pip_size = 0.0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def StopLossPips(self):
return self._stop_loss_pips.Value
@StopLossPips.setter
def StopLossPips(self, value):
self._stop_loss_pips.Value = value
@property
def TakeProfitPips(self):
return self._take_profit_pips.Value
@TakeProfitPips.setter
def TakeProfitPips(self, value):
self._take_profit_pips.Value = value
def OnReseted(self):
super(backtesting_trade_assistant_panel_strategy, self).OnReseted()
self._sma = None
self._pip_size = 0.0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
def _calculate_pip_size(self):
sec = self.Security
if sec is None:
return 0.0001
step = sec.PriceStep
if step is None or float(step) <= 0:
return 0.0001
step_val = float(step)
decimals = sec.Decimals
if decimals is not None and (int(decimals) == 5 or int(decimals) == 3):
return step_val * 10.0
return step_val
def OnStarted2(self, time):
super(backtesting_trade_assistant_panel_strategy, self).OnStarted2(time)
self._pip_size = self._calculate_pip_size()
self._sma = SimpleMovingAverage()
self._sma.Length = 20
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._sma, self._process_candle).Start()
def _reset_position(self):
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
def _process_candle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormed:
return
price = float(candle.ClosePrice)
sma_val = float(sma_value)
# Check stop-loss and take-profit
if self.Position != 0 and self._entry_price > 0:
if self.Position > 0:
if self._stop_price is not None and price <= self._stop_price:
self.SellMarket(abs(float(self.Position)))
self._reset_position()
return
if self._take_price is not None and price >= self._take_price:
self.SellMarket(abs(float(self.Position)))
self._reset_position()
return
elif self.Position < 0:
if self._stop_price is not None and price >= self._stop_price:
self.BuyMarket(abs(float(self.Position)))
self._reset_position()
return
if self._take_price is not None and price <= self._take_price:
self.BuyMarket(abs(float(self.Position)))
self._reset_position()
return
# Entry: SMA crossover
if self.Position == 0:
pip = self._pip_size if self._pip_size > 0 else 1.0
sl_pips = float(self.StopLossPips)
tp_pips = float(self.TakeProfitPips)
if price > sma_val:
self.BuyMarket()
self._entry_price = price
self._stop_price = price - sl_pips * pip if sl_pips > 0 else None
self._take_price = price + tp_pips * pip if tp_pips > 0 else None
elif price < sma_val:
self.SellMarket()
self._entry_price = price
self._stop_price = price + sl_pips * pip if sl_pips > 0 else None
self._take_price = price - tp_pips * pip if tp_pips > 0 else None
def CreateClone(self):
return backtesting_trade_assistant_panel_strategy()