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

Меня зовут Андрей Бирюков. Я — независимый эксперт в области ИТ и ИБ, преподаю в учебных центрах и пишу статьи и книги. 

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

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

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

И сегодня мы поговорим об этом мощнейшем инструменте финансового анализа.

Псевдоизображения в финансах

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

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

Свёрточные сети в финансовом анализе: от компьютерного зрения к прогнозированию рынков - 1

В целом свёрточные нейросети были спроектированы под три индуктивных смещения.

  • Локальность — важные признаки формируются в окрестности точки, а не по всему входу сразу.

  • Трансляционная инвариантность — один и тот же паттерн может появиться в любом месте входа.

  • Иерархия масштабов — простые признаки комбинируются в сложные по мере углубления.

Но в финансах эти смещения будут работать частично. Давайте разберём несколько конкретных примеров использования CNN. И начнём с рассмотрения временного ряда как «псевдоизображения» (1 D‑CNN).

Перед нами стоит задача классифицировать 60-минутные окна BTC‑USD, разбив их на три класса:

  • «Рост > +0.5%».

  • «Падение < −0.5%».

  • «Боковик».

В качестве горизонта мы будем рассматривать следующие 30 минут.

Здесь самое важное — определить, что нам нужно подать на вход. Использование сырых цен — это очень плохой вариант. Дело в том, что они нестационарны, то есть абсолютный уровень и волатильность дрейфуют во времени, и CNN выучивает не форму паттерна, а конкретные значения цены в тренировочном окне, из‑за чего трансляционная инвариантность свёртки превращается из полезного свойства в источник ложных срабатываний.

Вместо этого мы используем лог‑доходности и нормализацию:

import numpy as np
import pandas as pd

def build_features(df, window=60):
    """
    df: DataFrame с колонками open, high, low, close, volume
    Возвращает массив окон признаков и метки.
    """

    # 1. Лог-доходности вместо цен (убираем нестационарность уровня)
    df['ret'] = np.log(df['close'] / df['close'].shift(1))
    df['hl_range'] = np.log(df['high'] / df['low'])
    df['vol_z'] = (df['volume'] - df['volume'].rolling(200).mean()) / df['volume'].rolling(200).std()

    # 2. Z-score по скользящему окну (адаптация к режиму волатильности)
    for col in ['ret', 'hl_range', 'vol_z']:
        roll = df[col].rolling(200)
        df[f'{col}_z'] = (df[col] - roll.mean()) / (roll.std() + 1e-8)

    features = df[['ret_z', 'hl_range_z', 'vol_z_z']].dropna().values

    # 3. Метки: forward return через 30 шагов
    fwd = np.log(df['close'].shift(-30) / df['close'])
    labels = np.where(fwd > 0.005, 2, np.where(fwd < -0.005, 0, 1))

    X, y = [], []

    for i in range(window, len(features) - 30):
        X.append(features[i - window:i].T)  # (channels, time)
        y.append(labels[i])
    return np.array(X), np.array(y)

Архитектура

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

Так, нормализация считается на скользящем окне, а не по всему датасету, потому что иначе у нас будет утечка будущего (look‑ahead bias), то есть ситуация, когда модель при обучении или оценке использует информацию, которая фактически недоступна в момент реального принятия решения.

Также метки здесь считаются через shift(-30), то есть последние 30 строк должны быть отброшены, иначе тренируемся на будущем.

И в обязательном порядке должен использоваться Z‑score по волатильности, так как без него модель выучит «высокая волатильность = сигнал», а это режимный артефакт, а не паттерн.

import torch
import torch.nn as nn
 
class TemporalCNN(nn.Module):
    def init(self, n_channels=3, n_classes=3):
        super().__init__()
        self.net = nn.Sequential(
            nn.Conv1d(n_channels, 32, kernel_size=5, padding=2),
            nn.BatchNorm1d(32),
            nn.ReLU(),
            nn.MaxPool1d(2),
            nn.Conv1d(32, 64, kernel_size=3, padding=1),
            nn.BatchNorm1d(64),
            nn.ReLU(),
            nn.MaxPool1d(2),
            nn.Conv1d(64, 64, kernel_size=3, padding=1, dilation=2),  # dilated — расширяем рецептивное поле
            nn.ReLU(),
            nn.AdaptiveAvgPool1d(1),
        )
        self.head = nn.Linear(64, n_classes)

    def forward(self, x):
        return self.head(self.net(x).squeeze(-1))

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

Причём между концом обучения и началом теста не остаётся пересечений, так как этот зазор нужен, чтобы метки обучающих примеров (которые считаются по будущим значениям, например forward return через 30 шагов) не «подглядывали» в тестовый период, а двигающееся окно обучения эмулировало реальное переобучение модели по мере поступления новых данных.

def walk_forward_splits(n_samples, n_splits=5, embargo=30):
    """Purged walk-forward CV (López de Prado)."""

    fold_size = n_samples // (n_splits + 1)
    for k in range(n_splits):
        train_end = fold_size * (k + 1)
        test_start = train_end + embargo
        test_end = min(test_start + fold_size, n_samples)
        if test_end <= test_start:
            break
        yield np.arange(0, train_end), np.arange(test_start, test_end)

2 D‑представления ряда

Главный недостаток подхода 1 D‑CNN на финансовых рядах — это ограниченное рецептивное поле и слабая работа с дальними зависимостями, так как свёртка видит только локальную окрестность. Поэтому «эхо» событий спустя сотни шагов, долгую память и режимные сдвиги она улавливает плохо.

