Wavelet-анализ финансовых данных: преобразование Фурье vs вейвлеты, многомасштабный анализ волатильности

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

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

Преобразование Фурье и его ограничения

Преобразование Фурье раскладывает сигнал на сумму синусоид с различными частотами. Для дискретного сигнала формула выглядит так:

X(k) = Σ(n=0 to N-1) x(n) · e^(-i2πkn/N)

где:

  • X(k) — коэффициент для частоты k;
  • x(n) — значение сигнала в момент n;
  • N — длина сигнала;
  • i — мнимая единица.

Преобразование показывает, какие частоты присутствуют в сигнале, но не показывает, когда они активны.

Проблема стационарности

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

Метод Short-Time Fourier Transform (STFT) частично решает проблему через анализ окон фиксированной длины. Однако приходится жертвовать одним из двух: узкое окно дает хорошее временное разрешение, но плохое частотное; широкое окно — наоборот. Принцип неопределенности Габора ограничивает одновременную точность во времени и частоте.

Потеря временной локализации

Коэффициенты Фурье отражают наличие частоты во всем сигнале. Если краткосрочный всплеск волатильности длился 2-3 дня, Фурье-анализ месячных данных размазывает этот сигнал по всему периоду. Для построения торговых стратегий критична точная локализация: нужно знать не только что волатильность изменилась, но и когда именно.

import numpy as np
import matplotlib.pyplot as plt
from scipy import signal

# Генерация сигнала с изменяющейся частотой
t = np.linspace(0, 10, 1000)
freq = np.where(t < 5, 2, 10)  # 2 Гц до 5 сек, потом 10 Гц
sig = np.sin(2 * np.pi * freq * t)

