csc20021001 GitHub ↗
Project 03 · Deep divePython · PostgreSQL · Explainable SQL

Crypto Transaction Risk Pipeline

這不是只寫幾條 SQL 的規則範例,而是一條可以重跑的端到端資料管線:從生成事件、載入資料庫、執行偵測、合併案件,到輸出分析師與 KRI 報表。

先用一句話理解這個專案

它模擬交易所風險團隊背後的資料工程:把交易、登入、裝置、帳戶與錢包事件放進 PostgreSQL,用 16 條透明 SQL 規則找訊號,再整理成可排序的人工案件。

Pipeline 是什麼?

Pipeline 是一連串有明確輸入、輸出與順序的資料處理階段。重點不是「跑一次成功」,而是失敗後可重跑、資料不重複、結果可追溯。

專案目的:補上「偵測規則前後」的整條資料路徑

風控規則通常只佔系統的一小段。規則之前要有一致的事件、主鍵、時間與資料品質;規則之後還要去重、計分、合併案件、排序與輸出。少了這些,SQL 查到的結果很難穩定進入人工營運。

這個專案把交易風險分析拆成可以單獨檢查的階段。Python 用固定 seed 生成匿名合成資料,PostgreSQL 保管具關聯與約束的事件,SQL 規則輸出標準 evidence,Python 再把不同規則的 hits 組成 alert 與 case。最後才用隔離的 synthetic labels 評估 precision / recall,並輸出可供試算表使用的 KRI。

目的是展示「資料工程 + 風險方法 + 營運輸出」如何接在一起,而不是聲稱它已經是能直接部署到交易所的犯罪偵測產品。

要解決的問題:資料很多,不代表能產生可信案件

Problem A

事件彼此沒有穩定關係

交易若連不到 account、wallet、device 與 login,規則只能看單一事件,無法建立上下文。

Problem B

重跑會重複寫入

資料載入、alert 與 case 若沒有 idempotency key,失敗重試可能把同一件事寫兩次。

Problem C

規則結果無法交接

每條 SQL 若輸出不同欄位、沒有 reason code 與 JSON evidence,下游無法一致計分與審查。

還有一個評估問題:若偵測 SQL 可以讀取 ground-truth labels,成績會被標答案。專案因此把標籤放在獨立表,規則只讀 transaction、wallet、account、device 與 login;等所有規則執行完,evaluation command 才能使用 labels。

整體解法:八個可重跑、可稽核的階段

完整流程從資料生成開始,到人可以讀的報表結束。每個 CLI command 都能獨立執行,因此可以在中間停下來檢查 CSV、schema、rule hit 或 case,而不是把所有邏輯藏在一個巨大腳本。

Generate固定 seed 合成事件
LoadCOPY staging 與約束
Detect16 條 SQL 規則
Composealert scoring + cases
Exportreports + KRI + evaluation
Stage 1–2

生成與資料庫初始化

建立 user、account、device、wallet、transaction、login 與 ground-truth CSV;schema 包含 PK、FK、domain check、comments 與索引。

Stage 3

可重放載入

使用 PostgreSQL COPY staging;source event 有 unique idempotency key,重跑不會新增重複事件。

Stage 4–5

規則與案件組合

SQL 產生 reason code + JSON evidence,alert 以 deterministic fingerprint 去重,再以 account / day fingerprint 合併 case。

Stage 6–8

報表、評估與 KRI

輸出案件 CSV / JSON、confusion matrix、FP / FN review queue,以及 daily operational metrics。

「可重跑」是這裡的核心:交易與登入有唯一事件鍵,alert 有規則與事件組成的 fingerprint,case 有 account/day fingerprint。若流程在中間失敗,可以重新執行,不需要先手動刪掉半套結果。

資料模型如何把風險上下文接起來

