qis 3.0.4__tar.gz → 3.0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. {qis-3.0.4 → qis-3.0.6}/PKG-INFO +1 -1
  2. {qis-3.0.4 → qis-3.0.6}/pyproject.toml +4 -3
  3. {qis-3.0.4 → qis-3.0.6}/qis/examples/factsheets/strategy_benchmark.py +15 -2
  4. {qis-3.0.4 → qis-3.0.6}/qis/file_utils.py +12 -8
  5. {qis-3.0.4 → qis-3.0.6}/qis/plots/boxplot.py +1 -0
  6. {qis-3.0.4 → qis-3.0.6}/qis/plots/derived/perf_table.py +11 -0
  7. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/multi_portfolio_data.py +34 -9
  8. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/reports/strategy_benchmark_factsheet.py +245 -28
  9. {qis-3.0.4 → qis-3.0.6}/qis/settings.yaml +1 -0
  10. {qis-3.0.4 → qis-3.0.6}/LICENSE.txt +0 -0
  11. {qis-3.0.4 → qis-3.0.6}/README.md +0 -0
  12. {qis-3.0.4 → qis-3.0.6}/qis/__init__.py +0 -0
  13. {qis-3.0.4 → qis-3.0.6}/qis/examples/best_returns.py +0 -0
  14. {qis-3.0.4 → qis-3.0.6}/qis/examples/bond_futures_portfolio.py +0 -0
  15. {qis-3.0.4 → qis-3.0.6}/qis/examples/bootstrap_analysis.py +0 -0
  16. {qis-3.0.4 → qis-3.0.6}/qis/examples/boxplot_conditional_returns.py +0 -0
  17. {qis-3.0.4 → qis-3.0.6}/qis/examples/btc_asset_corr.py +0 -0
  18. {qis-3.0.4 → qis-3.0.6}/qis/examples/constant_notional.py +0 -0
  19. {qis-3.0.4 → qis-3.0.6}/qis/examples/constant_weight_portfolios.py +0 -0
  20. {qis-3.0.4 → qis-3.0.6}/qis/examples/core/perf_bbg_prices.py +0 -0
  21. {qis-3.0.4 → qis-3.0.6}/qis/examples/core/price_plots.py +0 -0
  22. {qis-3.0.4 → qis-3.0.6}/qis/examples/core/us_election.py +0 -0
  23. {qis-3.0.4 → qis-3.0.6}/qis/examples/credit_spreads.py +0 -0
  24. {qis-3.0.4 → qis-3.0.6}/qis/examples/credit_trackers.py +0 -0
  25. {qis-3.0.4 → qis-3.0.6}/qis/examples/europe_futures.py +0 -0
  26. {qis-3.0.4 → qis-3.0.6}/qis/examples/factsheets/multi_assets.py +0 -0
  27. {qis-3.0.4 → qis-3.0.6}/qis/examples/factsheets/multi_strategy.py +0 -0
  28. {qis-3.0.4 → qis-3.0.6}/qis/examples/factsheets/pyblogs_reports.py +0 -0
  29. {qis-3.0.4 → qis-3.0.6}/qis/examples/factsheets/strategy.py +0 -0
  30. {qis-3.0.4 → qis-3.0.6}/qis/examples/generate_option_rolls.py +0 -0
  31. {qis-3.0.4 → qis-3.0.6}/qis/examples/interpolation_infrequent_returns.py +0 -0
  32. {qis-3.0.4 → qis-3.0.6}/qis/examples/leveraged_strategies.py +0 -0
  33. {qis-3.0.4 → qis-3.0.6}/qis/examples/long_short.py +0 -0
  34. {qis-3.0.4 → qis-3.0.6}/qis/examples/momentum_indices.py +0 -0
  35. {qis-3.0.4 → qis-3.0.6}/qis/examples/oakmark_analysis.py +0 -0
  36. {qis-3.0.4 → qis-3.0.6}/qis/examples/ohlc_vol_analysis.py +0 -0
  37. {qis-3.0.4 → qis-3.0.6}/qis/examples/overnight_returns.py +0 -0
  38. {qis-3.0.4 → qis-3.0.6}/qis/examples/perf_external_assets.py +0 -0
  39. {qis-3.0.4 → qis-3.0.6}/qis/examples/perp_pricing.py +0 -0
  40. {qis-3.0.4 → qis-3.0.6}/qis/examples/readme_performances.py +0 -0
  41. {qis-3.0.4 → qis-3.0.6}/qis/examples/risk_return_frontier.py +0 -0
  42. {qis-3.0.4 → qis-3.0.6}/qis/examples/rolling_performance.py +0 -0
  43. {qis-3.0.4 → qis-3.0.6}/qis/examples/seasonality.py +0 -0
  44. {qis-3.0.4 → qis-3.0.6}/qis/examples/sharpe_vs_sortino.py +0 -0
  45. {qis-3.0.4 → qis-3.0.6}/qis/examples/simulate_quant_strats.py +0 -0
  46. {qis-3.0.4 → qis-3.0.6}/qis/examples/test_ewm.py +0 -0
  47. {qis-3.0.4 → qis-3.0.6}/qis/examples/test_scatter.py +0 -0
  48. {qis-3.0.4 → qis-3.0.6}/qis/examples/try_pybloqs.py +0 -0
  49. {qis-3.0.4 → qis-3.0.6}/qis/examples/universe_corrs.py +0 -0
  50. {qis-3.0.4 → qis-3.0.6}/qis/examples/vix_beta_to_equities_bonds.py +0 -0
  51. {qis-3.0.4 → qis-3.0.6}/qis/examples/vix_conditional_returns.py +0 -0
  52. {qis-3.0.4 → qis-3.0.6}/qis/examples/vix_spy_by_year.py +0 -0
  53. {qis-3.0.4 → qis-3.0.6}/qis/examples/vix_tenor_analysis.py +0 -0
  54. {qis-3.0.4 → qis-3.0.6}/qis/examples/vol_without_weekends.py +0 -0
  55. {qis-3.0.4 → qis-3.0.6}/qis/local_path.py +0 -0
  56. {qis-3.0.4 → qis-3.0.6}/qis/models/README.md +0 -0
  57. {qis-3.0.4 → qis-3.0.6}/qis/models/__init__.py +0 -0
  58. {qis-3.0.4 → qis-3.0.6}/qis/models/linear/__init__.py +0 -0
  59. {qis-3.0.4 → qis-3.0.6}/qis/models/linear/auto_corr.py +0 -0
  60. {qis-3.0.4 → qis-3.0.6}/qis/models/linear/corr_cov_matrix.py +0 -0
  61. {qis-3.0.4 → qis-3.0.6}/qis/models/linear/ewm.py +0 -0
  62. {qis-3.0.4 → qis-3.0.6}/qis/models/linear/ewm_convolution.py +0 -0
  63. {qis-3.0.4 → qis-3.0.6}/qis/models/linear/ewm_factors.py +0 -0
  64. {qis-3.0.4 → qis-3.0.6}/qis/models/linear/ewm_winsor_outliers.py +0 -0
  65. {qis-3.0.4 → qis-3.0.6}/qis/models/linear/pca.py +0 -0
  66. {qis-3.0.4 → qis-3.0.6}/qis/models/linear/plot_correlations.py +0 -0
  67. {qis-3.0.4 → qis-3.0.6}/qis/models/linear/ra_returns.py +0 -0
  68. {qis-3.0.4 → qis-3.0.6}/qis/models/stats/__init__.py +0 -0
  69. {qis-3.0.4 → qis-3.0.6}/qis/models/stats/bootstrap.py +0 -0
  70. {qis-3.0.4 → qis-3.0.6}/qis/models/stats/ohlc_vol.py +0 -0
  71. {qis-3.0.4 → qis-3.0.6}/qis/models/stats/rolling_stats.py +0 -0
  72. {qis-3.0.4 → qis-3.0.6}/qis/models/stats/test_bootstrap.py +0 -0
  73. {qis-3.0.4 → qis-3.0.6}/qis/perfstats/README.md +0 -0
  74. {qis-3.0.4 → qis-3.0.6}/qis/perfstats/__init__.py +0 -0
  75. {qis-3.0.4 → qis-3.0.6}/qis/perfstats/cond_regression.py +0 -0
  76. {qis-3.0.4 → qis-3.0.6}/qis/perfstats/config.py +0 -0
  77. {qis-3.0.4 → qis-3.0.6}/qis/perfstats/desc_table.py +0 -0
  78. {qis-3.0.4 → qis-3.0.6}/qis/perfstats/fx_ops.py +0 -0
  79. {qis-3.0.4 → qis-3.0.6}/qis/perfstats/perf_stats.py +0 -0
  80. {qis-3.0.4 → qis-3.0.6}/qis/perfstats/regime_classifier.py +0 -0
  81. {qis-3.0.4 → qis-3.0.6}/qis/perfstats/returns.py +0 -0
  82. {qis-3.0.4 → qis-3.0.6}/qis/perfstats/timeseries_bfill.py +0 -0
  83. {qis-3.0.4 → qis-3.0.6}/qis/plots/README.md +0 -0
  84. {qis-3.0.4 → qis-3.0.6}/qis/plots/__init__.py +0 -0
  85. {qis-3.0.4 → qis-3.0.6}/qis/plots/bars.py +0 -0
  86. {qis-3.0.4 → qis-3.0.6}/qis/plots/contour.py +0 -0
  87. {qis-3.0.4 → qis-3.0.6}/qis/plots/derived/__init__.py +0 -0
  88. {qis-3.0.4 → qis-3.0.6}/qis/plots/derived/data_timeseries.py +0 -0
  89. {qis-3.0.4 → qis-3.0.6}/qis/plots/derived/desc_table.py +0 -0
  90. {qis-3.0.4 → qis-3.0.6}/qis/plots/derived/drawdowns.py +0 -0
  91. {qis-3.0.4 → qis-3.0.6}/qis/plots/derived/prices.py +0 -0
  92. {qis-3.0.4 → qis-3.0.6}/qis/plots/derived/regime_class_table.py +0 -0
  93. {qis-3.0.4 → qis-3.0.6}/qis/plots/derived/regime_data.py +0 -0
  94. {qis-3.0.4 → qis-3.0.6}/qis/plots/derived/regime_pdf.py +0 -0
  95. {qis-3.0.4 → qis-3.0.6}/qis/plots/derived/regime_scatter.py +0 -0
  96. {qis-3.0.4 → qis-3.0.6}/qis/plots/derived/returns_heatmap.py +0 -0
  97. {qis-3.0.4 → qis-3.0.6}/qis/plots/derived/returns_scatter.py +0 -0
  98. {qis-3.0.4 → qis-3.0.6}/qis/plots/errorbar.py +0 -0
  99. {qis-3.0.4 → qis-3.0.6}/qis/plots/heatmap.py +0 -0
  100. {qis-3.0.4 → qis-3.0.6}/qis/plots/histogram.py +0 -0
  101. {qis-3.0.4 → qis-3.0.6}/qis/plots/histplot2d.py +0 -0
  102. {qis-3.0.4 → qis-3.0.6}/qis/plots/lineplot.py +0 -0
  103. {qis-3.0.4 → qis-3.0.6}/qis/plots/pie.py +0 -0
  104. {qis-3.0.4 → qis-3.0.6}/qis/plots/qqplot.py +0 -0
  105. {qis-3.0.4 → qis-3.0.6}/qis/plots/reports/__init__.py +0 -0
  106. {qis-3.0.4 → qis-3.0.6}/qis/plots/reports/econ_data_single.py +0 -0
  107. {qis-3.0.4 → qis-3.0.6}/qis/plots/reports/gantt_data_history.py +0 -0
  108. {qis-3.0.4 → qis-3.0.6}/qis/plots/reports/price_history.py +0 -0
  109. {qis-3.0.4 → qis-3.0.6}/qis/plots/reports/utils.py +0 -0
  110. {qis-3.0.4 → qis-3.0.6}/qis/plots/scatter.py +0 -0
  111. {qis-3.0.4 → qis-3.0.6}/qis/plots/stackplot.py +0 -0
  112. {qis-3.0.4 → qis-3.0.6}/qis/plots/table.py +0 -0
  113. {qis-3.0.4 → qis-3.0.6}/qis/plots/time_series.py +0 -0
  114. {qis-3.0.4 → qis-3.0.6}/qis/plots/utils.py +0 -0
  115. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/README.md +0 -0
  116. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/__init__.py +0 -0
  117. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/backtester.py +0 -0
  118. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/ewm_portfolio_risk.py +0 -0
  119. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/portfolio_data.py +0 -0
  120. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/reports/__init__.py +0 -0
  121. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/reports/brinson_attribution.py +0 -0
  122. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/reports/config.py +0 -0
  123. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/reports/multi_assets_factsheet.py +0 -0
  124. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/reports/multi_strategy_factseet_pybloqs.py +0 -0
  125. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/reports/multi_strategy_factsheet.py +0 -0
  126. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/reports/strategy_benchmark_factsheet_pybloqs.py +0 -0
  127. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/reports/strategy_factsheet.py +0 -0
  128. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/reports/strategy_signal_factsheet.py +0 -0
  129. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/strats/__init__.py +0 -0
  130. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/strats/quant_strats_delta1.py +0 -0
  131. {qis-3.0.4 → qis-3.0.6}/qis/portfolio/strats/seasonal_strats.py +0 -0
  132. {qis-3.0.4 → qis-3.0.6}/qis/sql_engine.py +0 -0
  133. {qis-3.0.4 → qis-3.0.6}/qis/test_data.py +0 -0
  134. {qis-3.0.4 → qis-3.0.6}/qis/utils/README.md +0 -0
  135. {qis-3.0.4 → qis-3.0.6}/qis/utils/__init__.py +0 -0
  136. {qis-3.0.4 → qis-3.0.6}/qis/utils/dates.py +0 -0
  137. {qis-3.0.4 → qis-3.0.6}/qis/utils/df_agg.py +0 -0
  138. {qis-3.0.4 → qis-3.0.6}/qis/utils/df_cut.py +0 -0
  139. {qis-3.0.4 → qis-3.0.6}/qis/utils/df_freq.py +0 -0
  140. {qis-3.0.4 → qis-3.0.6}/qis/utils/df_groups.py +0 -0
  141. {qis-3.0.4 → qis-3.0.6}/qis/utils/df_melt.py +0 -0
  142. {qis-3.0.4 → qis-3.0.6}/qis/utils/df_ops.py +0 -0
  143. {qis-3.0.4 → qis-3.0.6}/qis/utils/df_str.py +0 -0
  144. {qis-3.0.4 → qis-3.0.6}/qis/utils/df_to_scores.py +0 -0
  145. {qis-3.0.4 → qis-3.0.6}/qis/utils/df_to_weights.py +0 -0
  146. {qis-3.0.4 → qis-3.0.6}/qis/utils/generic.py +0 -0
  147. {qis-3.0.4 → qis-3.0.6}/qis/utils/np_ops.py +0 -0
  148. {qis-3.0.4 → qis-3.0.6}/qis/utils/ols.py +0 -0
  149. {qis-3.0.4 → qis-3.0.6}/qis/utils/sampling.py +0 -0
  150. {qis-3.0.4 → qis-3.0.6}/qis/utils/struct_ops.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: qis
