Используйте свой компьютер для принятия обоснованных решений при торговле акциями: Практическое введение — Часть 4: Парсинг прибыли на акцию (EPS)

Table Of Content
- Введение
- Подход
- **Парсинг Yahoo Finance: Прибыль на акцию**
- Column Non-Null Count Dtype
- **Получение цен акций для компании**
- **Получение статистики по S&P 500**
- **Получение доходности акций и объема с Yahoo Finance**
- **Объединение всех частей вместе**
- Доходность выше S&P500
- Column Non-Null Count Dtype
- **Анализ и визуализация**
- **Заключение**
Введение
Я точно помню день, когда начал заниматься торговлей акциями — это было 28 апреля 2020 года, когда Google объявил о своей прибыли за первый квартал и поднялся на более чем 7% за 1 день. Я подумал, что у Facebook похожий бизнес, и он представит отчет на следующий день, поэтому я вложил свои первые $1000 в акции Facebook. И это была быстрая победа — они поднялись на более чем 10% за 1 день после показа "стабильности в доходе от рекламы после падения в марте". Та же стратегия сработала и для акций Facebook во время отчета за второй квартал: они поднялись на 8% после сообщения о более высокой прибыли на акцию $1.8 вместо ожидаемых $1.39. После второго успеха я наконец решил провести полноценный анализ в масштабе и написать статью об этом, которую вы можете прочитать ниже.
Гипотеза состоит в следующем: "Финансовые результаты становятся чрезвычайно важными в трудные времена (например, COVID-19 в 2020 году): некоторые устойчивые отрасли получают диспропорционально высокий объем торговли акциями и рост цен".
Я хотел бы найти хотя бы одну сильную идею, которая будет работать большую часть времени. Просто как тот Металлический человек из Слайго на картинке, указывающий на вход в Слайго уже несколько веков.
Подход
Владение акциями дает вам право на получение части прибыли компании. В идеальном мире цена акций должна зависеть от прибыли компании, так как это дисконтированные будущие прибыли. Если в какой-то момент компания зарабатывает больше, чем раньше, это может означать, что рост компании ускоряется, и цена акций должна быть выше. Поэтому одним из самых важных финансовых показателей является прибыль на акцию (EPS). Каждый квартал аналитики делают прогнозы о прибыли (или убытках) компании, а затем сравнивают эти прогнозы с фактической отчетностью EPS. Если компания делает лучше, чем прогнозировалось, это должно вызвать рост цены акций, и наоборот.
В этой статье мы собираемся проверить это в масштабе — для сотен акций, которые сообщили о прибыли во втором квартале 2020 года. Мы проверим зависимость изменения цены акций от фактической прибыли на акцию, прогнозируемой прибыли на акцию и Surprise (= фактическая_EPS/прогнозируемая_EPS-1). Ниже представлен краткий обзор разделов и тем, рассмотренных в статье:
В разделе Парсинг Yahoo Finance: Прибыль на акцию вы узнаете, как получить информацию о прибыли на акцию для широкого спектра компаний за указанный период времени (начиная с одного дня), парся ее с веб-сайта Yahoo Finance. Затем, в разделе Упаковка всего в одну функцию парсинга, вы объедините все, что вы узнали в предыдущем разделе, в одну функцию, чтобы получить еженедельную статистику по датам и EPS. После этого, в разделе Получение данных о ценах акций для компании, вы рассмотрите, как можно получить данные о доходности акций и объеме для определенной компании. В разделе Получение статистики S&P 500, вы узнаете, как получить данные S&P 500 для оценки того, как определенный символ справляется по сравнению с индексом. В разделе Получение доходности акций и объема с Yahoo Finance вы узнаете, как получить данные о доходности акций и объеме для всех тикеров, найденных в Yahoo Finance. В разделе Слияние всех частей вместе вы получите объединенный фрейм данных со статистикой из всех предыдущих частей. И, наконец, в разделе Анализ и визуализация вы увидите примеры графиков, построенных на основе набора данных.
Эта статья является четвертой частью серии, которая охватывает использование компьютерных технологий для принятия обоснованных решений в торговле акциями. Обратитесь к части 1, чтобы быть проведенным через процесс настройки рабочей среды, необходимой для следования примерам, представленным в остальной части серии. Затем, часть 2 рассматривает несколько известных финансовых API, позволяющих получать и анализировать данные о акциях программно. В части 3 вы исследовали, влияет ли рынок акций на новости.
Парсинг Yahoo Finance: Прибыль на акцию
Вам может быть интересна информация о прибыли на акцию для широкого спектра компаний за определенный период времени. Вы можете получить эту информацию, выполнив парсинг веб-сайта Yahoo Finance по адресу https://finance.yahoo.com/calendar/earnings:
Для парсинга вам понадобится установить библиотеку BeautifulSoup в Colab:
!pip install beautifulsoup4Затем убедитесь, что импортированы следующие библиотеки:
import requests
from bs4 import BeautifulSoup
import pandas as pd
import numpy as npПредположим, вы хотите получить данные за 2020–07–27. Вам нужно указать следующий URL:
url = “https://finance.yahoo.com/calendar/earnings?from=2020-07-26&to=2020-08-01&day=2020-07-27"Затем отправьте следующий запрос:
r = requests.get(url)Чтобы убедиться, что все прошло как ожидалось, проверьте статус запроса:
r.okЕсли все в порядке, вы должны увидеть:
TrueТеперь вы можете перейти к контенту:
r.contentОднако ваша задача - найти данные таблицы внутри него. Это можно легко сделать с помощью BeautifulSoup следующим образом:
soup = BeautifulSoup(r.text)
table = soup.find_all(‘table’)
len(table)#output:
1Найдена только одна таблица, что хорошо. Теперь получим все названия столбцов таблицы:
spans = soup.table.thead.find_all(‘span’)
columns = []
for span in spans:
print(span.text)
columns.append(span.text)Вот столбцы (обратитесь к скриншоту на рисунке 1, чтобы убедиться, что все столбцы были найдены):
Символ
Компания
Время звонка о прибыли
Прогноз прибыли на акцию
Отчетная прибыль на акцию
Сюрприз(%)Теперь вы можете перейти к строкам:
rows = soup.table.tbody.find_all(‘tr’)
len(rows)Как видите, у вас есть 100 строк в таблице, полученной с веб-страницы. В следующем фрагменте кода вы загружаете строки в pandas dataframe, читая их построчно:
stocks_df = pd.DataFrame(columns=columns)for row in rows:
elems = row.find_all(‘td’)
dict_to_add = {}
for i,elem in enumerate(elems):
dict_to_add[columns[i]] = elem.text
stocks_df = stocks_df.append(dict_to_add, ignore_index=True)В результате данные в dataframe должны выглядеть следующим образом:
stocks_df
У вас должно быть 100 полученных строк. Мы будем использовать все столбцы, но обратите внимание, что значения времени звонка о прибыли не указаны во многих случаях. Некоторые другие проблемы в наборе данных:
- отсутствующие значения: некоторые значения для прогноза прибыли на акцию, отчетной прибыли на акцию и сюрприза неизвестны
- некоторые значения в этих столбцах являются целыми числами: их нужно преобразовать в числа с плавающей запятой
Чтобы избавиться от отсутствующих значений, вы можете применить следующие фильтры к набору данных:
filter1 = stocks_df[‘Сюрприз(%)’]!=’-’
filter2 = stocks_df[‘Прогноз прибыли на акцию’]!=’-’
filter3 = stocks_df[‘Отчетная прибыль на акцию’]!=’-’stocks_df_noMissing = stocks_df[filter1 & filter2 & filter3]Вы должны увидеть, что количество строк уменьшилось после этого:
len(stocks_df_noMissing)79На следующем шаге вы решаете другую проблему и преобразуете все значения в столбцах "Прогноз прибыли на акцию", "Отчетная прибыль на акцию" и "Сюрприз" в числа с плавающей запятой:
stocks_df_noMissing[‘Прогноз прибыли на акцию’] = stocks_df_noMissing[‘Прогноз прибыли на акцию’].astype(float)stocks_df_noMissing[‘Отчетная прибыль на акцию’] = stocks_df_noMissing[‘Отчетная прибыль на акцию’].astype(float)stocks_df_noMissing[‘Сюрприз(%)’] = stocks_df_noMissingstocks_df_noMissing[‘Сюрприз(%)’].astype(float)В результате у вас должен быть следующий dataframe:
stocks_df_noMissing.info()<class ‘pandas.core.frame.DataFrame’>
Int64Index: 79 entries, 0 to 99
Data columns (total 6 columns):
# Column Non-Null Count Dtype
— — — — — — — — — — — — — — -
0 Символ 79 non-null object
1 Компания 79 non-null object
2 Время звонка о прибыли 79 non-null object
3 Прогноз прибыли на акцию 79 non-null float64
4 Отчетная прибыль на акцию 79 non-null float64
5 Сюрприз(%) 79 non-null float64import requests
from bs4 import BeautifulSoup
import pandas as pd
import numpy as nm
from datetime import datetime, timedelta, date
def get_scrapped_week(from_dt, to_dt):
url = "https://finance.yahoo.com/calendar/earnings"
offset = 0
size = 100
fst = 1
for day_date in (datetime.strptime(from_dt, '%Y-%m-%d') + timedelta(n) for n in range(6)):
day_dt = datetime.strftime(day_date, '%Y-%m-%d')
print(day_dt)
while True:
params = {'from': from_dt, 'to': to_dt, 'day': day_dt, 'offset':offset, 'size': size}
r = requests.get(url, params=params)
soup = BeautifulSoup(r.text)
if fst == 1:
spans = soup.table.thead.find_all('span')
columns = []
for span in spans:
print(span.text)
columns.append(span.text)
stocks_df = pd.DataFrame(columns=columns)
fst = 0
rows = soup.table.tbody.find_all('tr')
for row in rows:
elems = row.find_all('td')
dict_to_add = {}
dict_to_add['Date'] = day_dt
for i,elem in enumerate(elems):
dict_to_add[columns[i]]=elem.text
stocks_df = stocks_df.append(dict_to_add, ignore_index=True)
if len(rows) != 100:
print(len(rows)+offset)
offset = 0
break
else:
offset = offset + 100
return stocks_df
stocks_df = get_scrapped_week('2020-07-05', '2020-07-11')
stocks_df = stocks_df.append(get_scrapped_week('2020-07-12', '2020-07-18'))
stocks_df = stocks_df.append(get_scrapped_week('2020-07-19', '2020-07-25'))
stocks_df = stocks_df.append(get_scrapped_week('2020-07-26', '2020-08-01'))
filter1 = stocks_df['Surprise(%)']!='-'
filter2 = stocks_df['EPS Estimate']!='-'
filter3 = stocks_df['Reported EPS']!='-'
stocks_df_noMissing = stocks_df[filter1 & filter2 & filter3]
stocks_df_noMissing['EPS Estimate'] = stocks_df_noMissing['EPS Estimate'].astype(float)
stocks_df_noMissing['Reported EPS'] = stocks_df_noMissing['Reported EPS'].astype(float)
stocks_df_noMissing['Surprise(%)'] = stocks_df_noMissing['Surprise(%)'].astype(float)
stocks_df_noMissing.set_index('Symbol')Получение цен акций для компании
В этом разделе мы рассмотрим, как получить данные о доходности акций и объеме для определенной компании. Для начала вам нужно установить библиотеку yfinance, необходимую для получения данных о акциях:
!pip install yfinanceДавайте начнем с попытки получить одну акцию на одну дату, рассчитав доходность и изменение объема.
import yfinance as yf
import numpy as np
import pandas as pd
from datetime import datetime
from datetime import timedeltaВот что мы видим для символа FB:
row = stocks_df_noMissing[stocks_df_noMissing['Symbol']=='FB']
print(row)Symbol Company Earnings Call Time EPS Estimate Reported EPS Surprise(%) Date776 FB Facebook, Inc. Time Not Supplied 1.39 1.8 29.59 2020-07-29Вы можете легко извлечь дату из этого:
date = row['Date'].values[0]
print(date)2020-07-29Теперь давайте получим данные о ценах акций для этой даты и близлежащих дат с использованием библиотеки yfinance:
date = datetime.strptime(row['Date'].values[0], '%Y-%m-%d')print(date + timedelta(days=3))
print(date - timedelta(days=1))
ticker = yf.Ticker('FB')
hist = yf.download("FB", start= date - timedelta(days=1), end=date + timedelta(days=3))Вывод должен выглядеть следующим образом:
2020–08–01 00:00:00
2020–07–28 00:00:00
[*********************100%***********************] 1 of 1 completedЕсли вы проверите переменную hist, она должна содержать следующие данные:
На следующем шаге вы определяете рост цены акций и объема за последние два дня наблюдения.
hist['r2'] = np.log(hist['Open'] / hist['Open'].shift(2))hist['volume_rise'] = np.log(hist['Volume'] / hist['Volume'].shift(2))Таким образом, обновленный набор данных показывает вам рост объема и цены для Facebook:
Если вы хотите посмотреть последнее значение доходности (r2 = доходность за 2 дня на следующее утро после события) в наборе данных, вы можете извлечь его следующим образом:
hist.r2.values[-1]0.10145051589492579И изменение объема торговли можно просмотреть следующим образом:
hist.volume_rise.values[-1]1.361648662790037Получение статистики по S&P 500
Как упоминалось в части 2, распространенной практикой является сравнение производительности акций с индексом S&P 500. Для начала давайте получим данные по индексу S&P 500 за указанный период времени:
import pandas_datareader.data as pdr
from datetime import datestart = datetime(2020,7,1)
end = datetime(2020,8,10)print(f’Период 1 месяц до сегодняшнего дня: {start} до {end} ‘)spx_index = pdr.get_data_stooq(‘^SPX’, start, end)# Индекс S&P500 рос почти весь июль 2020 года → необходимо скорректировать рост акций после даты отчетностиspx_index[‘Open’].plot.line()
Вы можете применить к индексу ту же технику, которую использовали в предыдущем разделе для определения роста цены акций, рассчитав результаты для ежедневного 2-дневного дохода в июле-августе 2020 года:
spx_index[‘r2’] = np.log(np.divide(spx_index[‘Open’] , spx_index[‘Open’].shift(2)))spx_index[‘r2’].plot.line()
В табличной форме те же данные будут выглядеть следующим образом:
spx_index.head(30)
Open High Low Close Volume r2 Date2020–08–10 3356.04 3363.29 3335.44 3360.47 2565981272 NaN
2020–08–07 3340.05 3352.54 3328.72 3351.28 2279160879 NaN
2020–08–06 3323.17 3351.03 3318.14 3349.16 2414075395 -0.009843
2020–08–05 3317.37 3330.77 3317.37 3327.77 2452040105 -0.006813
2020–08–04 3289.92 3306.84 3286.37 3306.51 2403695283 -0.010056
2020–08–03 3288.26 3302.73 3284.53 3294.61 2379546705 -0.008814
…В следующем фрагменте кода вы заполняете массив доходности S&P для соответствующих акций. Важно отметить, что если есть «пробел» для определенной даты, мы берем ближайшее предыдущее значение:
array_returns_snp500 = []for index,row in stocks_df_noMissing.iterrows(): start_dt = datetime.strptime(row[‘Date’], ‘%Y-%m-%d’) — timedelta(days = 1) end_dt = datetime.strptime(row[‘Date’], ‘%Y-%m-%d’) + timedelta(days = 3) # у нас нет пропусков более 4 дней -> попытаемся найти ближайшее значение доходности S&P500 в таблице:
cur_dt = end_dt
while cur_dt >= start_dt:
rez_df = spx_index[cur_dt.strftime(‘%Y-%m-%d’)]
if len(rez_df)>0:
array_returns_snp500.append(rez_df.r2.values[0])
break
else:
cur_dt = cur_dt — timedelta(days = 1)Чтобы убедиться, что все работает как ожидается, вы можете проверить длину обоих наборов данных: нового созданного array_returns_snp500 и ранее введенного stocks_df_noMissing в этой статье:
len(array_returns_snp500)1698len(stocks_df_noMissing)1698В обоих случаях у вас должно быть одинаковое количество.
Получение доходности акций и объема с Yahoo Finance
В этом разделе мы рассмотрим, как получить данные о доходности акций и объеме для всех тикеров, найденных в Yahoo Finance. В следующем скрипте вы можете рассчитать доходность за 2 дня по открытой цене после отчетности относительно цены 2 дня назад для каждого тикера:
array_tickers = []
array_returns = []
array_volume_rise = []
array_volume_usd = []
array_snp500 = []
for index,row in stocks_df_noMissing.iterrows():
start_dt = datetime.strptime(row[‘Date’], ‘%Y-%m-%d’) — timedelta(days = 1)
end_dt = datetime.strptime(row[‘Date’], ‘%Y-%m-%d’) + timedelta(days = 3)
hist = yf.download(row[‘Symbol’], start = start_dt, end = end_dt)
# Нам нужны полные данные: объем и цена для всех дат для расчета доходности и роста объема
# ТАКЖЕ: если end_dt - нерабочий день (суббота, воскресенье), мы не можем непосредственно рассчитать статистику доходности
if len(hist)<4:
continue
hist[‘r2’] = np.log(np.divide(hist[‘Open’] , hist[‘Open’].shift(2)))
hist[‘volume_rise’] = np.log(np.divide(hist[‘Volume’], hist[‘Volume’].shift(2)))
hist[‘volume_usd’] = hist[‘Volume’] * hist[‘Open’]
print(row)
print(index)
print(‘ — — — — — — — ‘)
array_tickers.append(row[‘Symbol’])
array_returns.append(hist.r2.values[-1])
array_volume_rise.append(hist.volume_rise.values[-1])
array_volume_usd.append(hist.volume_usd.values[-1])
# Мы добавляем только значения S&P для акций, у которых есть все данные
array_snp500.append(array_returns_snp500[index])Скрипт генерирует огромный вывод (приблизительно 1000 записей, если вы помните). Ниже приведен только фрагмент:
[*********************100%***********************] 1 of 1 completedSymbol AEOJF
Company AEON Financial Service Co., Ltd.
Earnings Call Time Time Not Supplied
EPS Estimate 14.03
Reported EPS -5
Surprise(%) -135.67
Date 2020–07–07
Name: 37, dtype: object
37
— — — — — — — [*********************100%***********************] 1 of 1 completedSymbol BBBY
Company Bed Bath & Beyond Inc.
Earnings Call Time Time Not Supplied
EPS Estimate -1.22
Reported EPS -1.96
Surprise(%) -60.39
Date 2020–07–07
Name: 43, dtype: object
43
— — — — — — —
…В заключение, было бы интересно узнать, сколько акций имеют следующие финансовые показатели: объем торгов, рост объема (в количестве акций) и доходность:
len(array_tickers)1003Объединение всех частей вместе
Наконец, давайте объединим все финансовые данные, которые мы получили до сих пор, чтобы увидеть полную "картину" для каждой из торгуемых акций. Для этого мы создадим dataframe:
returns_df = pd.DataFrame(columns=['Тикер', 'Доходность', 'Рост объема', 'Объем торгов в долларах', 'Доходность S&P500'])И загрузим его данными из созданных нами наборов данных:
returns_df = pd.DataFrame([array_tickers, array_returns, array_volume_rise, array_volume_usd, array_snp500]).transpose()
returns_df.columns = ['Тикер', 'Доходность', 'Рост объема', 'Объем торгов в долларах', 'Доходность S&P500']
returns_df.set_index('Тикер', inplace=True)
returns_df.dropna(inplace=True)
returns_df['Доходность'] = returns_df['Доходность'].astype(float)
returns_df['Рост объема'] = returns_df['Рост объема'].astype(float)
returns_df['Объем торгов в долларах'] = returns_df['Объем торгов в долларах'].astype(float)
returns_df['Доходность S&P500'] = returns_df['Доходность S&P500'].astype(float)
returns_df['Доходность в %'] = np.exp(returns_df['Доходность'])
returns_df['Рост объема в %'] = np.exp(returns_df['Рост объема'])Также было бы интересно узнать, какие компании имели доходность выше S&P500, и добавить эту информацию в набор данных:
# Доходность выше S&P500
returns_df['Скорректированная доходность'] = returns_df['Доходность'] - returns_df['Доходность S&P500']
returns_df['Скорректированная доходность в %'] = np.exp(returns_df['Скорректированная доходность'])Метрика "Скорректированная доходность" служит показателем относительного роста по отношению к общему индексу S&P500.
Возможно, вам захочется создать набор гистограмм для каждого столбца в dataframe returns_df, чтобы увидеть представление о распределении данных. Если вы хотите иметь гистограммы без значений INF, вы можете заменить их в dataframe следующим образом:
returns_df = returns_df.replace([np.inf, -np.inf], np.nan)
returns_df.hist(figsize=(20,10), bins=100)
На следующем шаге вы объединяете dataframe returns_df с dataframe stocks_df_noMissing:
stocks_and_returns = stocks_df_noMissing.set_index('Symbol').join(returns_df)
stocks_and_returns.head()Возможно, вам захочется удалить значения INF из итогового набора данных:
stocks_and_returns_no_missing = stocks_and_returns.replace([np.inf, -np.inf], np.nan).dropna()Вот что у вас должно получиться в итоге:
stocks_and_returns_no_missing.info()
<class 'pandas.core.frame.DataFrame'>
Index: 997 entries, AA to ZEN
Data columns (total 14 columns):
# Column Non-Null Count Dtype
--- --- --- --- --- --- --- --- --- --- --- --- --- -
0 Company 997 non-null object
1 Earnings Call Time 997 non-null object
2 EPS Estimate 997 non-null float64
3 Reported EPS 997 non-null float64
4 Surprise(%) 997 non-null float64
5 Date 997 non-null object
6 Returns 997 non-null float64
7 Volume Rise 997 non-null float64
8 Volume Trade USD 997 non-null float64
9 Returns S&P500 997 non-null float64
10 Returns in % 997 non-null float64
11 Volume Rise in % 997 non-null float64
12 Adj. Returns 997 non-null float64
13 Adj. Returns in % 997 non-null float64
dtypes: float64(11), object(3)
memory usage: 116.8+ KBПришло время получить результаты нашего анализа. Какие 50 самых торгуемых акций вокруг даты публикации квартального отчета?
top50_volume = stocks_and_returns_no_missing.sort_values(by='Volume Trade USD', ascending=False).head(50)
print(top50_volume)
Теперь, какие 200 самых торгуемых акций? Запустите этот код, чтобы узнать:
top200_volume = stocks_and_returns_no_missing.sort_values(by='Volume Trade USD', ascending=False).head(200)
print(top200_volume)Анализ и визуализация
Давайте поиграем с нашим набором данных, пытаясь извлечь интересную информацию из него. В частности, было бы интересно посмотреть на распределение доходности. Чтобы увидеть визуальное подведение итогов информации, мы построим несколько графиков:
На следующем графике вы можете увидеть график столбцов Surprise и Returns в наборе данных top50_volume. Эта информация может быть очень полезной, если вы хотите начать инвестировать в наиболее прибыльные акции.
top50_volume[['Surprise(%)','Returns in %']].plot.scatter(x='Surprise(%)', y='Returns in %')
Есть несколько наблюдений:
- большинство акций показывают результат около ожидаемой прибыли на акцию (EPS)
- 2 выброса Surprise показали 60-кратный и 80-кратный фактический EPS по сравнению с прогнозируемым и небольшую положительную доходность
На следующем графике вы можете увидеть ту же зависимость, но для набора данных top200_volume.
top200_volume[['Surprise(%)','Returns in %']].plot.scatter(x='Surprise(%)', y='Returns in %')
Несколько замечаний по этому графику:
- есть несколько новых выбросов с Surprise(%) > 100% и Surprise(%) `< 2000%, которые показали очень впечатляющую доходность 10%-30% всего за 2 дня после объявления квартальных результатов
- сильное отрицательное отклонение Surprise не означает автоматическое снижение цены: 5 самых левых точек показали значение от -10% до +10% в течение 2 дней, что соответствует -10% до +10% доходности
Что, если "Returns in %" зависит не только от относительного "Surprise in %", но больше от абсолютного значения "Reported EPS"?
На следующем графике мы строим график Surprise и Reported EPS по отношению к Returns in % для ТОП 50. Мы используем подграфики, чтобы показать значения осей:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
top50_volume[['Surprise(%)','Reported EPS','Returns in %']].plot.scatter(x='Reported EPS', y='Surprise(%)', c='Returns in %', colormap='RdYlGn', ax=ax)
`
В общем, мы видим, что многие акции сообщают о прибыли на акцию (EPS) от 0 до 2,5 долларов с небольшим положительным отклонением и умеренным ростом (светло-зеленый: до 5% доходности).
На следующем графике вы можете увидеть ту же зависимость, но для ТОП 200:
fig, ax = plt.subplots()
top200_volume[['Surprise(%)','Reported EPS','Returns in %']].plot.scatter(x='Reported EPS', y='Surprise(%)', c='Returns in %', colormap='RdYlGn', ax=ax)
На следующем графике мы строим график Surprise и Reported EPS по отношению к Adj. Returns in % для ТОП 50.
fig, ax = plt.subplots()
top50_volume[['Surprise(%)','Reported EPS','Adj. Returns in %']].plot.scatter(x='Reported EPS', y='Surprise(%)', c='Adj. Returns in %', colormap='RdYlGn', ax=ax)
Как видите, Фигура 7 и Фигура 9 не очень отличаются - индивидуальные изменения акций гораздо выше, чем среднее значение S&P500, поэтому Adj. Returns и Returns очень близки.
На следующем графике вы можете увидеть ту же зависимость, но для ТОП 200.
Опять же, Фигура 10 очень похожа на Фигуру 8 (Adjusted Returns и Non-Adjusted Returns).
На следующем графике мы строим график Reported EPS и EPS Estimate по отношению к Returns in % для ТОП 50.
fig, ax = plt.subplots()
top50_volume.plot.scatter(x='Reported EPS', y='EPS Estimate', c='Returns in %', colormap='RdYlGn', ax=ax)
На следующем графике мы строим график Reported EPS и EPS Estimate по отношению к Adj. Returns in % для ТОП 50.
Вы увидите похожую картину при построении Adj. Returns vs. Returns:
fig, ax = plt.subplots()
top50_volume.plot.scatter(x='Reported EPS', y='EPS Estimate', c='Adj. Returns in %', colormap='RdYlGn', ax=ax)
На следующей гистограмме мы сравниваем доходность ТОП 50 и ТОП 200 акций с наибольшим объемом торговли. Вы можете заметить, что распределение акций ТОП 200 (синий) имеет более "колоколообразную" форму вокруг 0 и слегка положительной доходности по сравнению с акциями ТОП 50 (оранжевый):
top200_volume['Adj. Returns in %'].hist(bins=50, alpha=0.5)
top50_volume['Adj. Returns in %'].hist(bins=50, alpha = 0.5)
Заключение
Мы показали, как парсить финансовые прогнозы с веб-сайта и как связать их с доходностью акций. Второй квартал 2020 года оказался очень успешным для топ-50 акций (по объему торговли) - большинство из них показали положительное отклонение от ожидаемой прибыли на акцию (EPS) и высокую краткосрочную доходность. Результат остается сильным даже после вычета соответствующей доходности индекса S&P500 (т.е. топ-50 акций имели более высокий положительный рост, чем средняя динамика индекса). Когда рассматриваем топ-200 акций, результат становится несколько сложнее - средняя доходность меньше, и есть больше вариации в EPS и доходности.
