Продукт включает в себя агрегированные дневные данные по величине нетто-объема (покупка - продажа) в бумагах и в деньгах по топ 30, 70 и 100 клиентам. С 10:00 до 18:30 учитываются все сделки всех клиентов в анонимном "стаканном" режиме. В 18:30 выявляются самые крупные клиенты по абсолютной величине нетто-объема за сегодняшний торговый день и их значения суммируются. Таким образом на конец дня получается три значения p30, p70 и p100
На данный момент нетто-объем рассчитывается по десяти инструментам: SBER, GAZP, LKOH, GMKN, VTBR, ROSN, MGNT, ALRS, SBERP, AFLT
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%pylab inline
plt.rcParams['figure.figsize'] = (15, 8)
plt.style.use('ggplot')
Рассмотрим данные на примере акции Сбербанка (SBER). Загрузим исторические значения нетто-объемов с 2014 по 2017 год включительно (4 года)
ticker = 'SBER'
X = pd.read_csv('../netflows_2014_2017/' + ticker + '_netflow.csv')
X['date'] = pd.to_datetime(X['date'])
X = X.set_index('date')
X = X[['p30', 'p70', 'p100', 'oi']][3:]
X.head()
# Базовая статистика по ключевым показателям
X.describe()
# Time-Series график. Кумулятивная сумма p30, p70 и p100
X[['p30', 'p70', 'p100']].cumsum().rolling(5).mean().plot()
# Распределения величин p30, p70 и p100. Среднее и медиана ~0
X[['p30', 'p70', 'p100']].plot.box()
Посчитаем корреляции между p30, p70, p100 и today_change, tomorrow_change. Скачаем историю цен и объемов:
# получение дневных свечей по любой указанной бумаге используя MOEX API
def get_ohlcv(ticker, date_from, date_till):
P = pd.DataFrame()
# используем цикл, так как есть ограничение в выдаче значений за один запрос (max = 500)
for i in range(5):
url = 'http://iss.moex.com/iss/engines/stock/markets/shares/boards/tqbr/securities/' + ticker + '/candles.csv' \
'?from=' + date_from + \
'&till=' + date_till + \
'&interval=24' \
'&start=' + str(500*i)
P = P.append(pd.read_csv(url, ';',skiprows=2))
P['date'] = P.apply(lambda row: row['begin'][:10], axis =1)
P['date'] = pd.to_datetime(P['date'], format = '%Y-%m-%d')
P = P.set_index('date')
#объем умножаем на 2, чтобы получить оборот
P['volume'] = 2 * P['volume']
P['value'] = 2 * P['value']
P.drop(['begin', 'end'], axis = 1, inplace = True)
return P
Y = get_ohlcv(ticker, '2014-01-01', '2018-01-30')
# change - процентное изменение цены за сегодня (close to open)
Y['today_change'] = 100 * (Y['close'] - Y['open']) / Y['open']
# tom_change - процентное изменение цены, которое будет завтра (close(+1) to сlose(0))
Y['tomorrow_change'] = 100 * Y['close'].pct_change().shift(-1)
Y.head()
# К таблице с нетто-объемом добавляем изменения цен и объемы
X = pd.merge(X, Y[['today_change', 'tomorrow_change', 'volume', 'value']], how='left', left_index = True, right_index = True)
X.head()
# Кумулятивная сумма p30, p70, p100 (цветные линии) и движение цены - today_change (черная линия)
X[['p30', 'p70', 'p100']].cumsum().rolling(5).mean().plot()
plt.ylabel('В штуках бумаг')
X['today_change'].cumsum().rolling(5).mean().plot(secondary_y = True, c='#000000')
plt.ylabel('%')
Корреляции значений p30, p70, p100 с движением цены today_change и tomorrow_change на промежутке за 4 года
X[['p30', 'p70', 'p100', 'today_change', 'tomorrow_change']].corr()
Следовательно, можно предположить, что today_change обусловлена p30, p70 и p100. А зависимость между нетто-объемом и tomorrow_change слабая, но тем не менее наблюдается стабильная положительная корреляция. Далее оценим эту зависимость.
Пробуем добиться увеличения значения корреляции между p30, p70, p100 и tomorrow_change:
Z = X.copy()
Z['p30_vol'] = 100 * Z['p30'] / Z['volume']
Z['p70_vol'] = 100 * Z['p70'] / Z['volume']
Z['p100_vol'] = 100 * Z['p100'] / Z['volume']
Z = Z.astype(float)
Z.corr()
При нормировании p30, p70, p100 на объем корреляция между p30_vol, p70_vol, p100_vol и tomorrow_change стала ~0.1
Посчитаем прирост (дельту) p30, p70 и p100 относительно среднего значения за 1-5 предыдущих дней:
cols = ['p30', 'p70', 'p100', 'p30_vol', 'p70_vol', 'p100_vol']
for col in cols:
for i in [1,2,3,4,5]:
Z[col + '_' + str(i)] = Z[col] - Z[col].shift(periods=1, freq=None, axis=0).rolling(i, min_periods = 1).mean()
print('Размерность новой таблицы ' + str(Z.shape))
Z.head()
# топ 10 показателей с максимальной корреляцией
cols = Z.corr()['tomorrow_change'].sort_values(ascending = False)[1:11]
#показатели, полученные на основе прироста показывают большую корреляцию
cols
Приросты значений p30, p70 и p100 относительно предыдущих дней показали корреляцию ~0.11
Аноличные значения по другим инструментам:
TICKER | POS vs TOD_CHG | POS vs TOM_CHG | POS/VOL vs TOM_CHG | ΔPOS/VOL vs TOM_CHG |
---|---|---|---|---|
SBER | 0.75 | 0.09 | 0.10 | 0.11 |
GAZP | 0.73 | 0.09 | 0.08 | 0.11 |
LKOH | 0.65 | 0.08 | 0.08 | 0.13 |
GMKN | 0.60 | 0.04 | 0.07 | 0.05 |
MGNT | 0.60 | 0.04 | 0.07 | 0.06 |
ROSN | 0.67 | 0.10 | 0.11 | 0.12 |
VTBR | 0.56 | 0.10 | 0.01 | 0.06 |
ALRS | 0.45 | 0.00 | 0.04 | 0.07 |
SBERP | 0.50 | 0.07 | 0.10 | 0.09 |
AFLT | 0.50 | 0.06 | 0.07 | 0.07 |
На истории в 4 года оценим, что дает корреляция ~0.1
Среднее значение величин p30, p70, p100 и их производных находится в окресностях 0. Посчитаем статистику сонаправленности нетто-объема и tomorrow_change по дням и по величине изменения цен(%) на основе одного из показателей p70_vol_3* (корреляция с tomorrow_change ~ 0.11):
*p70 нормированный на объем и дельта относительно среднего значения предыдущих трех дней
Z = Z[1:]
Z = Z[Z['tomorrow_change'] != 0]
Z['feature'] = np.sign(Z['p70_vol_3'])
Z['base'] = np.sign(Z['tomorrow_change'])
pd.crosstab(index = Z['feature'].astype(int), columns = Z['base'].astype(int))
В таком же разрезе, посчитаем кумулятивную сумму процентых изменений цен в случаях сонаправленности и разнонаправленности:
pd.crosstab(index = Z['feature'].astype(int), columns = Z['base'].astype(int), values = Z['tomorrow_change'].abs().astype(int), aggfunc = 'sum')
При сонаправленности нетто-объема и tomorrow_change изменение цены = 615 (280+325) и при разнонаправленности = 425 (234+191)
Статистика сонаправленности построенной на одном показателе p70_vol_3 равна 180 (615-425).
* Для простоты, проценты складывались кумулятивно. Пример: Если цена менялась +2%, -1%, +1%, получаем +2%
Дневной time-series график оговоренной выше статистики (черная линия - движение цены SBER, пять цветных - накопленная статистика сонаправленности по топ 5 показателям с наибольшей корреляцией):
R = pd.DataFrame()
for col in cols.index[:5]:
R[col] = Z.apply(lambda x: x['tomorrow_change'] if x[col] > 0 else (-1 * x['tomorrow_change']), axis=1)
R['base'] = Z['tomorrow_change']
R.cumsum().plot(style=['#CE1126', '#E26EB2', '#FFA100', '#8D3C1E', '#63B1E5', '#000000'])
plt.ylabel('%')
Аналогичные графики по другим инструментам:
Ссылка на сайт - https://moex.com/ru/analyticalproducts?netflow2
Email - support.dataproducts@moex.com