Esta estratégia é uma portagem para StockSharp do consultor especialista do MetaTrader 5 e_RP_250. O sistema original opera reversões detectadas por um indicador personalizado rPoint. Como esse indicador não está disponível no StockSharp, a conversão recria o mesmo comportamento com rastreadores de preço mais alto e mais baixo em janela móvel. Quando um novo swing high ou swing low aparece, a estratégia inverte a posição e anexa a mesma lógica de stop-loss, take-profit e trailing opcional que a versão MQL.
O código fonte original não publicou resultados de desempenho verificados, portanto você deve realizar sua própria avaliação antes de implantar a estratégia em produção.
Lógica de trading
Subscrever velas definidas pelo parâmetro CandleType (velas de 5 minutos por padrão).
Rastrear o maior máximo e o menor mínimo nas últimas ReversePoint barras (250 por padrão).
Quando a vela atual estabelece um novo maior máximo, fechar qualquer posição comprada e abrir uma posição vendida.
Quando a vela atual estabelece um novo menor mínimo, fechar qualquer posição vendida e abrir uma posição comprada.
Os níveis protetores de stop-loss e take-profit são expressos em pontos de preço e são reproduzidos através de StartProtection.
Stops trailing opcionais bloqueiam lucros uma vez que o preço se move pelo número de pontos configurado.
Apenas uma posição está ativa a qualquer momento. A estratégia também bloqueia ordens duplicadas durante a mesma vela ao lembrar o último tempo de execução, replicando a proteção TimeN do script MQL.
Parâmetros
Parâmetro
Descrição
TakeProfitPoints
Distância em pontos de preço para a ordem de take-profit (padrão 15). Definir como zero para desabilitar a tomada de lucro automática.
StopLossPoints
Distância em pontos de preço para a ordem de stop-loss (padrão 999). Definir como zero para operar sem stop fixo.
TrailingStopPoints
Distância opcional de stop trailing em pontos de preço (padrão 0 desabilita a lógica de trailing).
ReversePoint
Número de velas usadas para detectar pontos de reversão. Valores maiores reagem mais lentamente mas filtram o ruído.
CandleType
Agregação de velas a analisar. O padrão é um período de 5 minutos mas pode ser alterado para qualquer DataType.
Gestão de posição
StartProtection aplica as mesmas distâncias de stop-loss e take-profit que o especialista MT5.
O stop trailing rastreia o preço mais favorável após a entrada e sai quando o preço reverte pelo valor configurado.
Sinais de reversão do lado oposto fecham imediatamente a posição atual antes de abrir uma nova.
Notas de uso
Certifique-se de que a fonte de dados suporta o tipo de vela selecionado, caso contrário nenhum sinal será gerado.
A estratégia depende de preços decimais. Verifique se a propriedade PriceStep do ativo reflete corretamente o valor do ponto.
Teste diferentes valores de ReversePoint para adaptar a sensibilidade ao rompimento à volatilidade do 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()