The Global Stop Timer Strategy is a risk-management overlay converted from the MetaTrader expert Exp_GStop_Tm.
It continuously watches the portfolio value on every finished candle and halts trading once a global profit target
or loss limit is reached. Additionally, it can restrict trading to a user-defined time window and force all open
positions to be flattened whenever the window is closed.
How It Works
When the strategy starts it records the initial portfolio balance as a reference point.
Each time the subscribed candle series closes, the strategy reads the current portfolio value and calculates the
difference from the initial balance.
Depending on the selected StopCalculationMode, the difference is converted to a percentage or left as currency.
If the loss exceeds StopLoss or the profit exceeds TakeProfit, the strategy enters the stopped state, logs the
event, and sends market orders to close any remaining position.
When the optional trading window is enabled and the current time leaves the window, the strategy also attempts to
flatten the position. Once the position size becomes zero the stop flag is reset, allowing trading to resume inside
the next valid window.
The strategy itself never opens new positions. It is designed to supervise other strategies or manual trades and to
protect the account from excessive drawdown or to lock in account-wide profits.
Trading Window Logic
The trading window replicates the original expert logic:
If the start hour is less than the end hour, trading is allowed between the start minute (inclusive) and the end
minute (exclusive) on the same day.
If the start and end hours are equal, trading is permitted only when the current minute is between StartMinute
(inclusive) and EndMinute (exclusive).
If the start hour is greater than the end hour, the session wraps past midnight. Trading is enabled from the start
time until midnight and resumes from midnight until the end time on the following day.
Parameters
StopCalculationMode – choose between percentage-based or currency-based global stops.
StopLoss – global loss threshold. Treated as a percentage when the percent mode is active, otherwise as account
currency.
TakeProfit – global profit target. Uses the same unit as StopLoss.
UseTradingWindow – enable or disable the session filter.
StartHour / StartMinute – starting time of the allowed trading window.
EndHour / EndMinute – closing time of the allowed trading window.
CandleType – candle series that defines how often the account state is evaluated.
Notes
Because stop checks happen on candle close, use a small timeframe (for example, one minute) when rapid reaction is
required.
The strategy closes only the position managed by this strategy instance. Run separate instances if multiple
securities need individual supervision.
Use alongside other trading strategies by attaching it as a parent strategy or running it on the same security to
provide account-wide protection.
using System;
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>
/// Global Stop Timer strategy (simplified). Trades momentum with
/// time-based position management using EMA trend filter.
/// </summary>
public class GlobalStopTimerStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _momentumLength;
private readonly StrategyParam<int> _emaLength;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MomentumLength
{
get => _momentumLength.Value;
set => _momentumLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public GlobalStopTimerStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_momentumLength = Param(nameof(MomentumLength), 10)
.SetGreaterThanZero()
.SetDisplay("Momentum Length", "Momentum period", "Indicators");
_emaLength = Param(nameof(EmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var momentum = new Momentum { Length = MomentumLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
decimal prevMomentum = 100m;
var hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(momentum, ema, (ICandleMessage candle, decimal momVal, decimal emaVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevMomentum = momVal;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevMomentum = momVal;
return;
}
var close = candle.ClosePrice;
var bullishCross = prevMomentum <= 100m && momVal > 100m;
var bearishCross = prevMomentum >= 100m && momVal < 100m;
if (bullishCross && close > emaVal && Position <= 0)
BuyMarket();
else if (bearishCross && close < emaVal && Position >= 0)
SellMarket();
prevMomentum = momVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
}
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.Indicators import Momentum, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class global_stop_timer_strategy(Strategy):
def __init__(self):
super(global_stop_timer_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candles", "General")
self._momentum_length = self.Param("MomentumLength", 10) \
.SetDisplay("Momentum Length", "Momentum period", "Indicators")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "EMA period", "Indicators")
self._prev_momentum = 100.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def MomentumLength(self):
return self._momentum_length.Value
@property
def EmaLength(self):
return self._ema_length.Value
def OnReseted(self):
super(global_stop_timer_strategy, self).OnReseted()
self._prev_momentum = 100.0
self._has_prev = False
def OnStarted2(self, time):
super(global_stop_timer_strategy, self).OnStarted2(time)
self._prev_momentum = 100.0
self._has_prev = False
momentum = Momentum()
momentum.Length = self.MomentumLength
ema = ExponentialMovingAverage()
ema.Length = self.EmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(momentum, ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, mom_value, ema_value):
if candle.State != CandleStates.Finished:
return
mv = float(mom_value)
if not self._has_prev:
self._prev_momentum = mv
self._has_prev = True
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_momentum = mv
return
close = float(candle.ClosePrice)
ev = float(ema_value)
bullish_cross = self._prev_momentum <= 100 and mv > 100
bearish_cross = self._prev_momentum >= 100 and mv < 100
if bullish_cross and close > ev and self.Position <= 0:
self.BuyMarket()
elif bearish_cross and close < ev and self.Position >= 0:
self.SellMarket()
self._prev_momentum = mv
def CreateClone(self):
return global_stop_timer_strategy()