Ver en GitHub

Estrategia inversa

Descripción general

La estrategia inversa es un sistema de negociación de reversión a la media que combina Bollinger bandas y el índice de fuerza relativa (RSI) para identificar movimientos agotados. La estrategia busca reversiones de precios cerca de los sobres Bollinger y al mismo tiempo requiere que RSI retroceda desde una zona de sobreventa o sobrecompra. Una vez que se cumplen ambas condiciones, la estrategia entra en contra del movimiento anterior y gestiona las operaciones con paradas y objetivos fijos basados ​​en bandas.

Lógica de trading

  1. Suscríbase a la serie de velas configuradas (velas predeterminadas de 5 minutos).
  2. Calcula Bollinger Bandas usando una media móvil simple con el período configurado y el multiplicador de desviación.
  3. Calcule RSI utilizando el período retrospectivo configurado.
  4. Realice un seguimiento de la vela terminada anterior para detectar cruces:
    • Configuración larga: el cierre anterior está por debajo de la banda inferior anterior y RSI está por debajo del umbral de sobreventa. El cierre actual debe volver a superar la banda inferior mientras que RSI sube por encima del nivel de sobreventa.
    • Configuración corta: el cierre anterior está por encima de la banda superior anterior y RSI está por encima del umbral de sobrecompra. El cierre actual debe volver a caer por debajo de la banda superior mientras que RSI cae por debajo del nivel de sobrecompra.
  5. Cuando se active una configuración larga, compre en el mercado, establezca un stop de protección una desviación estándar por debajo del cierre de entrada y una toma de ganancias dos desviaciones estándar por encima de él.
  6. Cuando se active una configuración corta, venda en el mercado, establezca un stop de protección una desviación estándar por encima del cierre de entrada y una toma de ganancias dos desviaciones estándar por debajo de él.
  7. Gestionar posiciones abiertas:
    • Cierre las operaciones largas si el precio toca la banda superior, toca el stop o alcanza el objetivo de obtención de beneficios.
    • Cierre las operaciones cortas si el precio toca la banda inferior, llega al tope o alcanza el objetivo de obtención de beneficios.

Parámetros

Nombre Descripción Predeterminado
CandleType Plazo de suscripción de la vela. marco de tiempo de 5 minutos
BollingerPeriod Número de barras utilizadas para la media móvil y la desviación estándar Bollinger. 20
BollingerWidth Multiplicador de desviación estándar aplicado a Bollinger bandas. 2.0
RsiPeriod Número de barras utilizadas para calcular el RSI. 14
RsiOverbought RSI umbral que indica condiciones de sobrecompra para entradas cortas. 70
RsiOversold RSI umbral que indica condiciones de sobreventa para entradas largas. 30

Todos los parámetros admiten la optimización a través del StockSharp Designer o Runner. El ajuste de los niveles de sobreventa/sobrecompra cambia la agresividad de la detección de reversión, mientras que el ancho Bollinger controla hasta dónde debe extenderse el precio antes de que se consideren las señales.

Notas de uso

  • La estrategia utiliza el StockSharp API de alto nivel con suscripciones de velas automáticas y vinculación de indicadores.
  • Todas las operaciones comerciales se basan en órdenes de mercado (BuyMarket/SellMarket). Los niveles de stop-loss y take-profit se manejan en código en lugar de como órdenes pendientes.
  • La configuración predeterminada apunta a reversiones importantes en los gráficos intradiarios, pero se puede adaptar a períodos de tiempo más altos cambiando CandleType.
  • Considere combinar la estrategia con filtros adicionales (tendencia, volatilidad, tiempo de sesión) cuando se ejecute en entornos en vivo.
using System;
using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Simplified from "Reverse" MetaTrader expert.
/// Uses Bollinger Band touches with RSI confirmation for mean-reversion entries.
/// Enters long when price crosses above lower band with RSI oversold,
/// enters short when price crosses below upper band with RSI overbought.
/// </summary>
public class ReverseStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _bollingerPeriod;
	private readonly StrategyParam<decimal> _bollingerWidth;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiOverbought;
	private readonly StrategyParam<decimal> _rsiOversold;

	private ExponentialMovingAverage _ema;
	private RelativeStrengthIndex _rsi;

	private decimal _prevClose;
	private decimal _prevRsi;
	private decimal _prevLower;
	private decimal _prevUpper;
	private bool _hasPrev;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int BollingerPeriod
	{
		get => _bollingerPeriod.Value;
		set => _bollingerPeriod.Value = value;
	}

	public decimal BollingerWidth
	{
		get => _bollingerWidth.Value;
		set => _bollingerWidth.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 ReverseStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for signals", "General");

		_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Period", "MA length for Bollinger Bands", "Indicators");

		_bollingerWidth = Param(nameof(BollingerWidth), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Width", "Standard deviation multiplier", "Indicators");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");

		_rsiOverbought = Param(nameof(RsiOverbought), 70m)
			.SetDisplay("RSI Overbought", "Upper threshold for short signals", "Signals");

		_rsiOversold = Param(nameof(RsiOversold), 30m)
			.SetDisplay("RSI Oversold", "Lower threshold for long signals", "Signals");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_ema = new ExponentialMovingAverage { Length = BollingerPeriod };
		_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		_hasPrev = false;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_ema, _rsi, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _ema);
			DrawIndicator(area, _rsi);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal rsiValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_ema.IsFormed || !_rsi.IsFormed)
			return;

		var close = candle.ClosePrice;
		var bandOffset = emaValue * (BollingerWidth / 100m);
		var upperBand = emaValue + bandOffset;
		var lowerBand = emaValue - bandOffset;

		if (!_hasPrev)
		{
			_prevClose = close;
			_prevRsi = rsiValue;
			_prevLower = lowerBand;
			_prevUpper = upperBand;
			_hasPrev = true;
			return;
		}

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// Long: price crosses up from below lower band + RSI was oversold
		var longSignal = _prevClose < _prevLower && close >= lowerBand && _prevRsi < RsiOversold;
		// Short: price crosses down from above upper band + RSI was overbought
		var shortSignal = _prevClose > _prevUpper && close <= upperBand && _prevRsi > RsiOverbought;

		if (longSignal)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		else if (shortSignal)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		// Exit long at upper band
		if (Position > 0 && close >= upperBand)
			SellMarket(Position);

		// Exit short at lower band
		if (Position < 0 && close <= lowerBand)
			BuyMarket(Math.Abs(Position));

		_prevClose = close;
		_prevRsi = rsiValue;
		_prevLower = lowerBand;
		_prevUpper = upperBand;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_ema = null;
		_rsi = null;
		_prevClose = 0;
		_prevRsi = 0;
		_prevLower = 0;
		_prevUpper = 0;
		_hasPrev = false;

		base.OnReseted();
	}
}