Skip to main content

finlab-sentinel

Python versions Windows Linux macOS License: MIT CI coverage

finlab-sentinel 是 finlab 套件的防禦層,用於監控 data.get API 的資料變化,防止未預期的資料異動影響回測或選股結果。

功能特色

  • 自動比對: 每次 data.get 時自動比對歷史資料
  • 滾動備份: 保留 7 天(可配置)的備份資料
  • 智慧檢測:
    • 數值容差比對(可配置 rtol/atol)
    • dtype 變更檢測
    • NA 類型差異檢測(pd.NA vs np.nan vs None)
  • 彈性政策:
    • append_only: 只允許新增,不允許刪除或修改歷史
    • threshold: 允許小幅度變更(如 10% 以內)
    • 黑名單配置:指定可修改歷史的資料集
  • 可配置行為:
    • 拋出例外(預設)
    • 警告並使用快取
    • 警告並使用新資料
  • Preprocess Hook: 比對前預處理(如四捨五入),支援萬用字元模式
  • 通知機制: 支援自訂 callback(如 LINE、email 通知)
  • CLI 工具: 管理備份、查看差異、接受新資料
  • 時間旅行: 回到歷史時間點取得當時的備份資料
  • 永久 Patch: accept 新資料時自動保存舊 baseline 快照,不受備份清理影響,隨時可查閱與匯出

安裝

pip install finlab-sentinel

或使用 uv:

uv add finlab-sentinel

快速開始

import finlab_sentinel

# 啟用 sentinel
finlab_sentinel.enable()

# 正常使用 finlab
from finlab import data
close = data.get('price:收盤價')  # 自動備份並比對

# 如果資料異常,會根據配置拋出例外或警告

配置

建立 sentinel.toml 檔案:

[storage]
path = "~/.finlab-sentinel/"
retention_days = 7

[comparison]
rtol = 1e-5
change_threshold = 0.10

[comparison.policies]
default_mode = "append_only"
history_modifiable = ["fundamental_features:某些財報資料"]
# 允許 NA→有值 的轉換(例如:預估資料後來補上)
allow_na_to_value = ["price:收盤價"]

[anomaly]
behavior = "raise"  # raise | warn_return_cached | warn_return_new
save_reports = true

# 可選:設定通知 callback
# callback = "myproject.notifications:send_line"

CLI 使用

# 列出所有備份
sentinel list

# 清理過期備份
sentinel cleanup --days 14

# 查看資料差異
sentinel diff "price:收盤價"

# 接受新資料作為基準
sentinel accept "price:收盤價" --reason "確認資料修正"

# 匯出備份
sentinel export "price:收盤價" -o ./backup.parquet

# 管理永久 patch(accept 時自動產生)
sentinel patch list
sentinel patch show <patch_id>
sentinel patch export <patch_id> -o ./old_data.parquet
sentinel patch delete <patch_id>

處理資料異常

當檢測到資料異常時:

from finlab_sentinel import DataAnomalyError

try:
    close = data.get('price:收盤價')
except DataAnomalyError as e:
    print(f"資料異常: {e.report.summary}")
    # 檢查報告詳情
    print(f"變動比例: {e.report.comparison_result.change_ratio:.1%}")

    # 如果確認要接受新資料
    from finlab_sentinel.core.interceptor import accept_current_data
    accept_current_data('price:收盤價', reason="確認資料修正")

Preprocess Hook

Preprocess hook 讓你可以在比對前先對資料做預處理,例如四捨五入、排序欄位等。這在處理預期的浮點數精度差異時特別有用。

注意: 預處理只用於比對,回傳給使用者的永遠是原始資料。

import finlab_sentinel

# 註冊特定 dataset 的 preprocess hook
finlab_sentinel.register_preprocess_hook(
    "price:收盤價",
    lambda df: df.round(2)  # 四捨五入到小數第二位
)

# 支援萬用字元模式
finlab_sentinel.register_preprocess_hook(
    "price:*",  # 符合所有 price: 開頭的 dataset
    lambda df: df.round(2)
)

# 也支援 ? 萬用字元(符合單一字元)
finlab_sentinel.register_preprocess_hook(
    "price:?",
    lambda df: df.round(2)
)

finlab_sentinel.enable()

# 使用 finlab
from finlab import data
close = data.get('price:收盤價')  # 比對時會先 round(2),但回傳原始資料

進階用法

import finlab_sentinel

# 自訂預處理函式
def normalize_for_comparison(df):
    """標準化 DataFrame 以忽略預期的差異"""
    df = df.copy()
    # 四捨五入數值欄位
    numeric_cols = df.select_dtypes(include=['float64', 'float32']).columns
    df[numeric_cols] = df[numeric_cols].round(4)
    # 排序欄位(忽略欄位順序差異)
    df = df[sorted(df.columns)]
    return df

finlab_sentinel.register_preprocess_hook("fundamental_features:*", normalize_for_comparison)

# 取消註冊
finlab_sentinel.unregister_preprocess_hook("price:收盤價")

# 清除所有 hooks
finlab_sentinel.clear_preprocess_hooks()

優先順序

當多個 pattern 都符合時,精確匹配優先於萬用字元匹配:

