Estratégia de reversão RPoint 250
A Estratégia de reversão RPoint 250 é uma versão StockSharp do MetaTrader 4 consultor especialista e_RPoint_250. O robô original
depende de um indicador personalizado chamado RPoint que destaca a oscilação máxima e mínima mais recente. Porque esse indicador é
não disponível em StockSharp, a conversão reproduz o mesmo comportamento com os indicadores Highest e Lowest integrados.
Sempre que um novo extremo substitui o anteriormente detectado, a estratégia imediatamente inverte a posição e restaura a mesma.
lógica de stop-loss, take-profit e trailing definidas na versão MQL.
Fluxo de trabalho de negociação
- Assine a série de velas especificada por
CandleType (padrão: velas de 5 minutos).
- Acompanhe o máximo e o mínimo contínuos nas últimas
ReversePoint barras. Esses valores representam os níveis RPoint emulados.
- Se o preço imprimir uma nova máxima máxima, feche qualquer posição longa e abra uma posição curta com volume
OrderVolume.
- Se o preço imprimir um novo mínimo, feche qualquer posição curta e abra uma posição longa com volume
OrderVolume.
- Aplique ordens de proteção usando
StartProtection. As distâncias de stop-loss e take-profit são expressas em pontos de preço por meio de
os parâmetros StopLossPoints e TakeProfitPoints.
- Opcionalmente, acompanhe os lucros por
TrailingStopPoints. O mecanismo de trilha mede até que ponto o preço se moveu em favor do
posição e fecha-a quando o preço retrocede pelo número configurado de pontos.
- Lembre-se do tempo da vela da última entrada bem-sucedida para evitar a abertura de múltiplas negociações dentro da mesma barra, correspondendo ao
TimeN proteção do script MQL.
A estratégia sempre mantém no máximo uma posição aberta. Fecha negociações existentes antes de entrar na direção oposta e
nunca aumenta.
Parâmetros
| Parâmetro |
Tipo |
Padrão |
Descrição |
OrderVolume |
decimal |
0.1 |
Volume enviado com cada ordem de mercado. Espelha a entrada Lots na versão MetaTrader. |
TakeProfitPoints |
decimal |
15 |
Distância até a ordem de lucro medida em faixas de preço. Defina como 0 para desativar as metas de lucro. |
StopLossPoints |
decimal |
999 |
Distância até o stop de proteção expressa em faixas de preço. Defina como 0 para negociar sem um stop fixo. |
TrailingStopPoints |
decimal |
0 |
Distância final opcional em faixas de preço. Quando zero, a lógica final é desabilitada. |
ReversePoint |
int |
250 |
Número de velas consideradas ao pesquisar as últimas oscilações máximas e mínimas. Valores maiores suavizam o ruído. |
CandleType |
DataType |
TimeSpan.FromMinutes(5).TimeFrame() |
Agregação de velas analisada pela estratégia. Altere-o para corresponder ao período do gráfico usado em MetaTrader. |
Notas de implementação
Highest e Lowest estão vinculados à assinatura de vela por meio do Bind API de alto nível, portanto, nenhuma fila de indicador manual é
necessário.
StartProtection reproduz as distâncias originais de stop-loss e take-profit em unidades de preço absoluto. StockSharp lida com o
colocação de pedido assim que uma nova posição aparecer.
- Os trailing stops são implementados monitorando cada vela concluída. Quando o preço recua pelo número configurado de pontos de
ao melhor preço alcançado após a entrada, a posição é fechada com uma ordem de mercado.
- A classe armazena os níveis de reversão executados mais recentemente (
_executedHighLevel e _executedLowLevel) para evitar duplicação
entradas. Isso é equivalente às variáveis Reverse_High / Reverse_Low no código MQL.
- O campo
_lastSignalTime espelha a variável TimeN e bloqueia vários pedidos dentro da mesma vela, evitando
submissões duplas acidentais em mercados ilíquidos.
Diretrizes de uso
- Anexe a estratégia a um portfólio que suporte o instrumento selecionado e o tipo de vela.
- Ajuste
OrderVolume para cumprir o tamanho do contrato e as regras de gerenciamento de risco do seu corretor.
- Ajuste
ReversePoint para corresponder à volatilidade do ativo negociado. Valores mais altos geram menos reversões, mas mais significativas.
- Verifique se
StopLossPoints, TakeProfitPoints e TrailingStopPoints são compatíveis com o PriceStep da segurança.
- Execute um backtest no StockSharp Designer ou Backtester para confirmar o comportamento antes de negociar capital real.
- Monitore a saída do log: mensagens informativas destacarão as mudanças de posição e podem ajudar a validar a conversão.
Como o indicador RPoint é aproximado com componentes integrados, pequenas diferenças em relação à execução MetaTrader são
possível em dados históricos com lacunas ou regras de arredondamento diferentes. Sempre valide os resultados com seus próprios feeds de dados de mercado
antes de confiar na estratégia na produção.
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>
/// Reverse-point breakout strategy converted from the MetaTrader 4 expert e_RPoint_250.
/// </summary>
public class RPoint250Strategy : Strategy
{
private readonly StrategyParam<decimal> _orderVolume;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _trailingStopPoints;
private readonly StrategyParam<int> _reversePoint;
private readonly StrategyParam<DataType> _candleType;
private Highest _highest;
private Lowest _lowest;
private decimal _lastHighLevel;
private decimal _lastLowLevel;
private decimal _executedHighLevel;
private decimal _executedLowLevel;
private DateTimeOffset? _lastSignalTime;
private decimal _priceStep;
private decimal _trailingDistance;
private decimal? _bestLongPrice;
private decimal? _bestShortPrice;
public RPoint250Strategy()
{
_orderVolume = Param(nameof(OrderVolume), 1m)
.SetDisplay("Order Volume", "Base volume for market entries.", "Trading")
;
_takeProfitPoints = Param(nameof(TakeProfitPoints), 500m)
.SetDisplay("Take Profit Points", "Take profit distance expressed in price points.", "Risk")
;
_stopLossPoints = Param(nameof(StopLossPoints), 999m)
.SetDisplay("Stop Loss Points", "Stop loss distance expressed in price points.", "Risk")
;
_trailingStopPoints = Param(nameof(TrailingStopPoints), 0m)
.SetDisplay("Trailing Stop Points", "Optional trailing distance in price points.", "Risk")
;
_reversePoint = Param(nameof(ReversePoint), 250)
.SetDisplay("Reverse Point Length", "Number of candles scanned for the latest reversal levels.", "Signals")
.SetGreaterThanZero()
;
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle aggregation used for calculations.", "General");
}
/// <summary>
/// Market order volume used for both entries and reversals.
/// </summary>
public decimal OrderVolume
{
get => _orderVolume.Value;
set => _orderVolume.Value = value;
}
/// <summary>
/// Take-profit distance in price points.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Stop-loss distance in price points.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Trailing-stop distance in price points.
/// </summary>
public decimal TrailingStopPoints
{
get => _trailingStopPoints.Value;
set => _trailingStopPoints.Value = value;
}
/// <summary>
/// Number of candles used to approximate the rPoint indicator.
/// </summary>
public int ReversePoint
{
get => _reversePoint.Value;
set => _reversePoint.Value = value;
}
/// <summary>
/// Candle type processed by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highest = null;
_lowest = null;
_lastHighLevel = 0m;
_lastLowLevel = 0m;
_executedHighLevel = 0m;
_executedLowLevel = 0m;
_lastSignalTime = null;
_priceStep = 0m;
_trailingDistance = 0m;
_bestLongPrice = null;
_bestShortPrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highest = new Highest { Length = Math.Max(1, ReversePoint) };
_lowest = new Lowest { Length = Math.Max(1, ReversePoint) };
_priceStep = Security?.PriceStep ?? 0m;
if (_priceStep <= 0m)
_priceStep = 1m;
var takeDistance = TakeProfitPoints > 0m ? _priceStep * TakeProfitPoints : 0m;
var stopDistance = StopLossPoints > 0m ? _priceStep * StopLossPoints : 0m;
_trailingDistance = TrailingStopPoints > 0m ? _priceStep * TrailingStopPoints : 0m;
// Apply the same static protection as in the original MQL script.
var tp = takeDistance > 0m ? new Unit(takeDistance, UnitTypes.Absolute) : (Unit)null;
var sl = stopDistance > 0m ? new Unit(stopDistance, UnitTypes.Absolute) : (Unit)null;
if (tp != null || sl != null)
StartProtection(tp, sl);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_highest, _lowest, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _highest);
DrawIndicator(area, _lowest);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal highestValue, decimal lowestValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_highest.IsFormed || !_lowest.IsFormed)
return;
// Capture the latest swing levels as soon as they appear.
if (highestValue == candle.HighPrice && highestValue != _lastHighLevel)
_lastHighLevel = highestValue;
if (lowestValue == candle.LowPrice && lowestValue != _lastLowLevel)
_lastLowLevel = lowestValue;
if (Position > 0)
{
_bestLongPrice = _bestLongPrice is null || candle.HighPrice > _bestLongPrice
? candle.HighPrice
: _bestLongPrice;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Close the long position when price retraces by the trailing distance.
if (_trailingDistance > 0m && _bestLongPrice is decimal bestLong && bestLong - candle.LowPrice >= _trailingDistance)
{
SellMarket(Position);
_bestLongPrice = null;
return;
}
// Reverse the position when a new high reversal point appears.
if (_lastHighLevel != 0m && _lastHighLevel != _executedHighLevel)
{
SellMarket(Position);
_bestLongPrice = null;
return;
}
}
else if (Position < 0)
{
_bestShortPrice = _bestShortPrice is null || candle.LowPrice < _bestShortPrice
? candle.LowPrice
: _bestShortPrice;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Close the short position when price rallies by the trailing distance.
if (_trailingDistance > 0m && _bestShortPrice is decimal bestShort && candle.HighPrice - bestShort >= _trailingDistance)
{
BuyMarket(-Position);
_bestShortPrice = null;
return;
}
// Reverse the position when a new low reversal point appears.
if (_lastLowLevel != 0m && _lastLowLevel != _executedLowLevel)
{
BuyMarket(-Position);
_bestShortPrice = null;
return;
}
}
else
{
_bestLongPrice = null;
_bestShortPrice = null;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (OrderVolume <= 0m)
return;
if (_lastSignalTime == candle.OpenTime)
return;
// Enter short when the reversal high changes.
if (_lastHighLevel != 0m && _lastHighLevel != _executedHighLevel)
{
SellMarket(OrderVolume);
_executedHighLevel = _lastHighLevel;
_lastSignalTime = candle.OpenTime;
_bestShortPrice = candle.ClosePrice;
return;
}
// Enter long when the reversal low changes.
if (_lastLowLevel != 0m && _lastLowLevel != _executedLowLevel)
{
BuyMarket(OrderVolume);
_executedLowLevel = _lastLowLevel;
_lastSignalTime = candle.OpenTime;
_bestLongPrice = candle.ClosePrice;
}
}
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import Highest, Lowest
class r_point250_strategy(Strategy):
def __init__(self):
super(r_point250_strategy, self).__init__()
self._order_volume = self.Param("OrderVolume", 1.0) \
.SetDisplay("Order Volume", "Base volume for market entries", "Trading")
self._take_profit_points = self.Param("TakeProfitPoints", 500.0) \
.SetDisplay("Take Profit Points", "Take profit distance in price points", "Risk")
self._stop_loss_points = self.Param("StopLossPoints", 999.0) \
.SetDisplay("Stop Loss Points", "Stop loss distance in price points", "Risk")
self._trailing_stop_points = self.Param("TrailingStopPoints", 0.0) \
.SetDisplay("Trailing Stop Points", "Optional trailing distance in price points", "Risk")
self._reverse_point = self.Param("ReversePoint", 250) \
.SetDisplay("Reverse Point Length", "Number of candles scanned for reversal levels", "Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candle aggregation used for calculations", "General")
self._last_high_level = 0.0
self._last_low_level = 0.0
self._executed_high_level = 0.0
self._executed_low_level = 0.0
self._last_signal_time = None
self._price_step = 1.0
self._trailing_distance = 0.0
self._best_long_price = None
self._best_short_price = None
@property
def OrderVolume(self):
return self._order_volume.Value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@property
def TrailingStopPoints(self):
return self._trailing_stop_points.Value
@property
def ReversePoint(self):
return self._reverse_point.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(r_point250_strategy, self).OnStarted2(time)
highest = Highest()
highest.Length = max(1, self.ReversePoint)
lowest = Lowest()
lowest.Length = max(1, self.ReversePoint)
ps = self.Security.PriceStep if self.Security is not None else None
self._price_step = float(ps) if ps is not None else 1.0
if self._price_step <= 0:
self._price_step = 1.0
tp_pts = float(self.TakeProfitPoints)
sl_pts = float(self.StopLossPoints)
trail_pts = float(self.TrailingStopPoints)
take_dist = self._price_step * tp_pts if tp_pts > 0 else 0.0
stop_dist = self._price_step * sl_pts if sl_pts > 0 else 0.0
self._trailing_distance = self._price_step * trail_pts if trail_pts > 0 else 0.0
tp = Unit(take_dist, UnitTypes.Absolute) if take_dist > 0 else None
sl = Unit(stop_dist, UnitTypes.Absolute) if stop_dist > 0 else None
if tp is not None or sl is not None:
self.StartProtection(tp, sl)
self._highest = highest
self._lowest = lowest
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, lowest, self.ProcessCandle).Start()
def ProcessCandle(self, candle, highest_value, lowest_value):
if candle.State != CandleStates.Finished:
return
if not self._highest.IsFormed or not self._lowest.IsFormed:
return
highest_value = float(highest_value)
lowest_value = float(lowest_value)
high_price = float(candle.HighPrice)
low_price = float(candle.LowPrice)
close_price = float(candle.ClosePrice)
if highest_value == high_price and highest_value != self._last_high_level:
self._last_high_level = highest_value
if lowest_value == low_price and lowest_value != self._last_low_level:
self._last_low_level = lowest_value
if self.Position > 0:
if self._best_long_price is None or high_price > self._best_long_price:
self._best_long_price = high_price
if (self._trailing_distance > 0 and self._best_long_price is not None
and self._best_long_price - low_price >= self._trailing_distance):
self.SellMarket(self.Position)
self._best_long_price = None
return
if self._last_high_level != 0 and self._last_high_level != self._executed_high_level:
self.SellMarket(self.Position)
self._best_long_price = None
return
elif self.Position < 0:
if self._best_short_price is None or low_price < self._best_short_price:
self._best_short_price = low_price
if (self._trailing_distance > 0 and self._best_short_price is not None
and high_price - self._best_short_price >= self._trailing_distance):
self.BuyMarket(-self.Position)
self._best_short_price = None
return
if self._last_low_level != 0 and self._last_low_level != self._executed_low_level:
self.BuyMarket(-self.Position)
self._best_short_price = None
return
else:
self._best_long_price = None
self._best_short_price = None
ov = float(self.OrderVolume)
if ov <= 0:
return
if self._last_signal_time == candle.OpenTime:
return
if self._last_high_level != 0 and self._last_high_level != self._executed_high_level:
self.SellMarket(ov)
self._executed_high_level = self._last_high_level
self._last_signal_time = candle.OpenTime
self._best_short_price = close_price
return
if self._last_low_level != 0 and self._last_low_level != self._executed_low_level:
self.BuyMarket(ov)
self._executed_low_level = self._last_low_level
self._last_signal_time = candle.OpenTime
self._best_long_price = close_price
def OnReseted(self):
super(r_point250_strategy, self).OnReseted()
self._last_high_level = 0.0
self._last_low_level = 0.0
self._executed_high_level = 0.0
self._executed_low_level = 0.0
self._last_signal_time = None
self._best_long_price = None
self._best_short_price = None
def CreateClone(self):
return r_point250_strategy()