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:
- 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.
- 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.
- 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.
La estrategia actualiza el comentario con tres líneas:
Base lots, Risk lots, Open lots, Lots to adjust: vista rápida de las métricas de tamaño de posición.
Risk, Floating PnL: configuración de riesgo, beneficio flotante en unidades monetarias y beneficio flotante como porcentaje del saldo.
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);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, UnitTypes, Unit
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class risk_monitor_strategy(Strategy):
def __init__(self):
super(risk_monitor_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 10).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._tp_points = self.Param("TakeProfitPoints", 500.0).SetNotNegative().SetDisplay("Take Profit", "TP distance", "Risk")
self._sl_points = self.Param("StopLossPoints", 500.0).SetNotNegative().SetDisplay("Stop Loss", "SL distance", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(risk_monitor_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
self._entry_price = 0
def OnStarted2(self, time):
super(risk_monitor_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
self._entry_price = 0
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self._fast_period.Value
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self._slow_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast_ema, slow_ema, self.OnProcess).Start()
tp_val = float(self._tp_points.Value)
sl_val = float(self._sl_points.Value)
tp = Unit(tp_val, UnitTypes.Absolute) if tp_val > 0 else None
sl = Unit(sl_val, UnitTypes.Absolute) if sl_val > 0 else None
self.StartProtection(tp, sl)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
pf = self._prev_fast
ps = self._prev_slow
self._prev_fast = fast_val
self._prev_slow = slow_val
if pf is None or ps is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
prev_diff = pf - ps
curr_diff = fast_val - slow_val
if prev_diff <= 0 and curr_diff > 0:
if self.Position < 0:
self.BuyMarket(abs(self.Position))
if self.Position == 0:
self.BuyMarket(self.Volume)
elif prev_diff >= 0 and curr_diff < 0:
if self.Position > 0:
self.SellMarket(self.Position)
if self.Position == 0:
self.SellMarket(self.Volume)
def CreateClone(self):
return risk_monitor_strategy()