qis 3.2.2__tar.gz → 3.2.4__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.2.2 → qis-3.2.4}/PKG-INFO +1 -1
- {qis-3.2.2 → qis-3.2.4}/pyproject.toml +1 -1
- {qis-3.2.2 → qis-3.2.4}/qis/examples/core/perf_bbg_prices.py +34 -5
- {qis-3.2.2 → qis-3.2.4}/qis/models/__init__.py +2 -2
- {qis-3.2.2 → qis-3.2.4}/qis/models/linear/ewm.py +37 -13
- {qis-3.2.2 → qis-3.2.4}/qis/models/linear/ewm_factors.py +21 -1
- {qis-3.2.2 → qis-3.2.4}/qis/plots/derived/perf_table.py +1 -3
- {qis-3.2.2 → qis-3.2.4}/qis/plots/derived/returns_heatmap.py +1 -1
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/multi_portfolio_data.py +2 -1
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/portfolio_data.py +15 -2
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/reports/config.py +3 -3
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/reports/multi_assets_factsheet.py +41 -14
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/reports/multi_strategy_factseet_pybloqs.py +17 -16
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/reports/strategy_benchmark_factsheet.py +2 -0
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/reports/strategy_factsheet.py +3 -4
- {qis-3.2.2 → qis-3.2.4}/qis/settings.yaml +0 -1
- {qis-3.2.2 → qis-3.2.4}/LICENSE.txt +0 -0
- {qis-3.2.2 → qis-3.2.4}/README.md +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/__init__.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/best_returns.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/bond_futures_portfolio.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/bootstrap_analysis.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/boxplot_conditional_returns.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/btc_asset_corr.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/constant_notional.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/constant_weight_portfolios.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/core/price_plots.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/core/us_election.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/credit_spreads.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/credit_trackers.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/europe_futures.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/factsheets/multi_assets.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/factsheets/multi_strategy.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/factsheets/pyblogs_reports.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/factsheets/strategy.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/factsheets/strategy_benchmark.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/generate_option_rolls.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/interpolation_infrequent_returns.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/leveraged_strategies.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/long_short.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/momentum_indices.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/ohlc_vol_analysis.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/overnight_returns.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/perf_external_assets.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/perp_pricing.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/readme_performances.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/risk_return_frontier.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/rolling_performance.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/seasonality.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/sharpe_vs_sortino.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/simulate_quant_strats.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/test_ewm.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/test_scatter.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/try_pybloqs.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/universe_corrs.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/vix_beta_to_equities_bonds.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/vix_conditional_returns.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/vix_spy_by_year.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/vix_tenor_analysis.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/examples/vol_without_weekends.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/file_utils.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/local_path.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/models/README.md +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/models/linear/__init__.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/models/linear/auto_corr.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/models/linear/corr_cov_matrix.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/models/linear/ewm_convolution.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/models/linear/ewm_winsor_outliers.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/models/linear/pca.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/models/linear/plot_correlations.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/models/linear/ra_returns.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/models/stats/__init__.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/models/stats/bootstrap.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/models/stats/ohlc_vol.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/models/stats/rolling_stats.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/models/stats/test_bootstrap.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/perfstats/README.md +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/perfstats/__init__.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/perfstats/cond_regression.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/perfstats/config.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/perfstats/desc_table.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/perfstats/fx_ops.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/perfstats/perf_stats.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/perfstats/regime_classifier.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/perfstats/returns.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/perfstats/timeseries_bfill.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/README.md +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/__init__.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/bars.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/boxplot.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/contour.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/derived/__init__.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/derived/data_timeseries.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/derived/desc_table.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/derived/drawdowns.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/derived/prices.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/derived/regime_class_table.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/derived/regime_data.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/derived/regime_pdf.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/derived/regime_scatter.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/derived/returns_scatter.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/errorbar.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/heatmap.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/histogram.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/histplot2d.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/lineplot.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/pie.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/qqplot.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/reports/__init__.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/reports/econ_data_single.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/reports/gantt_data_history.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/reports/price_history.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/reports/utils.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/scatter.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/stackplot.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/table.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/time_series.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/plots/utils.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/README.md +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/__init__.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/backtester.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/ewm_portfolio_risk.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/reports/__init__.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/reports/brinson_attribution.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/reports/multi_strategy_factsheet.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/reports/strategy_benchmark_factsheet_pybloqs.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/reports/strategy_signal_factsheet.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/strats/__init__.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/strats/quant_strats_delta1.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/portfolio/strats/seasonal_strats.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/sql_engine.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/test_data.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/README.md +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/__init__.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/dates.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/df_agg.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/df_cut.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/df_freq.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/df_groups.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/df_melt.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/df_ops.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/df_str.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/df_to_scores.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/df_to_weights.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/generic.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/np_ops.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/ols.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/sampling.py +0 -0
- {qis-3.2.2 → qis-3.2.4}/qis/utils/struct_ops.py +0 -0
{qis-3.2.2 → qis-3.2.4}/PKG-INFO
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: qis
|
3
|
-
Version: 3.2.
|
3
|
+
Version: 3.2.4
|
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
|
@@ -22,15 +22,44 @@ def run_report():
|
|
22
22
|
'BNPXOV3U Index': 'BNP 3M Long DHhedged Puts'
|
23
23
|
}
|
24
24
|
|
25
|
+
benchmark = 'SPTR Index'
|
26
|
+
tickers = {
|
27
|
+
benchmark: benchmark,
|
28
|
+
'BNPIV1EE Index': 'BNP Europe 1Y Volatility',
|
29
|
+
'BNPIV1UE Index': 'BNP US 1Y Volatility',
|
30
|
+
'BNPXVO3A Index': 'BNP VOLA 3 Index',
|
31
|
+
'AIJPVT1U Index': 'JPM Volatility Trend Following',
|
32
|
+
'JPOSLVUS Index': 'JPM US Long Variance',
|
33
|
+
'JPOSPRU2 Index': 'JPM US Put Ratio',
|
34
|
+
'JPOSTUDN Index': 'JPM US Equity Tail Hedge',
|
35
|
+
'JPRC85BE Index': 'JPM Dynamic 85% Rolling Collar EU',
|
36
|
+
'JPRC85BU Index': 'JPM Dynamic 85% Rolling Collar US',
|
37
|
+
'JPUSVXCR Index': 'JPM US Volatility Call Ratio'
|
38
|
+
}
|
39
|
+
|
40
|
+
benchmark = 'HYG US Equity'
|
41
|
+
tickers = {
|
42
|
+
benchmark: benchmark,
|
43
|
+
'NMVVR1EL Index': 'IRVING1 EUR',
|
44
|
+
'NMVVR1UL Index': 'IRVING1 USD',
|
45
|
+
'NMVVR1L Index': 'IRVING1',
|
46
|
+
'BNPXLVRE Index': 'BNP Long Rates Vol EUR',
|
47
|
+
'BNPXLVRU Index': 'BNP Long Rates Vol USD',
|
48
|
+
'BXIIULSV Index': 'Barclays Long Rates Vol',
|
49
|
+
'BXIIUGNT Index': 'Barclays Gamma Neutral Vol',
|
50
|
+
'BXIIUENT Index': 'Barclays Triangle Vol'
|
51
|
+
}
|
52
|
+
|
25
53
|
prices = fetch_field_timeseries_per_tickers(tickers=tickers, freq='B', field='PX_LAST').ffill()
|
26
54
|
print(prices)
|
27
|
-
qis.save_df_to_csv(df=prices, file_name='qis_vol_indices', local_path=qis.get_output_path())
|
55
|
+
# qis.save_df_to_csv(df=prices, file_name='qis_vol_indices', local_path=qis.get_output_path())
|
28
56
|
|
29
|
-
time_period = qis.TimePeriod('
|
30
|
-
kwargs = qis.fetch_default_report_kwargs(time_period=time_period, add_rates_data=False)
|
57
|
+
time_period = qis.TimePeriod('31Dec2024', '07Apr2025')
|
58
|
+
#kwargs = qis.fetch_default_report_kwargs(time_period=time_period, add_rates_data=False)
|
59
|
+
kwargs = qis.fetch_factsheet_config_kwargs(factsheet_config=qis.FACTSHEET_CONFIG_DAILY_DATA_SHORT_PERIOD)
|
31
60
|
|
32
61
|
fig = qis.generate_multi_asset_factsheet(prices=prices,
|
33
|
-
benchmark=
|
62
|
+
benchmark=benchmark,
|
34
63
|
time_period=time_period,
|
35
64
|
**kwargs)
|
36
65
|
qis.save_figs_to_pdf(figs=[fig],
|
@@ -70,7 +99,7 @@ def run_unit_test(unit_test: UnitTests):
|
|
70
99
|
|
71
100
|
if __name__ == '__main__':
|
72
101
|
|
73
|
-
unit_test = UnitTests.
|
102
|
+
unit_test = UnitTests.REPORT
|
74
103
|
|
75
104
|
is_run_all_tests = False
|
76
105
|
if is_run_all_tests:
|
@@ -56,8 +56,8 @@ from qis.models.linear.ewm_factors import (LinearModel,
|
|
56
56
|
EwmLinearModel,
|
57
57
|
compute_portfolio_benchmark_betas,
|
58
58
|
compute_portfolio_benchmark_beta_alpha_attribution,
|
59
|
-
compute_benchmarks_beta_attribution
|
60
|
-
)
|
59
|
+
compute_benchmarks_beta_attribution,
|
60
|
+
estimate_linear_model)
|
61
61
|
|
62
62
|
from qis.models.linear.pca import(
|
63
63
|
compute_eigen_portfolio_weights,
|
@@ -664,7 +664,7 @@ def compute_ewm_vol(data: Union[pd.DataFrame, pd.Series, np.ndarray],
|
|
664
664
|
|
665
665
|
|
666
666
|
def compute_ewm_newey_west_vol(data: Union[pd.DataFrame, pd.Series, np.ndarray],
|
667
|
-
num_lags: int =
|
667
|
+
num_lags: int = 2,
|
668
668
|
span: Optional[Union[float, np.ndarray]] = None,
|
669
669
|
ewm_lambda: Union[float, np.ndarray] = 0.94,
|
670
670
|
mean_adj_type: MeanAdjType = MeanAdjType.NONE,
|
@@ -675,7 +675,8 @@ def compute_ewm_newey_west_vol(data: Union[pd.DataFrame, pd.Series, np.ndarray],
|
|
675
675
|
annualization_factor: Optional[float] = None,
|
676
676
|
warmup_period: Optional[int] = None,
|
677
677
|
nan_backfill: NanBackfill = NanBackfill.FFILL
|
678
|
-
) -> Union[pd.DataFrame, pd.Series, np.ndarray]
|
678
|
+
) -> Tuple[Union[pd.DataFrame, pd.Series, np.ndarray],
|
679
|
+
Union[pd.DataFrame, pd.Series, np.ndarray]]:
|
679
680
|
"""
|
680
681
|
implementation of newey west vol estimator
|
681
682
|
implementation of ewm recursion for variance/volatility computation
|
@@ -694,7 +695,6 @@ def compute_ewm_newey_west_vol(data: Union[pd.DataFrame, pd.Series, np.ndarray],
|
|
694
695
|
nan_backfill=nan_backfill)
|
695
696
|
|
696
697
|
# initial conditions
|
697
|
-
a = a
|
698
698
|
if init_value is None:
|
699
699
|
init_value = set_init_dim1(data=a, init_type=init_type)
|
700
700
|
|
@@ -703,18 +703,40 @@ def compute_ewm_newey_west_vol(data: Union[pd.DataFrame, pd.Series, np.ndarray],
|
|
703
703
|
if isinstance(init_value, np.ndarray):
|
704
704
|
init_value = float(init_value)
|
705
705
|
|
706
|
-
|
706
|
+
if span is not None:
|
707
|
+
ewm_lambda = 1.0 - 2.0 / (span + 1.0)
|
708
|
+
ewm_lambda_1 = 1.0 - ewm_lambda
|
709
|
+
|
710
|
+
def matrix_recursion(a_m: np.ndarray) -> np.ndarray:
|
711
|
+
t = a.shape[0]
|
712
|
+
last_covar = np.zeros((a.shape[1], a.shape[1]))
|
713
|
+
ewm_m = np.zeros_like(a_m)
|
714
|
+
for idx in range(0, t):
|
715
|
+
r_ij = np.outer(a[idx], a_m[idx])
|
716
|
+
covar = ewm_lambda_1 * r_ij + ewm_lambda * last_covar
|
717
|
+
fill_value = last_covar
|
718
|
+
last_covar = np.where(np.isfinite(covar), covar, fill_value)
|
719
|
+
ewm_m[idx, :] = np.diag(last_covar) + np.diag(np.transpose(last_covar))
|
720
|
+
return ewm_m
|
707
721
|
|
722
|
+
ewm0 = ewm_recursion(a=np.square(a), ewm_lambda=ewm_lambda, init_value=init_value, nan_backfill=nan_backfill)
|
723
|
+
nw_adjustment = np.zeros_like(ewm0)
|
708
724
|
# compute m recursions
|
709
|
-
for m in np.arange(1, num_lags):
|
725
|
+
for m in np.arange(1, num_lags+1):
|
710
726
|
# lagged value
|
711
727
|
a_m = np.empty_like(a)
|
712
728
|
a_m[m:] = a[:-m]
|
713
729
|
a_m[:m] = np.nan
|
714
|
-
|
715
|
-
|
730
|
+
# qqq
|
731
|
+
ewm_m = matrix_recursion(a_m=a_m)
|
732
|
+
nw_adjustment += (1.0-m/(num_lags+1))*ewm_m
|
733
|
+
|
734
|
+
ewm_nw = ewm0 + nw_adjustment
|
735
|
+
nw_ratio = np.divide(ewm_nw, ewm0, where=ewm0 > 0.0)
|
736
|
+
|
716
737
|
if warmup_period is not None: # set to nan first nonnan in warmup_period
|
717
|
-
|
738
|
+
ewm_nw = npo.set_nans_for_warmup_period(a=ewm_nw, warmup_period=warmup_period)
|
739
|
+
nw_ratio = npo.set_nans_for_warmup_period(a=nw_ratio, warmup_period=warmup_period)
|
718
740
|
|
719
741
|
if annualize or annualization_factor is not None:
|
720
742
|
if annualization_factor is None:
|
@@ -723,16 +745,18 @@ def compute_ewm_newey_west_vol(data: Union[pd.DataFrame, pd.Series, np.ndarray],
|
|
723
745
|
else:
|
724
746
|
warnings.warn(f"in compute_ewm annualization_factor for np array default is 1")
|
725
747
|
annualization_factor = 1.0
|
726
|
-
|
748
|
+
ewm_nw = annualization_factor * ewm_nw
|
727
749
|
|
728
750
|
if apply_sqrt:
|
729
|
-
|
751
|
+
ewm_nw = np.sqrt(ewm_nw)
|
730
752
|
|
731
753
|
if isinstance(data, pd.DataFrame):
|
732
|
-
|
754
|
+
ewm_nw = pd.DataFrame(data=ewm_nw, index=data.index, columns=data.columns)
|
755
|
+
nw_ratio = pd.DataFrame(data=nw_ratio, index=data.index, columns=data.columns)
|
733
756
|
elif isinstance(data, pd.Series):
|
734
|
-
|
735
|
-
|
757
|
+
ewm_nw = pd.Series(data=ewm_nw, index=data.index, name=data.name)
|
758
|
+
nw_ratio = pd.Series(data=nw_ratio, index=data.index, name=data.name)
|
759
|
+
return ewm_nw, nw_ratio
|
736
760
|
|
737
761
|
|
738
762
|
def compute_roll_mean(data: Union[pd.DataFrame, pd.Series, np.ndarray],
|
@@ -10,6 +10,7 @@ from typing import Dict, Optional, Tuple, Literal
|
|
10
10
|
from enum import Enum
|
11
11
|
|
12
12
|
# qis
|
13
|
+
import qis as qis
|
13
14
|
import qis.utils.df_ops as dfo
|
14
15
|
import qis.perfstats.returns as ret
|
15
16
|
import qis.plots.time_series as pts
|
@@ -51,7 +52,10 @@ class LinearModel:
|
|
51
52
|
factor_exposures = pd.DataFrame.from_dict(factor_exposures)
|
52
53
|
return factor_exposures
|
53
54
|
|
54
|
-
def get_asset_factor_betas(self,
|
55
|
+
def get_asset_factor_betas(self,
|
56
|
+
time_period: TimePeriod = None,
|
57
|
+
asset: str = None
|
58
|
+
) -> pd.DataFrame:
|
55
59
|
"""
|
56
60
|
return df of asset exposures to factors
|
57
61
|
"""
|
@@ -61,6 +65,8 @@ class LinearModel:
|
|
61
65
|
for factor, factor_exp in self.loadings.items():
|
62
66
|
exps[factor] = factor_exp[asset]
|
63
67
|
exps = pd.DataFrame.from_dict(exps)
|
68
|
+
if time_period is not None:
|
69
|
+
exps = time_period.locate(exps)
|
64
70
|
return exps
|
65
71
|
|
66
72
|
def get_asset_factor_attribution(self, asset: str = None, add_total: bool = True) -> pd.DataFrame:
|
@@ -247,6 +253,18 @@ def compute_benchmarks_beta_attribution(portfolio_nav: pd.Series,
|
|
247
253
|
return joint_attrib
|
248
254
|
|
249
255
|
|
256
|
+
def estimate_linear_model(price: pd.Series, hedges: pd.DataFrame,
|
257
|
+
freq: str = 'W-WED',
|
258
|
+
span: int = 26,
|
259
|
+
mean_adj_type: MeanAdjType = MeanAdjType.NONE
|
260
|
+
) -> EwmLinearModel:
|
261
|
+
y = qis.to_returns(price.to_frame(), freq=freq, is_log_returns=True, drop_first=True)
|
262
|
+
x = qis.to_returns(hedges, freq=freq, is_log_returns=True, drop_first=True)
|
263
|
+
ewm_linear_model = EwmLinearModel(x=x.reindex(index=y.index), y=y)
|
264
|
+
ewm_linear_model.fit(span=span, is_x_correlated=True, mean_adj_type=mean_adj_type)
|
265
|
+
return ewm_linear_model
|
266
|
+
|
267
|
+
|
250
268
|
class UnitTests(Enum):
|
251
269
|
MODEL = 1
|
252
270
|
ATTRIBUTION = 2
|
@@ -313,3 +331,5 @@ if __name__ == '__main__':
|
|
313
331
|
run_unit_test(unit_test=unit_test)
|
314
332
|
else:
|
315
333
|
run_unit_test(unit_test=unit_test)
|
334
|
+
|
335
|
+
|
@@ -4,8 +4,6 @@ import pandas as pd
|
|
4
4
|
import matplotlib.pyplot as plt
|
5
5
|
from typing import List, Tuple, Callable, Optional, Dict, Union
|
6
6
|
from enum import Enum
|
7
|
-
|
8
|
-
import qis
|
9
7
|
# qis
|
10
8
|
import qis.utils.dates as da
|
11
9
|
import qis.utils.df_str as dfs
|
@@ -106,7 +104,7 @@ def get_ra_perf_benchmark_columns(prices: pd.DataFrame,
|
|
106
104
|
df[perf_column.to_str(**kwargs)] = dfs.series_to_str(ds=ra_perf_table[perf_column.to_str()],
|
107
105
|
var_format=perf_column.to_format(**kwargs))
|
108
106
|
else:
|
109
|
-
df[perf_column.to_str(
|
107
|
+
df[perf_column.to_str()] = ra_perf_table[perf_column.to_str()]
|
110
108
|
|
111
109
|
if drop_benchmark:
|
112
110
|
df = df.drop(benchmark, axis=0)
|
@@ -649,7 +649,8 @@ class MultiPortfolioData:
|
|
649
649
|
ax: plt.Subplot = None,
|
650
650
|
**kwargs) -> None:
|
651
651
|
|
652
|
-
turnover = self.get_turnover(turnover_rolling_period=turnover_rolling_period,
|
652
|
+
turnover = self.get_turnover(turnover_rolling_period=turnover_rolling_period,
|
653
|
+
freq_turnover=freq_turnover,
|
653
654
|
is_unit_based_traded_volume=is_unit_based_traded_volume,
|
654
655
|
time_period=time_period)
|
655
656
|
freq = pd.infer_freq(turnover.index)
|
@@ -339,6 +339,8 @@ class PortfolioData:
|
|
339
339
|
def get_turnover(self,
|
340
340
|
is_agg: bool = False,
|
341
341
|
is_grouped: bool = False,
|
342
|
+
group_data: pd.Series = None,
|
343
|
+
group_order: List[str] = None,
|
342
344
|
time_period: TimePeriod = None,
|
343
345
|
roll_period: Optional[int] = 260,
|
344
346
|
is_vol_adjusted: bool = False,
|
@@ -366,11 +368,15 @@ class PortfolioData:
|
|
366
368
|
turnover = pd.Series(np.nansum(turnover, axis=1), index=turnover.index, name=self.nav.name)
|
367
369
|
turnover = turnover.reindex(index=self.nav.index)
|
368
370
|
elif is_grouped: # agg by groups
|
371
|
+
if group_data is None:
|
372
|
+
group_data = self.group_data
|
373
|
+
if group_order is None:
|
374
|
+
group_order = self.group_order
|
369
375
|
turnover = dfg.agg_df_by_groups_ax1(df=turnover,
|
370
|
-
group_data=
|
376
|
+
group_data=group_data,
|
371
377
|
agg_func=np.nansum,
|
372
378
|
total_column=str(self.nav.name) if add_total else None,
|
373
|
-
group_order=
|
379
|
+
group_order=group_order)
|
374
380
|
else:
|
375
381
|
if add_total:
|
376
382
|
turnover = pd.concat([turnover.sum(axis=1).rename(self.nav.name), turnover], axis=1)
|
@@ -1495,6 +1501,13 @@ class StrategySignalData:
|
|
1495
1501
|
data_dict[key] = time_period.locate(df)
|
1496
1502
|
return StrategySignalData(**data_dict)
|
1497
1503
|
|
1504
|
+
def rename_data(self, names_map: Dict[str, str]) -> StrategySignalData:
|
1505
|
+
data_dict = asdict(self)
|
1506
|
+
for key, df in data_dict.items():
|
1507
|
+
if df is not None:
|
1508
|
+
data_dict[key] = df.rename(names_map, axis=1)
|
1509
|
+
return StrategySignalData(**data_dict)
|
1510
|
+
|
1498
1511
|
def get_current_signal_by_groups(self, group_data: pd.Series,
|
1499
1512
|
group_order: List[str] = None
|
1500
1513
|
) -> Dict[str, pd.DataFrame]:
|
@@ -82,9 +82,9 @@ class FactsheetConfig(NamedTuple):
|
|
82
82
|
# create enumerations
|
83
83
|
FACTSHEET_CONFIG_DAILY_DATA_LONG_PERIOD = FactsheetConfig()
|
84
84
|
|
85
|
-
FACTSHEET_CONFIG_DAILY_DATA_SHORT_PERIOD = FactsheetConfig(heatmap_freq='
|
86
|
-
x_date_freq='
|
87
|
-
freq_regime='
|
85
|
+
FACTSHEET_CONFIG_DAILY_DATA_SHORT_PERIOD = FactsheetConfig(heatmap_freq='ME',
|
86
|
+
x_date_freq='ME',
|
87
|
+
freq_regime='W-WED',
|
88
88
|
freq_reg='W-WED',
|
89
89
|
alpha_an_factor=52
|
90
90
|
)
|
@@ -47,14 +47,21 @@ class MultiAssetsReport:
|
|
47
47
|
self.perf_params = perf_params
|
48
48
|
self.regime_params = regime_params
|
49
49
|
|
50
|
-
def get_prices(self,
|
51
|
-
|
50
|
+
def get_prices(self,
|
51
|
+
benchmark: str = None,
|
52
|
+
add_benchmarks_to_navs: bool = False,
|
53
|
+
time_period: TimePeriod = None) -> pd.DataFrame:
|
54
|
+
if add_benchmarks_to_navs:
|
55
|
+
prices = pd.concat([self.benchmark_prices, self.prices], axis=1)
|
56
|
+
elif benchmark is not None and benchmark not in self.prices.columns:
|
52
57
|
if isinstance(self.benchmark_prices, pd.Series):
|
53
58
|
prices = pd.concat([self.benchmark_prices, self.prices], axis=1)
|
54
59
|
else:
|
55
60
|
prices = pd.concat([self.benchmark_prices[benchmark], self.prices], axis=1)
|
56
61
|
else:
|
57
62
|
prices = self.prices
|
63
|
+
# check in case
|
64
|
+
prices = prices.loc[:, ~prices.columns.duplicated(keep='first')]
|
58
65
|
if time_period is not None:
|
59
66
|
prices = time_period.locate(prices)
|
60
67
|
return prices
|
@@ -63,7 +70,7 @@ class MultiAssetsReport:
|
|
63
70
|
regime_benchmark: str,
|
64
71
|
data_df: pd.DataFrame,
|
65
72
|
time_period: TimePeriod = None,
|
66
|
-
regime_params: BenchmarkReturnsQuantileRegimeSpecs =
|
73
|
+
regime_params: BenchmarkReturnsQuantileRegimeSpecs = None
|
67
74
|
) -> None:
|
68
75
|
if isinstance(self.benchmark_prices, pd.Series):
|
69
76
|
pivot_prices = self.benchmark_prices
|
@@ -78,10 +85,11 @@ class MultiAssetsReport:
|
|
78
85
|
data_df=data_df,
|
79
86
|
pivot_prices=pivot_prices,
|
80
87
|
benchmark=regime_benchmark,
|
81
|
-
regime_params=regime_params)
|
88
|
+
regime_params=regime_params or self.regime_params)
|
82
89
|
|
83
90
|
def plot_ra_perf_table(self,
|
84
91
|
benchmark: str,
|
92
|
+
add_benchmarks_to_navs: bool = False,
|
85
93
|
time_period: TimePeriod = None,
|
86
94
|
perf_columns: List[PerfStat] = qis.BENCHMARK_TABLE_COLUMNS,
|
87
95
|
perf_params: PerfParams = None,
|
@@ -89,8 +97,10 @@ class MultiAssetsReport:
|
|
89
97
|
ax: plt.Subplot = None,
|
90
98
|
**kwargs
|
91
99
|
) -> None:
|
92
|
-
prices = self.get_prices(benchmark,
|
93
|
-
|
100
|
+
prices = self.get_prices(benchmark=benchmark, add_benchmarks_to_navs=add_benchmarks_to_navs,
|
101
|
+
time_period=time_period)
|
102
|
+
title = title or f"RA performance table for {self.perf_params.freq_vol}-freq returns with" \
|
103
|
+
f" beta to {benchmark}: {qis.get_time_period(prices).to_str()}"
|
94
104
|
#if len(prices.columns) >= 12:
|
95
105
|
# local_kwargs = qis.update_kwargs(kwargs, dict(fontsize=3, pad=10, bbox=(0, -0.4, 1.0, 1.6)))
|
96
106
|
#else:
|
@@ -153,7 +163,8 @@ class MultiAssetsReport:
|
|
153
163
|
perf_params: PerfParams = None,
|
154
164
|
ax: plt.Subplot = None,
|
155
165
|
**kwargs) -> None:
|
156
|
-
prices = self.get_prices(time_period=time_period, benchmark=regime_benchmark
|
166
|
+
prices = self.get_prices(time_period=time_period, benchmark=regime_benchmark,
|
167
|
+
add_benchmarks_to_navs=add_benchmarks_to_navs)
|
157
168
|
prices0 = prices
|
158
169
|
if not add_benchmarks_to_navs and regime_benchmark in prices.columns:
|
159
170
|
prices0 = prices0.drop(regime_benchmark, axis=1)
|
@@ -194,6 +205,7 @@ class MultiAssetsReport:
|
|
194
205
|
self.add_regime_shadows(ax=ax, regime_benchmark=regime_benchmark, data_df=prices)
|
195
206
|
|
196
207
|
def plot_annual_returns(self,
|
208
|
+
add_benchmarks_to_navs: bool = False,
|
197
209
|
heatmap_freq: str = 'YE',
|
198
210
|
date_format: str = '%Y',
|
199
211
|
time_period: TimePeriod = None,
|
@@ -205,7 +217,8 @@ class MultiAssetsReport:
|
|
205
217
|
new_kwargs=dict(fontsize=table_fontsize,
|
206
218
|
square=False,
|
207
219
|
x_rotation=90))
|
208
|
-
qis.plot_periodic_returns_table(prices=self.get_prices(time_period=time_period
|
220
|
+
qis.plot_periodic_returns_table(prices=self.get_prices(time_period=time_period,
|
221
|
+
add_benchmarks_to_navs=add_benchmarks_to_navs),
|
209
222
|
freq=heatmap_freq,
|
210
223
|
ax=ax,
|
211
224
|
title=title or f"{heatmap_freq} Returns",
|
@@ -214,11 +227,12 @@ class MultiAssetsReport:
|
|
214
227
|
|
215
228
|
def plot_corr_table(self,
|
216
229
|
corr_freq: str = 'W-WED',
|
230
|
+
add_benchmarks_to_navs: bool = True,
|
217
231
|
time_period: TimePeriod = None,
|
218
232
|
ax: plt.Subplot = None,
|
219
233
|
**kwargs
|
220
234
|
) -> None:
|
221
|
-
prices = self.get_prices(time_period=time_period)
|
235
|
+
prices = self.get_prices(time_period=time_period, add_benchmarks_to_navs=add_benchmarks_to_navs)
|
222
236
|
if len(prices.columns) == 1: # cannot compute corr
|
223
237
|
return
|
224
238
|
if len(prices.columns) >= 12:
|
@@ -322,6 +336,7 @@ class MultiAssetsReport:
|
|
322
336
|
roll_freq=freq_sharpe,
|
323
337
|
legend_stats=legend_stats,
|
324
338
|
regime_benchmark=regime_benchmark,
|
339
|
+
regime_params=self.regime_params,
|
325
340
|
ax=ax,
|
326
341
|
**kwargs)
|
327
342
|
return fig
|
@@ -381,11 +396,12 @@ class MultiAssetsReport:
|
|
381
396
|
time_period: TimePeriod = None,
|
382
397
|
benchmark: Optional[str] = None,
|
383
398
|
perf_column: PerfStat = PerfStat.SHARPE_RF0,
|
399
|
+
add_benchmarks_to_navs: bool = True,
|
384
400
|
title: str = None,
|
385
401
|
ax: plt.Subplot = None,
|
386
402
|
**kwargs
|
387
403
|
) -> None:
|
388
|
-
prices = self.get_prices(benchmark=benchmark, time_period=time_period)
|
404
|
+
prices = self.get_prices(benchmark=benchmark, add_benchmarks_to_navs=add_benchmarks_to_navs, time_period=time_period)
|
389
405
|
qis.plot_ra_perf_bars(prices=prices,
|
390
406
|
benchmark=benchmark,
|
391
407
|
perf_column=perf_column,
|
@@ -404,8 +420,10 @@ def generate_multi_asset_factsheet(prices: pd.DataFrame,
|
|
404
420
|
heatmap_freq: str = 'YE',
|
405
421
|
time_period: TimePeriod = None, # time period for reporting
|
406
422
|
figsize: Tuple[float, float] = (8.3, 11.7), # A4 for portrait
|
407
|
-
fontsize: int =
|
423
|
+
fontsize: int = 4,
|
408
424
|
factsheet_name: str = None,
|
425
|
+
performance_bars: Tuple[PerfStat, PerfStat] = (PerfStat.SHARPE_RF0, PerfStat.MAX_DD),
|
426
|
+
drop_1y_ra_perf_table: bool = True,
|
409
427
|
**kwargs
|
410
428
|
) -> plt.Figure:
|
411
429
|
# use passed benchmark
|
@@ -490,16 +508,22 @@ def generate_multi_asset_factsheet(prices: pd.DataFrame,
|
|
490
508
|
**kwargs)
|
491
509
|
|
492
510
|
report.plot_performance_bars(ax=fig.add_subplot(gs[0:2, 2]),
|
493
|
-
|
511
|
+
add_benchmarks_to_navs=add_benchmarks_to_navs,
|
512
|
+
benchmark=benchmark,
|
513
|
+
perf_column=performance_bars[0], **kwargs)
|
494
514
|
report.plot_performance_bars(ax=fig.add_subplot(gs[0:2, 3]),
|
495
|
-
|
515
|
+
add_benchmarks_to_navs=add_benchmarks_to_navs,
|
516
|
+
benchmark=benchmark,
|
517
|
+
perf_column=performance_bars[1], **kwargs)
|
496
518
|
|
497
|
-
if len(prices.columns) >= 8:
|
519
|
+
if drop_1y_ra_perf_table or len(prices.columns) >= 8:
|
498
520
|
report.plot_ra_perf_table(benchmark=benchmark,
|
521
|
+
add_benchmarks_to_navs=add_benchmarks_to_navs,
|
499
522
|
ax=fig.add_subplot(gs[2:4, 2:]),
|
500
523
|
**kwargs)
|
501
524
|
else: # plot two tables
|
502
525
|
report.plot_ra_perf_table(benchmark=benchmark,
|
526
|
+
add_benchmarks_to_navs=add_benchmarks_to_navs,
|
503
527
|
ax=fig.add_subplot(gs[2, 2:]),
|
504
528
|
**kwargs)
|
505
529
|
|
@@ -513,14 +537,17 @@ def generate_multi_asset_factsheet(prices: pd.DataFrame,
|
|
513
537
|
**local_kwargs)
|
514
538
|
|
515
539
|
report.plot_annual_returns(ax=fig.add_subplot(gs[4:6, 2:]),
|
540
|
+
add_benchmarks_to_navs=add_benchmarks_to_navs,
|
516
541
|
heatmap_freq=heatmap_freq,
|
517
542
|
**kwargs)
|
518
543
|
|
519
544
|
report.plot_corr_table(freq=perf_params.freq,
|
545
|
+
add_benchmarks_to_navs=add_benchmarks_to_navs,
|
520
546
|
ax=fig.add_subplot(gs[6:8, 2]),
|
521
547
|
**kwargs)
|
522
548
|
report.plot_corr_table(freq=perf_params.freq,
|
523
549
|
ax=fig.add_subplot(gs[6:8, 3]),
|
550
|
+
add_benchmarks_to_navs=add_benchmarks_to_navs,
|
524
551
|
**qis.update_kwargs(kwargs, dict(time_period=time_period1)))
|
525
552
|
|
526
553
|
report.plot_regime_data(benchmark=benchmark,
|
@@ -82,22 +82,22 @@ def generate_multi_portfolio_factsheet_with_pyblogs(multi_portfolio_data: MultiP
|
|
82
82
|
b_ra_perf_table = p.Block([p.Paragraph(f"Risk-adjusted Performance table for {key}", **KWARGS_TITLE),
|
83
83
|
p.Block(table,
|
84
84
|
formatters=[
|
85
|
-
tf.FmtPercent(n_decimals=2, columns=[PerfStat.TOTAL_RETURN.to_str(
|
86
|
-
PerfStat.PA_RETURN.to_str(
|
87
|
-
PerfStat.VOL.to_str(
|
88
|
-
PerfStat.MAX_DD.to_str(
|
89
|
-
PerfStat.ALPHA_AN.to_str(
|
90
|
-
PerfStat.R2.to_str(
|
85
|
+
tf.FmtPercent(n_decimals=2, columns=[PerfStat.TOTAL_RETURN.to_str(),
|
86
|
+
PerfStat.PA_RETURN.to_str(),
|
87
|
+
PerfStat.VOL.to_str(),
|
88
|
+
PerfStat.MAX_DD.to_str(),
|
89
|
+
PerfStat.ALPHA_AN.to_str(),
|
90
|
+
PerfStat.R2.to_str()
|
91
91
|
], apply_to_header_and_index=False),
|
92
92
|
fmt_highlight_base,
|
93
93
|
fmt_highlight_index,
|
94
94
|
tf.FmtReplaceNaN(value=''),
|
95
|
-
tf.FmtHeatmap(columns=[PerfStat.PA_RETURN.to_str(
|
96
|
-
PerfStat.SHARPE_EXCESS.to_str(
|
97
|
-
PerfStat.ALPHA_AN.to_str(
|
98
|
-
PerfStat.BETA.to_str(
|
99
|
-
tf.FmtHeatmap(columns=[PerfStat.MAX_DD.to_str(
|
100
|
-
PerfStat.SKEWNESS.to_str(
|
95
|
+
tf.FmtHeatmap(columns=[PerfStat.PA_RETURN.to_str(),
|
96
|
+
PerfStat.SHARPE_EXCESS.to_str(),
|
97
|
+
PerfStat.ALPHA_AN.to_str(),
|
98
|
+
PerfStat.BETA.to_str()]),
|
99
|
+
tf.FmtHeatmap(columns=[PerfStat.MAX_DD.to_str(),
|
100
|
+
PerfStat.SKEWNESS.to_str()], max_color=(255,0,255)),
|
101
101
|
tf.FmtAddCellBorder(each=1.0,
|
102
102
|
columns=ra_perf_table.columns.to_list()[:1],
|
103
103
|
color=tf.colors.GREY,
|
@@ -114,7 +114,7 @@ def generate_multi_portfolio_factsheet_with_pyblogs(multi_portfolio_data: MultiP
|
|
114
114
|
fig_size = qis.get_df_table_size(df=ra_perf_table)
|
115
115
|
fig_perf_bar, axs = plt.subplots(1, len(perf_columns), figsize=(12, 1.2*fig_size[1]), constrained_layout=True)
|
116
116
|
for idx, perf_column in enumerate(perf_columns):
|
117
|
-
df = ra_perf_table[perf_column.to_str(
|
117
|
+
df = ra_perf_table[perf_column.to_str()].to_frame()
|
118
118
|
colors = qis.compute_heatmap_colors(a=df.to_numpy())
|
119
119
|
qis.plot_vbars(df=df,
|
120
120
|
var_format=perf_column.to_format(**kwargs),
|
@@ -132,7 +132,8 @@ def generate_multi_portfolio_factsheet_with_pyblogs(multi_portfolio_data: MultiP
|
|
132
132
|
|
133
133
|
# 3. regime conditional
|
134
134
|
fig_size = qis.get_df_table_size(df=ra_perf_table)
|
135
|
-
fig_perf_regime, axs = plt.subplots(1, len(multi_portfolio_data.benchmark_prices.columns),
|
135
|
+
fig_perf_regime, axs = plt.subplots(1, len(multi_portfolio_data.benchmark_prices.columns),
|
136
|
+
figsize=(12, 1.2*fig_size[1]), constrained_layout=True)
|
136
137
|
if len(multi_portfolio_data.benchmark_prices.columns) == 1:
|
137
138
|
axs = [axs]
|
138
139
|
for idx, benchmark in enumerate(multi_portfolio_data.benchmark_prices.columns):
|
@@ -195,7 +196,7 @@ def generate_multi_portfolio_factsheet_with_pyblogs(multi_portfolio_data: MultiP
|
|
195
196
|
x_limits = (np.nanmin(xy[param_name]), np.nanmax(xy[param_name]))
|
196
197
|
|
197
198
|
fig_scatter1, ax = plt.subplots(1, 1, figsize=(14, 4.5), constrained_layout=True)
|
198
|
-
qis.plot_scatter(df=xy, x=param_name, y=PerfStat.SHARPE_EXCESS.to_str(
|
199
|
+
qis.plot_scatter(df=xy, x=param_name, y=PerfStat.SHARPE_EXCESS.to_str(), hue=hue_name,
|
199
200
|
title=f"Sharpe",
|
200
201
|
var_format='{:.2f}',
|
201
202
|
x_limits=x_limits,
|
@@ -207,7 +208,7 @@ def generate_multi_portfolio_factsheet_with_pyblogs(multi_portfolio_data: MultiP
|
|
207
208
|
blocks.append(b_fig_scatter1)
|
208
209
|
|
209
210
|
fig_scatter2, ax = plt.subplots(1, 1, figsize=(14, 4.5), constrained_layout=True)
|
210
|
-
qis.plot_scatter(df=xy, x=param_name, y=PerfStat.MAX_DD.to_str(
|
211
|
+
qis.plot_scatter(df=xy, x=param_name, y=PerfStat.MAX_DD.to_str(), hue=hue_name,
|
211
212
|
title=f"Max DD",
|
212
213
|
var_format='{:.2%}',
|
213
214
|
x_limits=x_limits,
|
@@ -507,6 +507,8 @@ def weights_tracking_error_report_by_ac_subac(multi_portfolio_data: MultiPortfol
|
|
507
507
|
ac_group_order: List[str] = None,
|
508
508
|
sub_ac_group_data: pd.Series = None,
|
509
509
|
sub_ac_group_order: List[str] = None,
|
510
|
+
turnover_groups: pd.Series = None,
|
511
|
+
turnover_order: List[str] = None,
|
510
512
|
time_period: TimePeriod = None,
|
511
513
|
perf_params: PerfParams = PERF_PARAMS,
|
512
514
|
regime_params: BenchmarkReturnsQuantileRegimeSpecs = REGIME_PARAMS,
|
@@ -40,8 +40,8 @@ def generate_strategy_factsheet(portfolio_data: PortfolioData,
|
|
40
40
|
fontsize: int = 4,
|
41
41
|
weight_change_sample_size: int = 20,
|
42
42
|
weight_report_time_period: TimePeriod = None,
|
43
|
-
add_current_position_var_risk_sheet: bool =
|
44
|
-
add_weights_turnover_sheet: bool =
|
43
|
+
add_current_position_var_risk_sheet: bool = False,
|
44
|
+
add_weights_turnover_sheet: bool = False,
|
45
45
|
add_grouped_exposures: bool = False,
|
46
46
|
add_grouped_cum_pnl: bool = False,
|
47
47
|
add_weight_change_report: bool = False,
|
@@ -608,8 +608,7 @@ def generate_strategy_factsheet(portfolio_data: PortfolioData,
|
|
608
608
|
|
609
609
|
fig = qis.generate_price_history_report(prices=portfolio_data.prices,
|
610
610
|
**qis.update_kwargs(kwargs, dict(fontsize=4, figsize=figsize,
|
611
|
-
perf_columns=perf_columns
|
612
|
-
df_to_add=df_to_add)))
|
611
|
+
perf_columns=perf_columns)))
|
613
612
|
fig.suptitle('Program Instrument Universe', fontweight="bold", fontsize=8, color='blue')
|
614
613
|
figs.append(fig)
|
615
614
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|