Открыть на GitHub

Стратегия Global Stop Timer

Global Stop Timer — это конвертация советника MetaTrader Exp_GStop_Tm, выполняющая роль защитного модуля. Она отслеживает стоимость портфеля на закрытии каждой свечи и прекращает торговлю, когда достигается глобальная прибыль или допускается максимальная просадка. Дополнительно стратегия способна ограничивать торговлю указанным временем сессии и принудительно закрывать позицию при выходе за его пределы.

Логика работы

  • При запуске стратегия запоминает текущее значение портфеля и использует его как базовую точку.
  • После закрытия каждой подписанной свечи вычисляется разница между текущей стоимостью портфеля и исходным значением.
  • В зависимости от режима StopCalculationMode эта разница переводится в проценты или используется как абсолютная величина в валюте счёта.
  • Если убыток превысил StopLoss, либо прибыль превысила TakeProfit, стратегия помечает себя как остановленную, выводит сообщение и отправляет рыночные заявки для закрытия оставшейся позиции.
  • При включённом торговом окне стратегия также закрывает позицию, если текущее время выходит за пределы окна. После того как позиция становится нулевой, флаг останова сбрасывается и торговля может возобновиться при следующем входе во временное окно.

Стратегия не генерирует торговых сигналов. Её назначение — сопровождать другие стратегии или ручные сделки, защищая портфель от чрезмерных потерь и фиксируя совокупную прибыль.

Логика торгового окна

Временной фильтр повторяет оригинальный алгоритм:

  • Если час начала меньше часа окончания, торговля разрешена от минуты начала (включительно) до минуты окончания (не включительно) в тот же день.
  • Если час начала и час окончания равны, торговля возможна только когда текущая минута находится в диапазоне StartMinute (включительно) — EndMinute (не включительно).
  • Если час начала больше часа окончания, окно переходит через полночь: торговля включена от времени старта до полуночи, а затем продолжается с полуночи до времени окончания следующего дня.

Параметры

  • StopCalculationMode — выбор между процентным и валютным глобальным стопом.
  • StopLoss — максимальный допустимый убыток. В режиме процентов трактуется как значение в процентах, иначе как сумма в валюте счёта.
  • TakeProfit — целевая прибыль, измеряется в тех же единицах, что и StopLoss.
  • UseTradingWindow — включает или выключает временной фильтр.
  • StartHour / StartMinute — время начала разрешённой торговой сессии.
  • EndHour / EndMinute — время завершения торговой сессии.
  • CandleType — тип свечей, определяющий частоту проверки состояния счёта.

Особенности

  • Для быстрого реагирования стоит выбирать небольшой таймфрейм, например одну минуту, чтобы проверка условий выполнялась чаще.
  • Стратегия закрывает только позицию, управляемую этим экземпляром. Для контроля нескольких инструментов запустите отдельные экземпляры.
  • Модуль можно использовать совместно с другими стратегиями, подключая его как родительскую стратегию либо запуская на том же инструменте для обеспечения глобальной защиты.
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);
		}
	}
}