Die TST Pullback Reversal Strategy achtet auf aggressive Intrabar-Umkehrungen. Es wurde vom ursprünglichen MetaTrader 4 Expert Advisor TST.mq4 konvertiert und unter Verwendung des High-Level-StockSharp API neu erstellt. Die Methode sucht nach Kerzen, bei denen sich der Preis stark von der Kerzenöffnung entfernt hat, nachdem er ein Intraday-Extrem erreicht hat, und schwächt diese Bewegung dann in Erwartung einer Mean-Reversion ab. Die Strategie handelt sowohl Long- als auch Short-Positionen und verwendet statische Stop-Loss- und Take-Profit-Level, die in Preisschritten gemessen werden.
Signallogik
Lange Einrichtung
Die Kerze schließt unter ihrem Eröffnungswert (Open > Close).
Der Abstand zwischen dem Kerzenhoch und dem Schlusskurs ist größer als GapPoints * PriceStep.
Auf demselben Balken wurde zuvor kein Trade ausgeführt.
Wenn die Strategie zufrieden ist, schließt sie alle Short-Positionen und kauft OrderVolume Einheiten (zuzüglich der Größe, die zum Umwandeln von einer Short- in eine Long-Position erforderlich ist).
Kurze Einrichtung
Die Kerze schließt über ihrem Eröffnungskurs (Close > Open).
Der Abstand zwischen dem Schlusskurs und dem Kerzentief ist größer als GapPoints * PriceStep.
Auf demselben Balken wurde zuvor kein Trade ausgeführt.
Wenn die Strategie zufrieden ist, schließt sie alle Long-Positionen und verkauft OrderVolume Einheiten (zuzüglich der Größe, die zum Umwandeln von einer Long- in eine Short-Position erforderlich ist).
Positionsmanagement
Ein neuer Trade weist sofort statische Stop-Loss- und Take-Profit-Level zu, die aus dem Füllpreis und den Parametern StopLossPoints/TakeProfitPoints berechnet werden.
Bei jeder fertigen Kerze überprüft die Strategie das Hoch/Tief der Kerze, um festzustellen, ob der Stopp oder das Ziel berührt wurde, und verlässt die Position, wenn dies ausgelöst wird. Stop-Loss-Checks haben Vorrang vor Take-Profit-Checks.
Nach einem Ausstieg werden die gespeicherten Risikostufen gelöscht, aber die Strategie merkt sich weiterhin die Balkenzeit, um einen erneuten Eintritt während derselben Kerze zu vermeiden (Reproduzieren des NevBar()-Schutzes aus der MQL4-Version).
Parameter
StopLossPoints (Standard 500): Abstand vom Einstieg bis zum Schutzstopp, ausgedrückt in Preisschritten.
TakeProfitPoints (Standard 100): Abstand vom Einstieg bis zum Gewinnziel, ausgedrückt in Preisschritten.
GapPoints (Standardwert 500): Mindestrückgang zwischen dem Kerzenextrem und dem Schlusskurs, der erforderlich ist, um ein Signal zu erzeugen.
OrderVolume (Standard 0.1): Menge, die mit jeder neuen Market-Order gesendet wird.
CandleType (Standard 1 hour): Zeitrahmen der über SubscribeCandles bereitgestellten Kerzen.
Alle entfernungsbasierten Einstellungen werden mit dem PriceStep des Instruments multipliziert. Wenn das Wertpapier keinen Schritt meldet, fällt die Strategie auf 1 zurück.
Implementierungshinweise
Die Konvertierung verwendet die übergeordnete API von StockSharp und erstellt keine benutzerdefinierten Indikatorsammlungen.
Um mit dem Strategy Designer kompatibel zu bleiben, werden nur fertige Kerzen verarbeitet; Dies nähert sich den Intrabar-Entscheidungen des MT4-Roboters an, indem vervollständigte Bardaten verwendet werden.
Ein dediziertes Flag _lastSignalBarTime repliziert den NevBar()-Guard aus dem MQL4-Code, sodass nur eine Order pro Kerze geöffnet werden kann.
Die Handhabung des Ordervolumens spiegelt das MT4-Verhalten wider: Bestehende gegensätzliche Positionen werden abgeflacht, bevor die neue Richtung in einer einzelnen Marktorder festgelegt wird.
Stop-Loss- und Take-Profit-Orders werden innerhalb der Strategielogik simuliert (anstelle serverseitiger Orders), um den ursprünglichen Abständen zu entsprechen und gleichzeitig die Kontrolle innerhalb von StockSharp zu behalten.
Nutzungstipps
Wählen Sie GapPoints relativ zur Volatilität des gehandelten Instruments; Größere Werte verringern die Handelshäufigkeit, filtern jedoch kleinere Rückschläge.
Da Stop- und Zielprüfungen auf fertigen Kerzen basieren, sollten Sie kürzere Kerzendauern in Betracht ziehen, wenn Sie eine engere Ausführung benötigen.
Kombinieren Sie die Strategie mit zusätzlichen Filtern (Trend, Tageszeit, Volumen), wenn Sie sie auf Live-Märkten einsetzen, um Whipsaw-Trades zu reduzieren.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// TST Pullback Reversal: buys after deep pullback from candle high,
/// sells after rally from candle low. Uses ATR for thresholds.
/// </summary>
public class TstStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _pullbackMultiplier;
private readonly StrategyParam<decimal> _stopMultiplier;
private readonly StrategyParam<decimal> _takeMultiplier;
private decimal _entryPrice;
private decimal _stopPrice;
private decimal _takePrice;
public TstStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
_pullbackMultiplier = Param(nameof(PullbackMultiplier), 0.5m)
.SetDisplay("Pullback Mult", "ATR multiplier for pullback threshold.", "Signals");
_stopMultiplier = Param(nameof(StopMultiplier), 2.0m)
.SetDisplay("Stop Mult", "ATR multiplier for stop loss.", "Risk");
_takeMultiplier = Param(nameof(TakeMultiplier), 1.0m)
.SetDisplay("Take Mult", "ATR multiplier for take profit.", "Risk");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public decimal PullbackMultiplier
{
get => _pullbackMultiplier.Value;
set => _pullbackMultiplier.Value = value;
}
public decimal StopMultiplier
{
get => _stopMultiplier.Value;
set => _stopMultiplier.Value = value;
}
public decimal TakeMultiplier
{
get => _takeMultiplier.Value;
set => _takeMultiplier.Value = value;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_stopPrice = 0;
_takePrice = 0;
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (atrVal <= 0)
return;
var close = candle.ClosePrice;
var open = candle.OpenPrice;
var high = candle.HighPrice;
var low = candle.LowPrice;
var threshold = atrVal * PullbackMultiplier;
var stopDist = atrVal * StopMultiplier;
var takeDist = atrVal * TakeMultiplier;
// Risk management
if (Position > 0)
{
if (_stopPrice > 0 && close <= _stopPrice)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
_takePrice = 0;
return;
}
if (_takePrice > 0 && close >= _takePrice)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
_takePrice = 0;
return;
}
}
else if (Position < 0)
{
if (_stopPrice > 0 && close >= _stopPrice)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
_takePrice = 0;
return;
}
if (_takePrice > 0 && close <= _takePrice)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
_takePrice = 0;
return;
}
}
// Entry: deep pullback from high = buy reversal
if (Position == 0)
{
if (open > close && high - close > threshold)
{
_entryPrice = close;
_stopPrice = close - stopDist;
_takePrice = close + takeDist;
BuyMarket();
}
else if (close > open && close - low > threshold)
{
_entryPrice = close;
_stopPrice = close + stopDist;
_takePrice = close - takeDist;
SellMarket();
}
}
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import AverageTrueRange
class tst_strategy(Strategy):
def __init__(self):
super(tst_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period", "Indicators")
self._pullback_multiplier = self.Param("PullbackMultiplier", 0.5) \
.SetDisplay("Pullback Mult", "ATR multiplier for pullback threshold", "Signals")
self._stop_multiplier = self.Param("StopMultiplier", 2.0) \
.SetDisplay("Stop Mult", "ATR multiplier for stop loss", "Risk")
self._take_multiplier = self.Param("TakeMultiplier", 1.0) \
.SetDisplay("Take Mult", "ATR multiplier for take profit", "Risk")
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def AtrLength(self):
return self._atr_length.Value
@property
def PullbackMultiplier(self):
return self._pullback_multiplier.Value
@property
def StopMultiplier(self):
return self._stop_multiplier.Value
@property
def TakeMultiplier(self):
return self._take_multiplier.Value
def OnStarted2(self, time):
super(tst_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, atr_val):
if candle.State != CandleStates.Finished:
return
av = float(atr_val)
if av <= 0:
return
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
threshold = av * float(self.PullbackMultiplier)
stop_dist = av * float(self.StopMultiplier)
take_dist = av * float(self.TakeMultiplier)
# Risk management
if self.Position > 0:
if self._stop_price > 0 and close <= self._stop_price:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
return
if self._take_price > 0 and close >= self._take_price:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
return
elif self.Position < 0:
if self._stop_price > 0 and close >= self._stop_price:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
return
if self._take_price > 0 and close <= self._take_price:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
return
# Entry: deep pullback from high = buy reversal
if self.Position == 0:
if open_p > close and high - close > threshold:
self._entry_price = close
self._stop_price = close - stop_dist
self._take_price = close + take_dist
self.BuyMarket()
elif close > open_p and close - low > threshold:
self._entry_price = close
self._stop_price = close + stop_dist
self._take_price = close - take_dist
self.SellMarket()
def OnReseted(self):
super(tst_strategy, self).OnReseted()
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
def CreateClone(self):
return tst_strategy()