112 lines
4.5 KiB
Python
112 lines
4.5 KiB
Python
"""
|
|
BB(10, 2.5) 均值回归策略回测 — 15分钟K线 | 2020-2025 | 200U | 万五手续费 | 90%返佣次日8点到账
|
|
"""
|
|
import sys, time
|
|
sys.stdout.reconfigure(line_buffering=True)
|
|
sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parents[1]))
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
import matplotlib
|
|
matplotlib.use("Agg")
|
|
import matplotlib.pyplot as plt
|
|
from pathlib import Path
|
|
|
|
from strategy.bb_backtest import BBConfig, run_bb_backtest
|
|
from strategy.data_loader import load_klines
|
|
|
|
out_dir = Path(__file__).resolve().parent / "results"
|
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# ============================================================
|
|
# 加载 15 分钟 K 线数据 (2020-2025)
|
|
# ============================================================
|
|
YEARS = list(range(2020, 2026))
|
|
data = {}
|
|
|
|
print("加载 15 分钟 K 线数据 (2020-2025)...")
|
|
t0 = time.time()
|
|
for y in YEARS:
|
|
end_year = y + 1 if y < 2025 else 2026 # 2025 数据到 2026-01-01 前
|
|
df = load_klines('15m', f'{y}-01-01', f'{end_year}-01-01')
|
|
data[y] = df
|
|
print(f" {y}: {len(df):>7,} 条 ({df.index[0]} ~ {df.index[-1]})")
|
|
print(f"数据加载完成 ({time.time()-t0:.1f}s)\n")
|
|
|
|
# ============================================================
|
|
# 配置: 200U | 万五手续费 | 90%返佣次日8点到账
|
|
# ============================================================
|
|
cfg = BBConfig(
|
|
bb_period=10,
|
|
bb_std=2.5,
|
|
leverage=50,
|
|
initial_capital=200.0,
|
|
margin_pct=0.01, # 1% 权益/单
|
|
max_daily_loss=50.0, # 固定值备用
|
|
max_daily_loss_pct=0.05, # 日亏损上限 = 当日起始权益的 5%
|
|
fee_rate=0.0005, # 万五 (0.05%) 开平仓各万五
|
|
rebate_rate=0.0,
|
|
rebate_pct=0.90, # 90% 手续费返佣
|
|
rebate_hour_utc=0, # UTC 0点 = 北京时间早上8点到账
|
|
# 强平
|
|
liq_enabled=True,
|
|
maint_margin_rate=0.005, # 0.5% 维持保证金率
|
|
# 滑点
|
|
slippage_pct=0.0005, # 0.05% 滑点
|
|
# 市场容量限制
|
|
max_notional=500000.0, # 单笔最大名义价值 50万U
|
|
# 加仓
|
|
pyramid_enabled=True,
|
|
pyramid_step=0.01, # 递增加仓
|
|
pyramid_max=3,
|
|
)
|
|
|
|
# ============================================================
|
|
# 运行回测 (滚仓: 200U 起,逐年累加不复位)
|
|
# ============================================================
|
|
df_full = pd.concat([data[y] for y in YEARS])
|
|
r_full = run_bb_backtest(df_full, cfg)
|
|
d_full = r_full.daily_stats
|
|
eq_full = d_full["equity"].astype(float)
|
|
pnl_full = d_full["pnl"].astype(float)
|
|
eq_curve = r_full.equity_curve["equity"].dropna()
|
|
|
|
final_eq = float(eq_full.iloc[-1])
|
|
ret_pct = (final_eq - 200) / 200 * 100
|
|
dd_full = float((eq_full - eq_full.cummax()).min())
|
|
sharpe_full = float(pnl_full.mean() / pnl_full.std()) * np.sqrt(365) if pnl_full.std() > 0 else 0
|
|
win_full = sum(1 for t in r_full.trades if t.net_pnl > 0) / max(len(r_full.trades), 1) * 100
|
|
|
|
print("=" * 100)
|
|
print(" BB(10, 2.5) 15分钟K线 | 200U 起滚仓 | 万五 fee | 90% 返佣 | 含强平+滑点+容量限制")
|
|
print("=" * 100)
|
|
liq_count = sum(1 for t in r_full.trades if t.fee == 0.0 and t.net_pnl < 0)
|
|
print(f" 初始本金: 200U | 最终权益: {final_eq:,.0f}U | 收益率: {ret_pct:+,.1f}%")
|
|
print(f" 交易次数: {len(r_full.trades)} | 胜率: {win_full:.1f}% | 强平次数: {liq_count} | 最大回撤: {dd_full:+,.0f}U")
|
|
print(f" 总手续费: {r_full.total_fee:,.0f} | 总返佣: {r_full.total_rebate:,.0f} | Sharpe: {sharpe_full:.2f}")
|
|
print("-" * 100)
|
|
print(" 年末权益:")
|
|
for y in YEARS:
|
|
mask = eq_curve.index.year == y
|
|
if mask.any():
|
|
yr_eq = float(eq_curve.loc[mask].iloc[-1])
|
|
print(f" {y} 年末: {yr_eq:,.0f}U")
|
|
print("=" * 100)
|
|
|
|
# ============================================================
|
|
# 生成权益曲线图 (对数坐标,滚仓复利)
|
|
# ============================================================
|
|
fig, ax = plt.subplots(1, 1, figsize=(14, 6), dpi=120)
|
|
eq_ser = eq_curve
|
|
days = (eq_ser.index - eq_ser.index[0]).total_seconds() / 86400
|
|
ax.semilogy(days, eq_ser.values.clip(min=1), color="#2563eb", linewidth=1.2)
|
|
ax.axhline(y=200, color="gray", linestyle="--", alpha=0.6)
|
|
ax.set_title("BB(10, 2.5) 15min | 200U | 0.05% fee 90% rebate | 2020-2025", fontsize=12, fontweight="bold")
|
|
ax.set_xlabel("Days")
|
|
ax.set_ylabel("Equity (USDT, log scale)")
|
|
ax.grid(True, alpha=0.3)
|
|
|
|
chart_path = out_dir / "bb_15m_200u_2020_2025.png"
|
|
plt.savefig(chart_path, bbox_inches="tight")
|
|
print(f"\n图表已保存: {chart_path}")
|