- PVSM.RU - https://www.pvsm.ru -
Совсем недавно решал задачу построения стакана котировок на основе исторических данных Московской Биржи. В открытых источниках ничего подобного не нашел, пришлось начинать с нуля и копать самому. Есть некоторые нюансы, о которых нужно знать. Про них буду упоминать по ходу.
Про биржевую торговлю, инфраструктуру и тестирование алгоритмов на исторических данных много писал и пишет IT Invest [1], спасибо ему. От себя добавлю, что на данных OrderLogs мы анализируем глубину рынка, ликвидность, спреды и еще много чего. Результаты используем в наших торговых алгоритмах.
Специально выбрал Фондовый рынок, так как тут больше всего вопросов. Валютный и Срочный рынок имеют свои особенности, но там проще. Реализация алгоритма на Java, код на GitHub [2].
Цель: Получить стакан котировок на любой момент времени.
Файл со всеми заявками/сделками (Full Order Log), полученный от Биржи за 1 день, простой CSV формат, объем ~ 1 Гб. Описание формата и примеры данных есть на сайте Биржи. Вот фрагмент файла:
Так как данные получены прямо из торговой системы, то поле «Время» — это время фиксации событий в торговой системе. Это надо принять к сведению. Кто получает маркетдату через FAST [3], тем данные приходят с задержкой (latency ~ 0.1 мс в зоне колокации). Это я к тому, что, если вы тестируете алгоритмы на данных Биржи, то там во времени не учитываются задержки, которые есть в наших реалиях. Так что если такие времена не критичны, можно «забить» на latency.
Фондовый рынок стартует в 9:50 и закрывается в 18:50, проходя 3 фазы:
09:50 – 10:00 — Аукцион открытия (Формируется цена открытия)
10:00 – 18:40 — Основные торги
18:40 – 18:50 — Аукцион закрытия (формируется цена закрытия)
Файл содержит только «Основные торги» + неисполенные заявки с аукциона открытия.
Понадобится 2 типа объекта «Orders» и «OrderBook». Orders — это дискретные заявки (строка в файле). А объект OrderBook — это агрегированные по цене заявки имеющий 2 поля: цена и объем, т.е. складываем все объемы с одинаковой ценой.
Давайте соберем стакан для инструмента «XXXX» на момент времени AABBCCDDD. С первой строки начинаем собирать заявки (объекты Orders), касающиеся бумаги «XXXX» в коллекции. Заявки Buy в одну, Sell в другую. И будем перебирать все заявки пока time<=AABBCCDDD.
Если тип события = 1 (поставить заявку), добавляем заявку в коллекцию Buy или Sell в зависимости от направления заявки.
Если тип события = 0 (удалить заявку), то по номеру заявки находим ее в коллекции и удаляем. Она там непременно должна быть, ибо по логике, нельзя удалить то, чего нет. Если не нашли нужную заявку, надо бить тревогу (либо ошибка в самих данных, либо ошиблись мы сами).
Если тип события = 2 (сделка), то по номеру заявки находим ее в коллекции и смотрим на ее объем:
Типы событий = 2 всегда идут попарно — две стороны сделки. Как только нарушается условие time<=AABBCCDDD, выходим из цикла. На этот момент у нас в коллекциях содержатся все активные заявки на момент AABBCCDDD. Сортируем элементы коллекций по цене. Теперь настало время создания объектов типа OrderBook, напомню, это ценовые уровни (агрегированные показатели цена — объем), т.е. то, что привыкли видеть в стакане котировок. Циклом проходим по всем заявкам и складываем объемы заявок с одинаковой ценой. Вот и весь алгоритм.
У рыночныех заявок в поле цена указывается «0». Такие заявки, так же обрабатываем как обычные. Они исполняются в моменте.
Айсберг заявки также встречаются в ордерлогах. Как показала практика, их невозможно идетифицировать наверняка. При формировании данных Биржа применяет свой алгоритм, который скрывает «айсберги». Но все равно, в некоторых случаях они мелькают, например висит заявка с объемом 10, а когда превращается в сделку — объем уже 100.
Знание этой информации достаточно, чтобы собирать стаканы, анализировать спреды и глубину рынка.
А теперь самое вкусное: давайте проверим наш алгоритм на практике. 2 февраля 2016 специально сделал скриншоты с терминала Bloomberg со стаканом, зафиксировав время. Инструменты брал не самые ликвидные: Яндекс (YNDX), Камаз (KMAZ)… дабы избежать сложности с синхронизацией времени. Паралельно собрал стакан на Биржевых данных описанным выше алгоритмом. Вот результат на 16:56:21:
Данные идентичны.
В потоке около 15 млн записей, где содержится около 300 инструментов. Самый ликвидный инструмент SBER за целый торговый день прогоняет за 20 сек. Для SBERа глубина стакана в ту или другую сторону — 500-600 ценовых уровней. Если через каждый «час» сохранять состояние стакана, время обработки сокращается до секунды, что меня вполне устраивает. А если еще записать нужный инструмет в отдельный поток, время сократится до десятых секунд.
Таким образом, стакан по любому инструменту на любой момент времени получаем менее чем за 1 секунду. Уверен, кто-то сможет предложить более эффективный алгоритм, буду рад.
P.S.: Кусок исторических данных можно взять на сайте Биржи [4] бесплатно. Если нужны данные имено за 2 февраля, выгружу на Я.Диск (~1Gb). Дайте знать.
Автор: fivelife
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/111378
Ссылки в тексте:
[1] IT Invest: https://habrahabr.ru/company/itinvest/
[2] GitHub: https://github.com/FiveLife/FullOrderBook/
[3] FAST: https://habrahabr.ru/company/itinvest/blog/243657/
[4] сайте Биржи: http://moex.com/ru/orders?historicaldata
[5] Источник: https://habrahabr.ru/post/276635/
Нажмите здесь для печати.