在 GitHub 上查看

RoBoost 策略

该策略是将原始 MQL4 智能交易系统 RoBoostj 转换为 C# 的实现。 它基于 RSI 指标结合简单的价格动量判断,在选定的 K 线周期上运行 (默认使用 1 小时 K 线)。

策略逻辑

  • 当上一根 K 线收盘价高于当前 K 线收盘价且 RSI 低于 RSI Down 阈值时, 策略开仓做空。
  • 当上一根 K 线收盘价低于或等于当前收盘价且 RSI 高于 RSI Up 阈值时, 策略开仓做多。
  • 持仓通过以下风险控制工具管理:
    • 固定的 Take ProfitStop Loss,单位为价格点;
    • 可选的跟踪止损,当盈利达到 Trail Start 距离后启用, 止损价与当前价格保持 Trail Step 的间隔。

参数

名称 说明
CandleType 用于计算的 K 线类型。
RsiPeriod RSI 指标的周期长度。
RsiUp 触发做多的 RSI 阈值。
RsiDown 触发做空的 RSI 阈值。
TakeProfit 从入场价起的止盈距离(点数)。
StopLoss 从入场价起的止损距离(点数)。
UseTrailing 是否启用跟踪止损。
TrailStart 达到该盈利距离后开始跟踪止损。
TrailStep 跟踪止损与当前价格之间保持的距离。

所有距离均以绝对价格单位表示,需要根据交易品种的最小变动单位进行调整。

使用方法

  1. 将策略添加到项目中或在 StockSharp Designer 中打开。
  2. 根据个人需求配置参数。
  3. 启动策略,系统会自动订阅所选 K 线并依据 RSI 值和收盘价管理交易。

该策略仅用于学习和演示,使用前请在历史数据上充分测试。

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>
/// RSI based strategy converted from the original RoBoostj MQL4 robot.
/// Opens long or short positions depending on price momentum and RSI values.
/// Includes optional trailing stop management.
/// </summary>
public class RoBoostStrategy : Strategy
{
	private readonly StrategyParam<decimal> _takeProfit;
	private readonly StrategyParam<decimal> _stopLoss;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _rsiUp;
	private readonly StrategyParam<int> _rsiDown;
	private readonly StrategyParam<bool> _useTrailing;
	private readonly StrategyParam<decimal> _trailStart;
	private readonly StrategyParam<decimal> _trailStep;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _entryPrice;
	private bool _isLong;
	private decimal _trailingStopPrice;
	private decimal _previousClose;
	private bool _isFirst = true;

	/// <summary>
	/// Take profit distance from entry price.
	/// </summary>
	public decimal TakeProfit
	{
		get => _takeProfit.Value;
		set => _takeProfit.Value = value;
	}

	/// <summary>
	/// Stop loss distance from entry price.
	/// </summary>
	public decimal StopLoss
	{
		get => _stopLoss.Value;
		set => _stopLoss.Value = value;
	}

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

	/// <summary>
	/// RSI threshold for long entries.
	/// </summary>
	public int RsiUp
	{
		get => _rsiUp.Value;
		set => _rsiUp.Value = value;
	}

	/// <summary>
	/// RSI threshold for short entries.
	/// </summary>
	public int RsiDown
	{
		get => _rsiDown.Value;
		set => _rsiDown.Value = value;
	}

	/// <summary>
	/// Enables trailing stop logic.
	/// </summary>
	public bool UseTrailing
	{
		get => _useTrailing.Value;
		set => _useTrailing.Value = value;
	}

	/// <summary>
	/// Distance at which trailing stop becomes active.
	/// </summary>
	public decimal TrailStart
	{
		get => _trailStart.Value;
		set => _trailStart.Value = value;
	}

	/// <summary>
	/// Distance maintained from current price when trailing stop is active.
	/// </summary>
	public decimal TrailStep
	{
		get => _trailStep.Value;
		set => _trailStep.Value = value;
	}

	/// <summary>
	/// Type of candles used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="RoBoostStrategy"/>.
	/// </summary>
	public RoBoostStrategy()
	{
		_takeProfit = Param(nameof(TakeProfit), 500m)
			.SetGreaterThanZero()
			.SetDisplay("Take Profit", "Take profit distance in points", "Risk Management");

		_stopLoss = Param(nameof(StopLoss), 1000m)
			.SetGreaterThanZero()
			.SetDisplay("Stop Loss", "Stop loss distance in points", "Risk Management");

		_rsiPeriod = Param(nameof(RsiPeriod), 7)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI calculation length", "Indicator")
			
			.SetOptimize(5, 20, 1);

		_rsiUp = Param(nameof(RsiUp), 50)
			.SetDisplay("RSI Up", "RSI threshold for longs", "Indicator")
			
			.SetOptimize(45, 70, 5);

		_rsiDown = Param(nameof(RsiDown), 50)
			.SetDisplay("RSI Down", "RSI threshold for shorts", "Indicator")
			
			.SetOptimize(30, 55, 5);

		_useTrailing = Param(nameof(UseTrailing), false)
			.SetDisplay("Use Trailing", "Enable trailing stop", "Risk Management");

		_trailStart = Param(nameof(TrailStart), 5m)
			.SetGreaterThanZero()
			.SetDisplay("Trail Start", "Profit distance to activate trailing", "Risk Management");

		_trailStep = Param(nameof(TrailStep), 2m)
			.SetGreaterThanZero()
			.SetDisplay("Trail Step", "Distance between price and trailing stop", "Risk Management");

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

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

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

		_entryPrice = 0m;
		_isLong = false;
		_trailingStopPrice = 0m;
		_previousClose = 0m;
		_isFirst = true;
	}

	/// <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);
			DrawIndicator(area, rsi);
			DrawOwnTrades(area);
		}
	}

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var currentClose = candle.ClosePrice;

		if (_isFirst)
		{
			_previousClose = currentClose;
			_isFirst = false;
			return;
		}

		if (Position == 0)
		{
			if (_previousClose > currentClose && rsiValue < RsiDown)
			{
				SellMarket();
				_entryPrice = currentClose;
				_isLong = false;
				_trailingStopPrice = 0m;
			}
			else if (_previousClose <= currentClose && rsiValue >= RsiUp)
			{
				BuyMarket();
				_entryPrice = currentClose;
				_isLong = true;
				_trailingStopPrice = 0m;
			}
		}
		else
		{
			ManagePosition(currentClose);
		}

		_previousClose = currentClose;
	}

	private void ManagePosition(decimal currentPrice)
	{
		if (_entryPrice == 0m)
			return;

		if (_isLong)
		{
			var profit = currentPrice - _entryPrice;
			if (profit >= TakeProfit || -profit >= StopLoss)
			{
				SellMarket();
				return;
			}

			if (UseTrailing)
			{
				if (profit >= TrailStart)
				{
					var newStop = currentPrice - TrailStep;
					if (_trailingStopPrice < newStop)
						_trailingStopPrice = newStop;
				}

				if (_trailingStopPrice != 0m && currentPrice <= _trailingStopPrice)
					SellMarket();
			}
		}
		else
		{
			var profit = _entryPrice - currentPrice;
			if (profit >= TakeProfit || -profit >= StopLoss)
			{
				BuyMarket();
				return;
			}

			if (UseTrailing)
			{
				if (profit >= TrailStart)
				{
					var newStop = currentPrice + TrailStep;
					if (_trailingStopPrice == 0m || _trailingStopPrice > newStop)
						_trailingStopPrice = newStop;
				}

				if (_trailingStopPrice != 0m && currentPrice >= _trailingStopPrice)
					BuyMarket();
			}
		}
	}
}