Сравнение временных финансовых рядов: методы, метрики и примеры на Python

Сравнение временных рядов биржевых активов помогает выявить у кого больше доходность и риск, как они связаны между собой — движутся ли синхронно, один ли опережает другой, и можно ли использовать эту связь для хеджирования, арбитража или прогнозирования.

Понимание того, как правильно сравнивать финансовые временные ряды, может серьезно повлиять на качество торговых стратегий. При этом такой анализ временных рядов обладает рядом характеристик, которые делают его особенно сложным для анализа:

1. Нестационарность

Нестационарность — пожалуй, самая важная характеристика финансовых рядов. Статистические свойства этих рядов, такие как среднее значение и дисперсия, меняются со временем. Это создает существенные сложности при построении прогнозных моделей, так как большинство классических методов анализа временных рядов предполагают стационарность данных.

2. Высокая волатильность

Финансовые ряды часто демонстрируют периоды как низкой, так и высокой волатильности. Особенно заметна кластеризация волатильности, когда за периодами значительных колебаний следуют периоды относительного спокойствия. Это явление известно как «volatility clustering» и требует специальных подходов для моделирования.

3. Наличие «тяжелых хвостов» (heavy tails) в распределении доходностей

В отличие от нормального распределения, которое часто предполагается в классических финансовых теориях, реальные рыночные данные показывают более высокую вероятность экстремальных событий.

Распределение доходностей финансовых инструментов лучше описывается распределениями с «тяжелыми хвостами», такими как распределение Стьюдента или α-устойчивые распределения.

4. Асимметрия распределения доходностей

Доходности активов часто имеют асимметричное распределение, с более длинным хвостом в сторону отрицательных значений. Это отражает тот факт, что рыночные падения обычно происходят быстрее, чем рост.

5. Длинная память (long memory) или персистентность

Финансовые ряды демонстрируют долгосрочную зависимость, что может быть измерено с помощью показателя Херста. Это означает, что прошлые значения могут влиять на будущие значения даже с большим временным лагом.

Эти характеристики играют ключевую роль при выборе методов для сравнения финансовых временных рядов. Понимание этих особенностей помогает нам выбрать наиболее подходящие инструменты для анализа и избежать ловушек, связанных с применением неподходящих методов.

Подготовка данных для сравнения

Перед применением любых методов сравнения временных рядов необходимо провести тщательную предобработку и выравнивание данных. Качество этого этапа напрямую влияет на результаты всего анализа. В своей практике я последовательно выполняю следующие шаги:

Обработка пропущенных значений

Финансовые данные часто содержат пропуски из-за нерабочих дней, технических сбоев или низкой ликвидности инструментов. Существует несколько подходов к решению этой проблемы:

  • Исключение дней с пропущенными данными (может привести к потере важной информации);
  • Линейная или полиномиальная интерполяция;
  • Заполнение предыдущими значениями (backward fill);
  • Использование более сложных методов, таких как MICE (Multivariate Imputation by Chained Equations).

Обработка выбросов

Финансовые ряды часто содержат выбросы, которые могут представлять собой как ошибки данных, так и реальные экстремальные события. При сравнении рядов важно решить, как обрабатывать такие выбросы:

  • Винзоризация (замена экстремальных значений на определенные процентили);
  • Использование робастных метрик, нечувствительных к выбросам;
  • Логарифмическое преобразование для сжатия распределения.

Приведение к одинаковой частоте

Для корректного сравнения все анализируемые временные ряды должны иметь одинаковую частоту. Иногда требуется повышение (upsampling) или понижение (downsampling) частоты данных.

Нормализация

Перед сравнением финансовые ряды часто нормализуют для устранения масштабных эффектов. Наиболее распространенные методы:

  • Стандартизация (z-score);
  • Min-Max нормализация;
  • Нормализация по отношению к базовому периоду.

Преобразование в доходности

Анализ цен активов часто заменяется анализом их доходностей, которые обладают более удобными статистическими свойствами:

  • Простые доходности: (P₂ — P₁) / P₁;
  • Логарифмические доходности: ln(P₂/P₁).

Давайте рассмотрим пример предобработки финансовых данных для сравнения на Python:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import datetime
from alpha_vantage.timeseries import TimeSeries
from scipy import stats
from statsmodels.tsa.stattools import adfuller
import warnings

warnings.filterwarnings('ignore')

# Параметры
api_key = '_________' # Замените на свой ключ https://www.alphavantage.co/support/#api-key
start_date = datetime.datetime(2023, 5, 11)
end_date = datetime.datetime(2025, 5, 11)

# Инициализация API Alpha Vantage
ts = TimeSeries(key=api_key, output_format='pandas')

# Загрузка данных
data_msft, meta_msft = ts.get_daily(symbol='MSFT', outputsize='full')
data_aapl, meta_aapl = ts.get_daily(symbol='AAPL', outputsize='full')

# Преобразуем индексы в datetime и сортируем по возрастанию
data_msft.index = pd.to_datetime(data_msft.index)
data_aapl.index = pd.to_datetime(data_aapl.index)
data_msft = data_msft.sort_index()
data_aapl = data_aapl.sort_index()

# Выбираем только нужный диапазон дат
msft_filtered = data_msft[(data_msft.index >= start_date) & (data_msft.index <= end_date)] aapl_filtered = data_aapl[(data_aapl.index >= start_date) & (data_aapl.index <= end_date)]

# Смотрим только цены закрытия
msft_close = msft_filtered['4. close']
aapl_close = aapl_filtered['4. close']

# Обработка пропущенных значений методом Backward Fill
msft_close = msft_close.fillna(method='bfill')
aapl_close = aapl_close.fillna(method='bfill')

# Логарифмические доходности
msft_returns = np.log(msft_close / msft_close.shift(1)).dropna()
aapl_returns = np.log(aapl_close / aapl_close.shift(1)).dropna()

# Стандартизация (z-score) для сравнения
msft_returns_standardized = (msft_returns - msft_returns.mean()) / msft_returns.std()
aapl_returns_standardized = (aapl_returns - aapl_returns.mean()) / aapl_returns.std()

# Визуализация
plt.figure(figsize=(12, 6))

plt.subplot(2, 1, 1)
plt.title('Исходные цены закрытия')
plt.plot(msft_close / msft_close.iloc[0] * 100, label='MSFT (нормализованный)')
plt.plot(aapl_close / aapl_close.iloc[0] * 100, label='AAPL (нормализованный)')
plt.legend()

