Лето подходит к концу и я хочу рассказать о своём увлечении последних месяцев — машинном обучении (Machine Learning) на данных Московской биржи. Эта тема уже давно не даёт мне покоя и в этот раз я решил подойти более основательно — активно использовал новейшие языковые модели, типа Claude Sonnet 5, Gemeni 3.7 Flash не только для написания кода, но и для работы с результатами этих скриптов. При этом не использовал полностью автономных агентов — все языковые модели работали как консультанты в чате — все решения принимал я, в том числе какую исходную информацию давать конкретной модели на каждом из этапов. Предвидя будущие вопросы я сразу отвечу что это мой эксперимент и он не только не зарабатывает, но и пока даже непонятно чем закончится — сейчас это всё в процессе. Эту статью решил написать не чтобы «засветить трейдерским граалем» — которых я кстати считаю что не существует, потому что вся информация общедоступна. Эта статья написана чтобы рассказать как есть об этой ML теме без прикрас и познакомиться с комментариями — среди негатива (обычно так) проскакивают интересные мысли. Я сам не работаю в финансах и моя позиция больше исследовательская, так что раскрыть какие‑то корпоративные секреты я не могу — просто прощупываю путь перед собой.
Ну и можно случайно познакомиться с интересными людьми, которые профессионально занимаются темой — так уже бывало.
Перед тем как начинать этот эксперимент я переписывался с Claude Fable 5 в чате — это самая умная и самая дорогая публично доступная большая языковая модель — у меня были некоторые свои мысли, а также источники в виде книг и статей и мне хотелось чтобы модель оформила все в пошаговый план действий. Консультация через чат‑бота стоила примерно $20.
В итоге я остановился на такой архитектуре, когда каждый шаг — это независимый набор Python скриптов, который запускается вручную и работают только с конкретным шагом.
Я хочу торговпть через российского брокера. Но в это же самое время я хочу покупать лучших и продавать худших одновременно. А на практике одновременно открывать и шорт и лонг, но акций на Московской бирже торгуется не так много (по сравнению например с американским рынком) и шортить акции на Московской бирже — дорого и не всегда есть возможность — особенно для частного лица. Из‑за этого я выбрал открывать длинную позицию в акциях и одновременно короткую — во фьючерсе.
Через API Московской биржи (ISS) можно бесплатно скачать данные о торгуемых сейчас акциях (если кто‑то выбыл со временем, то информации об этом нет — Мосбиржа выдаёт только текущий момент), а затем я сравниваю этот список со списком базовых активов, для которых на срочной секции существуют фьючерсы. В результате получаю именно те акции, для которых одновременно существует и акция и фьючерс.
Дальше мне нужен не просто текущий фьючерс, а история контрактов начиная с 2021 года — фьючерсы имеют ограниченный срок жизни. В отличие от акции, у обычного фьючерсного контракта есть дата экспирации. Например, контракт LKU6 относится к конкретному сроку поставки/исполнения и после окончания обращения перестаёт торговаться. Поэтому история фьючерса — это не один бесконечный временной ряд, а последовательность отдельных контрактов с перекрытием по датам.
Например, фьючерс на ПАО Сбербанк:
Поэтому для каждого базового актива я отдельно запрашиваю историю и отбираю контракты. А для каждого найденного контракта дополнительно запрашивается расширенная информация через MOEX ISS — его название, группа, дата начала торгов, последняя дата торгов и дата исполнения. В итоге получается не просто список тикеров, а небольшая база всех исторических контрактов, с которой дальше уже можно работать программно.
Дополнительно для акций я получаю через ISS Мосбиржи ИНН эмитента и через поставщика данных DaData подтягиваю название компании, адрес и руководителя. Это не нужно непосредственно для модели, но такой задел на будущее.
Ранее я уже проверял связь российских инструментов с мировыми ценами и увидел, что многие из них действительно быстро реагируют на изменения связанных глобальных рынков. Поэтому в текущем эксперименте я решил добавить несколько внешних факторов: нефть Brent, золото, натуральный газ, опосредованные индексы SPY ETF Trust, QQQ ETF Trust, ну и курсы CNY/RUB и Si, индекса МосБиржи, индекс RGBI (гособлигации).
Весь результат сохраняется в два текстовых JSON‑файла: отдельно акции, у которых есть фьючерсы, и отдельно дополнительные фьючерсные активы.
Получается примерно такая схема: сначала Московская биржа отвечает на вопрос «что сейчас вообще торгуется», затем мой код определяет пересечение акций и фьючерсов, после этого восстанавливает историю контрактов и обогащает полученный набор дополнительной информацией.
Причём всё это сделано отдельным независимым шагом. Если завтра я поменяю список активов или захочу добавить ещё один внешний фактор, мне не приходится переделывать весь последующий ML‑пайплайн — достаточно заново сформировать этот начальный слой данных.
Вообще чтобы что‑то делать с машинным обучением нужны не только список бумаг, но и данные по ним.
Все привыкли к обычным свечам когда есть цена открытия, максимальная цена, минимальная цена, цена закрытия и объём (обозначаются часто как OHLC). Все эти данные нарезаны по времени. Это такой общепринятый формат — по ним рисуется графики и в общем‑то всё понятно, но для моего эксперимента с машинным обучением одного OHLCV недостаточно.
Потому что внутри одной свечи может происходить много интересного: кто‑то совершает агрессивную покупку — то есть исполняет заявку по доступной цене продавца. Кто‑то выставляет большое количество лимитных заявок. Кто‑то снимает их, не дожидаясь исполнения. В какой‑то момент стакан может быть почти пустым, а через несколько миллисекунд в нём появится большой объём. Цена при этом может практически не измениться.
Уточнение: лимитная заявка — это условие «купить или продать не хуже указанной мной цены». Такие заявки образуют стакан — список доступных объёмов на разных ценовых уровнях. Например, путь цены:
Вариант A:
Вариант B:
То есть две свечи с одинаковыми OHLC могут быть получены совершенно разным поведением участников рынка.
Московская биржа через Algopack предоставляет уже агрегированную информацию о том, что происходило внутри временного интервала 5 минут:
Внутри алгопака есть:
TradeStats это сделки: это не просто объем, а профиль агрессии. Кто бил по рынку — покупатели или продавцы? Объемы разделены на vol_b (покупки) и vol_s (продажи), считаются средневзвешенные цены исполнения (vwap), количество сделок и дисбаланс объемов (disb).
OrderStats это заявки и намерения: сколько лимитных заявок было выставлено (put_orders, put_val) и сколько снято до исполнения (cancel_orders, cancel_val). Это индикатор реального давления и «пугливости» ликвидности.
OBStats это срез стакана (Order Book): глубина, спреды на 10-м уровне spread_lv10, эффективный спред на объем в 1 млн руб spread_1mio, а также дисбаланс объемов по всему стакану imbalance_vol.
Для модели это потенциально гораздо более интересный источник признаков, чем просто OHLC.
Биржевой стакан:
Для моего эксперимента пятиминутная агрегация — довольно крупный интервал. Если я захочу перейти к более быстрому анализу, возникает вопрос: можно ли получить те же показатели самостоятельно из потока сделок и стакана?
И у меня даже произошел спор с товарищем — можно ли собрать такие данные как в Алгопаке самому? И например учить на 5 минутках истории Алгопака, а для реалтайма собирать эти же данные самостоятельно, но из других доступных для частного лица источников и использовать эти более быстрые данные.
Его аргумент был вполне логичным: зачем Algopack, если можно самостоятельно подключиться к потоку рыночных данных, то есть записывать стакан и ленту сделок через API с минимальными интервалами, а потом посчитать все необходимые показатели?
И действительно — в значительной степени это возможно. Если у тебя постоянно пишется поток сделок, из него можно восстановить обычную статистику TradeStats: OHLC, объём, оборот, количество сделок, VWAP, объёмы покупок и продаж и другие производные признаки.
Со стаканом ситуация тоже похожая. Если постоянно поддерживать его актуальное состояние, можно периодически, но очень быстро, например 50 миллисекунд, делать снимок и самостоятельно рассчитывать спред, количество уровней, суммарный объём, дисбаланс и многие другие показатели.
Но есть нюанс: стакан — это ещё не история всех заявок. Представим, что на цене 100 рублей в стакане стояло 1000 лотов. Через несколько мгновений осталось 500. Что произошло?
Первый вариант — кто‑то снял заявку на 500 лотов.
Второй — кто‑то агрессивно купил эти 500 лотов.
Третий — заявка была каким‑то образом изменена.
Если у нас есть только агрегированное состояние стакана и лента сделок, однозначно ответить на этот вопрос нельзя.
Именно здесь появляется принципиальная разница между состоянием стакана и историей жизненного цикла отдельных заявок.
В L2-стакане мы видим объём, агрегированный по ценовым уровням. Но у нас нет идентификаторов конкретных заявок и полного жизненного цикла каждой из них:
Поэтому самостоятельно получить точное значение cancel_orders простым анализом изменений агрегированного стакана нельзя.
История событий с номерами заявок:
L2 показывает состояние. Полный лог позволяет восстанавливать последовательность событий.
Номера заявок вроде как можно видеть через QUIK или TRANSAQ, но это не точно — я сам так не пробовал и даже не исследовал вопрос.
И конечно, у Мосбиржи есть полная история всех заявок (Full Order Book, но это терабайты данных (Big Data), с которыми частному лицу работать невероятно сложно и дорого.
Поэтому здесь мой спор с товарищем закончился: он прав — значительную часть статистики можно посчитать самостоятельно, но это не то же самое, что иметь биржевую информацию о жизненном цикле каждой заявки.
Но есть и более прозаичная причина. Чтобы самостоятельно накапливать такую историю, нужно с сегодняшнего дня постоянно записывать рыночные данные. Не просто запустить один скрипт, а обеспечить непрерывную работу инфраструктуры, следить за соединениями, хранить поток данных и проверять, что ничего не потерялось. И если начать это делать сегодня, то завтра у тебя будет один день истории. Через неделю — неделя. А мне нужна история начиная с 2021 года.
Можно, конечно, построить собственную инфраструктуру и ждать несколько лет. Но для исследовательского проекта это довольно странный способ провести лето.
Algopack в этом смысле решает главную проблему сразу: исторические данные уже накоплены за прошлые годы и доступны для загрузки. Цена вопроса — деньги и определённые ограничения самого продукта.
Раз уж я собираюсь строить на этих данных модель, доверять им вслепую было бы странно.
Поэтому я написал отдельный валидатор и решил сравнить пятиминутные данные Algopack с обычными свечами, которые можно бесплатно получить через ISS Московской биржи.
Например, для фьючерса LKU6 за 2 июля 2026 года (когда я проводил подробный анализ) только за один день набралось около десяти случаев расхождения.
На первый взгляд некоторые различия были совсем небольшими: например, в 10:05, 13:10, 13:20, 15:25, 17:20 и 17:50 цена закрытия отличалась на один пункт, а объём — всего на два‑три контракта.
Ещё оказалось, что при сравнении нужно очень внимательно смотреть на границы интервалов: в Algopack время 09:05 означает фактически интервал с 09:00:00 до 09:04:59; а в стандартных свечах ISS время относится к началу минутного интервала.
А вот большие расхождения уже интереснее. Были и случаи, которые нельзя объяснить одним только пограничным timestamp.
Например, для того же LKU6 в 11:45 один источник показывал объём 135 контрактов, а другой — 229. Разница — 94 контракта. В 12:50 было 22 против 39.
Это уже не ситуация одна сделка попала в соседнюю пятиминутку, хотя писал им в поддержку и мне ответили, сказали что разобрались и пересчитали.
Теоретически вместо вопроса: «Куда пошла цена за последние пять минут?» можно будет задавать модели гораздо более содержательные вопросы:
«Что происходило с агрессивными покупками?»
«Увеличивался ли дисбаланс стакана?»
«Сколько заявок появилось и сколько было снято?»
«Насколько дорогим было бы исполнение крупной заявки?»
«Что происходило с ликвидностью непосредственно перед движением цены?»
Пока я не знаю, какие из этих признаков действительно окажутся полезными. И возможно, значительная их часть окажется бесполезной.
На этом этапе у меня ещё нет ни модели, ни торгового сигнала, ни ответа на главный вопрос — можно ли вообще заработать на этой информации.
Зато есть потраченное время и главное: исторический набор рыночных данных, собранный в единую структуру.
Я восстановил историю акций и фьючерсов с 2021 года, добавил туда внешние факторы и получили не только цены и объёмы, но и микроструктуру рынка: агрессивные покупки и продажи, заявки, снятия, спреды, глубину стакана и дисбаланс ликвидности.
При этом пришлось отдельно проверять данные Алгопака и искать причины расхождений с ISS.
Теперь данные подготовлены. И следующий вопрос гораздо интереснее: существуют ли в этих данных Московский биржи вообще информация, которую машинное обучение способно превратить в устойчивое торговое преимущество?
Автор: Михаил Шардин 🔗 Моя онлайн‑визитка 📢 Канал «Умный Дом Инвестора» в TG или MAX 25 августа 2026 года
Еще по теме:
Подпишитесь на нашу рассылку, чтобы получать уведомления о новых обновлениях, информации, скидках и т. д.