Coverage for src / jquantstats / _plots / _protocol.py: 100%

4 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-26 18:44 +0000

1"""Protocols describing the minimal interfaces required by the _plots subpackage.""" 

2 

3from __future__ import annotations 

4 

5from typing import Protocol, runtime_checkable 

6 

7import polars as pl 

8 

9from jquantstats._cost_model import CostModel 

10 

11 

12@runtime_checkable 

13class DataLike(Protocol): # pragma: no cover 

14 """Structural interface required by the :class:`~jquantstats._plots._data.DataPlots` class. 

15 

16 Any object satisfying this protocol can be passed as ``data`` without a 

17 concrete dependency on :class:`~jquantstats._data.Data`. 

18 """ 

19 

20 @property 

21 def all(self) -> pl.DataFrame: 

22 """Combined DataFrame of date index and returns columns.""" 

23 ... 

24 

25 @property 

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

27 """Names of the asset return columns.""" 

28 ... 

29 

30 

31@runtime_checkable 

32class PortfolioLike(Protocol): # pragma: no cover 

33 """Structural interface required by the :class:`~jquantstats._plots._portfolio.PortfolioPlots` class. 

34 

35 Any object satisfying this protocol can be passed as ``portfolio`` without a 

36 concrete dependency on :class:`~jquantstats.portfolio.Portfolio`. 

37 """ 

38 

39 prices: pl.DataFrame 

40 aum: float 

41 cost_model: CostModel 

42 

43 @property 

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

45 """Accumulated NAV series.""" 

46 ... 

47 

48 @property 

49 def tilt(self) -> PortfolioLike: 

50 """Tilt component portfolio.""" 

51 ... 

52 

53 @property 

54 def timing(self) -> PortfolioLike: 

55 """Timing component portfolio.""" 

56 ... 

57 

58 @property 

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

60 """Net-of-cost accumulated NAV series.""" 

61 ... 

62 

63 @property 

64 def drawdown(self) -> pl.DataFrame: 

65 """Drawdown series.""" 

66 ... 

67 

68 @property 

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

70 """Asset names.""" 

71 ... 

72 

73 @property 

74 def monthly(self) -> pl.DataFrame: 

75 """Monthly returns grouped by year and month.""" 

76 ... 

77 

78 @property 

79 def profits(self) -> pl.DataFrame: 

80 """Per-period profit series.""" 

81 ... 

82 

83 @property 

84 def stats(self) -> object: 

85 """Statistics facade (rolling_sharpe, rolling_volatility, annual_breakdown, sharpe).""" 

86 ... 

87 

88 def lag(self, n: int) -> PortfolioLike: 

89 """Return a lagged copy of this portfolio.""" 

90 ... 

91 

92 def smoothed_holding(self, n: int) -> PortfolioLike: 

93 """Return a smoothed-holdings copy of this portfolio.""" 

94 ... 

95 

96 def trading_cost_impact(self, max_bps: int = 20) -> pl.DataFrame: 

97 """Return a DataFrame of Sharpe vs. one-way trading costs.""" 

98 ... 

99 

100 def correlation(self, frame: pl.DataFrame, name: str = "portfolio") -> pl.DataFrame: 

101 """Return the correlation matrix including the portfolio profit series.""" 

102 ...