Ver en GitHub

Estrategia de Cierre por Beneficio

Esta estrategia monitorea la ganancia y pérdida realizada de todas las operaciones ejecutadas por la estrategia. Cuando el beneficio acumulado supera un umbral definido por el usuario, cierra inmediatamente cualquier posición abierta y opcionalmente cancela las órdenes activas. El mismo comportamiento puede activarse para la caída estableciendo un límite de pérdida.

La estrategia no analiza indicadores ni movimientos de precio. En cambio, actúa como una capa protectora que sale del mercado una vez que se alcanza un objetivo monetario o un nivel de stop. Una suscripción simple a velas se usa únicamente para proporcionar comprobaciones periódicas del valor PnL actual.

Parámetros

  • UseProfitToClose – habilitar o deshabilitar el cierre por objetivo de beneficio. Por defecto: true.
  • ProfitToClose – valor de beneficio en unidades de moneda que activa una salida completa. Por defecto: 20.
  • UseLossToClose – habilitar o deshabilitar el cierre por límite de pérdida. Por defecto: false.
  • LossToClose – valor de pérdida en unidades de moneda que activa una salida completa cuando se supera. Por defecto: 100.
  • ClosePendingOrders – cancelar todas las órdenes activas al cerrar posiciones. Por defecto: true.
  • CandleType – tipo de velas utilizadas para activar comprobaciones periódicas. Por defecto: marco temporal de 1 minuto.

Lógica de Trading

  1. Suscribirse a las velas del marco temporal seleccionado.
  2. En cada vela terminada, calcular el PnL realizado actual.
  3. Si el beneficio es mayor o igual a ProfitToClose, cerrar toda la posición y opcionalmente cancelar órdenes pendientes.
  4. Si el monitoreo de pérdidas está habilitado y el PnL actual es menor o igual a -LossToClose, cerrar toda la posición y opcionalmente cancelar órdenes pendientes.

Notas Adicionales

  • La estrategia cierra solo la posición del valor al que está vinculada.
  • Las órdenes pendientes se cancelan usando el método integrado CancelActiveOrders.
  • La lógica puede combinarse con otras estrategias de entrada para implementar toma de ganancias o protección de cartera.

Filtros

  • Categoría: Gestión de riesgo
  • Dirección: Ambos
  • Indicadores: Ninguno
  • Stops: Sí
  • Complejidad: Básico
  • Marco temporal: Cualquiera
  • Estacionalidad: No
  • Redes neuronales: No
  • Divergencia: No
  • Nivel de riesgo: Medio
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>
/// EMA crossover strategy with profit/loss exit targets.
/// </summary>
public class CloseAtProfitStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public CloseAtProfitStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 26)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };

		SubscribeCandles(CandleType).Bind(fast, slow, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished) return;

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_hasPrev = true;
			return;
		}

		// Fast crosses above slow => buy
		if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
		}
		// Fast crosses below slow => sell
		else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
	}
}