Нижегородский Филиал
Федерального Государственного Автономного
Образовательного Учреждения Высшего Профессионального
Образования
Национальный Исследовательский Университет
"Высшая Школа Экономики"
Факультет информатики, математики и компьютерных наук
Научный руководитель
Старший преподаватель кафедры ПМИ
факультета БИиПМ
Пономаренко Александр Александрович
Консультант
Доцент кафедры финансового менеджмента
Факультета экономики
Россохин Владимир Валерьевич
Нижний Новгород
2017
ОГЛАВЛЕНИЕ:
Глава 1 Введение…………………………………………………3
Парный Трейдинг……………………………………...4
Постановка задач по данной проблеме……………...5
Хороший и плохой временные ряды………………...6
Глава 2 Комплекс статистических методов анализа
Графика…………………………………………………10
Глава 3 Использование исследования, машинное обучение,
Машинное обучение……………………………………22
Практика – критерий истины………………………...27
Возможные модификации……………………………..30
Дальнейшее развитие темы…………………………...31
Список используемой литературы…………………...32
Приложения……………………………………………..33
2
ГЛАВА 1. Введение.
3
инвестору, для создания цепочки сделок приносящих прибыль. В основе
таких стратегий, как правило, лежат математически обоснованные принципы.
Парный Трейдинг
4
1000/110 или около 9, что в рассматриваемых условиях можно принять за
«справедливое» значение (далее Justice coef «J») Если бы по какой-то
причине цена акций «Лукойла» выросла на 50%(до 1500р) а цена «Газпрома»
всего на 24%(до 136р) Соотношение цен J= PRluc / PRgazp = 1500/136 что
примерно равно 11. В этом случае рациональные инвесторы начали бы
покупать бумаги «Газпрома» одновременно продавая акции «Лукойла»
способствуя росту предложения последних. Через некоторое время это
привело бы соотношение цен к справедливому уровню J=9
5
дополнении первым. В сумме эти два подхода могут дать вполне полезный
результат.
6
эффективным. Более того, в подавляющем количестве статей приводятся
примеры анализа временного ряда с позиции уже известного временного
ряда, иллюстрируя моменты принятия решения, открытия и закрытия
позиции. Этот подход может носить исключительно ознакомительный
характер. Для создания инструментов, оказывающих ощутимую помощь,
необходимо честно подходить к вопросу неопределенности временного ряда.
То есть инвестор в момент совершения сделок на отклонениях от
справедливого отношения корзин J не знает наверняка вернется ли их
соотношение к исходному уровню, либо же это изменение необратимо.
7
Первый график имеет заметную тенденцию роста показателя J (наглядно
иллюстрируется линией линейного тренда) И, хотя график довольно часто
пересекает линию тренда, это не помогает в нашей задаче, потому что
определить справедливый J для двух этих графиков невозможно, он
постоянно изменяется. Поэтому эту пару активов можно охарактеризовать
как «плохую», ввиду её непригодности к осуществлению стратегии парного
трейдинга, с точки зрения участника фондового рынка.
8
Рисунок 1. График изменения J для корзин(ALRS(1.0)) -(MOEX(0.8),PIKK(0.2)
9
ГЛАВА 2. Комплекс статистических методов анализа графика
1.
Для нашей задачи можно однозначно сказать, что «хорошее» значение для
коэффициента k=>0 так как в этом случае линия горизонтальна, а реальные
данные колеблются вокруг постоянного значения, которое и можно будет
считать справедливым J. Хорошее и плохое значение коэффициента b нельзя
определить так однозначно, хотя для наглядности и удобства желательно
чтобы b>>k.
Рисунок 3
S1
S2
10
Площадь S1 – смещение за всё время наблюдения, S2 – постоянная,
низменная часть.
1 2 k∗N2S
Введем коэффициент качества линии тренда как Qual Reg= S = b (где k и b
2
2.
Рисунок 4
11
линия тренда. Будем строить её из предположения что глобальная линия
тренда не может отличаться от модифицированной более чем на 50%.
Su m up
S q coef ( n )= (1)
Su mdown
12
Вполне разумным кажется допущение что в какие-то моменты график может
иметь сильный подъём или спад. Возьмем значения 0.75 и 1.33. Получаем
ограничение, что график необходимо обрезать если превышение площади
над или под графиком более чем на 30%
Рисунок 5
3.
covXY=M[(X-M(X))(Y-M(Y)]=M(XY)-M(X)M(Y)
Если PR1 – цена первой корзины в момент времени t, PR2- цена второй
корзины в тот же момент времени а PR 1 и PR 2 средние значения цена первой
и второй корзин соответственно, то корреляция двух корзин вычисляется по
формуле:
13
Cov PR 1−PR 2 ∑ ( PR 1−PR 1)(PR 2−PR 2)
Cor r coef = =
σ PR 1 σ PR 2 √∑ ¿ ¿ ¿ ¿
Корреляция наверняка может определить связь величин только в случае если
Cor r coef =1или Cor r coef =−1. В первом случае цены корзин строго связаны, во
4.
14
Логическая предпосылка состоит в следующем – если корзины
удовлетворяют нашим требованиям, и их отношение цен не отклоняется от
их J сильно, то логично предположить, что поодиночке их цены совершают
частые синхронные подъёмы и понижения в цене. При том мы можем
сформулировать вполне логичное требование, что для появления изменений
в отношение цен, и как следствие появление возможности для совершения
сделки необходимо чтобы частота совпадений в поведении корзин была в
промежутке от 0.6 до 0.8. В случае большего значения коэффициента,
отклонения будут редки и едва ли ощутимы, а в случае меньшего поведение
корзин становится независимым друг от друга и как следствие перестаёт
удовлетворять первой предпосылке.
Profi t 1
Simm=1 Если > 0(4 )
Profi t 2
Profi t 1
Simm=0 Если <0(5)
Profi t 2
¿ m coef =
∑ Simm (6)
N
5.
15
активы, по которым были совершены сделки. Лучше рассчитывать этот
показатель для точек уже отобранных в пункте 2.
6.
Начнем с очень логичного пожелания – для того чтобы временной ряд вызвал
максимальное доверие у инвестора, он очевидно должен походить на график
на рисунке 6
Рисунок 6
16 (7)
Это формула классического преобразования Фурье, мы же воспользуемся
формулой дискретного преобразования:
N−1 −2 πi
kn
x n= ∑ x n e N
(8)
n=0
N –число измерений
в момент времени n
k – частота измерений
Рисунок 7
17
низкочастотные, но амплитудные. А колебаний, совмещающих эти два
фактора в какой-либо ощутимой мере крайне мало.
Рисунок 8
18
плоскостного коэффициента оказало излишним, т.к. баланс площадей
соблюдён(0.87). Аппроксимация с помощью ряда Фурье в данном случае не
может считаться хорошей для прогнозирования соотношения цен. Хотя в
общем то остальные статистические показатели, описанные в работе у этого
ряда носят крайне положительный характер.
19
ГЛАВА 3. Использование исследования, машинное обучение,
проверка актуальности, пути модификации.
Рисунок 9
20
На рисунке 9 показана слабость первого критерия – линии линейного тренда
и коэффициента качества линейной аппроксимации. В данном случае график
более чем приемлем с точки зрения инвестора, однако будет иметь довольно
2 k∗N
слабые показатели с точки зрения отношения b
21
Рисунок 107
Ещё одна довольно важная проблема заключается в том, что с точки зрения
естественного восприятия инвестора, провал по одному из критериев может
быть с лихвой компенсирован другими (как, например, на рисунках 9 и 10).
22
Машинное обучение.
23
fur_GP_aprox_qual R-квадрат коэффициента для аппроксимации
предыдущего пункта
24
Все показатели описанные выше рассчитывались автоматически и
выносились на визуализацию, пример такой визуализации приведен на
рисунке 7. Далее инвестор оценивал доверие к графику по шкале от 1го до
10ти с точки зрения ведения игры используя стратегию парного трейдинга.
Всего в таблице оказалось 450 примеров. (из них для тестовой выборки
использовалась пятая часть от общего количества)
Feature ranking:
1. feature 'corr_coef' (0.145439)
2. feature 'number_crosses_on_GP' (0.128011)
3. feature 'regress_qual' (0.114004)
4. feature 'fur_full_aprox_sin_number' (0.081072)
25
5. feature 'fur_full_aprox_qual' (0.080913)
6. feature 'fur_GP_aprox_qual' (0.074676)
7. feature 'regress_coef_k' (0.069925)
8. feature 'regress_coef_b' (0.069882)
9. feature 'balanced points' (0.066918)
10. feature 'timeline balance' (0.059878)
11. feature 'simm_coef' (0.055809)
12. feature 'fur_GP_aprox_sin_number' (0.050340)
13. feature 'corr_p-value' (0.003133)
MOEX 1.0 MOEX MVID SNGS TATNP 0.1 0.3 0.2 0.4
Рисунок 13
MTSS VSMO PHOR NVTK 0.0 0.7 0.1 0.2 ALRS AKRN RSTI FEES 0.5 0.5 0.0 0.0
26
Рисунок 14
PHOR MSNG BANEP 0.4 0.1 0.5 NMTP ALRS GMKN LKOH 0.4 0.6 0.0 0.0
27
Практика – критерий истины
Рисунок 15.
28
придерживаясь стратегии парного трейдинга, то к 30 декабря смогли бы
получить вполне разумные дивиденды.
Рисунок 16
Рисунок 17
29
На рисунке 17 мы видим временной ряд, ранее представленный на рисунке
13, расширенный по принципу предыдущих двух. В данном случае мы так же
видим, что пара продолжает незначительно отклоняться от своего J, которое
мы могли бы зафиксировать в начале 2016 года. В данном случае сделки
видны предпосылки для совершения нескольких сделок сравнительно
небольшой прибыльности каждая.
Рисунок 18
30
Безусловно, для более полных выводов необходимы дальнейшие тесты, с
большим количеством примеров.
31
Возможные модификации
Крайне интересен вопрос, а как можно улучшить результат, или какие ещё
дополнительные тесты можно провести.
32
Дальнейшее развитие темы:
Для дальнейшего развития работы можно обозначить следующие пункты:
33
СПИСОК ИСПОЛЬЗОВАННОЙ ЛИТЕРАТУРЫ
34
ПРИЛОЖЕНИЕ
35
["ClosingPrice"].values[0]
# print(start_price)
number_of_asset_x=(summ*parts[i]/start_price)
number_of_assets.append(summ*parts[i]/start_price)
# print(start_price*number_of_asset_x)
# print(number_of_assets)
#В перменной number_of_assets упорядоченные количества акций, согласно
долям указаным в переменной parts
def openfunction(shares):
result = {}
for share in shares:
data = pd.read_excel('C:\\Users\\Слава\\Desktop\\Диплом\\Активы
для проверки\\xlsx активы\\' + share + '.xlsx')
result[share] = data
return result
assets_data = openfunction(activs)
basket_prices=[]
for date in dates:
n=0#n - счетчик пройденых активов второго цикла
summ_all=0
#summ_all - суммарная стоимость корзины на момент времени date
for activ in activs:
activ_full_inf=assets_data[activ]
#activ_full_inf - полный набор данных об активе с названием activ
price_activ = activ_full_inf.loc[activ_full_inf["Date"] == date]
["ClosingPrice"].values[0]
#price_activ - цена актива activ на момент даты date
summ_current=price_activ*number_of_assets[n]
#summ_current суммарная стоимость актива active корзины на момент
времени date(стоимость умноженная на количество акций(изначальное)
summ_all=summ_all+summ_current
n=n+1
basket_prices.append(summ_all)
return basket_prices
def justice_basket_generation(prices1,prices2):
justice_coef=[]
if sum(prices1)>sum(prices2):
for i in range (len(prices1)):
current_just=prices1[i]/prices2[i]
justice_coef.append(current_just)
else:
for i in range (len(prices1)):
current_just=prices2[i]/prices1[i]
justice_coef.append(current_just)
return justice_coef
def time_line_visualisation(signal):
times=[i for i in range (len(signal))]
plt.plot(times,signal)
def first_block_trend_line(signal):
times = [i for i in range(len(signal))]
linr = stats.linregress(times, signal)
return linr
def second_block_regress_quality(k,b,signals):
smeshenie = abs(k*len(signals))
regress_quality=smeshenie*2/b
return regress_quality
def third_blok_good_points(times,signals):
linr = stats.linregress(times, signals)
#print(linr)
n = 500
36
regresspoint = linr.slope * (len(signals) - (n / 2)) + linr.intercept
#print(regresspoint)
last_signals = 0
number_signals = len(signals)
#print(number_signals)
for i in range(n):
last_signals = last_signals + signals[number_signals - i - 1]
average_last = last_signals / n
#print(average_last)
simmetric_line = (regresspoint + average_last) / 2
#print(simmetric_line)
sq_up = 0
sq_down = 0
sq_coef = 0
for i in range(n):
delta_sq = signals[len(signals) - i - 1] - simmetric_line
if delta_sq > 0:
sq_up = sq_up + delta_sq
else:
sq_down = sq_down + abs(delta_sq)
#print(sq_up, sq_down)
sq_coef = sq_up / sq_down
#print(sq_coef)
sq_up_cont = sq_up
sq_down_cont = sq_down
sq_coef_cont = 0
while n < len(signals):
delta_sq_cont = signals[len(signals) - n - 1] - simmetric_line
if delta_sq > 0:
sq_up_cont = sq_up_cont + delta_sq_cont
else:
sq_down_cont = sq_down_cont + abs(delta_sq_cont)
sq_coef_cont = sq_up_cont / sq_down_cont
if sq_coef_cont > 1.5 or sq_coef_cont < 0.8:
if sq_coef_cont > 1:
sq_coef_cont = sq_down_cont / sq_up_cont
break
n = n + 10
if n > len(signals):
total_points = (len(signals))
else:
total_points = n
sq_coef = sq_coef_cont
#print(total_points, sq_coef)
return total_points, sq_coef,simmetric_line
def fourth_block_profit_balance(price1,price2):
number_of_simmularytis=0
ful_number=len(price1)
for i in range (len(price2)-1):
delta_price1=price1[i+1]-price1[i]
delta_price2=price2[i+1]-price2[i]
if delta_price1/delta_price2>0:
number_of_simmularytis=number_of_simmularytis+1
coeff_simmular=number_of_simmularytis/ful_number
return coeff_simmular
def fifth_block_cross_coeff(justice):
cross_effective_line=sum(justice)/len(justice)
previous=justice[0]
n_cross=0
for i in range (len(justice)-1):
37
a=previous-cross_effective_line
b=justice[i+1]-cross_effective_line
if a/b<0:
n_cross=n_cross+1
previous=justice[i+1]
return (n_cross)
def fur_analis_block(signals):
flen=len(signals) # это количество различных частот(удвоенное)
Mass_Amplitud = ((np.fft.fft(signals, flen)))#**2))#[:flen//2]
#Собственно само преобразование Фурье
# print(Mass_Amplitud[38])
# print(Mass_Amplitud[986])
# result=np.fft.ifft(Mass_Amplitud)
Mass_Amplitud_2 = (np.abs(np.fft.fft(signals, flen)**2))[:flen//2]
ampl_work=[]
for i in range (len(Mass_Amplitud_2)-1):
ampl_work.append(Mass_Amplitud_2[i+1]) # Костыль, убираю
первую частоту дабы дальше работать без неё(она слишком большая и просто
является постоянной величиной, вроде sin[x]+k)
Max_ampl_work=max(ampl_work)
# illustrated=[i for i in range(len(ampl_work))]
# plt.plot(illustrated,ampl_work)
# plt.show()
nomera_nezanulyaemih_ampl=[]
for i in range(len(Mass_Amplitud_2)-1):
if Mass_Amplitud_2[i+1]>Max_ampl_work/10:
nomera_nezanulyaemih_ampl.append(i+1)
n=list(nomera_nezanulyaemih_ampl)
for i in n:
nomera_nezanulyaemih_ampl.append(flen-i)
nomera_nezanulyaemih_ampl.append(0)
# print(nomera_nezanulyaemih_ampl)
#Блок зануления малых амплитуд в оригинальном преобразовании Фурье
for i in range(len(Mass_Amplitud)):
for k in nomera_nezanulyaemih_ampl:
if k==i:
ind=1
break
else:
ind=0
if ind==0:
Mass_Amplitud[i]=0
# print(Mass_Amplitud)
result = np.fft.ifft(Mass_Amplitud)
number_of_work_ampl=len(nomera_nezanulyaemih_ampl)
return result,number_of_work_ampl
#############################################################################
###
#Основная часть
#############################################################################
###
#Пара активов на десяточку
# activs=['VSMO', 'URKA']
# parts=[ 0.4,0.6]
# activs2=[ 'RTKMP','MOEX', 'GMKN','TRNFP']
# parts2=[ 0.4, 0.4,0.1,0.1]
#Пара корзин после обучения(плохих)
# activs=['MOEX']
# parts=[1.0]
# activs2=[ 'MOEX', 'MVID', 'SNGS', 'TATNP']
38
# parts2=[ 0.1, 0.3, 0.2, 0.4]
#ещё одна пара активов после обучения неплохая
# activs=['VSMO', 'PHOR', 'NVTK']
# parts=[0.7, 0.1, 0.2]
# activs2=[ 'ALRS', 'AKRN']
# parts2=[ 0.5,0.5]
#Ещё одна пара с крутым предсказательным эффектом
# activs=['PHOR', 'MSNG', 'BANEP']
# parts=[0.4, 0.1, 0.5]
# activs2=['NMTP', 'ALRS']
# parts2=[ 0.5,0.5]
first_basket_prices_good.append(first_basket_prices[len(first_basket_prices)-
good_points+i])
39
# 3.3 создаём новый массив цен второго актива
second_basket_prices_good=[]
for i in range (good_points):
second_basket_prices_good.append(second_basket_prices[len(second_basket_price
s)-good_points+i])
# 3.4 создаём новый массив дат
dates_goodN=[]
for i in range (good_points):
dates_goodN.append(justice_basket_dates[len(justice_basket_dates)-
good_points+i])
full_x_return = []
for i in range(len(justice_coef_full)):
full_x_return.append(i)
txtl1 = 'furier aproxx for full using ' + str(number_sin) + ' sinus'
plt.plot(full_x_return, furier_transformed_points, label=txtl1)#Фурье-
апроксимация всего графика
top_p=max(justice_coef_full)*1.5
low_p=0
# vertikal_coordinate = len(justice_coef_full)-len(justice_coef_goodp)
vertikal_coordinate = len(justice_coef_full)-250
x_c=[vertikal_coordinate,vertikal_coordinate]
y_c=[top_p,low_p]
plt.plot(x_c,y_c)
#блок построения линии, для обрезки координат с позиции хороших точек
good_points_x=[]
for i in range(len(justice_coef_goodp)):
good_points_x.append(len(justice_coef_full)-i)
good_points_y=[simmetric_line for i in range(len(good_points_x))]
plt.plot(good_points_x,good_points_y)#блок построения линейной апроксимации
на "хороших точках"
good_points_x_return=[]
for i in range(len(good_points_x)):
good_points_x_return.append(good_points_x[len(good_points_x)-i-1])
txtl='furier aproxx for good points using '+str(number_good_sin)+' sinus'
40
plt.plot(good_points_x_return,furier_transformed__good_points, label = txtl)
#Фурье-апроксимация для хороших точек
#print(len(furier_transformed_points))
graph_text='global regress(kx+b)='+str(round(k_coef,4))
+'x+'+str(round(b_coef,2))
graph_text=graph_text+' | '+'reg quality='+str(round(regress_quality,2))
graph_text=graph_text+' | '+'timeline balance '+str(round(sq_coef,2))+' for
'+str(good_points)+' points'
graph_text=graph_text+' | '+'baskets corr='+str(round(corr_prices,2))+' with
'+str(round(p_value_pr,2))+'pvalue'
graph_text=graph_text+' | '+'simm coef='+str(round(simm_coeff,2))
graph_text=graph_text+' | '+str(n_cross)+'times cross simmetic line'
plt.legend(title=graph_text)
plt.show()
test = sm.tsa.adfuller(justice_coef_full)
print ('adf: ', test[0])
print ('p-value: ', test[1])
print ('Critical values: ', test[4])
if test[0]> test[4]['5%']:
print ('есть единичные корни, ряд не стационарен')
else:
print ('единичных корней нет, ряд стационарен')
# print(k_coef,b_coef)
# print(regress_quality)
# print(good_points)
# print(sq_coef)
# print(corr_prices)
# print(simm_coeff)
# print(n_cross)
# print (basket_prices)
# times=[i for i in range (len(basket_prices))]
# plt.plot(times,basket_prices)
41
Машинное обучение на языке Python 3.0:
import numpy as np
import pandas as pd
%matplotlib inline
data = pd.read_excel('C:\\Users\\Слава\\Desktop\\Диплом\\
mach_learn_full.xlsx',sheetname=0,header=0)
data2= pd.read_excel('C:\\Users\\Слава\\Desktop\\Диплом\\
Mach_learn_prep.xlsx',sheetname=0,header=0)
data=data.sample(frac=1)
data
X=data[['regress_coef_k','regress_coef_b','regress_qual','balanced
points','timeline balance','corr_coef','corr_p-value','simm_coef',
'number_crosses_on_GP','fur_full_aprox_sin_number','fur_full_aprox_qu
al','fur_GP_aprox_sin_number','fur_GP_aprox_qual']]
y=data[['Easy_trust']]
N_train, _ = X_train.shape
N_test, _ = X_test.shape
y_train= y_train.values.ravel()
y_test=y_test.values.ravel()
X_Exp=data2[['regress_coef_k','regress_coef_b','regress_qual','balanced
points','timeline balance','corr_coef','corr_p-value','simm_coef',
'number_crosses_on_GP','fur_full_aprox_sin_number','fur_full_aprox_qu
al','fur_GP_aprox_sin_number','fur_GP_aprox_qual']]
X_Exp
42
#Random Forest
rf = ensemble.RandomForestClassifier(n_estimators = 1000)
rf.fit(X_train, y_train)
a2=rf.predict(X_Exp)
print(err_train, err_test)
print(a2)
np.sum(rf.feature_importances_)
N, d = X.shape
feature_names = X.columns
importances = rf.feature_importances_
indices = np.argsort(importances)[::-1]
print("Feature ranking:")
for f in range(d):
43