TakeProfitTimeGuardStrategy emula o comportamento do especialista MetaTrader Exp_GTakeProfit_Tm supervisionando o lucro no nível da conta e forçando um estado de posição plana fora de um horário de negociação configurável. A estratégia não abre posições por conta própria. Em vez disso, serve como uma camada de gerenciamento de risco sobreposta que fecha automaticamente qualquer exposição existente assim que o objetivo de lucro é atingido ou quando a negociação deve ser interrompida fora do intervalo de tempo permitido.
Lógica principal
Assina um fluxo de velas configurável (padrão 1 minuto) para avaliar o PnL realizado e não realizado usando o preço de fechamento mais recente.
Calcula o lucro total como a soma do PnL realizado (Strategy.PnL) e o PnL flutuante derivado do preço médio de posição atual.
Ignora perdas enquanto a janela de negociação está aberta, espelhando o comportamento original do consultor especializado.
Uma vez que o alvo de take-profit é atingido, define um sinalizador de stop interno e liquida repetidamente qualquer posição restante até que a conta esteja plana. O sinalizador de stop é redefinido após o portfólio retornar à posição zero.
Quando a janela de negociação opcional está habilitada, a estratégia fecha todas as posições sempre que o tempo atual cai fora do intervalo permitido, também aguardando até que o livro esteja plano antes de reabilitar a negociação.
Parâmetros
Parâmetro
Tipo
Padrão
Descrição
CandleType
DataType
período de 1 minuto
Série de velas usada para avaliar a lógica de lucro e horário.
TargetMode
ProfitTargetModes (Percent/Currency)
Percent
Seleciona se TakeProfitValue é interpretado como porcentagem do capital da conta ou como valor absoluto em moeda.
TakeProfitValue
decimal
100
Limite do alvo de lucro. Interpretado de acordo com TargetMode. Deve ser maior que zero.
UseTradingWindow
bool
true
Habilita ou desabilita o filtro de tempo.
StartTime
TimeSpan
00:00:00
Início da janela de negociação permitida (inclusive).
EndTime
TimeSpan
23:59:00
Fim da janela de negociação permitida. Quando o horário de início é maior que o horário de fim, a janela abrange a meia-noite.
Notas de comportamento
O valor inicial do portfólio é capturado quando a estratégia inicia (ou na primeira atualização se o valor era zero) e é usado como referência para o alvo percentual.
A estratégia calcula o PnL flutuante usando o preço de fechamento do eixo de vela mais recente; os resultados dependem da granularidade de vela selecionada.
Se o alvo de lucro for atingido, a estratégia continua enviando ordens de mercado para achatar a posição até que o livro esteja vazio. Registra o motivo do fechamento do livro.
Quando UseTradingWindow está habilitado e o relógio está fora da janela, a mesma rotina de achatamento é executada mesmo que o alvo de lucro não tenha sido atingido.
O sinalizador de stop (_stop) só é limpo após a posição retornar a zero, permitindo que a negociação seja retomada quando as condições permitirem.
Diferenças em relação à estratégia MQL original
Usa a API de alto nível do StockSharp (SubscribeCandles) em vez de manipuladores por tick.
Calcula o lucro flutuante a partir do preço médio de posição exposto por Strategy.PositionPrice.
Registra eventos de take-profit para monitoramento mais fácil.
A comparação de tempo é baseada em DateTimeOffset.CloseTime das velas assinadas.
Dicas de uso
Anexe a estratégia a um portfólio que já executa outra estratégia de negociação para atuar como camada de guarda.
Escolha um período de vela que corresponda à capacidade de resposta necessária para avaliação de lucros (por exemplo, 1 minuto para controle rápido).
Certifique-se de que as informações do portfólio (especialmente CurrentValue) estejam disponíveis; caso contrário, defina um saldo inicial explícito antes de executar alvos percentuais.
A estratégia pode ser combinada com StartProtection() em outra estratégia primária para adicionar controles de risco adicionais.
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>
/// Take Profit Time Guard strategy (simplified). Uses CCI momentum
/// with session time awareness for entries and profit management.
/// </summary>
public class TakeProfitTimeGuardStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cciLength;
private readonly StrategyParam<decimal> _upperLevel;
private readonly StrategyParam<decimal> _lowerLevel;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int CciLength
{
get => _cciLength.Value;
set => _cciLength.Value = value;
}
public decimal UpperLevel
{
get => _upperLevel.Value;
set => _upperLevel.Value = value;
}
public decimal LowerLevel
{
get => _lowerLevel.Value;
set => _lowerLevel.Value = value;
}
public TakeProfitTimeGuardStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_cciLength = Param(nameof(CciLength), 14)
.SetGreaterThanZero()
.SetDisplay("CCI Length", "CCI period", "Indicators");
_upperLevel = Param(nameof(UpperLevel), 100m)
.SetDisplay("Upper Level", "CCI level for sell signal", "Logic");
_lowerLevel = Param(nameof(LowerLevel), -100m)
.SetDisplay("Lower Level", "CCI level for buy signal", "Logic");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var cci = new CommodityChannelIndex { Length = CciLength };
decimal prevCci = 0;
var hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(cci, (ICandleMessage candle, decimal cciVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevCci = cciVal;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevCci = cciVal;
return;
}
// CCI crosses up from below lower level
if (prevCci < LowerLevel && cciVal >= LowerLevel && Position <= 0)
BuyMarket();
// CCI crosses down from above upper level
else if (prevCci > UpperLevel && cciVal <= UpperLevel && Position >= 0)
SellMarket();
prevCci = cciVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var cciArea = CreateChartArea();
if (cciArea != null)
DrawIndicator(cciArea, cci);
}
}
}
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 CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class take_profit_time_guard_strategy(Strategy):
def __init__(self):
super(take_profit_time_guard_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candles", "General")
self._cci_length = self.Param("CciLength", 14) \
.SetDisplay("CCI Length", "CCI period", "Indicators")
self._upper_level = self.Param("UpperLevel", 100.0) \
.SetDisplay("Upper Level", "CCI level for sell signal", "Logic")
self._lower_level = self.Param("LowerLevel", -100.0) \
.SetDisplay("Lower Level", "CCI level for buy signal", "Logic")
self._prev_cci = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def CciLength(self):
return self._cci_length.Value
@property
def UpperLevel(self):
return self._upper_level.Value
@property
def LowerLevel(self):
return self._lower_level.Value
def OnReseted(self):
super(take_profit_time_guard_strategy, self).OnReseted()
self._prev_cci = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(take_profit_time_guard_strategy, self).OnStarted2(time)
self._prev_cci = 0.0
self._has_prev = False
cci = CommodityChannelIndex()
cci.Length = self.CciLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(cci, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
cv = float(cci_value)
if not self._has_prev:
self._prev_cci = cv
self._has_prev = True
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_cci = cv
return
if self._prev_cci < self.LowerLevel and cv >= self.LowerLevel and self.Position <= 0:
self.BuyMarket()
elif self._prev_cci > self.UpperLevel and cv <= self.UpperLevel and self.Position >= 0:
self.SellMarket()
self._prev_cci = cv
def CreateClone(self):
return take_profit_time_guard_strategy()