Esta estrategia es un port en C# del experto de MetaTrader 5 Exp_SR-RateIndicator. Reproduce la lógica de trading original usando la API de alto nivel de StockSharp y una implementación personalizada del oscilador SR Rate. El indicador mide qué tan lejos está el precio ponderado de la vela dentro de un canal de soporte/resistencia suavizado y pinta un código de color que resalta las lecturas extremas.
El algoritmo procesa velas terminadas de un marco temporal configurable. Cada vez que el color del oscilador salta al extremo alcista o bajista, la estrategia cierra cualquier posición opuesta y abre una nueva operación en la dirección de la señal. Los niveles de stop loss y take profit protectores se aplican con las mismas distancias en puntos utilizadas en la versión de MetaTrader.
Oscilador SR Rate
El indicador calcula una banda suavizada gaussiana alrededor del precio usando una longitud de ventana configurable:
Para cada barra, el máximo, mínimo y cierre ponderado se suavizan con pesos gaussianos unilaterales de longitud seis.
El máximo suavizado más alto y el mínimo suavizado más bajo sobre la ventana definen un rango dinámico.
El cierre ponderado suavizado actual se normaliza dentro de ese rango y se mapea al intervalo [-100, 100].
El valor final del oscilador se convierte en cinco estados de color: 0 (fuertemente bajista), 1 (suavemente bajista), 2 (neutral), 3 (suavemente alcista) y 4 (fuertemente alcista).
Un color fuertemente alcista (4) indica que el precio alcanzó el extremo superior del rango, mientras que un color fuertemente bajista (0) señala una visita al extremo inferior.
Reglas de trading
Suscribirse a velas del tipo configurado y calcular el oscilador SR Rate en cada barra terminada.
Desplazar la evaluación de señales por SignalBar velas cerradas (por defecto: una barra atrás) para imitar el comportamiento del Asesor Experto.
Cuando el color desplazado se vuelve 4 y el color anterior es inferior a 4:
Cerrar cualquier posición corta existente si las salidas largas están habilitadas.
Abrir una nueva posición larga si las entradas largas están habilitadas y no hay otra posición activa.
Cuando el color desplazado se vuelve 0 y el color anterior es superior a 0:
Cerrar cualquier posición larga existente si las salidas cortas están habilitadas.
Abrir una nueva posición corta si las entradas cortas están habilitadas y no hay otra posición activa.
Solo puede estar abierta una posición a la vez. Las nuevas señales se ignoran hasta que se cierra la operación anterior.
Los niveles opcionales de stop loss y take profit se expresan en puntos de precio y se convierten automáticamente a precios absolutos usando el paso de precio del instrumento.
Parámetros
Nombre
Descripción
OrderVolume
Volumen de operación utilizado para cada orden de mercado.
EnableLongEntries
Habilitar/deshabilitar apertura de posiciones largas.
EnableShortEntries
Habilitar/deshabilitar apertura de posiciones cortas.
EnableLongExits
Cerrar posiciones largas cuando aparece un color fuertemente bajista.
EnableShortExits
Cerrar posiciones cortas cuando aparece un color fuertemente alcista.
StopLossPoints
Distancia del stop loss en puntos del instrumento (convertido usando el paso de precio).
TakeProfitPoints
Distancia del take profit en puntos del instrumento (convertido usando el paso de precio).
SlippagePoints
Deslizamiento máximo tolerado al cerrar posiciones. Preservado por compatibilidad; la API de alto nivel no aplica control explícito de deslizamiento.
CandleType
Tipo de vela y marco temporal utilizados para calcular el indicador.
SignalBar
Número de velas cerradas para desplazar la evaluación de señales (por defecto 1).
WindowSize
Longitud de la ventana rodante utilizada por la normalización SR Rate.
HighLevel
Nivel del oscilador que define el extremo alcista (por defecto +20).
LowLevel
Nivel del oscilador que define el extremo bajista (por defecto -20).
Notas
La estrategia funciona con cualquier instrumento que suministre velas OHLC estándar.
Las señales solo se procesan en velas terminadas; los recálculos intrabarra se ignoran, igual que en la implementación de MetaTrader.
El manejo del deslizamiento en el experto original dependía de la configuración de ejecución. Las órdenes de mercado de StockSharp ya respetan las reglas del exchange, por lo tanto el parámetro SlippagePoints se mantiene solo con fines de documentación.
El indicador almacena solo la cantidad mínima de historial requerida para evaluar la ventana, evitando el uso innecesario de memoria.
La versión en Python se omite intencionalmente según las directrices del proyecto.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class SrRateIndicatorStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public SrRateIndicatorStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class sr_rate_indicator_strategy(Strategy):
def __init__(self):
super(sr_rate_indicator_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(sr_rate_indicator_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(sr_rate_indicator_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return sr_rate_indicator_strategy()