Skip to main content

Начало работы с Numerai сигналов: Сентимент анализ

В этой статье показано, как использовать Stock News API и Fin BERT для функции Numerai Signals.
Created on July 2|Last edited on February 16
Этот отчет является переводом « Getting Started with Numerai Signals: Sentiment Analysis » Carlo Lepelaars
Numerai Signals
Этот отчет является переводом «Getting Started with Numerai Signals: Sentiment Analysis » Carlo Lepelaars

Kaggle Notebook→

Введение

В этой статье мы познакомим вас с функцией Numerai Signals и покажем, как начать работу на примере сентимент анализа. В частности, мы будем смотреть заголовки новостей, чтобы предсказывать рейтинг акций на основе сентимент оценоки. Для общего введения в Numerai можете рассмотреть также другие мои статьи.

Что такое сигналы Numerai?

Numerai - это краудсорсинговый хедж-фонд с искусственным интеллектом, который работает на основе прогнозов, сделанных учеными по всему миру. Классическая функция Numerai предоставляет вам анонимные данные, которые можно смоделировать с помощью машинного обучения. Numerai Signals обобщает эту идею и позволяет использовать любые данные, которые вы хотите. Это позволяет специалистам по обработке данных проявлять творческий подход к источникам данных, а также к методам моделирования.


Чтобы получить представление о концепции Numerai Signals, посмотрите это короткое видео:


Итак: давайте погрузимся в полный цикл подачи заявок на Numerai Signals с использованием сентимент анализа!
Полный код также реализован в Kaggle Notebook. Мы начнем с самой важной части любого конвейера обработки данных. А именно получение хороших данных! Для этого мы должны использовать чистый API (интерфейс прикладного программирования) для стандартных новостей.

 Полный код →

Stock News API (stocknewsapi.com)

Stock News API (stocknewsapi.com) - это онлайн-сервис для удобного поиска новостных статей, основанных на широком спектре акций. Он собирает статьи из примерно 10 различных источников новостей и предоставляет дополнительную информацию для облегчения анализа данных. Это позволяет нам получать самую последнюю статью за каждую неделю в удобном формате JSON, который мы будем анализировать в CSV для объединения данных. Stock News API предлагает 14-дневную бесплатную программу отслеживания со 100 вызовами API. После этого вы можете подписаться на 20 000 вызовов API в месяц за 20 долларов. API использует следующие источники новостей:


