Представьте ситуацию: вы решили проверить новую стратегию на исторических данных. Захотелось понять, насколько она эффективна, прежде чем запускать её в реальную торговлю. Но тут возникает вопрос: с чего начать?
Вот конкретный случай: в феврале 2024 года мне захотелось протестировать простую стратегию покупки при пробое уровня поддержки. Решил использовать pandas и его возможности по работе с временными рядами. Первая мысль была простой: открыть данные, нарисовать графики и посмотреть, где цена пробила уровень.
Но дальше начались сложности. Оказывается, в pandas есть нюансы, которые могут легко сбить новичка с толку. Например, проблема временных меток: датафреймы часто содержат индексы типа Timestamp, а не обычные числа. Ещё сложнее стало, когда пришлось работать с индексированными данными и вычислять прошлые значения.
Что делать, если вы хотите провести полноценный бэктест, но не знаете, с какого конца подойти к pandas? В следующей части расскажу про основные шаги и подводные камни, которые вас ждут.
Подготовка данных и первый шаг к бэктесту
Теперь перейдём непосредственно к практике. Допустим, у нас уже есть исторические котировки акций компании ABC за последние три года. Данные представлены в виде датафрейма df, который содержит столбцы 'Date', 'Open', 'High', 'Low', 'Close' и 'Volume'.
Для начала нам нужно привести временные метки к нужному формату. Обычно в pandas они хранятся в формате Timestamp, что удобно для работы с датами, но усложняет расчеты прошлых значений. Чтобы упростить дальнейшие вычисления, преобразуем timestamp в числовой формат, соответствующий дням:
df['Date'] = pd.to_datetime(df['Date'])
df.set_index('Date', inplace=True)
df.index = (df.index - df.index.min()).days
Теперь наши данные готовы к использованию. Следующий важный этап – определение уровней поддержки и сопротивления. Предположим, наша стратегия основана на пробое уровня поддержки. Для этого нам потребуется рассчитать минимальные цены за определённый период, например, за последние 20 дней.
support_level = df['Low'].rolling(window=20).min()
Таким образом, мы получаем серию минимальных цен за каждый день, начиная с 20-го дня назад. Теперь можно сравнить текущие цены с рассчитанными значениями поддержки и принимать торговые решения.
Однако стоит учесть один нюанс: иногда минимальные цены могут быть одинаковыми в течение нескольких дней подряд. Это создаёт проблему при попытке определить точные дни пробоя. Чтобы избежать таких ситуаций, добавим небольшой буфер безопасности:
buffer = 0.01 * support_level
df['Support_Buffer'] = support_level - buffer
Эти простые шаги помогут вам подготовить данные и заложить основу для дальнейших расчётов. В следующем разделе поговорим о том, как реализовать саму стратегию и отслеживать результаты бэктеста.
Проверяем стратегию на реальных данных: практический пример
Допустим, мы хотим протестировать нашу стратегию покупки при пробое уровня поддержки на реальных данных. Для этого возьмём исторические котировки акций Apple (AAPL) за период с января 2020 года по декабрь 2022 года. Эти данные можно свободно скачать с Yahoo Finance или другого сервиса.
Наша стратегия будет выглядеть так:
- Найти уровни поддержки и сопротивления на графике цены.
- Когда цена пробивает уровень поддержки снизу вверх, открывать длинную позицию.
- Закрывать позиции через фиксированный процентный прирост (+5%).
Шаг 1: Загрузка и подготовка данных
Сначала загружаем исторические котировки с Yahoo Finance:
import yfinance as yf
import pandas as pd
## Загружаем данные за последние два года
data = yf.download('AAPL', start='2020-01-01', end='2022-12-31')
## Проверяем структуру данных
print(data.head())
Получаем датафрейм с колонками ['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume']. Теперь наша задача – найти уровни поддержки и сопротивления.
Шаг 2: Определение уровней поддержки и сопротивления
Уровни поддержки и сопротивления определяются как локальные минимумы и максимумы ценового графика. Для этого используем скользящие средние и стандартные отклонения:
## Рассчитываем простые скользящие средние
ma10 = data['Close'].rolling(window=10).mean()
ma20 = data['Close'].rolling(window=20).mean()
## Определяем уровни поддержки и сопротивления
support = ma10 - 2 * data['Close'].std()
resistance = ma20 + 2 * data['Close'].std()
## Добавляем новые колонки в датафрейм
data['Support'] = support
data['Resistance'] = resistance
Теперь у нас есть дополнительные колонки 'Support' и 'Resistance', которые помогут определить моменты пробоя.
Шаг 3: Тестирование стратегии
Реализуем саму стратегию покупки при пробое уровня поддержки:
def buy_on_breakout(df):
positions = []
for i in range(len(df)):
if df['Close'][i] > df['Support'][i]:
positions.append((df.index[i], 'Buy'))
elif df['Close'][i] < df['Support'][i]:
positions.pop() if len(positions) > 0 else None
return positions
positions = buy_on_breakout(data)
Этот фрагмент кода находит все случаи, когда цена пробивала уровень поддержки и открывала длинные позиции. Далее можно рассчитать прибыльность стратегии, используя полученные позиции.
Таким образом, с помощью простых инструментов pandas и базовых знаний технического анализа мы можем эффективно тестировать свои торговые стратегии даже на небольших объёмах данных.
Создание индикаторов и фильтрация сигналов
На предыдущем этапе мы подготовили наши данные и привели временные метки к нужному виду. Теперь пришло время добавить технические индикаторы и создать фильтры для наших торговых сигналов.
Допустим, наша стратегия предполагает вход в позицию при пробое уровня поддержки. Для этого можно использовать индикатор EMA (экспоненциальное скользящее среднее). Он поможет определить направление тренда и момент пробоя.
Вот пример кода для добавления EMA-индикатора в наш датафрейм:
df['ema_50'] = pd.Series(df['Close']).ewm(span=50, min_periods=50).mean()
Здесь мы рассчитали экспоненциальное скользящее среднее с периодом 50 баров. После этого добавим условие для фильтрации сигналов:
df['signal'] = np.where(df['Close'] > df['ema_50'], 1, 0)
Этот код создаёт новый столбец 'signal', значение которого равно 1, если текущая цена выше EMA, и 0 в противном случае. Таким образом, мы получаем простые сигналы для входа в сделку.
Далее необходимо выбрать только те дни, когда произошло событие пробоя. Это можно сделать следующим образом:
df_signal = df[(df['signal'].shift(1) 1)]
Здесь мы используем сдвиг значений на одну строку назад, чтобы найти моменты, когда произошёл переход сигнала с нуля на единицу – именно такие ситуации соответствуют нашим торговым сигналам.
Таким образом, после выполнения этих шагов у нас остаётся датафрейм df_signal, содержащий только важные торговые события, подходящие под нашу стратегию.
Следующий этап будет посвящен созданию функции для расчета доходности стратегии и оценке её эффективности.
Что я вынес из этой истории
Начать хотелось бы с простого наблюдения: работа с pandas требует внимания к деталям. Когда тестируешь стратегию вручную, кажется, что все просто. Но стоит чуть глубже погрузиться в процесс, и понимаешь, сколько подводных камней поджидает тебя на каждом шаге.
Например, простая задача вроде "определения прошлых цен" превращается в головоломку, если неправильно обработать временные метки. Пришлось потратить несколько дней, чтобы понять, почему мои результаты постоянно расходятся с реальными данными. А ведь всего лишь нужно было правильно конвертировать timestamps в числовой формат.
Ещё одна важная вещь, которую я осознал – важность тщательной проверки каждого шага перед запуском полноценной торговли. Если не проверять свои данные и алгоритмы заранее, можно потерять деньги на мелочах, которых даже не заметишь.
Вывод
- 🎣 Тестируй стратегию постепенно Не бросайся сразу торговать крупными суммами. Сначала проверь свою идею на небольших объёмах и минимальных рисках.
- ⚠ ️ Не забывай про тестирование на реальных данных Даже самая красивая теория может провалиться на практике. Используй реальные исторические данные, чтобы избежать неприятных сюрпризов.
- ⚠️ ️ Всегда проверяй корректность данных перед началом бэктеста. Ошибки в ценах или объемах могут привести к неверным результатам и ложному чувству уверенности в своей стратегии.