Ver en GitHub

Estrategia de seguimiento de riesgos

Descripción general

Risk Monitor Strategy es una versión del MetaTrader4 asesor experto risk.mq4. El guión original nunca abrió operaciones; en lugar de eso determinó cuántos lotes el comerciante podría implementar de forma segura en función del saldo de la cuenta y un porcentaje de riesgo definido por el usuario. esto La versión StockSharp mantiene el mismo espíritu: realiza diagnósticos continuos de cuentas, calcula tamaños de operaciones sugeridos y monitorea ganancias flotantes y realizadas, y publica los resultados directamente en el comentario de la estrategia para una rápida toma de decisiones.

A diferencia de las estrategias convencionales, Risk Monitor Strategy no envía órdenes automáticamente. Su función es de supervisión: da la comerciante una instantánea de la exposición actual, la capacidad disponible de acuerdo con el presupuesto de riesgo elegido y la rentabilidad de los cerrados posiciones. La línea de comentarios se actualiza cada vez que cambian las posiciones, PnL o las operaciones, de modo que la información siempre refleje las últimas novedades. estado de la cartera.

Cálculos

La estrategia deriva las cifras mostradas en el comentario de tres grupos de datos:

  1. Tamaño de lote base: calculado como AccountBalance / 1000 y alineado con el paso de volumen de seguridad. Esto refleja el original. Lógica MT4 donde cada 1000 unidades de saldo corresponden a 1 lote estándar.
  2. Tamaño del lote de riesgo: multiplica los lotes base por Risk % / 100, alinea el resultado con el paso de volumen y representa cuántos Los lotes podrán abrirse respetando el presupuesto de riesgo configurado.
  3. Lotes abiertos y diferencia: compara la posición neta absoluta con el tamaño del lote de riesgo. Si el comerciante está por debajo del umbral, la diferencia muestra cuántos lotes quedan disponibles antes de alcanzar el límite. Una pequeña diferencia negativa que es menor que el paso de volumen se redondea a cero para evitar ruidos confusos.

Para las ganancias, la estrategia distingue entre valores flotantes y realizados:

  • PnL flotante: leído de la propiedad de la estrategia PnL y expresado tanto en unidades de precio como como porcentaje del actual valor de la cartera.
  • Beneficio realizado – acumulado de operaciones propias. El componente divide cada relleno de cierre en partes positivas y negativas, aplica la comisión reportada y mantiene un total acumulado. La cifra final también se convierte en un porcentaje del capital social para coincida con la lectura MT4.

Parámetros

  • % de riesgo: parte del saldo de la cuenta que se puede comprometer para nuevas posiciones. Predeterminado: 10. El parámetro está expuesto para optimización para que se puedan realizar pruebas retrospectivas de diferentes presupuestos de riesgo rápidamente.

Formato de comentario

La estrategia actualiza el comentario con tres líneas:

  1. Base lots, Risk lots, Open lots, Lots to adjust: vista rápida de las métricas de tamaño de posición.
  2. Risk, Floating PnL: configuración de riesgo, beneficio flotante en unidades monetarias y beneficio flotante como porcentaje del saldo.
  3. Realized profit – beneficio cerrado acumulado y su porcentaje.

Todos los valores se redondean de forma similar al script MT4, respetando el paso del lote de seguridad y utilizando dos decimales para los valores monetarios. números. Debido a que el resultado se encuentra en el comentario, es inmediatamente visible en el gráfico o en la cuadrícula de estrategia sin necesidad de abrirlo. paneles adicionales.

Notas de uso

  • Adjunte la estrategia al instrumento cuyo equilibrio y posición desea supervisar. Funciona con posiciones netas (sin estilo MT4 cobertura) al igual que el propio StockSharp.
  • La estrategia tolera el comercio manual: reacciona a cualquier confirmación comercial para mantener las estadísticas sincronizadas.
  • El comentario se borra automáticamente cuando la estrategia se detiene o se reinicia, lo que evita que los valores obsoletos persistan entre sesiones.
  • No se proporciona ninguna implementación de Python; el paquete API contiene solo la versión C#.
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>
/// Risk-aware EMA crossover strategy inspired by the original MT4 Risk Monitor script.
/// Uses a risk percentage of account balance to size positions, combined with EMA crossover signals.
/// Tracks realized gains/losses and adjusts lot sizing accordingly.
/// </summary>
public class RiskMonitorStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _riskPercent;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<decimal> _stopLossPoints;

	private ExponentialMovingAverage _fastEma;
	private ExponentialMovingAverage _slowEma;

	private decimal? _prevFast;
	private decimal? _prevSlow;

	/// <summary>
	/// Candle type used for signal detection.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Percentage of the account balance allocated to new positions.
	/// </summary>
	public decimal RiskPercent
	{
		get => _riskPercent.Value;
		set => _riskPercent.Value = value;
	}

	/// <summary>
	/// Fast EMA period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Take profit distance in absolute points.
	/// </summary>
	public decimal TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Stop loss distance in absolute points.
	/// </summary>
	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="RiskMonitorStrategy"/>.
	/// </summary>
	public RiskMonitorStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle series used for calculations", "General");

		_riskPercent = Param(nameof(RiskPercent), 10m)
			.SetGreaterThanZero()
			.SetDisplay("Risk %", "Portion of balance used to size positions", "Risk Management")
			.SetOptimize(5m, 30m, 5m);

		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
			.SetOptimize(5, 30, 5);

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
			.SetOptimize(20, 80, 10);

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 500m)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Absolute take profit distance", "Risk");

		_stopLossPoints = Param(nameof(StopLossPoints), 500m)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Absolute stop loss distance", "Risk");

		Volume = 1;
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_fastEma = null;
		_slowEma = null;
		_prevFast = null;
		_prevSlow = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		_fastEma = new ExponentialMovingAverage { Length = FastPeriod };
		_slowEma = new ExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_fastEma, _slowEma, ProcessCandle)
			.Start();

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

		if (TakeProfitPoints > 0 || StopLossPoints > 0)
		{
			var tp = TakeProfitPoints > 0 ? new Unit(TakeProfitPoints, UnitTypes.Absolute) : null;
			var sl = StopLossPoints > 0 ? new Unit(StopLossPoints, UnitTypes.Absolute) : null;
			StartProtection(tp, sl);
		}

		base.OnStarted2(time);
	}

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

		var pf = _prevFast;
		var ps = _prevSlow;
		_prevFast = fastValue;
		_prevSlow = slowValue;

		if (pf == null || ps == null)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		// Detect crossover
		var prevDiff = pf.Value - ps.Value;
		var currDiff = fastValue - slowValue;

		if (prevDiff <= 0 && currDiff > 0)
		{
			// Bullish crossover
			if (Position < 0)
				BuyMarket(Math.Abs(Position));
			if (Position == 0)
				BuyMarket(Volume);
		}
		else if (prevDiff >= 0 && currDiff < 0)
		{
			// Bearish crossover
			if (Position > 0)
				SellMarket(Position);
			if (Position == 0)
				SellMarket(Volume);
		}
	}
}