qis 3.0.1__tar.gz → 3.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.
- {qis-3.0.1 → qis-3.0.3}/PKG-INFO +3 -3
- {qis-3.0.1 → qis-3.0.3}/README.md +2 -2
- {qis-3.0.1 → qis-3.0.3}/pyproject.toml +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/best_returns.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/bootstrap_analysis.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/boxplot_conditional_returns.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/btc_asset_corr.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/constant_notional.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/constant_weight_portfolios.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/core/price_plots.py +2 -2
- {qis-3.0.1 → qis-3.0.3}/qis/examples/factsheets/multi_assets.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/factsheets/multi_strategy.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/factsheets/pyblogs_reports.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/factsheets/strategy.py +2 -2
- {qis-3.0.1 → qis-3.0.3}/qis/examples/factsheets/strategy_benchmark.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/interpolation_infrequent_returns.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/leveraged_strategies.py +2 -2
- {qis-3.0.1 → qis-3.0.3}/qis/examples/long_short.py +2 -2
- {qis-3.0.1 → qis-3.0.3}/qis/examples/overnight_returns.py +2 -2
- {qis-3.0.1 → qis-3.0.3}/qis/examples/perf_external_assets.py +2 -2
- {qis-3.0.1 → qis-3.0.3}/qis/examples/readme_performances.py +2 -2
- {qis-3.0.1 → qis-3.0.3}/qis/examples/risk_return_frontier.py +2 -2
- {qis-3.0.1 → qis-3.0.3}/qis/examples/seasonality.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/sharpe_vs_sortino.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/simulate_quant_strats.py +2 -2
- {qis-3.0.1 → qis-3.0.3}/qis/examples/try_pybloqs.py +2 -2
- {qis-3.0.1 → qis-3.0.3}/qis/examples/universe_corrs.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/examples/vix_beta_to_equities_bonds.py +3 -3
- {qis-3.0.1 → qis-3.0.3}/qis/examples/vix_spy_by_year.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/qis/plots/reports/econ_data_single.py +3 -6
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/ewm_portfolio_risk.py +3 -1
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/multi_portfolio_data.py +8 -6
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/portfolio_data.py +17 -12
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/reports/config.py +10 -6
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/reports/strategy_factsheet.py +5 -4
- {qis-3.0.1 → qis-3.0.3}/qis/settings.yaml +1 -0
- {qis-3.0.1 → qis-3.0.3}/qis/test_data.py +1 -1
- {qis-3.0.1 → qis-3.0.3}/LICENSE.txt +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/__init__.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/bond_futures_portfolio.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/core/perf_bbg_prices.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/core/us_election.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/credit_spreads.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/credit_trackers.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/europe_futures.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/generate_option_rolls.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/momentum_indices.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/oakmark_analysis.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/ohlc_vol_analysis.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/perp_pricing.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/rolling_performance.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/test_ewm.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/test_scatter.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/vix_conditional_returns.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/vix_tenor_analysis.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/examples/vol_without_weekends.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/file_utils.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/local_path.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/README.md +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/__init__.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/linear/__init__.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/linear/auto_corr.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/linear/corr_cov_matrix.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/linear/ewm.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/linear/ewm_convolution.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/linear/ewm_factors.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/linear/ewm_winsor_outliers.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/linear/pca.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/linear/plot_correlations.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/linear/ra_returns.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/stats/__init__.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/stats/bootstrap.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/stats/ohlc_vol.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/stats/rolling_stats.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/models/stats/test_bootstrap.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/perfstats/README.md +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/perfstats/__init__.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/perfstats/cond_regression.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/perfstats/config.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/perfstats/desc_table.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/perfstats/fx_ops.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/perfstats/perf_stats.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/perfstats/regime_classifier.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/perfstats/returns.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/perfstats/timeseries_bfill.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/README.md +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/__init__.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/bars.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/boxplot.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/contour.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/derived/__init__.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/derived/data_timeseries.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/derived/desc_table.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/derived/drawdowns.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/derived/perf_table.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/derived/prices.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/derived/regime_class_table.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/derived/regime_data.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/derived/regime_pdf.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/derived/regime_scatter.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/derived/returns_heatmap.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/derived/returns_scatter.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/errorbar.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/heatmap.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/histogram.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/histplot2d.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/lineplot.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/pie.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/qqplot.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/reports/__init__.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/reports/gantt_data_history.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/reports/price_history.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/reports/utils.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/scatter.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/stackplot.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/table.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/time_series.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/plots/utils.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/README.md +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/__init__.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/backtester.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/reports/__init__.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/reports/brinson_attribution.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/reports/multi_assets_factsheet.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/reports/multi_strategy_factseet_pybloqs.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/reports/multi_strategy_factsheet.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/reports/strategy_benchmark_factsheet.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/reports/strategy_benchmark_factsheet_pybloqs.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/reports/strategy_signal_factsheet.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/strats/__init__.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/strats/quant_strats_delta1.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/portfolio/strats/seasonal_strats.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/sql_engine.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/README.md +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/__init__.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/dates.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/df_agg.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/df_cut.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/df_freq.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/df_groups.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/df_melt.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/df_ops.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/df_str.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/df_to_scores.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/df_to_weights.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/generic.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/np_ops.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/ols.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/sampling.py +0 -0
- {qis-3.0.1 → qis-3.0.3}/qis/utils/struct_ops.py +0 -0
{qis-3.0.1 → qis-3.0.3}/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.3
|
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
|
@@ -135,7 +135,7 @@ import qis
|
|
135
135
|
|
136
136
|
# define tickers and fetch price data
|
137
137
|
tickers = ['SPY', 'QQQ', 'EEM', 'TLT', 'IEF', 'SHY', 'LQD', 'HYG', 'GLD']
|
138
|
-
prices = yf.download(tickers, start=None, end=None)['
|
138
|
+
prices = yf.download(tickers, start=None, end=None)['Close'][tickers].dropna()
|
139
139
|
|
140
140
|
# plotting price data with minimum usage
|
141
141
|
with sns.axes_style("darkgrid"):
|
@@ -153,7 +153,7 @@ with sns.axes_style("darkgrid"):
|
|
153
153
|
|
154
154
|
```python
|
155
155
|
# plot risk-adjusted performance table with excess Sharpe ratio
|
156
|
-
ust_3m_rate = yf.download('^IRX', start=None, end=None)['
|
156
|
+
ust_3m_rate = yf.download('^IRX', start=None, end=None)['Close'].dropna() / 100.0
|
157
157
|
# set parameters for computing performance stats including returns vols and regressions
|
158
158
|
perf_params = qis.PerfParams(freq='ME', freq_reg='QE', alpha_an_factor=4.0, rates_data=ust_3m_rate)
|
159
159
|
# perf_columns is list to display different perfomance metrics from enumeration PerfStat
|
@@ -85,7 +85,7 @@ import qis
|
|
85
85
|
|
86
86
|
# define tickers and fetch price data
|
87
87
|
tickers = ['SPY', 'QQQ', 'EEM', 'TLT', 'IEF', 'SHY', 'LQD', 'HYG', 'GLD']
|
88
|
-
prices = yf.download(tickers, start=None, end=None)['
|
88
|
+
prices = yf.download(tickers, start=None, end=None)['Close'][tickers].dropna()
|
89
89
|
|
90
90
|
# plotting price data with minimum usage
|
91
91
|
with sns.axes_style("darkgrid"):
|
@@ -103,7 +103,7 @@ with sns.axes_style("darkgrid"):
|
|
103
103
|
|
104
104
|
```python
|
105
105
|
# plot risk-adjusted performance table with excess Sharpe ratio
|
106
|
-
ust_3m_rate = yf.download('^IRX', start=None, end=None)['
|
106
|
+
ust_3m_rate = yf.download('^IRX', start=None, end=None)['Close'].dropna() / 100.0
|
107
107
|
# set parameters for computing performance stats including returns vols and regressions
|
108
108
|
perf_params = qis.PerfParams(freq='ME', freq_reg='QE', alpha_an_factor=4.0, rates_data=ust_3m_rate)
|
109
109
|
# perf_columns is list to display different perfomance metrics from enumeration PerfStat
|
@@ -73,7 +73,7 @@ class UnitTests(Enum):
|
|
73
73
|
def run_unit_test(unit_test: UnitTests):
|
74
74
|
|
75
75
|
ticker = 'SPY'
|
76
|
-
prices = yf.download(ticker, start=None, end=None)['
|
76
|
+
prices = yf.download(ticker, start=None, end=None)['Close'].rename(ticker)
|
77
77
|
|
78
78
|
freq = 'ME'
|
79
79
|
wo_type = WoType.BEST
|
@@ -165,7 +165,7 @@ def run_unit_test(unit_test: UnitTests):
|
|
165
165
|
LOCAL_PATH = "C://Users//artur//OneDrive//analytics//outputs//"
|
166
166
|
|
167
167
|
# download spy prices
|
168
|
-
prices = yf.download(tickers=['SPY'], start=None, end=None, ignore_tz=True)['
|
168
|
+
prices = yf.download(tickers=['SPY'], start=None, end=None, ignore_tz=True)['Close'].rename('Realised')
|
169
169
|
|
170
170
|
if unit_test == UnitTests.PLOT_BOOTSRAPPED_PRICES:
|
171
171
|
# use small number of num_samples for illustration
|
@@ -15,7 +15,7 @@ def run_unit_test(unit_test: UnitTests):
|
|
15
15
|
if unit_test == UnitTests.RETURNS_BOXPLOT:
|
16
16
|
asset = 'SPY'
|
17
17
|
regime_benchmark = '^VIX'
|
18
|
-
prices = yf.download([asset, regime_benchmark], start=None, end=None)['
|
18
|
+
prices = yf.download([asset, regime_benchmark], start=None, end=None)['Close'].dropna()
|
19
19
|
prices = prices.asfreq('W-FRI', method='ffill')
|
20
20
|
data = pd.concat([prices[asset].pct_change(), # use returns over (t_0, t_1]
|
21
21
|
prices[regime_benchmark].shift(1) # use level at t_0
|
@@ -13,7 +13,7 @@ ASSET = 'QQQ'
|
|
13
13
|
CRYPTO = 'BTC-USD'
|
14
14
|
tickers = [ASSET, CRYPTO]
|
15
15
|
# fetch yahoo data
|
16
|
-
prices = yf.download(tickers, start=None, end=None)['
|
16
|
+
prices = yf.download(tickers, start=None, end=None)['Close'][tickers]
|
17
17
|
# resample to business days
|
18
18
|
prices = prices.asfreq('B', method='ffill').dropna()
|
19
19
|
# % returns
|
@@ -16,7 +16,7 @@ import qis
|
|
16
16
|
|
17
17
|
# load dprice data for given ticker
|
18
18
|
ticker = 'SPY'
|
19
|
-
price = yf.download(ticker, start=None, end=None)['
|
19
|
+
price = yf.download(ticker, start=None, end=None)['Close'].rename(ticker)
|
20
20
|
price = price.loc['2016':]
|
21
21
|
price_np = price.to_numpy()
|
22
22
|
|
@@ -8,7 +8,7 @@ import yfinance as yf
|
|
8
8
|
|
9
9
|
tickers_weights = dict(SPY=0.6, IEF=0.4)
|
10
10
|
tickers = list(tickers_weights.keys())
|
11
|
-
prices = yf.download(tickers, start=None, end=None)['
|
11
|
+
prices = yf.download(tickers, start=None, end=None)['Close'][tickers]
|
12
12
|
prices = prices.asfreq('B', method='ffill').dropna()
|
13
13
|
|
14
14
|
balanced_60_40a = qis.backtest_model_portfolio(prices=prices, weights=tickers_weights, rebalance_freq='QE',
|
@@ -96,7 +96,7 @@ class UnitTests(Enum):
|
|
96
96
|
|
97
97
|
def run_unit_test(unit_test: UnitTests):
|
98
98
|
|
99
|
-
ust_3m_rate = yf.download('^IRX', start=None, end=None)['
|
99
|
+
ust_3m_rate = yf.download('^IRX', start=None, end=None)['Close'].dropna() / 100.0
|
100
100
|
|
101
101
|
if unit_test == UnitTests.ETF_DATA:
|
102
102
|
regime_benchmark_str = 'SPY'
|
@@ -140,7 +140,7 @@ def run_unit_test(unit_test: UnitTests):
|
|
140
140
|
perf_params = qis.PerfParams(freq='W-WED', freq_reg='W-WED', freq_drawdown='B', rates_data=ust_3m_rate, alpha_an_factor=52)
|
141
141
|
kwargs = dict(x_date_freq='ME', heatmap_freq='ME', date_format='%b-%y', perf_params=perf_params)
|
142
142
|
|
143
|
-
prices = yf.download(tickers, start=None, end=None)['
|
143
|
+
prices = yf.download(tickers, start=None, end=None)['Close'][tickers].dropna()
|
144
144
|
|
145
145
|
if time_period is not None:
|
146
146
|
prices = time_period.locate(prices)
|
@@ -83,7 +83,7 @@ def run_unit_test(unit_test: UnitTests):
|
|
83
83
|
raise NotImplementedError
|
84
84
|
|
85
85
|
if prices is None:
|
86
|
-
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['
|
86
|
+
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['Close'][tickers]
|
87
87
|
|
88
88
|
prices = prices.asfreq('B', method='ffill') # make B frequency
|
89
89
|
prices = prices.dropna()
|
@@ -25,7 +25,7 @@ def fetch_universe_data() -> Tuple[pd.DataFrame, pd.DataFrame, pd.Series]:
|
|
25
25
|
GLD='Gold')
|
26
26
|
tickers = list(universe_data.keys())
|
27
27
|
group_data = pd.Series(universe_data) # for portfolio reporting
|
28
|
-
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['
|
28
|
+
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['Close'][tickers]
|
29
29
|
prices = prices.asfreq('B', method='ffill')
|
30
30
|
benchmark_prices = prices[['SPY', 'TLT']]
|
31
31
|
return prices, benchmark_prices, group_data
|
@@ -26,7 +26,7 @@ def fetch_riskparity_universe_data() -> Tuple[pd.DataFrame, pd.DataFrame, pd.Ser
|
|
26
26
|
GLD='Gold')
|
27
27
|
tickers = list(universe_data.keys())
|
28
28
|
group_data = pd.Series(universe_data) # for portfolio reporting
|
29
|
-
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['
|
29
|
+
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['Close'][tickers]
|
30
30
|
prices = prices.asfreq('B', method='ffill').loc['2003': ]
|
31
31
|
benchmark_prices = prices[['SPY', 'TLT']]
|
32
32
|
return prices, benchmark_prices, group_data
|
@@ -22,7 +22,7 @@ def fetch_universe_data() -> Tuple[pd.DataFrame, pd.DataFrame, pd.Series]:
|
|
22
22
|
GLD='Gold')
|
23
23
|
tickers = list(universe_data.keys())
|
24
24
|
group_data = pd.Series(universe_data) # for portfolio reporting
|
25
|
-
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['
|
25
|
+
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['Close'][tickers]
|
26
26
|
prices = prices.asfreq('B', method='ffill') # make B frequency
|
27
27
|
benchmark_prices = prices[['SPY', 'TLT']]
|
28
28
|
return prices, benchmark_prices, group_data
|
@@ -36,7 +36,7 @@ def fetch_equity_bond() -> Tuple[pd.DataFrame, pd.DataFrame, pd.Series]:
|
|
36
36
|
IEF='Bonds')
|
37
37
|
tickers = list(universe_data.keys())
|
38
38
|
group_data = pd.Series(universe_data) # for portfolio reporting
|
39
|
-
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['
|
39
|
+
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['Close'][tickers]
|
40
40
|
benchmark_prices = prices[['SPY', 'IEF']]
|
41
41
|
return prices, benchmark_prices, group_data
|
42
42
|
|
@@ -34,7 +34,7 @@ def fetch_universe_data() -> Tuple[pd.DataFrame, pd.DataFrame, pd.Series]:
|
|
34
34
|
GLD='Gold')
|
35
35
|
tickers = list(universe_data.keys())
|
36
36
|
group_data = pd.Series(universe_data) # for portfolio reporting
|
37
|
-
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['
|
37
|
+
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['Close'][tickers]
|
38
38
|
prices = prices.asfreq('B', method='ffill')
|
39
39
|
benchmark_prices = prices[['SPY', 'TLT']]
|
40
40
|
return prices, benchmark_prices, group_data
|
@@ -25,7 +25,7 @@ def run_unit_test(unit_test: UnitTests):
|
|
25
25
|
pivot = 'SPY'
|
26
26
|
asset = 'QQQ'
|
27
27
|
tickers = [pivot, asset]
|
28
|
-
prices = yf.download(tickers=tickers, start=None, end=None)['
|
28
|
+
prices = yf.download(tickers=tickers, start=None, end=None)['Close']
|
29
29
|
|
30
30
|
elif unit_test == UnitTests.BBG:
|
31
31
|
from bbg_fetch import fetch_field_timeseries_per_tickers
|
@@ -13,7 +13,7 @@ benchmark = 'SPY'
|
|
13
13
|
tickers = [benchmark, 'SSO', 'IEF']
|
14
14
|
|
15
15
|
# fetch prices
|
16
|
-
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['
|
16
|
+
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['Close'][tickers]
|
17
17
|
prices = prices.asfreq('B', method='ffill').dropna() # make B frequency
|
18
18
|
|
19
19
|
rebalance_freq = 'B' # each business day
|
@@ -27,7 +27,7 @@ unleveraged_portfolio = qis.backtest_model_portfolio(prices=prices[['SSO', 'IEF'
|
|
27
27
|
ticker='50/50 SSO/IEF').get_portfolio_nav()
|
28
28
|
|
29
29
|
# leveraged is funded at 100bp + 3m UST
|
30
|
-
funding_rate = 0.01 + yf.download('^IRX', start=None, end=None)['
|
30
|
+
funding_rate = 0.01 + yf.download('^IRX', start=None, end=None)['Close'].dropna() / 100.0
|
31
31
|
leveraged_portfolio = qis.backtest_model_portfolio(prices=prices[['SPY', 'IEF']],
|
32
32
|
weights={'SPY': 1.0, 'IEF': 0.5},
|
33
33
|
rebalance_freq=rebalance_freq,
|
@@ -24,13 +24,13 @@ def run_unit_test(unit_test: UnitTests):
|
|
24
24
|
time_period = TimePeriod('31Dec2021', '19Sep2022')
|
25
25
|
|
26
26
|
if unit_test == UnitTests.LONG_IEF_SHORT_LQD:
|
27
|
-
prices = yf.download(tickers=['IEF', 'LQD', 'LQDH', 'IGIB'], start=None, end=None)['
|
27
|
+
prices = yf.download(tickers=['IEF', 'LQD', 'LQDH', 'IGIB'], start=None, end=None)['Close']
|
28
28
|
prices = time_period.locate(prices)
|
29
29
|
rets = qis.to_returns(prices=prices, is_first_zero=True)
|
30
30
|
navs1 = qis.returns_to_nav(returns=4.0*(rets.iloc[:, 0] - rets.iloc[:, 1]).rename('4x(Long 10y ETF / Short IG ETF)'))
|
31
31
|
navs2 = qis.returns_to_nav(returns=-4.0*rets.iloc[:, 2].rename('4x(Short LQDH)'))
|
32
32
|
navs3 = qis.returns_to_nav(returns=4.0*(0.5*rets.iloc[:, 0] - rets.iloc[:, 3]).rename('4x(Long 10y ETF / Short IGIB)'))
|
33
|
-
spy = time_period.locate(yf.download(tickers=['SPY'])['
|
33
|
+
spy = time_period.locate(yf.download(tickers=['SPY'])['Close'])
|
34
34
|
prices2 = pd.concat([navs1, navs2, navs3, spy], axis=1)
|
35
35
|
|
36
36
|
with sns.axes_style('darkgrid'):
|
@@ -14,9 +14,9 @@ def compute_returns(ticker: str = 'SPY', time_period: qis.TimePeriod = None) ->
|
|
14
14
|
if time_period is not None:
|
15
15
|
ohlc_data = time_period.locate(ohlc_data)
|
16
16
|
# need to adjust open price for dividends
|
17
|
-
adjustment_ratio = ohlc_data['
|
17
|
+
adjustment_ratio = ohlc_data['Close'] / ohlc_data['Close']
|
18
18
|
adjusted_open = adjustment_ratio.multiply(ohlc_data['Open'])
|
19
|
-
adjusted_close = ohlc_data['
|
19
|
+
adjusted_close = ohlc_data['Close']
|
20
20
|
overnight_return = adjusted_open.divide(adjusted_close.shift(1)) - 1.0
|
21
21
|
intraday_return = adjusted_close.divide(adjusted_open) - 1.0
|
22
22
|
close_to_close_return = adjusted_close.divide(adjusted_close.shift(1)) - 1.0
|
@@ -15,14 +15,14 @@ import qis
|
|
15
15
|
svrpo = qis.load_df_from_csv(file_name='SVRPO_History', local_path=qis.get_resource_path())
|
16
16
|
# spy etf as benchmark
|
17
17
|
benchmark = 'SPY'
|
18
|
-
spy = yf.download([benchmark], start=None, end=None)['
|
18
|
+
spy = yf.download([benchmark], start=None, end=None)['Close'].rename(benchmark)
|
19
19
|
# merge
|
20
20
|
prices = pd.concat([spy, svrpo], axis=1).dropna()
|
21
21
|
# take last 3 years
|
22
22
|
prices = prices.loc['2020':, :]
|
23
23
|
|
24
24
|
# set parameters for computing performance stats including returns vols and regressions
|
25
|
-
ust_3m_rate = yf.download('^IRX', start=None, end=None)['
|
25
|
+
ust_3m_rate = yf.download('^IRX', start=None, end=None)['Close'].dropna() / 100.0
|
26
26
|
perf_params = qis.PerfParams(freq='ME', freq_reg='W-WED', alpha_an_factor=52.0, rates_data=ust_3m_rate)
|
27
27
|
|
28
28
|
# price perf
|
@@ -7,7 +7,7 @@ import qis as qis
|
|
7
7
|
|
8
8
|
# define tickers and fetch price data
|
9
9
|
tickers = ['SPY', 'QQQ', 'EEM', 'TLT', 'IEF', 'SHY', 'LQD', 'HYG', 'GLD']
|
10
|
-
prices = yf.download(tickers, start=None, end=None)['
|
10
|
+
prices = yf.download(tickers, start=None, end=None)['Close'][tickers].dropna()
|
11
11
|
|
12
12
|
# minimum usage
|
13
13
|
with sns.axes_style("darkgrid"):
|
@@ -28,7 +28,7 @@ qis.save_fig(fig, file_name='perf2', local_path="figures/")
|
|
28
28
|
# risk-adjusted performance table with specified data entries
|
29
29
|
# add rates for excess Sharpe
|
30
30
|
from qis import PerfStat
|
31
|
-
ust_3m_rate = yf.download('^IRX', start=None, end=None)['
|
31
|
+
ust_3m_rate = yf.download('^IRX', start=None, end=None)['Close'].dropna() / 100.0
|
32
32
|
|
33
33
|
# set parameters for computing performance stats including returns vols and regressions
|
34
34
|
perf_params = qis.PerfParams(freq='ME', freq_reg='QE', alpha_an_factor=4.0, rates_data=ust_3m_rate)
|
@@ -22,10 +22,10 @@ bond_etfs = {'SHV': '3m UST',
|
|
22
22
|
}
|
23
23
|
# fetch prices and rename to dict values
|
24
24
|
tickers = list(bond_etfs.keys())
|
25
|
-
prices = yf.download(tickers, start=None, end=None)['
|
25
|
+
prices = yf.download(tickers, start=None, end=None)['Close'][tickers].rename(bond_etfs, axis=1)
|
26
26
|
|
27
27
|
# set parameters for computing performance stats including returns vols and regressions
|
28
|
-
ust_3m_rate = yf.download('^IRX', start=None, end=None)['
|
28
|
+
ust_3m_rate = yf.download('^IRX', start=None, end=None)['Close'].dropna() / 100.0
|
29
29
|
perf_params = qis.PerfParams(freq='W-WED', rates_data=ust_3m_rate)
|
30
30
|
|
31
31
|
# time period for performance measurement
|
@@ -8,7 +8,7 @@ import qis
|
|
8
8
|
tickers = ['SPY', 'TLT', 'GLD']
|
9
9
|
|
10
10
|
# fetch prices
|
11
|
-
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['
|
11
|
+
prices = yf.download(tickers=tickers, start=None, end=None, ignore_tz=True)['Close'][tickers]
|
12
12
|
prices = prices.asfreq('B', method='ffill').dropna() # make B frequency
|
13
13
|
returns = qis.to_returns(prices, freq='ME', drop_first=True)
|
14
14
|
returns['month'] = returns.index.month
|
@@ -7,7 +7,7 @@ from qis import PerfStat
|
|
7
7
|
|
8
8
|
# define tickers and fetch price data
|
9
9
|
tickers = ['SPY', 'QQQ', 'EEM', 'TLT', 'IEF', 'SHY', 'LQD', 'HYG', 'GLD']
|
10
|
-
prices = yf.download(tickers, start=None, end=None)['
|
10
|
+
prices = yf.download(tickers, start=None, end=None)['Close'][tickers].dropna()
|
11
11
|
|
12
12
|
# define frequencies for vol computations
|
13
13
|
freqs = ['B', 'W-WED', 'ME', 'QE']
|
@@ -226,7 +226,7 @@ def run_unit_test(unit_test: UnitTests):
|
|
226
226
|
import yfinance as yf
|
227
227
|
|
228
228
|
if unit_test == UnitTests.BTC_SIMULATION:
|
229
|
-
prices = yf.download(tickers=['BTC-USD'], start=None, end=None)['
|
229
|
+
prices = yf.download(tickers=['BTC-USD'], start=None, end=None)['Close'].rename('BTC').dropna()
|
230
230
|
|
231
231
|
time_period = qis.TimePeriod('31Dec2015', '21Jun2023')
|
232
232
|
figs = create_time_series_report(prices=prices, time_period=time_period, vol_target=0.5, vol_af=360)
|
@@ -234,7 +234,7 @@ def run_unit_test(unit_test: UnitTests):
|
|
234
234
|
add_current_date=True, local_path=None)
|
235
235
|
|
236
236
|
elif unit_test == UnitTests.SPY_SIMULATION:
|
237
|
-
prices = yf.download(tickers=['SPY'], start=None, end=None)['
|
237
|
+
prices = yf.download(tickers=['SPY'], start=None, end=None)['Close'].rename('SPY').dropna()
|
238
238
|
time_period = qis.TimePeriod('31Dec2019', '15Aug2024')
|
239
239
|
figs = create_time_series_report(prices=prices, time_period=time_period, vol_target=0.15, vol_af=260)
|
240
240
|
fu.save_figs_to_pdf(figs=figs, file_name='spy_analysis', orientation='landscape',
|
@@ -131,7 +131,7 @@ class UnitTests(Enum):
|
|
131
131
|
|
132
132
|
def run_unit_test(unit_test: UnitTests):
|
133
133
|
|
134
|
-
ust_3m_rate = yf.download('^IRX', start=None, end=None)['
|
134
|
+
ust_3m_rate = yf.download('^IRX', start=None, end=None)['Close'].dropna() / 100.0
|
135
135
|
|
136
136
|
if unit_test == UnitTests.ETF_DATA:
|
137
137
|
regime_benchmark_str = 'SPY'
|
@@ -154,7 +154,7 @@ def run_unit_test(unit_test: UnitTests):
|
|
154
154
|
perf_params = qis.PerfParams(freq='W-WED', freq_reg='W-WED', freq_drawdown='B', rates_data=ust_3m_rate)
|
155
155
|
kwargs = dict(x_date_freq='ME', heatmap_freq='ME', date_format='%b-%y', perf_params=perf_params)
|
156
156
|
|
157
|
-
prices = yf.download(tickers, start=None, end=None)['
|
157
|
+
prices = yf.download(tickers, start=None, end=None)['Close'].dropna()
|
158
158
|
|
159
159
|
if time_period is not None:
|
160
160
|
prices = time_period.locate(prices)
|
@@ -11,7 +11,7 @@ class UnitTests(Enum):
|
|
11
11
|
def run_unit_test(unit_test: UnitTests):
|
12
12
|
|
13
13
|
tickers = ['SPY', 'QQQ', 'TLT', 'GLD']
|
14
|
-
prices = yf.download(tickers, start=None, end=None)['
|
14
|
+
prices = yf.download(tickers, start=None, end=None)['Close'][tickers].dropna()
|
15
15
|
|
16
16
|
if unit_test == UnitTests.CORR1:
|
17
17
|
fig, ax = plt.subplots(1, 1, figsize=(7, 7), tight_layout=True)
|
@@ -9,10 +9,10 @@ import qis
|
|
9
9
|
from qis import PortfolioData
|
10
10
|
|
11
11
|
# load VIX ETH
|
12
|
-
vix = yf.download(tickers=['VXX'], start=None, end=None, ignore_tz=True)['
|
12
|
+
vix = yf.download(tickers=['VXX'], start=None, end=None, ignore_tz=True)['Close'].asfreq('B', method='ffill').rename('Long VIX ETF')
|
13
13
|
|
14
14
|
# load becnhmarks benchmarks
|
15
|
-
benchmark_prices = yf.download(tickers=['SPY', 'TLT'], start=None, end=None, ignore_tz=True)['
|
15
|
+
benchmark_prices = yf.download(tickers=['SPY', 'TLT'], start=None, end=None, ignore_tz=True)['Close'].asfreq('B', method='ffill')
|
16
16
|
|
17
17
|
# create long-only portfolio using vix nav
|
18
18
|
vix_portfolio = PortfolioData(nav=vix)
|
@@ -20,7 +20,7 @@ vix_portfolio = PortfolioData(nav=vix)
|
|
20
20
|
# set timeperiod for analysis
|
21
21
|
time_period = qis.TimePeriod('31Dec2021', None)
|
22
22
|
perf_params = qis.PerfParams(freq='W-WED', freq_reg='W-WED', alpha_an_factor=52.0,
|
23
|
-
rates_data=yf.download('^IRX', start=None, end=None)['
|
23
|
+
rates_data=yf.download('^IRX', start=None, end=None)['Close'].dropna() / 100.0)
|
24
24
|
regime_params = qis.BenchmarkReturnsQuantileRegimeSpecs(freq='ME')
|
25
25
|
|
26
26
|
prices = pd.concat([vix, benchmark_prices], axis=1).sort_index().dropna()
|
@@ -81,7 +81,7 @@ def run_unit_test(unit_test: UnitTests):
|
|
81
81
|
time_period = qis.TimePeriod('01Jan1996', None)
|
82
82
|
|
83
83
|
if unit_test == UnitTests.VIX_SPY:
|
84
|
-
prices = yf.download(['SPY', '^VIX'], start=None, end=None)['
|
84
|
+
prices = yf.download(['SPY', '^VIX'], start=None, end=None)['Close']
|
85
85
|
fig = plot_vol_vs_underlying(spot=prices['SPY'].rename('S&P500'),
|
86
86
|
vol=prices['^VIX'].rename('VIX'),
|
87
87
|
time_period=time_period)
|
@@ -170,20 +170,17 @@ class UnitTests(Enum):
|
|
170
170
|
def run_unit_test(unit_test: UnitTests):
|
171
171
|
|
172
172
|
import matplotlib.pyplot as plt
|
173
|
-
import futures_strats.local_path as lp
|
174
|
-
local_path = lp.get_resource_path()
|
175
|
-
|
176
173
|
if unit_test == UnitTests.UPDATE_DATA:
|
177
174
|
from bbg_fetch import fetch_field_timeseries_per_tickers
|
178
175
|
vix_tickers = ['VIX1D Index', 'VIX9D Index', 'VIX Index', 'VIX3M Index', 'VIX6M Index', 'VIX1Y Index']
|
179
176
|
vols = 0.01 * fetch_field_timeseries_per_tickers(tickers=vix_tickers, field='PX_LAST', CshAdjNormal=True)
|
180
177
|
print(vols)
|
181
|
-
qis.save_df_to_csv(df=vols, file_name='vix_indices'
|
178
|
+
qis.save_df_to_csv(df=vols, file_name='vix_indices')
|
182
179
|
|
183
180
|
elif unit_test == UnitTests.RUN_REPORT:
|
184
|
-
df = qis.load_df_from_csv(file_name='vix_indices'
|
181
|
+
df = qis.load_df_from_csv(file_name='vix_indices')
|
185
182
|
figs = econ_data_report(data=df)
|
186
|
-
qis.save_figs_to_pdf(figs, file_name='vix_indices'
|
183
|
+
qis.save_figs_to_pdf(figs, file_name='vix_indices')
|
187
184
|
|
188
185
|
plt.show()
|
189
186
|
|
@@ -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:
|
@@ -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,
|