476 000 новостей против нейронки: пять попыток заставить текст предсказывать цену. nlp.. nlp. sentiment analysis.. nlp. sentiment analysis. алготрейдинг.. nlp. sentiment analysis. алготрейдинг. анализ данных.. nlp. sentiment analysis. алготрейдинг. анализ данных. бэктестинг.. nlp. sentiment analysis. алготрейдинг. анализ данных. бэктестинг. машинное+обучение.. nlp. sentiment analysis. алготрейдинг. анализ данных. бэктестинг. машинное+обучение. мосбиржа.. nlp. sentiment analysis. алготрейдинг. анализ данных. бэктестинг. машинное+обучение. мосбиржа. нейронные сети.
476 000 новостей против нейронки: пять попыток заставить текст предсказывать цену - 1

Гипотеза «новости двигают рынок» — самая интуитивная в трейдинге. Настолько интуитивная, что я потратил на её проверку недели GPU-часов, полный архив финансовых телеграм-каналов с 2021 года и пять принципиально разных постановок задачи — от простых счётчиков упоминаний до NLP-разметки и пер-минутного event study.

Результат: ноль из пяти. Ни одна постановка не улучшила боевую модель (с одной унизительной для NLP оговоркой — она в конце).

Но интересен не сам ноль — интересно, как именно проваливалась каждая попытка. Две из пяти сначала показали «улучшение» в бэктесте, и если бы я остановился на агрегатной цифре, сейчас бы искренне рассказывал вам, что нейронка с новостями торгует лучше. Эта статья — подробный разбор всех пяти механизмов: что строил, какие признаки считал, какие цифры получил и где именно каждая версия обманывала.

Это обещанное продолжение статьи про сидовый шум ±10 п.п. — протокол проверки оттуда, здесь он работает на живом сквозном примере.

Стенд, кратко

Для тех, кто не читал первую часть, минимальная рамка:

  • База: часовые свечи 20 ликвидных акций Мосбиржи, 2021–2026; рекуррентная сеть, бинарная метка «вырастет ли цена больше порога за несколько часов»; walk-forward с зазором на длину горизонта; комиссии в каждой цифре. Все Sharpe в статье — парные сравнения кандидата с бейзлайном в единой методике, а не обещания абсолютной доходности: сравнение переживает любые уточнения модели издержек, абсолютная цифра — нет.

  • Протокол против самообмана: (1) любая идея проверяется ансамблем из 5 сидов против ансамбля из 5 сидов — одиночный прогон нейронки на этих данных врёт на ±10 п.п.; (2) первый гейт — средний AUC пяти сидов: не сдвинулся — идея дальше не идёт; (3) пороги и параметры выбираются на первой половине out-of-sample периода, оценка — на второй, которую выбор «не видел»; (4) обязательная атрибуция: из каких бумаг пришла прибавка.

  • Бейзлайн: модель на цене + положении бумаги относительно корзины + микроструктуре часа. Важная деталь: бейзлайн — боевая модель на момент попытки, а она улучшалась по ходу кампании (счётчики проверялись против Sharpe 1.53, поздние попытки — против 2.16), поэтому у каждой попытки своя пара цифр.

Данные: как достать полмиллиона финансовых новостей

Сначала инженерная часть — она может пригодиться независимо от выводов.

Проблема: RSS финансовых СМИ не отдаёт историю. Подключив ленты сегодня, первую пригодную для обучения выборку вы соберёте через год.

Решение: Telegram. У крупных финансовых каналов история сообщений хранится с момента создания — и Telethon (MTProto-клиент) отдаёт её целиком, легально, через обычный аккаунт:

# суть; полный скрипт тривиален
from telethon import TelegramClient
client = TelegramClient("session", API_ID, API_HASH)
async for msg in client.iter_messages(channel, reverse=True,
                                      offset_date=START_DATE):
    save(channel, msg.date, msg.text)

Три канала (агрегатор рыночных новостей + два деловых СМИ) дали 476 тысяч постов с 2021 года — полное покрытие обучающего периода. Тикер-привязка — словарём ключевых слов (названия компаний, тикеры, продукты) с ручной чисткой ложных срабатываний. Поверх — живой RSS-коллектор, чтобы лента пополнялась в реальном времени.

