Backtest
# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
# --- ПАРАМЕТРЫ СТРАТЕГИИ И БЭКТЕСТА ---
# Эти параметры можно будет менять, чтобы улучшить результат
INITIAL_CAPITAL = 1000.0 # Начальный капитал в USDT для симуляции
RISK_PER_TRADE_PERCENT = 2.0 # Сколько % от капитала мы рискуем в одной сделке
RISK_REWARD_RATIO = 1.5 # Соотношение риска к прибыли (1.5 означает, что тейк-профит в 1.5 раза дальше стоп-лосса)
# Параметры для правил сигнала
PRICE_LOOKBACK_PERIOD = 5 # Сколько свечей назад смотрим для определения минимума
OI_LOOKBACK_PERIOD = 4 # Период для расчета среднего Открытого Интереса
MAX_FUNDING_RATE = 0.0001 # Максимальный фандинг для входа (0.01%)
# ---------------------------------------------------
def run_backtest():
print("--- Запуск исторического тестирования (Бэктеста) ---")
# --- Шаг 1: Загрузка и подготовка данных ---
try:
print("\n[1/4] Загрузка исторических данных из CSV файлов...")
ohlcv_df = pd.read_csv('avax_ohlcv.csv')
open_interest_df = pd.read_csv('AVAXUSDT_open_interest_15min.csv')
funding_df = pd.read_csv('AVAXUSDT_funding_history.csv')
# Приводим все временные метки к единому формату datetime
ohlcv_df['timestamp'] = pd.to_datetime(ohlcv_df['timestamp'])
open_interest_df['timestamp'] = pd.to_datetime(open_interest_df['timestamp'], utc=True)
funding_df['fundingRateTimestamp'] = pd.to_datetime(funding_df['fundingRateTimestamp'], utc=True)
# Агрегируем минутные свечи в 15-минутные
ohlcv_df.set_index('timestamp', inplace=True)
ohlcv_15m_df = ohlcv_df.resample('15T').agg({
'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last', 'volume': 'sum'
}).dropna()
ohlcv_15m_df.index = ohlcv_15m_df.index.tz_localize('UTC')
# Готовим остальные данные
open_interest_df.set_index('timestamp', inplace=True)
funding_df.set_index('fundingRateTimestamp', inplace=True)
funding_df = funding_df[~funding_df.index.duplicated(keep='first')]
# Объединяем все в один датафрейм
df = ohlcv_15m_df.join(open_interest_df['openInterest']).join(funding_df['fundingRate']).ffill().dropna()
print("✅ Данные успешно загружены и объединены.")
except FileNotFoundError as e:
print(f"ОШИБКА: Файл не найден! Убедитесь, что CSV файлы лежат в той же папке, что и скрипт.\n{e}")
return
# --- Шаг 2: Расчет индикаторов и условий ---
print("\n[2/4] Расчет индикаторов и условий для стратегии...")
df['rolling_low'] = df['low'].shift(1).rolling(window=PRICE_LOOKBACK_PERIOD).min()
df['oi_average'] = df['openInterest'].shift(1).rolling(window=OI_LOOKBACK_PERIOD).mean()
# Условие 1: Сбор ликвидности и разворот
condition1 = (df['low'] < df['rolling_low']) & (df['close'] > df['open'])
# Условие 2: Рост Открытого Интереса
condition2 = df['openInterest'] > df['oi_average']
# Условие 3: Нейтральный или медвежий фандинг
condition3 = df['fundingRate'] <= MAX_FUNDING_RATE
df['signal'] = condition1 & condition2 & condition3
print("✅ Условия рассчитаны.")
# --- Шаг 3: Симуляция торгов ---
print("\n[3/4] Запуск симуляции торгов... Это может занять некоторое время.")
trades = []
active_trade = None
for i in range(len(df)):
# Если у нас есть активная сделка, проверяем, не закрылась ли она
if active_trade:
# Проверка на Take Profit
if df['high'].iloc[i] >= active_trade['take_profit']:
active_trade['exit_price'] = active_trade['take_profit']
active_trade['result'] = 'WIN'
trades.append(active_trade)
active_trade = None
continue
# Проверка на Stop Loss
if df['low'].iloc[i] <= active_trade['stop_loss']:
active_trade['exit_price'] = active_trade['stop_loss']
active_trade['result'] = 'LOSS'
trades.append(active_trade)
active_trade = None
continue
# Если сигнала нет или уже есть активная сделка, пропускаем
if not df['signal'].iloc[i] or active_trade:
continue
# Генерируем новую сделку
entry_price = df['close'].iloc[i]
stop_loss = df['low'].iloc[i]
# Пропускаем сделку, если стоп-лосс слишком близко к цене входа
if entry_price <= stop_loss:
continue
risk_per_unit = entry_price - stop_loss
take_profit = entry_price + (risk_per_unit * RISK_REWARD_RATIO)
active_trade = {
'entry_date': df.index[i],
'entry_price': entry_price,
'stop_loss': stop_loss,
'take_profit': take_profit,
'result': None
}
print("✅ Симуляция завершена.")
# --- Шаг 4: Анализ результатов ---
print("\n[4/4] Анализ результатов и формирование отчета...")
if not trades:
print("\n--- ОТЧЕТ ПО БЭКТЕСТУ ---")
print("За весь период не было сгенерировано ни одной сделки по заданным условиям.")
return
wins = len([t for t in trades if t['result'] == 'WIN'])
losses = len([t for t in trades if t['result'] == 'LOSS'])
total_trades = len(trades)
win_rate = (wins / total_trades) * 100 if total_trades > 0 else 0
# Симуляция изменения капитала
capital = INITIAL_CAPITAL
for trade in trades:
risk_amount = capital * (RISK_PER_TRADE_PERCENT / 100)
if trade['result'] == 'WIN':
profit = risk_amount * RISK_REWARD_RATIO
capital += profit
elif trade['result'] == 'LOSS':
capital -= risk_amount
print("\n\n--- 📈 ОТЧЕТ ПО БЭКТЕСТУ 📈 ---")
print(f"Период тестирования: с {df.index[0].date()} по {df.index[-1].date()}")
print("-------------------------------------------------")
print(f"Всего сделок: {total_trades}")
print(f"Прибыльных сделок (Wins): {wins}")
print(f"Убыточных сделок (Losses): {losses}")
print(f"**Винрейт (Win Rate): {win_rate:.2f}%**")
print("-------------------------------------------------")
print(f"Начальный капитал: ${INITIAL_CAPITAL:,.2f}")
print(f"Конечный капитал: ${capital:,.2f}")
net_profit = capital - INITIAL_CAPITAL
net_profit_percent = (net_profit / INITIAL_CAPITAL) * 100
print(f"Чистая прибыль: ${net_profit:,.2f} ({net_profit_percent:.2f}%)")
print("-------------------------------------------------")
# --- Главный блок запуска ---
if name == "__main__":
run_backtest()