Ver en GitHub

Experto en Divergencia

Estrategia que opera divergencias de precio con RSI. Detecta divergencia alcista cuando el precio forma un mínimo más bajo pero el RSI forma un mínimo más alto, y divergencia bajista cuando el precio forma un máximo más alto pero el RSI forma un máximo más bajo. Entra en posiciones largas o cortas en consecuencia y utiliza un stop loss porcentual.

Detalles

  • Criterios de entrada:
    • Largo: el precio forma un nuevo mínimo y el RSI forma un mínimo más alto (divergencia alcista)
    • Corto: el precio forma un nuevo máximo y el RSI forma un máximo más bajo (divergencia bajista)
  • Largo/Corto: Ambos
  • Criterios de salida:
    • Largo: el precio alcanza el stop loss o aparece divergencia bajista
    • Corto: el precio alcanza el stop loss o aparece divergencia alcista
  • Stops: Porcentaje desde el precio de entrada
  • Valores predeterminados:
    • RsiPeriod = 14
    • StopLossPercent = 2m
    • CandleType = TimeSpan.FromMinutes(5).TimeFrame()
  • Filtros:
    • Categoría: Divergencia
    • Dirección: Ambos
    • Indicadores: RSI
    • Stops: Sí
    • Complejidad: Básico
    • Marco temporal: Medio plazo
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: Sí
    • Nivel de riesgo: Medio
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 trading RSI price divergences.
/// </summary>
public class DivergenceExpertStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _stopLossPercent;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<DateTimeOffset> _startDate;
	private readonly StrategyParam<DateTimeOffset> _endDate;

	private decimal _entryPrice;
	private decimal _stopPrice;
	private decimal _lastPriceHigh;
	private decimal _lastPriceLow;
	private decimal _lastRsiHigh;
	private decimal _lastRsiLow;

	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	public decimal StopLossPercent
	{
		get => _stopLossPercent.Value;
		set => _stopLossPercent.Value = value;
	}

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

	public DateTimeOffset StartDate
	{
		get => _startDate.Value;
		set => _startDate.Value = value;
	}

	public DateTimeOffset EndDate
	{
		get => _endDate.Value;
		set => _endDate.Value = value;
	}

	public DivergenceExpertStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI calculation period", "Parameters")
			
			.SetOptimize(5, 30, 5);

		_stopLossPercent = Param(nameof(StopLossPercent), 2m)
			.SetDisplay("Stop Loss (%)", "Max risk per trade in percent", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");

		_startDate = Param(nameof(StartDate), new DateTimeOffset(new DateTime(2017, 1, 1), TimeSpan.Zero))
			.SetDisplay("Start Date", "Backtest start date", "General");

		_endDate = Param(nameof(EndDate), new DateTimeOffset(new DateTime(2024, 7, 1), TimeSpan.Zero))
			.SetDisplay("End Date", "Backtest end date", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_entryPrice = default;
		_stopPrice = default;
		_lastPriceHigh = default;
		_lastPriceLow = default;
		_lastRsiHigh = default;
		_lastRsiLow = default;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var 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 rsi)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var time = candle.OpenTime;
		var inRange = time >= StartDate && time <= EndDate;

		if (!inRange)
		{
			if (Position != 0)
				if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();

			return;
		}


		var close = candle.ClosePrice;

		// Track new highs for bearish divergence detection
		if (candle.HighPrice > _lastPriceHigh)
		{
			if (_lastPriceHigh != 0m && rsi < _lastRsiHigh && Position >= 0)
			{
				if (Position > 0)
					if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();

				SellMarket();
				_entryPrice = close;
				_stopPrice = _entryPrice * (1m + StopLossPercent / 100m);
			}

			_lastPriceHigh = candle.HighPrice;
			_lastRsiHigh = rsi;
		}

		// Track new lows for bullish divergence detection
		if (_lastPriceLow == 0m || candle.LowPrice < _lastPriceLow)
		{
			if (_lastPriceLow != 0m && rsi > _lastRsiLow && Position <= 0)
			{
				if (Position < 0)
					if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();

				BuyMarket();
				_entryPrice = close;
				_stopPrice = _entryPrice * (1m - StopLossPercent / 100m);
			}

			_lastPriceLow = candle.LowPrice;
			_lastRsiLow = rsi;
		}

		if (Position > 0 && candle.LowPrice <= _stopPrice)
			if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
		else if (Position < 0 && candle.HighPrice >= _stopPrice)
			if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
	}
}