Die SurefireThing-Strategie ist eine StockSharp High-Level-Portierung des MetaTrader 4 Expertenberaters Surefirething. Es arbeitet mit abgeschlossenen Kerzen, berechnet die Höhe der ausstehenden Aufträge aus dem Bereich der vorherigen Sitzung und setzt das Engagement am Ende jedes Handelstages zurück. Die Logik basiert auf der Bereitstellung eines symmetrischen Paars von Limit-Orders, die versuchen, eine Mean-Reversion um den vorherigen Schlusskurs herum zu erfassen.
Handelslogik
Am Ende jedes Handelstages versucht die Strategie, die Position zu reduzieren und alle aktiven ausstehenden Aufträge zu stornieren.
Anhand der letzten abgeschlossenen Kerze des Vortages wird die Preisspanne (High - Low) gemessen und mit RangeMultiplier multipliziert (standardmäßig 1,1 wie im Original EA).
Die Hälfte der angepassten Spanne wird zum vorherigen Schlusskurs addiert, um den Verkaufslimit-Einstiegspreis zu erhalten. Der gleiche Abstand wird vom Schlusskurs abgezogen, um die Kauf-Limit-Order zu platzieren.
Stop-Loss- und Take-Profit-Offsets werden in Preisschritten ausgedrückt. Wenn das Instrument einen gültigen Security.Step anzeigt, werden diese in absolute Distanzen umgewandelt und über StartProtection verwaltet, sodass besetzte Positionen automatisch Schutzbefehle erhalten.
Die Auftragserteilung erfolgt einmal pro Handelstag. Wenn Füllungen auftreten, verarbeitet der angehängte Schutz Exits; andernfalls bleiben Bestellungen bis zum nächsten täglichen Reset aktiv.
Parameter
Parameter
Beschreibung
Standard
OrderVolume
Mit jeder ausstehenden Bestellung übermitteltes Volumen.
0.1
TakeProfitPoints
Abstand (in Preisschritten) zum Gewinnziel. Wird in einen absoluten Offset umgewandelt, wenn der Schritt bekannt ist.
10
StopLossPoints
Abstand (in Preisschritten) für den Schutzanschlag. Auf die gleiche Weise umgerechnet wie das Gewinnziel.
15
RangeMultiplier
Faktor, der vor der Berechnung der Einstiegspreise auf den vorherigen Kerzenbereich angewendet wird.
1.1
CandleType
Primärer Zeitrahmen, der von der Strategie verarbeitet wird. Standardmäßig werden 1-Minuten-Kerzen verwendet, können aber an das Originaldiagramm angepasst werden.
TimeSpan.FromMinutes(1)
Implementierungshinweise
High-Level API: Kerzen werden durch SubscribeCandles(CandleType) verbraucht und im ProcessCandle-Handler verarbeitet, sobald sie fertig sind.
Tägliches Zurücksetzen: CloseForNewDay storniert ausstehende Aufträge und schließt Positionen, wenn anhand der Kerzenzeitstempel ein neuer Kalendertag erkannt wird.
Schutzlogik: ConfigureProtection übersetzt die punktbasierten Risikokontrollen in Unit Instanzen und aktiviert StartProtection, sodass Stop-Loss- und Take-Profit-Orders nach Ausführungen automatisch neu erstellt werden.
Auftragslebenszyklus: Verweise auf beide ausstehenden Aufträge werden gespeichert und über CancelPendingOrder sowie OnOrderChanged gelöscht, wenn die Aufträge abgeschlossen oder storniert werden.
Preisnormalisierung: Security.ShrinkPrice wird verwendet, um berechnete Preise auf die Tick-Größe des Instruments zu runden, bevor neue Aufträge übermittelt werden.
Nutzungsempfehlungen
Richten Sie CandleType an dem Zeitrahmen aus, der vom ursprünglichen EA verwendet wurde (normalerweise das Diagramm, an dem es angehängt wurde), um die gleichen Referenzkerzen beizubehalten.
Passen Sie RangeMultiplier an, wenn Instrumente unterschiedliche Volatilitätseigenschaften aufweisen, damit die ausstehenden Aufträge in realistischen Abständen bleiben.
Wenn der Broker Mindesthalteabstände vorschreibt, stellen Sie sicher, dass TakeProfitPoints und StopLossPoints diese Einschränkungen nach der Umrechnung in absolute Preise einhalten.
Die Strategie geht von kontinuierlichen Intraday-Daten aus. Wenn große Lücken auftreten (Wochenenden, Feiertage), löst die nächste verfügbare Kerze immer noch einen Reset und eine neue Auftragserteilung basierend auf dem zuletzt beobachteten Balken aus.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Daily range breakout strategy.
/// Calculates buy/sell levels from previous day's range.
/// Buys when price drops below the lower level, sells when above the upper level.
/// Closes position at end of day.
/// </summary>
public class SurefireThingStrategy : Strategy
{
private readonly StrategyParam<decimal> _rangeMultiplier;
private readonly StrategyParam<DataType> _candleType;
private DateTime? _currentDay;
private decimal _buyLevel;
private decimal _sellLevel;
private bool _levelsReady;
private decimal _prevDayClose;
private decimal _prevDayHigh;
private decimal _prevDayLow;
private decimal _dayHigh;
private decimal _dayLow;
private decimal _dayClose;
private bool _hasPrevDay;
private bool _tradedToday;
public decimal RangeMultiplier
{
get => _rangeMultiplier.Value;
set => _rangeMultiplier.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public SurefireThingStrategy()
{
_rangeMultiplier = Param(nameof(RangeMultiplier), 0.5m)
.SetDisplay("Range Mult", "Multiplier for range-based levels", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle series", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_currentDay = null;
_buyLevel = 0m;
_sellLevel = 0m;
_levelsReady = false;
_prevDayClose = 0m;
_prevDayHigh = 0m;
_prevDayLow = 0m;
_dayHigh = 0m;
_dayLow = 0m;
_dayClose = 0m;
_hasPrevDay = false;
_tradedToday = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_currentDay = null;
_levelsReady = false;
_hasPrevDay = false;
_tradedToday = false;
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 day = candle.OpenTime.Date;
// New day detected
if (_currentDay == null || day > _currentDay.Value)
{
// Close position at end of previous day
if (Position > 0)
SellMarket();
else if (Position < 0)
BuyMarket();
// Save previous day stats
if (_currentDay != null)
{
_prevDayClose = _dayClose;
_prevDayHigh = _dayHigh;
_prevDayLow = _dayLow;
_hasPrevDay = true;
}
// Calculate new levels
if (_hasPrevDay)
{
var range = _prevDayHigh - _prevDayLow;
if (range > 0)
{
var halfRange = range * RangeMultiplier;
_buyLevel = _prevDayClose - halfRange;
_sellLevel = _prevDayClose + halfRange;
_levelsReady = true;
}
}
_currentDay = day;
_dayHigh = candle.HighPrice;
_dayLow = candle.LowPrice;
_dayClose = candle.ClosePrice;
_tradedToday = false;
}
else
{
if (candle.HighPrice > _dayHigh) _dayHigh = candle.HighPrice;
if (candle.LowPrice < _dayLow) _dayLow = candle.LowPrice;
_dayClose = candle.ClosePrice;
}
if (!_levelsReady)
return;
var price = candle.ClosePrice;
// Only one trade per day per direction
if (!_tradedToday && Position == 0)
{
if (price <= _buyLevel)
{
BuyMarket();
_tradedToday = true;
}
else if (price >= _sellLevel)
{
SellMarket();
_tradedToday = true;
}
}
}
}
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 surefire_thing_strategy(Strategy):
"""Daily range breakout strategy. Calculates buy/sell levels from previous day's range.
Buys when price drops below the lower level, sells when above the upper level.
Closes position at end of day."""
def __init__(self):
super(surefire_thing_strategy, self).__init__()
self._range_multiplier = self.Param("RangeMultiplier", 0.5) \
.SetDisplay("Range Mult", "Multiplier for range-based levels", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candle series", "General")
self._current_day = None
self._buy_level = 0.0
self._sell_level = 0.0
self._levels_ready = False
self._prev_day_close = 0.0
self._prev_day_high = 0.0
self._prev_day_low = 0.0
self._day_high = 0.0
self._day_low = 0.0
self._day_close = 0.0
self._has_prev_day = False
self._traded_today = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RangeMultiplier(self):
return self._range_multiplier.Value
def OnReseted(self):
super(surefire_thing_strategy, self).OnReseted()
self._current_day = None
self._buy_level = 0.0
self._sell_level = 0.0
self._levels_ready = False
self._prev_day_close = 0.0
self._prev_day_high = 0.0
self._prev_day_low = 0.0
self._day_high = 0.0
self._day_low = 0.0
self._day_close = 0.0
self._has_prev_day = False
self._traded_today = False
def OnStarted2(self, time):
super(surefire_thing_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
day = candle.OpenTime.Date
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
# New day detected
if self._current_day is None or day > self._current_day:
# Close position at end of previous day
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
# Save previous day stats
if self._current_day is not None:
self._prev_day_close = self._day_close
self._prev_day_high = self._day_high
self._prev_day_low = self._day_low
self._has_prev_day = True
# Calculate new levels
if self._has_prev_day:
day_range = self._prev_day_high - self._prev_day_low
if day_range > 0:
half_range = day_range * float(self.RangeMultiplier)
self._buy_level = self._prev_day_close - half_range
self._sell_level = self._prev_day_close + half_range
self._levels_ready = True
self._current_day = day
self._day_high = high
self._day_low = low
self._day_close = close
self._traded_today = False
else:
if high > self._day_high:
self._day_high = high
if low < self._day_low:
self._day_low = low
self._day_close = close
if not self._levels_ready:
return
# Only one trade per day
if not self._traded_today and self.Position == 0:
if close <= self._buy_level:
self.BuyMarket()
self._traded_today = True
elif close >= self._sell_level:
self.SellMarket()
self._traded_today = True
def CreateClone(self):
return surefire_thing_strategy()