Skip to main content

finlab-sentinel

Python versions Windows Linux macOS License: MIT CI coverage

finlab-sentinelfinlab 套件的防禦層,用於監控 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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

finlab_sentinel-0.1.8.tar.gz (69.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

finlab_sentinel-0.1.8-py3-none-any.whl (50.5 kB view details)

Uploaded Python 3

File details

Details for the file finlab_sentinel-0.1.8.tar.gz.

File metadata

  • Download URL: finlab_sentinel-0.1.8.tar.gz
  • Upload date:
  • Size: 69.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for finlab_sentinel-0.1.8.tar.gz
Algorithm Hash digest
SHA256 93c0bb30474e0a2bed1f0da2cb094596725349caed435fbf229b3b5fb8153ba1
MD5 4c855b316b6dd1c3c3fd98ac688ee817
BLAKE2b-256 a5f5ecf47e6464f6f96926171a52d44b5eb33cb92e22dd892b65556dcb3764d4

See more details on using hashes here.

File details

Details for the file finlab_sentinel-0.1.8-py3-none-any.whl.

File metadata

File hashes

Hashes for finlab_sentinel-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 0470b32168258b240bdc146ae85fa63f09b790b71ce8d142e80cf4123766fcd7
MD5 bcf5da11fe087b278f88b26980162f64
BLAKE2b-256 05f838f71b7c621de5bcd7b19b97c90e423be63578bd73c7169e5c258770ec20

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.8 This release

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

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