qis 3.0.5__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.5 → qis-3.0.6}/PKG-INFO +1 -1
  2. {qis-3.0.5 → qis-3.0.6}/pyproject.toml +1 -5
  3. {qis-3.0.5 → qis-3.0.6}/qis/examples/factsheets/strategy_benchmark.py +15 -2
  4. {qis-3.0.5 → qis-3.0.6}/qis/file_utils.py +12 -8
  5. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/strategy_benchmark_factsheet.py +244 -28
  6. {qis-3.0.5 → qis-3.0.6}/LICENSE.txt +0 -0
  7. {qis-3.0.5 → qis-3.0.6}/README.md +0 -0
  8. {qis-3.0.5 → qis-3.0.6}/qis/__init__.py +0 -0
  9. {qis-3.0.5 → qis-3.0.6}/qis/examples/best_returns.py +0 -0
  10. {qis-3.0.5 → qis-3.0.6}/qis/examples/bond_futures_portfolio.py +0 -0
  11. {qis-3.0.5 → qis-3.0.6}/qis/examples/bootstrap_analysis.py +0 -0
  12. {qis-3.0.5 → qis-3.0.6}/qis/examples/boxplot_conditional_returns.py +0 -0
  13. {qis-3.0.5 → qis-3.0.6}/qis/examples/btc_asset_corr.py +0 -0
  14. {qis-3.0.5 → qis-3.0.6}/qis/examples/constant_notional.py +0 -0
  15. {qis-3.0.5 → qis-3.0.6}/qis/examples/constant_weight_portfolios.py +0 -0
  16. {qis-3.0.5 → qis-3.0.6}/qis/examples/core/perf_bbg_prices.py +0 -0
  17. {qis-3.0.5 → qis-3.0.6}/qis/examples/core/price_plots.py +0 -0
  18. {qis-3.0.5 → qis-3.0.6}/qis/examples/core/us_election.py +0 -0
  19. {qis-3.0.5 → qis-3.0.6}/qis/examples/credit_spreads.py +0 -0
  20. {qis-3.0.5 → qis-3.0.6}/qis/examples/credit_trackers.py +0 -0
  21. {qis-3.0.5 → qis-3.0.6}/qis/examples/europe_futures.py +0 -0
  22. {qis-3.0.5 → qis-3.0.6}/qis/examples/factsheets/multi_assets.py +0 -0
  23. {qis-3.0.5 → qis-3.0.6}/qis/examples/factsheets/multi_strategy.py +0 -0
  24. {qis-3.0.5 → qis-3.0.6}/qis/examples/factsheets/pyblogs_reports.py +0 -0
  25. {qis-3.0.5 → qis-3.0.6}/qis/examples/factsheets/strategy.py +0 -0
  26. {qis-3.0.5 → qis-3.0.6}/qis/examples/generate_option_rolls.py +0 -0
  27. {qis-3.0.5 → qis-3.0.6}/qis/examples/interpolation_infrequent_returns.py +0 -0
  28. {qis-3.0.5 → qis-3.0.6}/qis/examples/leveraged_strategies.py +0 -0
  29. {qis-3.0.5 → qis-3.0.6}/qis/examples/long_short.py +0 -0
  30. {qis-3.0.5 → qis-3.0.6}/qis/examples/momentum_indices.py +0 -0
  31. {qis-3.0.5 → qis-3.0.6}/qis/examples/oakmark_analysis.py +0 -0
  32. {qis-3.0.5 → qis-3.0.6}/qis/examples/ohlc_vol_analysis.py +0 -0
  33. {qis-3.0.5 → qis-3.0.6}/qis/examples/overnight_returns.py +0 -0
  34. {qis-3.0.5 → qis-3.0.6}/qis/examples/perf_external_assets.py +0 -0
  35. {qis-3.0.5 → qis-3.0.6}/qis/examples/perp_pricing.py +0 -0
  36. {qis-3.0.5 → qis-3.0.6}/qis/examples/readme_performances.py +0 -0
  37. {qis-3.0.5 → qis-3.0.6}/qis/examples/risk_return_frontier.py +0 -0
  38. {qis-3.0.5 → qis-3.0.6}/qis/examples/rolling_performance.py +0 -0
  39. {qis-3.0.5 → qis-3.0.6}/qis/examples/seasonality.py +0 -0
  40. {qis-3.0.5 → qis-3.0.6}/qis/examples/sharpe_vs_sortino.py +0 -0
  41. {qis-3.0.5 → qis-3.0.6}/qis/examples/simulate_quant_strats.py +0 -0
  42. {qis-3.0.5 → qis-3.0.6}/qis/examples/test_ewm.py +0 -0
  43. {qis-3.0.5 → qis-3.0.6}/qis/examples/test_scatter.py +0 -0
  44. {qis-3.0.5 → qis-3.0.6}/qis/examples/try_pybloqs.py +0 -0
  45. {qis-3.0.5 → qis-3.0.6}/qis/examples/universe_corrs.py +0 -0
  46. {qis-3.0.5 → qis-3.0.6}/qis/examples/vix_beta_to_equities_bonds.py +0 -0
  47. {qis-3.0.5 → qis-3.0.6}/qis/examples/vix_conditional_returns.py +0 -0
  48. {qis-3.0.5 → qis-3.0.6}/qis/examples/vix_spy_by_year.py +0 -0
  49. {qis-3.0.5 → qis-3.0.6}/qis/examples/vix_tenor_analysis.py +0 -0
  50. {qis-3.0.5 → qis-3.0.6}/qis/examples/vol_without_weekends.py +0 -0
  51. {qis-3.0.5 → qis-3.0.6}/qis/local_path.py +0 -0
  52. {qis-3.0.5 → qis-3.0.6}/qis/models/README.md +0 -0
  53. {qis-3.0.5 → qis-3.0.6}/qis/models/__init__.py +0 -0
  54. {qis-3.0.5 → qis-3.0.6}/qis/models/linear/__init__.py +0 -0
  55. {qis-3.0.5 → qis-3.0.6}/qis/models/linear/auto_corr.py +0 -0
  56. {qis-3.0.5 → qis-3.0.6}/qis/models/linear/corr_cov_matrix.py +0 -0
  57. {qis-3.0.5 → qis-3.0.6}/qis/models/linear/ewm.py +0 -0
  58. {qis-3.0.5 → qis-3.0.6}/qis/models/linear/ewm_convolution.py +0 -0
  59. {qis-3.0.5 → qis-3.0.6}/qis/models/linear/ewm_factors.py +0 -0
  60. {qis-3.0.5 → qis-3.0.6}/qis/models/linear/ewm_winsor_outliers.py +0 -0
  61. {qis-3.0.5 → qis-3.0.6}/qis/models/linear/pca.py +0 -0
  62. {qis-3.0.5 → qis-3.0.6}/qis/models/linear/plot_correlations.py +0 -0
  63. {qis-3.0.5 → qis-3.0.6}/qis/models/linear/ra_returns.py +0 -0
  64. {qis-3.0.5 → qis-3.0.6}/qis/models/stats/__init__.py +0 -0
  65. {qis-3.0.5 → qis-3.0.6}/qis/models/stats/bootstrap.py +0 -0
  66. {qis-3.0.5 → qis-3.0.6}/qis/models/stats/ohlc_vol.py +0 -0
  67. {qis-3.0.5 → qis-3.0.6}/qis/models/stats/rolling_stats.py +0 -0
  68. {qis-3.0.5 → qis-3.0.6}/qis/models/stats/test_bootstrap.py +0 -0
  69. {qis-3.0.5 → qis-3.0.6}/qis/perfstats/README.md +0 -0
  70. {qis-3.0.5 → qis-3.0.6}/qis/perfstats/__init__.py +0 -0
  71. {qis-3.0.5 → qis-3.0.6}/qis/perfstats/cond_regression.py +0 -0
  72. {qis-3.0.5 → qis-3.0.6}/qis/perfstats/config.py +0 -0
  73. {qis-3.0.5 → qis-3.0.6}/qis/perfstats/desc_table.py +0 -0
  74. {qis-3.0.5 → qis-3.0.6}/qis/perfstats/fx_ops.py +0 -0
  75. {qis-3.0.5 → qis-3.0.6}/qis/perfstats/perf_stats.py +0 -0
  76. {qis-3.0.5 → qis-3.0.6}/qis/perfstats/regime_classifier.py +0 -0
  77. {qis-3.0.5 → qis-3.0.6}/qis/perfstats/returns.py +0 -0
  78. {qis-3.0.5 → qis-3.0.6}/qis/perfstats/timeseries_bfill.py +0 -0
  79. {qis-3.0.5 → qis-3.0.6}/qis/plots/README.md +0 -0
  80. {qis-3.0.5 → qis-3.0.6}/qis/plots/__init__.py +0 -0
  81. {qis-3.0.5 → qis-3.0.6}/qis/plots/bars.py +0 -0
  82. {qis-3.0.5 → qis-3.0.6}/qis/plots/boxplot.py +0 -0
  83. {qis-3.0.5 → qis-3.0.6}/qis/plots/contour.py +0 -0
  84. {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/__init__.py +0 -0
  85. {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/data_timeseries.py +0 -0
  86. {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/desc_table.py +0 -0
  87. {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/drawdowns.py +0 -0
  88. {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/perf_table.py +0 -0
  89. {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/prices.py +0 -0
  90. {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/regime_class_table.py +0 -0
  91. {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/regime_data.py +0 -0
  92. {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/regime_pdf.py +0 -0
  93. {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/regime_scatter.py +0 -0
  94. {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/returns_heatmap.py +0 -0
  95. {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/returns_scatter.py +0 -0
  96. {qis-3.0.5 → qis-3.0.6}/qis/plots/errorbar.py +0 -0
  97. {qis-3.0.5 → qis-3.0.6}/qis/plots/heatmap.py +0 -0
  98. {qis-3.0.5 → qis-3.0.6}/qis/plots/histogram.py +0 -0
  99. {qis-3.0.5 → qis-3.0.6}/qis/plots/histplot2d.py +0 -0
  100. {qis-3.0.5 → qis-3.0.6}/qis/plots/lineplot.py +0 -0
  101. {qis-3.0.5 → qis-3.0.6}/qis/plots/pie.py +0 -0
  102. {qis-3.0.5 → qis-3.0.6}/qis/plots/qqplot.py +0 -0
  103. {qis-3.0.5 → qis-3.0.6}/qis/plots/reports/__init__.py +0 -0
  104. {qis-3.0.5 → qis-3.0.6}/qis/plots/reports/econ_data_single.py +0 -0
  105. {qis-3.0.5 → qis-3.0.6}/qis/plots/reports/gantt_data_history.py +0 -0
  106. {qis-3.0.5 → qis-3.0.6}/qis/plots/reports/price_history.py +0 -0
  107. {qis-3.0.5 → qis-3.0.6}/qis/plots/reports/utils.py +0 -0
  108. {qis-3.0.5 → qis-3.0.6}/qis/plots/scatter.py +0 -0
  109. {qis-3.0.5 → qis-3.0.6}/qis/plots/stackplot.py +0 -0
  110. {qis-3.0.5 → qis-3.0.6}/qis/plots/table.py +0 -0
  111. {qis-3.0.5 → qis-3.0.6}/qis/plots/time_series.py +0 -0
  112. {qis-3.0.5 → qis-3.0.6}/qis/plots/utils.py +0 -0
  113. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/README.md +0 -0
  114. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/__init__.py +0 -0
  115. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/backtester.py +0 -0
  116. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/ewm_portfolio_risk.py +0 -0
  117. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/multi_portfolio_data.py +0 -0
  118. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/portfolio_data.py +0 -0
  119. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/__init__.py +0 -0
  120. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/brinson_attribution.py +0 -0
  121. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/config.py +0 -0
  122. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/multi_assets_factsheet.py +0 -0
  123. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/multi_strategy_factseet_pybloqs.py +0 -0
  124. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/multi_strategy_factsheet.py +0 -0
  125. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/strategy_benchmark_factsheet_pybloqs.py +0 -0
  126. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/strategy_factsheet.py +0 -0
  127. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/strategy_signal_factsheet.py +0 -0
  128. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/strats/__init__.py +0 -0
  129. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/strats/quant_strats_delta1.py +0 -0
  130. {qis-3.0.5 → qis-3.0.6}/qis/portfolio/strats/seasonal_strats.py +0 -0
  131. {qis-3.0.5 → qis-3.0.6}/qis/settings.yaml +0 -0
  132. {qis-3.0.5 → qis-3.0.6}/qis/sql_engine.py +0 -0
  133. {qis-3.0.5 → qis-3.0.6}/qis/test_data.py +0 -0
  134. {qis-3.0.5 → qis-3.0.6}/qis/utils/README.md +0 -0
  135. {qis-3.0.5 → qis-3.0.6}/qis/utils/__init__.py +0 -0
  136. {qis-3.0.5 → qis-3.0.6}/qis/utils/dates.py +0 -0
  137. {qis-3.0.5 → qis-3.0.6}/qis/utils/df_agg.py +0 -0
  138. {qis-3.0.5 → qis-3.0.6}/qis/utils/df_cut.py +0 -0
  139. {qis-3.0.5 → qis-3.0.6}/qis/utils/df_freq.py +0 -0
  140. {qis-3.0.5 → qis-3.0.6}/qis/utils/df_groups.py +0 -0
  141. {qis-3.0.5 → qis-3.0.6}/qis/utils/df_melt.py +0 -0
  142. {qis-3.0.5 → qis-3.0.6}/qis/utils/df_ops.py +0 -0
  143. {qis-3.0.5 → qis-3.0.6}/qis/utils/df_str.py +0 -0
  144. {qis-3.0.5 → qis-3.0.6}/qis/utils/df_to_scores.py +0 -0
  145. {qis-3.0.5 → qis-3.0.6}/qis/utils/df_to_weights.py +0 -0
  146. {qis-3.0.5 → qis-3.0.6}/qis/utils/generic.py +0 -0
  147. {qis-3.0.5 → qis-3.0.6}/qis/utils/np_ops.py +0 -0
  148. {qis-3.0.5 → qis-3.0.6}/qis/utils/ols.py +0 -0
  149. {qis-3.0.5 → qis-3.0.6}/qis/utils/sampling.py +0 -0
  150. {qis-3.0.5 → 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.5
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.5"
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>"]
@@ -50,10 +50,6 @@ pyarrow = ">=10.0.1"
50
50
  fsspec = ">=2022.11.0"
51
51
  yfinance = ">=0.1.38"
52
52
 
53
- #[build-system]
54
- #requires = ["poetry-core>=1.0.0"]
55
- #
56
-
57
53
  [build-system]
58
54
  requires = ["poetry-core>=1.0.0", "hatchling==1.27.0", "hatch-vcs"]
59
55
  #build-backend = "hatchling.build"
@@ -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)
@@ -557,11 +557,11 @@ def weights_tracking_error_report(multi_portfolio_data: MultiPortfolioData,
557
557
  fig, axs = plt.subplots(1, 2, figsize=figsize, tight_layout=True)
558
558
  qis.set_suptitle(fig, title=f"{strategy_data.ticker} Weights")
559
559
  figs['strategy_weights'] = fig
560
- plot_exposures(exposures_short=exposures_short,
561
- exposures_long=exposures_long,
562
- ylabel='Weights',
563
- var_format=var_format,
564
- 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)
565
565
 
566
566
  rc_kwargs = dict(covar_dict=multi_portfolio_data.covar_dict, freq='QE')
567
567
  # strategy risk contributions
@@ -578,11 +578,11 @@ def weights_tracking_error_report(multi_portfolio_data: MultiPortfolioData,
578
578
  fig, axs = plt.subplots(1, 2, figsize=figsize, tight_layout=True)
579
579
  qis.set_suptitle(fig, title=f"{strategy_data.ticker} Risk Contributions")
580
580
  figs['strategy_var'] = fig
581
- plot_exposures(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)
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)
586
586
 
587
587
  # benchmark weights
588
588
  benchmark_data = multi_portfolio_data.portfolio_datas[benchmark_idx]
@@ -594,11 +594,11 @@ def weights_tracking_error_report(multi_portfolio_data: MultiPortfolioData,
594
594
  fig, axs = plt.subplots(1, 2, figsize=figsize, tight_layout=True)
595
595
  qis.set_suptitle(fig, title=f"{benchmark_data.ticker} Weights")
596
596
  figs['benchmark_weights'] = fig
597
- plot_exposures(exposures_short=benchmark_exposures_short,
598
- exposures_long=benchmark_exposures_long,
599
- ylabel='Weights',
600
- var_format=var_format,
601
- 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)
602
602
 
603
603
  # benchmark var
604
604
  benchmark_risk_contributions_short = benchmark_data.compute_risk_contributions_implied_by_covar(
@@ -615,11 +615,11 @@ def weights_tracking_error_report(multi_portfolio_data: MultiPortfolioData,
615
615
  fig, axs = plt.subplots(1, 2, figsize=figsize, tight_layout=True)
616
616
  qis.set_suptitle(fig, title=f"{benchmark_data.ticker} Risk Contributions")
617
617
  figs['benchmark_var'] = fig
618
- plot_exposures(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)
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)
623
623
 
624
624
  # turnover
625
625
  fig, ax = plt.subplots(1, 1, figsize=figsize, tight_layout=True)
@@ -654,14 +654,181 @@ def weights_tracking_error_report(multi_portfolio_data: MultiPortfolioData,
654
654
  return figs, dfs
655
655
 
656
656
 
657
- def plot_exposures(exposures_short: pd.DataFrame,
658
- exposures_long: pd.DataFrame,
659
- axs: List[plt.Subplot],
660
- ylabel: str = 'weights',
661
- var_format: str = '{:.1%}',
662
- hue_var_name: str = 'asset class',
663
- **kwargs
664
- ) -> 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:
665
832
  qis.plot_stack(df=exposures_short,
666
833
  use_bar_plot=True,
667
834
  legend_stats=qis.LegendStats.AVG_NONNAN_LAST,
@@ -683,3 +850,52 @@ def plot_exposures(exposures_short: pd.DataFrame,
683
850
  y_limits=(0.0, None),
684
851
  ax=axs[1],
685
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)
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
File without changes
File without changes
File without changes