在 GitHub 上查看

JBrainUltraRSI 策略

该示例策略结合相对强弱指数 (RSI) 与 随机振荡指标(Stochastic) 来生成交易信号。 原始的 MetaTrader 专家顾问使用 JBrainTrendSig1UltraRSI 指标。本改写版本中使用随机振荡指标作为趋势过滤器,RSI 用于给出入场信号。

工作原理

  1. 指标
    • RSI:比较近期上涨和下跌幅度。当 RSI 向上穿过 50 表示看涨动能,向下穿过 50 表示看跌动能。
    • 随机振荡指标:比较收盘价与近期区间的位置。%K 与 %D 线的交叉确认趋势方向。
  2. 模式
    • JBrainSig1Filter – RSI 产生信号,随机振荡指标确认方向。
    • UltraRsiFilter – 随机振荡指标产生信号,RSI 进行过滤。
    • Composition – 只有当两个指标方向一致时才开仓。
  3. 交易规则
    • 出现买入信号且没有空头头寸时开多头。
    • 出现卖出信号且没有多头头寸时开空头。
    • 反向信号在允许的情况下关闭已有头寸。

参数

参数 说明
RsiPeriod RSI 计算周期
StochLength 随机指标 %K 周期
SignalLength 随机指标 %D 周期
Mode 指标组合模式
AllowLongEntry / AllowShortEntry 是否允许开多/开空
AllowLongExit / AllowShortExit 是否允许平多/平空
CandleType 使用的K线周期

说明

  • 策略使用 StockSharp 高级 API,通过 Bind / BindEx 处理指标。
  • 可以通过 StartProtection() 方法设置止盈止损保护。
  • 若图表区域可用,策略会绘制K线、指标以及自身交易。

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 combining RSI and Stochastic oscillator signals.
/// </summary>
public class JBrainUltraRsiStrategy : Strategy
{
        /// <summary>
        /// Combination modes for indicators.
        /// </summary>
        public enum AlgorithmModes
        {
                JBrainSig1Filter,
                UltraRsiFilter,
                Composition
        }

	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _stochLength;
	private readonly StrategyParam<int> _signalLength;
	private readonly StrategyParam<AlgorithmModes> _mode;
	private readonly StrategyParam<bool> _allowLongEntry;
	private readonly StrategyParam<bool> _allowShortEntry;
	private readonly StrategyParam<bool> _allowLongExit;
	private readonly StrategyParam<bool> _allowShortExit;
	private readonly StrategyParam<DataType> _candleType;
	
	private RelativeStrengthIndex _rsi;
	private StochasticOscillator _stochastic;
	private decimal? _prevRsi;
	private decimal? _prevK;
	private decimal? _prevD;
	
