Открыть на GitHub

Стратегия On Tick Market Watch

Обзор

On Tick Market Watch Strategy переносит логику скрипта MetaTrader scOnTickMarketWatch.mq5. Оригинальный скрипт непрерывно перебирает список Market Watch и при появлении нового тика отправляет пользовательское событие, печатая текущий бид и спред. В версии для StockSharp эта функциональность реализована как высокоуровневая стратегия, которая подписывается на данные Level1 и выводит информацию о тикax через логгер стратегии.

Стратегия не выполняет торговых операций и предназначена для диагностики/мониторинга входящего потока котировок по нескольким инструментам одного соединения. Event-driven модель StockSharp избавляет от необходимости вручную организовывать цикл и задержки, как в MQL.

Ключевые возможности

  • Отслеживание основного инструмента стратегии и дополнительных инструментов, перечисленных через запятую.
  • Подписка на Level1 по каждому инструменту для получения обновлений бид/аск.
  • Расчёт спреда (ask − bid), если обе цены доступны, и подробный вывод данных в лог.
  • Сохранение индекса инструмента в соответствии с порядком, указанным пользователем, что имитирует Market Watch.
  • Предупреждения, если указанный тикер отсутствует в текущем SecurityProvider.

Параметры

Имя Тип Значение по умолчанию Описание
SymbolsList string "" Список дополнительных инструментов через запятую (например, AAPL@NASDAQ,MSFT@NASDAQ). Инструменты добавляются после основного Strategy.Security и должны существовать в SecurityProvider.

Принцип работы

  1. В OnStarted стратегия формирует список наблюдаемых инструментов: первым идёт Strategy.Security, затем тикеры из SymbolsList.
  2. Для каждого инструмента вызывается SubscribeLevel1, и привязывается обработчик Level1ChangeMessage.
  3. Обработчик проверяет, содержит ли сообщение значения LastTradePrice, BestBidPrice или BestAskPrice.
  4. Бид берётся из BestBidPrice (либо LastTradePrice, если бид отсутствует), аск — из BestAskPrice. Если обе цены присутствуют, вычисляется спред.
  5. В лог записывается строка формата New tick on the symbol <id> index in the list=<index> bid=<bid> spread=<spread>. При отсутствии аска спред выводится как n/a.
  6. Если SecurityProvider не смог найти инструмент, генерируется предупреждение и инструмент пропускается.

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

  1. Задайте основной инструмент (Strategy.Security) через интерфейс или код.
  2. При необходимости добавьте дополнительные тикеры в параметр SymbolsList, разделяя их запятыми. Их порядок определяет индекс в сообщениях лога.
  3. Убедитесь, что подключение предоставляет Level1 для выбранных инструментов.
  4. Запустите стратегию — подписки активируются сразу, и в логе появятся сообщения о тиках.
  5. Используйте лог для контроля входящих данных и рассчитанных спредов.

Отличия от MQL-версии

  • Реализация полностью событийная: нет цикла и задержки Sleep.
  • Аналог SymbolsTotal(true) достигается сохранением порядка добавления инструментов, индексация начинается с 0.
  • В MetaTrader спред выражен в пунктах, а здесь — в ценовых единицах (decimal).
  • Вместо пользовательских событий используются стандартные лог-сообщения StockSharp.
  • При отсутствии аска выводится n/a, что подчёркивает неполноту текущего Level1.
  • Стратегия предназначена только для мониторинга и не отправляет заявок.

Пример лога

New tick on the symbol AAPL@NASDAQ index in the list=0 bid=171.25 spread=0.02
New tick on the symbol MSFT@NASDAQ index in the list=1 bid=324.10 spread=n/a

Пример показывает формат сообщений, которые стратегия пишет для каждого инструмента из списка Market Watch.

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>
/// On Tick Market Watch strategy (simplified).
/// Uses simple price momentum for entries.
/// </summary>
public class OnTickMarketWatchStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public OnTickMarketWatchStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candles", "General");

		_emaLength = Param(nameof(EmaLength), 20)
			.SetGreaterThanZero()
			.SetDisplay("EMA Length", "EMA period", "Indicators");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var ema = new ExponentialMovingAverage { Length = EmaLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, (ICandleMessage candle, decimal emaValue) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var close = candle.ClosePrice;

				if (close > emaValue && Position <= 0)
					BuyMarket();
				else if (close < emaValue && Position >= 0)
					SellMarket();
			})
			.Start();

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