Esta estratégia porta o RSI RFTL EA do MetaTrader 5 para a API de alto nível do StockSharp. Mantém a ideia original de negociar linhas de tendência de swing do RSI, aprimorada com a Recursive Filter Trend Line (RFTL) como filtro direcional. A implementação reproduz a tomada de decisão barra a barra do consultor especialista usando construções idiomáticas do StockSharp como StrategyParam, vinculações de indicadores e assinaturas de velas.
Como Funciona
Detecção de swing do RSI – os últimos 500 valores de RSI são varridos em busca de máximos e mínimos locais. Os picos devem subir acima de 40 e 60, enquanto os vales devem cair abaixo de 60 e 40, correspondendo à lógica de pontos de inflexão do MQL.
Projeção de linha de tendência – uma vez encontrados dois máximos ou mínimos válidos, a estratégia projeta a correspondente linha de tendência do RSI para a barra atual e a anterior. Swings intermediários que quebram os limiares 40/60 invalidam a linha, assim como no consultor especialista.
Confirmação RFTL – o valor anterior da Recursive Filter Trend Line (calculado com a tabela de coeficientes original) deve estar acima do fechamento anterior para shorts ou abaixo para longs. Isso mantém as entradas alinhadas com o filtro RFTL.
Filtragem de entrada – o RSI também deve permanecer no lado apropriado do neutro: shorts requerem RSI acima de 47/50, enquanto longs requerem RSI abaixo de 55/50.
Camada de risco – as distâncias de stop de proteção, take-profit e trailing stop são expressas em pips e atualizadas em cada vela concluída, imitando a rotina de modificação de trailing do MQL. Saídas adicionais ocorrem quando RSI supera 70 (fechar longs) ou cai abaixo de 30 (fechar shorts).
Lógica de Entrada
Configuração vendida
Dois mínimos de RSI abaixo de 60/40 definem uma linha de tendência ascendente cuja projeção agora é quebrada para baixo (RSI[1] < linha, RSI[2] > linha(anterior)).
O valor anterior de RFTL está acima do fechamento anterior, confirmando pressão descendente.
RSI permanece no lado de alta (RSI[2] > 50, RSI[0] > 47) e os topos detectados ficam mais atrás na história do que os fundos (pos₂ > pos₄), correspondendo à restrição de ordenamento do MQL.
Configuração comprada
Dois máximos de RSI acima de 40/60 definem uma linha de tendência descendente cuja projeção agora é quebrada para cima (RSI[1] > linha, RSI[2] < linha(anterior)).
O valor anterior de RFTL está abaixo do fechamento anterior.
RSI permanece no lado de baixa (RSI[2] < 50, RSI[0] < 55) e os fundos recentes são mais recentes do que os topos (pos₄ > pos₂).
Os sinais são avaliados apenas após todos os indicadores estarem formados e o histórico necessário ter sido acumulado, evitando negociações prematuras com dados parciais.
Gerenciamento de Risco
Stop Loss / Take Profit – configuráveis em pips. Se a vela atual negociar além do respectivo nível de preço, a posição é fechada imediatamente e o estado de trailing é reiniciado.
Trailing Stop – opcional. Uma vez que o preço se move por TrailingStopPips + TrailingStepPips a favor da negociação, o stop acompanha o fechamento enquanto aplica o mesmo avanço mínimo (TrailingStepPips) antes de apertar novamente.
Saída de Emergência RSI – longs fecham quando RSI cruza 70; shorts fecham quando cai abaixo de 30. Isso espelha as saídas hard-coded no EA original.
Parâmetros
Parâmetro
Padrão
Descrição
CandleType
1 hora
Período usado para os cálculos de RSI e RFTL.
TradeVolume
1
Volume de ordem enviado em cada entrada.
RsiPeriod
30
Período de lookback do oscilador RSI.
StopLossPips
50
Distância do stop de proteção em pips (0 desativa o stop).
TakeProfitPips
50
Distância do take-profit em pips (0 desativa o alvo).
TrailingStopPips
5
Deslocamento do trailing stop em pips (0 desativa o trailing).
TrailingStepPips
5
Melhoria adicional em pips necessária antes da atualização do trailing.
Todas as distâncias são multiplicadas pelo PriceStep do instrumento, correspondendo ao tratamento de ponto/pip da versão MQL.
Uso
Anexar a estratégia a um ativo e definir CandleType para o tamanho de barra usado nos testes do MetaTrader.
Ajustar os parâmetros de risco (stop, take, trailing) para as distâncias em pips usadas anteriormente. Definir um parâmetro como 0 desativa essa proteção.
Iniciar a estratégia; ela assinará as velas especificadas, calculará RSI e RFTL, e começará a monitorar sinais quando histórico suficiente for coletado.
Monitorar os widgets do gráfico – a área de preço exibe velas e a linha RFTL, enquanto o segundo painel mostra o oscilador RSI.
Notas e Diferenças
O indicador RFTL está implementado diretamente em C# com a tabela de coeficientes original; nenhum arquivo externo é necessário.
O gerenciamento de negociações permanece de posição única: a estratégia alterna entre comprado, vendido e sem posição, assim como o EA que rastreava apenas uma posição por símbolo/mágico.
Como os exits de stop e trailing são gerenciados dentro da estratégia (o StockSharp não executa automaticamente os stops do MT5), as reentradas são puladas na barra onde uma saída de proteção é acionada, o que é uma aproximação conservadora mas segura.
Os buffers de histórico são limitados a 600 registros para refletir os arrays de 500 elementos usados no código-fonte e evitar crescimento ilimitado de memória.
Todos os comentários inline foram reescritos em inglês e o código segue as diretrizes de estilo da API de alto nível do StockSharp.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// RSI-based trend strategy with a simple recursive filter (EMA) as trend confirmation.
/// Buys when RSI crosses above oversold level and EMA confirms uptrend.
/// Sells when RSI crosses below overbought level and EMA confirms downtrend.
/// </summary>
public class RsiRftlStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<decimal> _oversold;
private RelativeStrengthIndex _rsi;
private ExponentialMovingAverage _ema;
private decimal _prevRsi;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// RSI lookback period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// EMA period for trend filter.
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// Overbought RSI level.
/// </summary>
public decimal Overbought
{
get => _overbought.Value;
set => _overbought.Value = value;
}
/// <summary>
/// Oversold RSI level.
/// </summary>
public decimal Oversold
{
get => _oversold.Value;
set => _oversold.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public RsiRftlStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "Length of the RSI oscillator", "Indicator");
_emaPeriod = Param(nameof(EmaPeriod), 44)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "Length of the trend EMA filter", "Indicator");
_overbought = Param(nameof(Overbought), 75m)
.SetDisplay("Overbought", "RSI overbought level", "Levels");
_oversold = Param(nameof(Oversold), 25m)
.SetDisplay("Oversold", "RSI oversold level", "Levels");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_rsi = null;
_ema = null;
_prevRsi = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_rsi, _ema, OnProcess);
subscription.Start();
}
private void OnProcess(ICandleMessage candle, decimal rsiValue, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_rsi.IsFormed || !_ema.IsFormed)
{
_prevRsi = rsiValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevRsi = rsiValue;
return;
}
var close = candle.ClosePrice;
var trendUp = close > emaValue;
var trendDown = close < emaValue;
// Buy: RSI crosses above oversold + uptrend
if (_prevRsi < Oversold && rsiValue >= Oversold && trendUp && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 10;
}
// Sell: RSI crosses below overbought + downtrend
else if (_prevRsi > Overbought && rsiValue <= Overbought && trendDown && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 10;
}
// Exit long on overbought
if (Position > 0 && rsiValue > 80m)
{
SellMarket();
_entryPrice = 0;
_cooldown = 10;
}
// Exit short on oversold
else if (Position < 0 && rsiValue < 20m)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 10;
}
_prevRsi = rsiValue;
}
}
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 RelativeStrengthIndex, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class rsi_rftl_strategy(Strategy):
def __init__(self):
super(rsi_rftl_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "Length of the RSI oscillator", "Indicator")
self._ema_period = self.Param("EmaPeriod", 44) \
.SetDisplay("EMA Period", "Length of the trend EMA filter", "Indicator")
self._overbought = self.Param("Overbought", 75.0) \
.SetDisplay("Overbought", "RSI overbought level", "Levels")
self._oversold = self.Param("Oversold", 25.0) \
.SetDisplay("Oversold", "RSI oversold level", "Levels")
self._rsi = None
self._ema = None
self._prev_rsi = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def ema_period(self):
return self._ema_period.Value
@property
def overbought(self):
return self._overbought.Value
@property
def oversold(self):
return self._oversold.Value
def OnReseted(self):
super(rsi_rftl_strategy, self).OnReseted()
self._rsi = None
self._ema = None
self._prev_rsi = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(rsi_rftl_strategy, self).OnStarted2(time)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.rsi_period
self._ema = ExponentialMovingAverage()
self._ema.Length = self.ema_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._rsi, self._ema, self._process_candle)
subscription.Start()
def _process_candle(self, candle, rsi_value, ema_value):
if candle.State != CandleStates.Finished:
return
rsi_val = float(rsi_value)
ema_val = float(ema_value)
if not self._rsi.IsFormed or not self._ema.IsFormed:
self._prev_rsi = rsi_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_rsi = rsi_val
return
close = float(candle.ClosePrice)
trend_up = close > ema_val
trend_down = close < ema_val
ob = float(self.overbought)
os_level = float(self.oversold)
# Buy: RSI crosses above oversold + uptrend
if self._prev_rsi < os_level and rsi_val >= os_level and trend_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 10
# Sell: RSI crosses below overbought + downtrend
elif self._prev_rsi > ob and rsi_val <= ob and trend_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 10
# Exit long on strong overbought
if self.Position > 0 and rsi_val > 80.0:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 10
# Exit short on strong oversold
elif self.Position < 0 and rsi_val < 20.0:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 10
self._prev_rsi = rsi_val
def CreateClone(self):
return rsi_rftl_strategy()