ИИ Анализ новостного сентимента как торговый сигнал

от автора

Исходный код торговой стратегии опубликован по ссылке

по

по

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

Однако возникает вопрос: как же они работали раньше? В чём дело?

Что изменилось?

Новостной сентимент определяет режим. Индикатор работает внутри режима.

Практическое руководство как определить сентимент

Нужен векторный поиск по новостям. Его легко реализовать используя Scrapy + PostgreSQL + PgVector или Scrapy + MongoDB Atlas Vector Search. См. VectorEmbeddings + Cosine Distance. Я использую SaaS-решение, которого хватает в рамках free tier — tavily.com. Также подойдёт Perplexity Search API. Ниже — руководство по построению поискового запроса для получения новостного сентимента.

1. Score как критерий сортировки, но не фильтра

Слово «Трамп» не равно «Биткоин», но по смыслу подразумевает спекуляцию на рынке. Результаты с максимальным score воспринимаются аудиторией как прямая реклама. Нулевой score означает отсутствие упоминания биткоина. Искать нужно околонулевой score — именно он формирует настроение рынка.

Это работает по принципу продакт-плейсмента: пиво Corona в «Форсаже» или смартфон Sony в фильме о Джеймсе Бонде.

2. Домен первичен по отношению к поисковому запросу

Искать «SEC crypto enforcement action lawsuit» — ошибка. Настроение рынка формируют конкретные домены и блоги — первопроходцы рынка. Если они не сделают репост нового документа SEC, его никто не увидит. SEC влияет на розничного инвестора только через посредников: публикация регулятора сама по себе не двигает рынок — его двигает пост авторитетного блогера.

3. Время первично по отношению к смыслу публикации

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

4. Нужно искать тот фундаментал, который закладывают в будущее, а не придумывать свой

Критически важно использовать именно VectorEmbeddings + Cosine Distance, а не LLM для поиска сентимента. LLM увидит ключевое слово «Перекупленный RSI» и сформирует собственный вывод о движении рынка. Задача — найти не интерпретацию, а настроение, которое авторитетные участники закладывают в рынок. См. RAG Embedding Models.

5. Количество публикаций увеличивает шум

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

Важно

LLM и векторный поиск новостей нужно разделить физически. Векторный поиск следует осуществлять по смыслу слова «прогноз», тогда как LLM должна анализировать настроение рынка. В противном случае на выходе будет получена не закладываемая фундаментальная тенденция, которой предшествует изменение настроения участников рынка, а интерпретация индикаторов технического анализа.

Проверяем гипотезу

Кейс 1. Нейтрально-медвежий тренд

Используя упомянутые выше рекомендации, я сформировал поисковой запрос. В Tavily встроена LLM, которая делает краткое summary по новостям.

  • Поисковой запрос дал результат: нейтрально-медвежий сентимент Show Image

  • Реакция рынка

Кейс 2. Бычий сентимент

Повторим эксперимент на другой дате.

  • Поисковой запрос дал результат: бычий сентимент

  • Реакция рынка

Рекомендации по временному окну поиска

1. Не все новостные агентства указывают время публикации. Чтобы исключить look-ahead bias, их придётся убрать из выборки. Среди них:

В базе Tavily таким публикациям присваивается время Thu, ?? Jan ???? 00:00:00 GMT. Чтобы отфильтровать их, необходимо привести время к UTC — иначе смещение будет рассчитываться по вашему часовому поясу:

const hour = dayjs(publishedDate).utc().get("hour");const minute = dayjs(publishedDate).utc().get("minute");if (hour === 0 && minute === 0) {    console.warn(`fetchNews search invalid publishedDate query=${query} url=${url} from=${from} to=${to}`)    return false;}

2. Запрашивайте данные за -2 дня и фильтруйте последние 24 часа на своей стороне.

При поиске Tavily использует дату без уточнения времени, поэтому на границе 23:59–00:00 публикации будут теряться. Даже при идеальном парсере сайтов возникает проблема распределённых CDN-баз данных: одна и та же новость поступает читателям не одновременно, а по мере высвобождения серверных ресурсов.

3. Не пытайтесь опередить рынок.

Усреднение убивает направленность: позитив утра компенсирует негатив вечера, и сентимент стремится к шуму. Но если взять окно менее 24 часов, интерпретация сентимента становится неоднозначной: Трамп наносит удары по Ирану — непонятно, упадёт ли биткоин до нуля или вырастет. Окно в 24 часа оптимально для понимания контекста и отсечения шума.

Бектест

Все вышеупомянутые рекомендации удалось автоматизировать. ИИ-агент находит новостной сигнал:

И удерживает позицию до исчерпания новостного сентимента:

При этом для риск-менеджмента выставляется статистически недостижимый hard stop и trailing take-profit.

Почему не работают индикаторы

Торговый режим пытаются отладить на горизонте месяца, тогда как сентимент чередуется каждый день: медвежий — бычий — бычий — медвежий. В итоге обе стратегии — и бычья, и медвежья — выходят на уровень 50/50.

Что можно улучшить

Если выходить из позиции на изменении сентимента, часть прибыли будет упущена из-за задержки парсера новостей. Решение: выход на откате 3% от максимального PnL удачной позиции.

Это означает недоимку в 3% PnL, умноженную на 10 позиций — потенциально +30% к существующим 16%. При этом на момент открытой позиции новостной сентимент предсказуем. Как думаете, стоит ли попробовать использовать индикатор для выхода из позиции?

Спасибо за внимание!

ссылка на оригинал статьи https://habr.com/ru/articles/1025238/