台股 15 個每日資料 flow
本文件描述 scheduler 在 Asia/Taipei 時區執行的 15 個資料 flow。每個 flow 有自己的排程、重試、稽核紀錄與失敗邊界;不存在「15 個全部完成後才一起往下跑」的全域 gate。
排程與驗證契約
| # |
Flow |
Owner |
排程(台北時間) |
直接依賴 |
完成驗證 |
| 1 |
roster + industry |
Cortex |
07:00;08:00 重試;同目標日成功後停止 |
無 |
TWSE、TPEx 名冊分區完成;證據不完整時不得停用既有股票 |
| 2 |
commodities |
Cortex |
07:00;23:00 recovery |
無 |
FRED/Yahoo 各自依來源交易日語意保存最新資料 |
| 3 |
institutional |
Cortex |
16:30 起每 30 分鐘;最後 23:00;同目標日成功後停止 |
無 |
官方交易日同時存在 TWSE、TPEx 法人分區 |
| 4 |
Taiwan OHLC |
Cortex |
14:00–17:50 每 10 分鐘探測,同官方批次成功後跳過;18:10 起每 30 分鐘至 23:10 |
roster |
當日 TWSE/TPEx 均就緒才進 canonical range;保留既有 OHLC 修復,歷史 SES 仍需明確要求 |
| 5 |
market daily |
Cortex |
17:40 起每 30 分鐘;最後 23:10 |
無 |
index 為 available/derived_available,turnover 與 breadth 均為 available |
| 6 |
valuation |
Cortex |
valuation 18:10、20:10、23:00;monthly revenue 於每月 1–15 日 23:20 探測、週日 06:10 做窗口外修訂 audit;quarterly income 於法定財報窗口每日 23:25 探測、週日 06:20 做窗口外修訂 audit;startup 在窗口內補探測 |
roster、OHLC(衍生層) |
官方交易日存在 valuation row;歷史 recovery 走 FinMind 指定日期補抓;announcement job 只有資料實際變更才重寫 financial domain |
| 7 |
TX futures |
Cortex |
18:10、19:10、23:00;同目標日成功後停止 |
無 |
FinMind TX 法人未平倉資料狀態為 available |
| 8 |
ex-dividend forecast |
Cortex |
18:20、21:30 |
roster |
TWSE、TPEx provider coverage 以同一 snapshot date 原子保存;已結束月份的歷史股利 coverage 同步補齊 |
| 9 |
market surveillance |
Cortex |
18:40、21:30 |
roster(代碼正規化) |
注意/處置來源獨立同步,能力狀態寫入 sync_status |
| 10 |
day trade |
Cortex |
20:15、21:30、23:00 |
無 |
兩交易所完成 T、T+1、T+2 revision window,另保留 bootstrap window |
| 11 |
margin |
Cortex |
20:30、21:30、23:00;同一目標交易日成功後跳過後續觸發 |
roster |
個股 margin 分區與市場總額分區皆可用;不等待 day-trade flow;尚未成功才補跑,跳過時不新增執行紀錄或通知 |
| 12 |
short + borrow |
Cortex |
20:40;22:40 強制第二批 |
無 |
官方 short 與 borrow-sell 兩個狀態皆為 available |
| 13 |
ETF holdings |
Cortex |
16:30 catalog/full batch;16:40–19:50 每 10 分鐘;20:00、20:05、21:30、23:00;次日 07:30、09:30;startup |
roster、provider catalog |
每日先刷新 provider catalog 並稽核 active ETF coverage;enabled issuer 已進入 terminal 狀態 |
| 14 |
broker branch |
Data Hub acquisition;Cortex mirror |
Hub 21:30;Cortex 01:00 |
Hub complete batch |
Cortex 僅 mirror Hub 已完成的批次,不在 Cortex 重做 acquisition |
| 15 |
government banks |
Data Hub acquisition;Cortex mirror |
Hub 23:45;Cortex 01:00 |
Hub complete batch |
Cortex 僅 mirror Hub 已完成的批次,不在 Cortex 重做 acquisition |
衍生資料依賴
flowchart LR
roster[1 roster] --> ohlc[4 OHLC]
roster --> valuation[6 valuation]
ohlc --> technical[technical domain]
market[5 market daily] --> technical
ohlc --> financial[financial domain]
valuation --> financial
institutional[3 institutional] --> chip[chip domain]
institutional --> regime[market regime]
market --> regime
ohlc --> regime
futures[7 TX futures] --> chip
margin[11 margin] --> chip
short[12 short / borrow] --> chip
roster --> chip
chip --> cross[cross-domain SES]
ohlc --> etf[13 active-ETF facts / recap]
etfhold[13 ETF holdings] --> etf
- OHLC 自 14:00 獨立探測,market-daily 維持 17:40;17:40 兩者仍可平行,最後完成者觸發 technical readiness 檢查。提早取得 OHLC 不代表 technical/financial 所需的其他輸入已齊。
- 18:10 的 valuation 與 TX futures 是兩個平行 job。Financial 只等 OHLC + valuation;chip 只等自己的輸入。
- Margin 不等待 day-trade。兩者同屬晚間來源,但失敗與重試互不牽連。
- Technical、financial、chip、market-regime 與 active-ETF 各自有 readiness 邊界。OHLC readiness 以 canonical
sync_status 成功為準,不接受只有 ^TWII 或少量 ticker 的 partial row。Technical 與 financial 完成後各自送出自己的 recap analysis,不等待 chip。
- 每個可能最後完成的 domain/source 都會重試同一個 idempotent cross-domain writer;writer 以既有 PostgreSQL advisory lock 序列化,避免同日互相覆寫。Cross-domain 寫入後立即執行 Story Strategy finalizer;來源未齊或 publisher busy 時啟動既有 bounded retry。
- Technical、financial、market-regime 會把實際消費的來源內容 fingerprint 寫入
derived_materialization_state。每次計算前後都重讀 fingerprint;期間有來源 commit 時把本次標成 failed,下一個來源觸發或 recovery 會重跑。同鍵 valuation 修訂與不可靠的 transaction-start updated_at 不會再被誤判為 fresh。
- Technical/financial 的 recap analysis 必須讓該 domain 回傳
status=success 才能把 derived run 標為成功;payload、LLM 或 cache persistence failure 都保留 failed 狀態供 recovery 重試。
- ETF holdings 或 OHLC 修訂只刷新受影響的 active-ETF facts 與六個交易日 recap;不重播 technical/financial/chip/Story 共用 bundle。LLM 失敗只影響 recap,不回滾 holdings 或 facts。
執行與恢復語意
OHLC 官方公布窗口與當日排程
| 來源 |
官方檔案產製窗口(台北時間) |
現用公開 JSON |
| TWSE |
約 14:00 一般交易;15:30 加入零股、盤後定價;17:30 加入鉅額交易 |
exchangeReport/MI_INDEX?response=json&date=YYYYMMDD&type=ALLBUT0999 |
| TPEx |
首批產製表列 13:50、檔名說明標 14:00;含盤後批次產製表列 14:55、17:45 |
www/zh-tw/afterTrading/otc?date=YYYY/MM/DD&type=EW&response=json |
- 來源:TWSE 每日收盤行情、TPEx 上櫃股票基本資料。這些是官方檔案產製時間,不是免費 JSON API 的保證完成時間。首輪據兩市場首批窗口設在 14:00,之後依實際回應日期、schema 與非空資料判斷就緒,不能宣稱 API 每天 14:00 一定齊備。
- 2026-10-14 20:29 唯讀實測現用 JSON:兩市場 HTTP 200、回應日期均為
20261014,既有 strict parser 通過 TWSE 1,381 筆、TPEx 1,012 筆。此證據只能確認該時刻可用,不能回推當天第一次公布時分;每輪的 update_runs/task_runs 與 OHLC official sources ready 日誌可核對實際探測結果。
- 開始/結束時間、官方批次窗口與探測間隔集中於
services/daily_update_window.py,cron 與入口讀取相同定義。10 分鐘是本系統在延遲與請求量間的實作選擇,不是交易所公布頻率或官方承諾。14:55、17:45 批次在下一個探測時段 15:00、17:50 處理。
- 14:00–17:50 在既有 flow/date lease 內,先查該官方批次開始後是否已有成功的 OHLC flow;有則記
skipped,不抓官方端點、不建立 range、不觸發下游。查詢使用 run 的開始時間,避免上一批工作跨過新批次時刻才完成,誤認新批次已處理。
- 官方有效空資料(
resolved_empty/unresolved_no_rows)記 skipped,Telegram 明確顯示「來源端未更新」並等下一輪,17:50 後亦同;不建立正式 OHLC range,避免「尚未公布」凍結成不可重試的 listing evidence。日期不符、schema 不符、連線與解析失敗不論時段均記 failed;canonical 更新本身的錯誤也立即記失敗。摘要只在同 flow/目標日確有成功時,將正常 skipped 視為中性;始終未成功、failed/running/upstream-failure skip 仍會影響整體狀態。通過探測後,canonical acquisition 仍重新抓取並驗證自己的 immutable evidence,探測結果不直接入庫。
- 首批 OHLC 是當時的官方快照,成交量仍可能隨盤後/鉅額批次修訂;17:50 涵蓋 TPEx 17:45 批次,晚間 18:10、18:40……23:10 繼續同日修訂。已完成的 range 不會阻擋下一輪取得新快照;尚未完成的 range 沿用既有凍結證據與恢復語意。
- 全部批次順利完成時,早段最多 5 輪 canonical 更新(14:00、15:00、15:30、17:30、17:50),加既有晚間 11 輪,共 16 輪,較原本 12 輪最多增加 4 輪;失敗的工作仍可重試。依 PR review 的 10/14 唯讀容量估算,stage bar+membership 約 4.8 MB/輪,以每年 245 個交易日估算新增約 4.7 GB/年。這是容量估計,不是實測年度用量;本次不引入資料清理或保留期變更。
- 此入口只接受當下台北日期的官方交易日,14:00 前、官方休市與跨午夜過期目標均不執行,不因來源晚到改抓前日。交易日 calendar envelope 可含上一交易日後的休市日,以保留公司行為證據;不包含更早的交易日。
- 17:40 前若 OHLC 尚未進入當日,入庫前會查前一官方交易日的 technical/financial fresh marker、chip chain 成功紀錄、各 SES domain 日期,以及既有 cross-domain writer 的待更新清單。尚未完成就記
skipped,讓前一日仍可依既有最新日規則完成;17:40 起不再因這個新增條件延期。這些都是唯讀檢查,不補算歷史、不清除 dirty state;已進入當日則照常修訂。沿用現有 cross-domain eligibility 語意,不另定義歷史 NULL-vintage 修復或跨來源鎖定契約。
- 等待時,
update_runs.message 與 task_runs.error_message 記錄上述檢查順序中的第一個阻擋項目及日期,例如 financial@2026-10-14 not fresh;Story domain 檢查也保留各 domain 最新日期。這不是所有阻擋項目的完整清單;先修復該項後,下一輪才能確認後續條件。
- 此等待入口不重驅 D-1 的任何 SES producer。 若 derived 持續 failed/stale,且沒有其他既有流程使它恢復 fresh,14:00–17:30 會持續 skipped,當日最早退回 17:40 才能依原條件進行 OHLC 更新,並非每天保證提早。2026-10-15 唯讀核對
financial@2026-10-14 仍因 financial inputs changed during materialization 停在 failed(最後錯誤為 10/15 08:18,台北時間);若本版當日已部署且狀態未恢復,就會發生上述延期。公告 flow 競態與是否增加重試另由 #1910 追蹤,本 PR 不增加重算或自動重試。
SCHEDULED_CLOSE 的公司行為處理沿用既有 per-ticker action child 與 parent finalization,允許必要的歷史 OHLC 修復;不因一檔需要 child 就將整個市場永久停在 pending。既有 delta 分類仍將歷史 SES replay 留為 manual_required,一般 SES 只允許最新有效日。
- Startup/夜間 unfinished discovery 保留原有歷史 OHLC 中斷續跑語意及 factor/manual-replay 篩選;本次只調整當日 OHLC 啟動窗口,不重設歷史修復政策。歷史 OHLC 修復/續跑不等於歷史 SES 重算授權。
補跑與修訂通知
- 純補跑的
roster_industry、institutional、tx_futures、margin 在同 flow/目標日已有成功紀錄後,於 lease 內停止後續來源抓取,不新增來源 run 或 Telegram。法人/台指期的既有 chip、market-regime 與 cross-domain readiness 檢查仍可獨立恢復失敗或 stale 的衍生工作,chip/market-regime 沿用各自去重及 run 紀錄;cross-domain 僅在既有 eligibility 查詢發現缺少 coverage 或較新輸入時執行,不因來源 cron 重複呼叫 writer 與 Story finalizer。Story 發布失敗仍由既有 bounded retry、startup 與定時刷新恢復。失敗、等待與其他目標日不會阻擋補跑。法人還須確認本輪 TWSE、TPEx 寫入均成功,不能只靠舊資料判斷本次成功。
- OHLC、市場日資料、估值、當沖、short/borrow、除權息、注意/處置公告、月營收、季報、商品及 ETF 保留既有修訂抓取時段;Hub 每日鏡像亦保留,依實際新增/較新 revision/對帳刪除判斷是否更新。OHLC 早段同官方批次成功的跳過規則亦保留,不增加 canonical 抓取頻率。
- 實際內容未變時,run 保持
success,Telegram 顯示 ℹ️ Cortex 來源端未更新,包含來源與目標日;有效但尚未發布目標日資料則為 skipped 並說明來源日期/缺口。每次均通知,不做同來源/同日去重,也不限制次數。 連線、解析、來源不完整等真錯誤仍為 failed。
- 比較的是寫入範圍內的實際內容,不以抓取筆數、抓取時間或先前成功紀錄當作修訂證據。商品同時比較行情及近期指標;ETF 比較持股新增/修改/刪除,名冊中繼資料不在此計數範圍。公告無當日新公告是有效結果,不強制要求公告日等於交易日。
- ETF 未發布/來源日期不符不再當作抓取失敗;同輪有真實失敗仍優先報錯。既有完整性警示維持警示語意,不能以零變更或未發布掩蓋;同輪有未發布與警示仍為
skipped 並同時說明。長期未補齊且本日已抓取而節流的缺口維持 failed 告警,明示本輪未重新抓取,不能宣稱剛向來源確認未更新。無待抓取缺口的輪次明示「無新增補齊資料」,不宣稱已向來源確認未更新。
- 來源資料相同不代表衍生資料已完成;OHLC、估值與日資料仍保留原有 freshness/下游恢復路徑,估值未變時不再強制重算 financial。
各 flow 的恢復邊界
daily_flow_* source wrapper 以 flow + official target date 取得 PostgreSQL advisory lock,避免同一交易日的 cron、startup 與多 process 重複執行。Commodity cron/catchup 亦共用當日 commodities lease;surveillance、ETF 與 Hub 保留各自 runner 與 single-instance 控制。
- 每次執行寫入
update_runs 與 task_runs。一個 flow 失敗只把自己的 run 標成 failed,其他 flow 繼續。
- Startup、
--run-now 與每日 08:10 recovery 使用相同 source wrappers。它們掃描設定的 lookback 官方交易日,只補沒有成功 flow + date run 的來源;同一天的來源平行執行。每個日期在來源掃描後另做 technical/financial/market-regime freshness 檢查,因此來源已成功但 derived 缺漏、失敗或 stale 時仍會獨立補跑。
- Valuation 的當日入口使用 TWSE/TPEx 最新快照;recovery 遇到較舊官方交易日則呼叫 FinMind historical backfill 的單日區間,避免拿最新快照假裝補歷史日期。
- 每個 retry 都重新跑同一個 idempotent runner。Market-daily 各分區在後續
source_error/no_data/pending_source 時保留已 available 的值,其他成功分區仍可修訂;本輪錯誤仍記 failed。注意/處置公告獨立保存成功端點,任何端點抓取/解析失敗仍讓該 run failed,之後可重試。Partial market-chip upsert 只更新該來源擁有的欄位;失敗嘗試由 update_runs 保存,已是 available 的來源分區不會被降級成 no_data/source_error。
- ETF 16:30 full batch、startup full batch 與所有 retry window 共用
daily-data-flow:etf_holdings:<official date> PostgreSQL advisory lease;full 尚未完成時 retry 直接跳過,不會覆寫同一 ETF/日期的結果。
- Day-trade 歷史 replay 對
revision_stage/is_final 採單調更新;較舊 anchor 可以更新來源數值,但不能把既有 stage 2 final row 降成 stage 0/1。
- 無資料變更的 OHLC 成功重試會保留既有
sync_status.updated_at 內容版本;狀態或錯誤改變仍會推進 timestamp。
- Monthly revenue 與 quarterly income 保留獨立公告型入口,失敗與 valuation 互不牽連。公告窗口每日探測缺漏,週日 audit 捕捉窗口外更正;upsert 先做欄位差異判斷,只有實際新增/修訂才強制重跑 financial readiness。Startup 只在當下落於公告窗口時補一次,不在窗口外額外全量抓取。
- 每個可到達的 strategy writer 之前都執行 hash protected rule-catalog import;catalog 未變更時為 no-op。
公告型來源查證
- 月營收的正式來源是 MOPS 現行快照
t187ap05_L.csv/t187ap05_O.csv。證券交易法第 36 條要求每月 10 日以前申報上月營運;自 2026 會計年度起,保險業及具保險子公司的公開發行公司得延至 15 日。因此 scheduler 在每月 1–15 日探測,完整涵蓋一般與特殊期限。法規來源:證券交易法第 36 條、特殊適用範圍辦法第 3 條。
- 季損益的正式來源是 MOPS 現行快照
t187ap14_L.csv/t187ap14_O.csv。一般公司 Q1/Q2/Q3 是季末後 45 日、年度是年度終了後 3 個月;金控 Q1/Q3 最晚 5/30、11/29,金融業 Q2 最晚 8/31。Scheduler 因此使用 1/1–3/31、4/1–5/30、7/1–8/31、10/1–11/29 四段窗口;固定截止日遇休市時,窗口結束日依 TWSE 官方日曆順延至下一交易日。期限表來源:TWSE 財務報告申報注意事項。
- 2026-10-14 唯讀核對四個正式 CSV:上市/上櫃月營收皆為 2026-09 資料、出表日 2026-10-13;上市/上櫃季損益皆為 2026 Q2、出表日 2026-10-14。這些 endpoint 是會重新出表的全市場現行快照,不提供逐公告 webhook,所以採窗口 polling 加內容差異判斷;週日 audit 負責窗口外更正。23:20/23:25 是 Cortex 的操作時間,安排在 23:10 最後一輪日資料 flow 之後,並非外部法定時間。
週更邊界
- 週五 20:00 同時啟動近 30 日 institutional 維護與 TDCC ownership probe。兩者各自寫 task 狀態;institutional 失敗不會阻止 TDCC。
- TDCC 尚未發布時,沿用動態 DateTrigger:每次完成後約 30 分鐘再試,總預算 60 小時。完成條件是本週 ownership date 到齊、institutional 已到該 ownership date,且 chip snapshot 已反映該版本。
- 週 recap 仍以官方當週最後交易日與所需日更/夜間輸入判斷,不額外綁最新 TDCC。
操作檢查
- 用
update_runs.job_name = 'daily_flow_<flow>' 檢查 close-time source wrapper;commodity、market surveillance、ETF、Hub 分別查 commodity、market_surveillance、etf_retry、hub_participant_flows。
- 用同一 run 的
task_runs 確認實際步驟完成,避免只看 cron 是否 fire。
- Technical/financial 查
daily_derived_technical、daily_derived_financial;chip 查 nightly_chip_chain 與 sync_status。
- ETF 查
etf_holdings_sync、active-ETF facts 與 recap cache;不得以 MAX(stock_ohlc.timestamp) 當 ETF target date。
- Hub flow 先確認 Hub complete batch,再檢查 Cortex 01:00 mirror;Cortex 不直接呼叫外部 acquisition。