Один нюанс, который сэкономит вам вечер: авторизацию Telethon делайте двухшаговой и неинтерактивной (отправка кода и ввод кода — отдельные вызовы), иначе она не переживёт запуск из демона.

Пять попыток: план кампании

Постановки шли от грубых к тонким — каждая следующая отвечала на возражение к предыдущей:

#

Постановка

Возражение, на которое отвечает

1

Счётчики внимания

2

Сентимент (NLP)

«вы считали штуки, а важен знак»

3

Типы событий

«важен не тон, а ЧТО случилось»

4

Дивидендный календарь

«важны календарные события с датами»

5

Пер-минутная реакция цены

«важна не новость, а реакция рынка на неё»

Спойлер траектории: 1 — честная ничья, 2 — честный лёгкий минус, 3 — громкий провал, 4 и 5 — ложные победы, разоблачённые атрибуцией. Чем изощрённее постановка, тем коварнее провал.

476 000 новостей против нейронки: пять попыток заставить текст предсказывать цену - 2

Попытка 1. Счётчики внимания: ничья

Самая простая гипотеза: всплеск внимания к бумаге предвещает движение. Признаки на каждый бар:

  • число постов о бумаге за последние 24 часа и за неделю;

  • давность последнего поста (часов назад);

  • общерыночный поток постов за 4 часа и за сутки (прокси «на рынке что-то происходит»).

Итог: AUC пяти сидов не сдвинулся (0.542 против 0.543), честная вторая половина — Sharpe 1.49 против 1.53 у бейзлайна. Статистическая ничья.

Механизм: всплеск внимания не существует в вакууме — он приходит вместе со всплеском объёма и волатильности, которые модель уже видит в свече напрямую. Счётчик постов — это запаздывающий пересказ колонки volume. Информация не добавилась, добавился шум измерения той же информации.

Попытка 2. Сентимент: лёгкий минус

Возражение принято: считаем не «сколько», а «с каким знаком». Все 476 тысяч постов размечены компактной русскоязычной sentiment-моделью (rubert-tiny2-based) за одну ночь на GPU: sent = P(positive) − P(negative) для каждого поста.

Занятная деталь разметки: позитивными модель посчитала лишь 11% финансовых постов, негативными — 33%. Общедоменная sentiment-модель осторожна с финансовым позитивом — «компания увеличила выручку на 40%» для неё нейтральна, а любое упоминание санкций — негатив. Для наших целей это не фатально: признаку нужна не абсолютная точность, а стабильный сигнал в относительных изменениях.

Признаки: средний тон постов о бумаге за 24 часа и за неделю, тон последнего поста, среднерыночный тон за 4 и 24 часа.

Итог: AUC ниже бейзлайна у всех пяти сидов (0.540 против 0.544), честная половина 1.78 против 2.16. Не ничья — устойчивый лёгкий минус: признаки заняли ёмкость модели и ничего не заплатили за постой.

Механизм: тот же, что у счётчиков, плюс запаздывание. К моменту закрытия часового бара направление, о котором пишет пост, уже отыграно в цене этого же бара — модель видит его в доходности напрямую, без посредника-текста.

Попытка 3. Типы событий: громкий провал

476 000 новостей против нейронки: пять попыток заставить текст предсказывать цену - 3

Возражение принято: тон — примитив, важна семантика события. Дивиденды двигают цену не так, как санкции; отчётность — не так, как ставка ЦБ. Размечаем регэкспами пять событийных типов (на полмиллионе постов регэкспы честнее и в тысячу раз дешевле LLM):

  • дивиденды — 8.5 тыс. постов;

  • отчётность — 30 тыс.;

  • санкции — 20 тыс.;

  • ставка ЦБ — 6 тыс.;

  • M&A — 7 тыс.

Признаки: события каждого типа по бумаге в скользящих окнах, санкционный и ставочный фон по рынку, направление последней дивидендной новости (через сентимент из попытки 2).

