Files
codex_jxs_code/backtest_feb2025.py
2026-02-28 13:21:58 +08:00

224 lines
8.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
BB策略回测脚本 - 2025年2月份
参数: 逐仓 200U 1% 100倍 万五手续费 90%返佣
"""
import sys
from pathlib import Path
import pandas as pd
import numpy as np
from datetime import datetime
sys.path.insert(0, str(Path(__file__).resolve().parent))
from strategy.bb_backtest import run_bb_backtest, BBConfig
from strategy.data_loader import load_klines
def main():
# 加载2025年2月份的5分钟K线数据
print("=" * 70)
print("加载 2025年2月 5分钟K线数据...")
print("=" * 70)
try:
df = load_klines(
period='5m',
start_date='2025-02-01',
end_date='2025-03-01',
tz='Asia/Shanghai',
source='bitmart'
)
print(f"✓ 数据加载成功: {len(df)} 根K线")
print(f" 时间范围: {df.index[0]} ~ {df.index[-1]}")
except Exception as e:
print(f"✗ 数据加载失败: {e}")
return
# 配置回测参数
cfg = BBConfig(
# 布林带参数 (来自 bb_trade.py)
bb_period=10,
bb_std=2.5,
# 仓位管理
initial_capital=200.0, # 200U本金
margin_pct=0.01, # 每次开仓用权益的1%
leverage=100.0, # 100倍杠杆
# 逐仓配置
cross_margin=False, # 逐仓模式
maint_margin_rate=0.005, # 0.5% 维持保证金率
# 手续费和返佣
fee_rate=0.0005, # 万五手续费
rebate_rate=0.0, # 无即时返佣
rebate_pct=0.90, # 次日返佣90%
rebate_hour_utc=0, # UTC 0 点 (北京时间8点)
# 滑点
slippage_pct=0.0005, # 0.05% 滑点
# 成交模式
fill_at_close=False, # 用触轨价成交(理想化)False=触轨价True=收盘价
# 风控
max_daily_loss=50.0, # 日最大亏损50U(不启用百分比限制)
max_daily_loss_pct=0.0, # 不启用百分比
# 破产模型
liq_enabled=True, # 启用强平
# 加仓配置 (递增模式)
pyramid_enabled=True,
pyramid_step=0.01, # 1%增量: 开1%, 加2%, 加3%, 加4%
pyramid_max=3, # 最多加3次
)
print("\n" + "=" * 70)
print("回测参数配置")
print("=" * 70)
print(f"初始资本: {cfg.initial_capital} USDT")
print(f"杠杆: {cfg.leverage}x {'逐仓' if not cfg.cross_margin else '全仓'}")
print(f"开仓比例: {cfg.margin_pct:.0%}(递增: {cfg.pyramid_step:.0%}/次, 最多{cfg.pyramid_max}次)")
print(f"手续费: {cfg.fee_rate:.0%}")
print(f"返佣: {cfg.rebate_pct:.0%} 次日 UTC-0 {cfg.rebate_hour_utc}")
print(f"布林带: BB({cfg.bb_period}, {cfg.bb_std})")
print(f"滑点: {cfg.slippage_pct:.0%}")
print(f"日亏损限制: {cfg.max_daily_loss} USDT")
print(f"强平: {'启用' if cfg.liq_enabled else '禁用'}")
# 运行回测
print("\n" + "=" * 70)
print("运行回测...")
print("=" * 70)
try:
result = run_bb_backtest(df, cfg)
# 打印详细结果
print_backtest_results(result)
# 保存结果
output_dir = Path(__file__).parent / "strategy" / "results"
output_dir.mkdir(parents=True, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
# 保存交易明细
trades_df = pd.DataFrame([
{
"entry_time": t.entry_time,
"exit_time": t.exit_time,
"side": t.side,
"entry_price": f"{t.entry_price:.2f}",
"exit_price": f"{t.exit_price:.2f}",
"qty": f"{t.qty:.4f}",
"margin": f"{t.margin:.2f}",
"gross_pnl": f"{t.gross_pnl:.2f}",
"fee": f"{t.fee:.2f}",
"net_pnl": f"{t.net_pnl:.2f}",
}
for t in result.trades
])
trades_file = output_dir / f"bb_feb2025_trades_{timestamp}.csv"
trades_df.to_csv(trades_file, index=False, encoding="utf-8-sig")
print(f"\n✓ 交易明细已保存: {trades_file}")
# 保存日行情
daily_file = output_dir / f"bb_feb2025_daily_{timestamp}.csv"
result.daily_stats.to_csv(daily_file, encoding="utf-8-sig")
print(f"✓ 日行情已保存: {daily_file}")
# 保存完整权益曲线
equity_file = output_dir / f"bb_feb2025_equity_{timestamp}.csv"
result.equity_curve.to_csv(equity_file, encoding="utf-8-sig")
print(f"✓ 权益曲线已保存: {equity_file}")
except Exception as e:
print(f"✗ 回测失败: {e}")
import traceback
traceback.print_exc()
def print_backtest_results(result):
"""打印回测结果统计"""
print("\n" + "=" * 70)
print("回测结果汇总")
print("=" * 70)
# 基础统计
trades = result.trades
equity = result.equity_curve
daily = result.daily_stats
config = result.config
initial_eq = config.initial_capital
final_eq = equity["equity"].iloc[-1]
total_pnl = final_eq - initial_eq
pnl_pct = (total_pnl / initial_eq) * 100
print(f"\n📊 核心指标")
print(f" 初始权益: {initial_eq:.2f} USDT")
print(f" 最终权益: {final_eq:.2f} USDT")
print(f" 总损益: {total_pnl:+.2f} USDT ({pnl_pct:+.2f}%)")
print(f" 最大权益: {equity['equity'].max():.2f} USDT")
print(f" 最小权益: {equity['equity'].min():.2f} USDT")
if len(trades) > 0:
print(f"\n📈 交易统计")
print(f" 总交易数: {len(trades)}")
long_trades = [t for t in trades if t.side == "long"]
short_trades = [t for t in trades if t.side == "short"]
print(f" 多头交易: {len(long_trades)}")
print(f" 空头交易: {len(short_trades)}")
# 胜率
win_trades = [t for t in trades if t.net_pnl > 0]
loss_trades = [t for t in trades if t.net_pnl < 0]
print(f" 盈利交易: {len(win_trades)} 笔 ({len(win_trades)/len(trades)*100:.1f}%)")
print(f" 亏损交易: {len(loss_trades)} 笔 ({len(loss_trades)/len(trades)*100:.1f}%)")
if len(win_trades) > 0:
avg_win = sum(t.net_pnl for t in win_trades) / len(win_trades)
print(f" 平均盈利: {avg_win:+.2f} USDT")
if len(loss_trades) > 0:
avg_loss = sum(t.net_pnl for t in loss_trades) / len(loss_trades)
print(f" 平均亏损: {avg_loss:+.2f} USDT")
# 风险指标
total_net_pnl = sum(t.net_pnl for t in trades)
max_loss_trade = min(trades, key=lambda t: t.net_pnl)
max_win_trade = max(trades, key=lambda t: t.net_pnl)
print(f"\n💰 盈亏规模")
print(f" 总手续费: {result.total_fee:+.2f} USDT")
print(f" 总返佣: {result.total_rebate:+.2f} USDT")
print(f" 交易净损益: {total_net_pnl:+.2f} USDT")
print(f" 单笔最大亏损: {max_loss_trade.net_pnl:+.2f} USDT")
print(f" 单笔最大盈利: {max_win_trade.net_pnl:+.2f} USDT")
# 日行情统计
if len(daily) > 0:
print(f"\n📅 日度统计")
print(f" 交易天数: {len(daily)}")
daily_wins = len(daily[daily["pnl"] > 0])
daily_loss = len(daily[daily["pnl"] < 0])
print(f" 日赢利天数: {daily_wins}")
print(f" 日亏损天数: {daily_loss}")
max_dd = (equity["equity"] / equity["equity"].expanding().max() - 1).min()
print(f" 最大回撤: {max_dd*100:.2f}%")
if max_dd != 0:
print(f" 回撤恢复指标: {total_pnl / (max_dd * initial_eq):.2f}")
print("\n" + "=" * 70)
if __name__ == "__main__":
main()