post pandas-stoks
This commit is contained in:
parent
e27ad82516
commit
fe0dbb479a
210
content/post/2020-11-13-pandas-stoks.md
Normal file
210
content/post/2020-11-13-pandas-stoks.md
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
---
|
||||||
|
title: Анализ корреляций биржевых колебаний
|
||||||
|
date: 2020-11-12
|
||||||
|
tags: [Python, pandas, jupyter, MOEX, stoks, ETF]
|
||||||
|
---
|
||||||
|
|
||||||
|
Поэкспирементировал в Jupyter Lab и нарисовал несколько графиков на тему корреляции колебаний курса ETF и акций.
|
||||||
|
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
Ставим зависимости, если надо.
|
||||||
|
```python
|
||||||
|
# !conda install snappy python-snappy pyarrow fastparquet
|
||||||
|
# !pip install pandas_datareader
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Настраиваем графики и импортируем библиотеки.
|
||||||
|
```python
|
||||||
|
%matplotlib inline
|
||||||
|
%config InlineBackend.figure_format = 'svg' #векторный формат
|
||||||
|
|
||||||
|
from matplotlib import pyplot as plt
|
||||||
|
|
||||||
|
plt.style.use('ggplot') # Красивые графики
|
||||||
|
plt.rcParams['figure.figsize'] = (12, 5) # Размер картинок
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
from datetime import timedelta
|
||||||
|
from datetime import datetime
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import pandas_datareader.data as web
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Скачиваем информацию через API биржи в локальный каталог.
|
||||||
|
```python
|
||||||
|
|
||||||
|
tikers = 'SBER,AFKS,GAZP,MAIL,YNDX,FXUS,FXIT,FXCN,FXDE,FXWO,FXRW'.split(',')
|
||||||
|
|
||||||
|
def download_data(tiker, path):
|
||||||
|
filepath = os.path.join(path, f'{tiker}_2020.parquet')
|
||||||
|
if os.path.exists(filepath):
|
||||||
|
return
|
||||||
|
df = web.DataReader(tiker, 'moex', start='2020-01-01', end='2020-11-12')
|
||||||
|
df.to_parquet(filepath, engine='fastparquet')
|
||||||
|
|
||||||
|
for tiker in tikers:
|
||||||
|
print(f'downloading: {tiker}')
|
||||||
|
download_data(tiker, "D:\\data2\\Documents\\notebooks")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
downloading: SBER
|
||||||
|
downloading: AFKS
|
||||||
|
downloading: GAZP
|
||||||
|
downloading: MAIL
|
||||||
|
downloading: YNDX
|
||||||
|
downloading: FXUS
|
||||||
|
downloading: FXIT
|
||||||
|
downloading: FXCN
|
||||||
|
downloading: FXDE
|
||||||
|
downloading: FXWO
|
||||||
|
downloading: FXRW
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Загружаем кешированные на диске данные.
|
||||||
|
```python
|
||||||
|
|
||||||
|
def load_data(tiker, path):
|
||||||
|
result = None
|
||||||
|
filepath = os.path.join(path, f'{tiker}_2020.parquet')
|
||||||
|
if os.path.exists(filepath):
|
||||||
|
result = pd.read_parquet(filepath)
|
||||||
|
return result
|
||||||
|
|
||||||
|
dfs = {}
|
||||||
|
for tiker in tikers:
|
||||||
|
dfs[tiker] = load_data(tiker, "D:\\data2\\Documents\\notebooks")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Значения полей `BOARDID` (режимы торгов) у акций и ETF разные. Для акций берём `TQBR` а для фондов `TQTF`.
|
||||||
|
```python
|
||||||
|
dfs['FXIT']['BOARDID'].unique(), dfs['YNDX']['BOARDID'].unique()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(array(['TQTD', 'TQTF', 'SOTC', 'RPMO', 'PSRP', 'PSTF', 'PTTF'],
|
||||||
|
dtype=object),
|
||||||
|
array(['TQBR', 'SPEQ', 'SOTC', 'RPMO', 'RPEU', 'RPEO', 'MXBD', 'EQRP',
|
||||||
|
'PSRP', 'EQRD', 'LIQR', 'PSRD', 'PSRE', 'PTEQ', 'PSEQ'],
|
||||||
|
dtype=object))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Обрабатываем данные, чтобы убрать всё кроме нужного режима торгов, и интерполируем цену.
|
||||||
|
```python
|
||||||
|
|
||||||
|
def price_data(x_df, boardid='TQBR'):
|
||||||
|
xdf = x_df.copy()
|
||||||
|
xdf = xdf.where(xdf['BOARDID'] == boardid)
|
||||||
|
xdf.dropna(axis=0, how='all', inplace=True)
|
||||||
|
xdf.dropna(axis=1, how='all', inplace=True)
|
||||||
|
if 0 == xdf.size:
|
||||||
|
return xdf
|
||||||
|
xdf['OPEN'] = xdf['OPEN'].interpolate(method='polynomial', order=2);
|
||||||
|
xdf['CLOSE'] = xdf['CLOSE'].interpolate(method='polynomial', order=2);
|
||||||
|
xdf['WAPRICE'] = xdf['WAPRICE'].interpolate(method='polynomial', order=2);
|
||||||
|
return xdf
|
||||||
|
|
||||||
|
# для простоты, считаем что если имя начинается на FX то это ETF
|
||||||
|
for tiker in tikers:
|
||||||
|
dfs[tiker] = price_data(dfs[tiker], boardid='TQTF' if 'FX' in tiker else 'TQBR')
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Нормализуем графики с помомщью отношения средневзвешеной (по объёму торгов) цены `WAPRICE` и медианой этого показателя.
|
||||||
|
Затем соединяем таблицы и строим графики.
|
||||||
|
```python
|
||||||
|
# WAPRICE/median
|
||||||
|
def calc_wdm(df):
|
||||||
|
return df['WAPRICE'] / df['WAPRICE'].median()
|
||||||
|
|
||||||
|
for tiker in tikers:
|
||||||
|
dfs[tiker][tiker] = calc_wdm(dfs[tiker])
|
||||||
|
|
||||||
|
df_base = dfs['FXIT'][[]]
|
||||||
|
|
||||||
|
def merge_dfs(base):
|
||||||
|
result = base
|
||||||
|
for tiker in tikers:
|
||||||
|
result = result.merge(dfs[tiker][[tiker]], how='outer', on='TRADEDATE')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
xxdf = merge_dfs(df_base)
|
||||||
|
|
||||||
|
xxdf['diff_us'] = xxdf['FXUS'] - xxdf['FXIT']
|
||||||
|
|
||||||
|
# рисуем графики
|
||||||
|
xxdf.plot(y='FXUS,FXIT,FXCN,FXDE,FXWO,FXRW'.split(','), kind='line', title='ETF: WAPRICE / median')
|
||||||
|
xxdf.plot(y='SBER,AFKS,GAZP,MAIL,YNDX'.split(','), kind='line', title='RU: WAPRICE / median')
|
||||||
|
xxdf.plot(y='diff_us'.split(','), kind='line', title='FXUS - FXIT')
|
||||||
|
xxdf.plot(y='FXIT,YNDX'.split(','), kind='line', title='FXIT & YNDX')
|
||||||
|
xxdf.plot(y='FXWO,FXRW,FXUS'.split(','), kind='line', title='FXUS & FXWO & FXRW');
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Все ETF Finex достаточно сильно коррелируют, за исключением FXRW конечно.
|
||||||
|
![svg](/img/pstoks-201113/output_8_1.svg)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
На российском рынке по произвольно выбранным акциям корреляция слабая.
|
||||||
|
![svg](/img/pstoks-201113/output_8_2.svg)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Разница в колебаниях FXUS и FXIT.
|
||||||
|
![svg](/img/pstoks-201113/output_8_3.svg)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Между Яндексом и FXIT некоторая корреляция есть.
|
||||||
|
![svg](/img/pstoks-201113/output_8_4.svg)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Между FXWO и FXUS корреляция очень сильная.
|
||||||
|
![svg](/img/pstoks-201113/output_8_5.svg)
|
2657
static/img/pstoks-201113/output_8_1.svg
Normal file
2657
static/img/pstoks-201113/output_8_1.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 79 KiB |
2371
static/img/pstoks-201113/output_8_2.svg
Normal file
2371
static/img/pstoks-201113/output_8_2.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 71 KiB |
1291
static/img/pstoks-201113/output_8_3.svg
Normal file
1291
static/img/pstoks-201113/output_8_3.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 42 KiB |
1410
static/img/pstoks-201113/output_8_4.svg
Normal file
1410
static/img/pstoks-201113/output_8_4.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 44 KiB |
1725
static/img/pstoks-201113/output_8_5.svg
Normal file
1725
static/img/pstoks-201113/output_8_5.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 54 KiB |
Loading…
Reference in New Issue
Block a user