Итог — худший в серии, с большим отрывом: AUC 0.519 против 0.544 у всех пяти сидов, честная половина Sharpe 0.38 против 2.16. Модель с событийными признаками разучилась торговать вообще.

Механизм — самый поучительный из пяти: медленные фоновые признаки. Признак «на рынке санкционная неделя» константен часами и сутками и одинаков для всех 20 бумаг. Для сети это идеальный идентификатор эпохи: вместо «санкции → продавай» она выучивает «в кварталах с таким фоном рынок вёл себя так-то» — то есть запоминает конкретные исторические отрезки train-выборки. На out-of-sample эта память не просто бесполезна — она активно вредна, потому что уверенно применяет чужой контекст. Я называю это ловушкой slow features; она у меня срабатывала на шести разных семействах признаков, событийные типы — самый чистый случай.

Практическое следствие: если ваш признак почти не меняется внутри дня и одинаков для всех инструментов — он кандидат не в фичи, а в идентификаторы эпохи. Обращайтесь осторожно.

Попытка 4. Дивидендный календарь: первая ложная победа

476 000 новостей против нейронки: пять попыток заставить текст предсказывать цену - 4

Возражение принято: фон — зло, возьмём точечные календарные события с датами и величинами. Биржевой API отдаёт историю дивидендных отсечек (280 выплат по нашим бумагам): близость отсечки (≤14 дней), дивидендная доходность выплаты, пост-гэповое окно (≤10 дней после отсечки — там, по фольклору, «закрывают гэп»).

Итог скрининга: Sharpe 2.49 против 2.16. Улучшение! Первое за четыре попытки. Признаюсь: рука уже тянулась записать победу и обновить прод.

Протокол потребовал атрибуцию, и она перевернула картину:

  • дивидендные плательщики суммарно: −22 тыс. ₽ к PnL против бейзлайна;

  • весь прирост — +21.7 тыс. ₽ на одной бумаге, у которой в тот период дивидендного признака не было вовсе (нули в этих колонках).

Улучшение пришло ровно оттуда, куда механизм признака указывать не может. Это не сигнал — это перетасовка сидового шума: дивидендный признак ненулевой лишь на ~3% баров, но само его добавление меняет размерность входа сети, а значит — всю раскладку случайной инициализации. Пять новых сидов — новый билет в лотерею из первой статьи, и в этот раз билет оказался выигрышным. На следующем еженедельном переобучении «прибавка» испарилась бы вместе с удачей.

Правило, которое я после этого сформулировал: если улучшение пришло не оттуда, куда указывает механизм признака — это не улучшение, а шум. Агрегатный Sharpe не является доказательством в принципе.

Попытка 5. Пер-минутная реакция: ложная победа, версия «про»

476 000 новостей против нейронки: пять попыток заставить текст предсказывать цену - 5

Последнее и самое сильное возражение: сама новость не важна — важно, как рынок на неё отреагировал. Это уже почти классический event study: если за 15 минут после поста цена пошла вверх на повышенном объёме — рынок «проголосовал», и это знание свежее любого текста.

Постановка самая трудоёмкая из пяти: минутные свечи, для каждого поста — окно 15 минут после публикации, доходность и всплеск объёма в окне (объём нормирован на собственную историю бумаги в это время суток). Получилось 34 тысячи размеченных реакций. Признаки на бар: подписанная реакция на последний пост, её давность, величина всплеска объёма, сумма реакций за сутки.

Итог скрининга: Sharpe 2.39 против 2.16, и просадка лучше. Опять победа? Атрибуция:

  • суммарный PnL против бейзлайна: −49 тыс. ₽ (Sharpe вырос, денег стало меньше — за счёт упавшего оборота);

  • бумаги, богатые новостями — те, где механизм обязан работать лучше всего — стали хуже: первый по капитализации банк −12.3 тыс., сама биржа −8 тыс.;

  • «выигрыш» пришёл на бумагу с 214 реакциями за пять лет — там, где признак практически пуст.

Паттерн, обратный механизму, в квадрате. Плюс новый симптом в коллекцию: рост Sharpe при падении PnL и оборота — артефакт, а не улучшение (отфильтровались и убыточные, и прибыльные сделки; знаменатель сжался быстрее числителя).

