my-github-tests 0.1.0__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 (64) hide show
  1. my_github_tests-0.1.0/PKG-INFO +22 -0
  2. my_github_tests-0.1.0/README.md +3 -0
  3. my_github_tests-0.1.0/data/__init__.py +0 -0
  4. my_github_tests-0.1.0/data/mac_portfolio_optimizer.zip +0 -0
  5. my_github_tests-0.1.0/mac_portfolio_optimizer/__init__.py +22 -0
  6. my_github_tests-0.1.0/mac_portfolio_optimizer/core/__init__.py +0 -0
  7. my_github_tests-0.1.0/mac_portfolio_optimizer/core/backtester_optimiser.py +295 -0
  8. my_github_tests-0.1.0/mac_portfolio_optimizer/core/current_portfolio_optimiser.py +279 -0
  9. my_github_tests-0.1.0/mac_portfolio_optimizer/data/__init__.py +0 -0
  10. my_github_tests-0.1.0/mac_portfolio_optimizer/data/__pycache__/__init__.cpython-311.pyc +0 -0
  11. my_github_tests-0.1.0/mac_portfolio_optimizer/data/__pycache__/__init__.cpython-312.pyc +0 -0
  12. my_github_tests-0.1.0/mac_portfolio_optimizer/data/__pycache__/excel_loader.cpython-311.pyc +0 -0
  13. my_github_tests-0.1.0/mac_portfolio_optimizer/data/__pycache__/excel_loader.cpython-312.pyc +0 -0
  14. my_github_tests-0.1.0/mac_portfolio_optimizer/data/__pycache__/mac_universe.cpython-311.pyc +0 -0
  15. my_github_tests-0.1.0/mac_portfolio_optimizer/data/__pycache__/mac_universe.cpython-312.pyc +0 -0
  16. my_github_tests-0.1.0/mac_portfolio_optimizer/data/db_loader.py +257 -0
  17. my_github_tests-0.1.0/mac_portfolio_optimizer/data/excel_loader.py +374 -0
  18. my_github_tests-0.1.0/mac_portfolio_optimizer/data/forecast_returns.py +96 -0
  19. my_github_tests-0.1.0/mac_portfolio_optimizer/data/mac_universe.py +419 -0
  20. my_github_tests-0.1.0/mac_portfolio_optimizer/local_path.py +50 -0
  21. my_github_tests-0.1.0/mac_portfolio_optimizer/mac_prod/__init__.py +0 -0
  22. my_github_tests-0.1.0/mac_portfolio_optimizer/mac_prod/fetch_prod_specs.py +67 -0
  23. my_github_tests-0.1.0/mac_portfolio_optimizer/mac_prod/futures_risk_model.py +497 -0
  24. my_github_tests-0.1.0/mac_portfolio_optimizer/mac_prod/prod_covar_specs.py +45 -0
  25. my_github_tests-0.1.0/mac_portfolio_optimizer/mac_prod/reporting.py +93 -0
  26. my_github_tests-0.1.0/mac_portfolio_optimizer/mac_prod/run_current_portfolio.py +115 -0
  27. my_github_tests-0.1.0/mac_portfolio_optimizer/mac_prod/run_funds_backtest.py +345 -0
  28. my_github_tests-0.1.0/mac_portfolio_optimizer/old_data/__init__.py +0 -0
  29. my_github_tests-0.1.0/mac_portfolio_optimizer/old_data/check_corrs.py +37 -0
  30. my_github_tests-0.1.0/mac_portfolio_optimizer/old_data/factor_returns.py +36 -0
  31. my_github_tests-0.1.0/mac_portfolio_optimizer/old_data/funds_alpha.py +79 -0
  32. my_github_tests-0.1.0/mac_portfolio_optimizer/old_data/funds_universe.py +167 -0
  33. my_github_tests-0.1.0/mac_portfolio_optimizer/old_data/index_universe.py +382 -0
  34. my_github_tests-0.1.0/mac_portfolio_optimizer/old_data/universe.py +59 -0
  35. my_github_tests-0.1.0/mac_portfolio_optimizer/old_data/universe_report.py +74 -0
  36. my_github_tests-0.1.0/mac_portfolio_optimizer/optimise_prod_portfolio.py +66 -0
  37. my_github_tests-0.1.0/mac_portfolio_optimizer/research/__init__.py +0 -0
  38. my_github_tests-0.1.0/mac_portfolio_optimizer/research/apac/__init__.py +0 -0
  39. my_github_tests-0.1.0/mac_portfolio_optimizer/research/apac/run_jp_bespoke_mandate.py +130 -0
  40. my_github_tests-0.1.0/mac_portfolio_optimizer/research/apac/run_range_saa.py +47 -0
  41. my_github_tests-0.1.0/mac_portfolio_optimizer/research/apac/run_saa_backtest.py +155 -0
  42. my_github_tests-0.1.0/mac_portfolio_optimizer/research/apac/set_risk_budget_for_range_saa.py +198 -0
  43. my_github_tests-0.1.0/mac_portfolio_optimizer/research/estimate_ar.py +261 -0
  44. my_github_tests-0.1.0/mac_portfolio_optimizer/research/paper/__init__.py +0 -0
  45. my_github_tests-0.1.0/mac_portfolio_optimizer/research/paper/article_figures.py +419 -0
  46. my_github_tests-0.1.0/mac_portfolio_optimizer/research/paper/cross_sharpe.py +43 -0
  47. my_github_tests-0.1.0/mac_portfolio_optimizer/research/paper/run_backtest_for_article.py +209 -0
  48. my_github_tests-0.1.0/mac_portfolio_optimizer/research/paper/slides_figures.py +97 -0
  49. my_github_tests-0.1.0/mac_portfolio_optimizer/research/paper/vol_check.py +115 -0
  50. my_github_tests-0.1.0/mac_portfolio_optimizer/research/returns_unsmoothing.py +259 -0
  51. my_github_tests-0.1.0/mac_portfolio_optimizer/research/risk_budget_factors.py +238 -0
  52. my_github_tests-0.1.0/mac_portfolio_optimizer/research/saa/__init__.py +0 -0
  53. my_github_tests-0.1.0/mac_portfolio_optimizer/research/saa/cma_backtester.py +330 -0
  54. my_github_tests-0.1.0/mac_portfolio_optimizer/research/saa/cma_saa_optimiser.py +310 -0
  55. my_github_tests-0.1.0/mac_portfolio_optimizer/research/saa/cma_saa_optimiser_old.py +312 -0
  56. my_github_tests-0.1.0/mac_portfolio_optimizer/research/saa/implied_returns.py +185 -0
  57. my_github_tests-0.1.0/mac_portfolio_optimizer/research/saa/saa_excel_loader.py +207 -0
  58. my_github_tests-0.1.0/mac_portfolio_optimizer/research/saa/saa_universe.py +58 -0
  59. my_github_tests-0.1.0/my_github_tests.egg-info/PKG-INFO +22 -0
  60. my_github_tests-0.1.0/my_github_tests.egg-info/SOURCES.txt +62 -0
  61. my_github_tests-0.1.0/my_github_tests.egg-info/dependency_links.txt +1 -0
  62. my_github_tests-0.1.0/my_github_tests.egg-info/top_level.txt +3 -0
  63. my_github_tests-0.1.0/pyproject.toml +158 -0
  64. my_github_tests-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,22 @@
