qis 2.1.33__tar.gz → 2.1.34__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-2.1.33 → qis-2.1.34}/PKG-INFO +2 -2
- {qis-2.1.33 → qis-2.1.34}/pyproject.toml +1 -1
- {qis-2.1.33 → qis-2.1.34}/qis/examples/factsheets/multi_assets.py +4 -3
- {qis-2.1.33 → qis-2.1.34}/qis/examples/factsheets/multi_strategy.py +2 -2
- {qis-2.1.33 → qis-2.1.34}/qis/examples/factsheets/strategy.py +1 -1
- {qis-2.1.33 → qis-2.1.34}/qis/examples/factsheets/strategy_benchmark.py +2 -2
- qis-2.1.34/qis/examples/perp_pricing.py +56 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/multi_portfolio_data.py +182 -124
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/portfolio_data.py +5 -1
- {qis-2.1.33 → qis-2.1.34}/LICENSE.txt +0 -0
- {qis-2.1.33 → qis-2.1.34}/README.md +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/__init__.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/best_returns.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/bond_futures_portfolio.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/bootstrap_analysis.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/boxplot_conditional_returns.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/btc_asset_corr.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/constant_notional.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/constant_weight_portfolios.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/core/perf_bbg_prices.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/core/price_plots.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/core/us_election.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/credit_spreads.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/credit_trackers.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/europe_futures.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/factsheets/pyblogs_reports.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/generate_option_rolls.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/interpolation_infrequent_returns.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/leveraged_strategies.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/long_short.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/momentum_indices.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/ohlc_vol_analysis.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/overnight_returns.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/perf_external_assets.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/readme_performances.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/risk_return_frontier.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/rolling_performance.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/seasonality.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/sharpe_vs_sortino.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/simulate_quant_strats.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/test_ewm.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/test_scatter.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/try_pybloqs.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/universe_corrs.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/vix_beta_to_equities_bonds.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/vix_conditional_returns.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/vix_spy_by_year.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/vix_tenor_analysis.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/examples/vol_without_weekends.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/file_utils.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/local_path.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/README.md +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/__init__.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/linear/__init__.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/linear/auto_corr.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/linear/corr_cov_matrix.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/linear/ewm.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/linear/ewm_convolution.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/linear/ewm_factors.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/linear/ewm_winsor_outliers.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/linear/pca.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/linear/plot_correlations.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/linear/ra_returns.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/stats/__init__.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/stats/bootstrap.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/stats/ohlc_vol.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/stats/rolling_stats.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/models/stats/test_bootstrap.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/perfstats/README.md +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/perfstats/__init__.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/perfstats/cond_regression.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/perfstats/config.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/perfstats/desc_table.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/perfstats/fx_ops.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/perfstats/perf_stats.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/perfstats/regime_classifier.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/perfstats/returns.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/perfstats/timeseries_bfill.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/README.md +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/__init__.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/bars.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/boxplot.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/contour.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/derived/__init__.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/derived/data_timeseries.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/derived/desc_table.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/derived/drawdowns.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/derived/perf_table.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/derived/prices.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/derived/regime_class_table.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/derived/regime_data.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/derived/regime_pdf.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/derived/regime_scatter.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/derived/returns_heatmap.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/derived/returns_scatter.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/errorbar.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/heatmap.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/histogram.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/histplot2d.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/lineplot.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/pie.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/qqplot.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/reports/__init__.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/reports/econ_data_single.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/reports/gantt_data_history.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/reports/price_history.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/reports/utils.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/scatter.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/stackplot.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/table.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/time_series.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/plots/utils.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/README.md +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/__init__.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/backtester.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/ewm_portfolio_risk.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/reports/__init__.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/reports/brinson_attribution.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/reports/config.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/reports/multi_assets_factsheet.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/reports/multi_strategy_factseet_pybloqs.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/reports/multi_strategy_factsheet.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/reports/strategy_benchmark_factsheet.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/reports/strategy_benchmark_factsheet_pybloqs.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/reports/strategy_factsheet.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/reports/strategy_signal_factsheet.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/strats/__init__.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/strats/quant_strats_delta1.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/portfolio/strats/seasonal_strats.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/settings.yaml +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/sql_engine.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/test_data.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/README.md +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/__init__.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/dates.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/df_agg.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/df_cut.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/df_freq.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/df_groups.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/df_melt.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/df_ops.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/df_str.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/df_to_scores.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/df_to_weights.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/generic.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/np_ops.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/ols.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/sampling.py +0 -0
- {qis-2.1.33 → qis-2.1.34}/qis/utils/struct_ops.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.3
|
2
2
|
Name: qis
|
3
|
-
Version: 2.1.
|
3
|
+
Version: 2.1.34
|
4
4
|
Summary: Implementation of visualisation and reporting analytics for Quantitative Investment Strategies
|
5
5
|
Home-page: https://github.com/ArturSepp/QuantInvestStrats
|
6
6
|
License: LICENSE.txt
|
@@ -23,8 +23,10 @@ class UnitTests(Enum):
|
|
23
23
|
|
24
24
|
def run_unit_test(unit_test: UnitTests):
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
end_date = '10Jan2025' # performance repoting
|
27
|
+
|
28
|
+
prices = None # if Noe, use yahoo finance data
|
29
|
+
|
28
30
|
if unit_test == UnitTests.CORE_ETFS:
|
29
31
|
benchmark = 'SPY'
|
30
32
|
tickers = [benchmark, 'QQQ', 'EEM', 'TLT', 'IEF', 'LQD', 'HYG', 'SHY', 'GLD']
|
@@ -78,7 +80,6 @@ def run_unit_test(unit_test: UnitTests):
|
|
78
80
|
prices = prices.rename(tickers, axis=1)
|
79
81
|
time_period = qis.get_time_period(prices)
|
80
82
|
|
81
|
-
|
82
83
|
else:
|
83
84
|
raise NotImplementedError
|
84
85
|
|
@@ -66,8 +66,8 @@ class UnitTests(Enum):
|
|
66
66
|
def run_unit_test(unit_test: UnitTests):
|
67
67
|
|
68
68
|
if unit_test == UnitTests.VOLPARITY_SPAN:
|
69
|
-
|
70
|
-
time_period = qis.TimePeriod('31Dec2005', '
|
69
|
+
# time period for portfolio reporting
|
70
|
+
time_period = qis.TimePeriod('31Dec2005', '10Jan2025')
|
71
71
|
|
72
72
|
prices, benchmark_prices, group_data = fetch_universe_data()
|
73
73
|
multi_portfolio_data = generate_volparity_multi_strategy(prices=prices,
|
@@ -86,7 +86,7 @@ class UnitTests(Enum):
|
|
86
86
|
|
87
87
|
def run_unit_test(unit_test: UnitTests):
|
88
88
|
|
89
|
-
time_period = qis.TimePeriod('31Dec2005', '
|
89
|
+
time_period = qis.TimePeriod('31Dec2005', '10Jan2025') # time period for portfolio reporting
|
90
90
|
time_period_short = TimePeriod('31Dec2022', time_period.end)
|
91
91
|
rebalancing_costs = 0.0010 # per traded volume
|
92
92
|
|
@@ -82,7 +82,7 @@ class UnitTests(Enum):
|
|
82
82
|
def run_unit_test(unit_test: UnitTests):
|
83
83
|
|
84
84
|
# time period for portfolio reporting
|
85
|
-
time_period = qis.TimePeriod('31Dec2006', '
|
85
|
+
time_period = qis.TimePeriod('31Dec2006', '10Jan2025')
|
86
86
|
prices, benchmark_prices, group_data = fetch_universe_data()
|
87
87
|
multi_portfolio_data = generate_volparity_multiportfolio(prices=prices,
|
88
88
|
benchmark_prices=benchmark_prices,
|
@@ -105,7 +105,7 @@ def run_unit_test(unit_test: UnitTests):
|
|
105
105
|
add_grouped_exposures=False, # for strategy factsheet
|
106
106
|
add_grouped_cum_pnl=False, # for strategy factsheet
|
107
107
|
**fetch_default_report_kwargs(time_period=time_period,
|
108
|
-
|
108
|
+
add_rates_data=True))
|
109
109
|
"""
|
110
110
|
|
111
111
|
"""
|
@@ -0,0 +1,56 @@
|
|
1
|
+
import pandas as pd
|
2
|
+
import numpy as np
|
3
|
+
import matplotlib.pyplot as plt
|
4
|
+
import seaborn as sns
|
5
|
+
import qis as qis
|
6
|
+
from bbg_fetch import fetch_field_timeseries_per_tickers, fetch_index_members_weights, fetch_bonds_info
|
7
|
+
|
8
|
+
|
9
|
+
index_ticker = 'I31415US Index'
|
10
|
+
|
11
|
+
end_dates = ['20180101', '20190101', '20200101', '20210101', '20220101', '20230101', '20240101', '20250101', '20250106']
|
12
|
+
|
13
|
+
data_out = {}
|
14
|
+
weighted_resets = []
|
15
|
+
for idx, end_date in enumerate(end_dates):
|
16
|
+
if idx > 0:
|
17
|
+
members = fetch_index_members_weights(index_ticker, END_DATE_OVERRIDE=end_dates[idx-1])
|
18
|
+
corp_index = [f"{x} corp" for x in members.index]
|
19
|
+
members.index = corp_index
|
20
|
+
"""
|
21
|
+
amt_outstanding = fetch_bonds_info(isins=members.index.to_list(), fields=['amt_outstanding'],
|
22
|
+
END_DATE_OVERRIDE=end_dates[idx-1])['amt_outstanding']
|
23
|
+
amt_outstanding = amt_outstanding.loc[members.index]
|
24
|
+
amt_outstanding.index = corp_index
|
25
|
+
"""
|
26
|
+
prices = fetch_field_timeseries_per_tickers(tickers=corp_index,
|
27
|
+
start_date=pd.Timestamp(end_dates[idx-1]),
|
28
|
+
end_date=pd.Timestamp(end_date),
|
29
|
+
freq='B')
|
30
|
+
prices = prices.resample('W-WED').last()
|
31
|
+
# market_value = prices.multiply(amt_outstanding, axis=1)
|
32
|
+
#market_value.divide(np.nansum(market_value, axis=1, keepdims=True), axis=1)
|
33
|
+
is_reset = (prices > 100).astype(float)
|
34
|
+
market_weights = members.iloc[:, 0]
|
35
|
+
weighted_reset = is_reset.multiply(market_weights, axis=1)
|
36
|
+
weighted_reset = weighted_reset.sum(1)
|
37
|
+
weighted_resets.append(weighted_reset)
|
38
|
+
data_out[f"{end_dates[idx-1]} members"] = members
|
39
|
+
data_out[f"{end_date} prices"] = prices
|
40
|
+
data_out[f"{end_date} is_reset"] = is_reset
|
41
|
+
|
42
|
+
weighted_resets = pd.concat(weighted_resets)
|
43
|
+
weighted_resets = weighted_resets.loc[~weighted_resets.index.duplicated(keep='first')]
|
44
|
+
weighted_resets = weighted_resets.to_frame('weighted_par_reset %')
|
45
|
+
print(weighted_resets)
|
46
|
+
|
47
|
+
data_out['weighted_resets'] = weighted_resets
|
48
|
+
qis.save_df_to_excel(data=data_out, file_name='perp_pricing')
|
49
|
+
|
50
|
+
with sns.axes_style("darkgrid"):
|
51
|
+
fig, ax = plt.subplots(1, 1, figsize=(15, 8), tight_layout=True)
|
52
|
+
qis.plot_time_series(weighted_resets,
|
53
|
+
title='weighted_par_reset',
|
54
|
+
ax=ax)
|
55
|
+
|
56
|
+
plt.show()
|
@@ -7,8 +7,7 @@ import numpy as np
|
|
7
7
|
import pandas as pd
|
8
8
|
import matplotlib.pyplot as plt
|
9
9
|
from dataclasses import dataclass
|
10
|
-
from typing import List, Optional, Tuple, Union
|
11
|
-
|
10
|
+
from typing import List, Optional, Tuple, Union, Dict
|
12
11
|
# qis
|
13
12
|
import qis as qis
|
14
13
|
from qis import PerfParams, PerfStat, RegimeData, BenchmarkReturnsQuantileRegimeSpecs, TimePeriod, RollingPerfStat
|
@@ -17,7 +16,6 @@ import qis.utils.df_groups as dfg
|
|
17
16
|
import qis.perfstats.returns as ret
|
18
17
|
import qis.perfstats.perf_stats as rpt
|
19
18
|
import qis.perfstats.regime_classifier as rcl
|
20
|
-
|
21
19
|
# plots
|
22
20
|
import qis.plots.time_series as pts
|
23
21
|
import qis.plots.derived.prices as ppd
|
@@ -28,6 +26,7 @@ import qis.plots.derived.drawdowns as cdr
|
|
28
26
|
from qis.portfolio.portfolio_data import PortfolioData, AttributionMetric
|
29
27
|
|
30
28
|
|
29
|
+
# default perf params
|
31
30
|
PERF_PARAMS = PerfParams(freq='W-WED')
|
32
31
|
REGIME_PARAMS = BenchmarkReturnsQuantileRegimeSpecs(freq='ME')
|
33
32
|
|
@@ -36,9 +35,15 @@ REGIME_PARAMS = BenchmarkReturnsQuantileRegimeSpecs(freq='ME')
|
|
36
35
|
class MultiPortfolioData:
|
37
36
|
"""
|
38
37
|
data structure to unify multi portfolio reporting
|
38
|
+
portfolio_datas: List[PortfolioData]
|
39
|
+
benchmark_prices: Union[pd.DataFrame, pd.Series] = None # Optional
|
40
|
+
pd_covars: Dict[pd.Timestamp, pd.DataFrame] = None # annualised covariance matrix
|
41
|
+
for investable universe for computing tracking error
|
39
42
|
"""
|
40
43
|
portfolio_datas: List[PortfolioData]
|
41
44
|
benchmark_prices: Union[pd.DataFrame, pd.Series] = None
|
45
|
+
pd_covars: Dict[pd.Timestamp, pd.DataFrame] = None
|
46
|
+
navs: pd.DataFrame = None # computed internally
|
42
47
|
|
43
48
|
def __post_init__(self):
|
44
49
|
# default frequency is freq of backtests, can be non for strats at different freqs
|
@@ -82,7 +87,7 @@ class MultiPortfolioData:
|
|
82
87
|
navs = pd.concat([self.benchmark_prices[benchmark].reindex(index=navs.index).ffill(), navs], axis=1)
|
83
88
|
elif add_benchmarks_to_navs:
|
84
89
|
navs = pd.concat([navs, self.benchmark_prices.reindex(index=navs.index).ffill()], axis=1).ffill()
|
85
|
-
|
90
|
+
|
86
91
|
if time_period is not None:
|
87
92
|
navs = time_period.locate(navs)
|
88
93
|
return navs
|
@@ -102,16 +107,170 @@ class MultiPortfolioData:
|
|
102
107
|
time_period: TimePeriod = None,
|
103
108
|
is_add_group_total: bool = False
|
104
109
|
) -> pd.DataFrame:
|
105
|
-
prices = self.portfolio_datas[portfolio_idx].get_group_navs(time_period=time_period,
|
110
|
+
prices = self.portfolio_datas[portfolio_idx].get_group_navs(time_period=time_period,
|
111
|
+
is_add_group_total=is_add_group_total)
|
106
112
|
if benchmark is not None:
|
107
113
|
benchmark_price = self.get_benchmark_price(benchmark=self.benchmark_prices.columns[0],
|
108
114
|
time_period=time_period)
|
109
115
|
prices = pd.concat([prices, benchmark_price], axis=1)
|
110
116
|
return prices
|
111
117
|
|
112
|
-
|
113
|
-
|
114
|
-
|
118
|
+
def get_ra_perf_table(self,
|
119
|
+
benchmark: str = None,
|
120
|
+
time_period: TimePeriod = None,
|
121
|
+
drop_benchmark: bool = False,
|
122
|
+
is_convert_to_str: bool = True,
|
123
|
+
perf_params: PerfParams = PERF_PARAMS,
|
124
|
+
perf_columns: List[PerfStat] = rpt.BENCHMARK_TABLE_COLUMNS,
|
125
|
+
**kwargs
|
126
|
+
) -> pd.DataFrame:
|
127
|
+
if benchmark is None:
|
128
|
+
benchmark = self.benchmark_prices.columns[0]
|
129
|
+
prices = self.get_navs(benchmark=benchmark, time_period=time_period)
|
130
|
+
ra_perf_table = ppt.get_ra_perf_benchmark_columns(prices=prices,
|
131
|
+
benchmark=benchmark,
|
132
|
+
drop_benchmark=drop_benchmark,
|
133
|
+
is_convert_to_str=is_convert_to_str,
|
134
|
+
perf_params=perf_params,
|
135
|
+
perf_columns=perf_columns,
|
136
|
+
**kwargs)
|
137
|
+
return ra_perf_table
|
138
|
+
|
139
|
+
def get_aligned_weights(self,
|
140
|
+
strategy_idx: int = 0,
|
141
|
+
benchmark_idx: int = 1,
|
142
|
+
freq: Optional[str] = 'B',
|
143
|
+
time_period: TimePeriod = None,
|
144
|
+
is_grouped: bool = False,
|
145
|
+
**kwargs
|
146
|
+
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
147
|
+
strategy_weights = self.portfolio_datas[strategy_idx].get_weights(time_period=time_period, freq=None,
|
148
|
+
is_input_weights=True, is_grouped=is_grouped)
|
149
|
+
benchmark_weights = self.portfolio_datas[benchmark_idx].get_weights(time_period=time_period, freq=None,
|
150
|
+
is_input_weights=True,
|
151
|
+
is_grouped=is_grouped)
|
152
|
+
tickers_union = qis.merge_lists_unique(list1=strategy_weights.columns.to_list(),
|
153
|
+
list2=benchmark_weights.columns.to_list())
|
154
|
+
# replace with ac order of benchmark
|
155
|
+
if is_grouped and self.portfolio_datas[benchmark_idx].group_order is not None:
|
156
|
+
tickers_union = self.portfolio_datas[benchmark_idx].group_order
|
157
|
+
strategy_weights = strategy_weights.reindex(columns=tickers_union)
|
158
|
+
benchmark_weights = benchmark_weights.reindex(columns=tickers_union)
|
159
|
+
return strategy_weights, benchmark_weights
|
160
|
+
|
161
|
+
def get_aligned_turnover(self,
|
162
|
+
strategy_idx: int = 0,
|
163
|
+
benchmark_idx: int = 1,
|
164
|
+
turnover_rolling_period: Optional[int] = 260,
|
165
|
+
freq_turnover: Optional[str] = 'B',
|
166
|
+
time_period: TimePeriod = None,
|
167
|
+
is_grouped: bool = False,
|
168
|
+
**kwargs
|
169
|
+
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
170
|
+
|
171
|
+
strategy_turnover = self.portfolio_datas[strategy_idx].get_turnover(time_period=time_period, freq=freq_turnover,
|
172
|
+
roll_period=turnover_rolling_period,
|
173
|
+
add_total=False, is_grouped=is_grouped)
|
174
|
+
benchmark_turnover = self.portfolio_datas[benchmark_idx].get_turnover(time_period=time_period,
|
175
|
+
freq=freq_turnover,
|
176
|
+
roll_period=turnover_rolling_period,
|
177
|
+
add_total=False, is_grouped=is_grouped)
|
178
|
+
tickers_union = qis.merge_lists_unique(list1=strategy_turnover.columns.to_list(),
|
179
|
+
list2=benchmark_turnover.columns.to_list())
|
180
|
+
# replace with ac order of benchmark
|
181
|
+
if is_grouped and self.portfolio_datas[benchmark_idx].group_order is not None:
|
182
|
+
tickers_union = self.portfolio_datas[benchmark_idx].group_order
|
183
|
+
strategy_turnover = strategy_turnover.reindex(columns=tickers_union)
|
184
|
+
benchmark_turnover = benchmark_turnover.reindex(columns=tickers_union)
|
185
|
+
return strategy_turnover, benchmark_turnover
|
186
|
+
|
187
|
+
def compute_tracking_error_implied_by_covar(self,
|
188
|
+
strategy_idx: int = 0,
|
189
|
+
benchmark_idx: int = 1
|
190
|
+
) -> pd.Series:
|
191
|
+
"""
|
192
|
+
compute Ex ante tracking error =
|
193
|
+
(strategy_weights - strategy_weights) @ covar @ (strategy_weights - strategy_weights).T
|
194
|
+
"""
|
195
|
+
if self.pd_covars is None:
|
196
|
+
raise ValueError(f"must pass pd_covars")
|
197
|
+
strategy_weights = self.portfolio_datas[strategy_idx].get_weights(freq=None, is_input_weights=True)
|
198
|
+
benchmark_weights = self.portfolio_datas[benchmark_idx].get_weights(freq=None, is_input_weights=True)
|
199
|
+
covar_index = list(self.pd_covars.keys())
|
200
|
+
investable_assets = self.pd_covars[covar_index[0]].columns.to_list()
|
201
|
+
strategy_weights = strategy_weights.reindex(index=covar_index, columns=investable_assets).ffill().fillna(0.0)
|
202
|
+
benchmark_weights = benchmark_weights.reindex(index=covar_index, columns=investable_assets).ffill().fillna(0.0)
|
203
|
+
|
204
|
+
weight_diffs = benchmark_weights - strategy_weights
|
205
|
+
tracking_error = {}
|
206
|
+
for date, pd_covar in self.pd_covars.items():
|
207
|
+
w = weight_diffs.loc[date]
|
208
|
+
tracking_error[date] = np.sqrt(w @ pd_covar @ w.T)
|
209
|
+
tracking_error = pd.Series(tracking_error)
|
210
|
+
return tracking_error
|
211
|
+
|
212
|
+
def compute_tracking_error_table(self,
|
213
|
+
strategy_idx: int = 0,
|
214
|
+
benchmark_idx: int = 1,
|
215
|
+
freq: Optional[str] = 'B',
|
216
|
+
time_period: TimePeriod = None,
|
217
|
+
af: float = 260,
|
218
|
+
is_norm_costs: bool = True,
|
219
|
+
**kwargs
|
220
|
+
) -> pd.DataFrame:
|
221
|
+
"""
|
222
|
+
compute realised tracking error = mean(P&L diff) / std(P&L diff)
|
223
|
+
"""
|
224
|
+
strategy_pnl = self.portfolio_datas[strategy_idx].get_attribution_table_by_instrument(time_period=time_period,
|
225
|
+
freq=freq)
|
226
|
+
benchmark_pnl = self.portfolio_datas[benchmark_idx].get_attribution_table_by_instrument(time_period=time_period,
|
227
|
+
freq=freq)
|
228
|
+
|
229
|
+
tickers_union = qis.merge_lists_unique(list1=strategy_pnl.columns.to_list(),
|
230
|
+
list2=benchmark_pnl.columns.to_list())
|
231
|
+
strategy_pnl = strategy_pnl.reindex(columns=tickers_union)
|
232
|
+
benchmark_pnl = benchmark_pnl.reindex(columns=tickers_union).reindex(index=strategy_pnl.index)
|
233
|
+
pnl_diff = strategy_pnl.subtract(benchmark_pnl)
|
234
|
+
|
235
|
+
strategy_weights = self.portfolio_datas[strategy_idx].get_weights(time_period=time_period, freq=None,
|
236
|
+
is_input_weights=True)
|
237
|
+
strategy_turnover = self.portfolio_datas[strategy_idx].get_turnover(time_period=time_period, freq=None,
|
238
|
+
roll_period=None, add_total=False)
|
239
|
+
strategy_cost = self.portfolio_datas[strategy_idx].get_costs(time_period=time_period, freq=freq,
|
240
|
+
roll_period=None,
|
241
|
+
add_total=False, is_norm_costs=is_norm_costs)
|
242
|
+
strategy_ticker = self.portfolio_datas[strategy_idx].ticker
|
243
|
+
|
244
|
+
benchmark_weights = self.portfolio_datas[benchmark_idx].get_weights(time_period=time_period, freq=None,
|
245
|
+
is_input_weights=True)
|
246
|
+
benchmark_turnover = self.portfolio_datas[benchmark_idx].get_turnover(time_period=time_period, freq=None,
|
247
|
+
roll_period=None, add_total=False)
|
248
|
+
benchmark_cost = self.portfolio_datas[benchmark_idx].get_costs(time_period=time_period, freq=freq,
|
249
|
+
roll_period=None,
|
250
|
+
add_total=False, is_norm_costs=is_norm_costs)
|
251
|
+
benchmark_ticker = self.portfolio_datas[benchmark_idx].ticker
|
252
|
+
|
253
|
+
# compute stats
|
254
|
+
total_strategy_perf = strategy_pnl.cumsum(0).iloc[-1, :].rename(f"{strategy_ticker} total perf")
|
255
|
+
total_benchmark_perf = benchmark_pnl.cumsum(0).iloc[-1, :].rename(f"{benchmark_ticker} total perf")
|
256
|
+
total_diff = total_strategy_perf.subtract(total_benchmark_perf).rename(
|
257
|
+
f"{strategy_ticker}-{benchmark_ticker} total perf")
|
258
|
+
|
259
|
+
tre = pd.Series(np.nanmean(pnl_diff, axis=0) / np.nanstd(pnl_diff, axis=0), index=tickers_union, name='TRE')
|
260
|
+
|
261
|
+
tre_table = pd.concat([total_diff, tre,
|
262
|
+
total_strategy_perf, total_benchmark_perf,
|
263
|
+
af * strategy_turnover.mean(0).rename(f"{strategy_ticker} an turnover"),
|
264
|
+
af * benchmark_turnover.mean(0).rename(f"{benchmark_ticker} an turnover"),
|
265
|
+
af * strategy_cost.mean(0).rename(f"{strategy_ticker} an cost"),
|
266
|
+
af * benchmark_cost.mean(0).rename(f"{benchmark_ticker} an cost"),
|
267
|
+
], axis=1)
|
268
|
+
|
269
|
+
return tre_table
|
270
|
+
|
271
|
+
# """
|
272
|
+
# plot methods
|
273
|
+
# """
|
115
274
|
def add_regime_shadows(self,
|
116
275
|
ax: plt.Subplot,
|
117
276
|
regime_benchmark: str,
|
@@ -144,7 +303,8 @@ class MultiPortfolioData:
|
|
144
303
|
ax=ax,
|
145
304
|
**kwargs)
|
146
305
|
if regime_benchmark is not None:
|
147
|
-
self.add_regime_shadows(ax=ax, regime_benchmark=regime_benchmark, index=prices.index,
|
306
|
+
self.add_regime_shadows(ax=ax, regime_benchmark=regime_benchmark, index=prices.index,
|
307
|
+
regime_params=regime_params)
|
148
308
|
|
149
309
|
def plot_rolling_perf(self,
|
150
310
|
rolling_perf_stat: RollingPerfStat = RollingPerfStat.SHARPE,
|
@@ -172,6 +332,7 @@ class MultiPortfolioData:
|
|
172
332
|
time_period=time_period,
|
173
333
|
roll_periods=sharpe_rolling_window,
|
174
334
|
legend_stats=legend_stats,
|
335
|
+
title=sharpe_title,
|
175
336
|
trend_line=None, # qis.TrendLine.ZERO_SHADOWS,
|
176
337
|
ax=ax,
|
177
338
|
**kwargs)
|
@@ -180,7 +341,7 @@ class MultiPortfolioData:
|
|
180
341
|
self.add_regime_shadows(ax=ax, regime_benchmark=regime_benchmark, index=prices.index,
|
181
342
|
regime_params=regime_params)
|
182
343
|
return fig
|
183
|
-
|
344
|
+
|
184
345
|
def plot_periodic_returns(self,
|
185
346
|
time_period: TimePeriod = None,
|
186
347
|
heatmap_freq: str = 'YE',
|
@@ -243,7 +404,8 @@ class MultiPortfolioData:
|
|
243
404
|
prices = self.get_navs(time_period=time_period, add_benchmarks_to_navs=add_benchmarks_to_navs)
|
244
405
|
cdr.plot_rolling_drawdowns(prices=prices, dd_legend_type=dd_legend_type, ax=ax, **kwargs)
|
245
406
|
if regime_benchmark is not None:
|
246
|
-
self.add_regime_shadows(ax=ax, regime_benchmark=regime_benchmark, index=prices.index,
|
407
|
+
self.add_regime_shadows(ax=ax, regime_benchmark=regime_benchmark, index=prices.index,
|
408
|
+
regime_params=regime_params)
|
247
409
|
|
248
410
|
def plot_rolling_time_under_water(self,
|
249
411
|
time_period: TimePeriod = None,
|
@@ -256,28 +418,8 @@ class MultiPortfolioData:
|
|
256
418
|
prices = self.get_navs(time_period=time_period, add_benchmarks_to_navs=add_benchmarks_to_navs)
|
257
419
|
cdr.plot_rolling_time_under_water(prices=prices, dd_legend_type=dd_legend_type, ax=ax, **kwargs)
|
258
420
|
if regime_benchmark is not None:
|
259
|
-
self.add_regime_shadows(ax=ax, regime_benchmark=regime_benchmark, index=prices.index,
|
260
|
-
|
261
|
-
def get_ra_perf_table(self,
|
262
|
-
benchmark: str = None,
|
263
|
-
time_period: TimePeriod = None,
|
264
|
-
drop_benchmark: bool = False,
|
265
|
-
is_convert_to_str: bool = True,
|
266
|
-
perf_params: PerfParams = PERF_PARAMS,
|
267
|
-
perf_columns: List[PerfStat] = rpt.BENCHMARK_TABLE_COLUMNS,
|
268
|
-
**kwargs
|
269
|
-
) -> pd.DataFrame:
|
270
|
-
if benchmark is None:
|
271
|
-
benchmark = self.benchmark_prices.columns[0]
|
272
|
-
prices = self.get_navs(benchmark=benchmark, time_period=time_period)
|
273
|
-
ra_perf_table = ppt.get_ra_perf_benchmark_columns(prices=prices,
|
274
|
-
benchmark=benchmark,
|
275
|
-
drop_benchmark=drop_benchmark,
|
276
|
-
is_convert_to_str=is_convert_to_str,
|
277
|
-
perf_params=perf_params,
|
278
|
-
perf_columns=perf_columns,
|
279
|
-
**kwargs)
|
280
|
-
return ra_perf_table
|
421
|
+
self.add_regime_shadows(ax=ax, regime_benchmark=regime_benchmark, index=prices.index,
|
422
|
+
regime_params=regime_params)
|
281
423
|
|
282
424
|
def plot_ra_perf_table(self,
|
283
425
|
benchmark: str = None,
|
@@ -291,7 +433,8 @@ class MultiPortfolioData:
|
|
291
433
|
if benchmark is None:
|
292
434
|
benchmark = self.benchmark_prices.columns[0]
|
293
435
|
prices = self.get_navs(benchmark=benchmark, time_period=time_period)
|
294
|
-
ra_perf_title = f"RA performance table for {perf_params.freq_vol}-freq returns with beta to {benchmark}:
|
436
|
+
ra_perf_title = f"RA performance table for {perf_params.freq_vol}-freq returns with beta to {benchmark}: " \
|
437
|
+
f"{qis.get_time_period(prices).to_str()}"
|
295
438
|
ppt.plot_ra_perf_table_benchmark(prices=prices,
|
296
439
|
benchmark=benchmark,
|
297
440
|
perf_params=perf_params,
|
@@ -341,11 +484,12 @@ class MultiPortfolioData:
|
|
341
484
|
benchmark_price = benchmark_price.reindex(index=strategy_prices.index, method='ffill')
|
342
485
|
if add_ac: # otherwise tables look too bad
|
343
486
|
ac_prices = pd.concat(ac_prices, axis=1)
|
344
|
-
prices = pd.concat([benchmark_price, strategy_prices, ac_prices],axis=1)
|
487
|
+
prices = pd.concat([benchmark_price, strategy_prices, ac_prices], axis=1)
|
345
488
|
else:
|
346
489
|
prices = pd.concat([strategy_prices, benchmark_price], axis=1)
|
347
490
|
|
348
|
-
ra_perf_title = f"RA performance table for {perf_params.freq_vol}-freq returns with beta to
|
491
|
+
ra_perf_title = f"RA performance table for {perf_params.freq_vol}-freq returns with beta to " \
|
492
|
+
f"{benchmark_price.name}: {qis.get_time_period(prices).to_str()}"
|
349
493
|
ppt.plot_ra_perf_table_benchmark(prices=prices,
|
350
494
|
benchmark=str(benchmark_price.name),
|
351
495
|
perf_params=perf_params,
|
@@ -476,7 +620,8 @@ class MultiPortfolioData:
|
|
476
620
|
**kwargs) -> None:
|
477
621
|
costs = []
|
478
622
|
for portfolio in self.portfolio_datas:
|
479
|
-
costs.append(portfolio.get_costs(roll_period=cost_rolling_period, freq=freq_cost, is_agg=True,
|
623
|
+
costs.append(portfolio.get_costs(roll_period=cost_rolling_period, freq=freq_cost, is_agg=True,
|
624
|
+
is_norm_costs=is_norm_costs).rename(portfolio.nav.name))
|
480
625
|
costs = pd.concat(costs, axis=1)
|
481
626
|
if time_period is not None:
|
482
627
|
costs = time_period.locate(costs)
|
@@ -814,90 +959,3 @@ class MultiPortfolioData:
|
|
814
959
|
regime_params=regime_params)
|
815
960
|
|
816
961
|
return figs
|
817
|
-
|
818
|
-
def compute_tracking_error_table(self,
|
819
|
-
strategy_idx: int = 0,
|
820
|
-
benchmark_idx: int = 1,
|
821
|
-
freq: Optional[str] = 'B',
|
822
|
-
time_period: TimePeriod = None,
|
823
|
-
af: float = 260,
|
824
|
-
is_norm_costs: bool = True,
|
825
|
-
**kwargs
|
826
|
-
) -> pd.DataFrame:
|
827
|
-
|
828
|
-
strategy_pnl = self.portfolio_datas[strategy_idx].get_attribution_table_by_instrument(time_period=time_period, freq=freq)
|
829
|
-
benchmark_pnl = self.portfolio_datas[benchmark_idx].get_attribution_table_by_instrument(time_period=time_period, freq=freq)
|
830
|
-
|
831
|
-
tickers_union = qis.merge_lists_unique(list1=strategy_pnl.columns.to_list(), list2=benchmark_pnl.columns.to_list())
|
832
|
-
strategy_pnl = strategy_pnl.reindex(columns=tickers_union)
|
833
|
-
benchmark_pnl = benchmark_pnl.reindex(columns=tickers_union).reindex(index=strategy_pnl.index)
|
834
|
-
pnl_diff = strategy_pnl.subtract(benchmark_pnl)
|
835
|
-
|
836
|
-
strategy_weights = self.portfolio_datas[strategy_idx].get_weights(time_period=time_period, freq=None, is_input_weights=True)
|
837
|
-
strategy_turnover = self.portfolio_datas[strategy_idx].get_turnover(time_period=time_period, freq=None, roll_period=None, add_total=False)
|
838
|
-
strategy_cost = self.portfolio_datas[strategy_idx].get_costs(time_period=time_period, freq=freq, roll_period=None,
|
839
|
-
add_total=False, is_norm_costs=is_norm_costs)
|
840
|
-
strategy_ticker = self.portfolio_datas[strategy_idx].ticker
|
841
|
-
|
842
|
-
benchmark_weights = self.portfolio_datas[benchmark_idx].get_weights(time_period=time_period, freq=None, is_input_weights=True)
|
843
|
-
benchmark_turnover = self.portfolio_datas[benchmark_idx].get_turnover(time_period=time_period, freq=None, roll_period=None, add_total=False)
|
844
|
-
benchmark_cost = self.portfolio_datas[benchmark_idx].get_costs(time_period=time_period, freq=freq, roll_period=None,
|
845
|
-
add_total=False, is_norm_costs=is_norm_costs)
|
846
|
-
benchmark_ticker = self.portfolio_datas[benchmark_idx].ticker
|
847
|
-
|
848
|
-
# compute stats
|
849
|
-
total_strategy_perf = strategy_pnl.cumsum(0).iloc[-1, :].rename(f"{strategy_ticker} total perf")
|
850
|
-
total_benchmark_perf = benchmark_pnl.cumsum(0).iloc[-1, :].rename(f"{benchmark_ticker} total perf")
|
851
|
-
total_diff = total_strategy_perf.subtract(total_benchmark_perf).rename(f"{strategy_ticker}-{benchmark_ticker} total perf")
|
852
|
-
|
853
|
-
tre = pd.Series(np.nanmean(pnl_diff, axis=0) / np.nanstd(pnl_diff, axis=0), index=tickers_union, name='TRE')
|
854
|
-
|
855
|
-
tre_table = pd.concat([total_diff, tre,
|
856
|
-
total_strategy_perf, total_benchmark_perf,
|
857
|
-
af*strategy_turnover.mean(0).rename(f"{strategy_ticker} an turnover"),
|
858
|
-
af*benchmark_turnover.mean(0).rename(f"{benchmark_ticker} an turnover"),
|
859
|
-
af*strategy_cost.mean(0).rename(f"{strategy_ticker} an cost"),
|
860
|
-
af*benchmark_cost.mean(0).rename(f"{benchmark_ticker} an cost"),
|
861
|
-
], axis=1)
|
862
|
-
|
863
|
-
return tre_table
|
864
|
-
|
865
|
-
def get_aligned_weights(self,
|
866
|
-
strategy_idx: int = 0,
|
867
|
-
benchmark_idx: int = 1,
|
868
|
-
freq: Optional[str] = 'B',
|
869
|
-
time_period: TimePeriod = None,
|
870
|
-
is_grouped: bool = False,
|
871
|
-
**kwargs
|
872
|
-
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
873
|
-
strategy_weights = self.portfolio_datas[strategy_idx].get_weights(time_period=time_period, freq=None, is_input_weights=True, is_grouped=is_grouped)
|
874
|
-
benchmark_weights = self.portfolio_datas[benchmark_idx].get_weights(time_period=time_period, freq=None, is_input_weights=True, is_grouped=is_grouped)
|
875
|
-
tickers_union = qis.merge_lists_unique(list1=strategy_weights.columns.to_list(), list2=benchmark_weights.columns.to_list())
|
876
|
-
if is_grouped and self.portfolio_datas[benchmark_idx].group_order is not None: # replace with ac order of benchmark
|
877
|
-
tickers_union = self.portfolio_datas[benchmark_idx].group_order
|
878
|
-
strategy_weights = strategy_weights.reindex(columns=tickers_union)
|
879
|
-
benchmark_weights = benchmark_weights.reindex(columns=tickers_union)
|
880
|
-
return strategy_weights, benchmark_weights
|
881
|
-
|
882
|
-
def get_aligned_turnover(self,
|
883
|
-
strategy_idx: int = 0,
|
884
|
-
benchmark_idx: int = 1,
|
885
|
-
turnover_rolling_period: Optional[int] = 260,
|
886
|
-
freq_turnover: Optional[str] = 'B',
|
887
|
-
time_period: TimePeriod = None,
|
888
|
-
is_grouped: bool = False,
|
889
|
-
**kwargs
|
890
|
-
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
891
|
-
|
892
|
-
strategy_turnover = self.portfolio_datas[strategy_idx].get_turnover(time_period=time_period, freq=freq_turnover,
|
893
|
-
roll_period=turnover_rolling_period,
|
894
|
-
add_total=False, is_grouped=is_grouped)
|
895
|
-
benchmark_turnover = self.portfolio_datas[benchmark_idx].get_turnover(time_period=time_period, freq=freq_turnover,
|
896
|
-
roll_period=turnover_rolling_period,
|
897
|
-
add_total=False, is_grouped=is_grouped)
|
898
|
-
tickers_union = qis.merge_lists_unique(list1=strategy_turnover.columns.to_list(), list2=benchmark_turnover.columns.to_list())
|
899
|
-
if is_grouped and self.portfolio_datas[benchmark_idx].group_order is not None: # replace with ac order of benchmark
|
900
|
-
tickers_union = self.portfolio_datas[benchmark_idx].group_order
|
901
|
-
strategy_turnover = strategy_turnover.reindex(columns=tickers_union)
|
902
|
-
benchmark_turnover = benchmark_turnover.reindex(columns=tickers_union)
|
903
|
-
return strategy_turnover, benchmark_turnover
|
@@ -1216,7 +1216,11 @@ class PortfolioData:
|
|
1216
1216
|
) -> None:
|
1217
1217
|
weights = self.get_weights(is_input_weights=True, freq=None, is_grouped=is_grouped)
|
1218
1218
|
if time_period is not None:
|
1219
|
-
weights_1 = time_period.locate(weights)
|
1219
|
+
weights_1 = time_period.locate(weights)
|
1220
|
+
if len(weights_1.index) > 0:
|
1221
|
+
weights_1 = weights_1.iloc[0, :]
|
1222
|
+
else:
|
1223
|
+
weights_1 = weights.iloc[-1, :]
|
1220
1224
|
else:
|
1221
1225
|
weights_1 = weights.iloc[-1, :]
|
1222
1226
|
if add_top_bar_values is None:
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|