users 是匿名合成身份;一個 user 可以有多個 accounts。account 再連到 devices、wallets、transactions 與 login_events。規則觸發後產生 risk_alerts,多個 alerts 可依帳戶與日期合併成 risk_cases,reviewer 的動作則記錄在 case_actions。

主要實體與它在調查裡的角色
Entity回答的問題關係
accounts誰的行為基準與案件?連到 device、wallet、transaction、login、alert、case
transactions發生了什麼資金事件?source / destination wallet、asset、amount、status、time
login_events提款前是否有新裝置、失敗登入或地理變化?account + device + country + timestamp
risk_alerts哪條規則在什麼時間看到什麼證據?rule、account、score、reason code、JSON evidence
risk_cases哪些 alerts 應該由 reviewer 一起處理?account/day grouping + priority + status
ground_truth_labels合成情境是否被抓到?只供事後 evaluation,不供規則讀取

這種關聯讓 SQL 能回答跨事件問題。例如「新裝置登入後十分鐘內提款」要同時看 device first seen、login timestamp 與 transaction direction;「快速進出」要把 incoming 與 outgoing funds 依帳戶和時間窗口配對。

16 條規則不是 16 個結論,而是 16 種調查線索

規則涵蓋交易速度、24 小時提款、rapid in-and-out、fan-in、fan-out、休眠重新啟動、新裝置提款、shared IP、門檻下拆分、高風險對手方、多事件速度、資產轉換、地理登入改變、登入失敗 burst、深夜活動與歷史金額偏離。

幾個代表性規則:訊號必須和誤報檢查一起看
RuleSignal主要合理解釋
Rapid in-and-out15 分鐘內提走至少 90% 入金套利、再平衡或資金調度
New-device withdrawal首次裝置登入後 10 分鐘內提款使用者合法更換裝置
Structuring below threshold多筆 9,000–9,999.99 美元提款產品或付款限制
Shared IP accounts同一 IP 一天出現至少 5 個帳戶NAT、企業網路或電信商 gateway
Historical deviation至少為歷史平均 5 倍且超過 10,000 美元預定的大額提款

Risk score 怎麼組成?

每條 rule 在版本化 catalog 中有明確 weight。不同訊號同時成立時增加小幅 corroboration bonus,總分上限 100;case priority 再以最高 alert score 加上額外 distinct rules 的加權。Severity band 為 low 0–39、medium 40–64、high 65–84、critical 85–100。

Analyst-facing case evidence
{
  "account_id": "ACC-00006",
  "risk_score": 78,
  "severity": "high",
  "reason_codes": ["NEW_DEVICE_WITHDRAWAL", "RAPID_IN_OUT"],
  "evidence": {
    "withdrawal_amount": 12500,
    "minutes_after_login": 4
  }
}
分數的正確用途

78 是 queue prioritization aid,不是 78% 犯罪機率。Reviewer 仍要查看反證、資料品質與規則已知誤報。

如何從空環境跑到案件與報表

完整版本需要 Python 3.11+、Docker Desktop 與 Git。一般性的 Python 環境與套件安裝方式保留在 README;這裡只留下專案特有的本機設定與 PostgreSQL 啟動步驟。.env 只供本機使用,不應提交到 Git。

Local configuration and PostgreSQL
Copy-Item .env.example .env

docker compose up -d --wait

接著可使用 one-command script,也可以把每個階段分開跑。分開執行比較適合第一次理解專案,因為每一步都能停下來看輸出。

Auditable pipeline stages
python -m risk_pipeline generate-data
python -m risk_pipeline init-db
python -m risk_pipeline load-data
python -m risk_pipeline run-rules
python -m risk_pipeline create-cases
python -m risk_pipeline export-report
python -m risk_pipeline evaluate-rules
python -m risk_pipeline export-kri --sla-hours 24