3
- Version: 3.0.4
3
+ Version: 3.0.6
4
4
  Summary: Implementation of visualisation and reporting analytics for Quantitative Investment Strategies
5
5
  License: LICENSE.txt
6
6
  Keywords: quantitative,investing,portfolio optimization,systematic strategies,volatility
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "qis"
3
- version = "3.0.4"
3
+ version = "3.0.6"
4
4
  description = "Implementation of visualisation and reporting analytics for Quantitative Investment Strategies"
5
5
  license = "LICENSE.txt"
6
6
  authors = ["Artur Sepp <artursepp@gmail.com>"]
@@ -51,5 +51,6 @@ fsspec = ">=2022.11.0"
51
51
  yfinance = ">=0.1.38"
52
52
 
53
53
  [build-system]
54
- requires = ["poetry-core>=1.0.0"]
55
- build-backend = "poetry.core.masonry.api"
54
+ requires = ["poetry-core>=1.0.0", "hatchling==1.27.0", "hatch-vcs"]
55
+ #build-backend = "hatchling.build"
56
+ build-backend = "poetry.core.masonry.api"
@@ -17,7 +17,8 @@ from qis.portfolio.reports.config import fetch_default_report_kwargs
17
17
  from qis.portfolio.reports.strategy_benchmark_factsheet import (generate_strategy_benchmark_factsheet_plt,
18
18
  generate_strategy_benchmark_active_perf_plt,
19
19
  generate_performance_attribution_report,
20
- weights_tracking_error_report)
20
+ weights_tracking_error_report,
21
+ weights_tracking_error_report_cross)
21
22
 
