跳轉到

Block 已知落差與注意事項

所屬章節:積木 Block 以 2026-09-21 全量實跑結果為準(台股 405 / 美股 260,合計 665 個啟用積木)

部分章節保留 2026-08-20 當時的實測記錄(當時台股 403 / 美股 257), 需要現況請以 len(api.Block.get_table_tw()) 為準。 標示「已修」的章節說明的是舊行為,現況見該節內文。

Overview

積木的參數規格來自 Excel(block_spec.json),條件邏輯來自 python 類別 (condition registry)。兩邊是各自維護的,因此存在幾處落差。 這頁列出實測確認的項目,讓呼叫端知道該防什麼。

全量驗證基準(2026-09-21):台股 405 + 美股 260 個積木,每一個都實際跑過 screener_tw / screener_us,並檢查產生的 code 能否編譯:

con() 建構        665 / 665 成功
screener 實際選股  665 / 665 成功
產生的 code 可編譯  665 / 665 通過
失敗 0
選出檔數中位數:437 檔

選出 0 檔的有 12 個,查證後都正常:孤島晨星、內困三日翻紅、連續跌停這類罕見 型態當天沒出現;以及通用積木 a3 不給 right 時分母為 0,被除零防護擋掉 (相除積木不給分母本來就沒有意義)。

所以下面列的落差都不會讓積木執行失敗,它們影響的是「參數能不能調」 與「結果是否符合名稱」。


1. 預設值會正規化成 get_table 的詞彙(已修)

這一節的問題已在 2026-09-21 修掉,保留說明是因為舊筆記和舊程式碼可能還 依賴當時的行為。

以前 con() 對你指定的參數驗證合法值,但預設值不驗,所以 「con(name) 的輸出再傳回 con() 會被拒絕」——台股 59 個、美股 18 個積木中招。

成因是 Excel 規格與 condition class 對同一個值用不同寫法:

積木 class 預設 規格合法值
r19 市值排名前N名以上(下)公司 direction='up' 以上 / 以下
dfunc4 持股N張以上大戶,周月增減<>P% period_type='week' 周 / 月
dfunc12 散戶(大戶)持股… up_down='up' < / >
o29 連續D日(周、月) K棒為紅K table='日_K' 日 / 週 / 月
i14 近D日個股Beta<>N倍 multiple=1.0 0.5 / 0.8 / 1 / …

兩者送進 class 後等價('以上' 與 'up' 都得到 'up'),所以選股結果從來 沒錯,錯的是輸出的寫法與表上對不上。

現在 con() 會把預設值正規化成規格那一側:

api.Block.con_us(id='r19')
# 之前: {'id': 'r19', 'top_n': 50, 'direction': 'up'}
# 現在: {'id': 'r19', 'top_n': 50, 'direction': '以上'}

台股 26 個、美股 4 個參數受影響,全部歸零。實測 665 個積木 resolve 後的行為, 只有 3 個從 1.0 變成 1(數值相同),其餘完全未變。

連帶 con() 變成冪等——輸出丟回去得到同一份結果,所以可以拿既有條件改參數:

c = api.Block.con_tw('今日收盤價<>開高低P%', percent=3)
api.Block.con_tw(dict(c)) == c        # True
c2 = api.Block.con_tw({**c, 'percent': 5})

現況往返測試:

市場 積木數 往返成功 仍失敗
台股 405 394 8(全部是第 4 節的重複編碼)
美股 260 252 5(同上)

(各扣掉 3 個 operator 必填的通用積木,它們不給參數本來就建不出來。)

剩下的失敗只剩重複編碼一種原因:那些積木不能用 id 指定,要用名稱。


2. list_conditions 比 get_table 多,多的那些建不出來

input

len(list_conditions(market='tw')), len(api.Block.get_table_tw())

output

(406, 403)

美股是 (301, 260)。list_conditions 讀 registry(程式碼裡註冊的全部類別), get_table 讀 block_spec.json(只收 Excel 中「狀態=啟用」的積木)。

