Skip to content

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 cost_per_unit or cost_bps is negative, or if both are non-zero (which would silently double-count costs).

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 with cost_per_unit=cost and

CostModel

cost_bps=0.0.

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 with cost_per_unit=0.0 and

CostModel

cost_bps=bps.

Examples:

>>> CostModel.turnover_bps(5.0)
CostModel(cost_per_unit=0.0, cost_bps=5.0)

zero() -> CostModel classmethod

Create a zero-cost model (no transaction costs).

Returns:

Name Type Description
A CostModel

class:CostModel with both parameters set to 0.0.

Examples:

>>> CostModel.zero()
CostModel(cost_per_unit=0.0, cost_bps=0.0)