Esta estrategia es una portación a StockSharp del asesor experto de MetaTrader 5 e_RP_250. El sistema original opera reversiones detectadas por un indicador personalizado rPoint. Dado que ese indicador no está disponible en StockSharp, la conversión recrea el mismo comportamiento con rastreadores de precio máximo y mínimo móviles. Cuando aparece un nuevo máximo o mínimo de swing, la estrategia invierte la posición y adjunta la misma lógica de stop-loss, take-profit y trailing opcional que la versión MQL.
El código fuente original no publicó resultados de rendimiento verificados, por lo que debe realizar su propia evaluación antes de desplegar la estrategia en producción.
Lógica de trading
Suscribirse a velas definidas por el parámetro CandleType (velas de 5 minutos por defecto).
Rastrear el máximo más alto y el mínimo más bajo en las últimas ReversePoint barras (250 por defecto).
Cuando el candle actual establece un nuevo máximo más alto, cerrar cualquier posición larga y abrir una posición corta.
Cuando el candle actual establece un nuevo mínimo más bajo, cerrar cualquier posición corta y abrir una posición larga.
Los niveles protectores de stop-loss y take-profit se expresan en puntos de precio y se reproducen a través de StartProtection.
Los stops trailing opcionales bloquean ganancias una vez que el precio se mueve el número de puntos configurado.
Solo hay una posición activa en cualquier momento. La estrategia también bloquea órdenes duplicadas durante el mismo candle recordando el último tiempo de ejecución, replicando la protección TimeN del script MQL.
Parámetros
Parámetro
Descripción
TakeProfitPoints
Distancia en puntos de precio para la orden de take-profit (predeterminado 15). Establecer en cero para deshabilitar la toma de ganancias automática.
StopLossPoints
Distancia en puntos de precio para la orden de stop-loss (predeterminado 999). Establecer en cero para operar sin un stop fijo.
TrailingStopPoints
Distancia opcional de stop trailing en puntos de precio (predeterminado 0 deshabilita la lógica de trailing).
ReversePoint
Número de velas usadas para detectar puntos de reversión. Los valores más grandes reaccionan más lento pero filtran el ruido.
CandleType
Agregación de velas a analizar. El predeterminado es un marco temporal de 5 minutos pero puede cambiar a cualquier DataType.
Gestión de posición
StartProtection aplica las mismas distancias de stop-loss y take-profit que el experto de MT5.
El stop trailing rastrea el precio más favorable después de la entrada y sale cuando el precio revierte el monto configurado.
Las señales de reversión del lado opuesto cierran inmediatamente la posición actual antes de abrir una nueva.
Notas de uso
Asegúrese de que la fuente de datos soporte el tipo de vela seleccionado, de lo contrario no se generarán señales.
La estrategia depende de precios decimales. Verifique que la propiedad PriceStep del instrumento refleje correctamente el valor del punto.
Pruebe diferentes valores de ReversePoint para adaptar la sensibilidad al rompimiento a la volatilidad del instrumento operado.
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 e_RP_250 MQL script.
/// </summary>
public class ERp250Strategy : Strategy
{
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 _latestHighSignal;
private decimal _latestLowSignal;
private decimal _lastExecutedHigh;
private decimal _lastExecutedLow;
private DateTimeOffset? _lastSignalTime;
private decimal? _bestLongPrice;
private decimal? _bestShortPrice;
private decimal _trailingDistance;
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
public decimal TrailingStopPoints
{
get => _trailingStopPoints.Value;
set => _trailingStopPoints.Value = value;
}
public int ReversePoint
{
get => _reversePoint.Value;
set => _reversePoint.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public ERp250Strategy()
{
_takeProfitPoints = Param(nameof(TakeProfitPoints), 15m)
.SetDisplay("Take Profit Points", "Take profit distance in price points", "Risk")
;
_stopLossPoints = Param(nameof(StopLossPoints), 999m)
.SetDisplay("Stop Loss Points", "Stop loss distance in price points", "Risk")
;
_trailingStopPoints = Param(nameof(TrailingStopPoints), 0m)
.SetDisplay("Trailing Stop Points", "Trailing stop distance in price points", "Risk")
;
_reversePoint = Param(nameof(ReversePoint), 400)
.SetDisplay("Reverse Point Length", "Candles used to confirm reversal points", "Signals")
.SetGreaterThanZero()
;
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to analyse", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highest = null;
_lowest = null;
_latestHighSignal = 0m;
_latestLowSignal = 0m;
_lastExecutedHigh = 0m;
_lastExecutedLow = 0m;
_lastSignalTime = null;
_bestLongPrice = null;
_bestShortPrice = null;
_trailingDistance = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highest = new Highest { Length = ReversePoint };
_lowest = new Lowest { Length = ReversePoint };
var step = Security?.PriceStep ?? 1m;
if (step <= 0m)
step = 1m;
var takeDistance = TakeProfitPoints > 0m ? step * TakeProfitPoints : 0m;
var stopDistance = StopLossPoints > 0m ? step * StopLossPoints : 0m;
_trailingDistance = TrailingStopPoints > 0m ? step * TrailingStopPoints : 0m;
// Enable protective orders that match the original stop and take-profit distances.
StartProtection(
takeDistance > 0m ? new Unit(takeDistance, UnitTypes.Absolute) : default,
stopDistance > 0m ? new Unit(stopDistance, UnitTypes.Absolute) : default
);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var highValue = _highest.Process(new DecimalIndicatorValue(_highest, candle.HighPrice, candle.OpenTime) { IsFinal = true }).ToNullableDecimal();
var lowValue = _lowest.Process(new DecimalIndicatorValue(_lowest, candle.LowPrice, candle.OpenTime) { IsFinal = true }).ToNullableDecimal();
if (highValue is null || lowValue is null)
return;
// Update the latest reversal levels detected by the rolling highest/lowest indicators.
if (highValue.Value == candle.HighPrice)
_latestHighSignal = candle.HighPrice;
if (lowValue.Value == candle.LowPrice)
_latestLowSignal = candle.LowPrice;
// Manage an existing long position by trailing profits and reacting to opposite signals.
if (Position > 0)
{
_bestLongPrice = (_bestLongPrice is null || candle.HighPrice > _bestLongPrice) ? candle.HighPrice : _bestLongPrice;
if (_trailingDistance > 0m && _bestLongPrice is decimal bestLong && bestLong - candle.ClosePrice >= _trailingDistance)
{
SellMarket();
_bestLongPrice = null;
return;
}
if (_latestHighSignal != 0m && _latestHighSignal != _lastExecutedHigh)
{
SellMarket();
_bestLongPrice = null;
return;
}
}
else if (Position < 0)
{
_bestShortPrice = (_bestShortPrice is null || candle.LowPrice < _bestShortPrice) ? candle.LowPrice : _bestShortPrice;
if (_trailingDistance > 0m && _bestShortPrice is decimal bestShort && candle.ClosePrice - bestShort >= _trailingDistance)
{
BuyMarket();
_bestShortPrice = null;
return;
}
if (_latestLowSignal != 0m && _latestLowSignal != _lastExecutedLow)
{
BuyMarket();
_bestShortPrice = null;
return;
}
}
else
{
_bestLongPrice = null;
_bestShortPrice = null;
}
if (Position != 0)
return;
// Avoid placing more than one order within the same candle.
if (_lastSignalTime == candle.OpenTime)
return;
// Execute a new short position when a fresh reversal high is detected.
if (_latestHighSignal != 0m && _latestHighSignal != _lastExecutedHigh)
{
SellMarket();
_lastExecutedHigh = _latestHighSignal;
_lastSignalTime = candle.OpenTime;
_bestShortPrice = candle.ClosePrice;
_bestLongPrice = null;
return;
}
// Execute a new long position when a fresh reversal low is detected.
if (_latestLowSignal != 0m && _latestLowSignal != _lastExecutedLow)
{
BuyMarket();
_lastExecutedLow = _latestLowSignal;
_lastSignalTime = candle.OpenTime;
_bestLongPrice = candle.ClosePrice;
_bestShortPrice = null;
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class e_rp250_strategy(Strategy):
def __init__(self):
super(e_rp250_strategy, self).__init__()
self._take_profit_points = self.Param("TakeProfitPoints", 15.0)
self._stop_loss_points = self.Param("StopLossPoints", 999.0)
self._trailing_stop_points = self.Param("TrailingStopPoints", 0.0)
self._reverse_point = self.Param("ReversePoint", 400)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._latest_high_signal = 0.0
self._latest_low_signal = 0.0
self._last_executed_high = 0.0
self._last_executed_low = 0.0
self._last_signal_time = None
self._best_long_price = None
self._best_short_price = None
self._trailing_distance = 0.0
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@TakeProfitPoints.setter
def TakeProfitPoints(self, value):
self._take_profit_points.Value = value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@StopLossPoints.setter
def StopLossPoints(self, value):
self._stop_loss_points.Value = value
@property
def TrailingStopPoints(self):
return self._trailing_stop_points.Value
@TrailingStopPoints.setter
def TrailingStopPoints(self, value):
self._trailing_stop_points.Value = value
@property
def ReversePoint(self):
return self._reverse_point.Value
@ReversePoint.setter
def ReversePoint(self, value):
self._reverse_point.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(e_rp250_strategy, self).OnStarted2(time)
self._highest = Highest()
self._highest.Length = self.ReversePoint
self._lowest = Lowest()
self._lowest.Length = self.ReversePoint
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if step <= 0.0:
step = 1.0
take_distance = step * float(self.TakeProfitPoints) if float(self.TakeProfitPoints) > 0.0 else 0.0
stop_distance = step * float(self.StopLossPoints) if float(self.StopLossPoints) > 0.0 else 0.0
self._trailing_distance = step * float(self.TrailingStopPoints) if float(self.TrailingStopPoints) > 0.0 else 0.0
tp_unit = Unit(take_distance, UnitTypes.Absolute) if take_distance > 0.0 else Unit()
sl_unit = Unit(stop_distance, UnitTypes.Absolute) if stop_distance > 0.0 else Unit()
self.StartProtection(tp_unit, sl_unit)
self._latest_high_signal = 0.0
self._latest_low_signal = 0.0
self._last_executed_high = 0.0
self._last_executed_low = 0.0
self._last_signal_time = None
self._best_long_price = None
self._best_short_price = None
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
high_result = process_float(self._highest, candle.HighPrice, candle.OpenTime, True)
low_result = process_float(self._lowest, candle.LowPrice, candle.OpenTime, True)
if high_result.IsEmpty or low_result.IsEmpty:
return
high_value = float(high_result)
low_value = float(low_result)
if abs(high_value - high) < 1e-10:
self._latest_high_signal = high
if abs(low_value - low) < 1e-10:
self._latest_low_signal = low
# Manage existing long position
if self.Position > 0:
if self._best_long_price is None or high > self._best_long_price:
self._best_long_price = high
if self._trailing_distance > 0.0 and self._best_long_price is not None and self._best_long_price - close >= self._trailing_distance:
self.SellMarket()
self._best_long_price = None
return
if self._latest_high_signal != 0.0 and self._latest_high_signal != self._last_executed_high:
self.SellMarket()
self._best_long_price = None
return
elif self.Position < 0:
if self._best_short_price is None or low < self._best_short_price:
self._best_short_price = low
if self._trailing_distance > 0.0 and self._best_short_price is not None and close - self._best_short_price >= self._trailing_distance:
self.BuyMarket()
self._best_short_price = None
return
if self._latest_low_signal != 0.0 and self._latest_low_signal != self._last_executed_low:
self.BuyMarket()
self._best_short_price = None
return
else:
self._best_long_price = None
self._best_short_price = None
if self.Position != 0:
return
# Avoid placing more than one order within the same candle
if self._last_signal_time is not None and self._last_signal_time == candle.OpenTime:
return
# Short on fresh reversal high
if self._latest_high_signal != 0.0 and self._latest_high_signal != self._last_executed_high:
self.SellMarket()
self._last_executed_high = self._latest_high_signal
self._last_signal_time = candle.OpenTime
self._best_short_price = close
self._best_long_price = None
return
# Long on fresh reversal low
if self._latest_low_signal != 0.0 and self._latest_low_signal != self._last_executed_low:
self.BuyMarket()
self._last_executed_low = self._latest_low_signal
self._last_signal_time = candle.OpenTime
self._best_long_price = close
self._best_short_price = None
def OnReseted(self):
super(e_rp250_strategy, self).OnReseted()
self._latest_high_signal = 0.0
self._latest_low_signal = 0.0
self._last_executed_high = 0.0
self._last_executed_low = 0.0
self._last_signal_time = None
self._best_long_price = None
self._best_short_price = None
self._trailing_distance = 0.0
def CreateClone(self):
return e_rp250_strategy()