多出來的分兩種,成因不同:

情況 台股 美股 說明
Excel 有列但標「停用」 4:m15、r22、r23、r24 42:o53~o94 等 刻意停用。例如 m15(今日收盤價<>D日均線)已被啟用中的「今日收盤價<>D日(周月)均線」取代
Excel 完全沒有這個編碼 3:o2x、s9x、s9y 0 程式碼已完整實作(分別 222 / 217 / 245 行、有 docstring 與完整參數),但 Excel 未登錄,看起來是漏登

input

api.Block.con_tw('m15')

output

ValueError: 找不到積木 'm15'(market=tw)

建議:要列出「可用」的積木,用 get_table_tw() 的 key,不要用 list_conditions()。


3. category 有兩套詞彙,互不通用

來源 值 用在哪
ConditionMetadata.category 英文模組名,台股 11 種(ohlc、volume、financial…) list_conditions(category=...) 的過濾
block_spec.json 的 category 中文次類別,台股 37 種(漲跌幅、K棒型態、MACD指標…) 積木完整參考 的分組

input

list_conditions(category='漲跌幅', market='tw')     # 中文 -> 空的

output

{}

input

len(list_conditions(category='volume', market='tw'))   # 英文 -> 有結果

output

19

美股的 block_spec.json 次類別全為空字串,所以美股完整參考頁只有一個「(未分類)」群組。


4. 多個積木名稱共用同一個 python編碼

台股 4 組、美股 2 組的積木名稱共用同一個 python編碼(con() 輸出的 id)。 共用本身不是問題,判斷標準是:兩個名稱是否宣稱不同的邏輯卻跑出同一件事。

市場 id 兩個名稱 判定
台股 m9 D日均線斜率<>0.4 / <>-0.4 曾是錯誤,已修
美股 m9 D日均線斜率<>0.4 / <>-0.4 曾是錯誤,已修
台股 s1 近N日期間外資/投信/自營/官股/三大買賣超 <> V張 / 官股買(賣)超>N張 正常,參數集不同
台股 r4 近D日,官股買(賣)超張數前N名 / 法人買賣超排行前N名 正常,參數集不同
台股 m21 長期四均線糾結(5MA/10MA/20MA) / 同名 (old) 不是錯誤,同一積木的舊別名
美股 r23 成交值排行前N名 / 成交金額排行前N名 不是錯誤,同一積木的別名

m9:曾經是真錯誤(已修)

兩個名稱宣稱的門檻不同(0.4 / -0.4),但 con() 產生的 dict 一模一樣, 等於其中一個名稱在說謊。兩個市場的成因不同:

  • 台股:兩列的『python參數』都寫 'value': -0.4(複製貼上漏改), 但『邏輯基礎』欄正確帶著各自的符號。build_spec.py 在映射毫無歧義時 (python參數 恰好一個數值固定值、模板恰好一個數字字面值)以模板為準校正。 全部 660 個積木裡只有這一列被校正,校正會在 python build_spec.py 的輸出中列出。
  • 美股:兩列的『python參數』完全沒有 value kwarg,所以都用 class 預設的 0.4, -0.4 那個名稱取用不到。已在 美股積木API.xlsx 的「均線積木」分頁補上。

修好後:

api.Block.con_tw('D日均線斜率<>0.4')    # value=0.4     ->     2 檔
api.Block.con_tw('D日均線斜率<>-0.4')   # value=-0.4    ->  2697 檔
api.Block.con_us('D日均線斜率<>0.4')    # value=0.4     ->    56 檔
api.Block.con_us('D日均線斜率<>-0.4')   # value=-0.4    -> 12531 檔

m21 與 r23:別名,不是錯誤

