Source code for fynance.backtest.cost

#!/usr/bin/env python3
# coding: utf-8

""" Transaction cost models for the vectorized backtest engine.

Concretizes the :class:`~fynance.core.protocols.CostModel` seam. Only the
proportional (turnover-based) model ships in 2.0; non-linear slippage is a
documented extension point.

"""

from __future__ import annotations

# Third-party packages
import numpy as np
from numpy.typing import NDArray

# Local packages
from fynance.portfolio.sizing import transaction_cost

__all__ = ['ProportionalCost']


[docs] class ProportionalCost: """ Proportional transaction cost: ``(fee + slippage) * turnover``. Turnover at each step is the absolute traded weight :math:`\\sum_i |w_{t,i} - w_{t-1,i}|` (the first step charges the initial position). Conforms to :class:`~fynance.core.protocols.CostModel`. Parameters ---------- fee : float Proportional fee per unit traded (e.g. ``0.001`` = 10 bps). slippage : float Additional proportional slippage per unit traded. Examples -------- >>> import numpy as np >>> cost = ProportionalCost(fee=0.01) >>> cost(np.array([[1.0, 0.0], [0.5, 0.5], [0.5, 0.5]])) array([0.01, 0.01, 0. ]) """ def __init__(self, fee: float = 0.0, slippage: float = 0.0): """ Store the cost rates. """ self.fee = fee self.slippage = slippage
[docs] def __call__(self, weights: NDArray) -> NDArray[np.float64]: """ Return the per-step proportional cost of a weight book. """ rate = self.fee + self.slippage if rate == 0.0: w = np.asarray(weights, dtype=np.float64) return np.zeros(w.shape[0], dtype=np.float64) return transaction_cost(weights, fee=rate)