Ver en GitHub

Estrategia de agente cercano

Descripción general

Close Agent Strategy es un asistente de gestión de riesgos que refleja el asesor experto de MQL CloseAgent. La estrategia no abre nuevas operaciones. En cambio, monitorea las posiciones existentes y las cierra cuando el precio se extiende más allá de las bandas Bollinger mientras que el índice de fuerza relativa (RSI) alcanza zonas extremas. La herramienta puede detectar posiciones creadas manualmente o mediante otras estrategias automatizadas y, opcionalmente, liquidar todo una vez que se alcanza un objetivo de ganancias global.

Indicadores y datos

  • Velas: período de tiempo configurable (predeterminado: 5 minutos) utilizado para calcular los indicadores.
  • Bollinger Bandas (largo 21, ancho 2): detecta variaciones de precios por encima de la banda superior o por debajo de la banda inferior.
  • Índice de Fuerza Relativa (longitud 13): confirma si el mercado está sobrecomprado (>70) o sobrevendido (<30).
  • Datos de nivel 1: captura la última oferta y solicita cotizaciones para evaluar las condiciones de salida con la mayor precisión posible.

Parámetros

  • Modo de cierre (CloseMode): selecciona qué posiciones son elegibles para cerrar.
    • Manual – solo posiciones sin este identificador de estrategia (operaciones manuales u otros bots).
    • Auto: solo posiciones abiertas por esta instancia de estrategia.
    • Both: monitorea cada posición en el símbolo de estrategia.
  • Tipo de vela (CandleType): período de tiempo utilizado para calcular Bollinger Bandas y RSI.
  • Modo de operación (OperationMode):
    • LiveBar – utiliza la última vela en formación; reacciona más rápido pero puede utilizar datos sin terminar.
    • NewBar – espera a que se cierre una vela antes de generar una señal (más seguro pero más lento).
  • Cerrar todo el objetivo (CloseAllTarget): si la ganancia flotante (PnL) alcanza este valor absoluto, todas las posiciones monitoreadas se cierran inmediatamente.
  • Habilitar alertas (EnableAlerts): cuando es verdadero, registra un mensaje cada vez que se activa una salida, incluida la estimación de ganancias obtenidas.

Lógica de trading

  1. Se suscribe a cotizaciones de Nivel 1 y a la serie de velas configuradas. Bollinger Bandas y RSI se actualizan para cada vela entrante.
  2. Mantiene un búfer de historial compacto para que la estrategia pueda hacer referencia a la vela cerrada más reciente cuando OperationMode se establece en NewBar.
  3. Comprueba si se alcanza el objetivo de beneficio global. Cuando CloseAllTarget > 0 y PnL supera el umbral, todas las posiciones elegibles se igualan a precios de mercado.
  4. Para cada posición monitoreada en el símbolo de estrategia:
    • Posiciones largas: se cierran cuando la mejor oferta está por encima de la banda superior Bollinger, RSI está por encima de 70 y el precio se mantiene por encima del precio medio de entrada.
    • Posiciones cortas: se cierran cuando la mejor venta está por debajo de la banda Bollinger inferior, RSI está por debajo de 30 y el precio se mantiene por debajo del precio promedio de entrada.
  5. Utiliza cotizaciones de oferta/demanda cuando estén disponibles; de lo contrario, vuelve al cierre de la última vela procesada para evitar salidas perdidas.

Notas de uso

  • La estrategia está diseñada como una capa protectora y supone que las posiciones podrían abrirse externamente.
  • Dado que la lógica actúa únicamente sobre las salidas del mercado, la estrategia debe funcionar junto con otros sistemas comerciales para gestionar la exposición al riesgo.
  • Las alertas aparecen en el registro del Diseñador cuando EnableAlerts está activo, coincidiendo con las alertas originales MQL.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Closes open positions when price stretches beyond Bollinger Bands and RSI reaches extreme values.
/// Adds SMA crossover entries for backtesting.
/// </summary>
public class CloseAgentStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _bollingerLength;
	private readonly StrategyParam<decimal> _rsiOverbought;
	private readonly StrategyParam<decimal> _rsiOversold;

	/// <summary>
	/// Candle type used to calculate indicators.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Length of the RSI indicator.
	/// </summary>
	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	/// <summary>
	/// Length of the Bollinger Bands indicator.
	/// </summary>
	public int BollingerLength
	{
		get => _bollingerLength.Value;
		set => _bollingerLength.Value = value;
	}

	/// <summary>
	/// RSI threshold for overbought.
	/// </summary>
	public decimal RsiOverbought
	{
		get => _rsiOverbought.Value;
		set => _rsiOverbought.Value = value;
	}

	/// <summary>
	/// RSI threshold for oversold.
	/// </summary>
	public decimal RsiOversold
	{
		get => _rsiOversold.Value;
		set => _rsiOversold.Value = value;
	}

	/// <summary>
	/// Initializes parameters.
	/// </summary>
	public CloseAgentStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe used for indicators", "General");

		_rsiLength = Param(nameof(RsiLength), 13)
			.SetGreaterThanZero()
			.SetDisplay("RSI Length", "RSI period", "Indicators");

		_bollingerLength = Param(nameof(BollingerLength), 21)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Length", "Bollinger Bands period", "Indicators");

		_rsiOverbought = Param(nameof(RsiOverbought), 70m)
			.SetRange(0m, 100m)
			.SetDisplay("RSI Overbought", "Overbought threshold", "Signals");

		_rsiOversold = Param(nameof(RsiOversold), 30m)
			.SetRange(0m, 100m)
			.SetDisplay("RSI Oversold", "Oversold threshold", "Signals");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0m;
		_prevSlow = 0m;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var smaFast = new SimpleMovingAverage { Length = 10 };
		var smaSlow = new SimpleMovingAverage { Length = 30 };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(smaFast, smaSlow, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

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

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

		var crossUp = _prevFast <= _prevSlow && fast > slow;
		var crossDown = _prevFast >= _prevSlow && fast < slow;

		if (crossUp && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (crossDown && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
	}
}