在 GitHub 上查看

Supertrend RSI Divergence

Supertrend RSI Divergence 策略基于 that uses Supertrend indicator along with RSI divergence to identify trading opportunities。

测试表明年均收益约为 67%,该策略在股票市场表现最佳。

当 Divergence confirms divergence setups 在日内(15m)数据上得到确认时触发信号,适合积极交易者。

止损依赖于 ATR 倍数以及 SupertrendPeriod, SupertrendMultiplier 等参数,可根据需要调整以平衡风险与收益。

详情

  • 入场条件:参见指标条件实现.
  • 多空方向:双向.
  • 退出条件:反向信号或止损逻辑.
  • 止损:是,基于指标计算.
  • 默认值:
    • SupertrendPeriod = 10
    • SupertrendMultiplier = 3.0m
    • RsiPeriod = 14
    • CandleType = TimeSpan.FromMinutes(15).TimeFrame()
  • 过滤器:
    • 分类: 趋势跟随
    • 方向: 双向
    • 指标: Divergence
    • 止损: 是
    • 复杂度: 中等
    • 时间框架: 日内 (15m)
    • 季节性: 否
    • 神经网络: 否
    • 背离: 是
    • 风险等级: 中等
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 uses Supertrend indicator along with RSI divergence to identify trading opportunities.
/// </summary>
public class SupertrendRsiDivergenceStrategy : Strategy
{
	private readonly StrategyParam<int> _supertrendPeriod;
	private readonly StrategyParam<decimal> _supertrendMultiplier;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private SuperTrend _supertrend;
	private RelativeStrengthIndex _rsi;

	// Data for divergence detection
	private readonly SynchronizedList<decimal> _prices = [];
	private readonly SynchronizedList<decimal> _rsiValues = [];

	// Supertrend state tracking
	private decimal _supertrendValue;
	private TrendDirections _trendDirection = TrendDirections.None;

	/// <summary>
	/// Supertrend period.
	/// </summary>
	public int SupertrendPeriod
	{
		get => _supertrendPeriod.Value;
		set => _supertrendPeriod.Value = value;
	}

	/// <summary>
	/// Supertrend multiplier.
	/// </summary>
	public decimal SupertrendMultiplier
	{
		get => _supertrendMultiplier.Value;
		set => _supertrendMultiplier.Value = value;
	}

	/// <summary>
	/// RSI period.
	/// </summary>
	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	/// <summary>
	/// Candle type to use for the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="SupertrendRsiDivergenceStrategy"/>.
	/// </summary>
	public SupertrendRsiDivergenceStrategy()
	{
		_supertrendPeriod = Param(nameof(SupertrendPeriod), 10)
		.SetDisplay("Supertrend Period", "Supertrend ATR period", "Supertrend")
		
		.SetOptimize(5, 20, 1);

		_supertrendMultiplier = Param(nameof(SupertrendMultiplier), 3.0m)
		.SetDisplay("Supertrend Multiplier", "Supertrend ATR multiplier", "Supertrend")
		
		.SetOptimize(2.0m, 5.0m, 0.5m);

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
		.SetDisplay("RSI Period", "RSI period for divergence detection", "RSI")
		
		.SetOptimize(8, 20, 2);

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
		.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

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

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

		_prices.Clear();
		_rsiValues.Clear();
_trendDirection = TrendDirections.None;
		_supertrendValue = 0;
	}

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

		_supertrend = new SuperTrend
		{
			Length = SupertrendPeriod,
			Multiplier = SupertrendMultiplier
		};

		_rsi = new RelativeStrengthIndex
		{
			Length = RsiPeriod
		};

		var subscription = SubscribeCandles(CandleType);

		subscription
			.Bind(_supertrend, _rsi, ProcessCandle)
			.Start();

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent));

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

	private void ProcessCandle(ICandleMessage candle, decimal supertrendValue, decimal rsiValue)
	{
		// Skip unfinished candles
		if (candle.State != CandleStates.Finished)
		return;


		// Extract values from indicators
		_supertrendValue = supertrendValue;
		decimal rsi = rsiValue;

		// Store values for divergence calculation
		_prices.Add(candle.ClosePrice);
		_rsiValues.Add(rsi);

		// Keep reasonable history
		while (_prices.Count > 50)
		{
			_prices.RemoveAt(0);
			_rsiValues.RemoveAt(0);
		}

		// Determine Supertrend trend direction
TrendDirections previousDirection = _trendDirection;

		if (candle.ClosePrice > _supertrendValue)
_trendDirection = TrendDirections.Up;
		else if (candle.ClosePrice < _supertrendValue)
_trendDirection = TrendDirections.Down;

		// Check for trend direction change
bool trendDirectionChanged = previousDirection != TrendDirections.None && previousDirection != _trendDirection;

		// Check for divergence
		bool bullishDivergence = CheckBullishDivergence();
		bool bearishDivergence = CheckBearishDivergence();

		if (Position != 0)
			return;

		// Bullish setup - price above Supertrend with RSI not overbought
		if (candle.ClosePrice > _supertrendValue && rsi < 60m)
			BuyMarket();
		// Bearish setup - price below Supertrend with RSI not oversold
		else if (candle.ClosePrice < _supertrendValue && rsi > 40m)
			SellMarket();
	}

	private bool CheckBullishDivergence()
	{
		// Need at least a few candles for divergence check
		if (_prices.Count < 5 || _rsiValues.Count < 5)
		return false;

		// Check for bullish divergence: price making lower lows while RSI making higher lows
		// Look at the last 5 candles for a simple check
		decimal currentPrice = _prices[_prices.Count - 1];
		decimal previousPrice = _prices[_prices.Count - 2];

		decimal currentRsi = _rsiValues[_rsiValues.Count - 1];
		decimal previousRsi = _rsiValues[_rsiValues.Count - 2];

		// Bullish divergence: price lower but RSI higher
		bool divergence = currentPrice < previousPrice && currentRsi > previousRsi;

		if (divergence)
		{
			LogInfo($"Bullish Divergence Detected: Price {previousPrice:F2}->{currentPrice:F2}, RSI {previousRsi:F2}->{currentRsi:F2}");
		}

		return divergence;
	}

	private bool CheckBearishDivergence()
	{
		// Need at least a few candles for divergence check
		if (_prices.Count < 5 || _rsiValues.Count < 5)
		return false;

		// Check for bearish divergence: price making higher highs while RSI making lower highs
		// Look at the last 5 candles for a simple check
		decimal currentPrice = _prices[_prices.Count - 1];
		decimal previousPrice = _prices[_prices.Count - 2];

		decimal currentRsi = _rsiValues[_rsiValues.Count - 1];
		decimal previousRsi = _rsiValues[_rsiValues.Count - 2];

		// Bearish divergence: price higher but RSI lower
		bool divergence = currentPrice > previousPrice && currentRsi < previousRsi;

		if (divergence)
		{
			LogInfo($"Bearish Divergence Detected: Price {previousPrice:F2}->{currentPrice:F2}, RSI {previousRsi:F2}->{currentRsi:F2}");
		}

		return divergence;
	}

	// Trend direction enum for tracking Supertrend state
	private enum TrendDirections
	{
		None,
		Up,
		Down
	}
}