В качестве альтернативы мы рассмотрим популярные способы превратить 1 D‑ряд в «картинку».

Свёрточные сети в финансовом анализе: от компьютерного зрения к прогнозированию рынков - 2

Начнём с STFT, или спектрограммы.

  • Плюс такого подхода в том, что мы физически осмысленно можем ловить смену частот (ускорение/замедление трендов, кластеры волатильности).

  • Минус — необходимость вручную подбирать фиксированное разрешение по частоте и времени (принцип неопределённости). Соответственно, параметр nperseg подбираем под горизонт.

import scipy.signal as ss

def stft_image(series, nperseg=32, noverlap=24):
    f, t, Z = ss.stft(series, nperseg=nperseg, noverlap=noverlap)
    return np.abs(Z)  # (freq_bins, time_bins)

Multi‑asset correlation map хорошо ловит режимные сдвиги (risk‑on/risk‑off), что бывает реально полезно для портфельных задач и для BTC как рискового актива. При этом он не работает на одном активе, а данные нужно синхронизировать.

def corr_image(returns_df, window=60):
    """Матрица корреляций по окну — 'картинка' кросс-активных связей."""
    return returns_df.iloc[-window:].corr().values

Таким образом, выбор подхода зависит от задачи:

  • Для одного актива с коротким горизонтом используем 1 D‑CNN на нормализованных доходностях.

  • В случае сильной волатильности применяем STFT + CNN.

  • Для портфельных и кросс‑активных сигналов нужны correlation maps + CNN или GNN.

GAF — академически красиво, но дорого

Использование превращения 1 D‑ряда в «картинку» плохо тем, что оно чаще всего приносит сложность без добавления информации. Например, STFT навязывает фиксированное разрешение по времени и частоте.

В качестве альтернативы можно рассмотреть метод GAF (Gramian Angular Field), в котором мы будем кодировать одномерный временной ряд в двумерное изображение. Идея в том, что исходный ряд сначала нормализуется, затем переводится в полярные координаты (угол кодирует значение, радиус — время), после чего попарные отношения между точками ряда формируют матрицу‑«картинку».

Свёрточные сети в финансовом анализе: от компьютерного зрения к прогнозированию рынков - 3

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

import lightgbm as lgb
from sklearn.metrics import roc_auc_score
 
# Обучаем CNN на вспомогательной задаче (например, предсказание знака forward return)
cnn = TemporalCNN().cuda()
# ... обучение ...
 
# Извлекаем эмбеддинги
cnn.eval()
embeddings = []
with torch.no_grad():
    for batch in loader:
        emb = cnn.net(batch.cuda()).squeeze(-1).cpu().numpy()
        embeddings.append(emb)

X_emb = np.vstack(embeddings)  # (n, 64)

Дальше мы можем использовать стандартный пайплайн: LightGBM + walk‑forward + калибровка вероятностей.

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

На практике такая гибридная схема на BTC‑данных стабильно даёт Sharpe выше, чем end‑to‑end CNN, и, что важнее, устойчивее к смене режима.

Детектор аномалий / микроструктурных сигналов

А вот здесь CNN будет ближе всего к «честному» компьютерному зрению. Здесь наша задача заключается в том, чтобы найти на «карте» ордербука локальные структуры, такие как стенки, iceberg‑паттерны или спуфинг.

Свёрточные сети в финансовом анализе: от компьютерного зрения к прогнозированию рынков - 4

Для практической реализации нам потребуется следующий код: 

class OrderBookCNN(nn.Module):
    """Вход: (batch, 1, price_levels, time_steps) — 2D-снимок стакана."""

    def init(self):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(1, 16, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
            nn.Conv2d(16, 32, 3, padding=1), nn.ReLU(), nn.MaxPool2d(2),
            nn.AdaptiveAvgPool2d(1),
        )
        self.head = nn.Linear(32, 2)  # аномалия / норма

    def forward(self, x):
        return self.head(self.features(x).flatten(1))

Здесь аналогия с CV работает почти буквально, так как есть:

  • Локальность по уровням цены и по времени.

  • Трансляционная инвариантность по уровням (стенка на 5-м уровне = стенка на 15-м).

  • Иерархия «тик → паттерн → режим».

Итог: как строить пайплайн

Для построения пайплайна с использованием CNN в финансовом анализе необходимо начать с baseline без CNN, то есть использовать логистическую регрессию или LightGBM на инженерных признаках (доходности, волатильность, RSI, режимные индикаторы).

Затем добавьте 1 D‑CNN на нормализованных доходностях как отдельную модель и сравните с baseline на purge/embargo CV.

Если CNN выигрывает, то используйте её как энкодер признаков, а финальное решение отдайте бустингу. Ну а 2 D‑представления (STFT, correlation maps) добавляйте только в том случае, если у вас есть гипотеза, что именно мультимасштабная или кросс‑активная структура несёт сигнал. Иначе это будет усложнением без выигрыша.

Заключительный тезис заключается в том, что сила CNN в финансах — не в «предсказании курса биткоина», а в извлечении локальных мультимасштабных признаков, которые потом используются в более устойчивых моделях.

Свёрточное зрение здесь действительно работает, но как компонент пайплайна, а не как волшебная кнопка.

Свёрточные сети в финансовом анализе: от компьютерного зрения к прогнозированию рынков - 5

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

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

  • 27 октября, 20:00. «Свёрточные сети: от распознавания изображений до прогнозирования курса биткоина». Записаться

  • 18 ноября, 20:00. «Нейросети в алгоритмической торговле — учим модель принимать торговые решения». Записаться

Автор: Andrey_Biryukov

Источник