plt.subplot(2, 1, 2)
plt.title('Стандартизированные логарифмические доходности')
plt.plot(msft_returns_standardized, label='MSFT', alpha=0.7)
plt.plot(aapl_returns_standardized, label='AAPL', alpha=0.7)
plt.legend()
plt.tight_layout()
plt.show()

# Обнаружение выбросов методом Тьюки
def detect_outliers_iqr(data, threshold=1.5):
    q1 = data.quantile(0.25)
    q3 = data.quantile(0.75)
    iqr = q3 - q1
    lower_bound = q1 - threshold * iqr
    upper_bound = q3 + threshold * iqr
    return (data < lower_bound) | (data > upper_bound)

msft_outliers = detect_outliers_iqr(msft_returns)
aapl_outliers = detect_outliers_iqr(aapl_returns)

print(f"Количество выбросов в доходностях MSFT: {msft_outliers.sum()}")
print(f"Количество выбросов в доходностях AAPL: {aapl_outliers.sum()}")

# Тест на стационарность
def test_stationarity(series, name):
    result = adfuller(series)
    print(f"Тест на стационарность для {name}:")
    print(f"ADF Статистика: {result[0]}")
    print(f"p-значение: {result[1]}")
    if result[1] < 0.05:
        print("Ряд стационарен (отклоняем нулевую гипотезу)")
    else:
        print("Ряд нестационарен (не можем отклонить нулевую гипотезу)")
    print("-" * 40)

test_stationarity(msft_close, "MSFT цены")
test_stationarity(msft_returns, "MSFT доходности")
test_stationarity(aapl_close, "AAPL цены")
test_stationarity(aapl_returns, "AAPL доходности")

График нормализованных цен закрытия и логарифмических доходностей акций Microsoft и Apple за 2 года

Рис. 1: График нормализованных цен закрытия и логарифмических доходностей акций Microsoft и Apple за 2 года

Количество выбросов в доходностях MSFT: 19
Количество выбросов в доходностях AAPL: 27
Тест на стационарность для MSFT цены:
ADF Статистика: -1.9932006694681195
p-значение: 0.28956230351638645
Ряд нестационарен (не можем отклонить нулевую гипотезу)
----------------------------------------
Тест на стационарность для MSFT доходности:
ADF Статистика: -23.223594564757416
p-значение: 0.0
Ряд стационарен (отклоняем нулевую гипотезу)
----------------------------------------
Тест на стационарность для AAPL цены:
ADF Статистика: -1.8742306520827512
p-значение: 0.34427921708030523
Ряд нестационарен (не можем отклонить нулевую гипотезу)
----------------------------------------
Тест на стационарность для AAPL доходности:
ADF Статистика: -11.98243969752886
p-значение: 3.67808033449303e-22
Ряд стационарен (отклоняем нулевую гипотезу)

Этот код демонстрирует важные шаги предобработки финансовых данных:

  1. Загрузка данных с использованием API Alpha Vantage;
  2. Обработка пропущенных значений;
  3. Преобразование цен в логарифмические доходности;
  4. Стандартизация для корректного сравнения рядов разного масштаба;
  5. Визуализация исходных и преобразованных данных;
  6. Выявление выбросов с помощью метода межквартильного размаха (IQR);
  7. Проверка на стационарность с помощью расширенного теста Дики-Фуллера.

Только после такой комплексной предобработки мы можем переходить к применению методов сравнения временных рядов.

Основные метрики для сравнения финансовых временных рядов

После предобработки данных мы можем применить различные метрики для количественного сравнения финансовых временных рядов. Я выделяю несколько групп метрик, каждая из которых позволяет оценить сходство рядов с разных сторон.

Описательные статистики

Эти характеристики часто используются как отправная точка для более детального анализа и сравнения финансовых инструментов.

Описательные статистики предоставляют компактный набор числовых характеристик, описывающих центральную тенденцию, разброс и форму распределения временного ряда:

  • Среднее значение — характеризует центральную тенденцию ряда и интерпретируется как средняя доходность актива;
  • Медиана — значение, которое делит упорядоченный ряд пополам (более устойчива к выбросам, чем среднее);
  • Стандартное отклонение — основная мера разброса значений ряда (в финансах интерпретируется как мера волатильности актива);
  • Размах — разность между максимальным и минимальным значениями ряда;
  • Квартили и процентили — показывают распределение данных и позволяют оценить асимметрию;
  • Асимметрия (Skewness) — мера асимметрии распределения (например отрицательная асимметрия указывает на более длинный «хвост» в области отрицательных значений);
  • Эксцесс (Kurtosis) — мера «остроты пика» распределения; высокий эксцесс указывает на более тяжелые хвосты распределения, что типично для финансовых рядов.

Сравнение этих характеристик между различными активами позволяет выявить их статистические различия и сходства.

Примеры важных сравнений:

  • Активы с похожей средней доходностью, но разной волатильностью;
  • Активы с разными характеристиками асимметрии и эксцесса, что указывает на разную подверженность экстремальным событиям;
  • Сравнение медианы и среднего для выявления искажений, вызванных выбросами.

Для расчета описательных статистик двух временных рядов и визуализации их распределений можно использовать следующий код:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats

def compare_descriptive_stats(returns_dict):
    """
    Сравнение описательных статистик для нескольких временных рядов доходностей
    
    Параметры:
    returns_dict - словарь, где ключи - названия активов, значения - временные ряды доходностей
    """
    # Создаем DataFrame для сравнения
    stats_df = pd.DataFrame()
    
    for asset, returns in returns_dict.items():
        stats_df[asset] = [
            returns.mean(),                       # Среднее
            returns.median(),                     # Медиана
            returns.std(),                        # Стандартное отклонение
            returns.min(),                        # Минимум
            returns.max(),                        # Максимум
            returns.quantile(0.25),               # Первый квартиль
            returns.quantile(0.75),               # Третий квартиль
            stats.skew(returns),                  # Асимметрия
            stats.kurtosis(returns)               # Эксцесс
        ]
    
    stats_df.index = [
        'Среднее', 'Медиана', 'Стандартное отклонение', 
        'Минимум', 'Максимум', 'Первый квартиль', 'Третий квартиль',
        'Асимметрия', 'Эксцесс'
    ]
    
    return stats_df

# Применение функции к нашим данным
returns_dict = {
    'MSFT': msft_returns,
    'AAPL': aapl_returns
}

stats_comparison = compare_descriptive_stats(returns_dict)
print(stats_comparison)

# Визуализация распределений
plt.figure(figsize=(12, 6))
sns.histplot(msft_returns, kde=True, stat="density", label="MSFT")
sns.histplot(aapl_returns, kde=True, stat="density", label="AAPL")
plt.title('Распределение доходностей')
plt.legend()


