Cost Models¶
CostModel is the public API for specifying transaction costs in jQuantStats.
Import it directly from the top-level package:
Portfolio route only
Cost modelling requires the Portfolio entry point — it needs position
data to compute turnover and per-unit costs.
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.
| 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 per_unit
- 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 turnover_bps
- 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)
Warning
Always use the named constructors — per_unit, turnover_bps, zero —
rather than the dataclass constructor directly. They make your intent
explicit and guarantee mutual exclusivity.
API Reference¶
jquantstats.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
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)
Source code in src/jquantstats/_cost_model.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | |
per_unit(cost)
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:
| Type | Description |
|---|---|
CostModel
|
A |
CostModel
|
|
Examples:
Source code in src/jquantstats/_cost_model.py
turnover_bps(bps)
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:
| Type | Description |
|---|---|
CostModel
|
A |
CostModel
|
|
Examples: