Векторизованный расчет SuperTrend на Python без медленных циклов

Векторизованный расчет SuperTrend на Python без медленных циклов Анализ данных и Бэктесты
Пошаговое руководство по реализации индикатора SuperTrend на Python с использованием pandas, numpy и numba. Избавляемся от медленных циклов и ускоряем расчет в 100 раз.
Суть: Чистый расчет SuperTrend в pandas через стандартные методы невозможен без циклов из-за рекурсивной зависимости текущего значения от предыдущего. Оптимальное решение для Senior Quant разработчика — вынести рекурсивный шаг в JIT-компилируемую функцию с помощью numba.njit. Это дает ускорение в 100+ раз по сравнению с обычным циклом for в Python, сохраняя удобство работы с DataFrame.

Исходный код

Индикатор SuperTrend сочетает в себе средний истинный диапазон (ATR) и медианную цену для построения динамических уровней поддержки и сопротивления. Поскольку расчет финальных линий (Final Upper Band и Final Lower Band) зависит от их же значений на предыдущем шаге, классическая векторизация в pandas здесь бессильна.

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

import numpy as np
import pandas as pd
from numba import njit

@njit
def _calculate_supertrend_fast(high, low, close, basic_ub, basic_lb):
    """
    Быстрый JIT-компилируемый цикл для расчета рекурсивных полос SuperTrend.
    """
    n = len(close)
    upper_band = np.zeros(n)
    lower_band = np.zeros(n)
    supertrend = np.zeros(n)
    direction = np.ones(n)  # 1 - восходящий тренд, -1 - нисходящий

    # Инициализация первого значения
    upper_band[0] = basic_ub[0]
    lower_band[0] = basic_lb[0]
    supertrend[0] = basic_ub[0]

    for i in range(1, n):
        # Расчет Final Upper Band
        if basic_ub[i] < upper_band[i-1] or close[i-1] > upper_band[i-1]:
            upper_band[i] = basic_ub[i]
        else:
            upper_band[i] = upper_band[i-1]

        # Расчет Final Lower Band
        if basic_lb[i] > lower_band[i-1] or close[i-1] < lower_band[i-1]:
            lower_band[i] = basic_lb[i]
        else:
            lower_band[i] = lower_band[i-1]

        # Определение направления тренда и значения SuperTrend
        if direction[i-1] == 1:
            if close[i] < lower_band[i]:
                direction[i] = -1
                supertrend[i] = upper_band[i]
            else:
                direction[i] = 1
                supertrend[i] = lower_band[i]
        else:
            if close[i] > upper_band[i]:
                direction[i] = 1
                supertrend[i] = lower_band[i]
            else:
                direction[i] = -1
                supertrend[i] = upper_band[i]

    return supertrend, direction

def get_supertrend(df: pd.DataFrame, period: int = 10, multiplier: float = 3.0) -> pd.DataFrame:
    """
    Векторизованный расчет SuperTrend с использованием Numba.
    """
    high = df['high'].values
    low = df['low'].values
    close = df['close'].values

    # 1. Векторизованный расчет True Range (TR)
    hl = high - low
    hc = np.abs(high - np.roll(close, 1))
    lc = np.abs(low - np.roll(close, 1))
    hc[0] = 0
    lc[0] = 0
    tr = np.maximum(hl, np.maximum(hc, lc))

    # 2. Расчет ATR через экспоненциальное скользящее среднее (Wilder's MA)
    alpha = 1 / period
    atr = pd.Series(tr).ewm(alpha=alpha, adjust=False).mean().values

    # 3. Расчет базовых полос (Basic Bands)
    hl2 = (high + low) / 2
    basic_ub = hl2 + multiplier * atr
    basic_lb = hl2 - multiplier * atr

    # 4. Запуск быстрого JIT-цикла
    supertrend, direction = _calculate_supertrend_fast(high, low, close, basic_ub, basic_lb)

    # Возвращаем результат в виде DataFrame
    result_df = df.copy()
    result_df['SuperTrend'] = supertrend
    result_df['ST_Direction'] = direction
    return result_df

Разбор параметров

  • df: Входной pandas.DataFrame, который обязательно должен содержать колонки high, low и close с ценовыми данными.
  • period: Период усреднения для индикатора ATR (по умолчанию равен 10). Определяет чувствительность индикатора к волатильности рынка.
  • multiplier: Множитель ширины канала ATR (по умолчанию равен 3.0). Чем выше множитель, тем реже индикатор меняет направление тренда.
  • _calculate_supertrend_fast: Вспомогательная функция, декорированная @njit. Она компилирует Python-код в машинные инструкции, убирая накладные расходы интерпретатора при итерации по массивам.

Как запустить

Для запуска скрипта вам понадобятся библиотеки pandas, numpy и numba. Установить их можно стандартной командой:

pip install pandas numpy numba

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

# Генерируем тестовый датасет
np.random.seed(42)
n_bars = 100000
close_prices = np.cumsum(np.random.randn(n_bars)) + 100
high_prices = close_prices + np.random.rand(n_bars) * 2
low_prices = close_prices - np.random.rand(n_bars) * 2

data = pd.DataFrame({
    'high': high_prices,
    'low': low_prices,
    'close': close_prices
})

# Расчет индикатора
import time
start_time = time.time()
result = get_supertrend(data, period=10, multiplier=3.0)
print(f"Время расчета для {n_bars} строк: {time.time() - start_time:.4f} сек")
print(result[['close', 'SuperTrend', 'ST_Direction']].tail())

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

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

Для комплексной оценки результатов полученной торговой системы вам обязательно понадобятся метрики риска. Рекомендуем использовать готовый скрипт для расчета Calmar Ratio и Sortino Ratio на Python.

Также вы можете провести детальный расчет коэффициента Шарпа и просадки в Python с pandas-ta для глубокого анализа полученной кривой доходности (Equity Curve) вашей стратегии.

Оцените статью
FinFluct