這兩組的兩個名稱描述的是同一件事,跑出同一個結果也就是正確的, 沒有任何名稱在說謊:

  • m21 的 (old) 後綴本身就標示它是舊別名,兩列所有機器可讀欄位完全相同。
  • r23 的「成交值」與「成交金額」在中文財經語境同義,class 相同。 唯一差別是 top_n 的選項清單:
成交值排行前N名    top_n: [50, 100, 150, 200, 500, 1000]
成交金額排行前N名   top_n: [5, 10, 100, 150, 200, 500, 1000]     ← 多了 5、10

傳同一個 top_n 給兩者,con() 的 dict 與選出的股票完全相同(實測 top_n=100 與 200 皆是)。所以只在不指定 top_n(預設都是 50)時才看起來撞在一起, 明確指定就沒有歧義——這也是第 1 節「不要依賴預設值」的用意。

成交金額排行前N名 的『python參數』把 id 寫死成 'r23' 而非 '${ID}', 且『功能說明』欄留著別的積木的文字(股本:小於N億…)。兩者都不影響行為 (build_spec.py 忽略 id 欄、不讀『功能說明』),但看 Excel 時會誤導。


5. 零命中時 dataframe 是 (0, 0),沒有欄位

見 執行選股 Example 5。安全寫法:

df = r['dataframe']
symbols = df['stock_id'].tolist() if len(df) else []

6. api.MSMP.list['all'] 含重複代號

2873 筆但只有 2872 個不同代號('7834' 出現兩次)。 screener 的股池過濾不受影響,但自己拿去當 DataFrame 的 columns 會踩到 InvalidIndexError。見 股池 末段。


7. ConditionMetadata.path 格式不統一

同一批 metadata 裡出現四種格式:

'/blocks.api.ppi.volume/v1'
'/blocks.api.api.volume/v2'
'/blocks-api/api/volume/v3x'
'/blocks-api/api/us/ohlc/o1'

這個欄位目前沒有被程式使用,不要拿它做解析或路由。


8. 有 6 個標記「啟用」的積木被靜默丟棄

build_spec.py 第 86 行 df.dropna(subset=['python編碼', '積木名稱']) 會砍掉沒填 python編碼 的列,且不發任何警告。 Excel 中有 6 列狀態是「啟用」卻沒填 python編碼,因此完全不會出現在 get_table / con 裡:

市場 分頁 積木名稱
台股 漲跌幅積木 未知
台股 漲跌幅積木 停利,較成本價獲利>P%
台股 漲跌幅積木 停利,較成本價獲利>M千元
台股 漲跌幅積木 停損,較成本價虧損>P%
台股 產業概念&市值 已公布股利政策的股票
美股 財報積木 最近一季合約負債佔股本年增率<>P%

所以「Excel 標啟用的數量」與「實際可用的積木數」不相等:

台股  Excel 啟用 408 列  ->  扣掉缺 python編碼 5 列  ->  block_spec.json 403 個
美股  Excel 啟用 261 列  ->  扣掉缺 python編碼 1 列  ->  block_spec.json 260 個

要判斷某個積木到底能不能用,只看 get_table_tw() 的 key, 不要以 Excel 的狀態欄為準。


9. 有 8 個積木的固定值仍未收錄(複合值)

規格釘死的字面常數(例如 'value': -0.4)現在會由 con() 帶進輸出,見 組積木 Example 7。但『python參數』裡「佔位符 + 字面後綴」的 複合值仍未支援,因為需要模板代入:

id 積木 python參數裡的複合值
o29 連續D日(周、月) K棒為紅K 'table': '${A}_K'
o30 連續D日(周、月) K棒為黑K 'table': '${A}_K'
v5 今日(周)成交量>上一日(周)成交量P倍 'table': '${A}_K'
v7 日周月成交量創近D日新高(低) 'table': '${A}_K'
v8 日(周)成交量漸增(減)連續3次 'table': '${A}_K'
m5 今日收盤價突(跌)破D日均線 'table': '${T}_K'
m7 今日收盤價(乖離率)<>D日均線P% 'table': '${T}_K'
r4 近D日,官股買(賣)超張數前N名 'direction': '${A}超'

