Diese Strategie reproduziert den MetaTrader "21hour" Expert Advisor innerhalb von StockSharp. Sie operiert während zwei konfigurierbarer Handelsfenster und verwendet ausstehende Stop-Orders, um Ausbrüche an der Ober- und Unterseite der Range zu erfassen. Am Ende jedes Fensters liquidiert die Strategie jede offene Exposition und entfernt die aktiven Orders, um sicherzustellen, dass jeder Handelstag sauber beginnt.
Kernidee
Die Handelsrichtung wird ausschließlich durch die Kursbewegung rund um die angegebenen Sitzungsstartzeiten bestimmt.
Zu Beginn jeder Sitzung umklammert die Strategie den Markt mit einem Buy-Stop über dem aktuellen Ask und einem Sell-Stop unter dem aktuellen Bid.
Wenn eine Stop-Order ausgeführt wird, wird die entgegengesetzte Seite sofort storniert und eine Take-Profit-Order mit festem Abstand platziert.
Zur konfigurierten Sitzungsendzeit wird jede Position geschlossen und alle Orders werden storniert, auch wenn der Take-Profit noch nicht erreicht wurde.
Datenfluss
Kerzen: 1-Minuten-Kerzen (konfigurierbar) werden nur zur Bereitstellung von Zeitstempeln und zur Auslösung der stündlichen Terminprüfungen verwendet.
Orderbuch: Level-1-Quotes liefern die aktuellen besten Bid/Ask-Werte, die die Aktivierungspreise der Stop-Orders definieren.
Handelsregeln
Einsstiegsplanung
Zu FirstSessionStartHour (Standard 08:00 Serverzeit) und zu SecondSessionStartHour (Standard 22:00) führt die Strategie aus:
Platziert einen Buy-Stop bei Ask + StepPoints * PriceStep.
Platziert einen Sell-Stop bei Bid - StepPoints * PriceStep.
Es ist nur eine Position erlaubt. Wenn beim Start der anderen Sitzung bereits eine Position offen ist, werden alle ausstehenden Einstiegsorders vor der Platzierung neuer entfernt.
Order-Management
Wenn eine der Stop-Orders ausgeführt wird, wird der entgegengesetzte Stop sofort storniert.
Eine Take-Profit-Limit-Order wird bei EntryPrice ± TakeProfitPoints * PriceStep je nach Handelsrichtung registriert.
Order-Größen sind durch den Volume-Parameter festgelegt (Standard 1 Lot).
Zu FirstSessionStopHour (Standard 21:00) und SecondSessionStopHour (Standard 23:00) schließt die Strategie alle offenen Positionen zum Marktpreis und storniert alle verbleibenden ausstehenden Orders.
Wenn die Position manuell geschlossen wird, entfernt die Strategie auch die ausstehende Take-Profit-Order.
Parameter
Parameter
Standard
Beschreibung
Volume
1
Order-Volumen für Stop-Einstiege und Take-Profit-Ausstiege.
FirstSessionStartHour
8
Stunde (0-23), wenn die erste Handelssitzung beginnt.
FirstSessionStopHour
21
Stunde, wenn die erste Sitzung endet und Positionen geschlossen werden.
SecondSessionStartHour
22
Stunde, wenn die Abendsitzung beginnt. Muss nach der ersten Sitzung liegen.
SecondSessionStopHour
23
Stunde, wenn die zweite Sitzung endet. Muss nach dem Stop der ersten Sitzung liegen.
StepPoints
5
Abstand vom besten Quote zur Stop-Order, gemessen in Preisschritten.
TakeProfitPoints
40
Abstand zwischen Einstiegspreis und Take-Profit-Limit, gemessen in Preisschritten.
CandleType
1 Minute
Kerzentyp für die Intraday-Terminprüfungen.
Alle Parameter werden validiert, um überlappende Sitzungen oder unmögliche Stundenkombinationen zu vermeiden.
Zeitrahmen: Intraday, zeitplangesteuert (1-Minuten-Kerzen nur für Timing).
Risikokontrollen: Fester Take-Profit plus erzwungenes Flat am Sitzungsende (kein Stop-Loss).
Markttypen: Für FX, Indizes oder jedes Instrument mit kontinuierlichen Handelszeiten und zuverlässigen Quotes konzipiert.
Komplexität: Niedrig – keine Indikatoren, rein zeit- und preisbasiert.
Implementierungshinweise
Die Strategie erfordert einen gültigen Security.PriceStep; Orders werden übersprungen, wenn Preisschritt oder Quotes nicht verfügbar sind.
Take-Profit-Volumina verwenden das ausgeführte Handelsvolumen, wenn verfügbar, mit Fallback auf die aktuelle Position oder das konfigurierte Volumen.
Der Code enthält englische Inline-Kommentare zur Klarheit und spiegelt die ursprüngliche MQL-Logik wider, während StockSharp High-Level-APIs genutzt werden (SubscribeCandles, SubscribeOrderBook, Hilfsparameter und Order-Helpers).
using System;
using System.Linq;
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;
/// <summary>
/// 21-hour session breakout strategy. Places simulated stop entries via candle breakout logic.
/// </summary>
public class TwentyOneHourSessionBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _firstSessionStartHour;
private readonly StrategyParam<int> _firstSessionStopHour;
private readonly StrategyParam<decimal> _stepPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<DataType> _candleType;
private decimal? _sessionOpen;
private decimal _entryPrice;
private bool _inSession;
public int FirstSessionStartHour
{
get => _firstSessionStartHour.Value;
set => _firstSessionStartHour.Value = value;
}
public int FirstSessionStopHour
{
get => _firstSessionStopHour.Value;
set => _firstSessionStopHour.Value = value;
}
public decimal StepPoints
{
get => _stepPoints.Value;
set => _stepPoints.Value = value;
}
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public TwentyOneHourSessionBreakoutStrategy()
{
_firstSessionStartHour = Param(nameof(FirstSessionStartHour), 2)
.SetDisplay("Session Start", "Hour of the trading window start", "Schedule");
_firstSessionStopHour = Param(nameof(FirstSessionStopHour), 20)
.SetDisplay("Session Stop", "Hour of the trading window stop", "Schedule");
_stepPoints = Param(nameof(StepPoints), 40m)
.SetGreaterThanZero()
.SetDisplay("Step Points", "Distance from session open to breakout level", "Orders");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 200m)
.SetGreaterThanZero()
.SetDisplay("Take Profit Points", "Take-profit distance", "Orders");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candles used to drive the trading schedule", "Data");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sessionOpen = null;
_entryPrice = 0m;
_inSession = false;
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var hour = candle.OpenTime.Hour;
var priceStep = Security?.PriceStep ?? 1m;
// Session start: record the open price
if (hour >= FirstSessionStartHour && hour < FirstSessionStopHour)
{
if (!_inSession)
{
_sessionOpen = candle.OpenPrice;
_inSession = true;
}
if (_sessionOpen == null)
return;
var stepOffset = StepPoints * priceStep;
var buyLevel = _sessionOpen.Value + stepOffset;
var sellLevel = _sessionOpen.Value - stepOffset;
// Breakout entry
if (Position == 0)
{
if (candle.HighPrice >= buyLevel)
{
BuyMarket();
_entryPrice = buyLevel;
}
else if (candle.LowPrice <= sellLevel)
{
SellMarket();
_entryPrice = sellLevel;
}
}
// Take profit
if (Position > 0)
{
var tp = _entryPrice + TakeProfitPoints * priceStep;
if (candle.HighPrice >= tp)
{
SellMarket();
_sessionOpen = candle.ClosePrice;
}
}
else if (Position < 0)
{
var tp = _entryPrice - TakeProfitPoints * priceStep;
if (candle.LowPrice <= tp)
{
BuyMarket();
_sessionOpen = candle.ClosePrice;
}
}
}
else
{
// Session end: close position
if (Position > 0)
SellMarket();
else if (Position < 0)
BuyMarket();
_inSession = false;
_sessionOpen = null;
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class twenty_one_hour_session_breakout_strategy(Strategy):
def __init__(self):
super(twenty_one_hour_session_breakout_strategy, self).__init__()
self._first_session_start_hour = self.Param("FirstSessionStartHour", 2)
self._first_session_stop_hour = self.Param("FirstSessionStopHour", 20)
self._step_points = self.Param("StepPoints", 40.0)
self._take_profit_points = self.Param("TakeProfitPoints", 200.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._session_open = None
self._entry_price = 0.0
self._in_session = False
@property
def FirstSessionStartHour(self):
return self._first_session_start_hour.Value
@FirstSessionStartHour.setter
def FirstSessionStartHour(self, value):
self._first_session_start_hour.Value = value
@property
def FirstSessionStopHour(self):
return self._first_session_stop_hour.Value
@FirstSessionStopHour.setter
def FirstSessionStopHour(self, value):
self._first_session_stop_hour.Value = value
@property
def StepPoints(self):
return self._step_points.Value
@StepPoints.setter
def StepPoints(self, value):
self._step_points.Value = value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@TakeProfitPoints.setter
def TakeProfitPoints(self, value):
self._take_profit_points.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(twenty_one_hour_session_breakout_strategy, self).OnStarted2(time)
self._session_open = None
self._entry_price = 0.0
self._in_session = False
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
hour = candle.OpenTime.Hour
price_step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if price_step <= 0.0:
price_step = 1.0
start_hour = int(self.FirstSessionStartHour)
stop_hour = int(self.FirstSessionStopHour)
if hour >= start_hour and hour < stop_hour:
if not self._in_session:
self._session_open = float(candle.OpenPrice)
self._in_session = True
if self._session_open is None:
return
step_offset = float(self.StepPoints) * price_step
buy_level = self._session_open + step_offset
sell_level = self._session_open - step_offset
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self.Position == 0:
if high >= buy_level:
self.BuyMarket()
self._entry_price = buy_level
elif low <= sell_level:
self.SellMarket()
self._entry_price = sell_level
if self.Position > 0:
tp = self._entry_price + float(self.TakeProfitPoints) * price_step
if high >= tp:
self.SellMarket()
self._session_open = close
elif self.Position < 0:
tp = self._entry_price - float(self.TakeProfitPoints) * price_step
if low <= tp:
self.BuyMarket()
self._session_open = close
else:
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._in_session = False
self._session_open = None
def OnReseted(self):
super(twenty_one_hour_session_breakout_strategy, self).OnReseted()
self._session_open = None
self._entry_price = 0.0
self._in_session = False
def CreateClone(self):
return twenty_one_hour_session_breakout_strategy()