# Объединяем данные в один DataFrame для визуализации
plot_data = pd.DataFrame({
    'MSFT': msft_returns,
    'AAPL': aapl_returns
})
plt.figure(figsize=(8, 6))
sns.boxplot(data=plot_data)
plt.title('Диаграмма размаха (Box Plot)')
plt.ylabel('Доходность')
plt.show()

plt.tight_layout()
plt.show()
                            MSFT       AAPL
Среднее                 0.000694   0.000267
Медиана                 0.001259   0.001511
Стандартное отклонение  0.014751   0.016888
Минимум                -0.063802  -0.097013
Максимум                0.096525   0.142617
Первый квартиль        -0.006988  -0.007051
Третий квартиль         0.009146   0.008569
Асимметрия              0.247717   0.467110
Эксцесс                 5.418618  13.168409

Диаграмма распределения наложенных друг на друга доходностей MSFT и AAPL

Рис. 2: Диаграмма распределения наложенных друг на друга доходностей MSFT и AAPL

Боксплоты доходностей акций Microsoft и Apple

Рис. 3: Боксплоты доходностей акций Microsoft и Apple

Метрики на основе расстояний

Метрики расстояний являются интуитивно понятным способом количественной оценки различий между временными рядами. В финансовом анализе наиболее часто применяются следующие:

Читайте также:  Вероятностные модели для прогнозирования цен биржевых активов

Евклидово расстояние

Это классическая мера расстояния, вычисляемая как корень из суммы квадратов разностей значений рядов в каждый момент времени. Хотя эта метрика проста в реализации, она имеет серьезные ограничения при работе с финансовыми рядами, так как очень чувствительна к шумам, выбросам и сдвигам по фазе.

Расстояние Манхэттена (Manhattan distance)

Рассчитывается как сумма абсолютных разностей между значениями рядов. Эта метрика менее чувствительна к выбросам, чем евклидово расстояние, но все еще требует точного совпадения временных точек.

Динамическое искажение времени (Dynamic Time Warping, DTW)

Более продвинутая метрика, которая находит оптимальное выравнивание между временными рядами, допуская растяжение и сжатие по временной оси.

DTW особенно полезен для финансовых рядов, где важные паттерны могут появляться со сдвигом во времени. Например, реакция различных активов на одно и то же глобальное событие может происходить с разной скоростью.

Расстояние Фреше (Frechet distance)

Мера сходства, учитывающая расположение и порядок точек в рядах. Можно представить это как минимальную длину поводка, необходимую для соединения собаки и хозяина, идущих по разным путям, при этом оба могут регулировать скорость, но не могут двигаться назад.

Расстояние редактирования (Edit distance)

Расстояние редактирования и его вариации, такие как расстояние Левенштейна, применяются к дискретизированным финансовым рядам для измерения минимального количества операций (вставок, удалений и замен), необходимых для преобразования одного ряда в другой.

Давайте рассмотрим пример реализации нескольких метрик расстояния на Python:

!pip install fastdtw
from scipy.spatial.distance import euclidean, cityblock
from fastdtw import fastdtw
import numpy as np
from dtw import dtw
import matplotlib.pyplot as plt

# Функция для расчета различных метрик расстояния
def calculate_distance_metrics(series1, series2):
    # Убедимся, что ряды имеют одинаковую длину и извлечем 1D массивы
    min_length = min(len(series1), len(series2))
    s1 = np.array(series1[-min_length:]).flatten()  
    s2 = np.array(series2[-min_length:]).flatten() 
    
    # Проверим размерности
    print(f"Shape of s1: {s1.shape}, Shape of s2: {s2.shape}") 
    
    # Евклидово расстояние
    euclidean_dist = euclidean(s1, s2)
    
    # Расстояние Манхэттена
    manhattan_dist = cityblock(s1, s2)
    
    # Динамическое искажение времени (DTW)
    dtw_dist, _ = fastdtw(s1.reshape(-1, 1), s2.reshape(-1, 1), dist=euclidean)
    
    # Нормализованные версии
    norm_euclidean = euclidean_dist / np.sqrt(min_length)
    norm_manhattan = manhattan_dist / min_length
    norm_dtw = dtw_dist / min_length
    
    return {
        'euclidean': euclidean_dist,
        'manhattan': manhattan_dist,
        'dtw': dtw_dist,
        'norm_euclidean': norm_euclidean,
        'norm_manhattan': norm_manhattan,
        'norm_dtw': norm_dtw
    }

# Рассчитаем метрики для наших стандартизированных доходностей
msft_values = msft_returns_standardized['4. close'].values.flatten()
aapl_values = aapl_returns_standardized['4. close'].values.flatten()

distance_metrics = calculate_distance_metrics(msft_values, aapl_values)

# Выведем результаты
for metric, value in distance_metrics.items():
    print(f"{metric}: {value:.4f}")

# Визуализация DTW-выравнивания
window_size = 100
s1 = msft_values[-window_size:]
s2 = aapl_values[-window_size:]

# Для DTW визуализации используем fastdtw
dtw_distance, dtw_path = fastdtw(s1.reshape(-1, 1), s2.reshape(-1, 1), dist=euclidean)

plt.figure(figsize=(12, 8))

# График временных рядов
plt.subplot(2, 1, 1)
plt.plot(s1, label='MSFT')
plt.plot(s2, label='AAPL')
plt.title('Стандартизированные доходности (последние 100 дней)')
plt.legend()

# График выравнивания DTW
plt.subplot(2, 1, 2)
for i, j in dtw_path:
    plt.plot([i, j], [s1[i], s2[j]], 'k-', alpha=0.1)
plt.plot(s1, 'bo-', label='MSFT')
plt.plot(s2, 'go-', label='AAPL')
plt.title('Выравнивание DTW')
plt.legend()

plt.tight_layout()
plt.show()
Shape of s1: (500,), Shape of s2: (500,)
euclidean: 21.4953
manhattan: 347.7026
dtw: 373.7776
norm_euclidean: 0.9613
norm_manhattan: 0.6954
norm_dtw: 0.7476

Визуализация расстояний между временным рядами методом оптимального выравнивания по времени (DTW)

Рис. 4: Визуализация расстояний между временным рядами методом оптимального выравнивания по времени (DTW)

Этот код демонстрирует расчет различных метрик расстояния между финансовыми временными рядами и визуализирует выравнивание по методу Dynamic Time Warping. DTW особенно полезен для сравнения финансовых рядов, так как позволяет учесть различия в скорости реакции активов на рыночные события.

Корреляционные метрики

