qis 3.0.2__tar.gz → 3.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {qis-3.0.2 → qis-3.0.4}/PKG-INFO +1 -1
- {qis-3.0.2 → qis-3.0.4}/pyproject.toml +1 -1
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/ewm_portfolio_risk.py +10 -2
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/multi_portfolio_data.py +8 -6
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/portfolio_data.py +17 -12
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/reports/config.py +8 -4
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/reports/strategy_factsheet.py +5 -4
- {qis-3.0.2 → qis-3.0.4}/qis/utils/np_ops.py +2 -3
- {qis-3.0.2 → qis-3.0.4}/LICENSE.txt +0 -0
- {qis-3.0.2 → qis-3.0.4}/README.md +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/__init__.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/best_returns.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/bond_futures_portfolio.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/bootstrap_analysis.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/boxplot_conditional_returns.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/btc_asset_corr.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/constant_notional.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/constant_weight_portfolios.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/core/perf_bbg_prices.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/core/price_plots.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/core/us_election.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/credit_spreads.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/credit_trackers.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/europe_futures.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/factsheets/multi_assets.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/factsheets/multi_strategy.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/factsheets/pyblogs_reports.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/factsheets/strategy.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/factsheets/strategy_benchmark.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/generate_option_rolls.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/interpolation_infrequent_returns.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/leveraged_strategies.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/long_short.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/momentum_indices.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/oakmark_analysis.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/ohlc_vol_analysis.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/overnight_returns.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/perf_external_assets.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/perp_pricing.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/readme_performances.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/risk_return_frontier.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/rolling_performance.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/seasonality.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/sharpe_vs_sortino.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/simulate_quant_strats.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/test_ewm.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/test_scatter.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/try_pybloqs.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/universe_corrs.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/vix_beta_to_equities_bonds.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/vix_conditional_returns.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/vix_spy_by_year.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/vix_tenor_analysis.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/examples/vol_without_weekends.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/file_utils.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/local_path.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/README.md +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/__init__.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/linear/__init__.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/linear/auto_corr.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/linear/corr_cov_matrix.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/linear/ewm.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/linear/ewm_convolution.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/linear/ewm_factors.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/linear/ewm_winsor_outliers.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/linear/pca.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/linear/plot_correlations.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/linear/ra_returns.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/stats/__init__.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/stats/bootstrap.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/stats/ohlc_vol.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/stats/rolling_stats.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/models/stats/test_bootstrap.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/perfstats/README.md +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/perfstats/__init__.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/perfstats/cond_regression.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/perfstats/config.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/perfstats/desc_table.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/perfstats/fx_ops.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/perfstats/perf_stats.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/perfstats/regime_classifier.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/perfstats/returns.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/perfstats/timeseries_bfill.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/README.md +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/__init__.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/bars.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/boxplot.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/contour.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/derived/__init__.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/derived/data_timeseries.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/derived/desc_table.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/derived/drawdowns.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/derived/perf_table.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/derived/prices.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/derived/regime_class_table.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/derived/regime_data.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/derived/regime_pdf.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/derived/regime_scatter.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/derived/returns_heatmap.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/derived/returns_scatter.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/errorbar.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/heatmap.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/histogram.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/histplot2d.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/lineplot.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/pie.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/qqplot.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/reports/__init__.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/reports/econ_data_single.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/reports/gantt_data_history.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/reports/price_history.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/reports/utils.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/scatter.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/stackplot.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/table.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/time_series.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/plots/utils.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/README.md +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/__init__.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/backtester.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/reports/__init__.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/reports/brinson_attribution.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/reports/multi_assets_factsheet.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/reports/multi_strategy_factseet_pybloqs.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/reports/multi_strategy_factsheet.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/reports/strategy_benchmark_factsheet.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/reports/strategy_benchmark_factsheet_pybloqs.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/reports/strategy_signal_factsheet.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/strats/__init__.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/strats/quant_strats_delta1.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/portfolio/strats/seasonal_strats.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/settings.yaml +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/sql_engine.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/test_data.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/README.md +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/__init__.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/dates.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/df_agg.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/df_cut.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/df_freq.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/df_groups.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/df_melt.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/df_ops.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/df_str.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/df_to_scores.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/df_to_weights.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/generic.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/ols.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/sampling.py +0 -0
- {qis-3.0.2 → qis-3.0.4}/qis/utils/struct_ops.py +0 -0
{qis-3.0.2 → qis-3.0.4}/PKG-INFO
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: qis
|
3
|
-
Version: 3.0.
|
3
|
+
Version: 3.0.4
|
4
4
|
Summary: Implementation of visualisation and reporting analytics for Quantitative Investment Strategies
|
5
5
|
License: LICENSE.txt
|
6
6
|
Keywords: quantitative,investing,portfolio optimization,systematic strategies,volatility
|
@@ -75,9 +75,11 @@ def compute_portfolio_vol(returns: pd.DataFrame,
|
|
75
75
|
init_type=init_type,
|
76
76
|
nan_backfill=nan_backfill)
|
77
77
|
|
78
|
+
if span is not None:
|
79
|
+
ewm_lambda = 1.0 - 2.0 / (span + 1.0)
|
80
|
+
|
78
81
|
portfolio_vol = compute_portfolio_var_np(returns=returns_np,
|
79
82
|
weights=weights_np,
|
80
|
-
span=span,
|
81
83
|
ewm_lambda=ewm_lambda)
|
82
84
|
|
83
85
|
if annualize:
|
@@ -215,7 +217,13 @@ def compute_portfolio_independent_var_by_ac(prices: pd.DataFrame,
|
|
215
217
|
def compute_portfolio_risk_contributions(w: Union[np.ndarray, pd.Series],
|
216
218
|
covar: Union[np.ndarray, pd.DataFrame]
|
217
219
|
) -> Union[np.ndarray, pd.Series]:
|
220
|
+
if isinstance(covar, pd.DataFrame) and isinstance(w, pd.Series): # make sure weights are alined
|
221
|
+
w = w.reindex(index=covar.index).fillna(0.0)
|
222
|
+
elif isinstance(covar, np.ndarray) and isinstance(w, np.ndarray):
|
223
|
+
assert covar.shape[0] == covar.shape[1] == w.shape[0]
|
224
|
+
else:
|
225
|
+
raise ValueError(f"unnsuported types {type(w)} and {type(covar)}")
|
218
226
|
portfolio_vol = np.sqrt(w.T @ covar @ w)
|
219
227
|
marginal_risk_contribution = covar @ w.T
|
220
228
|
rc = np.multiply(marginal_risk_contribution, w) / portfolio_vol
|
221
|
-
return rc
|
229
|
+
return rc
|
@@ -216,7 +216,7 @@ class MultiPortfolioData:
|
|
216
216
|
freq: Optional[str] = 'B',
|
217
217
|
time_period: TimePeriod = None,
|
218
218
|
af: float = 260,
|
219
|
-
|
219
|
+
is_unit_based_traded_volume: bool = True,
|
220
220
|
**kwargs
|
221
221
|
) -> pd.DataFrame:
|
222
222
|
"""
|
@@ -238,7 +238,7 @@ class MultiPortfolioData:
|
|
238
238
|
roll_period=None, add_total=False)
|
239
239
|
strategy_cost = self.portfolio_datas[strategy_idx].get_costs(time_period=time_period, freq=freq,
|
240
240
|
roll_period=None,
|
241
|
-
add_total=False,
|
241
|
+
add_total=False, is_unit_based_traded_volume=is_unit_based_traded_volume)
|
242
242
|
strategy_ticker = self.portfolio_datas[strategy_idx].ticker
|
243
243
|
|
244
244
|
# benchmark_weights = self.portfolio_datas[benchmark_idx].get_weights(time_period=time_period, freq=None, is_input_weights=True)
|
@@ -246,7 +246,7 @@ class MultiPortfolioData:
|
|
246
246
|
roll_period=None, add_total=False)
|
247
247
|
benchmark_cost = self.portfolio_datas[benchmark_idx].get_costs(time_period=time_period, freq=freq,
|
248
248
|
roll_period=None,
|
249
|
-
add_total=False,
|
249
|
+
add_total=False, is_unit_based_traded_volume=is_unit_based_traded_volume)
|
250
250
|
benchmark_ticker = self.portfolio_datas[benchmark_idx].ticker
|
251
251
|
|
252
252
|
# compute stats
|
@@ -606,12 +606,14 @@ class MultiPortfolioData:
|
|
606
606
|
turnover_rolling_period: Optional[int] = 260,
|
607
607
|
freq_turnover: Optional[str] = 'B',
|
608
608
|
var_format: str = '{:.0%}',
|
609
|
+
is_unit_based_traded_volume: bool = True,
|
609
610
|
ax: plt.Subplot = None,
|
610
611
|
**kwargs) -> None:
|
611
612
|
|
612
613
|
turnover = []
|
613
614
|
for portfolio in self.portfolio_datas:
|
614
|
-
turnover.append(portfolio.get_turnover(roll_period=turnover_rolling_period, freq=freq_turnover, is_agg=True
|
615
|
+
turnover.append(portfolio.get_turnover(roll_period=turnover_rolling_period, freq=freq_turnover, is_agg=True,
|
616
|
+
is_unit_based_traded_volume=is_unit_based_traded_volume).rename(portfolio.nav.name))
|
615
617
|
turnover = pd.concat(turnover, axis=1)
|
616
618
|
if time_period is not None:
|
617
619
|
turnover = time_period.locate(turnover)
|
@@ -635,13 +637,13 @@ class MultiPortfolioData:
|
|
635
637
|
time_period: TimePeriod = None,
|
636
638
|
regime_params: BenchmarkReturnsQuantileRegimeSpecs = REGIME_PARAMS,
|
637
639
|
var_format: str = '{:.2%}',
|
638
|
-
|
640
|
+
is_unit_based_traded_volume: bool = True,
|
639
641
|
ax: plt.Subplot = None,
|
640
642
|
**kwargs) -> None:
|
641
643
|
costs = []
|
642
644
|
for portfolio in self.portfolio_datas:
|
643
645
|
costs.append(portfolio.get_costs(roll_period=cost_rolling_period, freq=freq_cost, is_agg=True,
|
644
|
-
|
646
|
+
is_unit_based_traded_volume=is_unit_based_traded_volume).rename(portfolio.nav.name))
|
645
647
|
costs = pd.concat(costs, axis=1)
|
646
648
|
if time_period is not None:
|
647
649
|
costs = time_period.locate(costs)
|
@@ -167,13 +167,13 @@ class PortfolioData:
|
|
167
167
|
add_total: bool = False,
|
168
168
|
time_period: TimePeriod = None,
|
169
169
|
is_net: bool = False,
|
170
|
-
|
170
|
+
is_unit_based_traded_volume: bool = True,
|
171
171
|
is_compounded: bool = False,
|
172
172
|
freq: Optional[str] = None
|
173
173
|
) -> pd.DataFrame:
|
174
174
|
pnl = self.instrument_pnl.copy()
|
175
175
|
if is_net:
|
176
|
-
costs = self.get_costs(add_total=False,
|
176
|
+
costs = self.get_costs(add_total=False, is_unit_based_traded_volume=is_unit_based_traded_volume)
|
177
177
|
pnl = pnl.subtract(costs)
|
178
178
|
if add_total:
|
179
179
|
pnl.insert(loc=0, value=pnl.sum(1), column='Total')
|
@@ -335,12 +335,17 @@ class PortfolioData:
|
|
335
335
|
is_vol_adjusted: bool = False,
|
336
336
|
add_total: bool = True,
|
337
337
|
vol_span: int = 33,
|
338
|
-
freq: Optional[str] = None
|
338
|
+
freq: Optional[str] = None,
|
339
|
+
is_unit_based_traded_volume: bool = True
|
339
340
|
) -> Union[pd.DataFrame, pd.Series]:
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
341
|
+
|
342
|
+
if is_unit_based_traded_volume: # for unit generated backtest
|
343
|
+
turnover = (self.units.diff(1).abs()).multiply(self.prices)
|
344
|
+
abs_exposure = self.units.multiply(self.prices).abs().sum(axis=1)
|
345
|
+
# turnover = turnover.divide(self.nav.to_numpy(), axis=0)
|
346
|
+
turnover = turnover.divide(abs_exposure.to_numpy(), axis=0)
|
347
|
+
else: # for weight generated backtets
|
348
|
+
turnover = self.input_weights.diff(1).abs()
|
344
349
|
|
345
350
|
if is_vol_adjusted:
|
346
351
|
instrument_vols = compute_ewm_vol(data=qis.to_returns(self.prices, is_log_returns=True),
|
@@ -374,13 +379,13 @@ class PortfolioData:
|
|
374
379
|
is_grouped: bool = False,
|
375
380
|
time_period: TimePeriod = None,
|
376
381
|
add_total: bool = True,
|
377
|
-
|
382
|
+
is_unit_based_traded_volume: bool = True,
|
378
383
|
roll_period: Optional[int] = 260,
|
379
384
|
freq: Optional[str] = None
|
380
385
|
) -> Union[pd.DataFrame, pd.Series]:
|
381
386
|
|
382
387
|
costs = self.realized_costs
|
383
|
-
if
|
388
|
+
if is_unit_based_traded_volume:
|
384
389
|
costs = costs.divide(self.nav.to_numpy(), axis=0)
|
385
390
|
if is_agg:
|
386
391
|
costs = pd.Series(np.nansum(costs, axis=1), index=self.nav.index, name=self.nav.name)
|
@@ -533,7 +538,7 @@ class PortfolioData:
|
|
533
538
|
def get_performance_attribution_data(self,
|
534
539
|
attribution_metric: AttributionMetric = AttributionMetric.PNL,
|
535
540
|
time_period: TimePeriod = None,
|
536
|
-
|
541
|
+
is_unit_based_traded_volume: bool = True,
|
537
542
|
**kwargs
|
538
543
|
) -> Union[pd.DataFrame, pd.Series]:
|
539
544
|
if attribution_metric == AttributionMetric.PNL:
|
@@ -543,11 +548,11 @@ class PortfolioData:
|
|
543
548
|
elif attribution_metric == AttributionMetric.INST_PNL:
|
544
549
|
data = self.get_instruments_navs(time_period=time_period)
|
545
550
|
elif attribution_metric == AttributionMetric.COSTS:
|
546
|
-
data = self.get_costs(is_agg=False, is_grouped=False, roll_period=None,
|
551
|
+
data = self.get_costs(is_agg=False, is_grouped=False, roll_period=None, is_unit_based_traded_volume=is_unit_based_traded_volume,
|
547
552
|
add_total=False,
|
548
553
|
time_period=time_period)
|
549
554
|
data = data.sum(0)
|
550
|
-
#print(f"total costs,
|
555
|
+
#print(f"total costs, is_unit_based_traded_volume={is_unit_based_traded_volume} = {np.nansum(data)}")
|
551
556
|
elif attribution_metric == AttributionMetric.TURNOVER:
|
552
557
|
data = self.get_turnover(is_agg=False, is_grouped=False, roll_period=None,
|
553
558
|
add_total=False,
|
@@ -3,6 +3,8 @@ configuration for performance reports
|
|
3
3
|
"""
|
4
4
|
from enum import Enum
|
5
5
|
from typing import Dict, Any, Tuple, NamedTuple, Optional, List
|
6
|
+
|
7
|
+
import qis
|
6
8
|
from qis import PerfParams, BenchmarkReturnsQuantileRegimeSpecs, TimePeriod, PerfStat
|
7
9
|
import yfinance as yf
|
8
10
|
|
@@ -61,7 +63,7 @@ class FactsheetConfig(NamedTuple):
|
|
61
63
|
freq_turnover: str = 'B' # daily freq
|
62
64
|
cost_rolling_period: int = 260 # turnover = turnover.rolling(turnover_roll_period).sum()
|
63
65
|
freq_cost: Optional[str] = 'B' # for rolling costs
|
64
|
-
|
66
|
+
is_unit_based_traded_volume: bool = True # for long-only protfolio use nrmalised costs
|
65
67
|
factor_beta_span: int = 52 # to compute rolling beta
|
66
68
|
freq_beta: str = 'W-WED' # for scatter plot
|
67
69
|
exposures_freq: str = 'W-WED' # for plotting strategy exposures
|
@@ -101,7 +103,7 @@ FACTSHEET_CONFIG_MONTHLY_DATA_LONG_PERIOD = FactsheetConfig(freq='ME',
|
|
101
103
|
freq_cost='ME',
|
102
104
|
freq_var='ME',
|
103
105
|
var_span=12,
|
104
|
-
|
106
|
+
is_unit_based_traded_volume=True,
|
105
107
|
factor_beta_span=36,
|
106
108
|
freq_beta='ME',
|
107
109
|
exposures_freq='ME')
|
@@ -124,7 +126,7 @@ FACTSHEET_CONFIG_QUARTERLY_DATA_LONG_PERIOD = FactsheetConfig(freq='QE',
|
|
124
126
|
freq_cost='QE',
|
125
127
|
freq_var='QE',
|
126
128
|
var_span=12,
|
127
|
-
|
129
|
+
is_unit_based_traded_volume=True,
|
128
130
|
factor_beta_span=12,
|
129
131
|
freq_beta='QE',
|
130
132
|
exposures_freq='QE')
|
@@ -178,7 +180,8 @@ def fetch_default_perf_params() -> Tuple[PerfParams, BenchmarkReturnsQuantileReg
|
|
178
180
|
def fetch_default_report_kwargs(time_period: Optional[TimePeriod] = None,
|
179
181
|
reporting_frequency: ReportingFrequency = ReportingFrequency.DAILY,
|
180
182
|
long_threshold_years: float = 5.0,
|
181
|
-
add_rates_data: bool = True
|
183
|
+
add_rates_data: bool = True,
|
184
|
+
is_unit_based_traded_volume: bool = True
|
182
185
|
) -> Dict[str, Any]:
|
183
186
|
|
184
187
|
# use for number years > 5
|
@@ -207,6 +210,7 @@ def fetch_default_report_kwargs(time_period: Optional[TimePeriod] = None,
|
|
207
210
|
|
208
211
|
kwargs = fetch_factsheet_config_kwargs(factsheet_config=factsheet_config,
|
209
212
|
add_rates_data=add_rates_data)
|
213
|
+
kwargs = qis.update_kwargs(kwargs, dict(is_unit_based_traded_volume=is_unit_based_traded_volume))
|
210
214
|
return kwargs
|
211
215
|
|
212
216
|
|
@@ -51,7 +51,7 @@ def generate_strategy_factsheet(portfolio_data: PortfolioData,
|
|
51
51
|
is_1y_exposures: bool = False,
|
52
52
|
is_grouped: Optional[bool] = None,
|
53
53
|
dd_legend_type: qis.DdLegendType = qis.DdLegendType.SIMPLE,
|
54
|
-
|
54
|
+
is_unit_based_traded_volume: bool = True,
|
55
55
|
df_to_add: pd.DataFrame = None,
|
56
56
|
factsheet_name: str = None,
|
57
57
|
**kwargs
|
@@ -157,7 +157,8 @@ def generate_strategy_factsheet(portfolio_data: PortfolioData,
|
|
157
157
|
ax = fig.add_subplot(gs[10:12, :2])
|
158
158
|
turnover = portfolio_data.get_turnover(time_period=time_period, roll_period=turnover_rolling_period,
|
159
159
|
freq=freq_turnover,
|
160
|
-
is_grouped=is_grouped
|
160
|
+
is_grouped=is_grouped,
|
161
|
+
is_unit_based_traded_volume=is_unit_based_traded_volume)
|
161
162
|
freq = pd.infer_freq(turnover.index)
|
162
163
|
turnover_title = f"{turnover_rolling_period}-period rolling {freq}-freq Turnover"
|
163
164
|
qis.plot_time_series(df=turnover,
|
@@ -175,7 +176,7 @@ def generate_strategy_factsheet(portfolio_data: PortfolioData,
|
|
175
176
|
costs = portfolio_data.get_costs(time_period=time_period, roll_period=cost_rolling_period,
|
176
177
|
freq=freq_cost,
|
177
178
|
is_grouped=is_grouped,
|
178
|
-
|
179
|
+
is_unit_based_traded_volume=is_unit_based_traded_volume)
|
179
180
|
freq = pd.infer_freq(costs.index)
|
180
181
|
costs_title = f"{cost_rolling_period}-period rolling {freq}-freq Costs"
|
181
182
|
qis.plot_time_series(df=costs,
|
@@ -510,7 +511,7 @@ def generate_strategy_factsheet(portfolio_data: PortfolioData,
|
|
510
511
|
|
511
512
|
# costs
|
512
513
|
with sns.axes_style("whitegrid"):
|
513
|
-
local_kwargs = qis.update_kwargs(kwargs=kwargs, new_kwargs=dict(legend_loc=None,
|
514
|
+
local_kwargs = qis.update_kwargs(kwargs=kwargs, new_kwargs=dict(legend_loc=None, is_unit_based_traded_volume=is_unit_based_traded_volume))
|
514
515
|
portfolio_data.plot_performance_attribution(time_period=time_period,
|
515
516
|
attribution_metric=qis.AttributionMetric.COSTS,
|
516
517
|
ax=fig.add_subplot(gs[6, :2]),
|
@@ -249,14 +249,13 @@ def to_finite_ratio(x: Union[pd.Series, pd.DataFrame, np.ndarray],
|
|
249
249
|
return x_y
|
250
250
|
|
251
251
|
|
252
|
-
|
253
|
-
def covar_to_corr(covar: np.ndarray) -> np.ndarray:
|
252
|
+
def covar_to_corr(covar: Union[np.ndarray, pd.DataFrame]) -> Union[np.ndarray, pd.DataFrame]:
|
254
253
|
"""
|
255
254
|
compute correlation out of covariance
|
256
255
|
"""
|
257
256
|
inv_vol = np.reciprocal(np.sqrt(np.diag(covar)))
|
258
257
|
norm = np.outer(inv_vol, inv_vol)
|
259
|
-
covar
|
258
|
+
covar = covar.multiply(norm)
|
260
259
|
return covar
|
261
260
|
|
262
261
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|