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

4 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-23 06:13 +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 

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

39 @property 

40 def data(self) -> Data: 

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

42 

43 @property 

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

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

46 

47 @property 

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

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

50 

51 @property 

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

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

54 

55 @property 

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

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

58 

59 @property 

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

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