原始數值很難設門檻。標準化後的數值告訴你當前讀數有多不尋常——這才是真正重要的事。
假設你想建一個「買盤強勁時做多」的策略。你用淨市價買量作為訊號。在平靜的一週這個數值可能是 8,420 BTC,在劇烈波動的時期可能是 85,000 BTC。門檻要設多少?任何固定數字,一半的時候太敏感,另一半的時候又太遲鈍。
價格衍生的指標也有同樣問題:BTC 在 2 萬美元時,SMA 差值 500 USDT 代表一回事;在 9 萬美元時,完全是另一回事。
標準化把原始數值轉換成一個問題:「這個讀數距離正常值有幾個標準差?」 這讓讀數在不同市場環境和不同標的之間都有可比性。
Z-score 衡量當前值距離近期均值有幾個標準差:
z = (當前值 - 滾動均值) / 滾動標準差
用 30 天的滾動窗口,Z-score +2.0 代表當前讀數比過去一個月的平均高了 2 個標準差——不尋常,但不算極端。+3.5 就是近期標準來看非常高了。
Z-score 有個很好的特性:它大致上是無量綱的,可以跨標的比較。BTCUSDT 的 TI Z-score +2.0 和 ETHUSDT 的 TI Z-score +2.0 代表大致相同的意義——兩者相對於各自的近期歷史都是異常偏高。
在策略裡實作滾動 Z-score:
window = 720 # 30 天 × 24h,適用於 1h 週期
df['z'] = (
(df['raw_indicator'] - df['raw_indicator'].rolling(window).mean())
/ df['raw_indicator'].rolling(window).std()
)
你不需要自己標準化 Blave 的獨家指標。它們已經以標準化的分數交付,可以跨標的和時間段比較:
| 指標 | 已標準化? | 說明 |
|---|---|---|
| 多空力道(TI) | ✓ | 相對於滾動近期歷史標準化;可跨標的比較 |
| 籌碼集中度(HC) | ✓ | 標準化;正值代表多頭集中,負值代表空頭集中 |
| 巨鯨警報(WH) | ✓ | OI 和成交量變化各自標準化 |
| 爆倉(LQ) | ✓ | 標準化的爆倉強度 |
| 市場情緒(MS) | ✓ | 現貨合約價差,標準化 |
| 資金稀缺(CS) | ✓ | 穩定幣利用率,標準化 |
| 市場方向(MD) | ✓ | 全市場 BTC 方向訊號,標準化 |
| 擠壓動能(SM) | ✓ | 固定 1d 週期;附 scolor 方向標籤 |
這就是為什麼使用 Blave 指標的門檻策略用的是 ENTRY_TH = 1.693 這樣的數字,而不是原始的成交量數字——那個門檻有一致的意義:「當 TI 高於近期平均 1.7 個標準差時進場。」
不是所有指標都需要標準化。SMA 交叉訊號(SMA_fast > SMA_slow)本身已經是二元的——沒有什麼需要標準化。問題出現在你把指標的大小當作門檻的時候。
直接用價格減 SMA 的原始值設門檻,跨價格環境就會失效:
# ✗ BTC 從 2 萬漲到 9 萬之後,這個門檻就失準了 signal[df['Close'] - df['SMA_50'] > 500] = 1.0 # ✓ 用同一個差值的 Z-score,跨時期都有意義 diff = df['Close'] - df['SMA_50'] df['diff_z'] = (diff - diff.rolling(720).mean()) / diff.rolling(720).std() signal[df['diff_z'] > 1.5] = 1.0
MACD 的單位是價格,不同標的之間完全不可比較:
# ✗ MACD = 800 在 BTCUSDT 和台積電上代表完全不同的意義 signal[df['MACD'] > 800] = 1.0 # ✓ 對每個標的各自標準化,再用相同門檻 df['macd_z'] = (df['MACD'] - df['MACD'].rolling(720).mean()) / df['MACD'].rolling(720).std() signal[df['macd_z'] > 1.0] = 1.0
RSI(0–100)和原始 OI(數十億美元)不能直接相加,量級差太多:
# ✗ OI 的數值完全蓋掉 RSI 的訊號 combined = df['RSI'] + df['OI'] # ✓ 兩個都標準化後再合併 rsi_z = (df['RSI'] - df['RSI'].rolling(720).mean()) / df['RSI'].rolling(720).std() oi_z = (df['OI'] - df['OI'].rolling(720).mean()) / df['OI'].rolling(720).std() combined = rsi_z + oi_z signal[combined > 1.5] = 1.0
不同持股區間的股東人數(例如持有 1,000–5,000 股的人數)在大型股和小型股之間差距極大。台積電的散戶股東可能有數十萬人;小型股可能只有幾千人。原始人數無法作為跨股票的門檻。
真正有意義的是相對於該股自己近期歷史的變化——散戶參與度是否異常快速增加?把人數(或其變化率)相對於每檔股票自身的滾動窗口做標準化:
# 原始散戶股東人數無法跨股票比較
# ✗ 作為跨股票門檻沒有意義
signal[df['retail_holders'] > 50000] = 1.0
# ✓ 對每檔股票分別標準化:當前人數有多不尋常?
df['holders_z'] = (
(df['retail_holders'] - df['retail_holders'].rolling(52).mean())
/ df['retail_holders'].rolling(52).std() # 52 週滾動窗口
)
# Z-score > 2:散戶異常擁擠 → 可能的反向放空訊號
signal[df['holders_z'] > 2.0] = 0.0
signal[df['holders_z'] < -1.0] = 1.0
同樣的原則適用於融資餘額(margin_balance)、外資買賣超(foreign_net),或任何其他台股資料中絕對量級隨公司規模變化的欄位。
| 方法 | 公式 | 適合 |
|---|---|---|
| 滾動 Z-score | (x − 均值) / 標準差 | 大多數指標;保留極端值資訊 |
| 百分位排名 | 在滾動窗口內的 rank / count | 輸出有界 [0, 1];對極端值有抵抗力 |
| Min-max | (x − 最小值) / (最大值 − 最小值) | 簡單,但容易被窗口內的極端值扭曲 |
對大多數 Type A 策略來說,滾動 Z-score 是預設的最佳選擇。如果你想要有界的訊號(例如「歷史讀數的前 10%」),且不想被窗口中的極端值影響 Z-score,百分位排名是更好的替代。
← 回到目錄