Konvertierung des MetaTrader 5-Expertenberaters Spasm (barabashkakvn's Edition) zur High-Level-API von StockSharp.
Handelt Ausbrüche aus einem adaptiven Kanal, der durch die jüngste Volatilität dimensioniert wird, und wechselt zwischen bullischen und bärischen Regimes.
Funktioniert auf jedem Instrument und Zeitrahmen, der durch den Parameter CandleType bereitgestellt wird, standardmäßig Stundenkerzen.
Datenvorbereitung
Abonniert die durch CandleType definierte Kerzenserie für das Strategie-Wertpapier.
Erstellt einen Volatilitätsschätzer aus den letzten VolatilityPeriod Kerzen:
Wenn UseWeightedVolatility deaktiviert ist, ist der Schätzer ein einfacher gleitender Durchschnitt der Kerzenspanne.
Wenn UseWeightedVolatility aktiviert ist, wird der Schätzer zu einem linear gewichteten gleitenden Durchschnitt, der die neuesten Balken betont.
Die Kerzenspanne ist standardmäßig High - Low. Wenn UseOpenCloseRange aktiviert ist, wird stattdessen die absolute Differenz zwischen Eröffnung und Schluss verwendet, was den Moduswechsel des Original-EA reproduziert.
Der rohe Durchschnittskurs wird in Preisschritte umgerechnet und mit VolatilityMultiplier multipliziert. Das Ergebnis wird auf eine ganzzahlige Anzahl von Schritten abgerundet und schließlich wieder mit der Tick-Größe des Instruments multipliziert, um den Ausbruchsschwellenwert zu bilden.
Während der ersten VolatilityPeriod * 3 abgeschlossenen Kerzen sammelt die Strategie das neueste höchste Hoch und niedrigste Tief zusammen mit ihren Zeitstempeln, um zu entscheiden, welcher Swing aktueller ist. Diese Information initialisiert den anfänglichen Trendzustand und die Referenzpreise, sobald genügend Kerzen verarbeitet wurden.
Parameter
Name
Standard
Beschreibung
Volume
1
Ordervolumen bei jedem Markteinstieg.
VolatilityMultiplier
5
Multiplikator für die gemittelte Volatilität zur Dimensionierung des Ausbruchspuffers.
VolatilityPeriod
24
Anzahl der Kerzen für die Volatilitätsmittelung und den anfänglichen Swing-Scan.
UseWeightedVolatility
false
Wechselt den Volatilitätsdurchschnitt von einfachem zu linear gewichtetem gleitenden Durchschnitt.
UseOpenCloseRange
false
Verwendet die absolute Eröffnungs-Schluss-Bewegung als Volatilitätsquelle anstatt der Hoch-Tief-Spanne.
StopLossFraction
0.5
Anteil des Volatilitätsschwellenwerts zur Berechnung des Stop-Loss-Abstands. Ein Minimum von drei Preisschritten wird erzwungen.
CandleType
1-Stunden-Zeitrahmen
Kerzentyp und Zeitrahmen für alle Berechnungen.
Handelslogik
Trend-Tracking
Die Strategie hält _highestPrice und _lowestPrice als Anker des aktuellen Swings.
Wenn der Preis um mehr als den aktuellen Schwellenwert über das gespeicherte Hoch hinaus steigt, wird _highestPrice auf das Kerzenhoch aktualisiert. Analog dazu aktualisiert ein Rückgang jenseits des Schwellenwerts _lowestPrice auf das Kerzentief.
Das boolesche _isTrendUp speichert, ob sich die Strategie derzeit im bullischen (true) oder bärischen (false) Regime befindet.
Einstiegsregeln
Wenn _isTrendUpfalse ist (bärisches Regime) und der Kerzenschluss _lowestPrice + threshold überschreitet, wechselt die Strategie in den bullischen Modus und sendet BuyMarket(Volume + Math.Abs(Position)). Dies schließt jegliches Short-Exposure und eröffnet eine Long-Position gleich Volume.
Wenn _isTrendUptrue ist (bullisches Regime) und der Kerzenschluss unter _highestPrice - threshold fällt, wechselt die Strategie in den bärischen Modus und sendet SellMarket(Volume + Math.Abs(Position)), um in eine Short-Position zu wechseln.
Stop-Management
Beim Eintritt in eine Long-Position wird der Stop-Preis bei entry - max(threshold * StopLossFraction, 3 * priceStep) platziert.
Beim Eintritt in eine Short-Position wird der Stop-Preis bei entry + max(threshold * StopLossFraction, 3 * priceStep) platziert.
Wenn das Tief einer Kerze den Long-Stop erreicht oder das Hoch den Short-Stop erreicht, wird die entsprechende Position durch eine Marktorder geschlossen. Stops sind deaktiviert, wenn StopLossFraction auf null gesetzt ist.
Risikokontrollen und Infrastruktur
StartProtection() wird beim Start aufgerufen, sodass die integrierten Risikoprotektionen aktiviert werden, sobald die Strategie startet.
Die Strategie reagiert nur auf abgeschlossene Kerzen, um Intrabar-Rauschen zu vermeiden und die balkenweise Neuberechnung des Original-EA zu spiegeln.
Alle Kommentare und Parameternamen werden gemäß den Anforderungen auf Englisch gehalten.
Unterschiede zur MQL-Version
Der ursprüngliche EA berechnete Schwellenwerte bei jedem Tick neu. In diesem Port wird die Logik auf abgeschlossenen Kerzen ausgeführt, da die High-Level-API mit Kerzenabonnements arbeitet.
Stop-Loss-Durchsetzung erfolgt auf Kerzendaten. Intrabar-Stop-Treffer, die sich innerhalb derselben Kerze umkehren, werden daher an den Kerzengrenzen ausgewertet.
Symbol-Eigenschaften wie Spread und broker-spezifische Stop-Level sind in StockSharp nicht in der gleichen Form verfügbar. Ein konservatives Minimum von drei Preisschritten wird verwendet, wenn der berechnete Stop-Abstand zu klein ist, was den Fallback der MetaTrader-Implementierung reproduziert.
Verwendungshinweise
Stellen Sie sicher, dass das Strategie-Wertpapier einen gültigen PriceStep liefert. Wenn er nicht angegeben ist, setzt der Code den Schritt standardmäßig auf 1.
Die Strategie ist richtungsagnostisch und kann auf Spot-, Futures- oder CFD-Instrumenten verwendet werden, solange der Feed die konfigurierten Kerzen liefert.
Es ist kein Take-Profit-Ziel definiert; Ausstiege erfolgen nur über Regimewechsel oder Stop-Loss-Auslöser.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Volatility breakout strategy converted from the MetaTrader Spasm expert advisor.
/// Tracks directional swings using adaptive thresholds derived from ATR.
/// Buys when price breaks above recent high + ATR*multiplier, sells when price breaks below recent low - ATR*multiplier.
/// </summary>
public class SpasmStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _volatilityMultiplier;
private decimal _highestPrice;
private decimal _lowestPrice;
private decimal _prevRange;
private bool _initialized;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal VolatilityMultiplier
{
get => _volatilityMultiplier.Value;
set => _volatilityMultiplier.Value = value;
}
public SpasmStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for analysis", "General");
_volatilityMultiplier = Param(nameof(VolatilityMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("Volatility Multiplier", "Multiplier applied to ATR for breakout bands", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highestPrice = 0m;
_lowestPrice = decimal.MaxValue;
_prevRange = 0m;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highestPrice = 0m;
_lowestPrice = decimal.MaxValue;
_prevRange = 0m;
_initialized = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!_initialized)
{
_highestPrice = candle.HighPrice;
_lowestPrice = candle.LowPrice;
_prevRange = candle.HighPrice - candle.LowPrice;
_initialized = true;
return;
}
// Update extremes
if (candle.HighPrice > _highestPrice)
_highestPrice = candle.HighPrice;
if (candle.LowPrice < _lowestPrice)
_lowestPrice = candle.LowPrice;
var threshold = _prevRange * VolatilityMultiplier;
if (threshold <= 0)
return;
// Breakout above lowest + threshold => buy
if (candle.ClosePrice > _lowestPrice + threshold && Position <= 0)
{
BuyMarket();
// Reset extremes after entry
_highestPrice = candle.HighPrice;
_lowestPrice = candle.LowPrice;
}
// Breakout below highest - threshold => sell
else if (candle.ClosePrice < _highestPrice - threshold && Position >= 0)
{
SellMarket();
// Reset extremes after entry
_highestPrice = candle.HighPrice;
_lowestPrice = candle.LowPrice;
}
_prevRange = candle.HighPrice - candle.LowPrice;
}
}
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
class spasm_strategy(Strategy):
def __init__(self):
super(spasm_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for analysis", "General")
self._volatility_multiplier = self.Param("VolatilityMultiplier", 2.0) \
.SetDisplay("Volatility Multiplier", "Multiplier applied to ATR for breakout bands", "Trading")
self._highest_price = 0.0
self._lowest_price = float('inf')
self._prev_range = 0.0
self._initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def VolatilityMultiplier(self):
return self._volatility_multiplier.Value
def OnReseted(self):
super(spasm_strategy, self).OnReseted()
self._highest_price = 0.0
self._lowest_price = float('inf')
self._prev_range = 0.0
self._initialized = False
def OnStarted2(self, time):
super(spasm_strategy, self).OnStarted2(time)
self._highest_price = 0.0
self._lowest_price = float('inf')
self._prev_range = 0.0
self._initialized = False
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if not self._initialized:
self._highest_price = high
self._lowest_price = low
self._prev_range = high - low
self._initialized = True
return
if high > self._highest_price:
self._highest_price = high
if low < self._lowest_price:
self._lowest_price = low
threshold = self._prev_range * float(self.VolatilityMultiplier)
if threshold <= 0:
self._prev_range = high - low
return
if close > self._lowest_price + threshold and self.Position <= 0:
self.BuyMarket()
self._highest_price = high
self._lowest_price = low
elif close < self._highest_price - threshold and self.Position >= 0:
self.SellMarket()
self._highest_price = high
self._lowest_price = low
self._prev_range = high - low
def CreateClone(self):
return spasm_strategy()