Корреляционные метрики играют центральную роль в финансовом анализе, особенно при построении диверсифицированных портфелей и стратегий парного трейдинга. В отличие от метрик расстояния, они фокусируются на измерении линейных и нелинейных зависимостей между рядами.

Коэффициент корреляции Пирсона

Это классическая мера линейной зависимости между двумя переменными. Несмотря на популярность, коэффициент Пирсона имеет существенные ограничения для финансовых рядов:

  • Чувствителен к выбросам;
  • Предполагает линейную связь между переменными;
  • Предполагает нормальное распределение данных;
  • Не улавливает нелинейные зависимости.

Ранговая корреляция Спирмена

Данный метод корреляций оценивает монотонную зависимость между рядами, используя ранги вместо абсолютных значений. Она более устойчива к выбросам и не требует предположений о линейности связи или нормальности распределений.

Коэффициент корреляции Кендалла (tau)

Еще одна ранговая корреляция, основанная на согласованности рангов пар наблюдений в двух рядах. Она особенно полезна для выявления структурных зависимостей и менее чувствительна к ошибкам данных, чем корреляция Спирмена.

Взаимная информация (Mutual Information)

Мера нелинейной зависимости, основанная на теории информации. В отличие от корреляционных коэффициентов, взаимная информация может выявлять сложные нелинейные зависимости между финансовыми рядами.

Коэффициент конкордации Кендалла

Данный метод является обобщением ранговой корреляции Кендалла на случай более двух временных рядов, что позволяет оценить общую согласованность движений группы активов.

Динамические корреляции

В финансовом анализе особенно важно учитывать изменения корреляций во времени. Используются такие методы, как:

  • Скользящая корреляция (Rolling Correlation);
  • Многомерные GARCH модели (DCC-GARCH, BEKK-GARCH);
  • Копула-функции.

Реализуем некоторые из этих метрик на Python:

import pandas as pd
import numpy as np
from scipy import stats
from sklearn.metrics import mutual_info_score
import matplotlib.pyplot as plt
import seaborn as sns

# Рассчитаем основные корреляционные метрики
def calculate_correlation_metrics(series1, series2):
    # Коэффициент корреляции Пирсона
    pearson_corr, pearson_p = stats.pearsonr(series1, series2)
    
    # Ранговая корреляция Спирмена
    spearman_corr, spearman_p = stats.spearmanr(series1, series2)
    
    # Коэффициент корреляции Кендалла
    kendall_corr, kendall_p = stats.kendalltau(series1, series2)
    
    # Взаимная информация
    # Для расчета взаимной информации дискретизируем данные
    bins = 10
    c_xy = np.histogram2d(series1, series2, bins)[0]
    mi = mutual_info_score(None, None, contingency=c_xy)
    
    # Нормализованная взаимная информация
    c_x = np.histogram(series1, bins)[0]
    c_y = np.histogram(series2, bins)[0]
    h_x = stats.entropy(c_x)  # Энтропия X
    h_y = stats.entropy(c_y)  # Энтропия Y
    if h_x + h_y > 0:  # Избегаем деления на ноль
        nmi = 2.0 * mi / (h_x + h_y)
    else:
        nmi = 0.0
    
    return {
        'pearson': (pearson_corr, pearson_p),
        'spearman': (spearman_corr, spearman_p),
        'kendall': (kendall_corr, kendall_p),
        'mutual_info': mi,
        'normalized_mutual_info': nmi
    }

# Рассчитаем корреляционные метрики для наших доходностей
correlation_metrics = calculate_correlation_metrics(
    msft_returns.values, 
    aapl_returns.values
)

# Выведем результаты
for metric, value in correlation_metrics.items():
    if metric in ['mutual_info', 'normalized_mutual_info']:
        print(f"{metric}: {value:.4f}")
    else:
        corr, p_value = value
        print(f"{metric}: корреляция = {corr:.4f}, p-значение = {p_value:.4f}")

# Рассчитаем и визуализируем скользящую корреляцию
def plot_rolling_correlation(returns1, returns2, windows=[30, 60, 90]):
    plt.figure(figsize=(12, 6))
    
    for window in windows:
        rolling_corr = returns1.rolling(window=window).corr(returns2)
        plt.plot(rolling_corr, label=f'Окно {window} дней')
    
    plt.axhline(y=0, color='r', linestyle='--', alpha=0.3)
    plt.axhline(y=correlation_metrics['pearson'][0], color='k', linestyle='-', 
                alpha=0.3, label='Общая корреляция')
    plt.title('Скользящая корреляция между MSFT и AAPL')
    plt.ylim(-1, 1)
    plt.legend()
    plt.show()

plot_rolling_correlation(msft_returns, aapl_returns)
pearson: корреляция = 0.5370, p-значение = 0.0000
spearman: корреляция = 0.5053, p-значение = 0.0000
kendall: корреляция = 0.3564, p-значение = 0.0000
mutual_info: 0.1548
normalized_mutual_info: 0.1312

График скользящей корреляции между котировками акций Microsoft и Apple

Рис. 5: График скользящей корреляции между котировками акций Microsoft и Apple

Этот код демонстрирует расчет различных корреляционных метрик и их визуализацию, включая классические корреляции (Пирсона, Спирмена, Кендалла), меры нелинейной зависимости (mutual_info — взаимная информация), динамическую корреляцию с использованием скользящих окон.

Понимание динамики корреляций между активами является ключевым для построения эффективных торговых стратегий и управления рисками портфеля.

Метрики на основе частотного анализа

Частотный анализ предоставляет мощный инструментарий для сравнения финансовых временных рядов, позволяя выявить сходства и различия в частотных характеристиках активов. Эти методы особенно полезны для выявления циклических паттернов и скрытых зависимостей.

Читайте также:  Волатильность акций, облигаций, деривативов. Как ее посчитать?

Спектральная плотность мощности

Данный метод позволяет оценить распределение вариации временного ряда по различным частотам. Сравнение спектральных плотностей двух финансовых рядов может выявить сходства в их циклической структуре.

Когерентность

Это мера линейной зависимости между двумя временными рядами на различных частотах. Высокая когерентность на определенной частоте указывает на сильную линейную связь между рядами на этой частоте, даже если общая корреляция низкая.

Кросс-спектральный анализ

Кросс-спектральный анализ позволяет оценить фазовые сдвиги между двумя временными рядами на различных частотах. Это особенно полезно для выявления запаздывающих или опережающих отношений между активами.

Вейвлет-анализ

Мощный метод, позволяющий проанализировать временной ряд одновременно во временной и частотной областях. Вейвлет-когерентность является мерой локальной корреляции между двумя временными рядами в пространстве время-частота.

