實盤覆盤
回測報表看的是理論部位;實盤覆盤看的是你真的成交了什麼。TradeReview 用實際成交價、手續費、交易稅、除權息與入出金,重建每天的持股、現金與淨值,算出排除入出金影響的報酬率(時間加權報酬),並產生和回測同一套的 FinLab 報表。
從券商帳戶直接覆盤
import pandas as pd
from finlab.online.sinopac_account import SinopacAccount
from finlab.portfolio import TradeReview
acc = SinopacAccount()
review = TradeReview.from_account(acc, start="2026-01-01")
review.summary() # 起訖淨值、外部淨投入、股利、費稅、損益、時間加權報酬
review.daily # 每日現金、持股市值、淨值、流入流出、報酬率、累計損益
review.holdings # 每日收盤持股股數
review.trades # FIFO 配對的每段實際交易(含股利),未出清的部位 exit_date 為空
review.limitations # 這次覆盤的資料限制,務必看過
review.report.display() # FinLab 報表:淨值曲線、績效指標、MAE/MFE、交易明細
from_account 會:
- 依券商 API 抓
start到今天的成交(各券商的差異見下表)。 - 由券商目前庫存倒推起始日的庫存(扣回期間成交、除回配股與分割),最後再把重建出的期末庫存和券商庫存對帳,不一致的代號會寫進
review.limitations。 - 台股自動套用 FinLab 公告的除權息:現金股利在除息交易日入帳,配股與 ETF 分割在除權交易日(分割為恢復買賣日)調整股數。
- 以未還原收盤價每日估值。
兩種報酬口徑
| 口徑 | 什麼時候用 | 淨值 | 外部資金流 |
|---|---|---|---|
| 帳戶模式 | 券商 API 有完整入出金(嘉信、幣安),或你給了 initial_cash 與 cash_events |
現金+持股市值 | 入金、出金 |
| 持股模式 | 台股券商(交割戶是銀行帳戶,API 查不到入出金) | 持股市值 | 買進金額視為開盤前投入;賣出淨額與股利視為收盤後取出 |
日報酬 r = (當日淨值 + 當日流出) / (前日淨值 + 當日流入) - 1,串接成累積報酬,報表的年化報酬、夏普、回撤都以此計算,不受入出金影響。帳戶模式下 pnl 是「淨值變化減去外部淨投入」。
要在台股帳戶算含現金的帳戶淨值,自己給起始現金與入出金:
review = TradeReview.from_account(
acc,
start="2026-01-01",
initial_cash=500_000,
cash_events=pd.DataFrame(
{
"time": ["2026-03-02"],
"amount": [100_000],
"type": ["deposit"],
}
),
)
和策略目標比對
compare 用「目標日收盤時」的實際部位和目標比,不會用到之後的成交:
target = pd.DataFrame({"2330": [2000], "2454": [1000]}, index=["2026-03-02"])
review.compare(target) # date, stock_id, target, actual, diff(股數)
review.compare(weights, kind="weight") # 也可以比權重
券商支援與限制
| 券商 | 成交來源 | 手續費/交易稅 | 已平倉部位 | 股利 | 入出金 | 預設口徑 |
|---|---|---|---|---|---|---|
| 永豐 | 已實現損益+明細、未實現部位明細 | 買進取自明細;賣出由已實現損益反推(合併記在 fee) | 可還原買進腿 | FinLab 自動 | 無 | 持股模式 |
| 口袋 | 已實現損益、未實現部位明細 | 無 | SDK 有明細查詢時同永豐;沒有時期間內賣出的股票視為起始日已持有 | FinLab 自動 | 無 | 持股模式 |
| 富邦 | filled_history(每次 30 天,需 fubon_neo 2.1.1 以上) |
API 無逐筆費稅,依 fee_ratio 與法定稅率估算 |
可 | FinLab 自動 | 無 | 持股模式 |
| 元富 | 對帳單 account_statment |
逐筆實際值 | 可 | FinLab 自動 | 無 | 持股模式 |
| 玉山(富果) | get_transactions_by_date(每次 180 天) |
逐筆實際值 | 可 | FinLab 自動 | 無 | 持股模式 |
| 嘉信 | 交易紀錄 get_transactions |
淨額與成交金額的差 | 可 | 券商紀錄 | 券商紀錄 | 帳戶模式 |
| 幣安 | myTrades(逐幣別) |
以報價幣或該幣支付的手續費;以 BNB 支付的未計入 | 只查目前持有與 symbols= 指定的幣別 |
報價幣配發 | 報價幣入出金 | 帳戶模式 |
共同限制:
- 只覆盤現股與融資的多單;融券、借券成交會略過並列在
limitations。融資利息不計。 - 台股成交大多只有日期;同一天先處理買進再處理賣出,每日收盤估值不受影響。
- 減資、面額變更沒有完整的每股退還現金與換股比例資料,不自動處理;遇到時把事件放進
corporate_actions(欄位stock_id, date, cash, ratio)。配股以「元/股 ÷ 面額」換算,零股以無條件捨去計算。 - 券商 API 查不到時間範圍外的資料時,覆盤只能從查得到的那天開始。
用自己的成交紀錄
券商不在上表,或要用匯出的成交檔時,把檔案整理成標準欄位後直接建立:
import pandas as pd
from finlab.markets.tw import TWMarket
from finlab.portfolio import TradeReview
from finlab.portfolio.trade_review import tw_corporate_actions
fills = pd.DataFrame(
{
"time": ["2026-01-05 09:01", "2026-01-07 10:00"],
"stock_id": ["1101", "1101"],
"action": ["buy", "sell"],
"quantity": [5000, 5000], # 股數,不是張數
"price": [35.2, 36.0],
"fee": [250, 256], # 該筆實際金額
"tax": [0, 540],
}
)
market = TWMarket()
review = TradeReview(
fills,
market.get_price("close", adj=False),
start="2026-01-05",
initial_holdings={"2330": 1000},
corporate_actions=tw_corporate_actions(
["1101", "2330"], "2026-01-05", "2026-12-31"
),
market=market,
)