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.
- {qis-3.0.5 → qis-3.0.6}/PKG-INFO +1 -1
- {qis-3.0.5 → qis-3.0.6}/pyproject.toml +1 -5
- {qis-3.0.5 → qis-3.0.6}/qis/examples/factsheets/strategy_benchmark.py +15 -2
- {qis-3.0.5 → qis-3.0.6}/qis/file_utils.py +12 -8
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/strategy_benchmark_factsheet.py +244 -28
- {qis-3.0.5 → qis-3.0.6}/LICENSE.txt +0 -0
- {qis-3.0.5 → qis-3.0.6}/README.md +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/__init__.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/best_returns.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/bond_futures_portfolio.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/bootstrap_analysis.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/boxplot_conditional_returns.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/btc_asset_corr.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/constant_notional.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/constant_weight_portfolios.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/core/perf_bbg_prices.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/core/price_plots.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/core/us_election.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/credit_spreads.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/credit_trackers.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/europe_futures.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/factsheets/multi_assets.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/factsheets/multi_strategy.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/factsheets/pyblogs_reports.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/factsheets/strategy.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/generate_option_rolls.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/interpolation_infrequent_returns.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/leveraged_strategies.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/long_short.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/momentum_indices.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/oakmark_analysis.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/ohlc_vol_analysis.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/overnight_returns.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/perf_external_assets.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/perp_pricing.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/readme_performances.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/risk_return_frontier.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/rolling_performance.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/seasonality.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/sharpe_vs_sortino.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/simulate_quant_strats.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/test_ewm.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/test_scatter.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/try_pybloqs.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/universe_corrs.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/vix_beta_to_equities_bonds.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/vix_conditional_returns.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/vix_spy_by_year.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/vix_tenor_analysis.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/examples/vol_without_weekends.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/local_path.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/README.md +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/__init__.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/linear/__init__.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/linear/auto_corr.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/linear/corr_cov_matrix.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/linear/ewm.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/linear/ewm_convolution.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/linear/ewm_factors.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/linear/ewm_winsor_outliers.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/linear/pca.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/linear/plot_correlations.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/linear/ra_returns.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/stats/__init__.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/stats/bootstrap.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/stats/ohlc_vol.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/stats/rolling_stats.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/models/stats/test_bootstrap.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/perfstats/README.md +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/perfstats/__init__.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/perfstats/cond_regression.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/perfstats/config.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/perfstats/desc_table.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/perfstats/fx_ops.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/perfstats/perf_stats.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/perfstats/regime_classifier.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/perfstats/returns.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/perfstats/timeseries_bfill.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/README.md +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/__init__.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/bars.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/boxplot.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/contour.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/__init__.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/data_timeseries.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/desc_table.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/drawdowns.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/perf_table.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/prices.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/regime_class_table.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/regime_data.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/regime_pdf.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/regime_scatter.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/returns_heatmap.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/derived/returns_scatter.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/errorbar.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/heatmap.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/histogram.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/histplot2d.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/lineplot.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/pie.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/qqplot.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/reports/__init__.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/reports/econ_data_single.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/reports/gantt_data_history.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/reports/price_history.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/reports/utils.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/scatter.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/stackplot.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/table.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/time_series.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/plots/utils.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/README.md +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/__init__.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/backtester.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/ewm_portfolio_risk.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/multi_portfolio_data.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/portfolio_data.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/__init__.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/brinson_attribution.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/config.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/multi_assets_factsheet.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/multi_strategy_factseet_pybloqs.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/multi_strategy_factsheet.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/strategy_benchmark_factsheet_pybloqs.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/strategy_factsheet.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/reports/strategy_signal_factsheet.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/strats/__init__.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/strats/quant_strats_delta1.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/portfolio/strats/seasonal_strats.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/settings.yaml +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/sql_engine.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/test_data.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/README.md +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/__init__.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/dates.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/df_agg.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/df_cut.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/df_freq.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/df_groups.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/df_melt.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/df_ops.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/df_str.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/df_to_scores.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/df_to_weights.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/generic.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/np_ops.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/ols.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/sampling.py +0 -0
- {qis-3.0.5 → qis-3.0.6}/qis/utils/struct_ops.py +0 -0
{qis-3.0.5 → qis-3.0.6}/PKG-INFO
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: qis
|
3
|
-
Version: 3.0.
|
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.
|
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.
|
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
|
235
|
-
|
236
|
-
|
237
|
-
|
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
|
241
|
-
|
242
|
-
|
243
|
-
|
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
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
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
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
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
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
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
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
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
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
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
|
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
|