Вейвлет-корреляция

Вейвлет-корреляция позволяет оценить корреляцию между двумя временными рядами на разных временных масштабах. Это особенно полезно для финансовых рынков, где корреляции часто зависят от временного горизонта (краткосрочного, среднесрочного, долгосрочного).

Давайте реализуем некоторые из этих методов на Python:

!pip install pycwt
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import signal
import pywt
import pycwt as wavelet

# Функция для оценки и сравнения спектральной плотности мощности
def compare_spectral_density(series1, series2, fs=1.0, nperseg=None):
    """
    Сравнивает спектральную плотность мощности двух временных рядов
    
    Параметры:
    series1, series2 - временные ряды для сравнения
    fs - частота дискретизации (для финансовых данных обычно 1/день для дневных данных)
    nperseg - длина сегмента для оценки спектра (по умолчанию используется значение scipy)
    """
    if nperseg is None:
        nperseg = min(256, min(len(series1), len(series2)) // 4)
    
    # Рассчитываем периодограммы Уэлча для обоих рядов
    f1, psd1 = signal.welch(series1, fs=fs, nperseg=nperseg, detrend='constant')
    f2, psd2 = signal.welch(series2, fs=fs, nperseg=nperseg, detrend='constant')
    
    # Вычисляем когерентность
    f, coh = signal.coherence(series1, series2, fs=fs, nperseg=nperseg)
    
    # Визуализируем результаты
    plt.figure(figsize=(12, 10))
    
    # Спектральная плотность мощности
    plt.subplot(3, 1, 1)
    plt.semilogy(f1, psd1, label='MSFT')
    plt.semilogy(f2, psd2, label='AAPL')
    plt.xlabel('Частота')
    plt.ylabel('PSD [V**2/Гц]')
    plt.title('Спектральная плотность мощности')
    plt.grid(True)
    plt.legend()
    
    # Когерентность
    plt.subplot(3, 1, 2)
    plt.plot(f, coh)
    plt.axhline(y=0.5, color='r', linestyle='--')
    plt.ylabel('Величина когерентности')
    plt.xlabel('Частота')
    plt.title('Когерентность между MSFT и AAPL')
    plt.grid(True)
    
    # Кросс-спектральная плотность
    f, Pxy = signal.csd(series1, series2, fs=fs, nperseg=nperseg)
    plt.subplot(3, 1, 3)
    plt.plot(f, np.abs(Pxy))
    plt.xlabel('Частота')
    plt.ylabel('Кросс-спектральная плотность')
    plt.title('Кросс-спектральная плотность MSFT и AAPL')
    plt.grid(True)
    
    plt.tight_layout()
    plt.show()

# Функция для вейвлет-анализа
def wavelet_analysis(series1, series2, title1='MSFT', title2='AAPL'):
    """
    Выполняет вейвлет-анализ и визуализирует вейвлет-когерентность двух временных рядов
    """
    # Подготовка данных
    t = np.arange(len(series1))
    dt = 1  # Шаг времени (1 для дневных данных)
    
    # Стандартизация данных
    s1_norm = (series1 - np.mean(series1)) / np.std(series1)
    s2_norm = (series2 - np.mean(series2)) / np.std(series2)
    
    # Вейвлет-преобразование для первого ряда
    mother = wavelet.Morlet(6)  # Материнский вейвлет Морле с параметром 6
    s1_wt, scales1, freqs1, coi1, _, _ = wavelet.cwt(s1_norm, dt, dj=1/12, s0=2*dt, J=7/dj, wavelet=mother)
    power1 = (np.abs(s1_wt)) ** 2
    
    # Вейвлет-преобразование для второго ряда
    s2_wt, scales2, freqs2, coi2, _, _ = wavelet.cwt(s2_norm, dt, dj=1/12, s0=2*dt, J=7/dj, wavelet=mother)
    power2 = (np.abs(s2_wt)) ** 2
    
    # Вейвлет-когерентность
    wct, aWCT, corr_coi, freq, sig = wavelet.wct(s1_norm, s2_norm, dt, dj=1/12, s0=2*dt, J=7/dj, 
                                          wavelet='morlet', sig=False)
    
    # Визуализация результатов
    plt.figure(figsize=(12, 10))
    
    # Вейвлет-спектр первого ряда
    ax1 = plt.subplot(3, 1, 1)
    levels = np.linspace(0, np.max(power1), 100)
    im = plt.contourf(t, np.log2(scales1), power1, levels=levels, cmap='viridis')
    plt.colorbar(im, orientation='vertical', label='Power')
    plt.title(f'Вейвлет-спектр {title1}')
    plt.ylabel('Период (дни)')
    
    # Вейвлет-спектр второго ряда
    ax2 = plt.subplot(3, 1, 2)
    levels = np.linspace(0, np.max(power2), 100)
    im = plt.contourf(t, np.log2(scales2), power2, levels=levels, cmap='viridis')
    plt.colorbar(im, orientation='vertical', label='Power')
    plt.title(f'Вейвлет-спектр {title2}')
    plt.ylabel('Период (дни)')
    
    # Вейвлет-когерентность
    ax3 = plt.subplot(3, 1, 3)
    levels = np.linspace(0, 1, 100)
    im = plt.contourf(t, np.log2(scales1), np.abs(wct), levels=levels, cmap='viridis')
    plt.colorbar(im, orientation='vertical', label='WCT')
    plt.title(f'Вейвлет-когерентность между {title1} и {title2}')
    plt.ylabel('Период (дни)')
    plt.xlabel('Время (дни)')
    
    plt.tight_layout()
    plt.show()
    
    return wct, scales1

# Применим функции к нашим данным
compare_spectral_density(msft_returns.values, aapl_returns.values)

Анализ спектральной плотности мощности, кросс-спектральной плотности и когерентности между MSFT и AAPL

Рис. 6: Анализ спектральной плотности мощности, кросс-спектральной плотности и когерентности между MSFT и AAPL

Этот код демонстрирует применение методов частотного анализа для сравнения финансовых временных рядов:

  1. Расчет и визуализация спектральной плотности мощности для двух активов;
  2. Оценка когерентности между временными рядами на различных частотах;
  3. Расчет кросс-спектральной плотности;
  4. Проведение вейвлет-анализа и визуализация вейвлет-когерентности.

Частотный анализ позволяет выявить скрытые закономерности в финансовых рядах, которые не видны при традиционном анализе корреляций. Например, два актива могут иметь низкую общую корреляцию, но высокую когерентность на определенных частотах, что может быть использовано в торговых стратегиях.

Сравнение сезонных и календарных эффектов

Финансовые временные ряды часто содержат сезонные и календарные эффекты, которые могут быть важными для сравнения поведения различных активов. Выявление и анализ этих эффектов позволяет лучше понять периодичность в доходностях и волатильности.

Внутринедельная сезонность

Доходности финансовых активов часто демонстрируют зависимость от дня недели («эффект дня недели», «эффект понедельника»). Сравнение этих эффектов для разных активов может выявить различия в их реакции на торговую активность в разные дни недели.

Внутримесячная сезонность

Многие финансовые инструменты демонстрируют эффекты, связанные с определенными днями месяца (например, эффект начала/конца месяца), что может быть связано с датами выплаты зарплат, датами экспирации опционов и фьючерсов, публикацией макроэкономической статистики.

Сезонные эффекты в течение года

Некоторые активы демонстрируют сезонные колебания в течение года, которые могут быть связаны с бизнес-циклом, сезонностью спроса на товары и услуги, фискальным годом компаний и т.д.

Для анализа этих эффектов можно использовать следующий код:

def analyze_calendar_effects(returns_dict):
    """
    Анализ календарных эффектов для нескольких временных рядов доходностей
    """
    results = {}
    
    for asset, returns in returns_dict.items():
        # Создаем DataFrame с доходностями и дополнительными временными признаками
        df = pd.DataFrame({'returns': returns})
        df['day_of_week'] = df.index.dayofweek
        df['month'] = df.index.month
        df['day_of_month'] = df.index.day
        df['week_of_year'] = df.index.isocalendar().week
        
        # Анализ эффекта дня недели
        day_of_week_effect = df.groupby('day_of_week')['returns'].agg(['mean', 'std', 'count'])
        day_of_week_effect.index = ['Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница']
        
        # Анализ внутримесячного эффекта
        # Группируем дни месяца на начало (1-10), середину (11-20) и конец (21-31)
        df['month_part'] = pd.cut(df['day_of_month'], bins=[0, 10, 20, 31], 
                                 labels=['Начало месяца', 'Середина месяца', 'Конец месяца'])
        month_part_effect = df.groupby('month_part')['returns'].agg(['mean', 'std', 'count'])
        
        # Анализ месячного эффекта
        month_effect = df.groupby('month')['returns'].agg(['mean', 'std', 'count'])
        month_effect.index = ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 
                             'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь']
        
        results[asset] = {
            'day_of_week': day_of_week_effect,
            'month_part': month_part_effect,
            'month': month_effect
        }
    
    return results

