TakeProfitTimeGuardStrategy emula el comportamiento del experto de MetaTrader Exp_GTakeProfit_Tm supervisando el beneficio a nivel de cuenta y forzando un estado de posición plana fuera de un horario de trading configurable. La estrategia no abre posiciones por sí sola. En cambio, sirve como capa de gestión de riesgo superpuesta que cierra automáticamente cualquier exposición existente una vez que se alcanza el objetivo de beneficio o cuando el trading debe detenerse fuera del rango de tiempo permitido.
Lógica principal
Se suscribe a un flujo de velas configurable (por defecto 1 minuto) para evaluar el PnL realizado y no realizado usando el precio de cierre más reciente.
Calcula el beneficio total como la suma del PnL realizado (Strategy.PnL) y el PnL flotante derivado del precio promedio de posición actual.
Ignora las pérdidas mientras la ventana de trading está abierta, replicando el comportamiento original del asesor experto.
Una vez que se alcanza el objetivo de take-profit, establece un indicador de stop interno y liquida repetidamente cualquier posición restante hasta que la cuenta esté plana. El indicador de stop se reinicia después de que el portafolio vuelva a posición cero.
Cuando la ventana de trading opcional está habilitada, la estrategia cierra todas las posiciones siempre que el tiempo actual cae fuera del rango permitido, también esperando hasta que el libro esté plano antes de rehabilitar el trading.
Parámetros
Parámetro
Tipo
Predeterminado
Descripción
CandleType
DataType
marco temporal de 1 minuto
Serie de velas usada para evaluar la lógica de beneficio y horario.
TargetMode
ProfitTargetModes (Percent/Currency)
Percent
Selecciona si TakeProfitValue se interpreta como porcentaje del capital de la cuenta o como importe absoluto en moneda.
TakeProfitValue
decimal
100
Umbral del objetivo de beneficio. Se interpreta según TargetMode. Debe ser mayor que cero.
UseTradingWindow
bool
true
Habilita o deshabilita el filtro de tiempo.
StartTime
TimeSpan
00:00:00
Inicio de la ventana de trading permitida (inclusive).
EndTime
TimeSpan
23:59:00
Fin de la ventana de trading permitida. Cuando el tiempo de inicio es mayor que el tiempo de fin, la ventana abarca la medianoche.
Notas de comportamiento
El valor inicial del portafolio se captura cuando la estrategia inicia (o en la primera actualización si el valor era cero) y se usa como referencia para el objetivo porcentual.
La estrategia calcula el PnL flotante usando el precio de cierre del último eje de vela; los resultados dependen de la granularidad de la vela seleccionada.
Si se cumple el objetivo de beneficio, la estrategia sigue enviando órdenes de mercado para aplanar la posición hasta que el libro esté vacío. Registra la razón del cierre del libro.
Cuando UseTradingWindow está habilitado y el reloj está fuera de la ventana, se ejecuta la misma rutina de aplanamiento incluso si el objetivo de beneficio no se alcanzó.
El indicador de stop (_stop) se borra solo después de que la posición vuelva a cero, permitiendo que el trading se reanude cuando las condiciones lo permitan.
Diferencias con la estrategia MQL original
Usa la API de alto nivel de StockSharp (SubscribeCandles) en lugar de manejadores por tick.
Calcula el beneficio flotante desde el precio promedio de posición expuesto por Strategy.PositionPrice.
Registra eventos de take-profit para un monitoreo más fácil.
La comparación de tiempo se basa en DateTimeOffset.CloseTime de las velas suscritas.
Consejos de uso
Adjunte la estrategia a un portafolio que ya ejecuta otra estrategia de trading para actuar como capa de guardia.
Elija un marco temporal de velas que coincida con la capacidad de respuesta requerida para la evaluación de beneficios (por ejemplo, 1 minuto para control rápido).
Asegúrese de que la información del portafolio (especialmente CurrentValue) esté disponible; de lo contrario, establezca un saldo inicial explícito antes de ejecutar objetivos porcentuales.
La estrategia se puede combinar con StartProtection() en otra estrategia primaria para agregar controles de riesgo adicionales.
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()