Ver no GitHub

Estratégia de Monitoramento de Risco

Visão geral

Risk Monitor Strategy é uma versão do MetaTrader 4 consultor especialista risk.mq4. O script original nunca abriu negociações; em vez disso determinou quantos lotes o trader poderia implantar com segurança com base no saldo da conta e em uma porcentagem de risco definida pelo usuário. Isto A versão StockSharp mantém o mesmo espírito: realiza diagnósticos contínuos da conta, calcula tamanhos de negociação sugeridos, monitora lucros flutuantes e realizados e publica os resultados diretamente no comentário da estratégia para uma tomada de decisão rápida.

Ao contrário das estratégias convencionais, a Risk Monitor Strategy não envia pedidos automaticamente. A sua função é de supervisão: dá ao trader um instantâneo da exposição atual, capacidade disponível de acordo com o orçamento de risco escolhido e a rentabilidade do fechado posições. A linha de comentários é atualizada sempre que as posições, PnL ou negociações mudam, para que as informações sempre reflitam as últimas estado do portfólio.

Cálculos

A estratégia deriva os números exibidos no comentário de três grupos de dados:

  1. Tamanho do lote base – calculado como AccountBalance / 1000 e alinhado à etapa de volume de segurança. Isso reflete o original Lógica MT4 onde cada 1000 unidades de saldo correspondem a 1 lote padrão.
  2. Tamanho do lote de risco – multiplica os lotes base por Risk % / 100, alinha o resultado à etapa de volume e representa quantos os lotes poderão ser abertos respeitando o orçamento de risco configurado.
  3. Lotes em aberto e diferença – compara a posição líquida absoluta com o tamanho do lote de risco. Se o comerciante estiver abaixo do limite, a diferença mostra quantos lotes restam disponíveis antes de atingir o limite. Uma pequena diferença negativa que é menor que o passo do volume é arredondado para zero para evitar ruídos confusos.

Para lucros, a estratégia distingue entre valores flutuantes e realizados:

  • PnL flutuante – lido da propriedade da estratégia PnL e expresso em unidades de preço e como porcentagem do valor atual valor do portfólio.
  • Lucro realizado – acumulado em negociações próprias. O componente divide cada preenchimento de fechamento em partes positivas e negativas, aplica a comissão informada e mantém um total corrente. O valor final também é convertido em um percentual do patrimônio líquido para corresponder à leitura MT4.

Parâmetros

  • % de risco – parcela do saldo da conta que pode ser comprometida em novas posições. Padrão: 10. O parâmetro é exposto para otimização para que diferentes orçamentos de risco possam ser testados rapidamente.

Formato de comentário

A estratégia atualiza o comentário com três linhas:

  1. Base lots, Risk lots, Open lots, Lots to adjust – visualização rápida das métricas de dimensionamento de posição.
  2. Risk, Floating PnL – configuração de risco, lucro flutuante em unidades monetárias e lucro flutuante em porcentagem do saldo.
  3. Realized profit – lucro fechado acumulado e sua porcentagem.

Todos os valores são arredondados de forma semelhante ao script MT4, respeitando a etapa do lote de segurança e utilizando duas casas decimais para valores monetários números. Como a saída fica no comentário, ela fica imediatamente visível no gráfico ou na grade de estratégia sem abrir painéis adicionais.

Notas de uso

  • Anexe a estratégia ao instrumento cujo equilíbrio e posição você deseja supervisionar. Funciona com posições líquidas (não estilo MT4 hedge) assim como o próprio StockSharp.
  • A estratégia tolera a negociação manual: ela reage a qualquer confirmação de negociação para manter as estatísticas sincronizadas.
  • O comentário é limpo automaticamente quando a estratégia é interrompida ou redefinida, evitando que valores obsoletos persistam nas sessões.
  • Nenhuma implementação Python é fornecida; o pacote API contém apenas a versão 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);
		}
	}
}