134 lines
3.6 KiB
Markdown
134 lines
3.6 KiB
Markdown
# Portfolio Trading Cost Model
|
|
|
|
This document describes the trading cost model used by `portfolio simulate`.
|
|
The current implementation is a simplified open-execution proportional cost
|
|
model. It is intentionally small, explicit, and easy to audit.
|
|
|
|
## Open-Execution Timeline
|
|
|
|
The simulator runs once per trading day:
|
|
|
|
1. A constructed portfolio row provides the target book for an execution date.
|
|
In the current file layout, a target dated `t` is executed at the next
|
|
available market date `d = next(t)`.
|
|
2. Trades are executed at `open[d]`.
|
|
3. Realized positions are held during the trading day.
|
|
4. Daily PnL is marked from `open[d]` to `close[d]` on the newly realized book,
|
|
plus any overnight gap from the previous realized holdings.
|
|
5. Trading cost is charged only on actually realized `traded_shares`, after all
|
|
constraints have clipped the desired trade.
|
|
|
|
This means a fully blocked order has `traded_shares = 0` and therefore zero
|
|
trading cost.
|
|
|
|
## Current Formula
|
|
|
|
For each symbol:
|
|
|
|
```text
|
|
trade_value_i = abs(traded_shares_i * execution_price_i)
|
|
trade_cost_i = trade_value_i * (cost_bps + slippage_bps) / 10000
|
|
```
|
|
|
|
where:
|
|
|
|
```text
|
|
execution_price_i = open_price_i
|
|
```
|
|
|
|
`cost_bps` is the proportional explicit trading-cost rate in basis points.
|
|
`slippage_bps` is modeled as an additional cash cost in basis points. The two
|
|
rates are added linearly. The CLI options `--cost-bps` and `--slippage-bps`
|
|
both default to `0.0`.
|
|
|
|
Example:
|
|
|
|
```text
|
|
traded_shares = 1000
|
|
execution_price = 20 yuan
|
|
cost_bps = 10
|
|
slippage_bps = 5
|
|
|
|
abs(1000 * 20) * 15 / 10000 = 30 yuan
|
|
```
|
|
|
|
## Slippage Convention
|
|
|
|
Slippage is not applied by changing the execution price. It is charged only as
|
|
a cash cost through `trade_cost`.
|
|
|
|
Do not double-count slippage by doing both:
|
|
|
|
```text
|
|
execution_price = open * (1 +/- slippage_bps / 10000)
|
|
trade_cost += trade_value * slippage_bps / 10000
|
|
```
|
|
|
|
The simulator should execute at the open price and subtract the slippage cash
|
|
cost from PnL.
|
|
|
|
## Relationship To The Simulator
|
|
|
|
`ReferenceSimulator.fill()` clips desired trades through constraints first, then
|
|
passes the actual `traded_shares` to the cost model. The per-name result is
|
|
stored in the fills parquet as `trade_cost`.
|
|
|
|
`ReferenceSimulator.run()` sums per-name `trade_cost` into the daily PnL row's
|
|
`cost` column and subtracts that total from daily PnL:
|
|
|
|
```text
|
|
pnl = overnight + intraday - cost_total
|
|
```
|
|
|
|
## What This Model Does Not Cover
|
|
|
|
The current model intentionally does not model:
|
|
|
|
- Minimum commissions.
|
|
- Buy/sell asymmetric fees.
|
|
- Sell-side stamp duty.
|
|
- Exchange handling fees.
|
|
- Regulatory fees.
|
|
- Transfer fees.
|
|
- Date-aware fee schedule changes.
|
|
- Nonlinear price impact.
|
|
- Auction liquidity / queue effects.
|
|
- Partial fills caused by open auction depth.
|
|
|
|
These omissions are deliberate. The current model is the default reference
|
|
model, not a detailed brokerage fee simulator.
|
|
|
|
## Future Extension
|
|
|
|
The simulator is structured around a cost model abstraction:
|
|
|
|
```python
|
|
class CostModel:
|
|
def compute(
|
|
self,
|
|
traded_shares,
|
|
execution_price,
|
|
side,
|
|
date,
|
|
metadata,
|
|
):
|
|
...
|
|
```
|
|
|
|
The current implementation is `SimpleProportionalCostModel`.
|
|
|
|
A future `AShareDetailedCostModel` can add:
|
|
|
|
- Commission, optionally subject to minimum commission.
|
|
- Sell-side stamp duty.
|
|
- Transfer fee.
|
|
- Exchange handling fee.
|
|
- Regulatory fee.
|
|
- Date-aware fee rates.
|
|
- Separate buy-side and sell-side rates.
|
|
- Optional nonlinear slippage / market-impact model.
|
|
|
|
Any future model must preserve the same high-level simulator contract: costs
|
|
are computed from realized trades after constraints, and slippage must not be
|
|
counted both through execution-price adjustment and cash cost.
|