Финансовые временные ряды нестационарны: волатильность меняется, тренды возникают и исчезают, корреляции нестабильны. Классический частотный анализ предполагает постоянство частотных компонент во времени, что противоречит природе рыночных данных. Вейвлет-преобразование решает эту проблему через одновременный анализ во временной и частотной областях.
Эта статья сравнивает подходы Фурье и вейвлетов к анализу финансовых данных, объясняет механику вейвлет-преобразования и показывает практическое применение для многомасштабного анализа волатильности.
Преобразование Фурье и его ограничения
Преобразование Фурье раскладывает сигнал на сумму синусоид с различными частотами. Для дискретного сигнала формула выглядит так:
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()

Рис. 1: Визуализация сигнала с изменяющейся частотой: исходный временной ряд, его амплитудный спектр и спектрограмма. Графики демонстрируют переход сигнала от низкой частоты (около 2 Гц) к высокой (около 10 Гц) и соответствующее изменение частотного состава во времени
Пример выше демонстрирует базовую проблему: Фурье-спектр показывает присутствие частот 2 Гц и 10 Гц, но не указывает, что первая доминирует в начале сигнала, а вторая — в конце.
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).
Для 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()

Рис. 3: Анализ волатильности с помощью непрерывного вейвлет-преобразования (CWT)
Верхний график иллюстрирует синтетический ценовой ряд с локальным всплеском волатильности. Нижний график показывает скалограмму, построенную с использованием вейвлета Morlet. Высокие значения коэффициентов (области высокой интенсивности) отражают периоды возрастания динамической активности сигнала.
На скалограмме заметно, что в диапазоне наблюдений 100–150 увеличилась энергия на малых масштабах (высокочастотные колебания), что соответствует всплеску волатильности в ценовом ряде. Яркие области на карте визуализируют локальные изменения энергии сигнала и позволяют наглядно оценить временную динамику волатильности.
При этом CWT имеет свои ограничения: коэффициенты сильно коррелированы между соседними масштабами, что приводит к избыточности данных, а вычислительная сложность возрастает с увеличением длины ряда и числа используемых масштабов.
Discrete Wavelet Transform: декомпозиция сигнала
DWT декомпозирует сигнал на ортогональные компоненты через последовательную фильтрацию и субдискретизацию. Каждый уровень разложения разделяет сигнал на приближение (approximation) и детали (details):
- Приближение — низкочастотная компонента, тренд сигнала;
- Детали — высокочастотные колебания, шум и краткосрочные флуктуации;
- На следующем уровне приближение снова декомпозируется, рекурсивно выделяя все более долгосрочные компоненты.
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). Типичные сценарии использования включают визуализацию рыночных режимов, исследовательский анализ временных рядов и построение индикаторов на основе энергии сигнала в определенных частотных диапазонах.
DWT используется для фильтрации, сжатия и построения фичей для моделей. Декомпозированные компоненты становятся признаками: тренд, среднесрочные колебания, краткосрочный шум. Модель обучается предсказывать каждую компоненту отдельно или использует их как независимые входы.
Если задача требует визуализации и интерпретации, то лучше выбирать CWT. Если нужна эффективная обработка и построение признаков — DWT.
Многомасштабный анализ волатильности
Волатильность финансовых активов проявляется на разных временных масштабах одновременно. Внутридневные всплески связаны с микроструктурой рынка и потоком ордеров. Недельные колебания отражают реакцию на новости и макроэкономические данные. Месячные и квартальные изменения обусловлены фундаментальными факторами и сезонностью.
Вейвлет-декомпозиция разделяет общую волатильность на компоненты разных масштабов, позволяя анализировать их независимо.
Декомпозиция временных рядов по масштабам
DWT разлагает логарифмические доходности на уровни детализации:
- Первый уровень (D1) содержит колебания периодом 2-4 бара;
- Второй (D2) — 4-8 баров;
- Третий (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}')

Рис. 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()

Рис. 6: Сравнение ряда с шумным сигналом и очищенного с помощью вейвлета db4
Представленный код выполняет удаление шума из временного ряда с помощью дискретного вейвлет-преобразования:
- Функция wavelet_denoise декомпозирует сигнал на приближение и детали;
- Затем рассчитывает порог на основе медианного абсолютного отклонения последних деталей;
- Затем обнуляет малые коэффициенты методом soft-thresholding;
- После чего восстанавливает сигнал через обратное вейвлет-преобразование.
В результате получается очищенный сигнал, на котором сохранены тренд и ключевые колебания, включая локальный всплеск, а высокочастотный шум эффективно подавлен.
В количественном анализе вейвлет-фильтрация часто применяется к ценовым рядам перед вычислением технических индикаторов или непосредственно к самим индикаторам для их сглаживания. Это позволяет уменьшить влияние краткосрочного шума, выделить устойчивые тренды и улучшить качество сигналов для последующего анализа или построения торговых стратегий.
Детекция режимов рынка
Распределение энергии по масштабам характеризует текущий режим рынка. Три основных режима: тренд, флэт, высокая волатильность.
- Тренд: доминирование энергии в 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).
Построение торговых сигналов
Многомасштабная декомпозиция генерирует сигналы через анализ согласованности компонент разных масштабов:
- Сигнал на покупку: краткосрочная компонента (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 делает метод пригодным для работы с потоковыми данными в реальном времени. Для алгоритмических систем это инструмент, который трансформирует сырые ценовые данные в структурированную многомасштабную информацию, напрямую используемую в логике принятия решений.