Идея скрестить RAG, LLM и классический ML для генерации торговых сигналов выглядит заманчиво: новости содержат контекст, LLM умеет его извлекать, а RandomForest — строить предсказания на табличных признаках. Но между «прочитать новость» и «выдать сигнал BUY/SELL/NEUTRAL» лежит инженерная пропасть. В этой статье — практическая архитектура такого пайплайна, критерии выбора компонентов, типичные ошибки и конкретные шаги, которые можно повторить. Важно: речь не о гарантированной прибыли, а о том, как собрать воспроизводимый и объяснимый конвейер.

От новостного скрапера к признакам: привязка к тикерам

Первый шаг — превратить поток новостей в структурированные события. RAG-система с LangGraph-агентом на локальных LLM (например, через Ollama) умеет искать релевантные фрагменты и извлекать сущности. Задача — сопоставить каждую новость с тикерами. Простое решение — NER плюс словарь алиасов компаний. Более надёжное — гибрид: точное совпадение по названию и тикеру, затем семантический поиск по эмбеддингам в PostgreSQL с расширением pgvector. Критерий выбора: полнота привязки важнее скорости на старте, но если новостей тысячи в день, добавьте кэш и батчевую обработку.

Типичная ошибка — привязывать новость только к одному тикеру. В реальности одна новость может влиять на несколько компаний (поставщики, конкуренты, регуляторы). Решение: сохраняйте несколько связей с весами уверенности. Это даст модели больше контекста и снизит шум.

Генерация признаков: что именно подаём в RandomForest

RandomForest не работает с сырым текстом. Нужны табличные признаки. Разделите их на три группы:

  • Текстовые метрики: тональность (от LLM или модели классификации), количество упоминаний тикера, наличие сущностей «регулятор», «дивиденды», «суд» и т.п.
  • Временные: час публикации, день недели, давность новости, частота публикаций по тикеру за последние N часов.
  • Рыночные (если доступны): волатильность за предыдущий период, объём торгов, отклонение цены от скользящей средней. Без них сигнал будет слабым, но начинать можно и с текстовых.

Критерий выбора признаков: каждый должен быть вычислим в момент времени t без заглядывания в будущее. Иначе получите утечку данных и красивые метрики на бэктесте, которые не воспроизведутся в реальности. Пример: не используйте тональность новости, опубликованной после закрытия торгов, для сигнала на этом же дне.

Для извлечения сущностей и тональности удобно использовать локальную LLM через Ollama — это дешевле и приватнее облачных API. Но не доверяйте LLM слепо: добавьте правила и пороги, а результаты сохраняйте в структурированном виде (JSONB в PostgreSQL).

Модель и объяснимость: почему RandomForest, а не «чёрный ящик»

RandomForest выбран не случайно: он устойчив к выбросам, работает на малых данных и даёт интерпретируемые признаки важности. На старте данных мало, и сложные нейросети будут переобучаться. Практические шаги:

  • Соберите датасет с метками BUY/SELL/NEUTRAL. Метки можно строить по будущей доходности за фиксированный горизонт (например, превышение порога вверх — BUY, вниз — SELL, иначе NEUTRAL).
  • Разделите данные по времени, а не случайно: train — прошлое, test — будущее. Иначе метрики будут завышены.
  • Обучите RandomForest с ограничением глубины и минимальным числом образцов в листе, чтобы избежать переобучения.
  • Оценивайте не только accuracy, но и precision/recall по классам, а также матрицу ошибок. Для торговли ложные срабатывания дороги.

Explainable UI — не роскошь. Пользователь должен видеть, какие признаки повлияли на сигнал: например, «тональность +0.8, упоминание регулятора, частота новостей выросла». Для этого подойдут SHAP-значения или встроенная важность признаков. Визуализацию удобно делать на Plotly и Streamlit, а API — на FastAPI.

Типичная ошибка — пытаться объяснить каждый сигнал постфактум. Лучше сразу логировать признаки и предсказания в базу, чтобы потом можно было воспроизвести решение.

Типичные грабли на малых данных и как их обойти

Малые данные — главный враг. Вот что часто ломает пайплайн:

  • Дисбаланс классов: сигналов BUY/SELL значительно меньше, чем NEUTRAL. Используйте взвешивание классов или пересэмплинг, но только на тренировочной выборке.
  • Переобучение на шуме: новости могут быть нерелевантны. Добавьте фильтр по минимальной уверенности привязки к тикеру.
  • Утечка через агрегаты: не считайте статистики по всему датасету — только по прошлому.
  • Игнорирование режимов рынка: модель, обученная на спокойном рынке, может плохо работать в период высокой волатильности. Добавьте признак волатильности или обучите отдельные модели для разных режимов.

Пример: если вы используете тональность от LLM как признак, проверьте, не коррелирует ли она с будущей доходностью на тесте. Если корреляция близка к нулю, признак бесполезен, и его стоит убрать, чтобы не увеличивать размерность.

Ещё одна ошибка — оценивать модель по одному периоду. Используйте скользящее окно валидации (walk-forward), чтобы увидеть стабильность.

Что дальше: архитектурное развитие

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