每一筆歷史資料都能被證明,而不是要你信任。
量化研究最怕的失效模式,是資料被悄悄改過。TWMD 為每一份快照發布經密碼學簽章的 point-in-time 檢核點,讓你能獨立證明一份歷史資料就是我們當時提供的、其後未經竄改——而且不需要信任我們。這是為回測完整性而設計的。
{
"dataset": "twse_daily_price",
"snapshot_version": "2026-08-14",
"root": "1b5b0b1995eb389f3ea7807f51d22ffcee8c4dedbc1c87d5d11d36ad033b95b9",
"leaf_count": 1378,
"path_length": 11,
"merkle_path_reaches_root": true,
"verified": true,
"proves": "integrity and origin, NOT semantic correctness"
}運作原理
三個步驟,每一步都是公開的。
這裡沒有任何一個環節,是只有我們看得到的系統內部發生的。這是設計上的限制條件,不是它的副產品。
- 01
經簽署的檢核點
每一份資料集快照都會被歸約成一個 Merkle root。這個 root 以我們的 Ed25519 私鑰簽署後公開發布。這道簽章讓我們對某個特定時點的某一版歷史負責,無法事後改口。
- 02
公開的演算法
構造方式以資料形式提供於 /v2/proof/recipe——雜湊函式、domain separation、leaf 編碼、路徑方向。任何人都能據此重算 leaf,或在完全不閱讀我們程式碼的情況下自行寫出第二個驗證器。
- 03
由你執行查核
下載 twmd_verify_proof.py,在你自己的環境執行。它會重算 leaf、沿 Merkle 路徑走到 root,並核對該 root 的 Ed25519 簽章。僅使用標準函式庫——不依賴我們的套件,也不依賴任何 PyPI 套件。
Proof 端點
四個端點,不需要金鑰。
每一個都可以直接在瀏覽器打開。一個需要我們的憑證才能查核的證明,不能證明任何事。
目前涵蓋的範圍
此清單於本頁產生時自檢核點端點讀取。涵蓋範圍仍在擴大中;在這一頁上,一份簡短而真實的清單,比一份冗長的預期清單有價值。
- tdcc_shareholding_distribution快照 2026-08-07-report_date · 40,188 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- major_event_taxonomy快照 2026-09-04-event_date · 173 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- valuation_data快照 2026-09-02-as_of_date · 1,081 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- valuation_core_daily快照 2026-09-03-date · 2,346 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- twse_daily_price快照 2026-09-03-trade_date · 1,380 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- trading_calendar快照 2026-09-03-trade_date · 2 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- tpex_daily_price快照 2026-09-03-trade_date · 1,013 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- taifex_put_call_ratio快照 2026-09-02-trade_date · 1 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- taifex_options_settlement_price快照 2026-09-02-trade_date · 12,388 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- taifex_options_delta快照 2026-09-02-trade_date · 8,520 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- taifex_inst_oi快照 2026-09-03-trade_date · 69 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- taifex_atm_iv快照 2026-09-03-trade_date · 1 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- stock_price_limit_daily快照 2026-09-03-trade_date · 894 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- securities_lending_available快照 2026-09-03-trade_date · 2,097 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- return_index_daily快照 2026-09-03-trade_date · 1 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- price_move_context快照 2026-09-03-trade_date · 434 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- options_daily_taifex快照 2026-09-03-trade_date · 11,010 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- market_overview_snapshots快照 2026-09-03-as_of_date · 1 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- market_index快照 2026-09-03-as_of_date · 1 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- market_breadth快照 2026-09-03-as_of_date · 2 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- major_event_taxonomy快照 2026-09-03-event_date · 182 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- limit_events快照 2026-09-03-trade_date · 42 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- institutional_flow快照 2026-09-02-trade_date · 1,338 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- institutional_by_type快照 2026-09-02-trade_date · 17,788 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- industry_index_daily快照 2026-09-03-date · 37 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- governance_t187ap33_l快照 2026-09-02-as_of_date · 1,094 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- futures_daily_context快照 2026-09-02-trade_date · 1 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- foreign_holding快照 2026-09-02-trade_date · 1,362 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- derivatives_market快照 2026-09-03-trade_date · 2,385 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- day_trading_suspension快照 2026-09-03-suspension_start_date · 8 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- day_trading快照 2026-09-02-trade_date · 1,232 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- block_trade_daily快照 2026-09-03-trade_date · 4 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- attention_disposal_events快照 2026-09-03-event_date · 38 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- valuation_core_daily快照 2026-09-02-date · 2,351 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- twse_daily_price快照 2026-09-02-trade_date · 1,380 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- trading_calendar快照 2026-09-02-trade_date · 2 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- tpex_daily_price快照 2026-09-02-trade_date · 1,013 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- taifex_put_call_ratio快照 2026-09-01-trade_date · 1 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- taifex_options_settlement_price快照 2026-09-01-trade_date · 12,030 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- taifex_options_delta快照 2026-09-01-trade_date · 8,794 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- taifex_inst_oi快照 2026-09-02-trade_date · 69 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- taifex_final_settlement快照 2026-09-01-settlement_date · 1 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- taifex_atm_iv快照 2026-09-02-trade_date · 1 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- stock_price_limit_daily快照 2026-09-02-trade_date · 895 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- short_sale_balance_control快照 2026-09-01-trade_date · 1,301 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- return_index_daily快照 2026-09-02-trade_date · 1 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- price_move_context快照 2026-09-02-trade_date · 373 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- options_daily_taifex快照 2026-09-02-trade_date · 11,712 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- market_value_weight快照 2026-08-25-as_of_date · 1,079 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
- market_overview_snapshots快照 2026-09-02-as_of_date · 1 列 · 簽署金鑰 ed25519:9b3481cf1cf7780a
自己跑一次
驗證器是一份腳本,不是一個頁面。
僅使用標準函式庫:不匯入我們的套件,也不依賴任何 PyPI 套件。在我們的 JavaScript 裡完成的驗證,等於要你先信任我們的 JavaScript——而那正是驗證存在的理由。
curl -O https://twmarketdata.com/twmd_verify_proof.py
python3 twmd_verify_proof.py --api https://api.twmarketdata.com --dataset tdcc_shareholding_distribution --row-key "00400A|TWSE|2026-08-07|TDCC_01|tdcc_official|shareholding_distribution"怎麼讀輸出
- root
- 該快照經簽署的 Merkle root——也就是我們所承諾的那個值
- leaf_count
- 該 root 涵蓋的列數
- path_length
- 從你那一列走到 root 所重算的雜湊次數
- verified
- 路徑抵達 root,且其 Ed25519 簽章核對通過
proves: "integrity and origin, NOT semantic correctness"
它證明這筆紀錄未經竄改,且確實來自我們。它不證明數字是對的。若官方來源公布了錯誤的數字,這會忠實地為那個錯誤數字背書——而任何宣稱相反的系統,等於宣稱它能證明交易所是對的,那是沒有東西做得到的。
Point-in-time
知道這件事在什麼時候才可能被知道。
揭露類資料的每一列都帶知識日期,並保守地設為法定申報截止日——因此依它過濾的回測,不會看到市場當時還看不到的數字。
每一列揭露資料都帶知識日期
月營收、綜合損益表、資產負債表與現金流量表都帶有 knowledge_date,並附上說明其來源的 kd_source,以及標示它是實際觀測還是推導而來的 kd_imputed。以該欄位對齊、而不是以財報期末日對齊,一個數字就會在它進入市場的時候進入你的回測,而不是在該季結束的時候。
它是法定截止日,而且刻意偏晚
2026-08-19 實測:所檢查的每一個揭露資料集,kd_source 都是 statutory_deadline、kd_imputed 都是 true——這個日期是法律上的申報截止日,不是實際公告時點(後者我們尚未擷取)。公司在截止日當天或之前申報,因此這個日期偏晚而非偏早:提早公告的公司看起來比實際慢。這犧牲了一些真實度,但不會洩漏未來資訊,而那是比較安全的偏誤方向。
日線價格沒有這個欄位
twse_daily_price 沒有 knowledge_date 欄位。對收盤價而言,交易日就是公布日,因此落差很小——但那仍是一個真實的落差,若 agent 假設此欄位普遍存在,拿到的會是 undefined 而不是日期。各資料集的 point-in-time 屬性列在涵蓋範圍頁。
REST 的 as-of 截斷
2026-08-22 實測:REST 資料集端點已實際套用。傳入 as_of 會把最新一列往回移到截斷點:2330 日線不帶參數回 2026-08-21,帶 as_of=2020-06-30 則回 2020-06-30。回應會回報 as_of_applied、實際過濾的欄位,以及被截掉幾列,讓你自己確認它生效,而不是聽我們說。這是**知識時點**截斷,不是日期截斷——as_of=2021-03-31 時月營收回的是 2 月,因為 2 月營收要到 3 月才申報。在申報型資料集上,那個知識時點採用的是**法定申報期限**而非實際公告日,因此資料不會比它真正公開的時間更早出現:偏保守、對回測是安全的方向,但不精確。日線沒有申報期限,直接以交易日截斷。
為什麼這件事重要
一個你可以執行的保證。
差別不在於別人不夠謹慎,而在於合約裡的一句話無法被測試,一道簽章可以。
可以執行的保證
這個產業的常態,是合約裡的一句話:歷史資料不會在未告知的情況下被修改。那是一個承諾,而承諾的價值取決於給出承諾的人。對 Merkle root 的一道簽章,是同一個保證換成你能親自測試的形式——在你自己的終端機、在數年之後。
實際的用途
回測完整性——讓一個結果能夠在可證明相同的輸入上重新導出。稽核——讓當初依某個數字做出的決策,能夠對應到那個數字當時的樣子。以及法遵留痕——在那裡,能證明輸入未被事後追補,本身就是全部的重點。
我們沒有在宣稱的事
這不是一套防竄改的即時資料庫,我們也不作此宣稱。可被證明的範圍更窄,而且能精確描述:對於一個我們已發布的檢核點,你手上的那一列就是該 root 所承諾的那一列。竄改之後才發布的東西一樣會被簽署——重點在於,先前那個 root 無法被收回。
工具介面
agent 能呼叫什麼,和資料記在同一本日誌裡。
工具描述是給模型的指令,不是關於模型的說明文件。悄悄改掉一條,就等於改掉了 agent 的行為,而一列資料都不用動——所以工具介面會被雜湊、簽章,並附加到資料集使用的同一本透明日誌裡,而不是留成一頁我們自己可以編輯的說明。
被錨定的、以及驗得過的
24 個工具,雜湊成一個 surface hash,於 2026-08-21 附加到元日誌的第 157 筆。有兩件事是實際驗過而不是選擇相信的:該筆的 root 與 manifest 回報的雜湊逐位元組相同,而且用端點自己公布的配方、從它提供的工具清單可以重算出同一個雜湊。所以「被證明的介面」就是「日誌承諾的介面」,而你看到的清單就是被雜湊的那份——又因為日誌只增不改,那一筆之後無法被移除或重排,否則一致性證明就會失敗。已發布 payload 的 ed25519 簽章,用驗證日誌自身條目的同一把金鑰即可驗過,因此整條鏈從頭到尾都不需要在任何一步信任我們。
為什麼欄位名看起來怪
那一筆在名為 merkle_root 的欄位裡放的是一個扁平的 sha256,回應本身就這樣寫,免得有人花一個下午去建一棵樹、算出完全不同的值。這個名字被凍結,是因為只增不改的日誌會對欄位集合做雜湊——為了修一個標籤而改名,等於改寫歷史,而那正是這一整頁拒絕的交易。
已在 /v2/proof/tool-manifest 提供,不需金鑰。它會回報雜湊配方與簽章的確切慣例,因此檢查可以照著規格寫,而不是照著我們的實作寫。
一致性
你的瀏覽器早就在信任的那一套透明性數學。
你的瀏覽器所接受的每一張憑證,都會對憑證透明度(Certificate Transparency)做檢查:一棵只增不改的 Merkle 樹、一個簽章過的樹根,以及讓任何人不必信任日誌營運者就能測試成員資格的稽核路徑。本頁的證明建立在同一套構造上——RFC 6962 的葉節點與內部節點雜湊、它的定義域分離前綴,以及它禁止複製落單節點的規則。共用的是數學。沒有共用的是治理:CT 有獨立稽核者互相交換所見,而我們沒有這樣的見證者。
inclusion proof 今天能定案的事
你手上的那一列,就是某個已發布的 root 所承諾的那一列。你重算路徑、用一把我們並非唯一持有者的金鑰驗簽,得到是或否。這一部分已經上線,而且不需要金鑰。
它無法定案的事
那棵樹與更早的樹之間是什麼關係。一個簽章過的 root 證明它承諾了什麼;它沒有說更早的 root 是不是承諾了別的東西。簽章信封自己就是這麼寫的——每個 checkpoint 都會回一個 does_not_prove 欄位。我們寧可讓 API 陳述自己的極限,也不要讓這一頁是唯一寫出這些限制的地方。
consistency proof 會補上什麼
一個「舊樹是新樹的前綴」的測試——歷史是長出來的,不是被改寫的。沒有它,讀者只能相信我們說的「更早的 root 仍然代表當初的意思」;有了它,這件事會變成又一件可以在你自己的終端機裡驗證、而不是選擇相信的事。
已在 /v2/proof/consistency 提供。驗證器會在與 inclusion proof 同一次執行中檢查它,下方的 transcript 是一次真實執行的實際輸出。
step 1 GET /v2/proof/meta-log entries [0, 157)
157 entries fetched
step 2 recompute each leaf from its own sth_canonical
157 leaves recomputed, 0 disagreed with the served leaf_hash
step 3 recompute both roots from those leaves
first ( 40) 06d57911469a82646ef1606124fab4060d4c5ea6dc3f915db10d48756fcb744c
second (157) 117c1c779f67cc8a04e6c4a0d0117814ba3053448e670dd96b215b9672753876
step 4 compare against the roots the API serves
first MATCH served 06d57911469a82646ef1606124fab4060d4c5ea6dc3f915db10d48756fcb744c
second MATCH served 117c1c779f67cc8a04e6c4a0d0117814ba3053448e670dd96b215b9672753876
result PASSstep 1 GET /v2/proof/meta-log entries [0, 157)
157 entries fetched
step 2 recompute each leaf from its own sth_canonical
157 leaves recomputed, 1 disagreed with the served leaf_hash
step 3 recompute both roots from those leaves
first ( 40) dba35b59d92d9c7a2a31238c8eb8d6d01b6e0d3424374c7e3de338a61787a383
second (157) dd8e8daf78f12b140cdea70e210dac08973dc265fee0c02159b3b897318047b5
step 4 compare against the roots the API serves
first MISMATCH served 06d57911469a82646ef1606124fab4060d4c5ea6dc3f915db10d48756fcb744c
second MISMATCH served 117c1c779f67cc8a04e6c4a0d0117814ba3053448e670dd96b215b9672753876
result FAIL — a recomputed root does not match the published one左:今天對這份日誌實際執行的結果。右:同一個檢查,但在雜湊之前改動了其中一筆——它先在葉節點失敗,接著兩個 root 都對不上。一個只會印出 PASS 的驗證,和一個永遠印出 PASS 的驗證,是分不出來的——這就是要附上第二次執行的原因。
這兩個 root 會隨著日誌成長而持續有效:對前 N 筆計算出的 Merkle root,不會因為第 N+1 筆被追加而改變。所以這次執行仍然可被複驗,而不是變成一份歷史紀念品——而如果哪一個 root 有一天對不上了,那不是頁面過期,那是日誌被改寫了。
這個證明不宣稱什麼
- 那些 head 所描述的資料樹本身是 append-only。它們是依 row key 排序的,那是另一種性質。
- 底下那些數字是正確的。
- 任何一筆是什麼時候被追加的——簽章見證不了自己的時間。
只看一次不算數
把 root 與你在更早一次造訪時記下的值比對。一份日誌只能相對於你先前看過的東西被證明是 append-only——單獨一次造訪什麼都證明不了,無論它重算了多少雜湊。
Superset
一份快照是不是另一份的子集——以及,這兩份到底能不能比?
第一版對兩種完全不同的情況給同一個答案:一列我們真的弄丟了,以及一個本來就不成立的問題——兩份快照涵蓋的期間根本不同。第二版先問可比性,再問葉子。下面四次執行就是要證明它在不同情況會說不同的話:一個 ok,三個拒絕,而且三個原因各不相同。
一週包含在該月裡 — ok
正式站真實輸出GET /v2/proof/superset/v2?dataset=twse_daily_price&from=2026-08-w3&to=2026-08
status ok
proof_version twmd-superset/v2
from 2026-08-w3 merkle_root 2bb461fdcfc4… leaf_count 6886
scope trade_date in [2026-08-17, 2026-08-22)
to 2026-08 merkle_root 5d1a0870103b… leaf_count 20663
scope trade_date in [2026-08-01, 2026-09-01)
scope.comparability.comparable true
scope.comparability.reason to_scope_contains_from_scope
claim every leaf of `from` is still a leaf of `to`
(and `to`'s scope contains `from`'s)
signed_by ed25519:9b3481cf1cf7780a
signature /f4iSVUCtgPcI3EEJaJScKfXbaOVcXgFX4jJV4jomYZ…
verify_recipe_url /v2/proof/recipe
public_key_url /v2/proof/public-key
entry_count 5000
entries_truncated true
next_cursor 4952|TWSE|2026-08-17|official_twse這一步驗了什麼:窄範圍裡的每一列都還在寬範圍裡,而且寬的 scope 包住窄的——所以這裡若少一列,那是我們資料少了,不是問題問錯了。請注意分頁:inclusion 條目一頁 5,000 筆,而這一週有 6,886 筆,所以是兩頁。status 涵蓋的是**全部**葉子——那個判定是伺服器對整個集合做的。但你要自己重建 from.merkle_root,得先照 next_cursor 走完兩頁;**這不是一次呼叫就把它從頭證到尾**。
兩段不重疊的期間 — 拒絕比較
正式站真實輸出GET /v2/proof/superset/v2?dataset=twse_daily_price&from=2026-08-w3&to=2026-08-w1
status scope_not_comparable
reason to_scope_narrower
from 2026-08-w3 trade_date in [2026-08-17, 2026-08-22)
to 2026-08-w1 trade_date in [2026-08-03, 2026-08-08)
message `to` covers [2026-08-03, 2026-08-08), which does not contain
`from`'s [2026-08-17, 2026-08-22)…
means this is a statement about the QUESTION, not about our data.
…the leaf sets were not compared at all.
(no removed_row_keys, no removed_count)這一步驗了什麼:這正是第一版會弄錯的情況。問「第三週是不是在第一週裡面」根本不是關於我們資料的問題,而回應裡**沒有** removed_row_keys 就是「什麼都沒被比較」的證據:端點拒絕了這個比較,而不是把好幾千列報成「不見了」。一個對荒謬問題回以驚人數字的工具,只會教人忽略它的警報。
scope 出現之前發布的快照 — 拒絕比較
正式站真實輸出GET /v2/proof/superset/v2?dataset=twse_daily_price&from=2026-08-14&to=2026-08
status scope_not_comparable
reason scope_unknown
from 2026-08-14 (id 1) merkle_root 1b5b0b1995eb… leaf_count 1378
kind: unknown — scope not recorded
to 2026-08 trade_date in [2026-08-01, 2026-09-01)
message `from`: this snapshot was published before scope was recorded
(superset/v1), so what it covers is not known. It is refused
rather than assumed: assuming a scope would manufacture the
comparability this endpoint exists to establish.這一步驗了什麼:在 scope 開始被記錄之前發布的每一份快照,kind 都是 unknown,而 v2 一律拒比而不是用猜的。**拒絕本身就是功能**:憑空給它一個 scope,等於捏造出這個端點本來要證明的那個可比性。這一次用的是正式站上真實存在的 checkpoint(id 1),不是本機合成——一個刻意設計的拒絕,值得用真實資料展示。
寬範圍裡少了一列 — 無法證明
本機重現GET /v2/proof/superset/v2?dataset=twse_daily_price&from=…&to=…
status not_provable
reason leaf_set_not_a_superset
removed_count 1
removed_row_keys ["2317|TWSE|2026-08-14|official_twse"]
message row(s) published in `from` are absent from `to`, or present with
different bytes. This is a TRUE answer about our data, not a
failed query.這一步驗了什麼:當一列真的不見時,端點會做什麼:把那一列指名出來。這一段是在臨時的本機資料庫上重現的,因為正式站不會製造缺列,而我們也不會為了讓畫面好看而假造一個。**標註本身就是重點**——在一頁主張「宣稱應該可被查證而不是聽起來厲害」的頁面上,一段不標註來源的本機輸出,會反駁它所在的這一頁。
怎麼讀那些範圍
範圍為半開區間:[period_from, period_to),上界排他。
/v2/proof/superset/v2 不需要金鑰。回應自帶驗證配方與公鑰網址,所以下面的 root 你可以自己重算,不必執行我們任何程式。
這個證明不宣稱什麼
這證明的是可比性判斷與子集完整性。它不證明底層數字語意正確——和本頁其他每一段守的是同一條界線。