Ver no GitHub

Global Stop Timer Strategy

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);
		}
	}
}