Estrategia de reversión RPoint 250
La Estrategia de reversión RPoint 250 es una StockSharp versión del MetaTrader 4 asesor experto e_RPoint_250. El robot original
se basa en un indicador personalizado llamado RPoint que resalta el máximo y el mínimo más recientes. Porque ese indicador es
no disponible en StockSharp, la conversión reproduce el mismo comportamiento con los indicadores integrados Highest y Lowest.
Cada vez que un nuevo extremo reemplaza al previamente detectado, la estrategia inmediatamente invierte la posición y restaura la misma.
Lógica de stop-loss, take-profit y trailing definida en la versión MQL.
Flujo de trabajo comercial
- Suscríbase a la serie de velas especificada por
CandleType (predeterminado: velas de 5 minutos).
- Realice un seguimiento del máximo y mínimo móvil de las últimas
ReversePoint barras. Estos valores representan los niveles de RPoint emulados.
- Si el precio alcanza un nuevo máximo, cierre cualquier posición larga y abra una posición corta con volumen
OrderVolume.
- Si el precio imprime un nuevo mínimo, cierre cualquier posición corta y abra una posición larga con volumen
OrderVolume.
- Aplicar órdenes de protección usando
StartProtection. Las distancias de stop-loss y take-profit se expresan en puntos de precio mediante
los parámetros StopLossPoints y TakeProfitPoints.
- Opcionalmente, siga las ganancias por
TrailingStopPoints. El motor de seguimiento mide hasta qué punto se ha movido el precio a favor del
posición y la cierra cuando el precio retrocede en el número de puntos configurado.
- Recuerde el tiempo de la vela de la última entrada exitosa para evitar abrir múltiples operaciones dentro de la misma barra, coincidiendo con el
TimeN salvaguarda del script MQL.
La estrategia siempre mantiene como máximo una posición abierta. Cierra las operaciones existentes antes de entrar en la dirección opuesta y
nunca se amplía.
Parámetros
| Parámetro |
Tipo |
Predeterminado |
Descripción |
OrderVolume |
decimal |
0.1 |
Volumen enviado con cada orden de mercado. Refleja la entrada Lots en la versión MetaTrader. |
TakeProfitPoints |
decimal |
15 |
Distancia a la orden de toma de ganancias medida en puntos de precio. Establezca en 0 para deshabilitar los objetivos de ganancias. |
StopLossPoints |
decimal |
999 |
Distancia al tope de protección expresada en puntos de precio. Establezca en 0 para operar sin un tope fijo. |
TrailingStopPoints |
decimal |
0 |
Distancia de seguimiento opcional en puntos de precio. Cuando es cero, la lógica de seguimiento está desactivada. |
ReversePoint |
int |
250 |
Número de velas consideradas al buscar el último máximo y mínimo. Los valores más altos suavizan el ruido. |
CandleType |
DataType |
TimeSpan.FromMinutes(5).TimeFrame() |
Agregación de velas analizada por la estrategia. Cámbielo para que coincida con el período de tiempo del gráfico utilizado en MetaTrader. |
Notas de implementación
Highest y Lowest están vinculados a la suscripción de velas a través del Bind API de alto nivel, por lo que no hay colas de indicadores manuales.
requerido.
StartProtection reproduce las distancias originales de stop-loss y take-profit en unidades de precio absoluto. StockSharp maneja el
colocación del pedido una vez que aparece una nueva posición.
- Los trailingstops se implementan monitoreando cada vela completada. Cuando el precio retrocede según el número configurado de puntos desde
el mejor precio alcanzado después de la entrada, la posición se cierra con una orden de mercado.
- La clase almacena los niveles de reversión ejecutados más recientes (
_executedHighLevel y _executedLowLevel) para evitar duplicados.
entradas. Esto es equivalente a las variables Reverse_High / Reverse_Low en el código MQL.
- El campo
_lastSignalTime refleja la variable TimeN y bloquea múltiples órdenes dentro de la misma vela, evitando
presentaciones dobles accidentales en mercados ilíquidos.
Pautas de uso
- Adjunte la estrategia a una cartera que admita el instrumento y el tipo de vela seleccionados.
- Ajuste
OrderVolume para cumplir con el tamaño del contrato y las reglas de gestión de riesgos de su corredor.
- Ajuste
ReversePoint para que coincida con la volatilidad del activo negociado. Los valores más altos producen menos cambios pero más significativos.
- Verifique que
StopLossPoints, TakeProfitPoints y TrailingStopPoints sean compatibles con el valor PriceStep.
- Ejecute una prueba retrospectiva en StockSharp Designer o Backtester para confirmar el comportamiento antes de operar con capital real.
- Supervise la salida del registro: los mensajes informativos resaltarán los cambios de posición y pueden ayudar a validar la conversión.
Debido a que el indicador RPoint se aproxima con componentes integrados, se observan diferencias menores con respecto a la ejecución de MetaTrader.
posible en datos históricos con lagunas o diferentes reglas de redondeo. Valide siempre los resultados con sus propios feeds de datos de mercado
antes de confiar en la estrategia en producción.
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()