# Применение функции к нашим данным
returns_dict = {
    'MSFT': msft_returns,
    'AAPL': aapl_returns
}

# Запускаем анализ
calendar_effects = analyze_calendar_effects(returns_dict)

# Визуализация эффекта дня недели
plt.figure(figsize=(14, 8))

# Средняя доходность по дням недели
plt.subplot(2, 1, 1)
for asset, data in calendar_effects.items():
    plt.bar(data['day_of_week'].index + [f' ({asset})' for _ in data['day_of_week'].index], 
            data['day_of_week']['mean'], 
            label=asset, alpha=0.7)
plt.title('Средняя доходность по дням недели')
plt.ylabel('Средняя доходность')
plt.grid(axis='y', linestyle='--', alpha=0.7)

# Волатильность по дням недели
plt.subplot(2, 1, 2)
for asset, data in calendar_effects.items():
    plt.bar(data['day_of_week'].index + [f' ({asset})' for _ in data['day_of_week'].index], 
            data['day_of_week']['std'], 
            label=asset, alpha=0.7)
plt.title('Волатильность по дням недели')
plt.ylabel('Стандартное отклонение')
plt.legend()
plt.grid(axis='y', linestyle='--', alpha=0.7)

plt.tight_layout()
plt.show()

# Визуализация месячного эффекта
plt.figure(figsize=(14, 8))

# Средняя доходность по месяцам
plt.subplot(2, 1, 1)
for asset, data in calendar_effects.items():
    plt.plot(data['month'].index, data['month']['mean'], marker='o', label=asset)
plt.title('Средняя доходность по месяцам')
plt.ylabel('Средняя доходность')
plt.grid(True, linestyle='--', alpha=0.7)

# Волатильность по месяцам
plt.subplot(2, 1, 2)
for asset, data in calendar_effects.items():
    plt.plot(data['month'].index, data['month']['std'], marker='o', label=asset)
plt.title('Волатильность по месяцам')
plt.ylabel('Стандартное отклонение')
plt.legend()
plt.grid(True, linestyle='--', alpha=0.7)

plt.tight_layout()
plt.show()

Графики сравнения средней доходности и волатильности акций MSFT и AAPL по дням недели

Рис. 7: Графики сравнения средней доходности и волатильности акций MSFT и AAPL по дням недели

Графики сравнения средней доходности и волатильности акций Microsoft и Apple по месяцам

Рис. 8: Графики сравнения средней доходности и волатильности акций Microsoft и Apple по месяцам

Данные методы анализа предоставляют основные инструменты для сравнения финансовых временных рядов. Они помогают выявить ключевые свойства рядов, что позволит далее принять решение о выборе этих активов для торговой стратегий и выборе подходящих моделей для прогнозирования.

Продвинутые методы сравнения временных финансовых рядов

В профессиональной среде (в инвестбанках и хедж-фондах) используются более сложные и комплексные методы для сравнения временных рядов. Эти методы позволяют выявить более глубокие зависимости и эффективнее использовать их для построения торговых стратегий.

Копулы для моделирования многомерных зависимостей

Копулы представляют собой мощный инструмент для моделирования многомерных зависимостей между финансовыми активами, особенно в условиях нестабильности рынка. Их главное преимущество заключается в возможности отделить маргинальные распределения переменных от структуры их зависимости.

  • Гауссова копула — самая простая форма копулы, основанная на многомерном нормальном распределении. Однако она не способна адекватно моделировать зависимость в хвостах распределений, что критично для оценки риска экстремальных событий;
  • t-копула — расширение гауссовой копулы, основанное на многомерном t-распределении. Она лучше моделирует зависимость в хвостах, что делает ее более подходящей для финансовых данных;
  • Архимедовы копулы (копула Клейтона, копула Гумбеля, копула Франка) — семейство копул, которые могут моделировать асимметричные зависимости, часто встречающиеся на финансовых рынках. Например, копула Клейтона хорошо подходит для моделирования нижней хвостовой зависимости, что важно для анализа рисков в периоды рыночных падений;
  • Лоза-копулы (Vine copulas) — гибкие структуры, позволяющие моделировать комплексные многомерные зависимости путем разложения на попарные копулы. Они особенно полезны при работе с большим количеством активов.
