my-github-tests 1.0.2__tar.gz → 1.0.3__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.
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/PKG-INFO +1 -1
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_github_tests.egg-info/PKG-INFO +1 -1
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_github_tests.egg-info/SOURCES.txt +4 -0
- my_github_tests-1.0.3/my_private_github/QuantScreener/quant_screener/create_universe_data.py +127 -0
- my_github_tests-1.0.3/my_private_github/QuantScreener/quant_screener/run_sp_screener.py +75 -0
- my_github_tests-1.0.3/my_private_github/QuantScreener/quant_screener/sp_screener.py +367 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/pe_for_saa.py +46 -6
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/pe_premia_with_factors.py +1 -1
- my_github_tests-1.0.3/my_private_github/mac_portfolio_optimizer/research/saa/__init__.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/pyproject.toml +1 -1
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/.gitattributes +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/.gitignore +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/.idea/.gitignore +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/.idea/inspectionProfiles/Project_Default.xml +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/.idea/misc.xml +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/.idea/modules.xml +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/.idea/my_private_github.iml +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/.idea/vcs.xml +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/GenAIassignment.md +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/LICENCSE.txt +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/README.md +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_github_tests.egg-info/dependency_links.txt +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_github_tests.egg-info/top_level.txt +0 -0
- {my_github_tests-1.0.2/my_private_github → my_github_tests-1.0.3/my_private_github/QuantScreener/quant_screener}/__init__.py +0 -0
- {my_github_tests-1.0.2/my_private_github/data → my_github_tests-1.0.3/my_private_github}/__init__.py +0 -0
- {my_github_tests-1.0.2/my_private_github/mac_portfolio_optimizer/core → my_github_tests-1.0.3/my_private_github/data}/__init__.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/README.md +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/__init__.py +0 -0
- {my_github_tests-1.0.2/my_private_github/mac_portfolio_optimizer/data → my_github_tests-1.0.3/my_private_github/mac_portfolio_optimizer/core}/__init__.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/core/backtester_optimiser.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/core/current_portfolio_optimiser.py +0 -0
- {my_github_tests-1.0.2/my_private_github/mac_portfolio_optimizer/mac_prod → my_github_tests-1.0.3/my_private_github/mac_portfolio_optimizer/data}/__init__.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/data/__pycache__/__init__.cpython-311.pyc +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/data/__pycache__/__init__.cpython-312.pyc +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/data/__pycache__/excel_loader.cpython-311.pyc +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/data/__pycache__/excel_loader.cpython-312.pyc +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/data/__pycache__/mac_universe.cpython-311.pyc +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/data/__pycache__/mac_universe.cpython-312.pyc +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/data/db_loader.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/data/excel_loader.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/data/forecast_returns.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/data/mac_universe.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/hf_mac_report_20251019_1723.pdf +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/local_path.py +0 -0
- {my_github_tests-1.0.2/my_private_github/mac_portfolio_optimizer/old_data → my_github_tests-1.0.3/my_private_github/mac_portfolio_optimizer/mac_prod}/__init__.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/mac_prod/fetch_prod_specs.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/mac_prod/futures_risk_model.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/mac_prod/prod_covar_specs.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/mac_prod/reporting.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/mac_prod/run_current_portfolio.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/mac_prod/run_funds_backtest.py +0 -0
- {my_github_tests-1.0.2/my_private_github/mac_portfolio_optimizer/research → my_github_tests-1.0.3/my_private_github/mac_portfolio_optimizer/old_data}/__init__.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/old_data/check_corrs.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/old_data/factor_returns.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/old_data/funds_alpha.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/old_data/funds_universe.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/old_data/index_universe.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/old_data/universe.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/old_data/universe_report.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/optimise_prod_portfolio.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/requirements.txt +0 -0
- {my_github_tests-1.0.2/my_private_github/mac_portfolio_optimizer/research/apac → my_github_tests-1.0.3/my_private_github/mac_portfolio_optimizer/research}/__init__.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/alts_universe.py +0 -0
- {my_github_tests-1.0.2/my_private_github/mac_portfolio_optimizer/research/paper → my_github_tests-1.0.3/my_private_github/mac_portfolio_optimizer/research/apac}/__init__.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/apac/run_jp_bespoke_mandate.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/apac/run_range_saa.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/apac/run_saa_backtest.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/apac/set_risk_budget_for_range_saa.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/cma_alts_universe.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/cma_betas.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/covar_reporting.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/db_qis_portfolio.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/estimate_ar.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/hedge_funds_portfolio.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/mac_covar_reporting.py +0 -0
- {my_github_tests-1.0.2/my_private_github/mac_portfolio_optimizer/research/saa → my_github_tests-1.0.3/my_private_github/mac_portfolio_optimizer/research/paper}/__init__.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/paper/article_figures.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/paper/cross_sharpe.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/paper/run_backtest_for_article.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/paper/slides_figures.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/paper/vol_check.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/returns_unsmoothing.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/risk_budget_factors.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/saa/cma_backtester.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/saa/cma_saa_optimiser.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/saa/cma_saa_optimiser_old.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/saa/implied_returns.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/saa/saa_excel_loader.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/research/saa/saa_universe.py +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/CMA_US_no_valuations.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/CMAs.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/MAC - PLF Allocation Tracker.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/MAC Allocation Tracker v.2_11mar2025.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/MAC Allocation Tracker v.2_15Apr2025.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/MAC Allocation Tracker v.2_may2025.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/MAC Allocation Tracker v.3.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/MAC Allocation Tracker v.4.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/MAC Allocation Tracker v.5.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/MAC Allocation Tracker v.6.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/MAC Allocation Tracker v.7.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/MAC Allocation Tracker v.8 - Copy (2).xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/MAC Allocation Tracker v.8 - Copy.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/MAC Allocation Tracker v.8.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/Step 1 SAA - Artur.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/WMI_Template_20250616.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/blackrock-capital-market-assumptions.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/cma/20250306_SAA_Update_en.pdf +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/cma/AC - Jakub.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/cma/CMA changes summary.docx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/cma/Global CMA & SAA universe.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/cma/Global CMA SAA universe - APAC.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/cma_factors.csv +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/cma_prices.csv +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/db_qis.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/db_qis_prices.csv +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/db_qis_universe.csv +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/dm_indices.csv +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/futures_prices.csv +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/futures_risk_factors.csv +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/hf_indices.csv +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/mac_prices.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/qach_prices.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/rate.csv +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/wmi_bbg_prices.csv +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/~$MAC Allocation Tracker v.4.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/~$MAC Allocation Tracker v.5.xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/resources/~$MAC Allocation Tracker v.8 - Copy (2).xlsx +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/settings.yaml +0 -0
- {my_github_tests-1.0.2 → my_github_tests-1.0.3}/setup.cfg +0 -0
|
@@ -16,6 +16,10 @@ my_github_tests.egg-info/SOURCES.txt
|
|
|
16
16
|
my_github_tests.egg-info/dependency_links.txt
|
|
17
17
|
my_github_tests.egg-info/top_level.txt
|
|
18
18
|
my_private_github/__init__.py
|
|
19
|
+
my_private_github/QuantScreener/quant_screener/__init__.py
|
|
20
|
+
my_private_github/QuantScreener/quant_screener/create_universe_data.py
|
|
21
|
+
my_private_github/QuantScreener/quant_screener/run_sp_screener.py
|
|
22
|
+
my_private_github/QuantScreener/quant_screener/sp_screener.py
|
|
19
23
|
my_private_github/data/__init__.py
|
|
20
24
|
my_private_github/mac_portfolio_optimizer/README.md
|
|
21
25
|
my_private_github/mac_portfolio_optimizer/__init__.py
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""
|
|
2
|
+
create universe data
|
|
3
|
+
"""
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import qis as qis
|
|
6
|
+
from typing import List, Tuple
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from bbg_fetch import fetch_fundamentals, fetch_field_timeseries_per_tickers
|
|
9
|
+
|
|
10
|
+
START_DATE = pd.Timestamp('31Dec2014') # pd.Timestamp('03Aug2001')
|
|
11
|
+
END_DATE = pd.Timestamp.now().normalize()
|
|
12
|
+
|
|
13
|
+
from quant_screener.sp_screener import Universe, FILE_NAME
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def fetch_universe_data_for_tickers(tickers: List[str],
|
|
17
|
+
benchmark_tickers: List[str] = ('NDDUWI Index', 'LGTRTRUU Index',)
|
|
18
|
+
) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
|
|
19
|
+
"""
|
|
20
|
+
fetch price and fundamental data for list of tickeers
|
|
21
|
+
"""
|
|
22
|
+
price_timeseries = fetch_field_timeseries_per_tickers(tickers=tickers, field='px_last',
|
|
23
|
+
start_date=START_DATE, end_date=END_DATE)
|
|
24
|
+
fundamentals = fetch_fundamentals(tickers=tickers, fields={'security_name': 'name',
|
|
25
|
+
'industry_sector': 'sector',
|
|
26
|
+
'id_isin': 'isin',
|
|
27
|
+
'crncy': 'crncy',
|
|
28
|
+
'12mo_put_imp_vol': 'implied_vol'})
|
|
29
|
+
benchmark_prices = fetch_field_timeseries_per_tickers(tickers=benchmark_tickers,
|
|
30
|
+
field='px_last',
|
|
31
|
+
start_date=START_DATE, end_date=END_DATE)
|
|
32
|
+
return price_timeseries, fundamentals, benchmark_prices
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_universe_tickers(local_path: str,
|
|
36
|
+
universe: Universe = Universe.SAMPLE,
|
|
37
|
+
file_name: str = FILE_NAME
|
|
38
|
+
) -> List[str]:
|
|
39
|
+
"""
|
|
40
|
+
read tickers from file_name by universe
|
|
41
|
+
"""
|
|
42
|
+
df = qis.load_df_from_excel(file_name=file_name, local_path=local_path, sheet_name=universe.value)
|
|
43
|
+
tickers = df['Ticker'].to_list()
|
|
44
|
+
tickers_split = [x.split(' ') for x in tickers]
|
|
45
|
+
tickers = [f"{x[0].upper()} {x[1].upper()} {x[-1]}" for x in tickers_split]
|
|
46
|
+
tickers = list(set(tickers))
|
|
47
|
+
|
|
48
|
+
if universe == Universe.GLOBAL: # add etf tickers
|
|
49
|
+
df = qis.load_df_from_excel(file_name=file_name, local_path=local_path, sheet_name='etfs')
|
|
50
|
+
for ticker, country in zip(df['Primary Ticker**'].astype(str), df['Country'].astype(str)):
|
|
51
|
+
if not ticker == 'nan' and not country == 'nan':
|
|
52
|
+
tickers.append(f"{ticker} {country} Equity")
|
|
53
|
+
return tickers
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def create_universe_data(local_path: str = None,
|
|
57
|
+
file_name: str = FILE_NAME,
|
|
58
|
+
universes: List[Universe] = (Universe.SAMPLE, Universe.GLOBAL, ),
|
|
59
|
+
benchmark_tickers: List[str] = ('NDDUWI Index', 'LGTRTRUU Index',)
|
|
60
|
+
) -> None:
|
|
61
|
+
"""
|
|
62
|
+
read tickers from file_name with sheets
|
|
63
|
+
"""
|
|
64
|
+
price_dfs = {}
|
|
65
|
+
for universe in universes:
|
|
66
|
+
tickers = get_universe_tickers(local_path=local_path, universe=universe, file_name=file_name)
|
|
67
|
+
price_timeseries, fundamentals, benchmark_prices = fetch_universe_data_for_tickers(tickers=tickers,
|
|
68
|
+
benchmark_tickers=benchmark_tickers)
|
|
69
|
+
price_dfs[f"{universe.value}_price"] = price_timeseries
|
|
70
|
+
price_dfs[f"{universe.value}_fundamentals"] = fundamentals
|
|
71
|
+
price_dfs['benchmark_prices'] = benchmark_prices # same for all
|
|
72
|
+
qis.save_df_to_excel(data=price_dfs, mode='a', file_name=FILE_NAME, local_path=local_path)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class UnitTests(Enum):
|
|
76
|
+
GET_UNIVERSE_TICKERS = 1
|
|
77
|
+
CREATE_UNIVERSE_DATA = 2
|
|
78
|
+
FETCH_UNIVERSE_FROM_LIST = 3
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def run_unit_test(unit_test: UnitTests):
|
|
82
|
+
|
|
83
|
+
pd.set_option('display.max_rows', 500)
|
|
84
|
+
pd.set_option('display.max_columns', 500)
|
|
85
|
+
pd.set_option('display.width', 1000)
|
|
86
|
+
|
|
87
|
+
local_path = f"C://Users//artur//OneDrive//analytics//qdev//resources//basket_screener//"
|
|
88
|
+
# local_path = f"C://Users//uarts//Python//quant_strats//resources//basket_screener//"
|
|
89
|
+
|
|
90
|
+
if unit_test == UnitTests.GET_UNIVERSE_TICKERS:
|
|
91
|
+
sample = get_universe_tickers(local_path=local_path, universe=Universe.SAMPLE)
|
|
92
|
+
all = get_universe_tickers(local_path=local_path, universe=Universe.GLOBAL)
|
|
93
|
+
print(qis.assert_list_subset(all, sample))
|
|
94
|
+
|
|
95
|
+
elif unit_test == UnitTests.CREATE_UNIVERSE_DATA:
|
|
96
|
+
create_universe_data(local_path=local_path, universes=[Universe.NOV19])
|
|
97
|
+
|
|
98
|
+
elif unit_test == UnitTests.FETCH_UNIVERSE_FROM_LIST:
|
|
99
|
+
tickers = ['NVDA US Equity',
|
|
100
|
+
'ADBE US Equity',
|
|
101
|
+
'SMH US Equity',
|
|
102
|
+
'SOXX US Equity',
|
|
103
|
+
'XBI US Equity',
|
|
104
|
+
'META US Equity',
|
|
105
|
+
'MC FP Equity',
|
|
106
|
+
'AMZN US Equity',
|
|
107
|
+
'ELV US Equity',
|
|
108
|
+
'TMO US Equity',
|
|
109
|
+
'GOOG US Equity',
|
|
110
|
+
'HSY US Equity',
|
|
111
|
+
'MSFT US Equity',
|
|
112
|
+
'PEP US Equity']
|
|
113
|
+
price_timeseries, fundamentals, benchmark_prices = fetch_universe_data_for_tickers(tickers=tickers)
|
|
114
|
+
print(price_timeseries)
|
|
115
|
+
print(fundamentals)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
if __name__ == '__main__':
|
|
119
|
+
|
|
120
|
+
unit_test = UnitTests.FETCH_UNIVERSE_FROM_LIST
|
|
121
|
+
|
|
122
|
+
is_run_all_tests = False
|
|
123
|
+
if is_run_all_tests:
|
|
124
|
+
for unit_test in UnitTests:
|
|
125
|
+
run_unit_test(unit_test=unit_test)
|
|
126
|
+
else:
|
|
127
|
+
run_unit_test(unit_test=unit_test)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
run screener on the fly
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import qis as qis
|
|
7
|
+
from typing import List, Tuple
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from quant_screener.sp_screener import UniverseScreener
|
|
10
|
+
from create_universe_data import fetch_universe_data_for_tickers
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class UnitTests(Enum):
|
|
14
|
+
CREATE_BACKETS_FROM_LIST = 1
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def run_unit_test(unit_test: UnitTests):
|
|
18
|
+
|
|
19
|
+
pd.set_option('display.max_rows', 500)
|
|
20
|
+
pd.set_option('display.max_columns', 500)
|
|
21
|
+
pd.set_option('display.width', 1000)
|
|
22
|
+
|
|
23
|
+
import matplotlib.pyplot as plt
|
|
24
|
+
import seaborn as sns
|
|
25
|
+
local_path = f"C://Users//artur//OneDrive//analytics//qdev//resources//basket_screener//"
|
|
26
|
+
# local_path = f"C://Users//uarts//Python//quant_strats//resources//basket_screener//"
|
|
27
|
+
|
|
28
|
+
if unit_test == UnitTests.CREATE_BACKETS_FROM_LIST:
|
|
29
|
+
|
|
30
|
+
tickers = ['NVDA US Equity',
|
|
31
|
+
'ADBE US Equity',
|
|
32
|
+
'SMH US Equity',
|
|
33
|
+
'SOXX US Equity',
|
|
34
|
+
'XBI US Equity',
|
|
35
|
+
'META US Equity',
|
|
36
|
+
'MC FP Equity',
|
|
37
|
+
'AMZN US Equity',
|
|
38
|
+
'ELV US Equity',
|
|
39
|
+
'TMO US Equity',
|
|
40
|
+
'GOOG US Equity',
|
|
41
|
+
'HSY US Equity',
|
|
42
|
+
'MSFT US Equity',
|
|
43
|
+
'PEP US Equity']
|
|
44
|
+
|
|
45
|
+
prices, fundamentals, benchmarks = fetch_universe_data_for_tickers(tickers=tickers)
|
|
46
|
+
|
|
47
|
+
screener = UniverseScreener(prices=prices, fundamentals=fundamentals, benchmarks=benchmarks)
|
|
48
|
+
|
|
49
|
+
selected_baskets, top_scores, corrs = screener.compute_top_baskets_min_pairs(span=104,
|
|
50
|
+
freq='W-WED',
|
|
51
|
+
vol_span=13,
|
|
52
|
+
top_quantile=None,
|
|
53
|
+
cluster_threshold=1,
|
|
54
|
+
max_number_inclusions=3
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
sample_baskets = screener.create_baskets_outputs(selected_baskets=selected_baskets, top_scores=top_scores,
|
|
58
|
+
corrs=corrs)
|
|
59
|
+
print(sample_baskets)
|
|
60
|
+
sample_baskets.to_clipboard()
|
|
61
|
+
qis.save_df_to_excel(sample_baskets, file_name='sample_baskets', local_path=local_path, add_current_date=True)
|
|
62
|
+
|
|
63
|
+
plt.show()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
if __name__ == '__main__':
|
|
67
|
+
|
|
68
|
+
unit_test = UnitTests.CREATE_BACKETS_FROM_LIST
|
|
69
|
+
|
|
70
|
+
is_run_all_tests = False
|
|
71
|
+
if is_run_all_tests:
|
|
72
|
+
for unit_test in UnitTests:
|
|
73
|
+
run_unit_test(unit_test=unit_test)
|
|
74
|
+
else:
|
|
75
|
+
run_unit_test(unit_test=unit_test)
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
"""
|
|
2
|
+
bond universe screener
|
|
3
|
+
"""
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import numpy as np
|
|
6
|
+
import qis as qis
|
|
7
|
+
import scipy.cluster.hierarchy as sch
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from typing import Tuple, List, Dict, Optional
|
|
10
|
+
from enum import Enum
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
FILE_NAME = 'PBA EQ'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Universe(Enum):
|
|
17
|
+
GLOBAL = 'global'
|
|
18
|
+
SAMPLE = 'sample'
|
|
19
|
+
NOV19 = 'NOV19'
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class UniverseScreener:
|
|
24
|
+
"""
|
|
25
|
+
data container for bond universe
|
|
26
|
+
"""
|
|
27
|
+
prices: pd.DataFrame
|
|
28
|
+
fundamentals: pd.DataFrame
|
|
29
|
+
benchmarks: pd.DataFrame
|
|
30
|
+
|
|
31
|
+
def __post_init__(self):
|
|
32
|
+
self.prices = self.prices.asfreq('B', method='ffill')
|
|
33
|
+
self.benchmarks = self.benchmarks.reindex(index=self.prices.index, method='ffill')
|
|
34
|
+
|
|
35
|
+
def get_implied_vols(self) -> pd.Series:
|
|
36
|
+
return self.fundamentals['implied_vol'].astype(float) / 100.0
|
|
37
|
+
|
|
38
|
+
def compute_realised_volatility(self, span: int = 13, freq: str = 'W-WED', af: float = 52.0) -> pd.Series:
|
|
39
|
+
# quarterly span for vol
|
|
40
|
+
returns = qis.to_returns(prices=self.prices, freq=freq, drop_first=True, is_log_returns=True)
|
|
41
|
+
vol = qis.compute_ewm_vol(data=returns, span=span, af=af)
|
|
42
|
+
return vol.iloc[-1, :]
|
|
43
|
+
|
|
44
|
+
def get_volatility(self, span: int = 52, freq: str = 'W-WED') -> pd.Series:
|
|
45
|
+
implied = self.get_implied_vols()
|
|
46
|
+
realised = self.compute_realised_volatility(span=span, freq=freq)
|
|
47
|
+
realised = realised.reindex(index=implied.index)
|
|
48
|
+
vol = pd.Series(np.where(np.isnan(implied.to_numpy()) == False, implied, realised), index=implied.index)
|
|
49
|
+
return vol
|
|
50
|
+
|
|
51
|
+
def estimate_r2_and_resid_corr(self, span: int = 52, freq: str = 'W-WED',
|
|
52
|
+
cluster_threshold: float = 5.0
|
|
53
|
+
) -> pd.DataFrame:
|
|
54
|
+
y = qis.to_returns(prices=self.prices, freq=freq, drop_first=True, is_log_returns=True)
|
|
55
|
+
x = qis.to_returns(prices=self.benchmarks, freq=freq, drop_first=True, is_log_returns=True)
|
|
56
|
+
ewm_linear_model = qis.EwmLinearModel(x=x, y=y)
|
|
57
|
+
ewm_linear_model.fit(span=span, is_x_correlated=True)
|
|
58
|
+
|
|
59
|
+
loadings = {}
|
|
60
|
+
for factor in x.columns:
|
|
61
|
+
loadings[factor] = ewm_linear_model.loadings[factor].iloc[-1, :]
|
|
62
|
+
loadings = pd.DataFrame.from_dict(loadings, orient='columns')
|
|
63
|
+
|
|
64
|
+
# estimate R^2
|
|
65
|
+
r2_t = ewm_linear_model.get_model_ewm_r2(span=span)
|
|
66
|
+
# qis.plot_time_series(df=r_2)
|
|
67
|
+
r2 = r2_t.iloc[-1, :] # .sort_values()
|
|
68
|
+
residual_corr_pd, residual_avg_corr = ewm_linear_model.get_model_residuals_corrs(span=span)
|
|
69
|
+
residual_corr_pd[np.isfinite(residual_corr_pd.to_numpy()) == False] = 0.0
|
|
70
|
+
# sns.clustermap(residual_corr_pd)
|
|
71
|
+
|
|
72
|
+
X = residual_corr_pd.to_numpy()
|
|
73
|
+
Z = sch.ward(sch.distance.pdist(X))
|
|
74
|
+
# sch.dendrogram(Z)
|
|
75
|
+
clusters = sch.fcluster(Z, t=cluster_threshold, criterion='distance')
|
|
76
|
+
clusters = pd.Series(clusters, index=self.prices.columns)
|
|
77
|
+
print(f"number of clusters: {len(clusters.unique())}")
|
|
78
|
+
|
|
79
|
+
df = pd.concat([loadings, r2.rename('r2'), residual_avg_corr.rename('resid corr'), clusters.rename('clusters')
|
|
80
|
+
], axis=1)
|
|
81
|
+
df = df.sort_values(by='r2')
|
|
82
|
+
return df
|
|
83
|
+
|
|
84
|
+
def compute_correlations(self, tickers: List[str], span: int = 52, freq: str = 'W-WED') -> pd.DataFrame:
|
|
85
|
+
returns = qis.to_returns(prices=self.prices[tickers], freq=freq, drop_first=True, is_log_returns=True)
|
|
86
|
+
corrs = qis.compute_ewm_covar(a=returns.to_numpy(), span=span, is_corr=True)
|
|
87
|
+
corrs = pd.DataFrame(corrs, index=tickers, columns=tickers)
|
|
88
|
+
return corrs
|
|
89
|
+
|
|
90
|
+
def compute_top_stocks(self,
|
|
91
|
+
span: int = 52,
|
|
92
|
+
freq: str = 'W-WED',
|
|
93
|
+
vol_span: int = 13,
|
|
94
|
+
cluster_threshold: float = 5.0,
|
|
95
|
+
top_quantile: Optional[float] = 0.75
|
|
96
|
+
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
|
97
|
+
"""
|
|
98
|
+
compute correlation factors and vol factor
|
|
99
|
+
take integrated scores on low rs, resid correlation and high vol vol
|
|
100
|
+
"""
|
|
101
|
+
correlation_factors = self.estimate_r2_and_resid_corr(span=span, freq=freq, cluster_threshold=cluster_threshold)
|
|
102
|
+
vols = self.get_volatility(span=vol_span, freq=freq).rename('implied_vol')
|
|
103
|
+
|
|
104
|
+
# compute scores for all universe
|
|
105
|
+
r2_score = qis.df_to_cross_sectional_score(1.0 - correlation_factors['r2'], is_sorted=True).rename('r2 score') # the more the better
|
|
106
|
+
resid_corr = qis.df_to_cross_sectional_score(1.0 - correlation_factors['resid corr'], is_sorted=True).rename('resid corr score') # the more the better
|
|
107
|
+
vol = qis.df_to_cross_sectional_score(vols, is_sorted=True).rename('vol score')
|
|
108
|
+
scores = pd.concat([r2_score, resid_corr, vol], axis=1)
|
|
109
|
+
# select top subset
|
|
110
|
+
if top_quantile is not None:
|
|
111
|
+
top_scores = qis.select_top_integrated_scores(scores=scores, top_quantile=top_quantile).copy()
|
|
112
|
+
else:
|
|
113
|
+
top_scores = scores
|
|
114
|
+
# merge outputs
|
|
115
|
+
all_scores = pd.concat([correlation_factors, vols], axis=1).reindex(index=top_scores.index)
|
|
116
|
+
top_scores = pd.concat([top_scores, all_scores], axis=1)
|
|
117
|
+
# add correlations
|
|
118
|
+
corrs = self.compute_correlations(tickers=top_scores.index.to_list(), span=span, freq=freq)
|
|
119
|
+
return top_scores, corrs
|
|
120
|
+
|
|
121
|
+
def compute_top_baskets(self, span: int = 52, freq: str = 'W-WED',
|
|
122
|
+
vol_span: int = 13,
|
|
123
|
+
cluster_threshold: float = 5.0,
|
|
124
|
+
top_quantile: Optional[float] = 0.75,
|
|
125
|
+
basket_size: int = 3
|
|
126
|
+
) -> Tuple[Dict[int, List[str]], pd.DataFrame, pd.DataFrame]:
|
|
127
|
+
top_scores, corrs = self.compute_top_stocks(span=span, freq=freq, vol_span=vol_span,
|
|
128
|
+
cluster_threshold=cluster_threshold, top_quantile=top_quantile)
|
|
129
|
+
assets = corrs.columns.to_list()
|
|
130
|
+
n_assets = len(assets)
|
|
131
|
+
available_indices = np.full(n_assets, True)
|
|
132
|
+
corrs_np = corrs.to_numpy()
|
|
133
|
+
selected_baskets = {}
|
|
134
|
+
for idx, asset in enumerate(corrs.columns):
|
|
135
|
+
selected_assets_basket = []
|
|
136
|
+
if available_indices[idx]: # select it first to the basket
|
|
137
|
+
selected_assets_basket.append(asset)
|
|
138
|
+
available_indices[idx] = False
|
|
139
|
+
#array_rank = np.argsort(corrs_np[:, idx]).argsort() # ranks by smalleest corr
|
|
140
|
+
#array_idx_rank = {array_rank[n]: n for n in np.arange(n_assets)} # assign rank to idx
|
|
141
|
+
#array_idx_rank = dict(sorted(array_idx_rank.items())) # sort by rank
|
|
142
|
+
array_idx_rank = np.argsort(corrs_np[:, idx]) # get indices of inreasing values
|
|
143
|
+
# print(array_idx_rank)
|
|
144
|
+
for ranked_idx in array_idx_rank:
|
|
145
|
+
if available_indices[ranked_idx]:
|
|
146
|
+
selected_assets_basket.append(assets[ranked_idx])
|
|
147
|
+
available_indices[ranked_idx] = False
|
|
148
|
+
if len(selected_assets_basket) == basket_size:
|
|
149
|
+
selected_baskets[idx+1] = selected_assets_basket
|
|
150
|
+
break
|
|
151
|
+
if np.all(available_indices == False): # all assets are taken
|
|
152
|
+
break
|
|
153
|
+
# print(f"idx={idx}: {selected_assets_basket}")
|
|
154
|
+
|
|
155
|
+
return selected_baskets, top_scores, corrs
|
|
156
|
+
|
|
157
|
+
def compute_top_baskets_min_pairs(self,
|
|
158
|
+
span: int = 52,
|
|
159
|
+
freq: str = 'W-WED',
|
|
160
|
+
vol_span: int = 13,
|
|
161
|
+
cluster_threshold: float = 5.0,
|
|
162
|
+
top_quantile: Optional[float] = 0.75,
|
|
163
|
+
basket_size: int = 3,
|
|
164
|
+
max_number_inclusions: int = 3
|
|
165
|
+
) -> Tuple[Dict[int, List[str]], pd.DataFrame, pd.DataFrame]:
|
|
166
|
+
"""
|
|
167
|
+
compute list of top stocks and their pairwise correlation
|
|
168
|
+
"""
|
|
169
|
+
top_scores, corrs = self.compute_top_stocks(span=span, freq=freq, vol_span=vol_span,
|
|
170
|
+
cluster_threshold=cluster_threshold, top_quantile=top_quantile)
|
|
171
|
+
corrs_np = corrs.to_numpy()
|
|
172
|
+
corr_pairs = {}
|
|
173
|
+
# collect corrs into a series
|
|
174
|
+
for row, asset1 in enumerate(corrs.columns):
|
|
175
|
+
for column, asset2 in enumerate(corrs.columns):
|
|
176
|
+
if column > row:
|
|
177
|
+
corr_pairs[f"{asset1}-{asset2}"] = pd.Series((asset1, asset2,
|
|
178
|
+
top_scores.loc[asset1, 'clusters'], top_scores.loc[asset2, 'clusters'],
|
|
179
|
+
corrs_np[row, column]),
|
|
180
|
+
index=['asset1', 'asset2', 'cluster1', 'cluster2', 'corr'])
|
|
181
|
+
|
|
182
|
+
# sort on last value in tuple
|
|
183
|
+
corr_pairs = pd.DataFrame.from_dict(corr_pairs, orient='index').sort_values(by='corr')
|
|
184
|
+
# remove pairs of stocks from the same
|
|
185
|
+
same_cluster = corr_pairs['cluster1'] == corr_pairs['cluster2']
|
|
186
|
+
corr_pairs = corr_pairs.loc[same_cluster == False, :]
|
|
187
|
+
|
|
188
|
+
selected_assets = {} # we count selected assets and and how many times they are included
|
|
189
|
+
n_assets = len(corrs.columns)
|
|
190
|
+
n_pairs = len(corr_pairs.index)
|
|
191
|
+
available_indices = np.full(n_pairs, True)
|
|
192
|
+
selected_baskets = {}
|
|
193
|
+
next_basket_idx = 1
|
|
194
|
+
|
|
195
|
+
def check_inclusion_capacity(asset: str) -> bool:
|
|
196
|
+
if asset not in selected_assets.keys():
|
|
197
|
+
can_be_included = True
|
|
198
|
+
else:
|
|
199
|
+
if selected_assets[asset] < max_number_inclusions:
|
|
200
|
+
can_be_included = True
|
|
201
|
+
else:
|
|
202
|
+
can_be_included = False
|
|
203
|
+
return can_be_included
|
|
204
|
+
|
|
205
|
+
def select_asset(asset: str) -> None:
|
|
206
|
+
if asset in selected_assets.keys():
|
|
207
|
+
selected_assets[asset] += 1
|
|
208
|
+
else:
|
|
209
|
+
selected_assets[asset] = 1
|
|
210
|
+
|
|
211
|
+
for idx, record in enumerate(corr_pairs.to_dict('records')): # to_dict('records') generates list of dict
|
|
212
|
+
selected_assets_basket = []
|
|
213
|
+
selected_assets_clusters = [] # do not include stocks from the same cluster
|
|
214
|
+
if available_indices[idx]: # select the pair to the basket if both assets didn't enter existing baskets
|
|
215
|
+
asset1, asset2, cluster1, cluster2 = record['asset1'], record['asset2'], record['cluster1'], record['cluster2']
|
|
216
|
+
if check_inclusion_capacity(asset1) and check_inclusion_capacity(asset2):
|
|
217
|
+
selected_assets_basket.append(asset1)
|
|
218
|
+
select_asset(asset1)
|
|
219
|
+
selected_assets_clusters.append(cluster1)
|
|
220
|
+
selected_assets_basket.append(asset2)
|
|
221
|
+
select_asset(asset2)
|
|
222
|
+
selected_assets_clusters.append(cluster2)
|
|
223
|
+
available_indices[idx] = False
|
|
224
|
+
|
|
225
|
+
# look for next fill
|
|
226
|
+
for idx1 in np.arange(0, n_pairs): # selet the tird asset if it didn't entered existing assets
|
|
227
|
+
if available_indices[idx1]:
|
|
228
|
+
asset1, asset2, cluster1, cluster2 = corr_pairs.iloc[idx1, 0], corr_pairs.iloc[idx1, 1],\
|
|
229
|
+
corr_pairs.iloc[idx1, 2], corr_pairs.iloc[idx1, 3]
|
|
230
|
+
if check_inclusion_capacity(asset1) and cluster1 not in selected_assets_clusters:
|
|
231
|
+
selected_assets_basket.append(asset1)
|
|
232
|
+
select_asset(asset1)
|
|
233
|
+
selected_assets_clusters.append(cluster1)
|
|
234
|
+
available_indices[idx1] = False
|
|
235
|
+
if len(selected_assets_basket) == basket_size:
|
|
236
|
+
selected_baskets[next_basket_idx] = selected_assets_basket
|
|
237
|
+
next_basket_idx += 1
|
|
238
|
+
break
|
|
239
|
+
if check_inclusion_capacity(asset2) and cluster2 not in selected_assets_clusters:
|
|
240
|
+
selected_assets_basket.append(asset2)
|
|
241
|
+
select_asset(asset2)
|
|
242
|
+
selected_assets_clusters.append(cluster2)
|
|
243
|
+
available_indices[idx1] = False
|
|
244
|
+
if len(selected_assets_basket) == basket_size:
|
|
245
|
+
selected_baskets[next_basket_idx] = selected_assets_basket
|
|
246
|
+
next_basket_idx += 1
|
|
247
|
+
break
|
|
248
|
+
if len(selected_assets) == n_assets or np.all(available_indices == False): # all assets are taken
|
|
249
|
+
break
|
|
250
|
+
return selected_baskets, top_scores, corrs
|
|
251
|
+
|
|
252
|
+
def create_baskets_outputs(self, selected_baskets: Dict[int, List[str]],
|
|
253
|
+
top_scores: pd.DataFrame,
|
|
254
|
+
corrs: pd.DataFrame
|
|
255
|
+
) -> pd.DataFrame:
|
|
256
|
+
df_tickers = {}
|
|
257
|
+
df_names = {}
|
|
258
|
+
impled_vols = {}
|
|
259
|
+
asset_corrs = {}
|
|
260
|
+
industry_sector = {}
|
|
261
|
+
id_isin = {}
|
|
262
|
+
clusters = {}
|
|
263
|
+
column_names = [f"Asset {n+1}" for n in np.arange(len(selected_baskets[list(selected_baskets.keys())[0]]))]
|
|
264
|
+
for idx, basket in selected_baskets.items():
|
|
265
|
+
df_tickers[f"basket {idx}"] = pd.Series(basket, index=column_names)
|
|
266
|
+
rename_map = dict(zip(basket, column_names))
|
|
267
|
+
df_names[f"basket {idx}"] = self.fundamentals.loc[basket, 'name'].rename(rename_map)
|
|
268
|
+
impled_vols[f"basket {idx}"] = self.fundamentals.loc[basket, 'implied_vol'].rename(rename_map)
|
|
269
|
+
industry_sector[f"basket {idx}"] = self.fundamentals.loc[basket, 'sector'].rename(rename_map)
|
|
270
|
+
id_isin[f"basket {idx}"] = self.fundamentals.loc[basket, 'isin'].rename(rename_map)
|
|
271
|
+
clusters[f"basket {idx}"] = top_scores.loc[basket, 'clusters'].rename(rename_map)
|
|
272
|
+
asset_corrs[f"basket {idx}"] = pd.Series(dict(corr_1_2=corrs.loc[basket[0], basket[1]],
|
|
273
|
+
corr_1_3=corrs.loc[basket[0], basket[2]],
|
|
274
|
+
corr_2_3=corrs.loc[basket[1], basket[2]]))
|
|
275
|
+
|
|
276
|
+
df_tickers = pd.DataFrame.from_dict(df_tickers, orient='index')
|
|
277
|
+
df_names = pd.DataFrame.from_dict(df_names, orient='index')
|
|
278
|
+
impled_vols = pd.DataFrame.from_dict(impled_vols, orient='index')
|
|
279
|
+
asset_corrs = pd.DataFrame.from_dict(asset_corrs, orient='index')
|
|
280
|
+
industry_sector = pd.DataFrame.from_dict(industry_sector, orient='index')
|
|
281
|
+
id_isin = pd.DataFrame.from_dict(id_isin, orient='index')
|
|
282
|
+
clusters = pd.DataFrame.from_dict(clusters, orient='index')
|
|
283
|
+
dfs = {'BBG Tickers': df_tickers, 'Names': df_names, 'Implied vols': impled_vols,
|
|
284
|
+
'Correlations': asset_corrs,
|
|
285
|
+
'Sector': industry_sector, 'ISIN': id_isin, 'Clusters': clusters}
|
|
286
|
+
dfs = pd.concat(dfs, axis=1)
|
|
287
|
+
return dfs
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def load_universe_screener(local_path: str,
|
|
291
|
+
universe: Universe = Universe.SAMPLE,
|
|
292
|
+
file_name: str = FILE_NAME
|
|
293
|
+
) -> UniverseScreener:
|
|
294
|
+
dataset_keys = [f"{universe.value}_price", f"{universe.value}_fundamentals", 'benchmark_prices']
|
|
295
|
+
dfs = qis.load_df_dict_from_excel(file_name=file_name, dataset_keys=dataset_keys, local_path=local_path)
|
|
296
|
+
|
|
297
|
+
prices = dfs[f"{universe.value}_price"].asfreq('B', method='ffill').ffill()
|
|
298
|
+
fundamentals = dfs[f"{universe.value}_fundamentals"]
|
|
299
|
+
benchmarks = dfs[f"benchmark_prices"].asfreq('B', method='ffill').ffill()
|
|
300
|
+
|
|
301
|
+
fundamentals = fundamentals.reindex(index=prices.columns)
|
|
302
|
+
universe_screener = UniverseScreener(prices=prices, fundamentals=fundamentals, benchmarks=benchmarks)
|
|
303
|
+
return universe_screener
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class UnitTests(Enum):
|
|
307
|
+
RUN_UNIVERSE = 1
|
|
308
|
+
CREATE_BASKETS = 2
|
|
309
|
+
CREATE_BASKETS2 = 3
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def run_unit_test(unit_test: UnitTests):
|
|
313
|
+
|
|
314
|
+
pd.set_option('display.max_rows', 500)
|
|
315
|
+
pd.set_option('display.max_columns', 500)
|
|
316
|
+
pd.set_option('display.width', 1000)
|
|
317
|
+
|
|
318
|
+
import matplotlib.pyplot as plt
|
|
319
|
+
import seaborn as sns
|
|
320
|
+
local_path = f"C://Users//artur//OneDrive//analytics//qdev//resources//basket_screener//"
|
|
321
|
+
# local_path = f"C://Users//uarts//Python//quant_strats//resources//basket_screener//"
|
|
322
|
+
|
|
323
|
+
screener = load_universe_screener(local_path=local_path, universe=Universe.NOV19)
|
|
324
|
+
|
|
325
|
+
if unit_test == UnitTests.RUN_UNIVERSE:
|
|
326
|
+
implied = screener.get_implied_vols().rename('Implied Vol')
|
|
327
|
+
realised = screener.compute_realised_volatility().rename('Realised Vol')
|
|
328
|
+
df = pd.concat([realised, implied], axis=1).dropna()
|
|
329
|
+
with sns.axes_style('darkgrid'):
|
|
330
|
+
fig, ax = plt.subplots(1, 1, figsize=(8, 6))
|
|
331
|
+
qis.plot_scatter(df=df,
|
|
332
|
+
full_sample_order=1,
|
|
333
|
+
fit_intercept=False,
|
|
334
|
+
title='Implied volatility vs Realised volatility',
|
|
335
|
+
alpha_format='{0:+0.2%}',
|
|
336
|
+
ax=ax)
|
|
337
|
+
|
|
338
|
+
elif unit_test == UnitTests.CREATE_BASKETS:
|
|
339
|
+
# selected_baskets, top_scores, corrs = screener.compute_top_baskets(top_quantile=0.50, cluster_threshold=7.0)
|
|
340
|
+
selected_baskets, top_scores, corrs = screener.compute_top_baskets_min_pairs(top_quantile=0.44, cluster_threshold=7.0)
|
|
341
|
+
for key, value in selected_baskets.items():
|
|
342
|
+
print(f"basket {key}: {value}")
|
|
343
|
+
|
|
344
|
+
baskets = screener.create_baskets_outputs(selected_baskets=selected_baskets, top_scores=top_scores, corrs=corrs)
|
|
345
|
+
correlation_factors = screener.estimate_r2_and_resid_corr(cluster_threshold=7.0)
|
|
346
|
+
data = dict(baskets=baskets, corrs=corrs, top_scores=top_scores, correlation_factors=correlation_factors)
|
|
347
|
+
qis.save_df_to_excel(data=data, file_name='selected_baskets_3', local_path=local_path, add_current_date=True)
|
|
348
|
+
|
|
349
|
+
elif unit_test == UnitTests.CREATE_BASKETS2:
|
|
350
|
+
selected_baskets, top_scores, corrs = screener.compute_top_baskets_min_pairs(top_quantile=None, cluster_threshold=1)
|
|
351
|
+
sample_baskets = screener.create_baskets_outputs(selected_baskets=selected_baskets, top_scores=top_scores, corrs=corrs)
|
|
352
|
+
print(sample_baskets)
|
|
353
|
+
qis.save_df_to_excel(sample_baskets, file_name='sample_baskets', local_path=local_path, add_current_date=True)
|
|
354
|
+
|
|
355
|
+
plt.show()
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
if __name__ == '__main__':
|
|
359
|
+
|
|
360
|
+
unit_test = UnitTests.CREATE_BASKETS2
|
|
361
|
+
|
|
362
|
+
is_run_all_tests = False
|
|
363
|
+
if is_run_all_tests:
|
|
364
|
+
for unit_test in UnitTests:
|
|
365
|
+
run_unit_test(unit_test=unit_test)
|
|
366
|
+
else:
|
|
367
|
+
run_unit_test(unit_test=unit_test)
|
|
@@ -6,6 +6,7 @@ import pandas as pd
|
|
|
6
6
|
import matplotlib.pyplot as plt
|
|
7
7
|
import seaborn as sns
|
|
8
8
|
import qis as qis
|
|
9
|
+
from qis import PerfStat
|
|
9
10
|
from enum import Enum
|
|
10
11
|
from typing import Tuple, Dict
|
|
11
12
|
from optimalportfolios import rolling_maximize_cara_mixture
|
|
@@ -69,6 +70,7 @@ class LocalTests(Enum):
|
|
|
69
70
|
CONSTRAINTS = 1
|
|
70
71
|
MAX_CARRA = 2
|
|
71
72
|
PLOT_MIXURE = 3
|
|
73
|
+
PE_PERFORMANCE = 4
|
|
72
74
|
|
|
73
75
|
|
|
74
76
|
@qis.timer
|
|
@@ -161,11 +163,12 @@ def run_local_test(local_test: LocalTests):
|
|
|
161
163
|
else:
|
|
162
164
|
raise NotImplementedError
|
|
163
165
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
166
|
+
if local_test != LocalTests.PE_PERFORMANCE:
|
|
167
|
+
rolling_covar_data = covar_estimator.fit_rolling_covars(
|
|
168
|
+
prices=universe_data.get_saa_prices(apply_unsmoothing_for_pe=apply_unsmoothing_for_pe),
|
|
169
|
+
risk_factor_prices=universe_data.get_risk_factors(),
|
|
170
|
+
time_period=time_period)
|
|
171
|
+
saa_taa_covar = rolling_covar_data.y_covars
|
|
169
172
|
|
|
170
173
|
if local_test == LocalTests.CONSTRAINTS:
|
|
171
174
|
|
|
@@ -263,6 +266,43 @@ def run_local_test(local_test: LocalTests):
|
|
|
263
266
|
|
|
264
267
|
plt.show()
|
|
265
268
|
|
|
269
|
+
elif local_test == LocalTests.PE_PERFORMANCE:
|
|
270
|
+
|
|
271
|
+
prices0 = universe_data.get_saa_prices(apply_unsmoothing_for_pe=False)
|
|
272
|
+
pe_reported = prices0[["Private Equity", "Private Debt"]].rename({"Private Equity": 'PE reported', "Private Debt": 'PD reported'}, axis=1)
|
|
273
|
+
benchmark_price = prices0["North America"].rename("S&P500")
|
|
274
|
+
|
|
275
|
+
prices1 = universe_data.get_saa_prices(apply_unsmoothing_for_pe=True)
|
|
276
|
+
pe_unsmoothed = prices1[["Private Equity", "Private Debt"]].rename({"Private Equity": 'PE unsmoothed', "Private Debt": 'PD unsmoothed'}, axis=1)
|
|
277
|
+
|
|
278
|
+
prices = pd.concat([pe_reported.iloc[:, 0], pe_unsmoothed.iloc[:, 0],
|
|
279
|
+
pe_reported.iloc[:, 1], pe_unsmoothed.iloc[:, 1]], axis=1)
|
|
280
|
+
|
|
281
|
+
perf_columns = [PerfStat.PA_RETURN,
|
|
282
|
+
PerfStat.VOL,
|
|
283
|
+
PerfStat.SHARPE_RF0,
|
|
284
|
+
PerfStat.MAX_DD,
|
|
285
|
+
PerfStat.SKEWNESS,
|
|
286
|
+
PerfStat.KURTOSIS,
|
|
287
|
+
PerfStat.ALPHA_AN,
|
|
288
|
+
PerfStat.BETA,
|
|
289
|
+
PerfStat.R2,
|
|
290
|
+
PerfStat.ALPHA_PVALUE]
|
|
291
|
+
|
|
292
|
+
prices = prices.loc['31Dec2004':, :]
|
|
293
|
+
qis.plot_ra_perf_table_benchmark(prices=prices, benchmark_price=benchmark_price,
|
|
294
|
+
perf_params=qis.PerfParams(freq='QE', alpha_an_factor=4),
|
|
295
|
+
perf_columns=perf_columns)
|
|
296
|
+
df = qis.to_returns(prices=pd.concat([benchmark_price,prices], axis=1).dropna(), freq='QE')
|
|
297
|
+
with sns.axes_style("darkgrid"):
|
|
298
|
+
qis.plot_histogram(df, legend_stats=qis.LegendStats.AVG_STD,
|
|
299
|
+
xvar_format='{:.1%}',
|
|
300
|
+
title='QUarterly returns',
|
|
301
|
+
add_data_std_pdf=True)
|
|
302
|
+
|
|
303
|
+
plt.show()
|
|
304
|
+
|
|
305
|
+
|
|
266
306
|
if __name__ == '__main__':
|
|
267
307
|
|
|
268
|
-
run_local_test(local_test=LocalTests.
|
|
308
|
+
run_local_test(local_test=LocalTests.PE_PERFORMANCE)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{my_github_tests-1.0.2 → my_github_tests-1.0.3}/.idea/inspectionProfiles/Project_Default.xml
RENAMED
|
File without changes
|
{my_github_tests-1.0.2 → my_github_tests-1.0.3}/.idea/inspectionProfiles/profiles_settings.xml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_github_tests.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{my_github_tests-1.0.2/my_private_github/data → my_github_tests-1.0.3/my_private_github}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{my_github_tests-1.0.2 → my_github_tests-1.0.3}/my_private_github/mac_portfolio_optimizer/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|