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