22
23
 
23
24
  def fetch_universe_data() -> Tuple[pd.DataFrame, pd.DataFrame, pd.Series]:
@@ -79,6 +80,7 @@ class UnitTests(Enum):
79
80
  PERFORMANCE_ATTRIBUTION = 2
80
81
  ACTIVE_PERFORMANCE = 3
81
82
  TRACKING_ERROR = 4
83
+ TRACKING_ERROR_CROSS = 5
82
84
 
83
85
 
84
86
  @qis.timer
@@ -140,12 +142,23 @@ def run_unit_test(unit_test: UnitTests):
140
142
  weights_tracking_error_report(multi_portfolio_data=multi_portfolio_data,
141
143
  time_period=time_period)
142
144
 
145
+ elif unit_test == UnitTests.TRACKING_ERROR_CROSS:
146
+ # compute pd_covras
147
+ covar_dict = qis.estimate_rolling_ewma_covar(prices=prices,
148
+ time_period=time_period,
149
+ returns_freq='W-WED',
150
+ rebalancing_freq='ME',
151
+ span=52)
152
+ multi_portfolio_data.covar_dict = covar_dict
153
+ weights_tracking_error_report_cross(multi_portfolio_data=multi_portfolio_data,
154
+ time_period=time_period)
155
+
143
156
  plt.show()
