Cost Models¶
CostModel is the public API for specifying transaction costs in jQuantStats.
Import it directly from the top-level package:
from jquantstats import CostModel, Portfolio
Overview¶
Every portfolio strategy incurs transaction costs. jQuantStats models these
costs through CostModel, a frozen dataclass that encapsulates exactly one
cost model at a time and enforces that the two models are never accidentally
combined.
Two models are supported:
| Model | Constructor | Scales with |
|---|---|---|
| Model A — per-unit | CostModel.per_unit(cost) |
Units of position change |
| Model B — turnover-bps | CostModel.turnover_bps(bps) |
AUM turnover |
| No cost | CostModel.zero() |
— |
Model A: per_unit — position-delta cost¶
Use this model when your cost scales with the number of units traded — for example, a fixed commission per share or a tick-size cost for futures.
from jquantstats import CostModel, Portfolio
# £0.01 cost per share traded (one-way)
cost = CostModel.per_unit(0.01)
pf = Portfolio.from_cash_position(
prices=prices,
cash_position=positions,
aum=1_000_000,
cost_model=cost,
)
# NAV after deducting position-delta costs
print(pf.net_cost_nav)
# Inspect the raw per-day cost series
print(pf.position_delta_costs)
When to use:
- Equity portfolios where commissions are quoted per share.
- Futures portfolios where tick-size friction dominates.
- Any strategy where absolute position changes (not AUM fraction) drive costs.
Model B: turnover_bps — AUM-proportional cost¶
Use this model when your cost scales with notional turnover as a fraction of AUM — for example, a bid/ask spread expressed in basis points.
from jquantstats import CostModel, Portfolio
# 5 bps one-way cost on AUM turnover
cost = CostModel.turnover_bps(5.0)
pf = Portfolio.from_cash_position(
prices=prices,
cash_position=positions,
aum=1_000_000,
cost_model=cost,
)
# Sweep Sharpe ratio across 0 → 20 bps in a single call
impact = pf.trading_cost_impact(max_bps=20)
print(impact)
When to use:
- Macro or fund-of-funds portfolios where trades are expressed as a fraction of AUM.
- Strategies where the bid/ask spread (in bps) is the dominant cost driver.
- Any scenario where you want to sweep cost assumptions and see impact on risk-adjusted returns.
No transaction costs¶
from jquantstats import CostModel
cost = CostModel.zero()
# Equivalent to the default — no cost_model argument
Combining models is an error¶
CostModel enforces mutual exclusivity. Passing both a non-zero per_unit
and a non-zero cost_bps raises a ValueError:
from jquantstats import CostModel
# This raises ValueError — only one model may be active at a time
CostModel(cost_per_unit=0.01, cost_bps=5.0)
Use the named constructors (per_unit, turnover_bps, zero) to make your
intent explicit and avoid this error.
API Reference¶
CostModel
dataclass
¶
Unified representation of a portfolio transaction-cost model.
Eliminates the implicit "pick one" contract between the two independent
cost parameters (cost_per_unit and cost_bps) on
:class:~jquantstats.portfolio.Portfolio. A CostModel
instance encapsulates one model at a time and can be passed to any
Portfolio factory method instead of specifying the raw float parameters.
Attributes:
| Name | Type | Description |
|---|---|---|
cost_per_unit |
float
|
One-way cost per unit of position change (Model A). Defaults to 0.0. |
cost_bps |
float
|
One-way cost in basis points of AUM turnover (Model B). Defaults to 0.0. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> CostModel.per_unit(0.01)
CostModel(cost_per_unit=0.01, cost_bps=0.0)
>>> CostModel.turnover_bps(5.0)
CostModel(cost_per_unit=0.0, cost_bps=5.0)
>>> CostModel.zero()
CostModel(cost_per_unit=0.0, cost_bps=0.0)
per_unit(cost: float) -> CostModel
classmethod
¶
Create a Model A (position-delta) cost model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cost
|
float
|
One-way cost per unit of position change. Must be non-negative. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
A |
CostModel
|
class: |
CostModel
|
|
Examples:
>>> CostModel.per_unit(0.01)
CostModel(cost_per_unit=0.01, cost_bps=0.0)
turnover_bps(bps: float) -> CostModel
classmethod
¶
Create a Model B (turnover-bps) cost model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bps
|
float
|
One-way cost in basis points of AUM turnover. Must be non-negative. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
A |
CostModel
|
class: |
CostModel
|
|
Examples:
>>> CostModel.turnover_bps(5.0)
CostModel(cost_per_unit=0.0, cost_bps=5.0)