Strategie für die tägliche Öffnungszeit des Fensters
Überblick
Die Strategie reproduziert das Verhalten des MetaTrader-Experten „OpenTime“. Es platziert Marktaufträge zu einer konfigurierbaren Tageszeit, schließt optional alle Engagements während eines speziellen Ausstiegsfensters und wendet einfache Money-Management-Regeln wie feste Stop-Loss-, Take-Profit- und Trailing-Schutzfunktionen an. Der Port verwendet das High-Level StockSharp Strategy API, sodass die Strategie mit anderen Komponenten innerhalb des Frameworks kombiniert werden kann.
Wie es funktioniert
Jede fertige Kerze aus dem ausgewählten Zeitrahmen löst eine Tageszeitprüfung aus.
Wenn die aktuelle Zeit in das Handelsfenster fällt, sendet die Strategie Marktaufträge für jede aktivierte Richtung:
Ist nur eine Seite freigegeben, wird die aktuelle Nettoposition verlängert bzw. umgekehrt, bis das gewünschte Volumen erreicht ist.
Wenn beide Seiten aktiviert sind, werden Kauf- und Verkaufsaufträge im selben Fenster erteilt. Da StockSharp das Risiko nebeneinander verrechnet, gleicht das Öffnen der zweiten Richtung automatisch das entgegengesetzte Risiko aus, bevor das neue Risiko entsteht.
Während das Schließfenster aktiv ist, ruft die Strategie einmal ClosePosition() auf, um das ausstehende Risiko zu reduzieren.
Optionale Stop-Loss-, Take-Profit- und Trailing-Stop-Abstände werden an StartProtection delegiert, das die Schutzaufträge mithilfe von Marktausstiegen verwaltet.
Parameter
Fenster schließen aktivieren – spiegelt das Flag TimeClose wider. Wenn diese Option aktiviert ist, definieren Close Position Time und Window Length, wann bestehende Geschäfte geschlossen werden.
Positionszeit schließen – tägliche Zeit, zu der das Ausstiegsfenster beginnt (Standard 20:50).
Handelszeit – tägliche Zeit, zu der neue Trades zulässig sind (Standard 18:50).
Fensterlänge – Dauer sowohl des Handels- als auch des Schlussfensters (Standard 5 Minuten, entsprechend der ursprünglichen Duration-Eingabe).
Kaufeinträge zulassen – entspricht dem Schalter MQL Buy; ermöglicht lange Einträge (Standard: false).
Auftragsvolumen – angestrebtes Nettovolumen für jeden neuen Trade (Standard 0,1 Lots). Die Strategie addiert den absoluten Wert der aktuellen Position, wenn ein entgegengesetztes Signal auftritt, sodass es zu Umkehrungen in einer einzigen Marktorder kommt.
Stop-Loss-Punkte – Abstand in Punkten für den Schutzstopp (Standard 0 deaktiviert den Stopp).
Take-Profit-Punkte – Abstand in Punkten für das Gewinnziel (Standard 0 deaktiviert das Ziel).
Trailing Stop verwenden – aktiviert die Trailing-Stop-Logik des ursprünglichen SimpleTrailing-Helfers.
Trailing Stop Points – Nachlaufdistanz ausgedrückt in Punkten (Standard 300).
Trailing Step Points – zusätzlicher Fortschritt erforderlich, bevor der Trailing Stop vorgezogen wird (Standard 3).
Kerzentyp – Zeitrahmen, der für die Zeitprüfungen verwendet wird (standardmäßige 1-Minuten-Kerzen).
Notizen
Die Punktgröße ergibt sich aus der Wertpapierpreisstufe. Für Anführungszeichen mit drei und fünf Dezimalstellen wird der Schritt mit 10 multipliziert, wodurch die vom Skript MQL verwendete Pip-Verarbeitung reproduziert wird.
StartProtection bringt Schutzstopps nur an, wenn mindestens einer der Abstände größer als Null ist. Wenn das Trailing ohne festen Stop-Loss aktiv ist, wird die Trailing-Distanz als anfänglicher Schutzwert geliefert.
Die Strategie verwaltet absichtlich keine ausstehenden Aufträge oder wiederholten Wiederholungsversuche, da StockSharp bereits eine automatische Fehlerbehandlung für Marktaufträge bietet.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Open Time Daily Window: trades during a specific time window using
/// EMA direction for entry. Closes position at the end of window.
/// </summary>
public class OpenTimeDailyWindowStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _tradeHour;
private readonly StrategyParam<int> _windowMinutes;
private readonly StrategyParam<int> _closeHour;
private decimal _prevEma;
private decimal _entryPrice;
public OpenTimeDailyWindowStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 20)
.SetDisplay("EMA Length", "EMA period for direction.", "Indicators");
_tradeHour = Param(nameof(TradeHour), 10)
.SetDisplay("Trade Hour", "Hour when trading window opens (UTC).", "Schedule");
_windowMinutes = Param(nameof(WindowMinutes), 120)
.SetDisplay("Window Minutes", "Duration of trading window.", "Schedule");
_closeHour = Param(nameof(CloseHour), 20)
.SetDisplay("Close Hour", "Hour to close positions (UTC).", "Schedule");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int TradeHour
{
get => _tradeHour.Value;
set => _tradeHour.Value = value;
}
public int WindowMinutes
{
get => _windowMinutes.Value;
set => _windowMinutes.Value = value;
}
public int CloseHour
{
get => _closeHour.Value;
set => _closeHour.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevEma = 0;
_entryPrice = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal)
{
if (candle.State != CandleStates.Finished)
return;
var hour = candle.OpenTime.Hour;
var minute = candle.OpenTime.Minute;
var totalMinutes = hour * 60 + minute;
var tradeStart = TradeHour * 60;
var tradeEnd = tradeStart + WindowMinutes;
var closeStart = CloseHour * 60;
var close = candle.ClosePrice;
// Close position at close hour
if (Position != 0 && totalMinutes >= closeStart && totalMinutes < closeStart + 30)
{
if (Position > 0)
SellMarket();
else
BuyMarket();
_entryPrice = 0;
}
if (_prevEma == 0)
{
_prevEma = emaVal;
return;
}
// Trade within window
var inWindow = totalMinutes >= tradeStart && totalMinutes < tradeEnd;
if (Position == 0 && inWindow)
{
var emaRising = emaVal > _prevEma;
var emaFalling = emaVal < _prevEma;
if (emaRising && close > emaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (emaFalling && close < emaVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevEma = emaVal;
}
}
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 ExponentialMovingAverage
class open_time_daily_window_strategy(Strategy):
def __init__(self):
super(open_time_daily_window_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "EMA period for direction", "Indicators")
self._trade_hour = self.Param("TradeHour", 10) \
.SetDisplay("Trade Hour", "Hour when trading window opens", "Schedule")
self._window_minutes = self.Param("WindowMinutes", 120) \
.SetDisplay("Window Minutes", "Duration of trading window", "Schedule")
self._close_hour = self.Param("CloseHour", 20) \
.SetDisplay("Close Hour", "Hour to close positions", "Schedule")
self._prev_ema = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def TradeHour(self):
return self._trade_hour.Value
@property
def WindowMinutes(self):
return self._window_minutes.Value
@property
def CloseHour(self):
return self._close_hour.Value
def OnStarted2(self, time):
super(open_time_daily_window_strategy, self).OnStarted2(time)
self._prev_ema = 0.0
self._entry_price = 0.0
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_val):
if candle.State != CandleStates.Finished:
return
ev = float(ema_val)
hour = candle.OpenTime.Hour
minute = candle.OpenTime.Minute
total_minutes = hour * 60 + minute
trade_start = self.TradeHour * 60
trade_end = trade_start + self.WindowMinutes
close_start = self.CloseHour * 60
close = float(candle.ClosePrice)
# Close position at close hour
if self.Position != 0 and total_minutes >= close_start and total_minutes < close_start + 30:
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
self._entry_price = 0.0
if self._prev_ema == 0:
self._prev_ema = ev
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_ema = ev
return
# Trade within window
in_window = total_minutes >= trade_start and total_minutes < trade_end
if self.Position == 0 and in_window:
ema_rising = ev > self._prev_ema
ema_falling = ev < self._prev_ema
if ema_rising and close > ev:
self._entry_price = close
self.BuyMarket()
elif ema_falling and close < ev:
self._entry_price = close
self.SellMarket()
self._prev_ema = ev
def OnReseted(self):
super(open_time_daily_window_strategy, self).OnReseted()
self._prev_ema = 0.0
self._entry_price = 0.0
def CreateClone(self):
return open_time_daily_window_strategy()