"""Trading cost models for portfolio execution simulation.""" from __future__ import annotations from abc import ABC, abstractmethod from dataclasses import dataclass from typing import Mapping import numpy as np class CostModel(ABC): """Interface for per-name execution cost models.""" @abstractmethod def compute( self, traded_shares: np.ndarray, execution_price: np.ndarray, side: np.ndarray, date, metadata: Mapping[str, object] | None = None, ) -> np.ndarray: """Return per-name trading cost in yuan.""" @dataclass(frozen=True) class SimpleProportionalCostModel(CostModel): """Simplified open-execution proportional cost model. Slippage is represented as an additional cash cost. The execution price is not adjusted by slippage, which avoids double-counting. """ cost_bps: float = 0.0 slippage_bps: float = 0.0 def compute( self, traded_shares: np.ndarray, execution_price: np.ndarray, side: np.ndarray, date, metadata: Mapping[str, object] | None = None, ) -> np.ndarray: shares = np.asarray(traded_shares, dtype=np.float64) price = np.asarray(execution_price, dtype=np.float64) open_price = np.where(np.isfinite(price), price, 0.0) trade_value = np.abs(shares * open_price) return trade_value * (self.cost_bps + self.slippage_bps) / 1e4