144
157
 
145
158
 
146
159
  if __name__ == '__main__':
147
160
 
148
- unit_test = UnitTests.TRACKING_ERROR
161
+ unit_test = UnitTests.TRACKING_ERROR_CROSS
149
162
 
150
163
  is_run_all_tests = False
151
164
  if is_run_all_tests:
@@ -231,17 +231,21 @@ def save_df_to_excel(data: Union[pd.DataFrame, List[pd.DataFrame], Dict[str, pd.
231
231
  if sheet_names is None:
232
232
  sheet_names = [f"Sheet {n+1}" for n, _ in enumerate(data)]
233
233
  for df, name in zip(data, sheet_names):
234
- df = delocalize_df(df)
235
- if transpose:
236
- df = df.T
237
- df.to_excel(excel_writer=excel_writer, sheet_name=name)
234
+ if df is not None:
235
+ df = delocalize_df(df)
236
+ if transpose:
237
+ df = df.T
238
+ df.to_excel(excel_writer=excel_writer, sheet_name=name)
238
239
  elif isinstance(data, dict): # publish with sheet names
239
240
  for key, df in data.items():
240
- df = delocalize_df(df)
241
- if transpose:
242
- df = df.T
243
- df.to_excel(excel_writer=excel_writer, sheet_name=key)
241
+ if df is not None:
242
+ df = delocalize_df(df)
243
+ if transpose:
244
+ df = df.T
245
+ df.to_excel(excel_writer=excel_writer, sheet_name=key)
244
246
  else:
247
+ if data is None:
248
+ raise ValueError(f"None data")
245
249
  if transpose:
246
250
  data = data.T
247
251
  data = delocalize_df(data)
@@ -552,6 +552,7 @@ def run_unit_test(unit_test: UnitTests):
552
552
  hue_var_name='instruments',
553
553
  y_var_name='weights',
554
554
  ylabel='weights',
555
+ legend_loc=None,
555
556
  showmedians=True,
556
557
  add_y_median_labels=True)
557
558
 
@@ -125,6 +125,8 @@ def plot_ra_perf_table_benchmark(prices: pd.DataFrame,
125
125
  fontsize: int = 10,
126
126
  transpose: bool = False,
127
127
  alpha_an_factor: float = None,
128
+ is_convert_to_str: bool = True,
129
+ df_to_add: pd.DataFrame = None,
128
130
  ax: plt.Subplot = None,
129
131
  **kwargs
130
132
  ) -> Tuple[Optional[plt.Figure], pd.DataFrame]:
@@ -138,10 +140,19 @@ def plot_ra_perf_table_benchmark(prices: pd.DataFrame,
138
140
  perf_columns=perf_columns,
139
141
  column_header=column_header,
140
142
  alpha_an_factor=alpha_an_factor,
143
+ is_convert_to_str=is_convert_to_str,
141
144
  **kwargs)
142
145
  if not drop_benchmark and special_rows_colors is None:
143
146
  special_rows_colors = [(1, 'skyblue')] # for benchmarl separation
144
147
  kwargs = sop.update_kwargs(kwargs, dict(special_rows_colors=special_rows_colors))
148
+
149
+ if df_to_add is not None:
150
+ df_to_add = df_to_add.reindex(index=ra_perf_table.index)
151
+ if is_convert_to_str:
152
+ df_to_add = df_to_add.fillna('')
153
+ ra_perf_table = pd.concat([ra_perf_table, df_to_add], axis=1)
154
+
155
+
145
156
  fig = ptb.plot_df_table(df=ra_perf_table,
146
157
  transpose=transpose,
147
158
  special_columns_colors=special_columns_colors,
@@ -427,6 +427,9 @@ class MultiPortfolioData:
427
427
  time_period: TimePeriod = None,
428
428
  perf_params: PerfParams = PERF_PARAMS,
429
429
  perf_columns: List[PerfStat] = rpt.BENCHMARK_TABLE_COLUMNS,
430
+ strategy_idx: int = 0,
431
+ benchmark_idx: int = 1,
432
+ add_turnover: bool = False,
430
433
  ax: plt.Subplot = None,
431
434
  **kwargs
432
435
  ) -> pd.DataFrame:
@@ -437,6 +440,14 @@ class MultiPortfolioData:
437
440
  drop_benchmark = False
438
441
  ra_perf_title = f"RA performance table for {perf_params.freq_vol}-freq returns with beta to {benchmark}: " \
439
442
  f"{qis.get_time_period(prices).to_str()}"
443
+
444
+ if add_turnover:
445
+ turnover = self.get_turnover(time_period=time_period, **kwargs)
446
+ turnover = turnover.mean(axis=0).to_frame('Turnover')
447
+ df_to_add = qis.df_to_str(turnover, var_format='{:,.0%}')
448
+ else:
449
+ df_to_add = None
450
+
440
451
  fig, ra_perf_table = ppt.plot_ra_perf_table_benchmark(prices=prices,
441
452
  benchmark=benchmark,
442
453
  perf_params=perf_params,
@@ -444,6 +455,7 @@ class MultiPortfolioData:
444
455
  drop_benchmark=drop_benchmark,
445
456
  title=ra_perf_title,
446
457
  rotation_for_columns_headers=0,
458
+ df_to_add=df_to_add,
447
459
  ax=ax,
448
460
  **kwargs)
449
461
  return ra_perf_table
@@ -598,7 +610,24 @@ class MultiPortfolioData:
598
610
  **kwargs)
599
611
  if benchmark is not None:
600
612
  self.add_regime_shadows(ax=ax, regime_benchmark=benchmark, index=diff.index, regime_params=regime_params)
601
-
613
+
614
+ def get_turnover(self,
615
+ time_period: TimePeriod = None,
616
+ turnover_rolling_period: Optional[int] = 12,
617
+ freq_turnover: Optional[str] = 'ME',
618
+ is_unit_based_traded_volume: bool = True,
619
+ **kwargs
620
+ ):
621
+ turnover = []
622
+ for portfolio in self.portfolio_datas:
623
+ turnover.append(portfolio.get_turnover(roll_period=turnover_rolling_period, freq=freq_turnover, is_agg=True,
624
+ is_unit_based_traded_volume=is_unit_based_traded_volume).rename(
625
+ portfolio.nav.name))
626
+ turnover = pd.concat(turnover, axis=1)
627
+ if time_period is not None:
628
+ turnover = time_period.locate(turnover)
629
+ return turnover
630
+
602
631
  def plot_turnover(self,
603
632
  benchmark: str = None,
604
633
  time_period: TimePeriod = None,
@@ -609,14 +638,10 @@ class MultiPortfolioData:
609
638
  is_unit_based_traded_volume: bool = True,
610
639
  ax: plt.Subplot = None,
611
640
  **kwargs) -> None:
612
-
613
- turnover = []
614
- for portfolio in self.portfolio_datas:
615
- turnover.append(portfolio.get_turnover(roll_period=turnover_rolling_period, freq=freq_turnover, is_agg=True,
616
- is_unit_based_traded_volume=is_unit_based_traded_volume).rename(portfolio.nav.name))
617
- turnover = pd.concat(turnover, axis=1)
618
- if time_period is not None:
619
- turnover = time_period.locate(turnover)
641
+
642
+ turnover = self.get_turnover(turnover_rolling_period=turnover_rolling_period, freq_turnover=freq_turnover,
643
+ is_unit_based_traded_volume=is_unit_based_traded_volume,
644
+ time_period=time_period)
620
645
  freq = pd.infer_freq(turnover.index)
621
646
  turnover_title = f"{turnover_rolling_period}-period rolling {freq}-freq Turnover"
622
647
  pts.plot_time_series(df=turnover,
@@ -542,6 +542,7 @@ def weights_tracking_error_report(multi_portfolio_data: MultiPortfolioData,
542
542
  add_benchmarks_to_navs=add_benchmarks_to_navs,
543
543
  perf_params=perf_params,
544
544
  time_period=time_period,
545
+ add_turnover=True,
545
546
  ax=ax,
546
547
  **kwargs)
547
548
  dfs['ra_perf_table'] = ra_perf_table
@@ -556,11 +557,11 @@ def weights_tracking_error_report(multi_portfolio_data: MultiPortfolioData,
556
557
  fig, axs = plt.subplots(1, 2, figsize=figsize, tight_layout=True)
557
558
  qis.set_suptitle(fig, title=f"{strategy_data.ticker} Weights")
558
559
  figs['strategy_weights'] = fig
559
- plot_exposures(exposures_short=exposures_short,
560
- exposures_long=exposures_long,
561
- ylabel='Weights',
562
- var_format=var_format,
563
- axs=axs, **kwargs)
560
+ plot_exposures_long_short_groups(exposures_short=exposures_short,
561
+ exposures_long=exposures_long,
562
+ ylabel='Weights',
563
+ var_format=var_format,
564
+ axs=axs, **kwargs)
564
565
 
565
566
  rc_kwargs = dict(covar_dict=multi_portfolio_data.covar_dict, freq='QE')
566
567
  # strategy risk contributions
@@ -577,11 +578,11 @@ def weights_tracking_error_report(multi_portfolio_data: MultiPortfolioData,
577
578
  fig, axs = plt.subplots(1, 2, figsize=figsize, tight_layout=True)
578
579
  qis.set_suptitle(fig, title=f"{strategy_data.ticker} Risk Contributions")
579
580
  figs['strategy_var'] = fig
580
- plot_exposures(exposures_short=qis.df_to_weight_allocation_sum1(risk_contributions_short),
581
- exposures_long=qis.df_to_weight_allocation_sum1(risk_contributions_long),
582
- ylabel='Risk Contributions',
583
- var_format=var_format,
584
- axs=axs, **kwargs)
581
+ plot_exposures_long_short_groups(exposures_short=qis.df_to_weight_allocation_sum1(risk_contributions_short),
582
+ exposures_long=qis.df_to_weight_allocation_sum1(risk_contributions_long),
583
+ ylabel='Risk Contributions',
584
+ var_format=var_format,
585
+ axs=axs, **kwargs)
585
586
 
586
587
  # benchmark weights
587
588
  benchmark_data = multi_portfolio_data.portfolio_datas[benchmark_idx]
@@ -593,11 +594,11 @@ def weights_tracking_error_report(multi_portfolio_data: MultiPortfolioData,
593
594
  fig, axs = plt.subplots(1, 2, figsize=figsize, tight_layout=True)
594
595
  qis.set_suptitle(fig, title=f"{benchmark_data.ticker} Weights")
595
596
  figs['benchmark_weights'] = fig
596
- plot_exposures(exposures_short=benchmark_exposures_short,
597
- exposures_long=benchmark_exposures_long,
598
- ylabel='Weights',
599
- var_format=var_format,
600
- axs=axs, **kwargs)
597
+ plot_exposures_long_short_groups(exposures_short=benchmark_exposures_short,
598
+ exposures_long=benchmark_exposures_long,
599
+ ylabel='Weights',
600
+ var_format=var_format,
601
+ axs=axs, **kwargs)
601
602
 
602
603
  # benchmark var
603
604
  benchmark_risk_contributions_short = benchmark_data.compute_risk_contributions_implied_by_covar(
@@ -614,11 +615,11 @@ def weights_tracking_error_report(multi_portfolio_data: MultiPortfolioData,
614
615
  fig, axs = plt.subplots(1, 2, figsize=figsize, tight_layout=True)
615
616
  qis.set_suptitle(fig, title=f"{benchmark_data.ticker} Risk Contributions")
616
617
  figs['benchmark_var'] = fig
617
- plot_exposures(exposures_short=qis.df_to_weight_allocation_sum1(benchmark_risk_contributions_short),
618
- exposures_long=qis.df_to_weight_allocation_sum1(benchmark_risk_contributions_long),
619
- ylabel='Risk Contributions',
620
- var_format=var_format,
621
- axs=axs, **kwargs)
618
+ plot_exposures_long_short_groups(exposures_short=qis.df_to_weight_allocation_sum1(benchmark_risk_contributions_short),
619
+ exposures_long=qis.df_to_weight_allocation_sum1(benchmark_risk_contributions_long),
620
+ ylabel='Risk Contributions',
621
+ var_format=var_format,
622
+ axs=axs, **kwargs)
622
623
 
623
624
  # turnover
624
625
  fig, ax = plt.subplots(1, 1, figsize=figsize, tight_layout=True)
@@ -653,14 +654,181 @@ def weights_tracking_error_report(multi_portfolio_data: MultiPortfolioData,
653
654
  return figs, dfs
654
655
 
655
656
 
656
- def plot_exposures(exposures_short: pd.DataFrame,
657
- exposures_long: pd.DataFrame,
658
- axs: List[plt.Subplot],
659
- ylabel: str = 'weights',
660
- var_format: str = '{:.1%}',
661
- hue_var_name: str = 'asset class',
662
- **kwargs
663
- ) -> None:
657
+ def weights_tracking_error_report_cross(multi_portfolio_data: MultiPortfolioData,
658
+ strategy_idx: int = 0,
659
+ benchmark_idx: int = 1,
660
+ group_data: pd.Series = None,
661
+ group_order: List[str] = None,
662
+ group_data_short: pd.Series = None,
663
+ group_order_short: List[str] = None,
664
+ time_period: TimePeriod = None,
665
+ perf_params: PerfParams = PERF_PARAMS,
666
+ regime_params: BenchmarkReturnsQuantileRegimeSpecs = REGIME_PARAMS,
667
+ add_benchmarks_to_navs: bool = True,
668
+ figsize: Tuple[float, float] = (11.7, 8.3),
669
+ var_format: str = '{:.1%}',
670
+ **kwargs
671
+ ) -> Tuple[Dict[str, plt.Figure], Dict[str, pd.DataFrame]]:
672
+
673
+ regime_benchmark = multi_portfolio_data.benchmark_prices.columns[0]
674
+ benchmark_price = multi_portfolio_data.benchmark_prices[regime_benchmark]
675
+
676
+ figs = {}
677
+ dfs = {}
678
+ with sns.axes_style('darkgrid'):
679
+
680
+ # navs + ra table
681
+ fig, ax = plt.subplots(1, 1, figsize=figsize, tight_layout=True)
682
+ figs['navs'] = fig
683
+ multi_portfolio_data.plot_nav(regime_benchmark=regime_benchmark,
684
+ time_period=time_period,
685
+ perf_params=perf_params,
686
+ regime_params=regime_params,
687
+ add_benchmarks_to_navs=add_benchmarks_to_navs,
688
+ title=f"Cumulative performance with background colors using bear/normal/bull "
689
+ f"regimes of {regime_benchmark} {regime_params.freq}-returns",
690
+ ax=ax,
691
+ **kwargs)
692
+
693
+ fig, ax = plt.subplots(1, 1, figsize=figsize, tight_layout=True)
694
+ figs['ra_table'] = fig
695
+ ra_perf_table = multi_portfolio_data.plot_ra_perf_table(benchmark_price=benchmark_price,
696
+ add_benchmarks_to_navs=add_benchmarks_to_navs,
697
+ perf_params=perf_params,
698
+ time_period=time_period,
699
+ add_turnover=True,
700
+ ax=ax,
701
+ **kwargs)
702
+ dfs['ra_perf_table'] = ra_perf_table
703
+
704
+ # strategy weights
705
+ strategy_data = multi_portfolio_data.portfolio_datas[strategy_idx]
706
+ weight_kwargs = dict(is_grouped=True, time_period=time_period, add_total=False, is_input_weights=True)
707
+ strategy_exposures_short = strategy_data.get_weights(group_data=group_data_short, group_order=group_order_short,
708
+ **weight_kwargs)
709
+ strategy_exposures_long = strategy_data.get_weights(group_data=group_data, group_order=group_order,
710
+ **weight_kwargs)
711
+
712
+ # benchmark weights
713
+ benchmark_data = multi_portfolio_data.portfolio_datas[benchmark_idx]
714
+ benchmark_exposures_short = benchmark_data.get_weights(group_data=group_data_short, group_order=group_order_short,
715
+ **weight_kwargs)
716
+ benchmark_exposures_long = benchmark_data.get_weights(group_data=group_data, group_order=group_order,
717
+ **weight_kwargs)
718
+
719
+ # plot strategy and benchmark weights
720
+ fig, axs = plt.subplots(1, 2, figsize=figsize, tight_layout=True)
721
+ qis.set_suptitle(fig, title=f"{strategy_data.ticker} Weights")
722
+ figs['strategy_benchmark_weights_stack'] = fig
723
+ plot_exposures_strategy_vs_benchmark_stack(strategy_exposures=strategy_exposures_short,
724
+ benchmark_exposures=benchmark_exposures_short,
725
+ axs=axs,
726
+ var_format=var_format,
727
+ **kwargs)
728
+
729
+ # boxplot
730
+ fig, axs = plt.subplots(1, 2, figsize=figsize, tight_layout=True)
731
+ qis.set_suptitle(fig, title=f"{strategy_data.ticker} Weights")
732
+ figs['strategy_benchmark_weights_box'] = fig
733
+ plot_exposures_strategy_vs_benchmark_boxplot(strategy_exposures=strategy_exposures_short,
734
+ benchmark_exposures=benchmark_exposures_short,
735
+ ax=axs[0],
736
+ ylabel='Weights',
737
+ var_format=var_format,
738
+ **kwargs)
739
+ plot_exposures_strategy_vs_benchmark_boxplot(strategy_exposures=strategy_exposures_long,
740
+ benchmark_exposures=benchmark_exposures_long,
741
+ ax=axs[1],
742
+ ylabel='Weights',
743
+ var_format=var_format,
744
+ **kwargs)
745
+
746
+ # risk contributions
747
+ rc_kwargs = dict(covar_dict=multi_portfolio_data.covar_dict, freq='QE')
748
+ strategy_risk_contributions_short = strategy_data.compute_risk_contributions_implied_by_covar(
749
+ group_data=group_data_short,
750
+ group_order=group_order_short,
751
+ **rc_kwargs)
752
+ strategy_risk_contributions_short = qis.df_to_weight_allocation_sum1(strategy_risk_contributions_short)
753
+ strategy_risk_contributions_long = strategy_data.compute_risk_contributions_implied_by_covar(
754
+ group_data=group_data,
755
+ group_order=group_order,
756
+ **rc_kwargs)
757
+ strategy_risk_contributions_long = qis.df_to_weight_allocation_sum1(strategy_risk_contributions_long)
758
+
759
+ benchmark_risk_contributions_short = benchmark_data.compute_risk_contributions_implied_by_covar(
760
+ group_data=group_data_short,
761
+ group_order=group_order_short,
762
+ **rc_kwargs)
763
+ benchmark_risk_contributions_short = qis.df_to_weight_allocation_sum1(benchmark_risk_contributions_short)
764
+
765
+ benchmark_risk_contributions_long = benchmark_data.compute_risk_contributions_implied_by_covar(
766
+ group_data=group_data,
767
+ group_order=group_order,
768
+ **rc_kwargs)
769
+ benchmark_risk_contributions_long = qis.df_to_weight_allocation_sum1(benchmark_risk_contributions_long)
770
+
771
+ # stack
772
+ fig, axs = plt.subplots(1, 2, figsize=figsize, tight_layout=True)
773
+ qis.set_suptitle(fig, title=f"Time Series Risk Contributions")
774
+ figs['time_series_risk_contrib'] = fig
775
+ plot_exposures_strategy_vs_benchmark_stack(strategy_exposures=strategy_risk_contributions_short,
776
+ benchmark_exposures=benchmark_risk_contributions_short,
777
+ axs=axs,
778
+ var_format=var_format,
779
+ **kwargs)
780
+
781
+ # box plots
782
+ fig, axs = plt.subplots(1, 2, figsize=figsize, tight_layout=True)
783
+ qis.set_suptitle(fig, title=f"Risk Contributions")
784
+ figs['strategy_var'] = fig
785
+ plot_exposures_strategy_vs_benchmark_boxplot(
786
+ strategy_exposures=strategy_risk_contributions_short,
787
+ benchmark_exposures=benchmark_risk_contributions_short,
788
+ ax=axs[0],
789
+ ylabel='Risk Contributions',
790
+ var_format=var_format,
791
+ **kwargs)
792
+ plot_exposures_strategy_vs_benchmark_boxplot(
793
+ strategy_exposures=strategy_risk_contributions_long,
794
+ benchmark_exposures=benchmark_risk_contributions_long,
795
+ ax=axs[1],
796
+ ylabel='Risk Contributions',
797
+ var_format=var_format,
798
+ **kwargs)
799
+
800
+ fig, ax = plt.subplots(1, 1, figsize=figsize, tight_layout=True)
801
+ figs['tre_time_series'] = fig
802
+ multi_portfolio_data.plot_tre_time_series(strategy_idx=strategy_idx,
803
+ benchmark_idx=benchmark_idx,
804
+ ax=ax,
805
+ time_period=time_period,
806
+ **kwargs)
807
+
808
+ # brinson
809
+ fig, axs = plt.subplots(1, 2, figsize=figsize, tight_layout=True)
810
+ figs['brinson'] = fig
811
+ axs = [axs[0], axs[1], None, None, None]
812
+ multi_portfolio_data.plot_brinson_attribution(strategy_idx=strategy_idx,
813
+ benchmark_idx=benchmark_idx,
814
+ time_period=time_period,
815
+ freq=None,
816
+ axs=axs,
817
+ total_column='Total Sum',
818
+ is_exclude_interaction_term=True,
819
+ **kwargs)
820
+
821
+ return figs, dfs
822
+
823
+
824
+ def plot_exposures_long_short_groups(exposures_short: pd.DataFrame,
825
+ exposures_long: pd.DataFrame,
826
+ axs: List[plt.Subplot],
827
+ ylabel: str = 'weights',
828
+ var_format: str = '{:.1%}',
829
+ hue_var_name: str = 'asset class',
830
+ **kwargs
831
+ ) -> None:
664
832
  qis.plot_stack(df=exposures_short,
665
833
  use_bar_plot=True,
666
834
  legend_stats=qis.LegendStats.AVG_NONNAN_LAST,
@@ -682,3 +850,52 @@ def plot_exposures(exposures_short: pd.DataFrame,
682
850
  y_limits=(0.0, None),
683
851
  ax=axs[1],
684
852
  **kwargs)
853
+
854
+
855
+ def plot_exposures_strategy_vs_benchmark_stack(strategy_exposures: pd.DataFrame,
856
+ benchmark_exposures: pd.DataFrame,
857
+ axs: List[plt.Subplot],
858
+ var_format: str = '{:.1%}',
859
+ **kwargs
860
+ ) -> None:
861
+ qis.plot_stack(df=benchmark_exposures,
862
+ use_bar_plot=True,
863
+ legend_stats=qis.LegendStats.AVG_NONNAN_LAST,
864
+ var_format=var_format,
865
+ colors=qis.get_n_sns_colors(n=len(benchmark_exposures.columns)),
866
+ title='SAA',
867
+ ax=axs[0],
868
+ **qis.update_kwargs(kwargs, dict(bbox_to_anchor=(0.5, 1.01), ncols=1,
869
+ framealpha=0.9)))
870
+ qis.plot_stack(df=strategy_exposures,
871
+ use_bar_plot=True,
872
+ legend_stats=qis.LegendStats.AVG_NONNAN_LAST,
873
+ var_format=var_format,
874
+ colors=qis.get_n_sns_colors(n=len(strategy_exposures.columns)),
875
+ title='TAA',
876
+ ax=axs[1],
877
+ **qis.update_kwargs(kwargs, dict(bbox_to_anchor=(0.5, 1.01), ncols=1,
878
+ framealpha=0.9)))
879
+
880
+
881
+ def plot_exposures_strategy_vs_benchmark_boxplot(strategy_exposures: pd.DataFrame,
882
+ benchmark_exposures: pd.DataFrame,
883
+ ax: plt.Subplot,
884
+ ylabel: str = 'weights',
885
+ var_format: str = '{:.1%}',
886
+ hue_var_name: str = 'asset class',
887
+ **kwargs
888
+ ) -> None:
889
+ dfs = dict(SAA=benchmark_exposures, TAA=strategy_exposures)
890
+ qis.df_dict_boxplot_by_columns(dfs=dfs,
891
+ hue_var_name=hue_var_name,
892
+ y_var_name=ylabel,
893
+ ylabel=ylabel,
894
+ showmedians=True,
895
+ add_y_median_labels=True,
896
+ yvar_format=var_format,
897
+ x_rotation=90,
898
+ # colors=qis.get_n_sns_colors(n=len(exposures_long.columns)),
899
+ y_limits=(0.0, None),
900
+ ax=ax,
901
+ **kwargs)
@@ -3,6 +3,7 @@
3
3
  # remove from git tracking:
4
4
  # git rm -r --cached setting.yaml
5
5
 
6
+
6
7
  RESOURCE_PATH:
7
8
  "..\\"
8
9
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes