Для анализа будем использовать данные ETF c базовой валютой USD: FXCN, FXRL, FXIT, FXUS и FXRU. Временной ряд рассмотрим за три года с 2018 по 2020 года. Само исследование проведем в Google Colaboratory.
Как обычно в начале импортируем все необходимые библиотеки для дальнейшей работы.
import pandas as pd import numpy as np import matplotlib.pyplot as plt from google.colab import files import warnings warnings.filterwarnings("ignore")Сначала необходимо получить данные. Есть несколько способов. Мы воспользовались — взяли их с Finam в формате csv. Дальше написал функцию для обработки полученных данных и при помощи concat свел их в один датафрейм.
def changeDF(df): df['date'] = pd.to_datetime(df['<DATE>'].astype(str), dayfirst=True) name =[x for x in globals() if globals()[x] is df][0] df = df.drop(['<DATE>','<TIME>', '<OPEN>', '<HIGH>', '<LOW>'], axis=1) df = df.set_index(['date']) df.columns = [name+'_cl', name + '_vol'] return df fxgd_change = changeDF(fxgd) fxrl_change = changeDF(fxrl) fxit_change = changeDF(fxit) fxus_change = changeDF(fxus) fxru_change = changeDF(fxru) fxcn_change = changeDF(fxcn) etf = pd.concat([fxgd_change, fxrl_change, fxit_change, fxus_change, fxru_change, fxcn_change], axis=1) etf.head()В результате получили:
print(etf.isnull().sum())
etf.dropna(inplace=True, axis=0)Дальше имеет смысл посмотреть тип значений:
etf.dtypes
etf.shapeВ результате получили (752, 12).
etf[-120:].describe()
В результате видно в каких пределах в последние полгода ETF провели большую часть времени с вероятностью 75%.
После построим графики движения цены во времени за прошедшие три года.
fig, axs = plt.subplots(3, 2, figsize=(15,15)) axs[0, 0].plot(etf.index, etf['fxgd_cl'], 'tab:blue' ) axs[0, 0].set_title('FXGD') axs[0, 1].plot(etf.index, etf['fxrl_cl'], 'tab:orange') axs[0, 1].set_title('FXRL') axs[1, 0].plot(etf.index, etf['fxit_cl'], 'tab:green') axs[1, 0].set_title('FXIT') axs[1, 1].plot(etf.index, etf['fxus_cl'], 'tab:red') axs[1, 1].set_title('FXUS') axs[2, 0].plot(etf.index, etf['fxru_cl'], 'tab:grey') axs[2, 0].set_title('FXRU') axs[2, 1].plot(etf.index, etf['fxcn_cl'], 'tab:purple') axs[2, 1].set_title('FXCN') for ax in axs.flat: ax.set(xlabel='Data', ylabel='Price') for ax in axs.flat: ax.label_outer()
Ежедневное процентное изменение цены etf вычисляется на основе процентного изменения между ценами закрытия 2 последовательных дней. Предположим, что цена закрытия вчера составляла 500 рублей, а сегодня она закрылась по 550 рублей. Таким образом, процентное изменение составляет 10%. т. е. ((550-500) / 500)*100. Здесь нет никакой тайны!
Далее, мы введем новый столбец, обозначающий дневную доходность в цене etf. Вычислить можно с помощью встроенной функции pct_change() в python. Так же немного переставлю колонки, чтоб визуально лучше воспринималось.
etf_cl = etf[['fxgd_cl', 'fxrl_cl', 'fxit_cl', 'fxus_cl', 'fxru_cl', 'fxcn_cl']] etf_cl_pct = etf_cl.pct_change()*100 etf_cl_pct.columns = ['fxgd_cl_pct', 'fxrl_cl_pct', 'fxit_cl_pct', 'fxus_cl_pct', 'fxru_cl_pct', 'fxcn_cl_pct'] etf_vol = etf[['fxgd_vol', 'fxrl_vol', 'fxit_vol', 'fxus_vol', 'fxru_vol', 'fxcn_vol']] etf_new = pd.concat([etf_cl, etf_vol, etf_cl_pct], axis = 1) etf_new.head()
etf_new = etf_new.dropna()Представим изменение ежедневной доходности в виде графика во времени:
fig, axs = plt.subplots(3, 2, figsize=(15,15)) axs[0, 0].plot(etf_new.index, etf_new['fxgd_cl_pct'], 'tab:blue') axs[0, 0].set_title('FXGD') axs[0, 1].plot(etf_new.index, etf_new['fxrl_cl_pct'], 'tab:orange') axs[0, 1].set_title('FXRL') axs[1, 0].plot(etf_new.index, etf_new['fxit_cl_pct'], 'tab:green') axs[1, 0].set_title('FXIT') axs[1, 1].plot(etf_new.index, etf_new['fxus_cl_pct'], 'tab:red') axs[1, 1].set_title('FXUS') axs[2, 0].plot(etf_new.index, etf_new['fxru_cl_pct'], 'tab:grey') axs[2, 0].set_title('FXRU') axs[2, 1].plot(etf_new.index, etf_new['fxcn_cl_pct'], 'tab:purple') axs[2, 1].set_title('FXCN') for ax in axs.flat: ax.set(xlabel='Data', ylabel='Price') for ax in axs.flat: ax.label_outer()
В течение большей части времени доходность составляет от -2% до 2% со скачками без пересечения отметки в 6% с обеих сторон. Наиболее шумной выглядит ETF FXCN.
Так же можно проверить новостные статьи за те дни, когда наблюдался резкий рост/падение цен на etf и понять чем было обусловлено.
Построим гистограмму распределения ежедневных доходов:
import seaborn as sns sns.set(style="darkgrid") fig, axs = plt.subplots(3, 2, figsize=(15,15)) sns.histplot(data=etf_new['fxgd_cl_pct'], kde=True, color="orange", ax=axs[0, 0]) axs[0,0].set_xlim(-10,10) sns.histplot(data=etf_new['fxrl_cl_pct'], kde=True, color="olive", ax=axs[0, 1]) axs[0,1].set_xlim(-10,10) sns.histplot(data=etf_new['fxit_cl_pct'], kde=True, color="gold", ax=axs[1, 0]) axs[1,0].set_xlim(-10,10) sns.histplot(data=etf_new['fxus_cl_pct'], kde=True, color="grey", ax=axs[1, 1]) axs[1,1].set_xlim(-10,10) sns.histplot(data=etf_new['fxru_cl_pct'], kde=True, color="teal", ax=axs[2, 0]) axs[2,0].set_xlim(-10,10) sns.histplot(data=etf_new['fxcn_cl_pct'], kde=True, color="brown", ax=axs[2, 1]) axs[2,1].set_xlim(-10,10) plt.show()
etf_new[['fxgd_cl_pct', 'fxrl_cl_pct', 'fxit_cl_pct', 'fxus_cl_pct', 'fxru_cl_pct', 'fxcn_cl_pct']].describe()
def trend(x): if x > -0.5 and x <= 0.5: return 'Практически или без изменений' elif x > 0.5 and x <= 1.5: return 'Небольшой позитив' elif x > -1.5 and x <= -0.5: return 'Небольшой негатив' elif x > 1.5 and x <= 2.5: return 'Позитив' elif x > -2.5 and x <= -1.5: return 'Негатив' elif x > 2.5 and x <= 5: return 'Значительный позитив' elif x > -5 and x <= -2.5: return 'Значительный негатив' elif x > 5: return 'Максимальный позитив' elif x <= -5: return 'Максимальный негатив' for stock in etf_trend.columns[12:]: etf_trend["Trend_" + str(stock)] = np.zeros(etf_trend[stock].count()) etf_trend["Trend_"+ str(stock)] = etf_trend[stock].apply(lambda x:trend(x))<br /><br /><br />
sns.set(style="darkgrid") fig, axs = plt.subplots(3, 2, figsize=(20,17)) axs[0, 0].pie(etf_trend['Trend_fxgd_cl_pct'].value_counts(), labels = etf_trend['Trend_fxgd_cl_pct'].value_counts().index, autopct="%.1f%%") axs[0, 0].set_title('FXGD') axs[0, 1].pie(etf_trend['Trend_fxrl_cl_pct'].value_counts(), labels = etf_trend['Trend_fxrl_cl_pct'].value_counts().index, autopct="%.1f%%") axs[0, 1].set_title('FXRL') axs[1, 0].pie(etf_trend['Trend_fxit_cl_pct'].value_counts(), labels = etf_trend['Trend_fxit_cl_pct'].value_counts().index, autopct="%.1f%%") axs[1, 0].set_title('FXIT') axs[1, 1].pie(etf_trend['Trend_fxus_cl_pct'].value_counts(), labels = etf_trend['Trend_fxus_cl_pct'].value_counts().index, autopct="%.1f%%") axs[1, 1].set_title('FXUS') axs[2, 0].pie(etf_trend['Trend_fxru_cl_pct'].value_counts(), labels = etf_trend['Trend_fxru_cl_pct'].value_counts().index, autopct="%.1f%%") axs[2, 0].set_title('FXRU') axs[2, 1].pie(etf_trend['Trend_fxcn_cl_pct'].value_counts(), labels = etf_trend['Trend_fxcn_cl_pct'].value_counts().index, autopct="%.1f%%") axs[2, 1].set_title('FXCN') plt.show()
sns.set(style="darkgrid") fig, axs = plt.subplots(6, 1, figsize=(30,35)) axs[0].stem(etf_trend.index[-253:], etf_trend['fxgd_cl_pct'][-253:]) axs[0].plot((etf_trend['fxgd_vol']/10000)[-253:], color = 'green', alpha = 0.5) axs[0].set_title('FXGD') axs[1].stem(etf_trend.index[-253:], etf_trend['fxrl_cl_pct'][-253:]) axs[1].plot((etf_trend['fxrl_vol']/10000)[-253:], color = 'green', alpha = 0.5) axs[1].set_title('FXRL') axs[2].stem(etf_trend.index[-253:], etf_trend['fxit_cl_pct'][-253:]) axs[2].plot((etf_trend['fxit_vol']/10000)[-253:], color = 'green', alpha = 0.5) axs[2].set_title('FXIT') axs[3].stem(etf_trend.index[-253:], etf_trend['fxus_cl_pct'][-253:]) axs[3].plot((etf_trend['fxus_vol']/10000)[-253:], color = 'green', alpha = 0.5) axs[3].set_title('FXUS') axs[4].stem(etf_trend.index[-253:], etf_trend['fxru_cl_pct'][-253:]) axs[4].plot((etf_trend['fxru_vol']/10000)[-253:], color = 'green', alpha = 0.5) axs[4].set_title('FXRU') axs[5].stem(etf_trend.index[-253:], etf_trend['fxcn_cl_pct'][-253:]) axs[5].plot((etf_trend['fxcn_vol']/10000)[-253:], color = 'green', alpha = 0.5) axs[5].set_title('FXCN')
Основное правило диверсификации — не клади все яйца в одну корзинку. По этому если мы решили собирать портфель из ETF, то они не должны быть сильно взаимосвязаны друг с другом. Математическим языком — коэффициент корреляции Пирсона между любой парой должен быть близок к 0. Смысл — они не должны падать синхронно, чтоб инвестиции не превратились в 0.
Проанализировать корреляцию между различными ETF можно с помощью парной диаграммы Seaborn. Для удобства оставим только процентные изменения за день в отдельном новом датафрейме.
На графике визуально можно увидеть наличие корреляции между различными ETF. Обратите внимание, что корреляционный анализ выполняется для ежедневного процентного изменения(дневной доходности) цены ETF, а не для их цены.
Из полученных графиков ясно видно, что следующие FXIT и FXUS не следует класть в одну корзину, так как между нми наблюдается сильная зависимость. Остальные могут быть включены в портфель, поскольку ни одна из двух оставшихся ETF не демонстрирует какой-либо существенной корреляции.
Но у визуального анализа есть существенный недостаток — он не предоставляет подробной информации о количественной оценки взаимосвязи, таких как значение R Пирсона и p нулевой гипотезы. В связи с чем при визуальном анализе остается под вопросом FXCN — есть ли у данного ETF сильная взаимосвязь с FXUS или нет.
Один из способов решения данного вопроса — построение графиков seaborn.jointplot с подробной информацией по значению R Пирсона (коэффициент корреляции Пирсона) для каждой пары ETF. Значение R Пирсона колеблется от -1 до 1. Отрицательное значение указывает на отрицательную линейную связь, в то время как положительное значение указывает на положительную связь. Значение R Пирсона ближе к 1 (или -1) указывает на сильную корреляцию, в то время как значение ближе к 0 указывает на слабую корреляцию.
Так же чем интересны данные графики — построение гистограмм распределения по краям, а так же значение p-value.
Но если рассматривать все пары, то нам потребуется большое количество графиков. По этому остановимся только на тех, которые вызывают сомнения:
from scipy.stats import stats a_1 = pct_chg_etf.fxit_cl_pct b_1 = pct_chg_etf.fxus_cl_pct b_2 = pct_chg_etf.fxcn_cl_pct g_1 = sns.jointplot('fxit_cl_pct', 'fxcn_cl_pct', pct_chg_etf, kind = 'scatter') r_1, p_1 = stats.pearsonr(a_1, b_1) g_1.ax_joint.annotate(f'$\\rho = {r_1:.3f}, p = {p_1:.3f}$', xy=(0.1, 0.9), xycoords='axes fraction', ha='left', va='center', bbox={'boxstyle': 'round', 'fc': 'powderblue', 'ec': 'navy'}) g_1.ax_joint.scatter(a_1, b_1) g_1.set_axis_labels(xlabel='fxit', ylabel='fxus', size=15) g_2 = sns.jointplot('fxus_cl_pct', 'fxit_cl_pct', pct_chg_etf, kind = 'scatter') r_2, p_2 = stats.pearsonr(a_1, b_2) g_2.ax_joint.annotate(f'$\\rho = {r_2:.3f}, p = {p_2:.3f}$', xy=(0.1, 0.9), xycoords='axes fraction', ha='left', va='center', bbox={'boxstyle': 'round', 'fc': 'powderblue', 'ec': 'navy'}) g_2.ax_joint.scatter(a_1, b_2) g_2.set_axis_labels(xlabel='fxit', ylabel='fxcn', size=15) plt.tight_layout() plt.show()
Волатильность-один из важнейших показателей на финансовых рынках. Говорят, что ценная бумага обладает высокой волатильностью, если ее стоимость может резко измениться за короткий промежуток времени. С другой стороны, более низкая волатильность означает, что стоимость имеет тенденцию быть относительно стабильной в течение определенного периода времени. Эти изменения обусловлены несколькими факторами, включая спрос и предложение, настроения, жадность, страх и т.д. Математически волатильность измеряется с помощью статистической меры, называемой «стандартным отклонением», которая измеряет отклонение актива от его средней стоимости.
Произведем расчет 5-дневной скользящей средней дневной доходности и стандартного отклонения. После этого построим график. Все это можно выполнить при помощи функций Pandas rolling() и std().
sns.set(style="darkgrid") fig, axs = plt.subplots(6, 1, figsize=(30,35)) for i, etf in enumerate(pct_chg_etf.columns): axs[i].plot(pct_chg_etf[etf].rolling(5).std()*np.sqrt(5)) axs[i].plot(pct_chg_etf[etf].rolling(7).mean()) axs[i].set_title(etf[:4], size=20)
volatility = pct_chg_etf[['fxgd_cl_pct', 'fxrl_cl_pct', 'fxit_cl_pct', 'fxus_cl_pct','fxru_cl_pct', 'fxcn_cl_pct']].rolling(5).std()*np.sqrt(5) volatility[:150].plot(linewidth=4, figsize = (35, 15)) plt.legend(loc=2, prop={'size': 16})
Как результат вы можете заметить, что наиболее сильная низкая волатильность характерна для ETF на российские еврооблигации — FXRU.
Многие трейдеры и инвесторы ищут инвестиции с более высокой волатильностью, чтобы получать более высокую прибыль. Если финансовый инструмент не движется, он не только обладает низкой волатильностью, но и имеет низкий потенциал прибыли. С другой стороны, ценные бумаги с очень высоким уровнем волатильности могут иметь огромный потенциал прибыли, но риск также высок.
какая эпоха наступила не подскажете?
Это всё ETF без валютного хеджа (или «хейджа»). Примеры ETF с хеджем — FXRB, FXRW, FXIP.
<DATE>,<TIME>,<OPEN>,<HIGH>,<LOW>,<CLOSE>,<VOL>.
В моем случае их сначала надо загрузить на colab:
uploaded = files.upload()for fn in uploaded.keys(): print('User uploaded file «{name}» with length {length} bytes'.format(name=fn, length=len(uploaded[fn])))
, а потом из них считать при помощи команды для примера etf золота:
fxgd =pd.read_csv('/content/FXGD.csv')
И так по всем.
Если вы будете делать на локальной машине, то меняете путь на тот, где у Вас лежат файлы и пропускаете этап загрузки данных.