Estratégia de Stop Loss Global e Janela de Negociação
Visão geral
Esta estratégia replica o comportamento do especialista MetaTrader Exp_GStopLoss_Tm, fornecendo uma camada de risco sobreposta que monitora o resultado combinado de todas as operações abertas pela instância da estratégia. O módulo não gera sinais de entrada por si próprio; em vez disso, rastreia o lucro e perda das posições existentes e aplica tanto um limite de stop loss global quanto uma janela de sessão de negociação opcional. Quando as perdas excedem o limite configurado ou o mercado se move fora do intervalo de tempo permitido, a estratégia liquida a exposição atual e bloqueia qualquer operação posterior até que o livro esteja novamente plano.
Lógica de negociação
Na inicialização, a estratégia registra o PnL realizado atual como referência base. Isso permite medir o lucro flutuante relativo ao estado plano mais recente.
Cada vela concluída produzida pelo tipo de vela configurado aciona uma verificação de risco. O período padrão é um minuto para emular vigilância em nível de tick sem sobrecarregar o sistema.
O módulo calcula o lucro não realizado como a diferença entre o PnL atual da estratégia e o valor base. PnL positivo é ignorado enquanto a estratégia permanece dentro da janela de negociação, espelhando o consultor especialista original.
Se o modo de perda estiver definido como Percent, a estratégia compara o percentual de perda absoluta com o patrimônio da conta obtido de Portfolio.CurrentValue. Para o modo Currency, a comparação é feita em unidades de moeda absolutas.
Uma vez que o limite de perda seja superado, o sinalizador de stop é travado e a estratégia começa a fechar a posição aberta na próxima iteração. O sinalizador só é liberado após o tamanho da posição retornar a zero e o PnL base ser atualizado.
Quando a janela de negociação opcional está habilitada, a verificação de risco também avalia se o tempo de fechamento da vela está dentro do intervalo permitido. A janela suporta sessões intradiárias que se estendem à meia-noite, espelhando a lógica do MetaTrader.
Sempre que o sinalizador de stop estiver ativo ou o filtro de sessão detectar que o mercado está fora do horário permitido, o módulo envia uma ordem de mercado na direção oposta para achatar a posição. Entradas de log informativas descrevem o motivo de cada saída.
Parâmetros
Nome
Descrição
LossMode
Seleciona como o limite de perda é interpretado: porcentagem do patrimônio atual da conta ou moeda absoluta da conta.
StopLoss
Valor do limite de perda. Para o modo porcentagem o número representa o percentual, enquanto o modo moeda usa a moeda da conta.
UseTimeFilter
Habilita a janela de negociação intradiária. Quando desabilitado, a estratégia ignora o filtro de tempo completamente.
StartTime
Início inclusivo da janela de negociação em UTC. Funciona junto com EndTime para definir a sessão válida.
EndTime
Fim exclusivo da janela de negociação em UTC. Suporta sessões wrap-around quando o tempo de fim é anterior ao de início.
CandleType
Assinatura de velas usada para conduzir a avaliação de risco periódica. O padrão é um período de 1 minuto.
Notas de implementação
O PnL base é recalculado sempre que o tamanho da posição retorna a zero, de modo que as operações subsequentes comecem com uma base limpa.
Os valores de patrimônio são obtidos do portfólio ao vivo, portanto o modo percentual se adapta tanto a mudanças realizadas quanto não realizadas no valor da conta.
Todos os comentários no código-fonte estão escritos em inglês, conforme exigido pelas convenções do projeto.
A estratégia desenha velas e operações próprias na área do gráfico padrão quando uma está disponível, ajudando a visualizar o comportamento durante os testes.
Diretrizes de uso
Anexe a estratégia ao instrumento que deseja supervisionar. A geração de ordens de outras estratégias ainda pode ocorrer; este módulo apenas monitora e fecha posições.
Configure o modo de perda e o limite que corresponda ao seu apetite de risco. Por exemplo, LossMode = Percent e StopLoss = 5 fecharão a posição após uma queda não realizada de 5% em relação ao patrimônio atual.
Defina os parâmetros StartTime e EndTime para limitar o trading a uma sessão intradiária específica. Para cobrir uma janela noturna, especifique um horário de início posterior ao horário de fim (por exemplo, 20:00 a 06:00).
Execute o backtest ou a sessão ao vivo. A estratégia reiniciará automaticamente o sinalizador de stop assim que todas as posições estiverem achatadas e continuará supervisionando as operações subsequentes.
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 Loss Time strategy (simplified). Trades EMA crossover with
/// session time filter and global stop loss based on drawdown.
/// </summary>
public class GlobalStopLossTimeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaFastLength;
private readonly StrategyParam<int> _emaSlowLength;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaFastLength
{
get => _emaFastLength.Value;
set => _emaFastLength.Value = value;
}
public int EmaSlowLength
{
get => _emaSlowLength.Value;
set => _emaSlowLength.Value = value;
}
public GlobalStopLossTimeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_emaFastLength = Param(nameof(EmaFastLength), 8)
.SetGreaterThanZero()
.SetDisplay("EMA Fast", "Fast EMA period", "Indicators");
_emaSlowLength = Param(nameof(EmaSlowLength), 21)
.SetGreaterThanZero()
.SetDisplay("EMA Slow", "Slow EMA period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var emaFast = new ExponentialMovingAverage { Length = EmaFastLength };
var emaSlow = new ExponentialMovingAverage { Length = EmaSlowLength };
decimal prevFast = 0, prevSlow = 0;
var hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(emaFast, emaSlow, (ICandleMessage candle, decimal fastVal, decimal slowVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevFast = fastVal;
prevSlow = slowVal;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevFast = fastVal;
prevSlow = slowVal;
return;
}
// EMA crossover signals
if (prevFast <= prevSlow && fastVal > slowVal && Position <= 0)
BuyMarket();
else if (prevFast >= prevSlow && fastVal < slowVal && Position >= 0)
SellMarket();
prevFast = fastVal;
prevSlow = slowVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, emaFast);
DrawIndicator(area, emaSlow);
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class global_stop_loss_time_strategy(Strategy):
def __init__(self):
super(global_stop_loss_time_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candles", "General")
self._ema_fast_length = self.Param("EmaFastLength", 8) \
.SetDisplay("EMA Fast", "Fast EMA period", "Indicators")
self._ema_slow_length = self.Param("EmaSlowLength", 21) \
.SetDisplay("EMA Slow", "Slow EMA period", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaFastLength(self):
return self._ema_fast_length.Value
@property
def EmaSlowLength(self):
return self._ema_slow_length.Value
def OnReseted(self):
super(global_stop_loss_time_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(global_stop_loss_time_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
ema_fast = ExponentialMovingAverage()
ema_fast.Length = self.EmaFastLength
ema_slow = ExponentialMovingAverage()
ema_slow.Length = self.EmaSlowLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema_fast, ema_slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema_fast)
self.DrawIndicator(area, ema_slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if not self._has_prev:
self._prev_fast = fv
self._prev_slow = sv
self._has_prev = True
return
if self._prev_fast <= self._prev_slow and fv > sv and self.Position <= 0:
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and fv < sv and self.Position >= 0:
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def CreateClone(self):
return global_stop_loss_time_strategy()