Читайте также:  Изучаем опционы на Netflix: комплексный анализ и стратегии

Давайте посмотрим как можно построить копула-модели на Python:

!pip install copulas
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
import seaborn as sns
from copulas.multivariate import GaussianMultivariate
from copulas.univariate import GaussianUnivariate

# Подготовка данных
returns_df = pd.DataFrame({
    'MSFT': msft_returns.values,
    'AAPL': aapl_returns.values
})

# Визуализация исходных данных
plt.figure(figsize=(10, 8))
sns.jointplot(data=returns_df, x='MSFT', y='AAPL', kind='scatter')
plt.suptitle('Совместное распределение доходностей MSFT и AAPL', y=1.05)
plt.tight_layout()
plt.show()

# Обучение гауссовой копулы
def fit_gaussian_copula(data):
    # Подгоняем одномерные распределения
    univariate_models = {}
    uniform_data = pd.DataFrame()
    
    for column in data.columns:
        model = GaussianUnivariate()
        model.fit(data[column])
        univariate_models[column] = model
        uniform_data[column] = model.cdf(data[column])
    
    # Визуализируем преобразованные в равномерные данные
    plt.figure(figsize=(10, 8))
    sns.jointplot(data=uniform_data, x=uniform_data.columns[0], y=uniform_data.columns[1], 
                  kind='scatter')
    plt.suptitle('Данные в пространстве копулы (после PIT)', y=1.05)
    plt.tight_layout()
    plt.show()
    
    # Рассчитываем корреляцию в пространстве копулы
    correlation = uniform_data.corr().values
    
    # Генерируем выборку из копулы
    size = len(data)
    normal_data = np.random.multivariate_normal(
        mean=[0, 0],
        cov=correlation,
        size=size
    )
    
    # Преобразуем в равномерные на [0, 1]
    uniform_samples = pd.DataFrame()
    for i in range(normal_data.shape[1]):
        uniform_samples[data.columns[i]] = stats.norm.cdf(normal_data[:, i])
    
    # Преобразуем обратно в исходное пространство
    samples = pd.DataFrame()
    for column in data.columns:
        samples[column] = univariate_models[column].ppf(uniform_samples[column])
    
    # Визуализируем сгенерированные данные
    plt.figure(figsize=(10, 8))
    sns.jointplot(data=samples, x=samples.columns[0], y=samples.columns[1], 
                  kind='scatter', alpha=0.5)
    plt.suptitle('Сгенерированные данные из гауссовой копулы', y=1.05)
    plt.tight_layout()
    plt.show()
    
    return univariate_models, correlation, samples

# Выполняем моделирование с помощью гауссовой копулы
univariate_models, correlation, samples = fit_gaussian_copula(returns_df)

# Расчет зависимости в хвостах распределения (tail dependence)
def calculate_tail_dependence(data, q=0.05):
    """
    Рассчитывает коэффициенты хвостовой зависимости для пары временных рядов
    """
    x = data.iloc[:, 0]
    y = data.iloc[:, 1]
    
    # Квантили для определения хвостов
    q_x_lower = np.quantile(x, q)
    q_x_upper = np.quantile(x, 1-q)
    q_y_lower = np.quantile(y, q)
    q_y_upper = np.quantile(y, 1-q)
    
    # Нижняя хвостовая зависимость: P(Y ≤ q_y_lower | X ≤ q_x_lower)
    lower_tail = np.mean((y <= q_y_lower) & (x <= q_x_lower)) / np.mean(x <= q_x_lower) # Верхняя хвостовая зависимость: P(Y > q_y_upper | X > q_x_upper)
    upper_tail = np.mean((y > q_y_upper) & (x > q_x_upper)) / np.mean(x > q_x_upper)
    
    return lower_tail, upper_tail

# Рассчитываем хвостовую зависимость для исходных данных и копулы
lower_tail_orig, upper_tail_orig = calculate_tail_dependence(returns_df)
lower_tail_sim, upper_tail_sim = calculate_tail_dependence(samples)

print(f"Оригинальные данные - нижняя хвостовая зависимость: {lower_tail_orig:.4f}")
print(f"Оригинальные данные - верхняя хвостовая зависимость: {upper_tail_orig:.4f}")
print(f"Гауссова копула - нижняя хвостовая зависимость: {lower_tail_sim:.4f}")
print(f"Гауссова копула - верхняя хвостовая зависимость: {upper_tail_sim:.4f}")

Диаграмма рассеяния совместного распределения доходностей MSFT и AAPL

Рис. 5: Диаграмма рассеяния совместного распределения доходностей MSFT и AAPL

Визуализация данных в пространстве копулы (после PIT)

Рис. 6: Визуализация данных в пространстве копулы (после PIT)

Визуализация сгенерированных данных из гауссовой копулы

Рис. 9: Визуализация сгенерированных данных из гауссовой копулы

Оригинальные данные - нижняя хвостовая зависимость: 0.2800
Оригинальные данные - верхняя хвостовая зависимость: 0.2400
Гауссова копула - нижняя хвостовая зависимость: 0.3600
Гауссова копула - верхняя хвостовая зависимость: 0.2400

Этот код демонстрирует использование гауссовой копулы для моделирования многомерных зависимостей между финансовыми временными рядами, а также расчет коэффициентов хвостовой зависимости. Копула-подход позволяет точнее оценивать риски экстремальных событий и строить более надежные торговые стратегии.

Коинтеграция и модели коррекции ошибок

Коинтеграция является одним из фундаментальных концепций в анализе финансовых временных рядов и лежит в основе многих стратегий статистического арбитража.

Два нестационарных временных ряда считаются коинтегрированными, если существует их линейная комбинация, которая является стационарной. Для этого используют несколько тестов и моделей:

  • Тест Энгла-Грейнджера — двухэтапный подход для тестирования коинтеграции: на 1-м этапе оценивается долгосрочное равновесное соотношение между переменными с помощью регрессии, на 2-м этапе проверяется стационарность остатков регрессии с помощью теста на единичный корень;
  • Тест Йохансена — более мощный метод для выявления коинтеграционных соотношений, особенно в многомерном случае. Он позволяет определить количество коинтеграционных векторов и оценить их параметры;
  • Модель коррекции ошибок (Error Correction Model, ECM) — динамическая модель, описывающая краткосрочную динамику переменных с учетом их долгосрочного равновесного соотношения. ECM показывает, как быстро система возвращается к равновесию после шока;
  • Векторная модель коррекции ошибок (Vector Error Correction Model, VECM) — обобщение ECM на многомерный случай, позволяющее моделировать взаимосвязи между несколькими коинтегрированными временными рядами.

