La estrategia Mean Reversion es una adaptación directa del asesor experto MetaTrader Mean reversion.mq4. La versión StockSharp mantiene la idea comercial original: comprar después de una serie prolongada de cierres a la baja y vender después de una carrera alcista similar. Las entradas se confirman mediante la alineación de tendencias utilizando dos promedios móviles ponderados lineales, la fuerza del impulso en un período de tiempo más alto y un filtro MACD mensual.
Una vez en posición, la estrategia recrea las reglas de administración de dinero de la versión MQL: stop-loss y take-profit configurables en pips, reubicación opcional del punto de equilibrio y un trailing stop que bloquea las ganancias a medida que el mercado se mueve a favor de la operación.
Lógica de trading
Período de tiempo de la señal: la estrategia opera en la serie de velas seleccionada (predeterminado 15 minutos).
Detección de agotamiento: recopila los últimos BarsToCount cierres. Una configuración larga requiere que el cierre más reciente sea más bajo que cada uno de los cierres anteriores, lo que indica una venta masiva. Una configuración corta necesita la condición opuesta.
Filtro de tendencias: el LWMA rápido (longitud FastMaLength) debe estar por encima del LWMA lento (SlowMaLength) para largos y por debajo para cortos.
Filtro de impulso: el indicador de impulso (período MomentumLength) se calcula en el período de tiempo superior de estilo MetaTrader (M15 → H1, H1 → D1, etc.). Al menos una de las últimas tres lecturas de impulso debe desviarse de 100 en más de MomentumThreshold.
MACD confirmación: un MACD mensual (26/12/9) debe tener la línea principal por encima de la línea de señal para largos y por debajo para cortos.
Si se cumplen todas las condiciones, la estrategia abre una posición usando OrderVolume. Las operaciones opuestas aplanan la posición actual antes de revertirse.
Gestión de Puestos
Stop-loss & take-profit: configurado en pips mediante StopLossPips y TakeProfitPips.
Equipo: cuando está habilitado, el tope se mueve al precio de entrada más BreakEvenOffsetPips después de que el precio avanza en BreakEvenTriggerPips.
Trailing stop: si EnableTrailing es verdadero y el beneficio no realizado supera TrailingStopPips, el stop sigue al precio con el paso TrailingStepPips.
Todas las conversiones de precios utilizan el tamaño del pip del instrumento para coincidir con el comportamiento de MetaTrader.
Parámetros
Nombre
Descripción
Predeterminado
OrderVolume
Tamaño de orden utilizado para las entradas al mercado.
1
CandleType
Serie de velas primarias utilizadas para señales.
M15
BarsToCount
Número de cierres anteriores comprobados por agotamiento.
10
FastMaLength
Período LWMA rápido.
6
SlowMaLength
Período LWMA lento.
85
MomentumLength
Período de impulso en el marco temporal superior.
14
MomentumThreshold
Desviación absoluta mínima de 100 para confirmación del impulso.
0.3
StopLossPips
Distancia de stop-loss en pips.
20
TakeProfitPips
Distancia de toma de ganancias en pips.
50
UseBreakEven
Habilite la reubicación de parada para alcanzar el punto de equilibrio.
false
BreakEvenTriggerPips
Beneficio en pips necesarios antes de mover el stop.
30
BreakEvenOffsetPips
Se agregan puntos adicionales al pasar al punto de equilibrio.
30
EnableTrailing
Activar la gestión de trailing stop.
true
TrailingStopPips
Beneficio en pips necesarios para comenzar a seguir.
40
TrailingStepPips
Distancia mantenida por el trailing stop.
40
Notas
El marco de tiempo superior para el impulso sigue MetaTrader pasos: M1→M15, M5→M30, M15→H1, M30→H4, H1→D1, H4→W1, D1→MN1, W1→MN1.
La confirmación de MACD siempre utiliza el período de tiempo mensual (MN1).
La estrategia espera tipos de velas basadas en plazos; No se admiten velas de tick o de rango.
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;
/// <summary>
/// Simplified from "Mean Reversion" MetaTrader expert.
/// Buys after multi-bar sell-off when RSI is oversold, sells after multi-bar rally when RSI is overbought.
/// Uses consecutive bar count for exhaustion detection with RSI confirmation.
/// </summary>
public class MeanReversionMomentumStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _barsToCount;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<decimal> _rsiOversold;
private RelativeStrengthIndex _rsi;
private readonly List<decimal> _closeHistory = new();
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int BarsToCount
{
get => _barsToCount.Value;
set => _barsToCount.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public decimal RsiOverbought
{
get => _rsiOverbought.Value;
set => _rsiOverbought.Value = value;
}
public decimal RsiOversold
{
get => _rsiOversold.Value;
set => _rsiOversold.Value = value;
}
public MeanReversionMomentumStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe", "General");
_barsToCount = Param(nameof(BarsToCount), 5)
.SetGreaterThanZero()
.SetDisplay("Bars To Count", "Number of consecutive bars for exhaustion detection", "Signal");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period for confirmation", "Indicators");
_rsiOverbought = Param(nameof(RsiOverbought), 70m)
.SetDisplay("RSI Overbought", "RSI level for sell signal", "Signals");
_rsiOversold = Param(nameof(RsiOversold), 30m)
.SetDisplay("RSI Oversold", "RSI level for buy signal", "Signals");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_closeHistory.Clear();
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
_closeHistory.Add(candle.ClosePrice);
if (_closeHistory.Count > BarsToCount + 1)
_closeHistory.RemoveAt(0);
if (!_rsi.IsFormed || _closeHistory.Count < BarsToCount + 1)
return;
var volume = Volume;
if (volume <= 0)
volume = 1;
// Count consecutive down bars
var downCount = 0;
for (int i = _closeHistory.Count - 1; i >= 1; i--)
{
if (_closeHistory[i] < _closeHistory[i - 1])
downCount++;
else
break;
}
// Count consecutive up bars
var upCount = 0;
for (int i = _closeHistory.Count - 1; i >= 1; i--)
{
if (_closeHistory[i] > _closeHistory[i - 1])
upCount++;
else
break;
}
// Multi-bar sell-off + RSI oversold -> mean reversion buy
if (downCount >= BarsToCount && rsiValue < RsiOversold)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
if (Position <= 0)
BuyMarket(volume);
}
// Multi-bar rally + RSI overbought -> mean reversion sell
else if (upCount >= BarsToCount && rsiValue > RsiOverbought)
{
if (Position > 0)
SellMarket(Position);
if (Position >= 0)
SellMarket(volume);
}
}
/// <inheritdoc />
protected override void OnReseted()
{
_closeHistory.Clear();
_rsi = null;
base.OnReseted();
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class mean_reversion_momentum_strategy(Strategy):
def __init__(self):
super(mean_reversion_momentum_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._bars_to_count = self.Param("BarsToCount", 5)
self._rsi_period = self.Param("RsiPeriod", 14)
self._rsi_overbought = self.Param("RsiOverbought", 70.0)
self._rsi_oversold = self.Param("RsiOversold", 30.0)
self._close_history = []
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def BarsToCount(self):
return self._bars_to_count.Value
@BarsToCount.setter
def BarsToCount(self, value):
self._bars_to_count.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def RsiOverbought(self):
return self._rsi_overbought.Value
@RsiOverbought.setter
def RsiOverbought(self, value):
self._rsi_overbought.Value = value
@property
def RsiOversold(self):
return self._rsi_oversold.Value
@RsiOversold.setter
def RsiOversold(self, value):
self._rsi_oversold.Value = value
def OnReseted(self):
super(mean_reversion_momentum_strategy, self).OnReseted()
self._close_history = []
def OnStarted2(self, time):
super(mean_reversion_momentum_strategy, self).OnStarted2(time)
self._close_history = []
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._process_candle).Start()
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
rsi_val = float(rsi_value)
bars_to_count = self.BarsToCount
self._close_history.append(close)
while len(self._close_history) > bars_to_count + 1:
self._close_history.pop(0)
if len(self._close_history) < bars_to_count + 1:
return
# Count consecutive down bars
down_count = 0
for i in range(len(self._close_history) - 1, 0, -1):
if self._close_history[i] < self._close_history[i - 1]:
down_count += 1
else:
break
# Count consecutive up bars
up_count = 0
for i in range(len(self._close_history) - 1, 0, -1):
if self._close_history[i] > self._close_history[i - 1]:
up_count += 1
else:
break
# Multi-bar sell-off + RSI oversold -> mean reversion buy
if down_count >= bars_to_count and rsi_val < float(self.RsiOversold):
if self.Position <= 0:
self.BuyMarket()
# Multi-bar rally + RSI overbought -> mean reversion sell
elif up_count >= bars_to_count and rsi_val > float(self.RsiOverbought):
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return mean_reversion_momentum_strategy()