The strategy reproduces the behaviour of the MetaTrader expert "OpenTime". It places market orders at a configurable time of day, optionally closes all exposure during a dedicated exit window, and applies simple money-management rules such as fixed stop-loss, take-profit, and trailing protection. The port uses the high-level StockSharp Strategy API, so the strategy can be combined with other components inside the framework.
How it works
Every finished candle from the selected timeframe triggers a time-of-day check.
When the current time falls inside the trading window, the strategy sends market orders for every enabled direction:
If only one side is enabled, the current net position is extended or reversed until the requested volume is reached.
When both sides are enabled, buy and sell orders are issued in the same window. Because StockSharp accounts exposure netted by side, opening the second direction automatically offsets the opposite exposure before establishing the new one.
While the closing window is active, the strategy calls ClosePosition() once to flatten any outstanding exposure.
Optional stop-loss, take-profit, and trailing stop distances are delegated to StartProtection, which manages the protective orders using market exits.
Parameters
Enable Close Window – mirrors the TimeClose flag. When enabled, Close Position Time and Window Length define when existing trades are closed.
Close Position Time – daily time at which the exit window begins (default 20:50).
Trading Time – daily time when new trades are allowed (default 18:50).
Window Length – duration of both the trading and closing windows (default 5 minutes, corresponding to the original Duration input).
Allow Sell Entries – corresponds to the MQL Sell switch; enables short entries (default true).
Allow Buy Entries – corresponds to the MQL Buy switch; enables long entries (default false).
Order Volume – target net volume for each new trade (default 0.1 lots). The strategy adds the absolute value of the current position when an opposite signal appears, so reversals occur in a single market order.
Stop-Loss Points – distance in points for the protective stop (default 0 disables the stop).
Take-Profit Points – distance in points for the profit target (default 0 disables the target).
Use Trailing Stop – enables the trailing stop logic from the original SimpleTrailing helper.
Trailing Step Points – additional progress required before advancing the trailing stop (default 3).
Candle Type – timeframe used for the time checks (default 1-minute candles).
Notes
The point size is derived from the security price step. For three- and five-decimal quotes the step is multiplied by 10, reproducing the pip handling used by the MQL script.
StartProtection attaches protective stops only when at least one of the distances is greater than zero. If trailing is active without a fixed stop-loss, the trailing distance is supplied as the initial protective value.
The strategy intentionally does not manage pending orders or repeated retries, because StockSharp already provides automatic error handling for market orders.
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()