Coverage for src/jquantstats/_portfolio_base.py: 100%

4 statements  

« prev     ^ index     » next       coverage.py v7.17.1, created at 2026-11-03 15:52 +0000

1"""Shared type-only declarations for the Portfolio mixins. 

2 

3The four Portfolio mixins (`PortfolioNavMixin`, `PortfolioAttributionMixin`, 

4`PortfolioTurnoverMixin`, `PortfolioCostMixin`) each consume attributes and 

5properties that are actually defined on *sibling* mixins or on the composed 

6`Portfolio` dataclass itself. So that every mixin type-checks in isolation under 

7``mypy --strict``, they all inherit this base, which declares that shared 

8surface once under ``TYPE_CHECKING``. 

9 

10At runtime the body is skipped entirely, so `_PortfolioMembers` is an empty 

11class: it adds nothing to instance layout (the mixins are slot-free already) and 

12nothing to behaviour — it exists purely to give the type checker a single source 

13of truth for the cross-mixin surface instead of repeating the stubs in every 

14mixin module. 

15""" 

16 

17from __future__ import annotations 

18 

19from typing import TYPE_CHECKING 

20 

21import polars as pl 

22 

23if TYPE_CHECKING: 

24 from .data import Data 

25 

26 

27class _PortfolioMembers: 

28 """Type-only declaration of the cross-mixin Portfolio surface (see module docstring).""" 

29 

30 if TYPE_CHECKING: 

31 # Raw inputs — real ``Portfolio`` dataclass fields. 

32 cashposition: pl.DataFrame 

33 prices: pl.DataFrame 

34 aum: float 

35 cost_per_unit: float 

36 cost_bps: float 

37 annual_fee: float 

38 compounding: bool 

39 realized_costs: pl.DataFrame | None 

40 

41 # Derived series / accessors defined on sibling mixins or ``Portfolio``. 

42 @property 

43 def data(self) -> Data: 

44 """Bridge to the legacy `Data` object (defined on `Portfolio`).""" 

45 

46 @property 

47 def assets(self) -> list[str]: 

48 """Asset column names (defined on `Portfolio`).""" 

49 

50 @property 

51 def returns(self) -> pl.DataFrame: 

52 """Daily returns (defined on `PortfolioNavMixin`).""" 

53 

54 @property 

55 def profit(self) -> pl.DataFrame: 

56 """Aggregate daily portfolio profit (defined on `PortfolioNavMixin`).""" 

57 

58 @property 

59 def nav_accumulated(self) -> pl.DataFrame: 

60 """Cumulative additive NAV (defined on `PortfolioNavMixin`).""" 

61 

62 @property 

63 def _return_base(self) -> pl.Series: 

64 """Per-row capital that returns are measured against (defined on `PortfolioNavMixin`).""" 

65 

66 @property 

67 def units(self) -> pl.DataFrame: 

68 """Units held per asset (defined on `PortfolioUnitsMixin`).""" 

69 

70 @property 

71 def turnover(self) -> pl.DataFrame: 

72 """Turnover frame (defined on `PortfolioTurnoverMixin`)."""