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) вашей стратегии.




