文章裡的每一個數字,以及它從哪裡來
這裡的方法足以讓你用手驗算,或用同樣的公開資料重建一份等價的研究,包括合約停止交易時適用的規則,以及這次執行所凍結的雜湊值。
交易所、交易範圍與期間
幣安 USDT 保證金永續合約。選樣日是2022年12月31日,之後的任何資訊都不參與排名。合約要合格,必須以 USDT 報價與計價保證金、到選樣日為止至少有 24 個月真實成交量歷史(以2020年12月31日或之前有第一個成交量為正的日子來衡量)、在2022年10月到12月之間有交易,而且不是對同一標的曝險的重複主張。列舉涵蓋這個交易所歷來上市過的全部 986 檔合約,留下 78 檔合格,並以2022年10月到12月的每日成交名目金額中位數排序。沒有任何合格合約是千單位計價或另一檔合約的第二種報價幣別,所以重複規則沒有剔除任何東西,排名就是原始的名目金額排名。
凍結的交易範圍是第1到第30名。流動性層級與階梯是同一份排名的兩種決定性切法,都在任何東西開跑前就固定了。
| 層級 | 市場 | 每日成交名目金額中位數 |
|---|---|---|
| T1,第1-6名 | BTC, ETH, DOGE, XRP, BNB, SOL | 6,978M 美元至 433M 美元 |
| T2,第7-14名 | MATIC, LTC, ETC, LINK, ADA, NEAR, DOT, EOS | 347M 美元至 128M 美元 |
| T3,第15-22名 | AVAX, SUSHI, CRV, AXS, ATOM, FIL, FTM, BCH | 127M 美元至 72M 美元 |
| T4,第23-30名 | TRX, UNI, WAVES, ALGO, AAVE, BAND, RSR, XMR | 58M 美元至 34M 美元 |
第1到6名正好重現前一份研究的六個市場。排名軸涵蓋 205 倍的成交名目金額差距。
階梯是 N 取 {6, 10, 15, 20, 25, 30} 的巢狀前 N 名交易範圍,分別對應36、60、90、120、150與180條策略腿,每一階都投入同樣的固定資金,且每條腿等權重。樣本外期間從2023年1月1日到2026年6月30日,共十四個完整且不重疊的季度。資料只拉到2026年6月30日(不含),所以登記在案的2026年7月部分窗口敏感度不適用。
資料
每檔合約自上市起的 UTC 小時K線、自2021年11月1日起的一分鐘K線(用來重建小時內的價格路徑)、帶有公布結算間隔且時間戳為真實時間的歷史資金費率事件,以及用於資金費率現金流的小時標記價格開盤價。一分鐘K線用完即丟;每小時保留下來的是引擎需要的衍生路徑資訊,也就是該小時最高價與最低價的時間戳、兩者的先後順序、分鐘數,以及修補旗標。
每個市場的面板都建在共同的 UTC 小時網格上,不做前向填補。成交量為正是判斷合約歷史起點與終點的依據。這一點很重要:本地的下市合約面板會在結算之後很久還用零成交量的K線填補,其中一檔補了 11,510 列、另一檔補了 16,527 列,兩檔都一路填到2026-04-30,儘管它們分別在2025年1月與2024年6月就結算了。若照檔案涵蓋範圍而不是成交量來讀,就會讓兩檔已死的合約在樣本外期間以陳舊價格繼續「交易」超過一年。
六個既有市場沿用前一份研究凍結的面板,逐位元組重用,這樣巢狀檢查才能是精確的。本地面板只是對新抓資料的交叉檢查而不是來源,任何K線層級的不一致都在凍結前對照交易所解決。
合約下市與強制結算
二十四檔新增合約中有四檔在樣本外期間停止交易。幣安在 UTC 09:00 對這四檔全部強制結算,而最後一個成交量為正的小時,在每一個案例中都與公告的結算時點相符。每則公告連同網址、抓取時間戳與 SHA-256 一起存進資料檢查會讀取的下市清單。
| 排名 | 合約 | 公告結算時間,UTC | 最後一個成交量為正的小時 |
|---|---|---|---|
| 7 | MATICUSDT | 2024-09-04 09:00 | 2024-09-04 08:00 |
| 14 | EOSUSDT | 2025-05-21 09:00 | 2025-05-21 08:00 |
| 21 | FTMUSDT | 2025-01-06 09:00 | 2025-01-06 09:00 |
| 25 | WAVESUSDT | 2024-06-11 09:00 | 2024-06-11 08:00 |
主要規則是不做接續。 合約停止交易時該條腿就結束,任何未平倉部位以最後觀察到的開盤價平倉,從下一根K線起這條腿在研究剩下的時間裡持有現金。存活的腿絕不重新標準化去吸收釋出的資金。這是交易所實際作為的字面描述,而不只是比較保守的選擇:部位被關閉並結算,而且沒有任何後續部位被建立。
登記在案的接續敏感度,以交易所自己的換幣公告中所指名的代幣來認定後繼合約,而且在任何價格比較之前就先認定,因為在這裡照代號相似度去挑會挑錯:這個交易所上有兩個與 Sonic 相似的代號同時在交易,只有其中一個才是 FTM 換成的代幣。只有當後繼合約第一次觀察到的開盤價,與前身最後成交的收盤價在依公告比例調整後相差在 5% 以內時,才會接受接續。
| 前身 | 後繼 | 沒有合約的空檔 | 依比例調整後的價格變動 | 5% 容差 |
|---|---|---|---|---|
| MATICUSDT | POLUSDT | 9天4小時 | +6.03% | 未通過,不接續 |
| EOSUSDT | AUSDT | 7天0小時 | +0.23% | 通過 |
| FTMUSDT | SUSDT | 10天1小時 | +3.87% | 通過 |
把兩檔通過的市場替換進去之後,主要對比從 -1.046 變成 -1.124,95% 區間為 -3.081 到 +0.856。結論不取決於這些遷移怎麼處理。
這個容差原本是為了抓出錯誤的換算比例而寫的,在這麼長的空檔下它無法把錯誤比例和真實的市場波動分開。即使如此它仍然維持原樣,因為在注意到這個弱點時,未通過的案例已經看得見了,而在知道某個測試會排除哪一檔合約之後再去調整它,正是預先登記要防止的模式。取而代之的是把後果記錄下來。
策略與參數網格
六個結構性家族,訊號事件語意完全沿用:EMA 趨勢、Donchian 突破、時間序列動能、RSI 反轉、布林通道反手,以及波動度壓縮突破。每個家族帶著一份 64 組數值參數的標準清單,由離散拉丁超立方生成器以種子 20260809 一次生成,在每個市場、每個窗口重複使用,並以雜湊值鎖定。沒有任何市場享有比別人更寬的搜尋。
每條腿只持有一個部位。同方向的訊號被忽略,反向訊號在下一個觀察到的開盤反手並收取兩次成交成本,停損與停利由進場時已知的 Wilder ATR(14) 決定,最長持有在 H 根完整持有K線之後於開盤出場。同一時間戳的事件順序是資金費率、跳空觸發的障礙、反手、最長持有、新進場,最後才是小時內障礙。小時內執行以一分鐘K線把該小時重建為開盤、第一個紀錄到的極值、第二個紀錄到的極值、收盤;跳空穿過停損以第一個可得的開盤成交,跳空穿過停利成交價不會優於停利價;當極值先後順序無法判定時,停損先執行。
逐步前推與挑選
for window in quarterly_windows(oos_start="2023-01-01", oos_end="2026-07-01"):
param_block = window.is_slice[:9] # nine months, scored
axis_block = window.is_slice[9:] # three months, retained unused
attempts = [score(cfg, param_block) for cfg in family_grid] # all 64, persisted
eligible = [a for a in attempts
if a.closed_trades >= 24 and a.distinct_weeks >= 6]
if not eligible:
cash_sleeve(window) # recorded, never dropped
continue
winner = max(eligible, key=lambda a: (a.net_return, -a.turnover, -a.canonical_index))
apply(winner, window.oos_slice) # OOS starts flat樣本內十二個月,切成九個月的參數區塊與三個月的軸選擇區塊,樣本外三個月,每次前進三個月,共十四個完整窗口。軸選擇區塊在這份研究裡沒有任何挑選功能,因為這裡沒有 picker;保留它但不使用,是為了讓窗口邊界、purge 行為與被評分的區塊,與前一份研究維持逐位元相同,好讓巢狀檢查可以是精確的。
資格條件要求參數區塊裡至少有 24 筆已平倉交易、分布在至少六個不同的日曆週,而且完全在樣本內決定,這樣就沒有任何樣本外資訊會挑選哪些腿是活的。找不到合格參數組的腿窗口以現金計入並被記錄,這一點很重要,因為流動性差的合約更常沒過這道門檻,若因此中止就會把研究變成一個以活躍度為準的存活篩選。整份執行中,2,520 個挑選決策裡有 54 個找不到合格參數組。平手時先比較低周轉,再比較較小的標準索引。
成本與資金費率
主要模型對每一次成交收取 5 個基點的吃單手續費與 2 個基點的滑價,從第1名到第30名的每個市場都一樣,按成交名目金額的絕對值計算,採用不利成交建構並把滑價分開入帳。維持一律相同,正是讓六市場那一階與前一份研究逐位元相同的原因,也讓排名對比衡量的是訊號品質,而不是這份研究自己發明的成本模型。
這在長尾端偏樂觀,所以有一項強制的不利敏感度按層級收取滑價、手續費不變。它在放大的成本下重新挑選每一組參數,因為每次成交更貴會改變哪一組參數贏得該區塊,而固定挑選結果會高估效果。
| 層級 | 每次成交滑價,主要 | 每次成交滑價,不利 |
|---|---|---|
| T1 | 2 bp | 2 bp |
| T2 | 2 bp | 4 bp |
| T3 | 2 bp | 6 bp |
| T4 | 2 bp | 8 bp |
這座階梯是宣告的,不是校準的,而且在所有地方都這樣標示。
規格書為這座階梯登記了兩個分支:一個是用樣本內K線上的 Abdi-Ranaldo 價差估計式來校準,另一個是在驗證失敗時採用上面這條宣告的階梯,並標示為未校準的壓力測試。校準分支因為兩個獨立的理由而失敗,兩者都在讀取任何樣本外數值之前就確立了。在樣本內的小時K線上,這個估計式對全部三十檔合約的平均項都是負的,於是被截在零,回傳一條平坦的階梯,那等於錯誤地宣稱執行成本不隨排名上升;改成日K線也救不回來,而在少數轉正的情況下,它算出來是 62 與 106 個基點,對照真實永續合約遠低於一個基點的價差。另外,庫存裡唯一的驗證資料來源起點是 2023-04-01,落在樣本外期間之內,用它來決定階梯,等於讓樣本外資訊選擇一個成本參數。因此採用登記在案的備援方案,而不是換上一個「看到第一個失敗後才挑的」估計式。
資金費率使用時間戳為真實時間的觀測事件,資金費率為正時扣多單、付空單,依結算時間戳前一刻所持有的部位計算,而且永遠不作為特徵。結算間隔按合約從交易所公布的資金費率歷史讀取,而不是一律假設八小時,因為在這個樣本期間有幾檔山寨幣合約改成四小時結算。應該存在卻缺失的資金費率觀測值算資料失敗,而不是當成零。
net_pnl = gross_pnl - fee - slippage - funding_cost assert abs(net_pnl - ledger.net_pnl).max() < 1e-9 # measured: exactly 0.0
統計推論
在每日序列上做以週分群的配對拔靴法,10,000 次重抽,種子 20260810,取百分位區間。在每個固定的樣本外窗口內,拔靴法以 UTC 週一到週日的日曆週為叢集重抽。季度邊界會把一個日曆週切開,所以邊界兩側的日子形成不同的片段。被抽到的片段裡,每個市場、家族、層級與階梯一起移動,這樣既保留了橫斷面的相依性,又不改變一個窗口的叢集數。
frag = df.groupby(["window", pd.Grouper(key="ts", freq="W-MON")])
blocks = [g for _, g in frag] # whole week-fragments move together
def stat(sample): # the primary estimand
return (sample.loc[sample.group == "added", "net_bp"].mean()
- sample.loc[sample.group == "incumbent", "net_bp"].mean())
draws = [stat(pd.concat(rng.choice(blocks, len(blocks)))) for _ in range(10_000)]
lo, hi = np.percentile(draws, [2.5, 97.5])被估計的量是 144 條新增腿減去 36 條既有腿的每腿淨期望值,單位是每個活躍腿日、以腿資金計價的基點。活躍腿日是合約當天有在交易的日子,所以合約下市之後的日子完全離開分母。
每個次要 p 值是拔靴法較小尾端機率的兩倍,並套用有限重抽修正 (count + 1) / (B + 1),上限為一,然後四個一起進入 5% 的 Benjamini-Hochberg。唯一的主要對比不做校正,而研究裡的其他比較都不能取代主標題。
檢定力在凍結之前就用前一份研究的樣本外面板量過:既有組平均值的以週分群標準誤是每天 1.00 個基點,是獨立日計算的 0.98 倍,因為腿報酬幾乎沒有序列相依,而差值的預估標準誤是 1.21,給出約 2.4 的 95% 半寬。能把 180 條腿的交易組合推到 Sharpe 為零的擺幅,是這個半寬的兩倍以上,所以這個設計能分辨到「真正重要幅度」的一半以下,但無法分辨每天約 2 個基點以下的差異。
Sharpe 紀律
在這裡 Sharpe 是被估計的量而不是診斷指標,所以它被濫用的方式都被明文封死。Sharpe 從不挑選參數組、市場、層級或階梯。所有 Sharpe 都在十四個窗口彙總、互不重疊的每日帳上計算;不會把各窗口的 Sharpe 平均,也不會把窗口數乘進策略數。每個 Sharpe 都與同一本帳的累積淨報酬並列呈現,而累積報酬為負的階梯,不能只憑 Sharpe 就被說成變好了。每日帳的一階自相關介於 -0.026 到 +0.006,所以約 0.52 的雜訊底線不需要實質放大。
廣度模型檢查
mult = np.sqrt(n_cells / (1 + (n_cells - 1) * rho_measured)) sr_hat = per_sleeve_sharpe.mean() * mult # both measured inside the replicate gap = sr_realized - sr_hat # carries its own bootstrap interval
rho_measured 是該階梯各腿每日淨報酬的實際平均兩兩相關係數。事先固定的登記讀法是:每一階的區間都含零,代表廣度交出的正是量到的相關結構所隱含的乘數、沒有多出任何東西;只要有任何一階的區間不含零,就代表這個模型不完整,並要報告偏離的方向與大小。
這個估計式在碰到真實資料之前就先驗證過,用的是每腿 Sharpe 已知、兩兩相關係數為已知常數的合成資料,rho 取 {0, 0.05, 0.2, 0.5}、腿數取 {36, 180},24 個案例全部落在容差之內。
有一個在核對過程中發現、選擇接受而不是修正的已聲明偏誤:相關係數是在每日的腿序列上計算的,而其中約四分之三的腿日正好是零,因為一條腿多數時間是空手的。這些零會機械性地壓低相關係數,進而抬高乘數、讓預測的 Sharpe 更負。六個落差的點估計全部為正,所以偏誤的方向與「實現值貼著預測值走」的結論一致。看到結果之後才換估計式,並不是可用的選項。
登記在案的反事實,把每一條新增的腿固定在既有市場的每腿平均與波動度,同時保留實際量到的 180 條腿相關結構,藉此把 Sharpe 差拆成廣度分量與期望值分量,殘差必須在數值容差內為零。實測結果是 -1.1876 與 +0.1221,殘差正好是 0.0000。
洩漏、完整性與可信度檢查
| 檢查 | 結果 |
|---|---|
| 巢狀檢查,36 條既有腿的引擎輸出 | 逐格完全相同 |
| 巢狀檢查,被挑選的參數組 | 504 列完全相同,0 個不一致 |
| 巢狀檢查,每日組合序列 | 最大絕對差 3.47e-18,容差為 1e-12 |
| Python 參考實作對照 Rust 引擎 | 120 個案例、41,309 筆交易,時點、方向與出場原因完全一致,價格差 1.94e-16 |
| 截斷未來資料的無前視檢查 | 427,080 筆較早的交易逐位元不變 |
| 隨機漫步虛無,零漂移 GBM,30 個種子 | 沒有任何家族的區間高於零 |
| 窗口邊界 | 全部 10,080 個腿-區塊組合中,零筆交易跨越邊界 |
| 資料完整性,30 個市場 | 零填補、零缺漏的小時內資料、主要期間零代理資料 |
| 下市日期對照交易所公告 | 4 檔全部落在結算的那個小時內 |
| 覆蓋率,僅樣本內區塊 | 97.86% 填充率,無附帶說明 |
| 完整選定明細上的帳務恆等式 | 殘差正好為 0.0 |
四道檢查各自帶著一個植入的缺陷,用來證明它們真的會失敗;每一道都會以非零代碼退出並指名該缺陷。三十個測試通過,backtest-guard 回報零自動化失敗。
評估器。 strategy-evaluator 釘在與前一份研究相同的 commit,執行了完整性、程序與輸入三個階段。成本在 183,842 列上對帳到 5.55e-17,前視檢查發現同一根K線的相關性弱於下一根,而它的「回報值對照重算值」檢查也用自己算出來的數字確認了公布的結果。它的整體判定把這組策略放在經驗虛無之內,觀察到的最大 Sharpe 是 -0.595,對上期望的虛無最大值 1.116,對一個每條手臂都在虧的交易組合來說,這是同意而不是矛盾。它的 no_purge 旗標是沿用來的:參數區塊結束的瞬間就是樣本外區塊開始的時點,而引擎是在交易層級做 purge,而不是靠日曆間隔,這一點已直接驗證並獨立複驗過。
從原始碼重新核對。 實作被從原始程式碼重新核對了一遍,過程中不參照已經寫好的說明。這一輪核對回報了一個阻斷級問題、六個重大與六個次要發現。從保存下來的 parquet 出發,逐位元重現了主要衡量指標、它的拆解、四個層級、六個階梯與四項次要檢定,重算每一個挑選決策且零不一致,驗證了每一組腿-區塊邊界組合,也確認合約下市之後沒有任何交易發生,以及 108 條現金腿貢獻的活躍腿日正好是零。
阻斷級問題是:那項強制的排名加權滑價敏感度其實從來沒有真的跑過,而某一版草稿卻寫得像跑過了。把它跑完之後,該成本模型下登記在案的結論就改變了,這也是文章把兩種讀法並列呈現的原因。另外兩項發現被記錄而不是修掉:合約停止交易時,引擎是丟棄而不是強制平掉仍未平倉的部位,共六筆交易對上 91,277 筆;以及某一版草稿曾把一個未登記的毛損益對比拉上主標題,現已撤回。
規模。 180 條腿、每條 56 個區塊,645,120 個候選的參數-區塊評分,每個成本政策 2,520 個挑選決策。跨越各成本政策,選定的交易明細共 527,013 列,其中 91,277 筆是滾動的樣本外交易,以四個 worker 花 358 秒引擎時間、15 GB 產生。分析在 16 個 worker 上平行化,並驗證與序列路徑逐位元相同。
補述做了什麼,以及怎麼做的
沒有重跑任何引擎。每一次成交的成本都是從研究保存下來的逐筆交易明細重建的,而這個拆解把儲存的手續費與滑價欄位重建到 4.3e-19 與正好為零,所以重新定價是對既有產物做轉換,而不是一次新的模擬。
| 成交 | 單型 | 手續費 | 滑價 |
|---|---|---|---|
| 下一根開盤進場 | 市價,吃單 | 5 bp | 2 bp |
| 停損出場,含跳空穿過停損 | 市價,吃單 | 5 bp | 2 bp |
| 反手的兩條腿 | 市價,吃單 | 5 bp | 2 bp |
| 最長持有於開盤出場 | 市價,吃單 | 5 bp | 2 bp |
| 停利出場 | 掛單限價,掛單方 | 2 bp | 0 bp |
幣安 USD-M VIP0 的公布費率。不假設任何手續費等級與返佣,所以這裡報告的節省是下限。
重新挑選是必要的而不是可選的,因為參數區塊是以淨報酬評分,而淨報酬取決於成本模型。因此每個模型都跑完整的登記挑選流程,每個家族、每個市場、每個窗口 64 次嘗試,而文章同時報告「重新定價但固定挑選」與「重新挑選」兩個數字,好讓兩者的差距看得見。
重新定價之後毛損益與資金費率逐位元不變,帳務恆等式維持正好為零,而在 156,661 列實質變動的資料中,沒有一列是非停利的出場。真正關鍵的對照組是:在沿用的全吃單模型下重跑挑選,會完全重現這份研究的 2,520 個挑選結果,零不一致。少了這一項,任何差異都可能是成本模型造成的,也可能是程式錯誤,而且無從分辨。
成交是以「觸價即成交」認定的,所以一張只被觸碰到的掛單限價單也算成交。這是對成交機率而言較樂觀的分支,而它只會強化結論。進場全程維持吃單,因為把進場也改成掛單會改變哪些交易會發生,而不只是它們的成本。
補述也事先登記了母研究不得不留著未修正的強制平倉費用。合約停止交易時仍未平倉的部位,應該要付一次完整的吃單出場成交費,而正好有 6 個被選中的樣本外腿窗口帶著這種情況。引擎會標記這筆丟棄但不會輸出該筆交易的價格,所以這筆費用無法事後補收;它的上限是每條腿 0.0023 個百分點。
凍結的雜湊值
規格書、交易範圍與每一份參數清單,都在讀取任何樣本外區塊之前就以雜湊值鎖定,而凍結紀錄在執行與分析之間再驗證過一次。在所有登記的雜湊值都相符之前,執行器拒絕讀取任何樣本外K線,也拒絕寫出任何樣本外產物。
| 產物 | SHA-256 |
|---|---|
| 交易範圍清單 | 60ce3d29574a5c7a0d9befecc5cb1ea9f00a82528cf7eb80b0a7ec85ae450149 |
| 資料清單,30 個市場 | e3129b4b1d711adec2cd905a4ce246b2b3ad7938c9614f31af6aca5d44daff03 |
| 研究區塊清單 | 325d3873324433b73979ace8c7450a1df901b46d6d6b6852553aab7dd0c26fbb |
| 凍結紀錄 | 38741abd0e08a1cdab605cd62bad15ce733b66799761fb421ec132c665014e3c |
| 引擎執行清單 | eb954b0d820c0f1e5e1745824c82d484cc006ac68ab6ac40729fb81cecdb3eac |
| 結果 | ef56d329aacd262d00d5b23679963514fb12055ff26a5b0f573e72d156f8e06d |
索取完整的流程程式
這一頁足以讓你用手核對文章裡的每一個數字,或用同樣的公開資料來源重建一份等價的研究。生產用的程式,也就是資料拉取、面板建構、Rust 引擎、實驗執行器與分析,並未在此公開。來信 daniel@daru.finance ,我會寄給你。

