Ver en GitHub

Estrategia TakeProfitTimeGuardStrategy

Descripción general

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

  1. 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.
  2. 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.
  3. 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.
  4. 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ó.
  5. 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);
		}
	}
}