Почему текст всегда опаздывает

Пять постановок, пять разных провалов, один корень.

На часовом горизонте новостной текст — это запаздывающий пересказ ценового ряда. Цепочка событий на реальном рынке: инсайдеры и связанные стороны действуют до публикации; алгоритмы и профессиональные ленты — в первые секунды-минуты; текст в телеграм-канале появляется, когда движение уже идёт; к закрытию часового бара оно уже в свече — в доходности, объёме, волатильности, положении бумаги относительно рынка. Всё, что «знает» новость, модель уже прочитала из цены, причём без ошибок парсинга и тикер-привязки.

Отсюда честная карта того, что я не проверял и почему:

  • Субсекундная реакция на новость — там текст ещё может опережать часть участников, но это HFT-ниша: колокация, комиссии маркетмейкера, война за микросекунды. На retail-инфраструктуре с комиссией 0.05% за сторону экономика мертва до всякого ML.

  • LLM-понимание текста вместо регэкспов и лёгкого сентимента. Дорого, но проверяемо; мой скептицизм структурный: LLM извлечёт смысл точнее, но не раньше — а хоронит новостные признаки именно запаздывание, не качество извлечения. Улучшать точность пересказа того, что уже в цене, можно бесконечно.

  • Длинные горизонты (недели), где фундаментальные новости могли бы работать, — у моей системы другой горизонт, а смена горизонта — отдельная большая история (спойлер: часовые признаки на горизонте недели не предсказывают ничего).

Если видите постановку, которую я упустил, — напишите в комментариях: стенд и протокол под рукой, осмысленную гипотезу могу прогнать и вернуться с цифрами. После прошлой статьи один такой расчёт по мотивам комментария уже случился.

Чек-лист: если проверяете новостные признаки сами

  1. Бейзлайн обязан видеть цену и объём полностью — иначе новости «сработают», просто пересказав их.

  2. AUC-гейт до денег: средний AUC нескольких сидов не сдвинулся — дальше не считать.

  3. Атрибуция по бумагам: прибавка обязана жить там, где признак есть. Прибавка на бумагах с нулями признака = перетасовка шума.

  4. Атрибуция по богатству признака: у бумаг с самыми частыми новостями эффект должен быть самым сильным. Обратный градиент — приговор.

  5. Sharpe при падающем PnL и обороте — артефакт фильтрации, а не улучшение.

  6. Осторожно с фоновыми признаками (константными внутри дней и общими для всех бумаг) — это идентификаторы эпохи, модель запомнит по ним историю.

  7. Редкий признак (единицы процентов баров) меняет размерность входа и перезапускает сидовую лотерею — сравнивайте только ансамбль против ансамбля.

Что в итоге

Ноль из пяти — это результат, купленный неделями GPU-часов, и я считаю покупку выгодной. Дешевле было бы поверить в любую из двух «побед» — но тогда бы я сейчас торговал шумом и рассказывал вам, как нейронка читает новости. Отрицательный результат с протоколом стоит больше положительного без него.

Полная честность требует сноски: самые тупые счётчики внимания в комбинации с микроструктурными признаками позже дали микроскопическую, но подтверждённую по полному протоколу прибавку — и в этом виде сидят в боевой модели. А весь NLP — сентимент, семантика, типы событий — проиграл счётчику постов. Это, пожалуй, самый унизительный для нейросетей вывод всей серии.

Система тем временем торгует виртуально в реальном времени, на очереди — реальные деньги с публичным трек-рекордом. Следующей статьёй планирую разбор главного страха любой обученной на истории системы — смены рыночного режима: что из измеренных страховок работает (еженедельное переобучение, автоматический выключатель), а что проваливается (почти всё остальное). Но очередь гибкая: если интереснее экономика издержек и исполнения, устройство live-инфраструктуры на дешёвом VPS или пошаговый разбор протокола проверки — скажите в комментариях, следующую тему могу выбрать из предложенных, либо пойду по уже намеченному плану.


Не является индивидуальной инвестиционной рекомендацией.

Автор: svtoroi

Источник