1
+ Metadata-Version: 2.4
2
+ Name: my-github-tests
3
+ Version: 0.1.0
4
+ Summary: my github tests
5
+ Author-email: AA SS <your.email@example.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ArturSepp/my_private_github
8
+ Project-URL: Repository, https://github.com/ArturSepp/my_private_github
9
+ Project-URL: Issues, https://github.com/ArturSepp/my_private_github/issues
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/markdown
19
+
20
+ # my_private_github
21
+ repo for my private projects
22
+ ISQ objectives and deliverables for 2025
@@ -0,0 +1,3 @@
1
+ # my_private_github
2
+ repo for my private projects
3
+ ISQ objectives and deliverables for 2025
File without changes
@@ -0,0 +1,22 @@
1
+ from mac_portfolio_optimizer.data.mac_universe import (SUB_ASSET_CLASS_DEFINITIONS,
2
+ MacUniverseData,
3
+ UniverseColumns,
4
+ RISK_FACTORS, SaaPortfolio, TaaPortfolio,
5
+ MacRangeConstraints,
6
+ SaaRangeConstraints,
7
+ AssetClasses,
8
+ RiskModel)
9
+
10
+ from mac_portfolio_optimizer.data.excel_loader import (load_mac_portfolio_universe,
11
+ load_universe_returns_from_sheet_data)
12
+
13
+ from mac_portfolio_optimizer.core.backtester_optimiser import (backtest_saa_taa_portfolios,
14
+ backtest_saa_risk_budget_portfolio,
15
+ range_backtest_lasso_portfolio_with_alphas,
16
+ tre_range_backtest_lasso_portfolio_with_alphas)
17
+
18
+ from mac_portfolio_optimizer.core.current_portfolio_optimiser import run_current_saa_portfolio, run_current_saa_taa_portfolios
19
+
20
+ from mac_portfolio_optimizer.mac_prod.reporting import generate_report
21
+
22
+ from mac_portfolio_optimizer.mac_prod.fetch_prod_specs import get_prod_covar_estimator, get_meta_params
@@ -0,0 +1,295 @@
1
+ """
2
+ implementation of optimisers for current portfolio and time series backtest
3
+ """
4
+ # packages
5
+ import pandas as pd
6
+ import qis as qis
7
+ from typing import Optional, Union, Tuple, Dict
8
+ from optimalportfolios import (rolling_risk_budgeting,
9
+ rolling_maximise_alpha_over_tre,
10
+ CovarEstimator,
11
+ compute_joint_alphas,
12
+ AlphasData,
13
+ Constraints,
14
+ EstimatedRollingCovarData)
15
+
16
+ from mac_portfolio_optimizer import MacUniverseData
17
+
18
+
19
+ def backtest_benchmarked_taa_portfolio(benchmark_rolling_weights: pd.DataFrame,
20
+ taa_alphas: pd.DataFrame,
21
+ covar_dict: Dict[pd.Timestamp, pd.DataFrame],
22
+ joint_prices: pd.DataFrame,
23
+ taa_constraints: Constraints,
24
+ joint_rebalancing_freq: pd.Series,
25
+ time_period: qis.TimePeriod = qis.TimePeriod('31Dec2003', None),
26
+ is_apply_tre_utility_objective: bool = False
27
+ ) -> pd.DataFrame:
28
+ """
29
+ wrapper for TAA optimisation given benchmark weights and alphas solve tre constraint optimisation
30
+ covar_dict is computed covariance for joint assets
31
+ """
32
+ joint_assets = joint_prices.columns
33
+ # generate rebalancing indicators
34
+ taa_rebalancing_indicators = qis.create_rebalancing_indicators_from_freqs(rebalancing_freqs=joint_rebalancing_freq,
35
+ time_period=time_period,
36
+ tickers=joint_assets)
37
+ # align alphas
38
+ taa_alphas = taa_alphas.reindex(index=list(covar_dict.keys()), method='ffill').ffill()
39
+ alphas_joint = taa_alphas.reindex(columns=joint_assets).fillna(0.0)
40
+ # reindex at covar frequency and joint prices columns
41
+ benchmark_rolling_weights = benchmark_rolling_weights.reindex(index=list(covar_dict.keys()), method='ffill').ffill()
42
+ benchmark_rolling_weights = benchmark_rolling_weights.reindex(columns=joint_assets).fillna(0.0)
43
+ taa_rolling_weights = rolling_maximise_alpha_over_tre(prices=joint_prices,
44
+ alphas=alphas_joint,
45
+ constraints0=taa_constraints,
46
+ benchmark_weights=benchmark_rolling_weights,
47
+ covar_dict=covar_dict,
48
+ time_period=time_period,
49
+ rebalancing_indicators=taa_rebalancing_indicators,
50
+ apply_total_to_good_ratio=False,
51
+ is_apply_tre_utility_objective=is_apply_tre_utility_objective)
52
+ return taa_rolling_weights
53
+
54
+
55
+ def backtest_saa_risk_budget_portfolio(universe_data: MacUniverseData,
56
+ covar_estimator: CovarEstimator = None,
57
+ saa_taa_covar: Dict[pd.Timestamp, pd.DataFrame] = None,
58
+ time_period: qis.TimePeriod = qis.TimePeriod('31Dec2003', None),
59
+ saa_rebalancing_freq: str = 'QE',
60
+ saa_constraints: Constraints = None,
61
+ management_fee: float = 0.0,
62
+ apply_unsmoothing_for_pe: bool = True,
63
+ **kwargs
64
+ ) -> Tuple[pd.DataFrame, qis.PortfolioData]:
65
+ """
66
+ implementation for computing saa risk budget portfolio
67
+ use QE or YE for saa_rebalancing_freq
68
+ saa_taa_covar can be passed or calculated on the fly
69
+ """
70
+ saa_rebalancing_schedule = qis.generate_dates_schedule(time_period=time_period, freq=saa_rebalancing_freq)
71
+ saa_risk_prices = universe_data.get_saa_prices(apply_unsmoothing_for_pe=apply_unsmoothing_for_pe)
72
+
73
+ if saa_taa_covar is not None:
74
+ saa_assets = universe_data.saa_prices.columns
75
+ saa_pd_covars = {}
76
+ for date in saa_rebalancing_schedule:
77
+ saa_pd_covars[date] = saa_taa_covar[date].loc[saa_assets, saa_assets]
78
+ else:
79
+ # it is important that returns frequency is saa_rebalancing_freq otherwise we cannot compute proper returns of PE and PD
80
+ # estimate covar at rebalancing schedule
81
+ if covar_estimator is None:
82
+ raise ValueError(f"must pass covar_estimator")
83
+ covar_estimator.rebalancing_freq = saa_rebalancing_freq
84
+ rolling_covar_data = covar_estimator.fit_rolling_covars(prices=saa_risk_prices,
85
+ risk_factor_prices=universe_data.get_risk_factors(),
86
+ time_period=time_period)
87
+ covar_dict = rolling_covar_data.y_covars
88
+ # set saa rebalancing freq
89
+ saa_pd_covars = {}
90
+ for date in saa_rebalancing_schedule:
91
+ saa_pd_covars[date] = covar_dict[date]
92
+
93
+ # generate rebalancing indicators
94
+ saa_rebalancing_indicators = qis.create_rebalancing_indicators_from_freqs(rebalancing_freqs=saa_rebalancing_freq,
95
+ time_period=time_period,
96
+ tickers=universe_data.saa_prices.columns.to_list())
97
+
98
+ if saa_constraints is None:
99
+ saa_constraints = universe_data.get_saa_constraints()
100
+ saa_rolling_weights = rolling_risk_budgeting(prices=saa_risk_prices,
101
+ time_period=time_period,
102
+ covar_dict=saa_pd_covars,
103
+ risk_budget=universe_data.get_saa_risk_budget(),
104
+ constraints0=saa_constraints,
105
+ rebalancing_indicators=saa_rebalancing_indicators,
106
+ apply_total_to_good_ratio=False) # nb
107
+
108
+ saa_portfolio_data = qis.backtest_model_portfolio(prices=universe_data.get_saa_prices(),
109
+ weights=saa_rolling_weights,
110
+ management_fee=management_fee,
111
+ ticker='SAA')
112
+ saa_portfolio_data.covar_dict = saa_pd_covars
113
+ return saa_rolling_weights, saa_portfolio_data
114
+
115
+
116
+ def backtest_saa_taa_portfolios(universe_data: MacUniverseData,
117
+ covar_estimator: CovarEstimator,
118
+ time_period: qis.TimePeriod = qis.TimePeriod('31Dec2003', None),
119
+ is_joint_saa_taa_covar: bool = True,
120
+ saa_rebalancing_freq: str = 'QE',
121
+ global_tracking_err_vol_constraint: Optional[float] = None,
122
+ group_tracking_err_vol_constraint: Optional[pd.Series] = None,
123
+ global_max_turnover_constraint: Optional[float] = None,
124
+ group_max_turnover_constraint: Optional[pd.Series] = None,
125
+ management_fee: float = 0.0,
126
+ is_saa_benchmark_for_betas: bool = False,
127
+ rebalancing_costs: float = 0.0,
128
+ apply_unsmoothing_for_pe: bool = True,
129
+ ) -> Tuple[qis.MultiPortfolioData, AlphasData, EstimatedRollingCovarData]:
130
+ """
131
+ wrapper for computing saa portfolio and saa-benchmarked taa portfolio
132
+ """
133
+ # 1. estimate covar
134
+ taa_covar_data = covar_estimator.fit_rolling_covars(risk_factor_prices=universe_data.get_risk_factors(),
135
+ prices=universe_data.get_joint_prices(apply_unsmoothing_for_pe=apply_unsmoothing_for_pe),
136
+ time_period=time_period)
137
+ covar_dict = taa_covar_data.y_covars
138
+
139
+ if is_joint_saa_taa_covar:
140
+ saa_taa_covar = covar_dict
141
+ else:
142
+ saa_taa_covar = None
143
+
144
+ # 2. run saa
145
+ saa_rolling_weights, saa_portfolio_data = backtest_saa_risk_budget_portfolio(universe_data=universe_data,
146
+ saa_taa_covar=saa_taa_covar,
147
+ time_period=time_period,
148
+ covar_estimator=covar_estimator,
149
+ saa_rebalancing_freq=saa_rebalancing_freq,
150
+ management_fee=management_fee,
151
+ apply_unsmoothing_for_pe=apply_unsmoothing_for_pe)
152
+
153
+ # 3. compute alphas
154
+ if is_saa_benchmark_for_betas:
155
+ benchmark_price = saa_portfolio_data.get_portfolio_nav()
156
+ else:
157
+ benchmark_price = universe_data.benchmarks.iloc[:, 0]
158
+ group_data, _ = universe_data.get_joint_sub_ac_group_data()
159
+ group_data_alphas, _ = universe_data.get_joint_ac_group_data()
160
+ # compute alpha using reported prices
161
+ taa_prices = universe_data.get_taa_prices(apply_unsmoothing_for_pe=apply_unsmoothing_for_pe)
162
+ manager_alphas = compute_joint_alphas(prices=taa_prices,
163
+ benchmark_price=benchmark_price,
164
+ risk_factors_prices=universe_data.get_risk_factors(),
165
+ alpha_beta_type=universe_data.get_alpha_beta_type(),
166
+ rebalancing_freq=universe_data.get_joint_rebalancing_freqs(),
167
+ estimated_betas=taa_covar_data.asset_last_betas_t,
168
+ group_data_alphas=universe_data.get_taa_asset_class_data(is_merge_alts_with_equity=True),
169
+ return_annualisation_freq_dict=universe_data.return_annualisation_freq_dict)
170
+
171
+ # 4. run tre portfolio
172
+ taa_constraints = universe_data.get_taa_constraints(
173
+ global_tracking_err_vol_constraint=global_tracking_err_vol_constraint,
174
+ group_tracking_err_vol_constraint=group_tracking_err_vol_constraint,
175
+ global_max_turnover_constraint=global_max_turnover_constraint,
176
+ group_max_turnover_constraint=group_max_turnover_constraint)
177
+
178
+ taa_rolling_weights = backtest_benchmarked_taa_portfolio(benchmark_rolling_weights=saa_rolling_weights,
179
+ taa_alphas=manager_alphas.alpha_scores,
180
+ joint_prices=universe_data.get_joint_prices(),
181
+ joint_rebalancing_freq=universe_data.get_joint_rebalancing_freqs(),
182
+ covar_dict=covar_dict,
183
+ time_period=time_period,
184
+ taa_constraints=taa_constraints)
185
+ prices = universe_data.get_joint_prices()
186
+ taa_portfolio_data = qis.backtest_model_portfolio(prices=prices,
187
+ weights=taa_rolling_weights,
188
+ management_fee=management_fee,
189
+ rebalancing_costs=rebalancing_costs,
190
+ ticker='TAA')
191
+ multi_portfolio_data = qis.MultiPortfolioData(portfolio_datas=[taa_portfolio_data, saa_portfolio_data],
192
+ benchmark_prices=universe_data.benchmarks,
193
+ covar_dict=covar_dict)
194
+ group_data, group_order = universe_data.get_joint_sub_ac_group_data()
195
+ [x.set_group_data(group_data=group_data, group_order=group_order) for x in multi_portfolio_data.portfolio_datas]
196
+ return multi_portfolio_data, manager_alphas, taa_covar_data
197
+
198
+
199
+ def range_backtest_lasso_portfolio_with_alphas(universe_data: MacUniverseData,
200
+ covar_estimator: CovarEstimator,
201
+ time_period: qis.TimePeriod = qis.TimePeriod('31Dec2003', None),
202
+ is_joint_saa_taa_covar: bool = True,
203
+ saa_rebalancing_freq: Union[str, pd.Series] = 'QE',
204
+ global_tracking_err_vol_constraint: Optional[float] = None,
205
+ group_tracking_err_vol_constraint: Optional[pd.Series] = None,
206
+ global_max_turnover_constraint: Optional[float] = None,
207
+ group_max_turnover_constraint: Optional[pd.Series] = None,
208
+ management_fee: float = 0.0,
209
+ is_saa_benchmark_for_betas: bool = False,
210
+ rebalancing_costs: float = 0.0,
211
+ ) -> (qis.MultiPortfolioData, qis.MultiPortfolioData):
212
+
213
+ spans = [24] # span, squeeze_factor
214
+ # reg_lambdas = [1e-4, 1e-5, 1e-6, 1e-7, 1e-8]
215
+ reg_lambdas = [1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 0.0]
216
+ # reg_lambdas = [5.0*1e-5, 5.0*1e-6, 5.0*1e-7]
217
+ # reg_lambdas = [5.0 * 1e-4, 1e-4, 5.0 * 1e-5, 1e-5, 5.0 * 1e-6, 1e-6]
218
+ squeeze_factors = [0.0]
219
+
220
+ saa_portfolio_datas = []
221
+ taa_portfolio_datas = []
222
+ for span in spans:
223
+ for squeeze_factor in squeeze_factors:
224
+ for reg_lambda in reg_lambdas:
225
+ ticker = f"span={span:0.0f}, lambda={reg_lambda:0.0e}, sqze={squeeze_factor:0.2f}"
226
+ covar_estimator.lasso_model.reg_lambda = reg_lambda
227
+ covar_estimator.lasso_model.span = span
228
+ covar_estimator.span_freq_dict = {'ME': span, 'QE': span / 4}
229
+ multi_portfolio_data, _, _ = backtest_saa_taa_portfolios(universe_data=universe_data,
230
+ time_period=time_period,
231
+ covar_estimator=covar_estimator,
232
+ is_joint_saa_taa_covar=is_joint_saa_taa_covar,
233
+ saa_rebalancing_freq=saa_rebalancing_freq,
234
+ is_saa_benchmark_for_betas=is_saa_benchmark_for_betas,
235
+ global_tracking_err_vol_constraint=global_tracking_err_vol_constraint,
236
+ group_tracking_err_vol_constraint=group_tracking_err_vol_constraint,
237
+ global_max_turnover_constraint=global_max_turnover_constraint,
238
+ group_max_turnover_constraint=group_max_turnover_constraint,
239
+ rebalancing_costs=rebalancing_costs,
240
+ management_fee=management_fee)
241
+ saa_portfolio_datas.append(multi_portfolio_data.portfolio_datas[1].set_ticker(ticker))
242
+ taa_portfolio_datas.append(multi_portfolio_data.portfolio_datas[0].set_ticker(ticker))
243
+
244
+ saa_multi_portfolio_data = qis.MultiPortfolioData(portfolio_datas=saa_portfolio_datas,
245
+ benchmark_prices=universe_data.benchmarks.iloc[:, 0])
246
+ taa_multi_portfolio_data = qis.MultiPortfolioData(portfolio_datas=taa_portfolio_datas,
247
+ benchmark_prices=universe_data.benchmarks.iloc[:, 0])
248
+ return saa_multi_portfolio_data, taa_multi_portfolio_data
249
+
250
+
251
+ def tre_range_backtest_lasso_portfolio_with_alphas(universe_data: MacUniverseData,
252
+ covar_estimator: CovarEstimator,
253
+ time_period: qis.TimePeriod = qis.TimePeriod('31Dec2003', None),
254
+ saa_rebalancing_freq: str = 'QE',
255
+ management_fee: float = 0.0,
256
+ is_saa_benchmark_for_betas: bool = False,
257
+ rebalancing_costs: float = 0.0,
258
+ is_grouped_constaints: bool = True
259
+ ) -> (qis.MultiPortfolioData, qis.MultiPortfolioData):
260
+
261
+ tracking_err_vol_constraints = [0.025]
262
+ turnover_constraints = [0.125, 0.25, 0.5]
263
+
264
+ saa_portfolio_datas = []
265
+ taa_portfolio_datas = []
266
+ for tracking_err_vol_constraint in tracking_err_vol_constraints:
267
+ for turnover_constraint in turnover_constraints:
268
+ if is_grouped_constaints:
269
+ ticker = f"group_tre_vol={tracking_err_vol_constraint:0.2%}, turnover={turnover_constraint:0.2%}"
270
+ group_tracking_err_vol_constraint = universe_data.set_group_uniform_tracking_error_constraint(tracking_err_vol_constraint=tracking_err_vol_constraint)
271
+ global_tracking_err_vol_constraint = None
272
+ else:
273
+ ticker = f"global_tre_vol={tracking_err_vol_constraint:0.2%}, turnover={turnover_constraint:0.2%}"
274
+ global_tracking_err_vol_constraint = tracking_err_vol_constraint
275
+ group_tracking_err_vol_constraint = None
276
+
277
+ multi_portfolio_data, _, _ = backtest_saa_taa_portfolios(universe_data=universe_data,
278
+ time_period=time_period,
279
+ covar_estimator=covar_estimator,
280
+ saa_rebalancing_freq=saa_rebalancing_freq,
281
+ global_tracking_err_vol_constraint=global_tracking_err_vol_constraint,
282
+ group_tracking_err_vol_constraint=group_tracking_err_vol_constraint,
283
+ global_max_turnover_constraint=turnover_constraint,
284
+ group_max_turnover_constraint=None,
285
+ management_fee=management_fee,
286
+ is_saa_benchmark_for_betas=is_saa_benchmark_for_betas,
287
+ rebalancing_costs=rebalancing_costs)
288
+ saa_portfolio_datas.append(multi_portfolio_data.portfolio_datas[1].set_ticker(ticker))
289
+ taa_portfolio_datas.append(multi_portfolio_data.portfolio_datas[0].set_ticker(ticker))
290
+
291
+ saa_multi_portfolio_data = qis.MultiPortfolioData(portfolio_datas=saa_portfolio_datas,
292
+ benchmark_prices=universe_data.benchmarks.iloc[:, 0])
293
+ taa_multi_portfolio_data = qis.MultiPortfolioData(portfolio_datas=taa_portfolio_datas,
294
+ benchmark_prices=universe_data.benchmarks.iloc[:, 0])
295
+ return saa_multi_portfolio_data, taa_multi_portfolio_data
@@ -0,0 +1,279 @@
1
+ """
2
+ optimisation engine for the current portfolio
3
+ """
4
+ import numpy as np
5
+ import pandas as pd
6
+ import matplotlib.pyplot as plt
7
+ import seaborn as sns
8
+ import qis as qis
9
+ from dataclasses import dataclass
10
+ from typing import Dict, Any, Tuple, Optional
11
+ from optimalportfolios import (CovarEstimator,
12
+ EstimatedCurrentCovarData,
13
+ EstimatedRollingCovarData,
14
+ wrapper_risk_budgeting,
15
+ compute_joint_alphas,
16
+ wrapper_maximise_alpha_over_tre)
17
+
18
+ from mac_portfolio_optimizer import MacUniverseData, backtest_saa_risk_budget_portfolio
19
+
20
+
21
+ @dataclass
22
+ class SaaTaaPortfolios:
23
+ """
24
+ output of current saa and taa portfolios
25
+ """
26
+ saa_valuation_date: pd.Timestamp
27
+ taa_valuation_date: pd.Timestamp
28
+ taa_df: pd.DataFrame
29
+ taa_covar: pd.DataFrame
30
+ saa_df: pd.DataFrame
31
+ saa_covar: pd.DataFrame
32
+ universe_data: MacUniverseData
33
+ taa_rolling_covar_data: EstimatedRollingCovarData
34
+ saa_current_covar_data: Optional[EstimatedCurrentCovarData]
35
+
36
+ taa_corr: pd.DataFrame = None
37
+ saa_corr: pd.DataFrame = None
38
+
39
+ def __post_init__(self):
40
+ self.taa_corr = qis.covar_to_corr(covar=self.taa_covar)
41
+ self.saa_corr = qis.covar_to_corr(covar=self.saa_covar)
42
+
43
+ def get_output_dict(self,
44
+ period_2y: qis.TimePeriod = qis.TimePeriod('31Dec2022', '31Dec2024'),
45
+ period_5y: qis.TimePeriod = qis.TimePeriod('31Dec2019', '31Dec2024'),
46
+ saa_kwargs: Dict[str, Any] = dict(perf_params=qis.PerfParams(freq='QE'), alpha_an_factor=4.0),
47
+ taa_kwargs: Dict[str, Any] = dict(perf_params=qis.PerfParams(freq='QE'), alpha_an_factor=4.0)
48
+ ) -> Dict[str, pd.DataFrame]:
49
+ """
50
+ produce outputs dfs
51
+ """
52
+ taa_df = self.taa_df.copy()
53
+ saa_df = self.saa_df.copy()
54
+
55
+ saa_prices = pd.concat([self.universe_data.benchmarks, self.universe_data.saa_prices], axis=1)
56
+ saa_perf_2y = qis.get_ra_perf_benchmark_columns(prices=period_2y.locate(saa_prices),
57
+ benchmark=self.universe_data.benchmarks.columns[0],
58
+ is_convert_to_str=False,
59
+ **saa_kwargs)
60
+ saa_perf_5y = qis.get_ra_perf_benchmark_columns(prices=period_5y.locate(saa_prices),
61
+ benchmark=self.universe_data.benchmarks.columns[0],
62
+ is_convert_to_str=False,
63
+ **saa_kwargs)
64
+
65
+ taa_prices = pd.concat([self.universe_data.benchmarks, self.universe_data.taa_prices], axis=1)
66
+ taa_perf_2y = qis.get_ra_perf_benchmark_columns(prices=period_2y.locate(taa_prices),
67
+ benchmark=self.universe_data.benchmarks.columns[0],
68
+ is_convert_to_str=False,
69
+ **taa_kwargs)
70
+ taa_perf_5y = qis.get_ra_perf_benchmark_columns(prices=period_5y.locate(taa_prices),
71
+ benchmark=self.universe_data.benchmarks.columns[0],
72
+ is_convert_to_str=False,
73
+ **taa_kwargs)
74
+
75
+ # betas
76
+ taa_betas = self.taa_rolling_covar_data.asset_last_betas_t[self.taa_valuation_date].T.loc[taa_df.index]
77
+ saa_betas = self.taa_rolling_covar_data.asset_last_betas_t[self.saa_valuation_date].T.loc[saa_df.index]
78
+
79
+ # taa r2
80
+ taa_df['r2'] = self.taa_rolling_covar_data.r2_pd[taa_df.index].iloc[-1, :]
81
+
82
+ # add betas
83
+ taa_df = pd.concat([taa_df, taa_betas], axis=1)
84
+
85
+ # taa vols
86
+ ewma_vol = qis.compute_ewm_vol(data=qis.to_returns(prices=taa_prices, is_log_returns=True),
87
+ mean_adj_type=qis.MeanAdjType.EWMA, span=24, annualization_factor=12)
88
+ taa_df['lasso-vol'] = np.sqrt(np.diag(self.taa_covar)) # todo
89
+ taa_df['ewm-vol'] = ewma_vol.iloc[-1, :]
90
+ taa_df['vol 2y'] = taa_perf_2y['Vol'].loc[taa_df.index]
91
+
92
+ # merge with universe taa data
93
+ taa_universe_df = self.universe_data.taa_universe_df.copy()
94
+ if 'Current Max' in taa_universe_df.columns: # remove level 2 attributions
95
+ col_index = taa_universe_df.columns.get_loc('Current Max')
96
+ columns_to_keep = taa_universe_df.columns[:col_index + 1]
97
+ taa_universe_df = taa_universe_df[columns_to_keep]
98
+
99
+ taa_df = pd.concat([taa_universe_df, taa_df, ], axis=1)
100
+ # saa r2 and vols
101
+ saa_df['r2'] = self.taa_rolling_covar_data.r2_pd[saa_df.index].iloc[-1, :]
102
+ saa_df['lasso-vol'] = np.sqrt(np.diag(self.saa_covar))
103
+ saa_df['vol 5y'] = saa_perf_5y['Vol'].loc[saa_df.index]
104
+
105
+ data = dict(taa_df=taa_df,
106
+ saa_df=saa_df,
107
+ taa_perf_2y=taa_perf_2y,
108
+ taa_perf_5y=taa_perf_5y,
109
+ saa_perf_2y=saa_perf_2y,
110
+ saa_perf_5y=saa_perf_5y,
111
+ taa_corr=self.taa_corr,
112
+ saa_corr=self.saa_corr,
113
+ taa_betas=taa_betas,
114
+ saa_betas=saa_betas)
115
+ return data
116
+
117
+ def plot_taa_corr(self, **kwargs) -> plt.Subplot:
118
+ df = self.taa_corr
119
+ with sns.axes_style('darkgrid'):
120
+ width, height = qis.get_df_table_size(df=df)
121
+ fig, ax = plt.subplots(1, 1, figsize=(width, width), constrained_layout=True)
122
+ qis.plot_heatmap(df=df,
123
+ var_format='{:.2f}',
124
+ cmap='PiYG',
125
+ ax=ax,
126
+ **kwargs)
127
+ return fig
128
+
129
+
130
+ def run_current_saa_portfolio(universe_data: MacUniverseData,
131
+ covar_estimator: CovarEstimator,
132
+ saa_pd_covar: pd.DataFrame = None,
133
+ saa_valuation_date: pd.Timestamp = pd.Timestamp('31Dec2024'),
134
+ saa_rebalancing_indicators: pd.Series = None # for PE
135
+ ) -> Tuple[pd.DataFrame, pd.DataFrame, Optional[EstimatedCurrentCovarData]]:
136
+ """
137
+ use QE or YE for saa_rebalancing_freq
138
+ """
139
+ if saa_pd_covar is None:
140
+ saa_covar_data = covar_estimator.fit_current_covars(
141
+ risk_factor_prices=universe_data.get_risk_factors().loc[:saa_valuation_date, :],
142
+ prices=universe_data.get_joint_prices().loc[:saa_valuation_date, :])
143
+ saa_pd_covar = saa_covar_data.y_covar
144
+ else:
145
+ saa_covar_data = None
146
+
147
+ saa_outputs = wrapper_risk_budgeting(pd_covar=saa_pd_covar,
148
+ constraints0=universe_data.get_saa_constraints(),
149
+ weights_0=None,
150
+ risk_budget=universe_data.get_saa_risk_budget(),
151
+ rebalancing_indicators=saa_rebalancing_indicators,
152
+ apply_total_to_good_ratio=False,
153
+ detailed_output=True)
154
+ return saa_outputs, saa_pd_covar, saa_covar_data
155
+
156
+
157
+ def run_current_saa_taa_portfolios(universe_data: MacUniverseData,
158
+ covar_estimator: CovarEstimator,
159
+ saa_valuation_date: pd.Timestamp = pd.Timestamp('31Dec2024'),
160
+ taa_valuation_date: pd.Timestamp = pd.Timestamp('28Feb2024'),
161
+ time_period: qis.TimePeriod = qis.TimePeriod('31Dec2003', None),
162
+ saa_rebalancing_freq: str = 'QE',
163
+ taa_weights_0: pd.Series = None,
164
+ is_joint_saa_taa_covar: bool = True,
165
+ taa_rebalancing_indicators: pd.Series = None,
166
+ global_tracking_err_vol_constraint: Optional[float] = None,
167
+ group_tracking_err_vol_constraint: Optional[pd.Series] = None,
168
+ global_max_turnover_constraint: float = None,
169
+ group_max_turnover_constraint: Optional[pd.Series] = None,
170
+ is_saa_benchmark_for_betas: bool = True,
171
+ use_current_min_max: bool = True
172
+ ) -> SaaTaaPortfolios:
173
+ """
174
+ optimisation of the current saa_taa portfolio
175
+ taa_weights_0 are provided in universe_data
176
+ taa_weights_0 = universe_data.get_taa_current_weights()
177
+ """
178
+ # 1. estimate rolling covar for taa and betas
179
+ # we need betas time series to estiate alphas
180
+ risk_factor_prices = universe_data.get_risk_factors().loc[:taa_valuation_date, :]
181
+ taa_rolling_covar_data = covar_estimator.fit_rolling_covars(risk_factor_prices=risk_factor_prices,
182
+ prices=universe_data.get_joint_prices().loc[:taa_valuation_date, :],
183
+ time_period=time_period)
184
+ if is_joint_saa_taa_covar:
185
+ saa_taa_covar = taa_rolling_covar_data.y_covars
186
+ if saa_valuation_date not in saa_taa_covar.keys():
187
+ raise KeyError(f"{saa_valuation_date} not in {saa_taa_covar.keys()}")
188
+ saa_assets = universe_data.saa_prices.columns
189
+ saa_pd_covar = saa_taa_covar[saa_valuation_date].loc[saa_assets, saa_assets]
190
+ else:
191
+ raise NotImplementedError
192
+
193
+ # construct saa portfolio: we need saa portfolio weights for benchmark calculation and low beta characteristics
194
+ saa_rebalancing_indicators = qis.create_rebalancing_indicators_from_freqs(rebalancing_freqs=saa_rebalancing_freq,
195
+ time_period=time_period,
196
+ tickers=universe_data.saa_prices.columns.to_list())
197
+ saa_rolling_weights, saa_portfolio_data = backtest_saa_risk_budget_portfolio(universe_data=universe_data,
198
+ saa_taa_covar=saa_taa_covar,
199
+ time_period=time_period,
200
+ covar_estimator=covar_estimator,
201
+ saa_rebalancing_freq=saa_rebalancing_freq,
202
+ management_fee=0.0)
203
+ # to do: use saa_rolling_weights
204
+ saa_df, saa_pd_covar, saa_current_covar_data = run_current_saa_portfolio(universe_data=universe_data,
205
+ covar_estimator=covar_estimator,
206
+ saa_pd_covar=saa_pd_covar,
207
+ saa_valuation_date=saa_valuation_date,
208
+ saa_rebalancing_indicators=saa_rebalancing_indicators.loc[saa_valuation_date, :])
209
+ rebalancing_freq = universe_data.get_joint_rebalancing_freqs()
210
+
211
+ current_saa_weights = saa_df['weights']
212
+ saa_df = saa_df.rename({'weights': 'SAA weight'}, axis=1)
213
+
214
+ # 2. compute alphas
215
+ if is_saa_benchmark_for_betas:
216
+ benchmark_price = saa_portfolio_data.get_portfolio_nav()
217
+ else:
218
+ benchmark_price = universe_data.benchmarks.iloc[:, 0]
219
+
220
+ joint_alphas = compute_joint_alphas(prices=universe_data.taa_prices,
221
+ benchmark_price=benchmark_price,
222
+ risk_factors_prices=universe_data.get_risk_factors(),
223
+ alpha_beta_type=universe_data.get_alpha_beta_type(),
224
+ rebalancing_freq=rebalancing_freq,
225
+ estimated_betas=taa_rolling_covar_data.asset_last_betas_t,
226
+ group_data_alphas=universe_data.get_taa_asset_class_data(is_merge_alts_with_equity=True),
227
+ return_annualisation_freq_dict=universe_data.return_annualisation_freq_dict)
228
+
229
+ # 3. run tre portfolio
230
+ if taa_weights_0 is None:
231
+ taa_weights_0 = universe_data.get_taa_current_weights()
232
+ taa_constraints = universe_data.get_taa_constraints(use_current_min_max=use_current_min_max,
233
+ global_tracking_err_vol_constraint=global_tracking_err_vol_constraint,
234
+ group_tracking_err_vol_constraint=group_tracking_err_vol_constraint,
235
+ global_max_turnover_constraint=global_max_turnover_constraint,
236
+ group_max_turnover_constraint=group_max_turnover_constraint)
237
+
238
+ # extend alphas and weights
239
+ taa_assets = universe_data.taa_prices.columns.to_list()
240
+ taa_covar = taa_rolling_covar_data.y_covars[taa_valuation_date].loc[taa_assets, taa_assets]
241
+ if taa_rebalancing_indicators is not None:
242
+ rebalancing_indicators = taa_rebalancing_indicators.reindex(index=taa_assets).fillna(0)
243
+ else:
244
+ rebalancing_indicators = pd.Series(1, index=taa_assets)
245
+ current_taa_weights = wrapper_maximise_alpha_over_tre(pd_covar=taa_covar,
246
+ alphas=joint_alphas.alpha_scores.loc[taa_valuation_date, :],
247
+ constraints0=taa_constraints,
248
+ benchmark_weights=current_saa_weights,
249
+ weights_0=taa_weights_0,
250
+ rebalancing_indicators=rebalancing_indicators,
251
+ apply_total_to_good_ratio=False,
252
+ detailed_output=True)
253
+
254
+ taa_weights = current_taa_weights['weights'].loc[taa_assets].rename('Taa Weights')
255
+ # do few round rounding to converge to weights with 2 decimals
256
+ for n in np.arange(5):
257
+ taa_weights = taa_weights / np.nansum(taa_weights)
258
+ taa_weights = taa_weights.round(decimals=4)
259
+
260
+ if taa_weights_0 is not None:
261
+ taa_weights0 = taa_weights_0.loc[universe_data.taa_prices.columns].rename('Given Taa Weights')
262
+ else:
263
+ taa_weights0 = pd.Series(0.0, index=universe_data.taa_prices.columns).rename('Given Taa Weights')
264
+
265
+ taa_rebalancing_indicator = rebalancing_indicators.rename('Is Rebalanced')
266
+ delta = taa_weights.subtract(taa_weights0).rename('Delta weights')
267
+ alphas = joint_alphas.get_alphas_snapshot(date=taa_valuation_date)
268
+ risk_contribs = current_taa_weights['asset_rc_ratio'].loc[taa_assets].rename('Risk Contribution')
269
+ taa_df = pd.concat([taa_rebalancing_indicator, taa_weights0, taa_weights, delta, risk_contribs, alphas], axis=1)
270
+ saa_taa_portfolios = SaaTaaPortfolios(saa_valuation_date=saa_valuation_date,
271
+ taa_valuation_date=taa_valuation_date,
272
+ taa_df=taa_df,
273
+ saa_df=saa_df,
274
+ taa_covar=taa_covar,
275
+ saa_covar=saa_pd_covar,
276
+ universe_data=universe_data,
277
+ taa_rolling_covar_data=taa_rolling_covar_data,
278
+ saa_current_covar_data=saa_current_covar_data)
279
+ return saa_taa_portfolios