每一步應該檢查什麼?

  1. generate-data:manifest 數量、主鍵唯一性、資產/chain 覆蓋與 10 種 injected scenario 是否存在。
  2. init-db / load-data:schema constraint、FK、COPY row count 與重跑是否不重複。
  3. run-rules:每條 SQL 的 result contract、reason code、evidence JSON 與 false-positive notes。
  4. create-cases:多條 alerts 是否依 account/day 正確合併,priority 是否和 distinct rules 一致。
  5. evaluate / export-kri:標籤隔離、confusion matrix、FP/FN queue、backlog 與 SLA 指標。

本次實際執行的操作截圖

以下畫面來自本次實際執行專案 CLI、generator、資料檢查、測試與 lint 的輸出,不是自行繪製的示意畫面。本次環境沒有 Docker CLI,因此不假裝 PostgreSQL 已經啟動;資料庫整合留待具備 Docker / PostgreSQL 的環境重現。

Operation 1. CLI help 實際列出八個可獨立執行的 pipeline 階段。
Operation 2. generator 實際產生 50 users、60 accounts 與 1,000 筆 transactions。
Operation 3. 直接檢查輸出的 transactions.csv,確認 ID、帳戶、類型、資產、金額與狀態。
Operation 4. pytest 實際完成 51 項測試,結果全部通過。
Operation 5. Ruff 實際完成程式碼檢查,沒有發現錯誤。

輸出不是只有一張 cases.csv

風險團隊、資料工程師與評估流程需要不同輸出。Case report 提供人工工作佇列;alert export 保留逐規則 evidence;evaluation report 提供 TP / FP / FN / TN;KRI report 則按日輸出 opened、resolved、backlog、high-risk rate 與 SLA breach。

51pytest tests passed
16explainable SQL rules
10injected scenarios
100K+default transactions

測試涵蓋 reproducibility、entity counts、referential integrity、injected labels、asset/chain coverage、scoring boundaries、score cap、unknown reason codes、SQL contracts、schema constraints、idempotency、KRI 公式與 evaluation metrics。Ruff 檢查也全部通過。

本次可驗證的實際小型資料

為了確認 generator 不只是讀 README,我在不啟動資料庫的情況下實際生成 1,000 筆交易:50 users、60 accounts、76 devices、100 wallets、300 login events 與 75 labels。以下是輸出資料的幾個代表列:

本次實際產生的 transactions.csv 前四列
transaction_idaccounttypeassetUSDstatus
TXN-000000001ACC-00059tradeETH267.83completed
TXN-000000002ACC-00020transferUSDT423.55completed
TXN-000000003ACC-00022withdrawalUSDT204.89completed
TXN-000000004ACC-00035depositBTC320.52completed
驗證邊界

本次環境沒有 Docker CLI,因此沒有假裝 PostgreSQL end-to-end 已執行。程式測試、lint 與 generator 已驗證;資料庫整合仍需要在具備 Docker / PostgreSQL 的環境依上述步驟執行。

限制與安全邊界:這是一個 production-shaped portfolio,不是 production system

預設資料量雖可展示關聯、規則與 queue,但合成分布無法重現所有交易所客群、對手策略或標籤延遲。USD conversion 使用固定示範價格,沒有市場資料 feed;wallet 關係只看一跳,沒有 graph database 或鏈上 tracing;country 也是模擬 IP 屬性。

  • 規則門檻和 weights 是說明性選擇,沒有用真實 production outcome 校準。
  • demo 使用單機 CSV load,不是 streaming 或 distributed orchestration。
  • 沒有 row-level access control、正式 secrets management、retention policy 與完整 observability。
  • account-level evaluation 不衡量 alert duplication、time-to-detection 或 case disposition accuracy。

專案不連接 Binance、錢包、鏈節點或任何交易所 API,也不包含 API key、真實身份、IP、裝置、地址或交易。生成資料與報表預設不進 Git。

Responsible use

所有 alerts 都是 leads,不是 verdicts。把這個 demo 改成客戶動作之前,仍需要法律、model risk、隱私、權限、閾值治理與人工審查流程。

圖片放大預覽