import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import matplotlib.ticker as mticker
import pandas as pd
import yfinance as yf
import scienceplots
import requests
from tqdm import tqdm
='ignore', invalid='ignore')
np.seterr(divide
%matplotlib inline
8 Лабораторна робота № 8
Тема. Дослідження процесів самоорганізації в складних системах із використання теорії випадкових матриць
Мета. Навчитись використовувати методи теорії випадкових матриць для дослідження колективних процесів у складних системах
8.1 Теоретичні відомості
Вивчення статистичних властивостей матриць з незалежними випадковими елементами — випадкових матриць — має багату історію, що починається з ядерної фізики [1–7], де проблема з’явилася більше 50 років тому при дослідженні енергетичних рівнів складних ядер. Теорія випадкової матриці (ТВМ) була розвинена в цьому контексті Вігнером (Wigner), Дайсоном (Dyson), Метою (Mehta) та іншими для пояснення статистики рівнів енергії складних квантових систем. Дослідники постулювали, що функція Гамільтона, яка описує важкі ядра, може бути задана матрицею \(H\) з незалежними випадковими елементами \(H_{ij}\), отриманими з розподілу імовірності. Відштовхуючись від цього припущення було зроблено низку вражаючих передбачень, які було підтверджено експериментально. Для складних квантових систем результати на основі ТВМ представляють середнє за всіма можливими взаємодіями. Відхилення від універсальних передбачень ТВМ відображують системну специфіку, невипадкові властивості системи, забезпечуючи ключові підходи до розуміння базової взаємодії системи. Недавні дослідження, що використовували методи аналізу ТВМ до аналізу властивостей матриці взаємних кореляцій \(C\) для економічних систем, показують, що близько 98% власних значень матриці \(C\) співпадають зі значеннями, отримуваними з використанням ТВМ, таким чином пропонуючи задовільний рівень хаотичності у вимірюваних крос-кореляціях. Також було знайдено, що існують відхилення від передбачень за допомогою ТВМ у близько 2% найбільших власних значень. Ці результати викликають наступні питання:
- Яка можлива інтерпретація для відхилень від ТВМ?
- Що можна сказати про структуру C з цих результатів?
- Яке практичне значення отриманих результатів?
Шляхом комп’ютерного моделювання виявлено, що найбільше власне значення матриці \(C\) представляє вплив ринку в цілому. Аналіз змісту власних значень, що відхиляються від ТВМ, показує існування взаємних кореляцій між акціями того ж самого типу діяльності, найбільш капіталізованими акціями, і акціями фірм, що мають бізнес у певному географічному секторі (локалізовані територіально). Обчислюючи скалярний добуток власних векторів від одного періоду часу до наступного, можна побачити, що власні вектори, що відхиляються, мають різні ступені стабільності в часі, визначеному кількісно величиною скалярного добутку. Найбільші два-три власних вектори стійкі протягом тривалих періодів часу, у той час як для іншої частини власних векторів, що відхиляються, стабільність у часі зменшується як тільки відповідні власні значення наближаються до верхньої межі ТВМ.
8.1.1 Знаходження коефіцієнтів матриці крос-кореляцій
Визначення кореляцій між різними акціями — тема, цікава тема не лише з точки зору розуміння економіки як складної динамічної системи, але також і прагматично, зокрема, з точки зору розміщення активів і оцінки портфельного ризику. Ми будемо аналізувати взаємні кореляції між акціями, застосовуючи поняття і методи теорії випадкових матриць, що використовуються в контексті складних квантових систем, де точний характер взаємодій між підодиницями невідомий.
Для визначення кількісно кореляцій спочатку обчислюється зміна цін (прибутковості) акції \(i=1,...,N\) за час \(\Delta t\),
\[ G_{i}(t) = \ln S_i(t+\Delta t) - \ln S_i(t), \tag{8.1}\]
де \(S_i(t)\) позначає ціну акції \(i\). Оскільки різні ціни мають різні рівні змінюванності (стандартні відхилення), визначатимемо стандартизовану прибутковість
\[ g_i(t) \equiv \left[ G_i(t) - \left\langle G_i \right\rangle \right] \big/ \sigma_i, \tag{8.2}\]
де \(\sigma_i \equiv \sqrt{\left\langle G_{i}^{2} \right\rangle - \left\langle G_i \right\rangle^{2}}\) — стандартне відхилення \(G_i\), а \(\left\langle...\right\rangle\) позначає середнє значення за досліджуваний період часу. Тоді знаходження матриці кореляцій \(C\) зводиться до обчислення формули:
\[ C_{ij} \equiv \left\langle g_i(t)g_j(t) \right\rangle. \tag{8.3}\]
За побудовою, елементи \(C_{ij}\) обмежені областю \(−1 \leq C_{ij} \leq 1\), де \(C_{ij} = 1\) відповідає повним кореляціям, \(C_{ij} = -1\) — повним антикореляціям, і \(C_{ij} = 0\) свідчить про некорельованність пар акцій.
Труднощі в аналізі важливості та значення коефіцієнтів крос-кореляції \(C_{ij}\) виникають внаслідок кількох причин, що полягають в наступному:
- ринкові умови з часом змінюються і взаємна кореляція, що існує між будь-якою парою акцій, може бути не постійною (нестаціонарною);
- скінчена довжина досліджуваного ряду, доступного для оцінювання взаємних кореляцій, додає так званий “шум вимірювання” — чим коротший досліджуваний ряд — тим менш точними будуть отримувані значення.
Яким же чином можна виділяти з \(C_{ij}\) ті акції, що залишилися корельованими на розглядуваному періоді часу? Щоб відповісти на це питання, перевіримо статистику \(C\) у порівнянні із так званою “нульовою гіпотезою” випадкової кореляційної матриці — матриці кореляцій, побудованої із взаємно некорельованих часових рядів. Якщо властивості \(C\) відповідають властивостям для випадкової матриці кореляцій, тоді можна говорити про те, що значення емпірично вимірюваних властивостей \(C\) випадкові. Навпаки, відхилення властивостей \(C\) від таких же властивостей для випадкової кореляційної матриці передає інформацію про “справжні” кореляції. Отже, нашою метою є порівняння властивостей \(C\) з такими ж властивостями випадкової матриці кореляцій і розділ властивостей \(C\) на дві групи: (a) частина \(C\), що відповідає властивостям випадкової кореляційної матриці (“шум”) і (b) частина \(C\), що відхиляється (“інформація”).
8.1.2 Розподіл власних значень
Для отримання інформації про взаємні кореляції \(C\) необхідно порівняти властивості \(C\) з такими ж властивостями випадкової матриці крос-кореляцій [8]. У матричній нотації така матриця може бути виражена як
\[ C = \frac{1}{L} GG^{T}, \tag{8.4}\]
де \(G\) — матриця розміру \(N \times L\) з елементами \(g_{im}=g_i(m\Delta t), i=1,...,N; m=0,...,L-1\) і \(G^{T}\) позначає транспонування \(G\). Розглянемо випадкову кореляційну матрицю
\[ R = \frac{1}{L} AA^{T}, \tag{8.5}\]
де \(A\) — матриця розміру \(N \times L\), що містить \(N\) часових рядів із \(L\) випадковими елементів \(a_{im}\) з нульовим середнім і одиничним відхиленням, що означають взаємну некорельованість. За побудовою \(R\) належить до типу матриць, які часто називають матрицями Вішарта у багатовимірній статистиці [9].
Статистичні властивості випадкових матриць \(R\) відомі [10,11]. Зокрема, у наближенні \(N \to \infty\), \(L \to \infty\), такому, що \(Q \equiv L/N(>1)\) фіксоване, показано аналітично [11], що функція розподілу щільності імовірності \(P_{rm}(\lambda)\) власних значень \(\lambda\) випадкової матриці кореляції \(R\) визначається як
\[ P_{rm}(\lambda) = \frac{Q}{2\pi}\frac{\sqrt{(\lambda_{+} - \lambda)(\lambda - \lambda_{-})}}{\lambda}, \tag{8.6}\]
із \(\lambda\) в межах границь \(\lambda_{-} \leq \lambda_{i} \leq \lambda_{+}\), де \(\lambda_{-}\) і \(\lambda_{+}\) — найменше та найбільше власні значення \(R\), які можна визначити аналітично як
\[ \lambda_{\pm} = 1 + 1/Q \pm 2\sqrt{1/Q}. \tag{8.7}\]
Вираз (8.6) є точним для випадку розподілених за Гаусом матричних елементів \(a_{im}\).
Порівняємо розподіл власних значень \(P(\lambda)\) для \(C\) з \(P_{rm}(\lambda)\). Для цього обчислимо власні значення \(\lambda_i\) матриці \(C\), причому \(\lambda_i\) впорядкуємо за зростанням (\(\lambda_{i+1} > \lambda_{i}\)). При дослідженнях зверніть увагу на присутність чіткої “великої частини” власних значень, що спадають у межах границь \([\lambda_{-}, \lambda_{+}]\) для \(P_{rm}(\lambda)\). Також зверніть увагу на відхилення для деяких найбільших і найменших власних значень отриманих за допомогою ТВМ.
Оскільки (8.6) є таким, що строго відповідає лише для \(L \to \infty\) і \(N \to \infty\), необхідно перевірити також відхилення від ідеального випадку, оскільки робота проводиться завжди зі скінченими рядами. При дослідженнях виявляється, що для кількох найбільших (найменших) власних значень ефект впливу скінчених величин \(L\) і \(N\) відсутній.
8.1.3 Розподіл власних векторів
Відхилення \(P(\lambda)\) від передбачення ТВМ \(P_{rm}(\lambda)\) свідчить про те, що ці відхилення також повинні відображатися в статистиці відповідних компонент власного вектора [8]. Відповідно, у даній лабораторній ми будемо аналізувати розподіл компоненти власного вектора. Розподіл компонент \(\left\{ u_{l}^{k}; l=1,...,N \right\}\) власного вектора \(u^k\) випадкової кореляційної матриці \(R\) має відповідати розподілу Гауса з нульовим середнім та одиничною дисперсією:
\[ \rho_{rm}(u) = \frac{1}{\sqrt{2\pi}}\exp\left( -u^{2} \big/ 2 \right). \]
8.1.4 Обернене відношення участі
Вивчивши інтерпретацію найбільшого власного значення, що значно відхиляється від результатів ТВМ, зосередимось на власних значеннях, що залишаються. Відхилення розподілу компонентів власного вектора \(u_k\) від ТВМ Гаусового передбачення більш явне, коли відстань від верхньої границі ТВМ \(\lambda_k - \lambda_{+}\) збільшується. Оскільки близькість до \(\lambda_{+}\) збільшує ефекти хаотичності, визначаємо кількість компонентів, що беруть значну участь у кожному власному векторі, що, у свою чергу, відображає ступінь відхилення від ТВМ для розподілу компонентів власного вектора. Для цього використовується поняття оберненого відношення участі (ОВУ) [12–14], що часто застосовується в теорії локалізації. ОВУ власного вектора \(u_k\) визначається як
\[ I^{k} \equiv \sum_{l=1}^{N}\left[ u_{l}^{k} \right]^4, \tag{8.8}\]
де \(u_{l}^{k}\), \(l=1,...,N\) — компоненти власного вектора \(u^{k}\). Значення \(I^{k}\) може бути проілюстровано двома граничними випадками:
- вектор з ідентичними компонентами \(u_{l}^{k} \equiv 1 \big/ \sqrt{N}\) має \(I^{k} = 1 \big/ N\);
- вектор з одним компонентом \(u_{1}^{k}=1\) і нульовими іншими має \(I^{k}=1\).
Таким чином, ОВУ визначає кількість даних з числа компонентів власного вектора, що значно впливають на ринок, заданий системою часових рядів. Наявність векторів з великими значеннями \(I^{k}\) також виникає в теорії локалізації Андерсона. У контексті теорії локалізації часто знаходять “випадкову смугу матриць”, що містять узагальнені стани з маленьким \(I^{k}\) в більшій частині спектра власних значень, тоді як основні стани локалізовані і мають великі \(I^{k}\). Виявлення локалізованих станів для маленьких і великих власних значень матриці крос-кореляцій \(C\) нагадує про локалізацію Андерсона і припускає, що \(C\) може мати випадкову зону матричної структури.
8.2 Хід роботи
Імпортуємо необхідні бібліотеки:
Визначаємо стиль рисунків:
'science', 'notebook', 'grid']) # стиль, що використовуватиметься
plt.style.use([# для виведення рисунків
= 16
size = {
params 'figure.figsize': (8, 6), # встановлюємо ширину та висоту рисунків за замовчуванням
'font.size': size, # розмір фонтів рисунку
'lines.linewidth': 2, # товщина ліній
'axes.titlesize': 'small', # розмір титулки над рисунком
'axes.labelsize': size, # розмір підписів по осям
'legend.fontsize': size, # розмір легенди
'xtick.labelsize': size, # розмір розмітки по осі Ох
'ytick.labelsize': size, # розмір розмітки по осі Ох
"font.family": "Serif", # сімейство стилів підписів
"font.serif": ["Times New Roman"], # стиль підпису
'savefig.dpi': 300, # якість збережених зображень
'axes.grid': False # побудова сітки на самому рисунку
}
# оновлення стилю згідно налаштувань plt.rcParams.update(params)
Виконуємо парсинг та фільтрацію заголовків акцій компаній:
= {
headers 'authority': 'api.nasdaq.com',
'accept': 'application/json, text/plain, */*',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36',
'origin': 'https://www.nasdaq.com',
'sec-fetch-site': 'same-site',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'referer': 'https://www.nasdaq.com/',
'accept-language': 'en-US,en;q=0.9',
}
= (
params 'tableonly', 'true'),
('limit', '25'),
('offset', '0'),
('download', 'true'),
(
)
= requests.get('https://api.nasdaq.com/api/screener/stocks', headers=headers, params=params)
r = r.json()['data']
data = pd.DataFrame(data['rows'], columns=data['headers'])
df = df.dropna(subset={'marketCap'})
df = df[~df['symbol'].str.contains("\/|\.|\^")]
df df.head()
symbol | name | lastsale | netchange | pctchange | marketCap | country | ipoyear | volume | sector | industry | url | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | A | Agilent Technologies Inc. Common Stock | $145.17 | -2.28 | -1.546% | 42542835578.00 | United States | 1999 | 835947 | Industrials | Biotechnology: Laboratory Analytical Instruments | /market-activity/stocks/a |
1 | AA | Alcoa Corporation Common Stock | $31.94 | 0.54 | 1.72% | 5735114141.00 | United States | 2016 | 3432603 | Industrials | Aluminum | /market-activity/stocks/aa |
2 | AACG | ATA Creativity Global American Depositary Shares | $1.06 | -0.02 | -1.852% | 33519500.00 | China | 2008 | 3530 | Real Estate | Other Consumer Services | /market-activity/stocks/aacg |
3 | AACI | Armada Acquisition Corp. I Common Stock | $11.01 | 0.00 | 0.00% | 0.00 | United States | 2021 | 1414 | Finance | Blank Checks | /market-activity/stocks/aaci |
4 | AACIW | Armada Acquisition Corp. I Warrant | $0.0749 | 0.0018 | 2.462% | 0.00 | United States | 2021 | 124 | Finance | Blank Checks | /market-activity/stocks/aaciw |
Фільтруємо та сортуємо заголовків акцій за їх капіталізацією:
def cust_filter(mkt_cap):
if 'M' in mkt_cap:
return float(mkt_cap[1:-1])
elif 'B' in mkt_cap:
return float(mkt_cap[1:-1]) * 1000
elif mkt_cap == '':
return 0.0
else:
return float(mkt_cap[1:]) / 1e6
'marketCap'] = df['marketCap'].apply(cust_filter)
df[= df.sort_values('marketCap', ascending=False)
df df.head()
symbol | name | lastsale | netchange | pctchange | marketCap | country | ipoyear | volume | sector | industry | url | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2886 | GOOG | Alphabet Inc. Class C Capital Stock | $151.05 | -0.72 | -0.474% | 878004.650000 | United States | 2004 | 10560963 | Technology | Computer Software: Programming Data Processing | /market-activity/stocks/goog |
395 | AMZN | Amazon.com Inc. Common Stock | $179.80 | 0.93 | 0.52% | 867651.156122 | United States | 1997 | 20789293 | Consumer Discretionary | Catalog/Specialty Distribution | /market-activity/stocks/amzn |
2887 | GOOGL | Alphabet Inc. Class A Common Stock | $149.99 | -0.78 | -0.517% | 864825.670000 | United States | 2004 | 13005329 | Technology | Computer Software: Programming Data Processing | /market-activity/stocks/googl |
16 | AAPL | Apple Inc. Common Stock | $171.60 | -0.68 | -0.395% | 649826.779600 | United States | 1980 | 37196662 | Technology | Computer Manufacturing | /market-activity/stocks/aapl |
4662 | NVDA | NVIDIA Corporation Common Stock | $954.34 | 11.45 | 1.214% | 385850.000000 | United States | 1999 | 47962939 | Technology | Semiconductors | /market-activity/stocks/nvda |
Визначаємо найпередовіші акцій за їх капіталізацією:
= 150
top = df.iloc[:top]['symbol'].tolist()
tickers_list 10] tickers_list[:
['GOOG', 'AMZN', 'GOOGL', 'AAPL', 'NVDA', 'META', 'MSFT', 'CRM', 'NVS', 'TMUS']
Зчитуємо дані з Yahoo Finance згідно створенного списку акцій. Вилучимо ціни закриття всіх акцій за період з 31 грудня 2001 року по 1 жовтня 2023 року:
= "2001-12-31"
start = "2023-12-01"
end = yf.download(tickers_list, start, end)["Adj Close"]
data_init
= 'time, days' # підпис по вісі Ох xlabel
Далі нам потребується обрати фінансовий індекс для порівняння з розрахованими індикаторами. Далі надається список усіх доступних для нас акцій:
= 'AAPL' # підпис по вісі Оу
ylabel
data_init.columns.values
array(['AAPL', 'ABBV', 'ABT', 'ACN', 'ADBE', 'AMAT', 'AMD', 'AMGN',
'AMZN', 'ARM', 'ASML', 'AVGO', 'AXP', 'BA', 'BABA', 'BAC', 'BBD',
'BDX', 'BHP', 'BKNG', 'BLK', 'BMO', 'BNH', 'C', 'CAT', 'CBRE',
'CCZ', 'CEG', 'CHKP', 'CHT', 'CMCSA', 'CMG', 'COP', 'COST', 'CRM',
'CVS', 'CVX', 'DE', 'DHR', 'DIS', 'DRI', 'DUKB', 'EBR', 'EL',
'ELV', 'ETN', 'EXR', 'FDX', 'FMX', 'FTS', 'FTV', 'GE', 'GMAB',
'GOOG', 'GOOGL', 'GS', 'GWW', 'HBAN', 'HD', 'HDB', 'HIG', 'HON',
'HPQ', 'HSBC', 'HSY', 'IBM', 'INTC', 'INTU', 'ISRG', 'ITW', 'JBHT',
'JNJ', 'JPM', 'KKR', 'KMI', 'KO', 'KOF', 'KVUE', 'LIN', 'LLY',
'LOW', 'LPLA', 'LRCX', 'LYG', 'MA', 'MDT', 'META', 'MKL', 'MMC',
'MRK', 'MS', 'MSFT', 'MU', 'MUFG', 'NEE', 'NFLX', 'NKE', 'NMR',
'NOK', 'NOW', 'NVDA', 'NVO', 'NVS', 'ORCL', 'PDD', 'PEP', 'PFE',
'PFG', 'PG', 'PGR', 'PLD', 'PM', 'PPL', 'QCOM', 'RACE', 'RSG',
'RTX', 'RY', 'SAP', 'SCHW', 'SHEL', 'SHG', 'SNPS', 'SNY', 'SPG',
'SPGI', 'STZ', 'SYK', 'T', 'TBB', 'TBC', 'TGT', 'TJX', 'TM', 'TMO',
'TMUS', 'TSLA', 'TSM', 'TTE', 'TXN', 'UBER', 'UL', 'UNH', 'UNP',
'UPS', 'V', 'VZ', 'WMT', 'WSM', 'XOM'], dtype=object)
= 5.0
perc = int(((100-perc)/100)*data_init.shape[0] + 1)
min_count = data_init.dropna(axis=1, thresh=min_count)
data_init data_init
Ticker | AAPL | ABT | ACN | ADBE | AMAT | AMD | AMGN | AMZN | ASML | AXP | ... | TTE | TXN | UL | UNH | UNP | UPS | VZ | WMT | WSM | XOM |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | |||||||||||||||||||||
2001-12-31 | 0.331081 | 14.912379 | 19.321152 | 15.452657 | 14.758861 | 15.860000 | 40.573254 | 0.541000 | 11.885590 | 22.767019 | ... | 10.828801 | 18.147404 | 8.532564 | 14.236670 | 9.228433 | 29.862459 | 14.652889 | 12.593814 | 14.009839 | 19.206507 |
2002-01-02 | 0.352246 | 14.936456 | 18.811560 | 15.845818 | 15.336689 | 16.389999 | 40.544498 | 0.548000 | 12.401443 | 22.939653 | ... | 10.930552 | 18.678860 | 8.558210 | 14.166265 | 9.163671 | 29.840557 | 14.970891 | 12.703233 | 13.405689 | 19.353123 |
2002-01-03 | 0.356478 | 14.949829 | 18.223030 | 16.462933 | 16.742651 | 19.370001 | 39.063622 | 0.595000 | 13.412239 | 23.304060 | ... | 10.987591 | 19.897335 | 8.483836 | 14.007341 | 9.500431 | 30.388485 | 15.467968 | 12.687912 | 13.562436 | 19.382444 |
2002-01-04 | 0.358142 | 14.923078 | 19.880976 | 17.866358 | 16.643274 | 20.000000 | 39.983780 | 0.612500 | 13.530748 | 24.116026 | ... | 11.038470 | 19.702888 | 8.386379 | 14.081776 | 9.714135 | 31.040512 | 15.625422 | 12.604758 | 13.742058 | 19.548613 |
2002-01-07 | 0.346199 | 14.818766 | 18.983816 | 17.995752 | 16.466602 | 19.980000 | 40.034107 | 0.617000 | 13.377389 | 24.039314 | ... | 10.904343 | 19.061247 | 8.296615 | 13.991246 | 9.702801 | 31.292574 | 15.529715 | 12.558800 | 13.768172 | 19.377560 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
2023-11-24 | 189.727905 | 102.375435 | 332.824677 | 619.429993 | 150.085632 | 122.309998 | 263.400879 | 146.740005 | 689.972473 | 163.890518 | ... | 67.166924 | 152.387482 | 47.501419 | 543.098999 | 221.883804 | 150.343994 | 36.789608 | 51.651596 | 182.729935 | 103.607109 |
2023-11-27 | 189.548126 | 102.216202 | 331.220490 | 619.270020 | 150.554840 | 122.650002 | 262.220093 | 147.729996 | 687.177368 | 163.511749 | ... | 67.088799 | 151.405243 | 47.402313 | 539.614685 | 217.501144 | 147.931061 | 36.730602 | 51.886585 | 182.710037 | 103.002724 |
2023-11-28 | 190.157349 | 101.569321 | 331.350037 | 623.320007 | 147.809494 | 122.010002 | 263.460419 | 147.029999 | 674.809021 | 165.126541 | ... | 67.352470 | 151.702881 | 47.471687 | 536.577087 | 218.866394 | 149.068314 | 36.848614 | 52.505505 | 183.207840 | 102.943283 |
2023-11-29 | 189.128662 | 103.131775 | 332.127197 | 617.390015 | 149.107300 | 123.849998 | 264.532043 | 146.320007 | 686.408752 | 166.890823 | ... | 66.268501 | 152.000534 | 47.104992 | 531.067627 | 219.103821 | 150.601105 | 37.133804 | 51.658218 | 186.314056 | 101.397644 |
2023-11-30 | 189.707916 | 103.788605 | 331.927948 | 611.010010 | 149.526596 | 121.160004 | 267.548492 | 146.089996 | 682.565491 | 170.220078 | ... | 66.454048 | 151.514374 | 47.273476 | 548.926025 | 222.863235 | 149.928665 | 37.694355 | 51.529133 | 186.712296 | 101.793961 |
5518 rows × 114 columns
= data_init.dropna(axis=1)
data_init data_init
Ticker | AAPL | ABT | ACN | ADBE | AMAT | AMD | AMGN | AMZN | ASML | AXP | ... | TTE | TXN | UL | UNH | UNP | UPS | VZ | WMT | WSM | XOM |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | |||||||||||||||||||||
2001-12-31 | 0.331081 | 14.912379 | 19.321152 | 15.452657 | 14.758861 | 15.860000 | 40.573254 | 0.541000 | 11.885590 | 22.767019 | ... | 10.828801 | 18.147404 | 8.532564 | 14.236670 | 9.228433 | 29.862459 | 14.652889 | 12.593814 | 14.009839 | 19.206507 |
2002-01-02 | 0.352246 | 14.936456 | 18.811560 | 15.845818 | 15.336689 | 16.389999 | 40.544498 | 0.548000 | 12.401443 | 22.939653 | ... | 10.930552 | 18.678860 | 8.558210 | 14.166265 | 9.163671 | 29.840557 | 14.970891 | 12.703233 | 13.405689 | 19.353123 |
2002-01-03 | 0.356478 | 14.949829 | 18.223030 | 16.462933 | 16.742651 | 19.370001 | 39.063622 | 0.595000 | 13.412239 | 23.304060 | ... | 10.987591 | 19.897335 | 8.483836 | 14.007341 | 9.500431 | 30.388485 | 15.467968 | 12.687912 | 13.562436 | 19.382444 |
2002-01-04 | 0.358142 | 14.923078 | 19.880976 | 17.866358 | 16.643274 | 20.000000 | 39.983780 | 0.612500 | 13.530748 | 24.116026 | ... | 11.038470 | 19.702888 | 8.386379 | 14.081776 | 9.714135 | 31.040512 | 15.625422 | 12.604758 | 13.742058 | 19.548613 |
2002-01-07 | 0.346199 | 14.818766 | 18.983816 | 17.995752 | 16.466602 | 19.980000 | 40.034107 | 0.617000 | 13.377389 | 24.039314 | ... | 10.904343 | 19.061247 | 8.296615 | 13.991246 | 9.702801 | 31.292574 | 15.529715 | 12.558800 | 13.768172 | 19.377560 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
2023-11-24 | 189.727905 | 102.375435 | 332.824677 | 619.429993 | 150.085632 | 122.309998 | 263.400879 | 146.740005 | 689.972473 | 163.890518 | ... | 67.166924 | 152.387482 | 47.501419 | 543.098999 | 221.883804 | 150.343994 | 36.789608 | 51.651596 | 182.729935 | 103.607109 |
2023-11-27 | 189.548126 | 102.216202 | 331.220490 | 619.270020 | 150.554840 | 122.650002 | 262.220093 | 147.729996 | 687.177368 | 163.511749 | ... | 67.088799 | 151.405243 | 47.402313 | 539.614685 | 217.501144 | 147.931061 | 36.730602 | 51.886585 | 182.710037 | 103.002724 |
2023-11-28 | 190.157349 | 101.569321 | 331.350037 | 623.320007 | 147.809494 | 122.010002 | 263.460419 | 147.029999 | 674.809021 | 165.126541 | ... | 67.352470 | 151.702881 | 47.471687 | 536.577087 | 218.866394 | 149.068314 | 36.848614 | 52.505505 | 183.207840 | 102.943283 |
2023-11-29 | 189.128662 | 103.131775 | 332.127197 | 617.390015 | 149.107300 | 123.849998 | 264.532043 | 146.320007 | 686.408752 | 166.890823 | ... | 66.268501 | 152.000534 | 47.104992 | 531.067627 | 219.103821 | 150.601105 | 37.133804 | 51.658218 | 186.314056 | 101.397644 |
2023-11-30 | 189.707916 | 103.788605 | 331.927948 | 611.010010 | 149.526596 | 121.160004 | 267.548492 | 146.089996 | 682.565491 | 170.220078 | ... | 66.454048 | 151.514374 | 47.273476 | 548.926025 | 222.863235 | 149.928665 | 37.694355 | 51.529133 | 186.712296 | 101.793961 |
5518 rows × 111 columns
= data_init.T
data data
Date | 2001-12-31 | 2002-01-02 | 2002-01-03 | 2002-01-04 | 2002-01-07 | 2002-01-08 | 2002-01-09 | 2002-01-10 | 2002-01-11 | 2002-01-14 | ... | 2023-11-16 | 2023-11-17 | 2023-11-20 | 2023-11-21 | 2023-11-22 | 2023-11-24 | 2023-11-27 | 2023-11-28 | 2023-11-29 | 2023-11-30 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Ticker | |||||||||||||||||||||
AAPL | 0.331081 | 0.352246 | 0.356478 | 0.358142 | 0.346199 | 0.341815 | 0.327301 | 0.320952 | 0.318231 | 0.319743 | ... | 189.468246 | 189.448257 | 191.206009 | 190.397049 | 191.066193 | 189.727905 | 189.548126 | 190.157349 | 189.128662 | 189.707916 |
ABT | 14.912379 | 14.936456 | 14.949829 | 14.923078 | 14.818766 | 14.722468 | 14.642225 | 14.775971 | 15.004192 | 15.006884 | ... | 99.777985 | 99.071396 | 100.713455 | 101.420052 | 102.206245 | 102.375435 | 102.216202 | 101.569321 | 103.131775 | 103.788605 |
ACN | 19.321152 | 18.811560 | 18.223030 | 19.880976 | 18.983816 | 19.579529 | 19.787670 | 19.967098 | 18.998173 | 17.900053 | ... | 326.129120 | 326.637238 | 329.696075 | 329.058411 | 331.917969 | 332.824677 | 331.220490 | 331.350037 | 332.127197 | 331.927948 |
ADBE | 15.452657 | 15.845818 | 16.462933 | 17.866358 | 17.995752 | 18.234634 | 18.727327 | 18.140078 | 17.931051 | 17.717054 | ... | 602.059998 | 602.659973 | 612.700012 | 610.989990 | 619.719971 | 619.429993 | 619.270020 | 623.320007 | 617.390015 | 611.010010 |
AMAT | 14.758861 | 15.336689 | 16.742651 | 16.643274 | 16.466602 | 16.849388 | 17.180624 | 16.926674 | 16.565987 | 16.676403 | ... | 154.216721 | 148.020569 | 151.985306 | 148.678024 | 149.227097 | 150.085632 | 150.554840 | 147.809494 | 149.107300 | 149.526596 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
UPS | 29.862459 | 29.840557 | 30.388485 | 31.040512 | 31.292574 | 31.352852 | 31.128199 | 31.150120 | 30.969296 | 30.700804 | ... | 145.280777 | 146.338913 | 147.911285 | 148.109070 | 149.078201 | 150.343994 | 147.931061 | 149.068314 | 150.601105 | 149.928665 |
VZ | 14.652889 | 14.970891 | 15.467968 | 15.625422 | 15.529715 | 15.493931 | 15.207698 | 15.360151 | 15.462824 | 15.652602 | ... | 35.580006 | 35.629177 | 36.120884 | 36.632263 | 36.730602 | 36.789608 | 36.730602 | 36.848614 | 37.133804 | 37.694355 |
WMT | 12.593814 | 12.703233 | 12.687912 | 12.604758 | 12.558800 | 12.657275 | 12.342160 | 12.473463 | 12.210861 | 12.202105 | ... | 51.644978 | 51.416603 | 51.400055 | 51.585403 | 51.191544 | 51.651596 | 51.886585 | 52.505505 | 51.658218 | 51.529133 |
WSM | 14.009839 | 13.405689 | 13.562436 | 13.742058 | 13.768172 | 13.856347 | 13.539575 | 13.840023 | 13.405689 | 13.193413 | ... | 170.693298 | 178.060654 | 181.146957 | 179.145844 | 180.669083 | 182.729935 | 182.710037 | 183.207840 | 186.314056 | 186.712296 |
XOM | 19.206507 | 19.353123 | 19.382444 | 19.548613 | 19.377560 | 19.402000 | 19.177185 | 19.250490 | 18.815535 | 18.883953 | ... | 101.516541 | 103.993515 | 103.537758 | 103.498123 | 103.052269 | 103.607109 | 103.002724 | 102.943283 | 101.397644 | 101.793961 |
111 rows × 5518 columns
8.2.1 Знаходження коефіцієнтів матриці крос-кореляцій
Перш за все нам потребується процедура для перетворення ряду до потрібного нам виду. Для цього визначимо функцію transformation()
. З її допомогою для всього ряду та у віконній процедурі будемо рахувати прибутковості та всі необхідні індикатори.
def transformation(signal, ret_type):
= signal.copy()
for_rmt # Зважаючи на вид ряду, виконуємо
# необхідні перетворення
if ret_type == 1:
for_rmt.valueselif ret_type == 2:
= for_rmt.diff(axis=1).values
for_rmt elif ret_type == 3:
= for_rmt.pct_change(axis=1).values
for_rmt elif ret_type == 4:
= for_rmt.pct_change(axis=1).dropna(axis=1).values
for_rmt -= np.nanmean(for_rmt, axis=1, keepdims=True)
for_rmt /= np.nanstd(for_rmt, axis=1, keepdims=True)
for_rmt elif ret_type == 5:
= for_rmt.pct_change(axis=1).values
for_rmt = for_rmt[~np.isnan(for_rmt).any(axis=1)]
for_rmt -= np.mean(for_rmt, axis=1, keepdims=True)
for_rmt /= np.std(for_rmt, axis=1, keepdims=True)
for_rmt = np.abs(for_rmt)
for_rmt elif ret_type == 6:
= for_rmt.values
for_rmt -= np.mean(for_rmt, axis=1, keepdims=True)
for_rmt /= np.std(for_rmt, axis=1, keepdims=True)
for_rmt
return for_rmt
type = 4
= transformation(data, type) log_ret
= log_ret.shape N, T
Будуємо матрицю кореляцій для матриці прибутковостей наших активів:
def calc_cross_corr(data):
= (1/T)*np.dot(data, data.T)
C = np.diag_indices(data.shape[0])
di = np.ma.asarray(C)
ccoef = np.ma.masked
ccoef[di] = ccoef.compressed()
ccoef_flat
return C, ccoef_flat
= calc_cross_corr(log_ret) C, ccoef_flat
І матрицю кореляцій для перемішаної матриці прибутковостей:
1234)
np.random.seed(= np.random.normal(size=(N,T))
random_stocks
= calc_cross_corr(random_stocks) R, ccoef_flat_rand
Виводимо результат:
= plt.figure(figsize=(8, 6))
fig = gridspec.GridSpec(2, 2)
gs
= fig.add_subplot(gs[0, 0])
ax1 = ax1.imshow(C, cmap='hot', interpolation='nearest')
im1
ax1.invert_yaxis()=ax1)
fig.colorbar(im1, ax
= fig.add_subplot(gs[0, 1])
ax2 = ax2.imshow(R, cmap='hot', interpolation='nearest')
im2
ax2.invert_yaxis()=ax2)
fig.colorbar(im2, ax
= fig.add_subplot(gs[1, :])
ax3 ='auto', density=True, label=r'$P^{init}(C_{ij})$')
ax3.hist(ccoef_flat, bins='auto', density=True, label=r'$P^{rand}(C_{ij})$')
ax3.hist(ccoef_flat_rand, binsr'$C_{ij}$')
ax3.set_xlabel(r'$P(C_{ij})$')
ax3.set_ylabel(
ax3.legend()
fig.align_labels(); plt.show()

На Рис. 8.1 видно, що розподіл коефіцієнтів кореляції для вихідної матриці значно відхиляється розподілу для випадкової матриці. Можна помітити, що \(P^{init}(C_{ij})\) представляється доволі розтягненим в діапазоні \(C_{ij}\in [0.1, 0.8]\), що вказує на те, що достатньо багато досліджуваних активів досить сильно відрізняють один від одного. Можна виокремити активи, які характеризуються досить значним ступенем лінійної залежності по відношенню до більності активів на протязі значного часу існування, а є такі активи, які проявляють лінійну залежність лише в конкретних ринкових умовах. Тим не менш, увесь ринок характеризується досить значним ступенем детермінованості.
8.2.2 Розподіл власних значень та векторів
def calc_lambd_eig(C):
return np.linalg.eig(C)
= calc_lambd_eig(C)
lambdas, u = calc_lambd_eig(R)
lambdas_rand, u_rand
= T/N
Q
= 1 + 1/Q + 2*np.sqrt(1/Q)
lambda_plus = 1 - 1/Q - 2*np.sqrt(1/Q)
lambda_minus
print("Верхня границя розподілу власних значень, що прогнозується ТВМ: ", lambda_plus)
print("Нижня границя розподілу власних значень, що прогнозується ТВМ: ", lambda_minus)
Верхня границя розподілу власних значень, що прогнозується ТВМ: 1.3038069950128888
Нижня границя розподілу власних значень, що прогнозується ТВМ: 0.6961930049871112
Генеруємо 1000 випадкових значень в діапазоні від \(\lambda_{-}\) до \(\lambda_{+}\), і генеруємо \(P_{rm}(\lambda)\).
= np.linspace(lambda_minus, lambda_plus, 1000)
lambda_random = (Q/(2*np.pi))*np.sqrt((lambda_plus-lambda_random)*(lambda_random-lambda_minus))/lambda_random P_rm
Виводимо результат:
= plt.subplots(2, 1, figsize=(8, 6))
fig, ax 0].hist(lambdas, bins=50, density=True, color="skyblue", label='$P^{init}_{\it{empiric}}$')
ax[0].hist(lambdas_rand, bins='auto', density=True, color="red", label='$P^{rand}_{\it{empiric}}$')
ax[0].set_xlabel('$\lambda_{i}$')
ax[0].set_ylabel('$P(\lambda_{i})$')
ax[0].text(20, 0.5, '$\lambda_{max}=$'+f'{lambdas.max():.2f}', ha='center', va='center')
ax[0].set_yscale('log')
ax[0].legend()
ax[
1].hist(lambdas, bins='auto', density=True, color="skyblue", label='$P^{init}_{\it{empiric}}$')
ax[1].hist(lambdas_rand, bins='auto', density=True, color="red", label='$P^{rand}_{\it{empiric}}$')
ax[1].set_xlabel('$\lambda_{i}$')
ax[1].set_ylabel('$P(\lambda_{i})$')
ax[1].set_xlim(lambda_minus-3*np.std(lambdas_rand), lambda_plus+3*np.std(lambdas_rand))
ax[1].scatter(lambda_random, P_rm, label='$P_{\it{RMT}}$', color='green')
ax[1].legend()
ax[
fig.tight_layout(); plt.show()

На Рис. 8.2 видно, що розподіл \(\lambda_i\) значно відхиляється від тієї частки, що відповідає випадковій матриці крос-кореляцій (\(P^{rand}_{empiric}\)). Для вихідного розподілу видно, що \(\lambda_{max}\) значно відхиляється від передбачення ТВМ. Для \(P^{init}_{empiric}\) знаходяться і такі власні значення, що менші за ті, що знаходяться на нижній границі випадкової матриці. Загалом, така закономірність вказує на те, що на ринку є один або декілька індексів, що регулюють динаміку всього ринку.
На Рис. 8.3 представлено щільність розподілу ймовірностей компонент власних векторів вихідної матриці прибутковостей і випадкової матриці при \(\lambda_{1}\), \(\lambda_{30}\) та \(\lambda_{70}\).
= plt.subplots(3, 1, figsize=(8, 6), sharex=True, sharey=True)
fig, ax 0].hist(u[:, 0], bins=50, density=True, label=r'$\rho(u)_{empiric}^{init}$')
ax[0].hist(u_rand[:, 0], bins=50, density=True, alpha=0.7, label=r'$\rho(u)_{empiric}^{rand}$')
ax[0].set_xlabel('$u_{1}$')
ax[0].set_ylabel('$P(u)$')
ax[0].set_title('Компоненти власного вектора при $ \lambda_{1} $')
ax[0].legend()
ax[0].set_yscale('log')
ax[
1].hist(u[:, 30], bins=50, density=True, label=r'$\rho(u)_{empiric}^{init}$')
ax[1].hist(u_rand[:, 30], bins=50, density=True, alpha=0.7, label=r'$\rho(u)_{empiric}^{rand}$')
ax[1].set_xlabel('$u_{30}$')
ax[1].set_ylabel('$P(u)$')
ax[1].set_title('Компоненти власного вектора при $ \lambda_{30} $')
ax[1].legend()
ax[1].set_yscale('log')
ax[
2].hist(u[:, 70], bins=50, density=True, label=r'$\rho(u)_{empiric}^{init}$')
ax[2].hist(u_rand[:, 70], bins=50, density=True, alpha=0.7, label=r'$\rho(u)_{empiric}^{rand}$')
ax[2].set_xlabel('$u_{70}$')
ax[2].set_ylabel('$P(u)$')
ax[2].set_title('Компоненти власного вектора при $ \lambda_{70} $')
ax[2].legend()
ax[2].set_yscale('log')
ax[
fig.tight_layout(); plt.show()

На Рис. 8.3 видно, що компоненти власного вектора при найбільшому власному значень значно відхиляються від передбачення ТВМ, що вказує на значний ступінь впливовості індексу, який відповідає \(\lambda_{1}\). При \(\lambda_{30}\) та \(\lambda_{70}\) розподіл компонент власних векторів вихідних значень починає збігатися до розподілу випадкових значень, що говорить про незначний вплив індексів, яким відповідають ці власні значення. Оскільки розподіл компонент їх векторів близький до ТВМ, можна сказати, що вони вносять найбільший шум у динаміку ринку.
На Рис. 8.4 представлено ОВУ для вихідної матриці крос-кореляцій та випадкової.
8.2.3 Обернене відношення участі
def calc_ipr(u):
return np.sum(u**4, axis=0)
= calc_ipr(u)
IPR = calc_ipr(u_rand) IPR_rand
= plt.subplots(1, 1)
fig, ax
='green', label=r'$IPR_{init}$', s=10**2, marker='x')
ax.scatter(lambdas, IPR, color='red', label=r'$IPR_{rand}$', s=10**2, marker='x')
ax.scatter(lambdas_rand, IPR_rand, colorr'$\lambda_{i}$')
ax.set_xlabel(r'$IPR$')
ax.set_ylabel('log')
ax.set_xscale(
ax.legend()
fig.tight_layout(); plt.show()

На Рис. 8.4 видно, що ОВУ для, наприклад, випадкової матриці концентрується в межах \(\lambda_i \approx 10^0\). Для даних, що значно відхиляються від випадкових, ОВУ має довгі хвости, які виходять далеко за межі передбачень ТВМ. Для набільшого власного вектора ОВУ локалізується далеко в правому хвості розподілу. Також видно, що деякі власні значення мають \(IRP \approx 0.25\). Це говорить про те, що компоненти деяких векторів розподілені асиметрично, що може вказувати на деяких ступінь впливу фондових індексів, яким властиві дані вектори.
8.2.4 Коефіцієнт поглинання
Цікаво буде буде порівняти максимальне власне значення \(\lambda_{max}\) з середнім значенням коефіцієнта кореляції і так званим коефіцієнтом поглинання (absorption ratio, AR), який є кумулятивною мірою ризику:
\[ AR = \sum_{k=1}^{n}\lambda_k \bigg/ \sum_{k=1}^{N}\lambda_k. \tag{8.9}\]
\(AR\) вказує, яку частину загальної варіації описують \(n\) із загальної кількості \(N\) власних значень.
Щоб вирішити, який власний вектор можна відкинути для розрахунків у чисельнику формули вище, не втрачаючи занадто багато інформації, нам потрібно перевірити відповідні власні значення: власні вектори з найменшими власними значеннями є найменш інформативними, тому їх і можна відсікти.
Загальний підхід полягає в упорядкуванні власних значень від найвищого до найнижчого.
Після сортування власних значень постає питання: “яку кількість найбільш інформативних власних компонент треба вибрати для розрахунку коефіцієнту поглинання?”. Корисною мірою є так звана “врахована (пояснена) дисперсія”, яка може бути обчислена за власними значеннями. Пояснена дисперсія говорить нам, яку частину інформації (дисперсії) можна віднести до кожної із головних компонент.
Визначимо слідуючу функцію для коефіцієнту поглинання:
def ar(lambdas, proc):
# сортуємо власні значення у спадному порядку
= np.sort(lambdas)[::-1]
sorted_lambdas
# розраховуємо кумулятивну варіацію
= np.cumsum(sorted_lambdas) / np.sum(sorted_lambdas)
cumulative_variance
# знаходимо індекс, де кумулятивна варіація перетинає "proc"
= np.argmax(cumulative_variance >= proc) + 1
index_percent
# виділяємо верхні власні значення, які описують встановлений відсоток даних
= sorted_lambdas[:index_percent]
selected_lambdas
# повертаємо коефіцієнт поглинання та кількість значень, що пояснюють обраний відсоток варіації
return np.sum(selected_lambdas)/np.sum(sorted_lambdas), len(selected_lambdas)
Розглянемо, як варіюється коефіцієнт поглинання при різних значеннях поясненої варіації власними значеннями матриці кореляцій. На Рис. 8.5 представлено залежність коефіцієнта поглинання від різних значень поясненої варіації власними значеннями матриць кореляції вихідних прибутковостей та випадкових. Також представлено залежність кількості поясненої варіації від кількості власних значень, що пояснюють такий відсоток.
= np.linspace(0, 0.99, 100)
proc_variance = np.array([ar(lambdas, proc)[0] for proc in proc_variance])
ar_init = np.array([ar(lambdas_rand, proc)[0] for proc in proc_variance])
ar_rand
= np.array([ar(lambdas, proc)[1] for proc in proc_variance])
lambdas_init_cnt = np.array([ar(lambdas_rand, proc)[1] for proc in proc_variance])
lambdas_rand_cnt
= plt.subplots(1, 2, figsize=(8, 6))
fig, ax
0].plot(proc_variance, ar_init, color='green', label=r'$AR_{init}$')
ax[0].plot(proc_variance, ar_rand, color='red', label=r'$AR_{rand}$')
ax[0].set_xlabel('Відсоток варіації\n(a)')
ax[0].set_ylabel('$AR$')
ax[0].legend()
ax[
1].plot(proc_variance, lambdas_init_cnt, color='green', label=r'$\#\lambda_{i}^{init}$')
ax[1].plot(proc_variance, lambdas_rand_cnt, color='red', label=r'$\#\lambda_{i}^{rand}$')
ax[1].set_xlabel('Відсоток варіації\n(b)')
ax[1].set_ylabel(r'Кі-ть $\lambda_i$')
ax[1].legend()
ax[
fig.tight_layout(); plt.show()

Як ми можемо бачити на Рис. 8.5, коефіцієнт поглинання для вихідної матриці кореляцій залишається сталим при перших 40% варіації. На рисунку справа видно, що лише одного власного значення достатньо для опису майже половини всього ринку. Для випадкової матриці кількість власних значень необхідних для опису системи зростає прямо пропоційно відсотку варіації. Отже, для нашої задачі при розрахунку коефіцієнту поглинання достатньо буде взяти до 40% варіації при врахуванні кількості необхідних власних значень у чисельнику (8.9).
8.2.5 Віконна процедура
Перш ніж розпочинати розрахунки визначимо функцію для побудови графіків. Вона буде виводити один із часових рядів досліджуваної бази акцій компаній та розраховані індикатори. Ми будемо виводити як двовимірні, так і тривимірні графіки.
def plot_2d(time_ser_index,
time_ser_values,
y1_values,
time_ser_label,
y1_label,
x_label,
file_name, ="magenta"):
clr
= plt.subplots(1, 1)
fig, ax
= ax.twinx()
ax2
"axes", 1.03))
ax2.spines.right.set_position((
= ax.plot(time_ser_index,
p1,
time_ser_values, "b-",
=fr"{time_ser_label}")
label= ax2.plot(time_ser_index,
p2, =clr, label=y1_label)
y1_values, color
ax.set_xlabel(x_label)f"{y1_label}")
ax.set_ylabel(
ax.yaxis.label.set_color(p1.get_color())
ax2.yaxis.label.set_color(p2.get_color())
= dict(size=4, width=1.5)
tkw ='x', rotation=45, **tkw)
ax.tick_params(axis='y', colors=p1.get_color(), **tkw)
ax.tick_params(axis='y', colors=p2.get_color(), **tkw)
ax2.tick_params(axis=[p1, p2])
ax2.legend(handles
+ ".jpg")
plt.savefig(file_name
; plt.show()
= 250 # розмір вікна
window = 1 # крок вікна
tstep = 4 # вид ряду:
ret_type # 1 - вихідний,
# 2 - детрендований (різниця між теп. значенням та попереднім)
# 3 - прибутковості звичайні,
# 4 - стандартизовані прибутковості,
# 5 - абсолютні значення (волатильності)
# 6 - стандартизований ряд
= data.shape[1]
length = 50
num_bins
= []
mean_corr_init = []
mean_corr_rand
= []
C_vals_init = []
C_vals_rand
= []
C_hist_vals_init = []
C_hist_vals_rand
= []
u_hist_vals_init = []
u_hist_vals_rand
= []
lambdas_vals_init = []
lambdas_vals_rand
= []
lambdas_vals_max_init = []
lambdas_vals_max_rand
= []
lambdas_hist_vals_init = []
lambdas_hist_vals_rand
= []
ipr_vals_init = []
ipr_vals_rand
= []
absorb_vals_init = [] absorb_vals_rand
for i in tqdm(range(0,length-window,tstep)):
# відбираємо фрагменти
= data.iloc[:, i:i+window].copy()
fragm
# виконуємо процедуру трансформації ряду
= transformation(fragm, ret_type)
fragm = fragm[~np.isnan(fragm).any(axis=1)]
fragm
# генеруємо випадковий фрагмент
= np.random.normal(size=(fragm.shape[0], fragm.shape[1]))
fragm_random
# розраховуємо крос-кореляції
= calc_cross_corr(fragm)
C_fragm, ccoef_flat_fragm = calc_cross_corr(fragm_random)
C_fragm_random, ccoef_flat_fragm_random
# розраховуємо гістограму крос-кореляцій
= np.histogram(ccoef_flat_fragm, bins=num_bins, density=True)[0]
C_fragm_hist = np.histogram(ccoef_flat_fragm_random, bins=num_bins, density=True)[0]
C_fragm_hist_rand
# розрахунок середнього значення коефіцієнта кореляції
= np.mean(ccoef_flat_fragm)
mean_correlation = np.mean(ccoef_flat_fragm_random)
mean_correlation_random
# розрахунок власних значень та векторів
= calc_lambd_eig(C_fragm)
lambdas_fragm, u_fragm = calc_lambd_eig(C_fragm_random)
lambdas_fragm_random, u_fragm_random
# розраховуємо гістограму власних значень
= np.histogram(lambdas_fragm, bins=num_bins, density=True)[0]
lambdas_fragm_hist = np.histogram(u_fragm, bins=num_bins, density=True)[0]
u_fragm_hist_init = np.histogram(lambdas_fragm_random, bins=num_bins, density=True)[0]
lambdas_fragm_hist_rand = np.histogram(u_fragm_random, bins=num_bins, density=True)[0]
u_fragm_hist_rand
# отримання максимального власного значення
= lambdas_fragm.max()
lambdas_fragm_max = lambdas_fragm_random.max()
lambdas_fragm_max_random
# отримання оберненого відношення участі
= calc_ipr(u_fragm)
ipr_fragm = calc_ipr(u_fragm_random)
ipr_random
# розрахунок коефіцієнта поглинання
= ar(lambdas_fragm, 0.05)[0]
absorb_init = ar(lambdas_fragm_random, 0.05)[0]
absorb_rand
# додаємо індикатори до масивів
mean_corr_init.append(mean_correlation)
mean_corr_rand.append(mean_correlation_random)
C_vals_init.append(ccoef_flat_fragm)
C_vals_rand.append(ccoef_flat_fragm_random)
C_hist_vals_init.append(C_fragm_hist)
C_hist_vals_rand.append(C_fragm_hist_rand)
lambdas_hist_vals_init.append(lambdas_fragm_hist)
lambdas_hist_vals_rand.append(lambdas_fragm_hist_rand)
u_hist_vals_init.append(u_fragm_hist_init)
u_hist_vals_rand.append(u_fragm_hist_rand)
lambdas_vals_init.append(lambdas_fragm)
lambdas_vals_rand.append(lambdas_fragm_random)
lambdas_vals_max_init.append(lambdas_fragm_max)
lambdas_vals_max_rand.append(lambdas_fragm_max_random)
ipr_vals_init.append(ipr_fragm)
ipr_vals_rand.append(ipr_random)
absorb_vals_init.append(absorb_init) absorb_vals_rand.append(absorb_rand)
100%|██████████| 5268/5268 [04:04<00:00, 21.51it/s]
= ['avg_corr_init', 'lambda_max', 'ar']
ind_names
= [mean_corr_init, lambdas_vals_max_init, absorb_vals_init]
indicators
= [r'$\langle C \rangle_{init}$', r'$\lambda_{max}$', r'$AR$']
measure_labels
for i in range(len(ind_names)):
= f"RMT_{ind_names[i]}_symbol={ylabel}_wind={window}_step={tstep}_seriestype={ret_type}"
name + ".txt", indicators[i]) np.savetxt(name
8.2.5.1 Середній коефіцієнт крос-кореляцій
= f"avg_corr_symbol={ylabel}_wind={window}_step={tstep}_seriestype={ret_type}"
file_name
plot_2d(data_init.loc[:, ylabel].index[window:length:tstep],
data_init.loc[:, ylabel].values[window:length:tstep],
mean_corr_init,
ylabel,0],
measure_labels[
xlabel,
file_name,="magenta") clr

На Рис. 8.6 видно, що середній ринковий ступінь крос-кореляцій зростає у (перед-)кризові періоди, що вказує на зростання самоорганізованості ринку. Усі індекси синхронізовано починають “відповідати” на зовнішні чинники, які змушують трейдерів колективно все відкуповувати або розпродавати.
На Рис. 8.7 представлений той самий показник, але у тривимірному просторі.
= np.linspace(-1, 1, num_bins)
Y
= np.arange(window, length, tstep)
X = np.expand_dims(X, axis=1)
X = np.repeat(a=X, repeats=Y.shape[0], axis=1)
X
= np.array(C_hist_vals_init)
Z
= plt.subplots(subplot_kw={"projection": "3d"}, figsize=(8, 6))
fig, ax
= ax.plot_surface(X, Y, Z, cmap='hot', rstride=2, cstride=2, linewidth=0)
surf
=16, labelpad=15)
ax.set_xlabel(xlabel, fontsizer"$C_{ij}$", fontsize=16, labelpad=15)
ax.set_ylabel(r"$P(C)$", fontsize=16, labelpad=15)
ax.set_zlabel(='both', which='major', labelsize=16, pad=5)
ax.tick_params(axis
60, 140)
ax.view_init(
fig.tight_layout()
f"RMT_3D_P(C)_wind={window}_step={tstep}_seriestype={ret_type}.jpg", bbox_inches="tight")
plt.savefig(
; plt.show()

8.2.5.2 Максимальне значення \(\lambda\)
На наступному рисунку (Рис. 8.8) представлено порівняльну динаміку індексу цін акцій компанії Apple та показника \(\lambda_{max}\).
= f"lambda_max_symbol={ylabel}_wind={window}_step={tstep}_seriestype={ret_type}"
file_name
plot_2d(data_init.loc[:, ylabel].index[window:length:tstep],
data_init.loc[:, ylabel].values[window:length:tstep],
lambdas_vals_max_init,
ylabel,1],
measure_labels[
xlabel,
file_name,="red") clr

Рис. 8.8 показує, що \(\lambda_{max}\) у схожий спосіб із \(\langle C \rangle_{init}\). Із динаміки даного показника можна зробити висновок, що серед усіх індексів найбільш впливовим є індекс, якому характерно \(\lambda_{max}\). Усі інші індекси, власне значення яких знаходиться в межах теоретичного передбачення ТВМ, вносять лише шумову інформацію в загальну динаміку ринку і реагують на всі інші збурення на фондовому ринку, слідуючи закономірностям найбільш капіталізованих індексів.
8.2.5.3 Обернене відношення участі
На Рис. 8.9 представлено тривимірну віконну динаміка показника оберненого відношення участі
def log_tick_formatter(val, pos=None):
return f"$10^{{{int(val)}}}$"
= np.array(lambdas_vals_init)
Y
= np.arange(window, length, tstep)
X = np.expand_dims(X, axis=1)
X = np.repeat(a=X, repeats=Y.shape[1], axis=1)
X
= np.array(ipr_vals_init)
Z
= plt.subplots(subplot_kw={"projection": "3d"}, figsize=(8, 6))
fig, ax
= ax.plot_surface(X, np.log(Y), Z, cmap='magma', rstride=2, cstride=2, linewidth=0.5)
surf
=16, labelpad=15)
ax.set_xlabel(xlabel, fontsizer"$\lambda_i$", fontsize=16, labelpad=15)
ax.set_ylabel(r"$IPR$", fontsize=16, labelpad=15)
ax.set_zlabel(='both', which='major', labelsize=16, pad=5)
ax.tick_params(axis
ax.yaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))=True))
ax.yaxis.set_major_locator(mticker.MaxNLocator(integer
fig.tight_layout()
f"RMT_3D_IPR_wind={window}_step={tstep}_seriestype={ret_type}.jpg", bbox_inches="tight")
plt.savefig(
; plt.show()

ОВУ на Рис. 8.9 характеризується значним зростанням у кризові періоди, у той час як для періодів релаксації цей показник залишається на рівні нуля.
8.2.5.4 Коефіцієнт поглинання
На Рис. 8.10 представлено порівняльну динаміку індексу цін акцій компаній Apple та коефіцієнту поглинання.
= f"ar_symbol={ylabel}_wind={window}_step={tstep}_seriestype={ret_type}"
file_name
plot_2d(data_init.loc[:, ylabel].index[window:length:tstep],
data_init.loc[:, ylabel].values[window:length:tstep],
absorb_vals_init,
ylabel,2],
measure_labels[
xlabel,
file_name,="black") clr

На Рис. 8.10 видно, що \(AR\) зростає під час крахів 2008, 2015-2016, 2021 і 2023 року. Це говорить про те, що в кризові періоди зростає активність одного або декілька індексів, які стають рушієм для усього фондового ринку.
8.3 Висновок
Таким чином, при наявності сукупності часових рядів, що є даними діяльності економічних об’єктів однієї області, можна провести дослідження стосовно структури вказаної області та взаємодії об’єктів всередині неї. Дослідження проводяться на основі теорії випадкових матриць, що дозволяє отримувати інформацію шляхом аналізу матриці крос-кореляцій, побудованої для сукупної бази економічних об’єктів.
8.4 Завдання для самостійної роботи
Оберіть певний фондовий індекс і проведіть дослідження крахових подій для ринку, що він представляє за допомогою теорії випадкових матриць.
8.5 Контрольні запитання
- Поясніть основну ідею теорії випадкових матриць
- Про що свідчить відмінність кореляційних і спектральних властивостей матриці даних і випадкової?
- Дослідіть, як змінюється розподіл власних значень у випадках:
- \(\lambda_{-} < \lambda < \lambda_{+}\);
- \(\lambda > \lambda_{+}\);
- \(\lambda < \lambda_{-}\).
- Порівняйте кольорову карту поля взаємних кореляцій випадкової матриці і заданої. Зробіть висновки