"""Bitget 原生回测基线:影子交易器要对照的那个数。 aggregate_robustness 证明总体期望在数据扰动下稳定,但逐笔清单不可复现。 所以滑点不能逐笔对照 Binance 回测,只能对照「同一 venue 上的总体毛均收益」。 这一步就是把那个数算出来。 跑法与 Binance 侧完全对齐:同一时间窗、同样 300k 根 1m、同一套 lib/ 代码、 同样 SL/TP/MAX_BARS = 1.5/3.0/48 与 entry_delay=1,只换数据源。 特别关注 SOL:Bitget 的 tick 是 0.001、Binance 是 0.01,粗 10 倍会让 `close > high[j-1]` 大量平局不触发。Bitget 上信号多出 74%,多出来的是否 同样赚钱,只有跑一遍才知道——这不是随机扰动,是系统性差异。 输出 out/bitget_baseline.csv。 """ from __future__ import annotations import argparse import os import sys import time import warnings from pathlib import Path import numpy as np import pandas as pd warnings.filterwarnings("ignore") for v in ("OMP_NUM_THREADS", "OPENBLAS_NUM_THREADS", "MKL_NUM_THREADS"): os.environ.setdefault(v, "1") HERE = Path(__file__).resolve().parent RESEARCH = HERE.parent sys.path.insert(0, str(RESEARCH)) sys.path.insert(0, str(RESEARCH.parent)) sys.path.insert(0, str(HERE)) pd.set_option("display.width", 260) from aggregate_robustness import run_once, stats # noqa: E402 from venue_parity import fetch_bitget # noqa: E402 # Binance 侧在同一窗口的实测值,来自 out/aggregate_robustness_*.csv 的 0bp 档 BINANCE_REF = {"BTC": 6.11, "ETH": 12.31, "SOL": 10.01} def main() -> None: ap = argparse.ArgumentParser() ap.add_argument("--symbols", default="BTC,ETH,SOL") ap.add_argument("--days", type=int, default=210, help="与 Binance 侧的 300k 根(208 天)对齐") ap.add_argument("--refresh", action="store_true") ap.add_argument("--tag", default="", help="产物文件名后缀,避免多次调用互相覆盖") args = ap.parse_args() from lib.data import load_local syms = [s.strip() for s in args.symbols.split(",")] print(f"[Bitget 原生基线] {syms} · 近 {args.days} 天 1m\n", flush=True) rows = [] for sym in syms: t0 = time.perf_counter() bg_l = fetch_bitget(sym, "1m", args.days, args.refresh) bg_h = fetch_bitget(sym, "5m", args.days, args.refresh) print(f"── {sym} {len(bg_l)} 根 1m / {len(bg_h)} 根 5m " f"{bg_l['date'].iloc[0]:%Y-%m-%d} ~ {bg_l['date'].iloc[-1]:%Y-%m-%d}" f" [拉取 {time.perf_counter() - t0:.0f}s]", flush=True) t1 = time.perf_counter() tr = run_once(bg_l, bg_h) s = stats(tr) rows.append({"品种": sym, "venue": "Bitget", **s}) print(f" Bitget : " + " · ".join(f"{k} {v}" for k, v in s.items()) + f" [{time.perf_counter() - t1:.0f}s]", flush=True) # 同窗口的 Binance 对照,直接重算一遍,避免口径漂移 lo, hi = int(bg_l.timestamp.min()), int(bg_l.timestamp.max()) bn_l = load_local(f"{sym}/USDT:USDT", "1m") bn_h = load_local(f"{sym}/USDT:USDT", "5m") if bn_l is None: continue bn_l = bn_l[(bn_l.timestamp >= lo) & (bn_l.timestamp <= hi)].reset_index(drop=True) bn_h = bn_h[(bn_h.timestamp >= lo) & (bn_h.timestamp <= hi)].reset_index(drop=True) t1 = time.perf_counter() s2 = stats(run_once(bn_l, bn_h)) rows.append({"品种": sym, "venue": "Binance", **s2}) print(f" Binance: " + " · ".join(f"{k} {v}" for k, v in s2.items()) + f" [{time.perf_counter() - t1:.0f}s]", flush=True) if not rows: print("无结果") return tb = pd.DataFrame(rows) tb["毛均bp"] = tb["毛均收益"].str.rstrip("%").astype(float) * 100 print("\n" + "=" * 120) print("########## 同窗口 · 同代码 · 只换数据源 ##########") print(tb.to_string(index=False)) print("\n########## 毛均收益对照(bp)##########") piv = tb.pivot_table(index="品种", columns="venue", values="毛均bp") piv["差额"] = (piv.get("Bitget", np.nan) - piv.get("Binance", np.nan)).round(2) cnt = tb.pivot_table(index="品种", columns="venue", values="笔数") piv["Bitget笔数"] = cnt.get("Bitget") piv["Binance笔数"] = cnt.get("Binance") piv["Bitget余量bp"] = (piv.get("Bitget", np.nan) - 6.0).round(2) print(piv.round(2).to_string()) print("\n########## 结论 ##########") print(" Bitget余量 = 该 venue 自己的毛均收益 − 6bp 双边费率(VIP1 taker + API 返50%)。") print(" 这就是影子交易器测出的滑点要去比的那条线,逐笔清单不参与比较。") print(" 两家毛均若接近 → 总体口径可迁移;若差很多 → 以 Bitget 的为准。") out = RESEARCH / "out" / f"bitget_baseline{args.tag}.csv" tb.to_csv(out, index=False) print(f"\n产物写入 {out}") if __name__ == "__main__": main()