Открыть на GitHub

Стратегия Alerting System Threshold

Alerting System Threshold Strategy — порт советника MetaTrader 5 «AlertingSystem» (каталог MQL 31843) на платформу StockSharp. В оригинале робот рисует две горизонтальные линии и воспроизводит звуковой сигнал, когда бид поднимается выше верхней линии или аск опускается ниже нижней. Конверсия на C# сохраняет ту же идею оповещений, но опирается на высокоуровневый API StockSharp и журналы стратегий.

Ключевая логика

  • Подписка на поток Level 1 (лучшие цены покупки и продажи).
  • Однократное срабатывание алерта при достижении бидом верхнего порога.
  • Однократное срабатывание алерта при падении аска ниже нижнего порога.
  • Автоматический сброс флагов, когда цены возвращаются внутрь диапазона.

В отличие от MQL-версии, которая проигрывала звук на каждом тике, реализация на StockSharp записывает одно информационное сообщение на событие. Это избавляет от засорения лога и при этом уведомляет трейдера о касании уровней.

Параметры

Параметр Описание Значение по умолчанию
UpperPrice Уровень бида, при котором активируется верхний алерт. 0 отключает. 0
LowerPrice Уровень аска, при котором активируется нижний алерт. 0 отключает. 0

Оба параметра созданы через StrategyParam<decimal>, поддерживают оптимизацию и изменение на лету. Во время работы можно двигать уровни так же, как перетаскиваются горизонтальные линии в MetaTrader.

Последовательность работы

  1. При запуске стратегия вызывает SubscribeLevel1().Bind(ProcessLevel1).Start().
  2. Сообщения Level1ChangeMessage обновляют кеш текущих bid/ask.
  3. После каждого обновления проверяются условия алертов:
    • Верхний — срабатывает один раз при BestBid >= UpperPrice после возвращения ниже уровня.
    • Нижний — срабатывает один раз при BestAsk <= LowerPrice после возврата выше уровня.
  4. Флаги сбрасываются автоматически, когда цена выходит из режима алерта.

Журналы и уведомления

Сообщения пишутся через AddInfoLog и содержат текущие цены и настроенный уровень. При необходимости можно подписаться на события логов стратегии и реализовать собственное оповещение — звук, e-mail, чат-бот.

Практические советы

  • Установите только нужный порог, второй оставьте 0, если он не требуется.
  • Для имитации звука подключите обработчик события Info и воспроизведите аудио в хост-приложении.
  • Стратегия не торгует и не открывает позиции, поэтому защита (StartProtection) не используется.

Отличия от оригинала

  • Вместо графических объектов используются данные Level 1.
  • Алерты выполняются по принципу «один раз за пробой», чтобы лог оставался читабельным.
  • Условия срабатывания, параметры и пороги полностью соответствуют MQL-сценарию.

Файлы

  • CS/AlertingSystemStrategy.cs — реализация стратегии.
  • README.md — описание на английском языке.
  • README_zh.md — перевод на китайский язык.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Alerting System Threshold strategy: RSI threshold crossover.
/// Buys when RSI crosses above oversold level, sells when crosses below overbought level.
/// </summary>
public class AlertingSystemThresholdStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<decimal> _overbought;

	private decimal _prevRsi;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }

	public AlertingSystemThresholdStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
		_oversold = Param(nameof(Oversold), 30m)
			.SetDisplay("Oversold", "RSI oversold level", "Signals");
		_overbought = Param(nameof(Overbought), 70m)
			.SetDisplay("Overbought", "RSI overbought level", "Signals");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevRsi = 0;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrev = false;
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ProcessCandle).Start();
	}

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

		if (_hasPrev)
		{
			// Buy on cross above oversold
			if (_prevRsi < Oversold && rsiValue >= Oversold && Position <= 0)
				BuyMarket();
			// Sell on cross below overbought
			else if (_prevRsi > Overbought && rsiValue <= Overbought && Position >= 0)
				SellMarket();
		}

		_prevRsi = rsiValue;
		_hasPrev = true;
	}
}