A Estratégia de Risk Reward Ratio é um port de alto nível do StockSharp do especialista MetaTrader "Risk Reward Ratio". A estratégia combina vários filtros de confirmação de momentum e tendência com um módulo disciplinado de gestão de risco. As entradas são geradas a partir da confluência de osciladores estocásticos, um cruzamento de média móvel ponderada linear (LWMA), um filtro RSI de 14 períodos e uma verificação de tendência MACD. O controle de risco é alcançado através de um stop-loss baseado em pips, um take-profit de ratio de recompensa automático, stops de seguimento e lógica de break-even opcionais, e um interruptor de saída de emergência que liquida imediatamente a posição.
A conversão mantém o espírito original do especialista MetaTrader usando as subscrições de velas e APIs de binding de indicadores do StockSharp. Todo o processamento de indicadores ocorre em velas concluídas e evita o acesso direto aos buffers de indicadores, preservando o paradigma de streaming do motor.
Lógica de trading
Confluência estocástica
Um estocástico rápido (5, 2, 2) fornece o sinal primário de momentum usando a linha %K.
Um estocástico lento (21, 10, 4) fornece o viés direcional através da sua linha %D suavizada.
Setups longos requerem que o %K rápido esteja acima do %D lento, enquanto setups curtos requerem o oposto.
Confirmação RSI
Um RSI de 14 períodos deve estar acima de 50 para negociações longas e abaixo de 50 para curtas, garantindo que o mercado esteja alinhado com a direção proposta.
Filtro de tendência via LWMAs
Duas médias móveis ponderadas linealmente (comprimentos 6 e 85) devem confirmar a direção: LWMA rápida acima da lenta para comprados e abaixo para vendidos.
Qualificador de tendência MACD
O histograma MACD (12, 26, 9) deve estar de acordo com a direção do sinal. A linha principal deve liderar a linha de sinal permanecendo no lado apropriado do zero.
Filtro de desvio de momentum
Um indicador de Momentum de 14 períodos mede a distância a partir de 100. Pelo menos uma das últimas três leituras de momentum deve exceder o limiar configurável para provar que o preço está acelerando o suficiente para justificar uma negociação.
Limites de posição
A exposição líquida é limitada por MaxPositions * TradeVolume, de modo que a estratégia não possa piramidizar além da restrição original do EA.
As ordens são enviadas como execuções de mercado usando BuyMarket e SellMarket. A estratégia ignora velas não concluídas e mantém todo o estado dentro de campos de classe para respeitar a arquitetura orientada a eventos do StockSharp.
Gestão de risco
Stop-loss em pips – Cada entrada instala um stop de proteção a StopLossPips * PriceStep do preço de preenchimento.
Take-profit com ratio de recompensa – A distância do take-profit é igual à distância do stop multiplicada por RewardRatio para manter uma relação fixa de recompensa-risco.
Trailing stop – Quando habilitado, o stop se move atrás do preço por TrailingStopPips assim que o mercado avança pelo menos essa distância da entrada.
Ajuste de break-even – Após BreakEvenTriggerPips de movimento favorável, o stop é movido para a entrada mais uma almofada adicional de BreakEvenOffsetPips (ou menos para vendidos), assegurando ganhos.
Interruptor de saída – Definir ExitSwitch como true achata a posição atual na próxima vela concluída e desabilita o processamento até que o sinalizador seja desativado.
Parâmetros
Nome
Padrão
Descrição
TradeVolume
0.1
Volume de cada ordem de mercado.
CandleType
Período de 15m
Série de velas principal.
FastMaPeriod
6
Período da LWMA rápida.
SlowMaPeriod
85
Período da LWMA lenta.
MomentumThreshold
0.3
Distância absoluta mínima do indicador de Momentum a partir de 100 necessária para permitir entradas.
RewardRatio
2
Múltiplo de take-profit relativo ao stop-loss.
StopLossPips
20
Distância do stop-loss em pips (múltiplos de PriceStep).
MaxPositions
10
Número máximo de unidades de volume (TradeVolume) permitidas simultaneamente.
EnableTrailing
true
Habilita atualizações de trailing stop baseadas em pips.
TrailingStopPips
40
Distância do trailing stop em pips.
EnableBreakEven
true
Ativa o gerenciamento de stop de break-even.
BreakEvenTriggerPips
30
Lucro (em pips) necessário antes de mover o stop para break-even.
BreakEvenOffsetPips
30
Deslocamento adicional em pips quando o stop se move para break-even.
ExitSwitch
false
Força a estratégia a fechar toda a exposição na próxima vela concluída.
Fluxo de trabalho
Configure o instrumento e a série de velas desejados, depois defina os parâmetros de risco.
Inicie a estratégia. Ela se subscreve às velas, vincula indicadores e começa a processar barras concluídas.
Quando as condições de entrada se alinham, o motor envia uma ordem de mercado e armazena os níveis de stop/alvo.
Em cada vela concluída, o bloco de risco avalia as regras de trailing, break-even e saída de emergência.
As saídas são acionadas ao atingir níveis de stop/take-profit, atualizações de trailing, ajustes de break-even ou o interruptor de emergência.
Notas
A conversão aproveita o binding de indicadores do StockSharp em vez do acesso manual a buffers, garantindo que cada indicador receba dados sincronizados.
Todos os cálculos dependem do PriceStep do instrumento. Se o passo for zero ou estiver ausente, as distâncias de risco permanecem desabilitadas para evitar níveis de preço inválidos.
A estratégia não modifica ordens pendentes; simplesmente envia ordens de mercado para abrir/fechar posições, espelhando a forma como o EA original achatava a exposição quando os limiares eram atingidos.
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()