Открыть на GitHub

Стратегия RoBoost

Данная стратегия представляет собой адаптацию советника MQL4 RoBoostj на C#. Алгоритм торгует одним инструментом, используя сигналы индикатора RSI в сочетании с простой оценкой ценового импульса. Расчёты выполняются на свечах выбранного типа (по умолчанию часовые свечи).

Логика

  • Если цена закрытия предыдущей свечи выше, чем текущей, и значение RSI опускается ниже порога RSI Down, открывается короткая позиция.
  • Если цена закрытия предыдущей свечи ниже либо равна текущей, а RSI поднимается выше порога RSI Up, открывается длинная позиция.
  • Открытые позиции сопровождаются следующими механизмами управления риском:
    • фиксированные уровни Take Profit и Stop Loss в ценовых пунктах;
    • опциональный трейлинг-стоп, который активируется после прохождения ценой расстояния Trail Start и сопровождает цену с шагом Trail Step.

Параметры

Имя Описание
CandleType Серия свечей для расчётов.
RsiPeriod Период индикатора RSI.
RsiUp Порог RSI для открытия длинных позиций.
RsiDown Порог RSI для открытия коротких позиций.
TakeProfit Расстояние до take profit от цены входа (пункты).
StopLoss Расстояние до stop loss от цены входа (пункты).
UseTrailing Включение логики трейлинг-стопа.
TrailStart Расстояние в пунктах, после которого активируется трейлинг.
TrailStep Расстояние в пунктах между ценой и уровнем трейлинг-стопа.

Все расстояния задаются в абсолютных ценовых единицах и требуют подбора в зависимости от шага цены конкретного инструмента.

Использование

  1. Добавьте стратегию в проект или откройте её в StockSharp Designer.
  2. Настройте параметры в соответствии с предпочтениями.
  3. Запустите стратегию – она автоматически подпишется на выбранную серию свечей и будет управлять сделками на основе значений 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();
			}
		}
	}
}