Ver en GitHub

Estrategia ProfitLossTrailStrategy

Descripción general

ProfitLossTrailStrategy es un ayudante de gestión de riesgos convertido desde el asesor experto de MetaTrader ProfitLossTrailEA v2.30. La estrategia no genera entradas por sí misma. En su lugar, supervisa la posición actualmente abierta en el valor configurado y aplica automáticamente salidas de protección:

  • niveles iniciales de stop-loss y take-profit;
  • gestión de trailing stop con distancia de activación opcional y control de paso trailing;
  • protección break-even con disparador de ganancia y desplazamiento configurables;
  • capacidad de eliminar niveles de protección existentes cuando el trader quiere gestionarlos manualmente.

El comportamiento coincide estrechamente con el modo de gestión de "cesta" del EA original: todas las órdenes de la misma dirección se tratan como una sola posición y los niveles de protección se recalculan cuando cambia la exposición.

Referencia de parámetros

Parámetro Descripción
Manage As Basket Cuando está habilitado (predeterminado), cada ejecución en la misma dirección recalcula el precio medio de entrada y refresca niveles de stop-loss/take-profit. Desactive el flag para conservar los niveles iniciales después de la primera ejecución.
Enable Take Profit Activa o desactiva la gestión automática de take-profit.
Take Profit (pips) Distancia en pips entre el precio de entrada y el objetivo de take-profit.
Enable Stop Loss Activa o desactiva la gestión automática de stop-loss.
Stop Loss (pips) Distancia en pips entre el precio de entrada y el stop de protección inicial.
Enable Trailing Stop Activa la gestión dinámica del stop cuando la posición está en ganancia.
Trailing Activation (pips) Ganancia mínima en pips requerida antes de que el trailing stop pueda moverse. Use 0 para activar inmediatamente.
Trailing Stop (pips) Distancia trailing base expresada en pips.
Trailing Step (pips) Ganancia adicional que debe obtenerse antes de ajustar más el trailing stop.
Enable Break-Even Habilita la rutina break-even que mueve el stop a ganancia después de una distancia disparadora.
Break-Even Trigger (pips) Distancia de ganancia que activa el movimiento a break-even.
Break-Even Offset (pips) Desplazamiento extra añadido por encima (largo) o por debajo (corto) del precio de entrada cuando break-even se activa.
Remove Take Profit Cuando se establece en true, se limpia cualquier valor actual de take-profit y no se emiten salidas take-profit.
Remove Stop Loss Cuando se establece en true, se limpia cualquier valor actual de stop-loss y no se emiten salidas stop-loss ni trailing.
Candle Type Serie de velas usada para monitorizar la acción del precio. Las comprobaciones de trailing, break-even y salida se evalúan en velas finalizadas.

Notas de uso

  1. Adjunte la estrategia a un valor y asegúrese de que las órdenes se coloquen externamente o por otra estrategia. ProfitLossTrailStrategy se centra exclusivamente en gestionar la exposición abierta.
  2. Configure los parámetros basados en pips para que coincidan con la cotización del instrumento. El tamaño de pip se deriva automáticamente de Security.PriceStep.
  3. Cuando tanto break-even como trailing stop están habilitados, el ajuste de break-even ocurre primero. Los pasos trailing posteriores solo ajustarán el stop si el nuevo nivel mejora el precio de protección actual al menos por la distancia de paso trailing especificada.
  4. Establecer Remove Stop Loss desactiva simultáneamente stop-loss, trailing y lógica break-even, reflejando el comportamiento del EA original.
  5. La estrategia usa órdenes de mercado (BuyMarket/SellMarket) para cerrar posiciones cuando se alcanzan niveles de protección.

Notas de conversión

  • Los modos "Order_By_Order" y "Same_Type_As_One" de MetaTrader se representan mediante el flag Manage As Basket. Gestionar niveles de stop por ticket no está soportado en StockSharp, por lo que el modo cesta se aplica por defecto.
  • Los filtros de magic number y comentario del EA original no son necesarios; la estrategia actúa solo sobre Strategy.Security configurado.
  • El dibujo en pantalla, las alertas sonoras y las actualizaciones de UI basadas en temporizador se omitieron porque StockSharp ya expone diagnósticos mediante logs y vinculaciones de gráfico.
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;

public class ProfitLossTrailStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public ProfitLossTrailStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}