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

3 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 _reports subpackage.""" 

2 

3from __future__ import annotations 

4 

5from typing import Protocol, runtime_checkable 

6 

7import polars as pl 

8 

9 

10@runtime_checkable 

11class StatsLike(Protocol): # pragma: no cover 

12 """Structural interface for the statistics methods used by :class:`~jquantstats._reports._data.Reports`.""" 

13 

14 def sharpe(self, periods: int | float | None = None) -> dict[str, float]: 

15 """Annualised Sharpe ratio per asset.""" 

16 ... 

17 

18 def sortino(self, periods: int | float | None = None) -> dict[str, float]: 

19 """Annualised Sortino ratio per asset.""" 

20 ... 

21 

22 def max_drawdown(self) -> dict[str, float]: 

23 """Maximum drawdown per asset.""" 

24 ... 

25 

26 def volatility(self, periods: int | float | None = None) -> dict[str, float]: 

27 """Annualised volatility per asset.""" 

28 ... 

29 

30 def value_at_risk(self, alpha: float = 0.05) -> dict[str, float]: 

31 """Value at Risk per asset.""" 

32 ... 

33 

34 def win_loss_ratio(self) -> dict[str, float]: 

35 """Win/loss ratio per asset.""" 

36 ... 

37 

38 def skew(self) -> dict[str, float]: 

39 """Skewness per asset.""" 

40 ... 

41 

42 def kurtosis(self) -> dict[str, float]: 

43 """Kurtosis per asset.""" 

44 ... 

45 

46 def summary(self) -> pl.DataFrame: 

47 """Full summary DataFrame (one row per metric, one column per asset).""" 

48 ... 

49 

50 

51@runtime_checkable 

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

53 """Structural interface required by the :class:`~jquantstats._reports._data.Reports` class. 

54 

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

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

57 """ 

58 

59 @property 

60 def stats(self) -> StatsLike: 

61 """Statistics facade.""" 

62 ... 

63 

64 

65@runtime_checkable 

66class PlotsLike(Protocol): # pragma: no cover 

67 """Structural interface for the portfolio plots facade used by :class:`~jquantstats._reports._portfolio.Report`.""" 

68 

69 def snapshot(self) -> object: 

70 """NAV + drawdown snapshot figure.""" 

71 ... 

72 

73 def rolling_sharpe_plot(self) -> object: 

74 """Rolling Sharpe figure.""" 

75 ... 

76 

77 def rolling_volatility_plot(self) -> object: 

78 """Rolling volatility figure.""" 

79 ... 

80 

81 def annual_sharpe_plot(self) -> object: 

82 """Annual Sharpe figure.""" 

83 ... 

84 

85 def monthly_returns_heatmap(self) -> object: 

86 """Monthly returns heatmap figure.""" 

87 ... 

88 

89 def correlation_heatmap(self) -> object: 

90 """Correlation heatmap figure.""" 

91 ... 

92 

93 def lead_lag_ir_plot(self) -> object: 

94 """Lead/lag IR figure.""" 

95 ... 

96 

97 def trading_cost_impact_plot(self) -> object: 

98 """Trading cost impact figure.""" 

99 ... 

100 

101 

102@runtime_checkable 

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

104 """Structural interface required by the :class:`~jquantstats._reports._portfolio.Report` class. 

105 

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

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

108 """ 

109 

110 prices: pl.DataFrame 

111 aum: float 

112 

113 @property 

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

115 """Asset names.""" 

116 ... 

117 

118 @property 

119 def plots(self) -> PlotsLike: 

120 """Portfolio plots facade.""" 

121 ... 

122 

123 @property 

124 def stats(self) -> StatsLike: 

125 """Statistics facade.""" 

126 ... 

127 

128 def turnover_summary(self) -> pl.DataFrame: 

129 """Turnover summary DataFrame.""" 

130 ...