finlab_sentinel.register_preprocess_hook("price:*", lambda df: df.round(1))
finlab_sentinel.register_preprocess_hook("price:收盤價", lambda df: df.round(2))

# "price:收盤價" 會使用 round(2)(精確匹配)
# "price:開盤價" 會使用 round(1)(萬用字元匹配)

時間旅行 (Time Travel)

時間旅行功能讓你可以回到過去的某個時間點,取得當時備份的資料。這對於重現歷史回測結果、調查資料異動,或驗證策略在特定時間點的表現非常有用。

基本用法

import finlab_sentinel as fs
from datetime import datetime

# 啟用 sentinel
fs.enable()

# 設定時間旅行到指定時間點
fs.set_time_travel(datetime(2024, 1, 5, 14, 30))

# 現在 data.get() 會回傳該時間點的備份資料
from finlab import data
close = data.get("price:收盤價")  # 回傳 2024-01-05 14:30 的備份資料

# 結束時間旅行,回到正常模式
fs.exit_time_travel()

查詢狀態

# 查詢目前時間旅行狀態
status = fs.get_time_travel_status()
print(status)
# {'enabled': True, 'target_time': '2024-01-05T14:30:00'}

錯誤處理

from finlab_sentinel import NoHistoricalDataError

try:
    fs.set_time_travel(datetime(2020, 1, 1))  # 很久以前
    close = data.get("price:收盤價")
except NoHistoricalDataError as e:
    print(f"找不到該時間點的備份資料: {e}")
finally:
    fs.exit_time_travel()

使用情境

  • 重現歷史回測: 確保使用與當時相同的資料進行回測
  • 調查資料異動: 比對不同時間點的資料差異
  • 驗證策略表現: 在特定歷史時間點驗證選股策略

永久 Patch

當你 accept 新資料作為 baseline 時,sentinel 會自動建立一個永久 patch,保存 accept 前的舊 baseline 完整快照與 diff 摘要。Patch 存放在 <storage.path>/patches/,不受滾動備份的 retention 清理影響,之後可以隨時查閱、匯出舊資料。

CLI 用法

# 列出所有 patch(可用 --dataset 過濾)
sentinel patch list

# 查看 patch 詳情(含 diff 摘要)
sentinel patch show "price__收盤價__2026-07-11T10-30-00"

# 匯出 accept 前的舊資料
sentinel patch export "price__收盤價__2026-07-11T10-30-00" -o ./old_data.parquet

# 刪除 patch(需確認,或加 --force)
sentinel patch delete "price__收盤價__2026-07-11T10-30-00"

Python API

import finlab_sentinel as fs

# 列出 patch
patches = fs.list_patches("price:收盤價")
for p in patches:
    print(p.patch_id, p.created_at, p.diff_summary["summary_text"])

# 取回 accept 前的舊 baseline DataFrame
old_close = fs.load_patch_data(patches[0].patch_id)

關閉自動建立

在 sentinel.toml 中設定:

[accept]
create_patch = false

注意:若 accept 時新資料與現有 baseline 完全相同(hash 一致),不會產生 patch;patch 建立失敗(如磁碟已滿)不會阻擋 accept 本身,只會記錄錯誤日誌。

自訂通知

def send_line_notification(report):
    """當檢測到異常時發送 LINE 通知"""
    import requests
    requests.post(
        "https://notify-api.line.me/api/notify",
        headers={"Authorization": f"Bearer {LINE_TOKEN}"},
        data={"message": f"finlab 資料異常: {report.summary}"}
    )

# 在 sentinel.toml 中設定
# [anomaly]
# callback = "myproject.notifications:send_line_notification"

開發

# Clone 專案
git clone https://github.com/yourusername/finlab-sentinel
cd finlab-sentinel

# 使用 uv 安裝開發依賴
uv sync --dev

# 執行測試
uv run pytest

# 執行 lint
uv run ruff check src/ tests/
uv run mypy src/

License

MIT License

Metadata

Release files for finlab-sentinel 0.1.9

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for finlab-sentinel 0.1.9
File Size Uploaded Provenance
finlab_sentinel-0.1.9.tar.gz 70.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for finlab-sentinel 0.1.9
File Interpreter ABI Platform
finlab_sentinel-0.1.9-py3-none-any.whl Python 3 none any Details

Total release size: 121.0 kB

Release files / finlab_sentinel-0.1.9.tar.gz

Download URL finlab_sentinel-0.1.9.tar.gz
Size 70.2 kB
Tags Source
SHA-256 checksum
How to use checksums
8bed94be08802a04026fdce7f9638cc9236a7f0e62828bd494b0d83e81d4f186
BLAKE2b-256 checksum
How to use checksums
964fee992129d27c2826ac6d5ed29d707c8c72143c5f299dc2147f9203944f30
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / finlab_sentinel-0.1.9-py3-none-any.whl

Download URL finlab_sentinel-0.1.9-py3-none-any.whl
Size 50.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
782289f00055e49ffda2c8af503dcab7def865c8afa393dc27884f22d45004d4
BLAKE2b-256 checksum
How to use checksums
f93c72203366bb6baa1b7974cd2de9e50e7d81afd4f27dbb745ef31d23f743b1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Security for finlab-sentinel 0.1.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page