Открыть на GitHub

Стратегия «Сборщик статистики спреда»

Общее описание

Стратегия Spread Data Collector — это перенос на StockSharp утилиты MetaTrader 5 «Spread data collector» (MQL №33314). Оригинальный советник не совершает сделок: он просто слушает поток котировок Bid/Ask и подсчитывает, сколько тиков попадает в заранее заданные диапазоны спреда. При смене торгового года или остановке советник выводит суммарную статистику. Вариант на C# полностью повторяет логику, используя высокоуровневый API SubscribeLevel1() и делая границы диапазонов настраиваемыми.

Как работает

  • При старте стратегия подписывается на обновления уровня 1 (Bid/Ask) по основному инструменту Security.
  • Как только доступны обе цены, рассчитывается спред и сравнивается с границами, пересчитанными из пунктов в абсолютную величину через Security.PriceStep.
  • Ведётся шесть счётчиков:
    1. Спред строго меньше первой границы.
    2. Спред между первой и второй границами.
    3. Спред между второй и третьей границами.
    4. Спред между третьей и четвёртой границами.
    5. Спред между четвёртой и пятой границами.
    6. Спред выше пятой границы.
  • Год определяется по времени биржи в сообщении Level1ChangeMessage.ServerTime. При смене года стратегия печатает отчёт за предыдущий и сбрасывает счётчики.
  • При остановке стратегии выводится отчёт за текущий год, после чего работа завершается.

Таким образом, стратегия сохраняет «безопасный» характер оригинальной утилиты: никакие заявки не отправляются, можно спокойно анализировать качество ликвидности и динамику спредов.

Параметры

Все настройки задаются в пунктах (по терминологии MetaTrader). Фактическая величина пересчитывается как пункты × Security.PriceStep.

Параметр Значение по умолчанию Назначение
FirstBucketPoints 10 Верхняя граница первого диапазона. Спреды строго ниже неё увеличивают первый счётчик.
SecondBucketPoints 20 Верхняя граница второго диапазона. Спреды из интервала [FirstBucketPoints, SecondBucketPoints) фиксируются во втором счётчике.
ThirdBucketPoints 30 Верхняя граница третьего диапазона. Спреды из интервала [SecondBucketPoints, ThirdBucketPoints) добавляются к третьему счётчику.
FourthBucketPoints 40 Верхняя граница четвёртого диапазона. Спреды из интервала [ThirdBucketPoints, FourthBucketPoints) идут в четвёртый счётчик.
FifthBucketPoints 50 Верхняя граница пятого диапазона. Спреды из интервала [FourthBucketPoints, FifthBucketPoints) увеличивают пятый счётчик.

Все пороги должны строго возрастать. Если Security.PriceStep не задан или неположителен, стратегия при запуске выбросит исключение, чтобы пользователь сразу заметил ошибку настройки.

Журнал и формат отчёта

Статистика выводится через AddInfoLog в формате:

Year=2024 Spread<=10pts=15342 Spread_10_20pts=2841 Spread_20_30pts=912 ... Spread>50pts=37

Формат полностью повторяет вывод функции Print в MetaTrader, поэтому можно легко сравнивать результаты двух платформ. Используйте просмотрщик логов StockSharp или перенаправьте вывод в файл.

Порядок использования

  1. Укажите инструмент в Strategy.Security и убедитесь, что его PriceStep соответствует понятию «пункт» в MetaTrader (для большинства Forex-пар это 0.0001).
  2. При необходимости скорректируйте границы диапазонов, сохранив строгий рост значений.
  3. Запустите стратегию и оставьте её работать: никаких заявок она не выставляет.
  4. Анализируйте годовые отчёты, чтобы оценить динамику спреда и качество ликвидности.

Стратегию можно запускать параллельно с боевыми торговыми системами. Она почти не потребляет ресурсов и помогает собирать долгосрочную статистику для риск-менеджмента и контроля поставщиков ликвидности.

namespace StockSharp.Samples.Strategies;

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

/// <summary>
/// Spread Data Collector strategy: RSI mean reversion.
/// Buys when RSI crosses below oversold, sells when RSI crosses above overbought.
/// </summary>
public class SpreadDataCollectorStrategy : 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 SpreadDataCollectorStrategy()
	{
		_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", "Levels");
		_overbought = Param(nameof(Overbought), 70m)
			.SetDisplay("Overbought", "RSI overbought level", "Levels");
	}

	/// <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)
		{
			if (_prevRsi >= Oversold && rsiValue < Oversold && Position <= 0)
				BuyMarket();
			else if (_prevRsi <= Overbought && rsiValue > Overbought && Position >= 0)
				SellMarket();
		}

		_prevRsi = rsiValue;
		_hasPrev = true;
	}
}