# Фурье-преобразование
fft = np.fft.fft(sig)
freqs = np.fft.fftfreq(len(sig), t[1] - t[0])
power = np.abs(fft[:len(fft)//2])

# Создаём фигуру с 3 подграфиками
plt.figure(figsize=(12, 10))

# Исходный сигнал
plt.subplot(3, 1, 1)
plt.plot(t, sig, color='steelblue')
plt.title("Исходный сигнал с изменяющейся частотой")
plt.xlabel("Время (с)")
plt.ylabel("Амплитуда")
plt.grid(True)

# Амплитудный спектр
plt.subplot(3, 1, 2)
plt.plot(freqs[:len(freqs)//2], power, color='darkorange')
plt.title("Амплитудный спектр (Фурье-преобразование)")
plt.xlabel("Частота (Гц)")
plt.ylabel("Амплитуда")
plt.grid(True)

# Спектрограмма (временная локализация частот)
f, t_spec, Sxx = signal.spectrogram(sig, fs=1/(t[1]-t[0]), nperseg=128)
plt.subplot(3, 1, 3)
plt.pcolormesh(t_spec, f, 10 * np.log10(Sxx), shading='gouraud', cmap='viridis')
plt.title("Спектрограмма сигнала (время ↔ частота)")
plt.ylabel("Частота (Гц)")
plt.xlabel("Время (с)")
plt.colorbar(label='Мощность (дБ)')

plt.tight_layout()
plt.show()

Визуализация сигнала с изменяющейся частотой: исходный временной ряд, его амплитудный спектр и спектрограмма. Графики демонстрируют переход сигнала от низкой частоты (около 2 Гц) к высокой (около 10 Гц) и соответствующее изменение частотного состава во времени

Рис. 1: Визуализация сигнала с изменяющейся частотой: исходный временной ряд, его амплитудный спектр и спектрограмма. Графики демонстрируют переход сигнала от низкой частоты (около 2 Гц) к высокой (около 10 Гц) и соответствующее изменение частотного состава во времени

Пример выше демонстрирует базовую проблему: Фурье-спектр показывает присутствие частот 2 Гц и 10 Гц, но не указывает, что первая доминирует в начале сигнала, а вторая — в конце.

👉🏻  Foundation-модели для временных рядов

Wavelet-преобразование: основы

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

Непрерывное вейвлет-преобразование (CWT) определяется как:

W(a,b) = (1/√a) ∫ x(t) · ψ*((t-b)/a) dt

где:

  • W(a,b) — вейвлет-коэффициент;
  • a — параметр масштаба (связан с частотой);
  • b — параметр сдвига (временная позиция);
  • ψ(t) — материнский вейвлет;
  • ψ* — комплексно-сопряженная функция;
  • x(t) — анализируемый сигнал.

Масштаб a обратно пропорционален частоте: малые значения соответствуют высоким частотам, большие — низким. Сдвиг b определяет временную позицию анализа.

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

Типы вейвлетов

Выбор вейвлета зависит от характеристик анализируемого сигнала и задачи:

  • Morlet вейвлет — комплексная функция, представляющая синусоиду в гауссовом окне. Хорошо локализован по частоте, применяется для выделения осциллирующих компонент. Оптимален для анализа периодичности в ценах и волатильности.
  • Mexican Hat — вторая производная гауссианы, симметричная функция. Эффективна для обнаружения резких изменений и экстремумов. Подходит для поиска разворотов тренда и аномалий.
  • Daubechies (db) — семейство ортогональных вейвлетов с компактным носителем. Используются в дискретном вейвлет-преобразовании (DWT) для декомпозиции сигнала. Параметр N определяет количество коэффициентов: db4, db6, db8. Высокие значения дают лучшее частотное разрешение, низкие — временное.
  • Symlets — модификация Daubechies с улучшенной симметрией. Снижают фазовые искажения при реконструкции сигнала.

Давайте рассмотрим разницу отработки сигнала каждым типом вейвлетов.

import numpy as np
import matplotlib.pyplot as plt
import pywt

# Параметры
np.random.seed(42)
n = 200                        # длина ряда
t = np.linspace(0, 2, n)       # 2 секунды
fs = (n - 1) / (t[-1] - t[0])  # приближенная частота дискретизации

# Генерация исходного сигнала
sig = (
    np.sin(2 * np.pi * 5 * t)                # основная низкая частота 5 Гц
    + 0.5 * np.sin(2 * np.pi * 20 * t)       # более высокая 20 Гц
    + np.where((t > 0.8) & (t < 1.0), 3, 0) # резкий скачок (широкий импульс) + np.where((t > 1.4) & (t < 1.42),
               4 * np.exp(-200 * (t - 1.41) ** 2), 0)  # узкий пик
    + 0.2 * np.random.randn(len(t))          # шум
)

wavelets = ['morl', 'mexh', 'db4', 'sym4']

# Визуализации
fig, axes = plt.subplots(5, 1, figsize=(12, 14), constrained_layout=True)

# Исходный сигнал
ax = axes[0]
ax.plot(t, sig, color='black', linewidth=1)
ax.set_title('Исходный сигнал (n=200)')
ax.set_ylabel('Амплитуда')
ax.grid(True)

# CWT для morl и mexh
for idx, name in enumerate(['morl', 'mexh'], start=1):
    ax = axes[idx]
    scales = np.arange(1, 64)  
    coef, freqs = pywt.cwt(sig, scales, name, sampling_period=(t[1] - t[0]))

    S = np.abs(coef)
    S = S / (S.max() + 1e-12)

    im = ax.pcolormesh(t, freqs, S, shading='auto')
    ax.set_ylabel(f'{name}')
    ax.set_ylim(freqs.max(), freqs.min()) 
    ax.set_title(f'CWT: {name} (нормированная амплитуда)')
    fig.colorbar(im, ax=ax, orientation='vertical', label='Нормированная амплитуда')

# DWT для db4 и sym4
for idx, name in enumerate(['db4', 'sym4'], start=3):
    ax = axes[idx]
    max_level = pywt.dwt_max_level(len(sig), pywt.Wavelet(name).dec_len)

    level = min(5, max_level)
    coeffs = pywt.wavedec(sig, name, level=level)
    details = coeffs[1:]  
    det_matrix = []
    # порядок: cD_n, cD_{n-1}, ..., cD1
    for lev, c in enumerate(details, start=1):
        try:
            rec = pywt.upcoef('d', c, wavelet=name, level=len(details) - (lev - 1), take=len(sig))
        except Exception:
            # fallback: интерполируем деталь до длины сигнала
            rec = np.interp(np.linspace(0, 1, len(sig)), np.linspace(0, 1, len(c)), c)
            rec = rec - np.mean(rec)
        det_matrix.append(rec)
    det_matrix = np.vstack(det_matrix)
    # нормализация построчно для визуальной читаемости
    det_norm = det_matrix / (np.max(np.abs(det_matrix), axis=1, keepdims=True) + 1e-12)
    im = ax.imshow(det_norm, aspect='auto', extent=[t[0], t[-1], 1, det_norm.shape[0]],
                   origin='lower', cmap='RdBu_r')
    ax.set_ylabel(f'{name} (детали)')
    ax.set_yticks(np.arange(1, det_norm.shape[0] + 1))
    ax.set_yticklabels([f'd{len(details)-i+1}' for i in range(det_norm.shape[0])])  # dN..d1
    ax.set_title(f'DWT детали (уровни) — {name}')
    fig.colorbar(im, ax=ax, orientation='vertical', label='Нормированная амплитуда')

axes[-1].set_xlabel('Время (с)')
plt.show()

Сравнение обработки одного и того же сигнала различными типами вейвлетов

Рис. 2: Сравнение обработки одного и того же сигнала различными типами вейвлетов

В верхней части показан исходный сигнал, содержащий низко- и высокочастотные компоненты, резкий скачок и короткий импульс. Следующие два графика иллюстрируют результаты непрерывного вейвлет-преобразования (CWT) с использованием вейвлетов Morlet и Mexican Hat. Два нижних графика демонстрируют дискретное вейвлет-разложение (DWT) с вейвлетами Daubechies (db4) и Symlet (sym4).

👉🏻  Buy или Sell: как бы решал эту задачу Бернулли?

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

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

Время-частотная локализация

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

  • На низких частотах (большие масштабы) вейвлет растягивается, захватывая больший временной интервал — хорошее частотное разрешение, слабое временное;
  • На высоких частотах (малые масштабы) вейвлет сжимается — точная временная локализация, но менее точное определение частоты.

Это естественное свойство, соответствующее принципу неопределенности, но в отличие от STFT разрешение автоматически адаптируется к частоте.

Непрерывное vs дискретное вейвлет-преобразование

CWT и DWT решают разные задачи анализа финансовых данных.

Continuous Wavelet Transform: спектрограммы волатильности

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

import numpy as np
import matplotlib.pyplot as plt
import pywt

np.random.seed(42)
t = np.arange(0, 200)
price = 100 * np.exp(np.cumsum(np.random.randn(200) * 0.01))

# Всплеск волатильности
price[100:150] += np.random.randn(50) * 10

# CWT
scales = np.arange(1, 50)
coefficients, frequencies = pywt.cwt(price, scales, 'morl')

# Нормировка для визуализации
S = np.abs(coefficients)
S = S / (S.max() + 1e-12)

plt.figure(figsize=(10, 6))

plt.subplot(2, 1, 1)
plt.plot(t, price, color='steelblue')
plt.title('Временной ряд с изменяющейся волатильностью')
plt.ylabel('Цена')
plt.grid(True)

plt.subplot(2, 1, 2)
plt.pcolormesh(t, frequencies, S, shading='auto', cmap='viridis')
plt.title('Непрерывное вейвлет-преобразование (Morlet)')
plt.xlabel('Время')
plt.ylabel('Частота')
plt.ylim(frequencies.max(), frequencies.min())  # высокие частоты сверху
plt.colorbar(label='Нормированная амплитуда')

plt.tight_layout()
plt.show()

Анализ волатильности с помощью непрерывного вейвлет-преобразования (CWT)

Рис. 3: Анализ волатильности с помощью непрерывного вейвлет-преобразования (CWT)

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

👉🏻  Как анализировать финансовые коэффициенты для выбора перспективных активов?

На скалограмме заметно, что в диапазоне наблюдений 100–150 увеличилась энергия на малых масштабах (высокочастотные колебания), что соответствует всплеску волатильности в ценовом ряде. Яркие области на карте визуализируют локальные изменения энергии сигнала и позволяют наглядно оценить временную динамику волатильности.

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

Discrete Wavelet Transform: декомпозиция сигнала

DWT декомпозирует сигнал на ортогональные компоненты через последовательную фильтрацию и субдискретизацию. Каждый уровень разложения разделяет сигнал на приближение (approximation) и детали (details):

  1. Приближение — низкочастотная компонента, тренд сигнала;
  2. Детали — высокочастотные колебания, шум и краткосрочные флуктуации;
  3. На следующем уровне приближение снова декомпозируется, рекурсивно выделяя все более долгосрочные компоненты.
import numpy as np
import matplotlib.pyplot as plt
import pywt

# Синтетический ряд с волатильностью
np.random.seed(42)
t = np.arange(0, 200)
price = 100 * np.exp(np.cumsum(np.random.randn(200) * 0.01))
price[100:150] += np.random.randn(50) * 10  # всплеск волатильности

# Дискретное вейвлет-преобразование (DWT)
wavelet = 'db4'
level = 4
coeffs = pywt.wavedec(price, wavelet, level=level)

approx = coeffs[0]       # приближение (тренд)
details = coeffs[1:]     # детали уровней 4,3,2,1

# Восстановление тренда (удаление шума)
price_denoised = pywt.waverec([coeffs[0]] + [None]*level, wavelet)
price_denoised = price_denoised[:len(price)]  # обрезаем до длины исходного ряда

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

n_subplots = 1 + 1 + len(details) + 1  # исходный + приближение + детали + восстановленный
subplot_idx = 1

# Исходный ряд
plt.subplot(n_subplots, 1, subplot_idx)
plt.plot(t, price, color='black')
plt.title('Исходный ценовой ряд')
plt.ylabel('Цена')
plt.grid(True)
subplot_idx += 1

# Приближение (тренд)
plt.subplot(n_subplots, 1, subplot_idx)
plt.plot(np.linspace(0, t[-1], len(approx)), approx, color='blue')
plt.title('Приближение (тренд) — уровень 0')
plt.ylabel('Амплитуда')
plt.grid(True)
subplot_idx += 1

# Детали уровней 4,3,2,1
for i, d in enumerate(details, start=1):
    plt.subplot(n_subplots, 1, subplot_idx)
    plt.plot(np.linspace(0, t[-1], len(d)), d, color='red')
    plt.title(f'Детали — уровень {level-i+1}')
    plt.ylabel('Амплитуда')
    plt.grid(True)
    subplot_idx += 1

# Восстановленный сигнал без шума
plt.subplot(n_subplots, 1, subplot_idx)
plt.plot(t, price_denoised, color='green')
plt.title('Восстановленный сигнал (только тренд, шум удалён)')
plt.ylabel('Цена')
plt.xlabel('Время')
plt.grid(True)

plt.tight_layout()
plt.show()

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

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

Функция wavedec рекурсивно разлагает сигнал на приближение и детали, где approx соответствует низкочастотной компоненте (тренду), а details отражают высокочастотные колебания, шум и краткосрочные флуктуации. С помощью waverec([coeffs[0]] + [None]*level, wavelet) можно восстановить только тренд, исключив шум.

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

Выбор подхода для конкретных задач

CWT применяется для построения детализированной карты изменений волатильности и анализа взаимосвязей между активами с помощью когерентности (Wavelet coherence). Типичные сценарии использования включают визуализацию рыночных режимов, исследовательский анализ временных рядов и построение индикаторов на основе энергии сигнала в определенных частотных диапазонах.

👉🏻  Анализ фьючерса на Brent с помощью Pandas, Sklearn, Hmmlearn

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

Если задача требует визуализации и интерпретации, то лучше выбирать CWT. Если нужна эффективная обработка и построение признаков — DWT.

Многомасштабный анализ волатильности

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

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

Декомпозиция временных рядов по масштабам

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

  1. Первый уровень (D1) содержит колебания периодом 2-4 бара;
  2. Второй (D2) — 4-8 баров;
  3. Третий (D3) — 8-16 баров.

Каждый следующий уровень удваивает характерный период.

# Генерация доходностей с многомасштабной структурой
np.random.seed(42)
n = 500
t = np.arange(n)
trend = -0.0003 * t   # Низкочастотный тренд (долгосрочная компонента)
mid_cycle = 0.01 * np.sin(2 * np.pi * t / 50) # Среднесрочный цикл (периоды ~50 баров)
short_cycle = 0.005 * np.sin(2 * np.pi * t / 10) # Краткосрочный цикл (периоды ~10 баров)
base_noise = np.random.randn(n) * 0.01   # Базовый шум

# Модуляция волатильности для консолидаций
vol_modulation = np.ones(n)
vol_modulation[100:150] *= 0.3  # первая консолидация
vol_modulation[300:350] *= 0.2  # вторая консолидация

# Локальные всплески волатильности
vol_spikes = np.zeros(n)
vol_spikes[50:60] = np.random.randn(10) * 0.05
vol_spikes[200:210] = np.random.randn(10) * 0.07
vol_spikes[400:410] = np.random.randn(10) * 0.04

# Финальный ряд доходностей
returns = trend + mid_cycle + short_cycle + base_noise * vol_modulation + vol_spikes

# Декомпозиция на 5 уровней
coeffs = pywt.wavedec(returns, 'db6', level=5)

# Извлечение компонент
approx = coeffs[0]  # долгосрочный тренд
details = coeffs[1:]  # детали от D5 до D1

# Вычисление волатильности на каждом масштабе
vol_multiscale = [np.std(d) for d in details]

# Визуализация доходностей и DWT компонент
plt.figure(figsize=(10, 16))

# Исходные доходности
plt.subplot(7, 1, 1)
plt.plot(returns, color='black')
plt.title('Логарифмические доходности с многомасштабной структурой')
plt.ylabel('Доходность')
plt.grid(True)

# Долгосрочный тренд (approx)
plt.subplot(7, 1, 2)
plt.plot(np.linspace(0, len(returns)-1, len(approx)), approx, color='blue')
plt.title('Долгосрочный тренд (approx)')
plt.ylabel('Амплитуда')
plt.grid(True)

# Детали D5–D1
for i, d in enumerate(details, start=1):
    plt.subplot(7, 1, i+2)
    plt.plot(np.linspace(0, len(returns)-1, len(d)), d, color='red')
    plt.title(f'Detail D{len(details)-i+1} (масштаб {2**(len(details)-i)}–{2**(len(details)-i+1)} баров)')
    plt.ylabel('Амплитуда')
    plt.grid(True)

plt.xlabel('Время')
plt.tight_layout()
plt.show()

# Вывод волатильности на каждом масштабе
for i, vol in enumerate(vol_multiscale[::-1], start=1):
    print(f'Волатильность на уровне D{i}: {vol:.5f}')

Дискретное вейвлет-преобразование доходностей с многомасштабной структурой: верхний график показывает исходный ряд; второй график — долгосрочный тренд (approx); следующие пять графиков — детали уровней D5–D1, отражающие колебания на разных временных масштабах. Такая визуализация демонстрирует распределение волатильности по масштабам и вклад каждого уровня в структуру временного ряда

Рис. 5: Дискретное вейвлет-преобразование доходностей с многомасштабной структурой: верхний график показывает исходный ряд; второй график — долгосрочный тренд (approx); следующие пять графиков — детали уровней D5–D1, отражающие колебания на разных временных масштабах. Такая визуализация демонстрирует распределение волатильности по масштабам и вклад каждого уровня в структуру временного ряда

Волатильность на уровне D1: 0.01498
Волатильность на уровне D2: 0.01334
Волатильность на уровне D3: 0.01525
Волатильность на уровне D4: 0.01345
Волатильность на уровне D5: 0.03566

Декомпозиция показывает, на каких масштабах сконцентрирована волатильность. Если энергия сосредоточена в D1-D2, доминирует краткосрочный шум. Если в D4-D5 — рынок демонстрирует среднесрочные колебания.

Выделение краткосрочной vs долгосрочной волатильности

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

👉🏻  Сезонность временных рядов. В чем отличие аддитивной от мультипликативной?

Краткосрочная волатильность (D1-D2) отражает ликвидность и микроструктурные эффекты. Высокие значения указывают на широкий bid-ask spread, низкую глубину стакана, проскальзывание. Для HFT стратегий и маркет-мейкинга это ключевой параметр.

Долгосрочная волатильность (D4-D6) связана с фундаментальной неопределенностью и макроэкономическими рисками. Рост этой компоненты предшествует крупным коррекциям и смене трендов. Для позиционных стратегий и управления портфелем долгосрочная волатильность определяет размер позиций и хеджирование.

Соотношение краткосрочной и долгосрочной волатильности индицирует режим рынка:

  • Если short_vol >> long_vol, наблюдается высокочастотный шум при стабильном тренде;
  • Если long_vol растет при умеренной short_vol, то это значит что формируется крупное движение.

Практическое применение

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

Фильтрация шума

Высокочастотный шум в ценовых данных искажает сигналы и провоцирует ложные входы. Удаление шумовых компонент через DWT улучшает качество индикаторов и снижает количество сделок.

import numpy as np
import matplotlib.pyplot as plt
import pywt

# Генерация сигнала с локальным всплеском волатильности
np.random.seed(42)
t = np.arange(0, 200)

# Тренд + шум с переменной амплитудой
base_trend = 0.05 * t  
volatility = 1 + 2 * np.exp(-0.5 * ((t - 100)/30)**2)  
noise = np.random.randn(len(t)) * volatility
signal = base_trend + noise
signal[95:105] += np.linspace(0, 5, 10) 

# Применяем вейвлет-фильтр
def wavelet_denoise(signal, wavelet='db4', level=3, threshold_method='soft'):
    coeffs = pywt.wavedec(signal, wavelet, level=level)
    sigma = np.median(np.abs(coeffs[-1])) / 0.6745
    threshold = sigma * np.sqrt(2 * np.log(len(signal)))
    coeffs_thresh = [coeffs[0]]
    for detail in coeffs[1:]:
        if threshold_method == 'soft':
            thresh_detail = pywt.threshold(detail, threshold, mode='soft')
        else:
            thresh_detail = pywt.threshold(detail, threshold, mode='hard')
        coeffs_thresh.append(thresh_detail)
    denoised = pywt.waverec(coeffs_thresh, wavelet)
    return denoised[:len(signal)]

signal_noisy = signal
signal_clean = wavelet_denoise(signal_noisy, level=4)

# Визуализация
plt.figure(figsize=(12, 5))
plt.plot(signal_noisy, color='gray', alpha=0.6, label='Шумный сигнал')
plt.plot(signal_clean, color='steelblue', lw=2, label='Очищенный сигнал (вейвлет)')
plt.title('Сравнение шумного и очищенного сигналов')
plt.xlabel('Время')
plt.ylabel('Амплитуда')
plt.legend()
plt.grid(True)
plt.show()

Сравнение ряда с шумным сигналом и очищенного с помощью вейвлета db4

Рис. 6: Сравнение ряда с шумным сигналом и очищенного с помощью вейвлета db4

Представленный код выполняет удаление шума из временного ряда с помощью дискретного вейвлет-преобразования:

  1. Функция wavelet_denoise декомпозирует сигнал на приближение и детали;
  2. Затем рассчитывает порог на основе медианного абсолютного отклонения последних деталей;
  3. Затем обнуляет малые коэффициенты методом soft-thresholding;
  4. После чего восстанавливает сигнал через обратное вейвлет-преобразование.

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

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

Детекция режимов рынка

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

  • Тренд: доминирование энергии в approximation и старших уровнях деталей (D4-D5). Низкая краткосрочная волатильность, стабильное направленное движение.
  • Флэт: равномерное распределение энергии по уровням. Отсутствие выраженных компонент, цена колеблется вокруг среднего.
  • Высокая волатильность: концентрация энергии в младших уровнях (D1-D2). Резкие краткосрочные колебания, неопределенность направления.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

np.random.seed(42)
n = 500
segments = [100, 50, 50, 50, 250]
assert sum(segments) == n

returns_data = np.zeros(n)
regimes_true = []

# Тренд вверх
returns_data[0:100] = np.linspace(0, 0.01, 100) + np.random.randn(100)*0.002
regimes_true += ['trend']*100

# Боковик спокойный
returns_data[100:150] = np.random.randn(50)*0.003
regimes_true += ['ranging']*50

# Боковик с высокой волатильностью
returns_data[150:200] = np.random.randn(50)*0.008
regimes_true += ['high_volatility']*50

# Боковик спокойный
returns_data[200:300] = np.random.randn(100)*0.002
regimes_true += ['ranging']*100

# Тренд вниз
returns_data[300:500] = np.linspace(0, -0.02, 200) + np.random.randn(200)*0.002
regimes_true += ['trend']*200

# Визуализация
colors = {'trend': 'green', 'ranging': 'orange', 'high_volatility': 'red'}

plt.figure(figsize=(14,5))
plt.plot(returns_data, color='black', alpha=0.7, label='Доходности')

for i in range(n):
    plt.axvspan(i, i+1, color=colors[regimes_true[i]], alpha=0.3)

# Создаем легенду для зон режимов
legend_patches = [mpatches.Patch(color=colors[r], alpha=0.3, label=r.capitalize()) for r in colors]
plt.legend(handles=[plt.Line2D([0], [0], color='black', lw=2, label='Доходности')] + legend_patches)

plt.title('Определение рыночных режимов с помощью вейвлетов')
plt.xlabel('Время')
plt.ylabel('Доходность')
plt.grid(True)
plt.show()

Определение рыночных режимов с помощью вейвлетов

Рис. 7: Определение рыночных режимов с помощью вейвлетов

Детекция режима используется для адаптации параметров стратегии. В трендовом режиме увеличиваются размеры позиций momentum стратегий. В высоковолатильном режиме сокращаются позиции или активируются стратегии на возврат к среднему (mean reversion).

👉🏻  Прогнозирование трафика и конверсий сайта с помощью SVM, SVR (опорных векторов)

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

Многомасштабная декомпозиция генерирует сигналы через анализ согласованности компонент разных масштабов:

  • Сигнал на покупку: краткосрочная компонента (D1-D2) направлена вверх, среднесрочная (D3-D4) также растет, долгосрочный тренд (approximation) положителен. Все масштабы согласованы — сильный сигнал;
  • Сигнал на продажу: обратная ситуация, все компоненты направлены вниз;
  • Отсутствие сигнала: компоненты разнонаправлены, нет согласованности масштабов.
def wavelet_multiscale_signal(returns, wavelet='db6', level=4, lookback=5):
    """
    Генерация торговых сигналов через согласованность масштабов
    """
    signals = []
    
    # Декомпозиция
    coeffs = pywt.wavedec(returns, wavelet, level=level)
    
    # Реконструкция масштабов
    short_term = pywt.waverec([None, coeffs[1], coeffs[2]] + [None]*(level-2), wavelet)[:len(returns)]
    mid_term = pywt.waverec([None, None, None, coeffs[3]] + [None]*(level-3), wavelet)[:len(returns)]
    long_term = pywt.waverec([coeffs[0]] + [None]*level, wavelet)[:len(returns)]
    
    for i in range(lookback, len(returns)):
        # Направление каждого масштаба
        short_dir = 1 if np.mean(short_term[i-lookback:i]) > 0 else -1
        mid_dir = 1 if np.mean(mid_term[i-lookback:i]) > 0 else -1
        long_dir = 1 if np.mean(long_term[i-lookback:i]) > 0 else -1
        
        # Согласованность
        if short_dir == mid_dir == long_dir == 1:
            signal = 1  # buy
        elif short_dir == mid_dir == long_dir == -1:
            signal = -1  # sell
        else:
            signal = 0  # hold
        
        signals.append(signal)
    
    return signals

# Генерация сигналов
trade_signals = wavelet_multiscale_signal(returns_data)

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

Параметры lookback и уровни декомпозиции подбираются под таймфрейм торговли. Для внутридневных стратегий используются level=3-4 и lookback=3-5. Для позиционных — level=5-6 и lookback=10-20.

Заключение

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

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