Ver en GitHub

Estrategia de Comercio Automático con RSI

Esta estrategia promedia los últimos valores de RSI para generar señales de trading. Calcula un Índice de Fuerza Relativa (RSI) estándar sobre un período configurable y luego aplica una media móvil simple al propio RSI. Las operaciones se abren cuando el RSI promediado cruza umbrales predefinidos y se cierran cuando se alcanza el umbral opuesto.

Lógica de trading

  1. Cálculo del RSI
    • El indicador utiliza RsiPeriod para calcular el RSI basado en precios de cierre de velas.
  2. Promediado del RSI
    • Los últimos AveragePeriod valores de RSI se suavizan con una media móvil simple.
  3. Reglas de entrada
    • Si BuyEnabled es true y no hay posición abierta, se envía una orden de compra cuando el RSI promediado supera BuyThreshold (por defecto 55).
    • Si SellEnabled es true y no hay posición abierta, se envía una orden de venta cuando el RSI promediado cae por debajo de SellThreshold (por defecto 45).
  4. Reglas de salida
    • Cuando CloseBySignal es true, las posiciones abiertas se cierran en señales opuestas:
      • Las posiciones largas se cierran cuando el RSI promediado cae por debajo de CloseBuyThreshold (por defecto 47).
      • Las posiciones cortas se cierran cuando el RSI promediado sube por encima de CloseSellThreshold (por defecto 52).

Parámetros

  • BuyEnabled – habilitar o deshabilitar entradas largas.
  • SellEnabled – habilitar o deshabilitar entradas cortas.
  • CloseBySignal – permitir salidas en señales RSI opuestas.
  • RsiPeriod – longitud del cálculo del RSI.
  • AveragePeriod – número de valores de RSI usados para el promediado.
  • BuyThreshold – valor del RSI promediado por encima del cual se abre una posición larga.
  • SellThreshold – valor del RSI promediado por debajo del cual se abre una posición corta.
  • CloseBuyThreshold – valor del RSI promediado por debajo del cual se cierra una posición larga.
  • CloseSellThreshold – valor del RSI promediado por encima del cual se cierra una posición corta.
  • CandleType – tipo de vela para las suscripciones.

Notas

Esta estrategia demuestra cómo los valores de indicadores pueden combinarse mediante vinculación en la API de alto nivel de StockSharp. Las funciones de trailing stop y gestión del dinero de la versión MQL original se omiten por simplicidad.

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>
/// Strategy that trades based on averaged RSI values.
/// Uses RSI smoothed by SMA to generate signals.
/// </summary>
public class AutoTradeWithRsiStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _averagePeriod;
	private readonly StrategyParam<decimal> _buyThreshold;
	private readonly StrategyParam<decimal> _sellThreshold;
	private readonly StrategyParam<DataType> _candleType;

	private ExponentialMovingAverage _rsiAvg;

	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int AveragePeriod { get => _averagePeriod.Value; set => _averagePeriod.Value = value; }
	public decimal BuyThreshold { get => _buyThreshold.Value; set => _buyThreshold.Value = value; }
	public decimal SellThreshold { get => _sellThreshold.Value; set => _sellThreshold.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public AutoTradeWithRsiStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI calculation period", "Indicator");

		_averagePeriod = Param(nameof(AveragePeriod), 21)
			.SetGreaterThanZero()
			.SetDisplay("Average Period", "SMA period to smooth RSI", "Indicator");

		_buyThreshold = Param(nameof(BuyThreshold), 55m)
			.SetDisplay("Buy Threshold", "Averaged RSI above which to buy", "Rules");

		_sellThreshold = Param(nameof(SellThreshold), 45m)
			.SetDisplay("Sell Threshold", "Averaged RSI below which to sell", "Rules");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle data type", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	protected override void OnReseted()
	{
		base.OnReseted();
		_rsiAvg = null;
	}

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

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		_rsiAvg = new ExponentialMovingAverage { Length = AveragePeriod };
		Indicators.Add(_rsiAvg);

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(rsi, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);

			var area2 = CreateChartArea();
			if (area2 != null)
				DrawIndicator(area2, rsi);
		}
	}

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

		if (!rsiValue.IsFormed)
			return;

		var avgResult = _rsiAvg.Process(rsiValue);
		if (!avgResult.IsFormed)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var avgRsi = avgResult.GetValue<decimal>();

		if (avgRsi > BuyThreshold && Position <= 0)
			BuyMarket();
		else if (avgRsi < SellThreshold && Position >= 0)
			SellMarket();
	}
}