	/// <summary>
	/// RSI calculation period.
	/// </summary>
	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}
	
	/// <summary>
	/// Stochastic %K period.
	/// </summary>
	public int StochLength
	{
		get => _stochLength.Value;
		set => _stochLength.Value = value;
	}
	
	/// <summary>
	/// Stochastic %D period.
	/// </summary>
	public int SignalLength
	{
		get => _signalLength.Value;
		set => _signalLength.Value = value;
	}
	
	/// <summary>
	/// Mode combining indicators.
	/// </summary>
	public AlgorithmModes Mode
	{
		get => _mode.Value;
		set => _mode.Value = value;
	}
	
	/// <summary>
	/// Permission to open long positions.
	/// </summary>
	public bool AllowLongEntry
	{
		get => _allowLongEntry.Value;
		set => _allowLongEntry.Value = value;
	}
	
	/// <summary>
	/// Permission to open short positions.
	/// </summary>
	public bool AllowShortEntry
	{
		get => _allowShortEntry.Value;
		set => _allowShortEntry.Value = value;
	}
	
	/// <summary>
	/// Permission to close long positions.
	/// </summary>
	public bool AllowLongExit
	{
		get => _allowLongExit.Value;
		set => _allowLongExit.Value = value;
	}
	
	/// <summary>
	/// Permission to close short positions.
	/// </summary>
	public bool AllowShortExit
	{
		get => _allowShortExit.Value;
		set => _allowShortExit.Value = value;
	}
	
	/// <summary>
	/// Candle type to process.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}
	
	/// <summary>
	/// Initializes a new instance of <see cref="JBrainUltraRsiStrategy"/>.
	/// </summary>
	public JBrainUltraRsiStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 13)
		.SetGreaterThanZero()
		.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
		;
		
		_stochLength = Param(nameof(StochLength), 9)
		.SetGreaterThanZero()
		.SetDisplay("Stochastic %K", "Period for %K line", "Indicators")
		;
		
		_signalLength = Param(nameof(SignalLength), 3)
		.SetGreaterThanZero()
		.SetDisplay("Stochastic %D", "Period for %D line", "Indicators")
		;
		
		_mode = Param(nameof(Mode), AlgorithmModes.Composition)
		.SetDisplay("Mode", "Algorithm to enter the market", "General");
		
		_allowLongEntry = Param(nameof(AllowLongEntry), true)
		.SetDisplay("Allow Long Entry", "Permission to open long positions", "Trading");
		
		_allowShortEntry = Param(nameof(AllowShortEntry), true)
		.SetDisplay("Allow Short Entry", "Permission to open short positions", "Trading");
		
		_allowLongExit = Param(nameof(AllowLongExit), true)
		.SetDisplay("Allow Long Exit", "Permission to close long positions", "Trading");
		
		_allowShortExit = Param(nameof(AllowShortExit), true)
		.SetDisplay("Allow Short Exit", "Permission to close short positions", "Trading");
		
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
		.SetDisplay("Candle Type", "Type of candles", "General");
	}
	
	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}
	
	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		
		_rsi = default;
		_stochastic = default;
		_prevRsi = default;
		_prevK = default;
		_prevD = default;
	}
	
	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		
		StartProtection(null, null);
		
		_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		_stochastic = new StochasticOscillator
		{
			K = { Length = StochLength },
			D = { Length = SignalLength },
		};
		
		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(_stochastic, ProcessCandle)
			.Start();

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

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

		// process RSI manually
		var rsiResult = _rsi.Process(candle.ClosePrice, candle.OpenTime, true);
		if (!rsiResult.IsFormed)
			return;

		var rsi = rsiResult.ToDecimal();

		var stoch = (StochasticOscillatorValue)stochValue;
		if (stoch.K is not decimal k || stoch.D is not decimal d)
		{
			_prevRsi = rsi;
			return;
		}

		var rsiUp = _prevRsi is decimal pr && pr <= 50m && rsi > 50m;
		var rsiDown = _prevRsi is decimal pr2 && pr2 >= 50m && rsi < 50m;
		var stochUp = _prevK is decimal pk && _prevD is decimal pd && pk <= pd && k > d;
		var stochDown = _prevK is decimal pk2 && _prevD is decimal pd2 && pk2 >= pd2 && k < d;

		var buySignal = false;
		var sellSignal = false;

		switch (Mode)
		{
			case AlgorithmModes.JBrainSig1Filter:
				buySignal = rsiUp && k > d;
				sellSignal = rsiDown && k < d;
				break;
			case AlgorithmModes.UltraRsiFilter:
				buySignal = stochUp && rsi > 50m;
				sellSignal = stochDown && rsi < 50m;
				break;
			case AlgorithmModes.Composition:
				buySignal = rsiUp && stochUp;
				sellSignal = rsiDown && stochDown;
				break;
		}

		if (buySignal)
		{
			if (Position < 0 && AllowShortExit)
				BuyMarket();
			if (AllowLongEntry && Position <= 0)
				BuyMarket();
		}
		else if (sellSignal)
		{
			if (Position > 0 && AllowLongExit)
				SellMarket();
			if (AllowShortEntry && Position >= 0)
				SellMarket();
		}

		_prevRsi = rsi;
		_prevK = k;
		_prevD = d;
	}
}