Получить данные можно с помощью Python и библиотеки запросов. Для получения доступа вам понадобится ключ / токен API из Stock News API. Например, вот как вы получаете последнюю новость об акциях Tesla, Inc. (TSLA):
import requests
api_key = "YOUR_API_KEY_HERE"
api_request = f"https://stocknewsapi.com/api/v1?tickers=TSLA&items=1&token={api_key}"
data = requests.get(api_request).json()['data']
print(data)
Вывод JSON будет выглядеть примерно так:
[
{
"news_url": "https://www.barrons.com/articles/fund-bought-tesla-apple-microsoft-stock-sold-att-51613063598",
"image_url": "https://cdn.snapi.dev/images/v1/a/7/im-298821.jpg",
"title": "A Huge Fund Bought Tesla, Apple, and Microsoft Stock. Here's What It Sold.",
"text": "Dutch pension fund PGGM initiated a position in EV-giant Tesla, bought more Apple and Microsoft stock, and sold AT&T stock in the fourth quarter.",
"source_name": "Barrons",
"date": "Sun, 14 Feb 2021 07:00:00 -0500",
"topics": ["paywall"],
"sentiment": "Neutral",
"type": "Article",
"tickers": [
"AAPL",
"MSFT",
"TSLA",
"T"
]
}
Для нашей задачи нас интересуют:
  • "title", чтобы делать сентимент прогнозы
  • «дата» для объединения данных. Обратите внимание, что дата включает часовой пояс (в данном случае GMT-5) и может варьироваться в зависимости от источника новостей.
  • "тикеры", чтобы присоединиться к прогнозу с правильной акцией.
Обратите внимание, что API предоставляет собственную классификацию настроений, которая может быть «Positive», «Neutral» или «Negative». Чтобы сделать нашу проблему более решаемой, мы отфильтруем статьи, которые относятся к категории «Neutral». Мы предполагаем, что поляризованные статьи являются наиболее важными для прогнозирования краткосрочных рыночных колебаний.

Получение данных (Data Retrieval)

Соответствующие данные, которые нам нужны, - это новостные статьи за прошлую неделю. Мы фильтруем для последних 7 дней, указывая & days = 7 в вызове API, и фильтруем статьи с помощью & type = article.
Кроме того, API ограничивает один вызов до 50 статей (& items = 50). В неделю может быть более 50 новостных статей для одного биржевого тикера, поэтому в этом случае мы создаем еще один вызов, пока у нас не будут все статьи за неделю или пока не будет достигнут указанный нами предел. Мы установили этот предел, чтобы не тратить напрасно вызовы API.


Цикл (the loop) для получения всех новостных статей Tesla Inc. (TSLA), с использованием дат которые мы вычислили ранее, будет выглядеть примерно так,:
import requests
import pandas as pd

page_cutoff_point = 10
dfs = []
i = 1
while i <= page_cutoff_point:
api_request = f"https://stocknewsapi.com/api/v1?tickers=TSLA&items=50&type=article&page={i}sortby=rank&days=7&token={api_key}"
data = requests.get(api_request).json()['data']
df = pd.DataFrame(data)
if df.empty:
break
dfs.append(df)
i += 1
tesla_df = pd.concat(dfs)
Окончательный Data Frame Pandas можно получить, запустив этот цикл по всем соответствующим биржевым тикерам и объединив их.

 Полный код →

Преодоление данных (Data Wrangling )

Чтобы подготовить данные для прогнозов сентимент анализа, мы выполняем ряд шагов. На высоком уровне выполняются следующие операции предварительной обработки:
  1. Удалите ненужные столбцы ('text', 'news_url', 'image_url', 'themes' и 'source_name')
  2. Отфильтруйте все статьи с "Neutral" sentiment.
  3. Преобразуйте все метки времени в UTC (ВКВ) (всемирное координированное время), чтобы все строки имели общий формат даты и времени (datetime).
Мы хотели бы иметь одну уникальную строку для каждого тикера акций, содержащую все заголовки новостей. Мы делаем это путем агрегирования данных для каждого тикера и каждой недели. Некоторые новостные статьи могут относиться к нескольким биржевым тикерам, поэтому сначала мы делаем пересечение набора, чтобы найти все соответствующие тикеры для сигналов Numerai. Все 2090 перекрывающихся тикеров уже вычислены и сохранены здесь как файл pickle, но в приведенном ниже коде показано, как вы можете вычислить их самостоятельно. Новые акции постоянно проходят листинг и делистинг, поэтому имеет смысл регулярно вычислять пересекающиеся биржевые тикеры.
relevant_tickers=stock_news_tickersnumerai_tickersrelevant\_tickers = stock\_news\_tickers \cap numerai\_tickers
import pickle
import requests
import pandas as pd

# Get all tickers available in stocknewsapi.com
ticker_request = f"https://stocknewsapi.com/api/v1/account/tickersdbv2?token={api_key}"
json_data = requests.get(ticker_request).json()['data']
stock_news_tickers = []
for row in json_data:
stock_news_tickers.append(row['ticker'])

# Get all Numerai Signals tickers
ticker_df = pd.read_csv("https://numerai-signals-public-data.s3-us-west-2.amazonaws.com/signals_ticker_map_w_bbg.csv")
numerai_tickers = set(ticker_df['ticker'])

# Compute intersection
relevant_tickers = list(set(stock_news_tickers).intersection(numerai_tickers))


Объединяя все заголовки новостей, которые относятся к тикеру, мы получаем строку для каждого тикера и заголовка, разделенные токеном [SEP] [SEP], который мы будем использовать для пакетного ввода заголовка новостей.
data.loc[:, 'title'] = data['title'] + " [SEP] "
Наконец, мы объединяем новый DataFrame в тикерах Numerai, чтобы получить формат тикера Bloomberg, который нам понадобится позже на этапе отправки.
# Aggregate news headlines for each ticker
dfs = []
for ticker in relevant_tickers:
aggregated = data[data['tickers'].apply(lambda x: ticker in x)].resample("W-fri", on='date').sum()
aggregated = aggregated.drop("tickers", axis=1)
aggregated['ticker'] = ticker
aggregated = aggregated.drop_duplicates("ticker", keep='last')
if aggregated.empty:
continue
dfs.append(aggregated)
new_df = pd.concat(dfs)
new_df['title'] = new_df['title'].astype(str)
merged = new_df.merge(ticker_df, on='ticker')
merged = merged.drop("yahoo", axis=1).dropna()

 Полный код →

Вывод (FinBERT)

И теперь мы, наконец, готовы заняться машинным обучением!
FinBERT - это модель-трансформер, разработанная в рамках магистерской диссертации Догу Араси и полностью обученная статьям о финансовых новостях. Поскольку FinBERT обучен работе с финансовыми новостями, это хорошая модель для решения задач обработки естественного языка (NLP) в финансовой сфере. FinBERT - это языковая модель, основанная на модели BERT (двунаправленные представления кодировщика), разработанной Google в 2018 году. Более подробное объяснение моделей Transformer вы можете найти в статье «Transformer Deep Dive».
Мы будем использовать библиотеку HuggingFace Transformers и PyTorch, чтобы получать сентимент прогнозы для всех заголовков. В частности, мы будем использовать предварительно обученную модель FinBERT, доступную на веб-сайте HuggingFace.


На высоком уровне конвейер (the pipline) проходит следующие этапы:
  1. Пакетируйте(Batch) заголовки новостей, чтобы у нас не закончилась память графического ( GPU) процессора.
def _chunks(lst, n):
""" Yield n-sized chunks from list. """
for i in range(0, len(lst), n):
yield lst[i:i + n]

batch_size = 8
for row in data:
ticker_headlines = row.split(" [SEP] ")[:-1]
.
for batch in _chunks(ticker_headlines, batch_size):
.
2. Для каждой партии (batch) токенизируйте заголовки. input_ids и attention_mask будут входными данными для модели FinBERT.
import torch
from transformers import AutoTokenizer
batch = ["The stock will go up in the upcoming week!", "Earnings are down for Q3"]
tokenizer = AutoTokenizer.from_pretrained("ipuneetrathore/bert-base-cased-finetuned-finBERT")
encoded = tokenizer(batch, add_special_tokens=True, max_length=200,
padding='max_length',return_attention_mask=True,
return_tensors='pt', truncation=True)
input_ids = torch.cat([encoded['input_ids']], dim=0).to('cuda')
attention_mask = torch.cat([encoded['attention_mask']], dim=0).to('cuda')
3. Получите активации softmax из предварительно обученной модели FinBERT.
import torch.nn.functional as F
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained("ipuneetrathore/bert-base-cased-finetuned-finBERT").eval().to('cuda')
model_output = model(input_ids, token_type_ids=None, attention_mask=attention_mask)
logits = model_output[0]
softmax_output = F.softmax(logits, dim=1).cpu().detach().numpy()
4. Вычислите сентимент анализа и возьмите среднее значение для всех прогнозов на биржевом тикере.
score(T)=1ni=1npositiveinegativeiscore(T) = \frac{1}{n} \sum_{i=1}^{n} positive_i - negative_i

где TT обозначает конкретный биржевой тикер, nn - общее количество заголовков.
Для единственной оценки тональности:
sentiment_score = softmax_output[:, 2] - softmax_output[:, 0]
После того, как мы собрали все sentiment оценки для данного тикера:
mean_score = np.array(sent_scores_ticker).ravel().mean()
5. Оценки тональности будут в диапазоне [1...1][-1...1],, но Numerai Signals принимает только прогнозы в диапазоне [0...1][0...1] Поэтому мы масштабируем прогнозы оценки настроений для всех биржевых тикеров до диапазона [0...1][0...1] с помощью MinMaxScaler scikit-learn.
from sklearn.preprocessing import MinMaxScaler
def scale_sentiment(sentiments):
""" Scale sentiment scores from [-1...1] to [0...1] """
mm = MinMaxScaler()
sent_proc = np.array(sentiments).reshape(-1, 1)
return mm.fit_transform(sent_proc)

 Полный код →

Подача

Теперь, когда у нас есть прогнозы, мы почти готовы к отправке в Numerai Signals! Для завершения DataFrame добавляется столбец, указывающий, что прогнозы относятся к «живым» данным (например, предстоящая неделя). Также делаем столбец даты, обозначающий наступающую пятницу. Наконец, DataFrame записывается в CSV и выгружается с помощью Numerai API.
import numerapi
from datetime import datetime
from dateutil.relativedelta import relativedelta, FR

# API settings for submitting to Numerai
NMR_PUBLIC_ID = "YOUR PUBLIC KEY"
NMR_SECRET_KEY = "YOUR SECRET KEY"
MODEL_NAME = "YOUR MODEL NAME"
SUB_PATH = "finbert_submission.csv"

# Initialize API with API Keys and add data_type column
NAPI = numerapi.SignalsAPI(NMR_PUBLIC_ID, NMR_SECRET_KEY)
final_df.loc[:, "data_type"] = "live"
# Add date column denoting last Friday
friday = int(str((datetime.now() + relativedelta(weekday=FR(-1))).date()).replace("-", ""))
final_df["friday_date"] = friday

# Save final DataFrame to CSV and upload predictions
cols = ["bloomberg_ticker", "friday_date", "data_type", "signal"]
final_df[cols].reset_index(drop=True).to_csv(SUB_PATH, index=False)
model_id = NAPI.get_models()[MODEL_NAME]
NAPI.upload_predictions(SUB_PATH, model_id=model_id)
После отправки вы можете проверить свою заявку на странице вашей модели на странице signal.numer.ai/tournament. Это должно выглядеть так как представлено ниже. Подавать должно быть не менее 10 тикеров акций, а всего около 5300 международных акций, по которым вы можете подавать еженедельные заявки. Общая сумма может меняться в неделю в зависимости от новых списков акций и исключения из них.

Пример проверки отправки на signal.numer.ai/tournament

 Полный код →

Оценка и ставки

Обратите внимание, что прогнозы из этого конвейера(the pipline) должны обозначать ранжирование всех указанных нами акций. Близость к 0 означает, что мы предпологаем, что на следующей неделе акции пойдут вниз, а близкие к 1 мы считаем, что они пойдут вверх. Numerai объединит прогнозы всех пользователей, чтобы создать портфель акций. Акции с оценкой сигнала, близкой к 0, будут проданы, а акции, близкие к 1, будут куплены. Пользователи оцениваются на основе показателя корреляции Спирмена(Spearman Correlation score), который будет иметь их прогнозы на предстоящей неделе. Кроме того, уникальность сигнала оценивается по шкале Meta Model Contribution (MMC). Numerai позволяет пользователям делать ставки на свои прогнозы, используя криптовалюту Numeraire (NMR), и пользователи будут зарабатывать или терять Numeraire на основе этих показателей.
Для получения дополнительной информации и общих советов по участию в Numerai ознакомьтесь с моей статьей о Numerai. Также ознакомьтесь с документацией по сигналам Numerai для получения дополнительных сведений об участии в сигналах Numerai.
Примеры прогнозов для сигналов Numerai


Вот и все! Я надеюсь, что эта статья вдохновила вас начать работу с Numerai Signals! Я также надеюсь, что вы узнали из этого отчета некоторые новые концепции. Если вы хотите, вы можете ознакомиться с разделом ресурсов ниже, чтобы узнать больше о сигналах Numerai, источниках данных, рекомендуемых статьях и ссылках на сообщество Numerai.
Если у вас есть какие-либо вопросы или отзывы, не стесняйтесь оставлять комментарии ниже. Вы также можете связаться со мной в Twitter @carlolepelaars.

Ресурсы

Образовательные ресурсы

Источники данных

Рекомендуемые статьи

Сообщество

Iterate on AI agents and models faster. Try Weights & Biases today.