La Estrategia de Risk Reward Ratio es un port de alto nivel en StockSharp del experto de MetaTrader "Risk Reward Ratio". La estrategia combina varios filtros de confirmación de impulso y tendencia con un módulo disciplinado de gestión del riesgo. Las entradas se generan a partir de la confluencia de osciladores estocásticos, un cruce de media móvil ponderada lineal (LWMA), un filtro RSI de 14 períodos y una verificación de tendencia con MACD. El control del riesgo se logra mediante un stop-loss basado en pips, un take-profit con ratio de recompensa automático, stops de seguimiento y lógica de break-even opcionales, y un interruptor de salida de emergencia que liquida inmediatamente la posición.
La conversión mantiene el espíritu original del experto de MetaTrader usando las suscripciones a velas e indicadores de StockSharp. Todo el procesamiento de indicadores ocurre en velas completadas y evita el acceso directo a los búferes de indicadores, preservando el paradigma de streaming del motor.
Lógica de trading
Confluencia estocástica
Un estocástico rápido (5, 2, 2) proporciona la señal principal de impulso usando la línea %K.
Un estocástico lento (21, 10, 4) suministra el sesgo direccional a través de su línea %D suavizada.
Los setups largos requieren que el %K rápido esté por encima del %D lento, mientras que los cortos requieren lo contrario.
Confirmación RSI
Un RSI de 14 períodos debe estar por encima de 50 para operaciones largas y por debajo de 50 para cortas, asegurando que el mercado esté alineado con la dirección propuesta.
Filtro de tendencia con LWMAs
Dos medias móviles ponderadas linealmente (longitudes 6 y 85) deben confirmar la dirección: la LWMA rápida sobre la lenta para largos y por debajo para cortos.
Calificador de tendencia MACD
El histograma MACD (12, 26, 9) debe estar de acuerdo con la dirección de la señal. La línea principal debe liderar a la línea de señal permaneciendo en el lado apropiado del cero.
Filtro de desviación de impulso
Un indicador de Momentum de 14 períodos mide la distancia desde 100. Al menos una de las últimas tres lecturas de momentum debe superar el umbral configurable para demostrar que el precio está acelerando lo suficiente para justificar una operación.
Límites de posición
La exposición neta está limitada por MaxPositions * TradeVolume, de modo que la estrategia no pueda piramidizar más allá de la restricción original del EA.
Las órdenes se envían como ejecuciones de mercado usando BuyMarket y SellMarket. La estrategia ignora las velas no finalizadas y mantiene todo el estado dentro de los campos de clase para respetar la arquitectura orientada a eventos de StockSharp.
Gestión del riesgo
Stop-loss en pips – Cada entrada instala un stop de protección a StopLossPips * PriceStep desde el precio de llenado.
Take-profit con ratio de recompensa – La distancia del take-profit es igual a la distancia del stop multiplicada por RewardRatio para mantener una relación fija de recompensa-riesgo.
Stop de seguimiento – Cuando está habilitado, el stop se mueve detrás del precio por TrailingStopPips una vez que el mercado avanza al menos esa distancia desde la entrada.
Ajuste de break-even – Después de BreakEvenTriggerPips de movimiento favorable, el stop se empuja a la entrada más un colchón adicional de BreakEvenOffsetPips (o menos para cortos), asegurando ganancias.
Interruptor de salida – Establecer ExitSwitch en true aplana la posición actual en la siguiente barra completada y deshabilita el procesamiento hasta que el indicador se desactive.
Parámetros
Nombre
Predeterminado
Descripción
TradeVolume
0.1
Volumen de cada orden de mercado.
CandleType
Marco temporal de 15m
Serie de velas principal.
FastMaPeriod
6
Período de la LWMA rápida.
SlowMaPeriod
85
Período de la LWMA lenta.
MomentumThreshold
0.3
Distancia absoluta mínima del indicador de Momentum desde 100 necesaria para permitir entradas.
RewardRatio
2
Múltiplo de take-profit relativo al stop-loss.
StopLossPips
20
Distancia del stop-loss en pips (múltiplos de PriceStep).
MaxPositions
10
Número máximo de unidades de volumen (TradeVolume) permitidas simultáneamente.
EnableTrailing
true
Habilita actualizaciones de stop de seguimiento basadas en pips.
TrailingStopPips
40
Distancia del stop de seguimiento en pips.
EnableBreakEven
true
Activa la gestión de stop de break-even.
BreakEvenTriggerPips
30
Ganancia (en pips) requerida antes de mover el stop al break-even.
BreakEvenOffsetPips
30
Desplazamiento adicional en pips cuando el stop se traslada al break-even.
ExitSwitch
false
Fuerza a la estrategia a liquidar toda la exposición en la siguiente vela completada.
Flujo de trabajo
Configure el instrumento y la serie de velas deseados, luego establezca los parámetros de riesgo.
Inicie la estrategia. Se suscribe a las velas, vincula indicadores y comienza a procesar barras completadas.
Cuando se alinean las condiciones de entrada, el motor envía una orden de mercado y almacena los niveles de stop/objetivo.
En cada vela completada, el bloque de riesgo evalúa las reglas de seguimiento, break-even y salida de emergencia.
Las salidas se activan al alcanzar niveles de stop/take-profit, actualizaciones de seguimiento, ajustes de break-even o el interruptor de emergencia.
Notas
La conversión aprovecha el binding de indicadores de StockSharp en lugar del acceso manual a búferes, asegurando que cada indicador reciba datos sincronizados.
Todos los cálculos se basan en el PriceStep del instrumento. Si el paso es cero o falta, las distancias de riesgo permanecen deshabilitadas para evitar niveles de precio no válidos.
La estrategia no modifica órdenes pendientes; simplemente envía órdenes de mercado para abrir/cerrar posiciones, replicando la forma en que el EA original aplanaba la exposición cuando se alcanzaban los umbrales.
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 RiskRewardRatioStrategy : 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 RiskRewardRatioStrategy()
{
_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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class risk_reward_ratio_strategy(Strategy):
def __init__(self):
super(risk_reward_ratio_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(risk_reward_ratio_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(risk_reward_ratio_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return risk_reward_ratio_strategy()