? Введение
В мире алготрейдинга многие уверены: «если стратегия показывает хорошие сигналы — значит она прибыльна». Увы, это заблуждение. Чтобы стратегия действительно была рабочей, нужно грамотно провести бэктест — не просто «посчитать винрейт», а рассчитать ключевые метрики: Profit Factor, Sharpe Ratio, Max Drawdown, и лишь потом делать выводы.
В этой статье я покажу, как протестировать стратегию по реальным историческим данным, сохранить сигналы, симулировать сделки, рассчитать метрики — и понять, стоит ли стратегия того, чтобы торговать ей на бирже.
Все примеры — на Python. В предыдущей статье я показывал написание бота и бектест кода, который просто выдаёт сухие сделки и реализованную прибыль в %. Однако существует много разных параметров и переменных стратегии, без которых ее использование обычно убыточно.
? 1. Получаем исторические данные и сигналы
Сначала загружаем данные и рассчитываем сигналы стратегии. Я не буду сейчас подробно описывать конкретную стратегию бота, функции и индикаторы. Остановимся на логике именно бектеста и анализе данных.
Начнём с импорта, инициализируем клиент биржи:
from binance.client import Client
import pandas as pd
client = Client()
Теперь нам нужно подгрузить исторические свечи, чтобы понять как стратегия отрабатывала на истории. В нашем бектесте будем анализировать 100000 5м свечей. Это около 350 дней, этого хватит для тестирования скальп стратегии.
Так как бинанс позволяет загружать до 1500 свечей за раз, то будем подгружать и добавлять новые свечи в массив постепенно.
def fetch_klines_paged(symbol='BTCUSDT', interval='5m', total_bars=100000, client=None):
if client is None:
client = Client()
limit = 1000
data = []
end_time = None # самый последний бар (новейшая точка)
while len(data) < total_bars:
bars_to_fetch = min(limit, total_bars - len(data))
try:
klines = client.futures_klines(
symbol=symbol,
interval=interval,
limit=bars_to_fetch,
endTime=end_time
)
except Exception as e:
print("Ошибка Binance API:", e)
break
if not klines:
break
data = klines + data # prepend! — старые свечи добавляем в начало
end_time = klines[0][0] - 1 # сдвиг назад по времени
time.sleep(0.2)
df = pd.DataFrame(data, columns=[
'timestamp', 'open', 'high', 'low', 'close', 'volume',
'close_time', 'quote_asset_volume', 'number_of_trades',
'taker_buy_base', 'taker_buy_quote', 'ignore'
])
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df[['open','high','low','close','volume']] = df[['open','high','low','close','volume']].astype(float)
df = df.drop_duplicates('timestamp')
df = df.sort_values('timestamp').reset_index(drop=True)
return df
Функция вернет нам dataframe со всеми нужными свечами, с ним мы и будем работать.
2. Создаём условия для входа в позицию и начинаем бектест
Начнём с создания условий для входа:
def check_long_condition(row):
return (
row['CSI'] > 0 and
row['CSI'] > row['CSI_prev'] and
isinstance(row['cluster_id'], str) and row['cluster_id'].startswith('bull') and
row['close'] > row['ema_fast'] > row['ema_slow']
)
def check_short_condition(row):
return (
row['CSI'] < 0 and
row['CSI'] < row['CSI_prev'] and
isinstance(row['cluster_id'], str) and row['cluster_id'].startswith('bear') and
row['close'] < row['ema_fast'] < row['ema_slow']
)
Они могут быть любые, я для примера вставил часть условий для своего бота.
Перейдём к основной логике запуска. Для начала получим наш df (массив 100000 свечей) и добавим на него наши нужные индикаторы:
if __name__ == "__main__":
client = Client()
df = fetch_klines_paged('BTCUSDT', '5m', total_bars=100000, client=client)
print(f"Получено свечей: {len(df)}")
df = compute_indicators(df) #функция вида df['RS'] = 1+rs ; return df
3. Добавляем нашу логику к свечам
Теперь нужно рассмотреть все свечи и проверять на них наши сигналы. Разделим long и short сигналы в разные блоки, чтобы потом было удобнее анализировать статистику. Также сохраним все наши сделки в .csv таблицу. При необходимости можно будет сделать анализ конкретных сигналов.
df['long_signal'] = df.apply(check_long_condition, axis=1)
df['short_signal'] = df.apply(check_short_condition, axis=1)
long_signals = df[df['long_signal']]
short_signals = df[df['short_signal']]
# ? Сохраняем сделки в CSV
long_signals.to_csv('long_signals.csv', sep=';', index=False)
short_signals.to_csv('short_signals.csv', sep=';', index=False)
df.to_csv('all_data.csv', sep=';', index=False)
? 4. Симулируем сделки и считаем прибыль
Теперь нужно понять: какую прибыль дали сигналы. Для простоты мы входим по цене close на сигнальной свече и выходим через 3 свечи (т.е. 15 минут спустя). Выход через 15минут был выведен на основе большого опыта работы через 5м сигналы путём смены интервала через циклы.
Для начала напишем функцию счёта профита:
def calculate_profits(df, signal_col='long_signal', direction='long', exit_after=3):
trades = []
for i in range(len(df) - exit_after):
if df.iloc[i][signal_col]:
entry_price = df.iloc[i]['close']
exit_price = df.iloc[i + exit_after]['close']
if direction == 'long':
profit = exit_price - entry_price
profit_pct = (exit_price / entry_price - 1) * 100
else:
profit = entry_price - exit_price
profit_pct = (entry_price / exit_price - 1) * 100
trades.append({
'entry_time': df.iloc[i]['timestamp'],
'exit_time': df.iloc[i + exit_after]['timestamp'],
'entry_price': entry_price,
'exit_price': exit_price,
'profit': profit,
'profit_%': profit_pct
'close': entry_price # ← добавляем сюда цену входа
})
return pd.DataFrame(trades)
И применим это к нашим сигналам в основном блоке:
long_trades = calculate_profits(df, signal_col='long_signal', direction='long', exit_after=3)
short_trades = calculate_profits(df, signal_col='short_signal', direction='short', exit_after=3)
long_trades.to_csv('long_trades.csv', sep=';', index=False)
short_trades.to_csv('short_trades.csv', sep=';', index=False)
? 5. Анализ метрик стратегии
И вот теперь — самое важное: какие метрики мы можем рассчитать и что они значат?
✅ Win Rate
Процент прибыльных сделок: насколько стратегия вообще угадывает?
win_rate = len(df[df['profit'] > 0]) / len(df) * 100
✅ Profit Factor
Сумма всей прибыли / сумма всех убытков. Главное — выше 1.
profit_factor = df[df['profit'] > 0]['profit'].sum() / abs(df[df['profit'] < 0]['profit'].sum())
✅ Sharpe Ratio
Стабильность стратегии: насколько равномерно зарабатываем?
sharpe = df['profit'].mean() / df['profit'].std()
✅ Max Drawdown
Максимальное падение капитала — нужен для оценки риска.
equity = df['profit'].cumsum()
rolling_max = equity.cummax()
drawdown = equity - rolling_max
max_dd = drawdown.min()
Также добавим классическую статистику в виде дельты и профита:
total_profit = trades_df['profit'].sum()
avg_price = trades_df['close'].mean()
delta = total_profit / avg_price if avg_price != 0 else float('nan')
✅ Всё вместе
Функция анализа:
def analyze_trades(trades_df):
if trades_df.empty:
print("Нет сделок для анализа.")
return
win_trades = trades_df[trades_df['profit'] > 0]
loss_trades = trades_df[trades_df['profit'] <= 0]
win_rate = len(win_trades) / len(trades_df) * 100
profit_factor = win_trades['profit'].sum() / abs(loss_trades['profit'].sum()) if not loss_trades.empty else float('inf')
# Sharpe Ratio (annualized)
daily_returns = trades_df['profit']
mean_return = daily_returns.mean()
std_return = daily_returns.std()
sharpe_daily = mean_return / std_return if std_return != 0 else 0
sharpe_annual = sharpe_daily * np.sqrt(252)
# Equity и просадка
equity_curve = trades_df['profit'].cumsum()
rolling_max = equity_curve.cummax()
drawdown = equity_curve - rolling_max
max_drawdown = drawdown.min()
# Общий профит и дельта
total_profit = trades_df['profit'].sum()
avg_price = trades_df['close'].mean()
delta = total_profit / avg_price if avg_price != 0 else float('nan')
print("\n? Результаты анализа сделок:")
print(f"Всего сделок: {len(trades_df)}")
print(f"Win Rate: {win_rate:.2f}%")
print(f"Profit Factor: {profit_factor:.2f}")
print(f"Sharpe Ratio (annualized): {sharpe_annual:.2f}")
print(f"Max Drawdown: {max_drawdown:.2f}")
print(f"? Общий профит: {total_profit:.2f}")
print(f"⚖️ Дельта (profit / avg BTC): {delta:.4f}")
Ну и теперь вызовем нашу функцию:
print("LONG TRADES:")
analyze_trades(long_trades)
print("\nSHORT TRADES:")
analyze_trades(short_trades)
Всю логику можно оформить как модуль, а сами сигналы и сделки анализировать и визуализировать, например, в Jupyter, Excel или Python-дашборде.
Давайте теперь посмотрим какие данные я получил проанализировав моего бота:
? Анализ лонг-сделок:
? Результаты анализа сделок:
Всего сделок: 417
Win Rate: 76.98%
Profit Factor: 6.89
Sharpe Ratio (annualized): 3.13
Max Drawdown: -1313.60
? Общий профит: 98254.50
⚖️ Дельта (profit / avg BTC): 1.1034
? Анализ шорт-сделок:
? Результаты анализа сделок:
Всего сделок: 336
Win Rate: 75.00%
Profit Factor: 8.02
Sharpe Ratio (annualized): 3.09
Max Drawdown: -1442.50
? Общий профит: 86625.40
⚖️ Дельта (profit / avg BTC): 0.9710
Всего сделок: 753 (417 лонгов + 336 шортов)
Разбор показателей
1. Win Rate: качество сигналов
Win Rate около 75% — очень высокий показатель. Это обеспечивается частотой сделок и маленьким временем удержания позиции. Выход на одну позицию меньше, но и винрейт очень высок. Однако высокая точность входов здесь достигается благодаря тщательному фильтрованию сигналов и вероятно жёсткому контролю риска.
2. Profit Factor (PF): соотношение прибыли к убыткам
PF = 6.89 (лонг) и 8.02 (шорт) — очень высокие значения. В индустрии PF > 2 уже считается хорошим. Значение около 7–8 говорит о том, что суммарный профит почти в 7–8 раз превышает убытки.
Вывод: Выдерживается дисциплина по стоп-лоссам и грамотный выбор целей для тейк-профитов.
3. Sharpe Ratio: риск и доходность
Sharpe Ratio около 3— это очень хороший результат, обычно фондовые стратегии имеют значения 1–2, а 3–4 считаются очень хорошими. Высокий Sharpe говорит о низкой волатильности дохода и стабильности.
Что важно: Для криптовалютного рынка с высокой волатильностью это крайне позитивный сигнал, что стратегия адекватно управляет рисками.
4. Max Drawdown: максимальное проседание капитала
Дровдаун в районе 1300–1400 условных единиц. Это менее 2% чистого движения на фиксированную позицию, тоесть 20% на всю позицию с плечом. (Т.к. средняя цена биткойна около 80к). Это отличный показатель для этой тс.
Общий профит и дельта
Общий профит порядка 98 000 для лонга и 86 000 для шорта — отличные результаты.
Дельта (profit / avg BTC) около 1 для каждого направлению — говорит о том, что на каждую среднюю единицу базового актива стратегия приносит примерно такую же сумму прибыли, что свидетельствует о высокой доходности позиции. Но не надо забывать, что прибыль идёт на полную позицию. Поэтому вложив 100$ выход будет 1000$ (на байбит фикс. плечо 10х).
Комиссии
Часть важность коммисий и проскальзывания опускается при анализе статистики. Но в HTF и скальп стратегии без этого никуда. Покажу анализ нашей стратегии:
? Условия:
Входной капитал: $1,000
Плечо: 10x → значит, ты торгуешь как будто бы $10,000
Комиссия на сделку (открытие + закрытие): 0.1% = 0.001
Сделок всего: 753
? Расчёт:
1. Объём одной сделки (с плечом):
1000*10 = 10000 USDT
2. Комиссия с одной сделки:
10,000×0.1% = 10$
3. Общая комиссия за 753 сделки:
10×753=7530 USDT
Так что видим что комиссионные сьели достаточно сильную часть прибыли. От этого не уйти, к сожалению, но всё же даже с учётом коммиссий видим хороший результат - 200% прибыли, т.е. 20000$-7530$=12470$ прибыли за год со вложенных всего 1000$ на позицию. (т.е. на счёте нужно иметь хотя бы чуть более 2к$, так как позиции редко, но бывает, что открываются подряд)
Заключение
Разобрали как рассчитать все необходимые показатели для понимания эффективность торговых алгоритмических систем.
Представленная торговая система демонстрирует впечатляющие показатели — высокие Win Rate, Profit Factor и Sharpe Ratio, а также управляемый Max Drawdown. Это результат продуманного алгоритма, который успешно балансирует между агрессивной доходностью и контролем риска.
Без аналитики полученной после бектеста статистика запуск ботов по трейдингу - чистое казино. Используйте стратегии с умом и всегда проводите тщательное тестирование.
Комментарии (12)
nikolz
07.08.2025 22:44Для простоты мы входим по цене close на сигнальной свече и выходим через 3 свечи (т.е. 15 минут спустя). Выход через 15минут был выведен на основе большого опыта работы через 5м сигналы путём смены интервала через циклы.
В этом подходе заложена систематическая ошибка высокой прибыли на истории и слив депозита в реальных торгах.
Проблема в том, что цена close - это цена последней сделки в свече. Т е в таком алгоритме совершается сделка по цене, которая была в прошлом и в момент выставления заявки предложений по такой цене уже нет.
Говорят, что такой алгоритм торговли заглядывает в будущее.
Чтобы нивелировать эту ошибку необходимо совершать сделки на следующих свечах после формирования сигнала совершения сделки. В итоге результат будет скромнее.
Другой ошибкой является реинвестирование прибыли в следующие сделки без учета ликвидности (глубины) рынка.
В результате получают прибыль в тысячи процентов и на истории превращают 100 долларов в миллион, что далеко от реальности.
alyaskatm
07.08.2025 22:44По close не согласен, это же бэктест. Как иначе его проводить, там все свечи - история. Ну на btc ликвидности хватит с головой, а за остальное автор поста и не говорил.
nikolz
07.08.2025 22:44Делаю так: Робот создает сигнал на закрытии свечи, а исполнение этого сигнала, т е покупка или продажа,на открытии следующей свечи. Для примера картинка Сбербанк тайм 30 минут (первое число -% прибыли, второе -state).
Ликвидность не на всем рынке, а в момент выставления заявки по цене заявки.
negrbluad Автор
07.08.2025 22:44Интересный момент, насчёт close вы абсолютно правы. Но я делал ручную проверку в xlsx таблицах через формулы, где заход осуществляется со следующей свечи, там результат будет отличаться менее чем на полпроцента, бывает даже в лучшую сторону на маленьких периодах)
О реинвестировании речи не было, ну а реализация глубины рынка конечно интересна и возможно, но не обязательна везде.nikolz
07.08.2025 22:44Существенно отличаться будет на утренних гэпах и резком движении рынка. Кроме того, есть внутридневные свечи с гэпом. Они часто бывают в точках разворота.
Есть еще другие моменты, которые позволяют на истории практически всегда получить положительный результат.
negrbluad Автор
07.08.2025 22:44Тут крипта, нету гэпов практически.
Если речь про фондовый рынок тогда да. Но я боюсь что конкретно этот бот на российской фонде будет плохо работать.nikolz
07.08.2025 22:44Да, я согласен, что на крипте иначе, чем на акциях. Я криптой не торгую. Но в любом случае, такое тестирование эквивалентно проведению некоторой линии аппроксимации цены через точки на графике цен, а это далеко от реальной торговли.
negrbluad Автор
07.08.2025 22:44конечно)
тесты всегда это в любом случае теория. Я тут например не учитывал проскальзывания, о чём написал тоже. А они могут кушать понемногу всё равно.
Sergio1155
07.08.2025 22:44Я так понимаю, что это про торговлю фьючерсами. Если это так, то второй вопрос-это торговля Р2Р, т.е. это сделки с физлицом? К сожалению, по опыту знакомого, это приводит к блокировке банковских карт и счетов, просто вопрос времени. К сожалению, на байбите 90 процентов трейдеров это мошенники. Возможно я в чем то не прав, пока активно интересуюсь темой но перспектив торговли на Байбите пока не вижу.
negrbluad Автор
07.08.2025 22:44байбит лишь биржа, зачем на ней использовать p2p?
можно купить крипту безопасно через оффлайн обменники, в москве таких очень много. За наличку отправят на нужный счёт без вопросов.
Brainphill
Занятное чтиво, не очевидна только защита от повторных сделок при открытых имеющихся. Так же нужно иметь ввиду, что на запилах, цена может отойти от индикатора несколько раз подряд, отсюда вытекает важный момент - ни один трейдер не заходит на размер депозита, да ещё и с плечами)) ещё важный момент, это выбор таймфрейма, так на 1-5 и даже 15 минутах роль индикаторов ниже, а на более старших - редкие сделки и как правило не возможность использовать значимые плечи.
Ещё хотел подсветить, что оценка за малый промежуток времени, советую поработать в направлении по выгрузке большого исторического массива в несколько лет, когда в массиве много разных циклов падений и роста, там индикаторы и симуляции совершенно не такие прикольные числа рисуют )
В целом большое спасибо за статью, круто читать автора, который размышляет и пишет код в похожем направлении. С удовольствием почитаю продолжение.
negrbluad Автор
да, конечно. Но тут есть хороший контроль риска и речь в конкретном примере о 5м тф.
А что насчёт оценки за малый промежуток? Для 5м стратегии год тестов это хороший период, тем более что рынок был откровенно говоря хреновый за это время) Ну а насчёт циклов согласен, моментами действительно статистика падает. Но показатели всё еще неплохие.
Благодарю за критику и похвалу)