Workflows
Real-Trade Review
On this page5
A backtest report shows theoretical positions; a real-trade review shows what you actually traded. TradeReview uses actual fill prices, fees, taxes, dividends and deposits/withdrawals to rebuild daily holdings, cash and NAV, computes time-weighted returns that exclude external cash flows, and produces the same FinLab report you get from a backtest.
By the end of this page you will have daily holdings and NAV, a time-weighted return, a FinLab report, and a table of how your actual positions differ from the strategy's targets.
Before you start, you need:
- A broker account connected through
finlab.online; see Live trading for setup. If your broker is not supported, or you want to use an exported fill file, see "Use your own fills" at the end of this page. - A start date for the review. When a broker API cannot return data that far back, the review can only start from the earliest available date.
Review straight from a broker account
Connect the broker account
This example uses Charles Schwab. Taiwan brokers and Binance are supported too; see the table below.
from finlab.online.schwab_account import SchwabAccount
acc = SchwabAccount()Build the review
from finlab.portfolio import TradeReview
review = TradeReview.from_account(acc, start="2026-01-01")from_account does four things:
- Pulls fills from
startto today through the broker API (see the table below). - Rebuilds starting holdings from the broker's current holdings (undoing fills, stock dividends and splits in the window), then reconciles the rebuilt ending holdings with the broker; mismatches are listed in
review.limitations. - Applies dividends: Schwab and Binance report them in the account records; for Taiwan accounts FinLab applies its dividend announcements automatically (cash dividends on the ex-dividend date, stock dividends and ETF splits on the ex-rights / resumption date).
- Values holdings daily at unadjusted close.
Read the limitations first
review.limitations # data limitations of this review: always read themReconciliation mismatches and skipped short-sale or securities-lending fills are listed here. Read them before you trust the numbers that follow.
Read the results
review.summary() # start/end NAV, net inflow, dividends, fees and tax, P&L, TWR
review.daily # daily cash, market value, NAV, flows, return, cumulative P&L
review.holdings # shares held at each close
review.trades # FIFO-matched round trips (with dividends); open lots have no exit_date
review.report.display() # FinLab report: equity curve, metrics, MAE/MFE, tradesCompare with strategy targets
compare uses the actual position at the close of each target date, never later fills. Targets can be share counts or weights:
import pandas as pd
target = pd.DataFrame({"AAPL": [20], "MSFT": [10]}, index=["2026-03-02"])
review.compare(target) # date, stock_id, target, actual, diff (shares)
weights = pd.DataFrame({"AAPL": [0.6], "MSFT": [0.4]}, index=["2026-03-02"])
review.compare(weights, kind="weight") # or compare weightsHow returns are computed: account mode and holdings mode
The review picks one of two modes, depending on what the broker reports:
| Mode | When | NAV | External flows |
|---|---|---|---|
| Account | Broker API has complete deposits/withdrawals (Schwab, Binance), or you pass initial_cash and cash_events | cash + market value | deposits, withdrawals |
| Holdings | Taiwan brokers (the settlement account is a bank account the API cannot see) | market value | buys are invested before the open; sell proceeds and dividends are withdrawn after the close |
Daily return r = (NAV + outflow) / (previous NAV + inflow) - 1, chained into the equity curve. CAGR, Sharpe and drawdown in the report are computed from it and are unaffected by deposits and withdrawals. In account mode, pnl is the NAV change minus net external inflow.
Brokers without deposit/withdrawal data (the Taiwan brokers) review holdings only. To get a full account NAV for such an account, pass starting cash and cash flows:
import pandas as pd
review = TradeReview.from_account(
acc,
start="2026-01-01",
initial_cash=50_000,
cash_events=pd.DataFrame(
{
"time": ["2026-03-02"],
"amount": [10_000],
"type": ["deposit"],
}
),
)Broker support
| Broker | Fill source | Fees / tax | Closed positions | Dividends | Deposits / withdrawals | Default mode |
|---|---|---|---|---|---|---|
| Schwab (U.S.) | get_transactions | net amount minus gross | yes | broker records | broker records | account |
| Sinopac (Taiwan) | realized P&L + detail, open lot detail | buys from detail; sells derived from realized P&L (combined in fee) | buy legs recovered | FinLab auto | none | holdings |
| Pocket (Taiwan) | realized P&L, open lot detail | none | as Sinopac when the SDK has the detail call; otherwise shares sold in the window are treated as held at start | FinLab auto | none | holdings |
| Fubon (Taiwan) | filled_history (30 days per call, fubon_neo 2.1.1+) | not in API; estimated from fee_ratio and statutory tax | yes | FinLab auto | none | holdings |
| Taishin, formerly Masterlink (Taiwan) | statement account_statment | actual per fill | yes | FinLab auto | none | holdings |
| E.SUN (Taiwan) | get_transactions_by_date (180 days per call) | actual per fill | yes | FinLab auto | none | holdings |
| Binance | myTrades (per symbol) | paid in quote or base asset; BNB-paid fees are not counted | only currently held assets and those passed in symbols= | quote-asset distributions | quote-asset deposits/withdrawals | account |
Limitations that apply to every broker:
- Only long cash and margin positions are reviewed; short-sale and securities-lending fills are skipped and listed in
limitations. Margin interest is not counted. - Fills that only have a date (most Taiwan brokers) are processed buys first, then sells, on the same day, which does not affect end-of-day valuation.
- Corporate actions the broker or FinLab does not supply are not applied automatically; add them to
corporate_actions(columnsstock_id, date, cash, ratio). Taiwan capital reductions and par value changes fall in this group; Taiwan stock dividends are converted with the par value, rounding fractional shares down. - When a broker API cannot return data that far back, the review can only start from the earliest available date.
Use your own fills
For other brokers or exported files, map them to the standard columns and build the review directly. quantity is in shares; fee and tax are the actual amounts of each fill, and missing fee/tax columns count as 0.
import pandas as pd
from finlab.markets.us import USMarket
from finlab.portfolio import TradeReview
fills = pd.DataFrame(
{
"time": ["2026-01-05 09:45", "2026-01-07 10:00"],
"stock_id": ["MSFT", "MSFT"],
"action": ["buy", "sell"],
"quantity": [50, 50], # shares
"price": [480.0, 486.5],
"fee": [0, 0], # actual amount of this fill; missing fee/tax columns count as 0
}
)
market = USMarket()
review = TradeReview(
fills,
market.get_price("close", adj=False),
start="2026-01-05",
initial_holdings={"AAPL": 100},
# Illustrative dividend: cash per share on the ex-date; ratio 2.0 would be a 2-for-1 split
corporate_actions=pd.DataFrame(
{"stock_id": ["AAPL"], "date": ["2026-02-09"], "cash": [0.25], "ratio": [1.0]}
),
market=market,
)For Taiwan fills, finlab.portfolio.trade_review.tw_corporate_actions(stock_ids, start, end) builds the corporate_actions table from FinLab's Taiwan dividend announcements.
Once built, review works exactly like one created from a broker account.
Next steps
- Live trading Connect a broker account, turn a backtest into positions and place orders.
- From research to live trading The full path from strategy idea through validation to deployment.
- Multi-strategy portfolios Run several strategies in one account and place their orders together.
- finlab.portfolio API Full parameters for
TradeReviewand the portfolio classes.
Was this page helpful?