在 GitHub 上查看

技术评级多时间框架资产策略

该策略综合多个时间框架的技术评级。 在1小时、4小时和日线级别上比较价格与简单移动平均线及RSI阈值。 当综合评级为正时开多单,为负时开空单。

细节

  • 入场:平均评级 > 0 买入;平均评级 < 0 卖出。
  • 指标:SMA、RSI。
  • 时间框架:1小时、4小时、1天。
  • 类型:趋势跟随。
  • 止损:无。
  • 方向:多头和空头。
  • 风险:中等。
  • 复杂度:中等。
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Aggregates SMA/RSI technical ratings from 1h, 4h and daily time frames.
/// </summary>
public class TechnicalRatingsOnMultiFramesAssetsStrategy : Strategy
{
	private readonly StrategyParam<int> _smaPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _bullRsi;
	private readonly StrategyParam<decimal> _bearRsi;
	private readonly StrategyParam<DataType> _hourly;
	private readonly StrategyParam<DataType> _fourHourly;
	private readonly StrategyParam<DataType> _daily;

	private readonly Dictionary<string, decimal> _ratings = [];

	public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public decimal BullRsiThreshold { get => _bullRsi.Value; set => _bullRsi.Value = value; }
	public decimal BearRsiThreshold { get => _bearRsi.Value; set => _bearRsi.Value = value; }
	public DataType HourlyCandleType { get => _hourly.Value; set => _hourly.Value = value; }
	public DataType FourHourCandleType { get => _fourHourly.Value; set => _fourHourly.Value = value; }
	public DataType DailyCandleType { get => _daily.Value; set => _daily.Value = value; }

	public TechnicalRatingsOnMultiFramesAssetsStrategy()
	{
		_smaPeriod = Param(nameof(SmaPeriod), 20).SetGreaterThanZero()
			.SetDisplay("SMA Period", "SMA period used by each rating.", "Indicators");
		_rsiPeriod = Param(nameof(RsiPeriod), 14).SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period used by each rating.", "Indicators");
		_bullRsi = Param(nameof(BullRsiThreshold), 55m)
			.SetDisplay("Bull RSI", "RSI threshold contributing a bullish vote.", "Indicators");
		_bearRsi = Param(nameof(BearRsiThreshold), 45m)
			.SetDisplay("Bear RSI", "RSI threshold contributing a bearish vote.", "Indicators");
		_hourly = Param(nameof(HourlyCandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("1h", "Primary technical-rating timeframe.", "Timeframes");
		_fourHourly = Param(nameof(FourHourCandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("4h", "Confirmation technical-rating timeframe.", "Timeframes");
		_daily = Param(nameof(DailyCandleType), TimeSpan.FromDays(1).TimeFrame())
			.SetDisplay("1d", "Daily technical-rating timeframe.", "Timeframes");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_ratings.Clear();
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		StartRating("1h", HourlyCandleType);
		StartRating("4h", FourHourCandleType);
		StartRating("1d", DailyCandleType);
	}

	private void StartRating(string key, DataType candleType)
	{
		var sma = new SimpleMovingAverage { Length = SmaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var subscription = SubscribeCandles(candleType);
		subscription.Bind(sma, rsi, (candle, smaValue, rsiValue) =>
		{
			if (candle.State != CandleStates.Finished || !sma.IsFormed || !rsi.IsFormed)
				return;

			var maVote = candle.ClosePrice > smaValue ? 1m : candle.ClosePrice < smaValue ? -1m : 0m;
			var rsiVote = rsiValue >= BullRsiThreshold ? 1m : rsiValue <= BearRsiThreshold ? -1m : 0m;
			_ratings[key] = (maVote + rsiVote) / 2m;
			Evaluate();
		}).Start();

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

	private void Evaluate()
	{
		if (_ratings.Count < 3)
			return;

		var average = (_ratings["1h"] + _ratings["4h"] + _ratings["1d"]) / 3m;

		if (average > 0m && Position <= 0)
			BuyMarket(Volume + Math.Abs(Position));
		else if (average < 0m && Position >= 0)
			SellMarket(Volume + Math.Abs(Position));
	}
}