這 8 個積木的該參數不會出現在 con() 輸出,condition class 會用自己的預設值, 所以「周」「月」的變體目前選不到(例如 o29 只會跑日 K)。 python build_spec.py 會把這 8 個逐筆列出。


10. v24 的「大盤總成交金額」是全市場個股加總

今日(周月)成交金額佔今日(周月)大盤總成交金額>P%(v24)的分母, 是同一張個股矩陣橫向加總得到的全市場成交金額,不是某個現成欄位—— MSMP 裡沒有大盤總成交金額這個欄位(api.MSMP.TSE 只有 OHLC 與技術指標)。

market_value = stock_value.sum(axis=1)          # 全市場當日成交額
ratio = stock_value.div(market_value, axis=0) * 100

所以分母包含 TSE + OTC + 興櫃 + ETF(也就是 日_K 覆蓋的全部標的), 各檔占比加總為 100%。實測 2026-08-19:

threshold 檔數 占比範圍
>1% 25 1.01% ~ 5.13%
>3% 2 3.11% ~ 5.13%
>5% 1(2330) 5.13%
>10% 0 —

>1% 那 25 檔占比合計 43.9%。換 stock_table 成 週_K / 月_K 也適用, 分母會自動變成該頻率的全市場加總。


11. 美股沒有股池

api.USMSMP 沒有 list,screener_us() 也沒有 pool 參數。見 股池。


12. 積木名稱可能與實際邏輯不符 —— 以 o6 為例

漲跌幅介於P1%到P2%之間(o6)的名稱沒提任何期間,參數也只有 percent1 / percent2, 讀起來像「當日漲跌幅」。實際產生的程式碼是:

close = msmp.日_K.adj_Close
close_shifted = close.shift(20)                       # ← 20 天,硬寫死
change_pct = ((close / close_shifted) - 1) * 100
lower_bound = change_pct > percent2
upper_bound = change_pct < percent1

也就是近 20 日漲跌幅,而且 20 沒有做成參數、改不了。

實測差距(資料日 2026-08-20,pool=['all','~etf']):

問法 檔數
con_tw('漲跌幅介於P1%到P2%之間', percent1=9, percent2=5) 183
直接算 日_K.漲跌幅 當日落在 5~9% 49

另外兩點:

  • percent1 是上界、percent2 是下界(要求 percent1 > percent2)。 按名稱直覺寫 percent1=5, percent2=9 會拋 ValueError: percent1 (5) must be greater than percent2 (9)。
  • 台股沒有「當日漲幅 > P%」的純積木。最接近的 o27 是 當日為長紅K,且漲幅>P%,多綁了一個長紅K條件。 要純粹的當日漲跌幅請直接用 api.MSMP.日_K.漲跌幅。

通則

積木名稱不是規格。 遇到「檔數與預期差很多」時,第一件事是印出 screener_tw(...)['code'] 看它實際算什麼——那份程式碼是唯一可信的語意來源, get_table_*() 只告訴你參數的合法值,不告訴你邏輯。


13. 參數合法值清單外的需求,改用通用積木

上面幾節講的都是「積木的規格與名稱對不上」,但更常見的情況是 規格本身就沒有你要的值——threshold 最低 20000、days 沒有 21、 n 沒有 300、5ma 固定含當根、倍數固定 1.0。

這些不是 bug,是每顆積木把窗格/聚合/位移/倍數寫死在 class 裡的必然結果。 遇到時不要硬套名稱相近的積木(那會選出語意不同的股票), 改用通用積木 a1/a2/a3,這些全部是參數。

實測 29 條常見條件(KD/MACD/RSI/布林/ADX/K棒型態/財報/營收/排行) 全部組得出來——技術指標類因為 日_技術指標 表早有現成欄位,直接比即可。