Реализуем анализ коинтеграции на Python:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.api as sm
from statsmodels.tsa.stattools import coint, adfuller

def test_cointegration(y1, y2, plot=True):

    # Тест коинтеграции
    score, pvalue, _ = coint(y1, y2)
    
    # Линейная регрессия для построения спреда
    X = sm.add_constant(y1)
    model = sm.OLS(y2, X).fit()
    spread = y2 - (model.params[0] + model.params[1] * y1)
    
    if plot:
        # Визуализация результатов
        fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))
        
        # График цен
        ax1.set_title('Цены закрытия MSFT и AAPL')
        ax1.plot(y1, label='MSFT')
        ax1.plot(y2, label='AAPL')
        ax1.legend()
        
        # График спреда
        ax2.set_title(f'Спред: AAPL - ({model.params[1]:.4f}*MSFT + {model.params[0]:.4f})')
        ax2.plot(spread, label='Спред')
        ax2.axhline(spread.mean(), color='r', linestyle='--', label='Среднее')
        ax2.legend()
        
        plt.tight_layout()
        plt.show()
    
    # Результаты теста ADF для спреда
    adf_result = adfuller(spread)
    
    # Вывод результатов
    print("\nРезультаты теста коинтеграции MSFT-AAPL:")
    print(f"Статистика Энгла-Грейнджера: {score:.4f}")
    print(f"p-значение: {pvalue:.4f}")
    print("\nРезультаты ADF теста для спреда:")
    print(f"ADF статистика: {adf_result[0]:.4f}")
    print(f"p-значение: {adf_result[1]:.4f}")
    
    # Интерпретация результатов
    coint_result = "КОИНТЕГРИРОВАНЫ" if pvalue < 0.05 else "НЕ коинтегрированы"
    spread_result = "СТАЦИОНАРЕН" if adf_result[1] < 0.05 else "НЕ стационарен" print(f"\nВывод: Ряды {coint_result} (p={pvalue:.4f}), спред {spread_result} (p={adf_result[1]:.4f})") return score, pvalue, spread def build_ecm(y1, y2): """Модель коррекции ошибок для коинтегрированных рядов""" # Проверка коинтеграции score, pvalue, spread = test_cointegration(y1, y2, plot=False) if pvalue >= 0.05:
        print("\nМодель ECM не может быть построена: ряды не коинтегрированы")
        return None
    
    # Построение ECM модели
    dy1 = y1.diff().dropna()
    dy2 = y2.diff().dropna()
    spread_lag = spread.shift(1).dropna()
    
    # Выравнивание индексов
    aligned_dy1 = dy1[dy1.index.isin(spread_lag.index)]
    aligned_dy2 = dy2[dy2.index.isin(spread_lag.index)]
    
    X = sm.add_constant(pd.DataFrame({
        'delta_MSFT': aligned_dy1,
        'spread_lag': spread_lag
    }))
    
    ecm_model = sm.OLS(aligned_dy2, X).fit()
    
    # Вывод результатов
    print("\nРезультаты модели коррекции ошибок (ECM):")
    print(ecm_model.summary())
    
    # Визуализация
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))
    
    ax1.set_title('Фактические vs предсказанные изменения AAPL')
    ax1.plot(aligned_dy2.values, label='Фактические')
    ax1.plot(ecm_model.fittedvalues, label='Предсказанные')
    ax1.legend()
    
    ax2.set_title('Остатки модели ECM')
    ax2.plot(ecm_model.resid)
    ax2.axhline(0, color='r', linestyle='--')
    
    plt.tight_layout()
    plt.show()
    
    return ecm_model

# Применяем функции к ценам закрытия (не к доходностям!)
print("Анализ коинтеграции между MSFT и AAPL")
test_cointegration(msft_close, aapl_close)
print("\nПостроение модели коррекции ошибок")
build_ecm(msft_close, aapl_close)

График цен закрытия MSFT и AAPL и их коинтеграционного спреда

Рис. 10: График цен закрытия MSFT и AAPL и их коинтеграционного спреда

Результаты теста коинтеграции MSFT-AAPL:
Статистика Энгла-Грейнджера: -1.8699
p-значение: 0.5952

Результаты ADF теста для спреда:
ADF статистика: -1.7766
p-значение: 0.3922

Вывод: Ряды НЕ коинтегрированы (p=0.5952), спред НЕ стационарен (p=0.3922)

Построение модели коррекции ошибок

Результаты теста коинтеграции MSFT-AAPL:
Статистика Энгла-Грейнджера: -1.8699
p-значение: 0.5952

Этот код демонстрирует анализ коинтеграции для финансовых временных рядов:

  1. Тестирование коинтеграции с помощью теста Энгла-Грейнджера;
  2. Визуализацию долгосрочного равновесного соотношения и спреда;
  3. Построение модели коррекции ошибок;
  4. Сравнение коинтеграции для различных пар финансовых инструментов.

Коинтеграционный анализ является основой многих стратегий статистического арбитража и особенно полезен для торговли парами финансовых инструментов.

Заключение

Сравнение временных финансовых рядов — это сложная, но крайне важная задача для трейдеров, аналитиков и алгоритмических стратегий. В этой статье мы рассмотрели ключевые методы и метрики для сравнения временных рядов:

  • Описательные статистики — помогают быстро оценить основные свойства данных, такие как средняя доходность, волатильность и асимметрия;
  • Метрики расстояний (евклидово, DTW, Фреше) — позволяют количественно измерить схожесть рядов, учитывая их динамику;
  • Корреляционный анализ (Пирсон, Спирмен, копулы) — выявляет линейные и нелинейные зависимости между активами;
  • Частотные методы (спектральный анализ, вейвлеты) — помогают обнаружить скрытые циклические паттерны;
  • Коинтеграция и модели коррекции ошибок — ключевые инструменты для парного трейдинга и статистического арбитража.

Какой метод сравнения временных рядов выбрать? Тут все зависит от конкретной задачи. К примеру, если требуется быстро сравнить ряды, то подойдут описательные статистики и корреляции. Если требуется глубокое погружение — то частотные методы. Для краткосрочного трейдинга скорее всего будут эффективны анализ DTW и скользящих корреляций, а для долгосрочного инвестирования — коинтеграция и волатильностные модели.

Экспериментируйте, комбинируйте методы и всегда проверяйте результаты на реальных данных — только так можно добиться устойчивых результатов в анализе финансовых временных рядов.