在 GitHub 上查看

cm RSI 策略

概述

该策略是 MetaTrader 4 专家顾问 "cm_RSI" 的移植版本。它利用相对强弱指数 (RSI) 来捕捉动量反转。

算法使用K线开盘价计算 RSI。当 RSI 在低于买入阈值后向上突破该阈值时,开多头仓位;当 RSI 在高于卖出阈值后向下跌破该阈值时,开空头仓位。每笔交易均使用固定的止盈和止损点数进行保护。

策略逻辑

  1. 使用用户设定的周期,通过开盘价计算 RSI。
  2. 如果前一个 RSI 值低于买入水平且当前值从下向上突破该水平,则按市价买入。
  3. 如果前一个 RSI 值高于卖出水平且当前值从上向下跌破该水平,则按市价卖出。
  4. 每笔交易使用相同的成交量,并附带止损和止盈。

参数

名称 说明
RsiPeriod RSI 计算周期。
BuyLevel 触发多头信号的 RSI 水平。
SellLevel 触发空头信号的 RSI 水平。
TakeProfit 绝对价格点数的止盈。
StopLoss 绝对价格点数的止损。
OrderVolume 每笔交易的成交量。
CandleType 用于计算的K线类型。

备注

  • 仅处理已完成的K线。
  • 任意时刻只持有一个仓位。
  • 通过 StartProtection 自动管理止损和止盈。
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>
/// Strategy based on RSI cross signals from the original cm_RSI expert.
/// </summary>
public class CmRsiStrategy : Strategy
{
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _buyLevel;
	private readonly StrategyParam<decimal> _sellLevel;
	private readonly StrategyParam<int> _takeProfit;
	private readonly StrategyParam<int> _stopLoss;
	private readonly StrategyParam<decimal> _orderVolume;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevRsi;
	private bool _isFirst = true;

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

	/// <summary>
	/// RSI level to trigger long entries.
	/// </summary>
	public decimal BuyLevel { get => _buyLevel.Value; set => _buyLevel.Value = value; }

	/// <summary>
	/// RSI level to trigger short entries.
	/// </summary>
	public decimal SellLevel { get => _sellLevel.Value; set => _sellLevel.Value = value; }

	/// <summary>
	/// Take profit in price points.
	/// </summary>
	public int TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }

	/// <summary>
	/// Stop loss in price points.
	/// </summary>
	public int StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }

	/// <summary>
	/// Volume applied to each trade.
	/// </summary>
	public decimal OrderVolume { get => _orderVolume.Value; set => _orderVolume.Value = value; }

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

	/// <summary>
	/// Initialize <see cref="CmRsiStrategy"/>.
	/// </summary>
	public CmRsiStrategy()
	{
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
			
			.SetOptimize(7, 21, 1);

		_buyLevel = Param(nameof(BuyLevel), 30m)
			.SetDisplay("Buy Level", "RSI level to enter long", "Indicators")
			
			.SetOptimize(10m, 40m, 5m);

		_sellLevel = Param(nameof(SellLevel), 70m)
			.SetDisplay("Sell Level", "RSI level to enter short", "Indicators")
			
			.SetOptimize(60m, 90m, 5m);

		_takeProfit = Param(nameof(TakeProfit), 200)
			.SetDisplay("Take Profit", "Take profit in price points", "Risk Management")
			
			.SetOptimize(100, 400, 50);

		_stopLoss = Param(nameof(StopLoss), 100)
			.SetDisplay("Stop Loss", "Stop loss in price points", "Risk Management")
			
			.SetOptimize(50, 200, 50);

		_orderVolume = Param(nameof(OrderVolume), 0.1m)
			.SetGreaterThanZero()
			.SetDisplay("Order Volume", "Volume of each trade", "General")
			
			.SetOptimize(0.1m, 1m, 0.1m);

		_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();
		_prevRsi = 0;
		_isFirst = true;
	}

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

		_isFirst = true;
		_prevRsi = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevRsi = rsiValue;
			_isFirst = false;
			return;
		}

		if (_isFirst)
		{
			_prevRsi = rsiValue;
			_isFirst = false;
			return;
		}

		// Open long when RSI crosses above buy level
		if (_prevRsi < BuyLevel && rsiValue > BuyLevel && Position <= 0)
			BuyMarket();

		// Open short when RSI crosses below sell level
		if (_prevRsi > SellLevel && rsiValue < SellLevel && Position >= 0)
			SellMarket();

		_prevRsi = rsiValue;
	}
}