qis 3.0.10__tar.gz → 3.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. {qis-3.0.10 → qis-3.1.2}/PKG-INFO +3 -2
  2. {qis-3.0.10 → qis-3.1.2}/README.md +2 -1
  3. {qis-3.0.10 → qis-3.1.2}/pyproject.toml +1 -1
  4. {qis-3.0.10 → qis-3.1.2}/qis/examples/bond_futures_portfolio.py +1 -1
  5. {qis-3.0.10 → qis-3.1.2}/qis/examples/constant_weight_portfolios.py +2 -2
  6. {qis-3.0.10 → qis-3.1.2}/qis/examples/factsheets/strategy.py +1 -1
  7. {qis-3.0.10 → qis-3.1.2}/qis/examples/leveraged_strategies.py +3 -3
  8. {qis-3.0.10 → qis-3.1.2}/qis/file_utils.py +5 -5
  9. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/__init__.py +2 -1
  10. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/backtester.py +7 -7
  11. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/multi_portfolio_data.py +14 -7
  12. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/portfolio_data.py +5 -2
  13. {qis-3.0.10 → qis-3.1.2}/qis/settings.yaml +0 -1
  14. {qis-3.0.10 → qis-3.1.2}/qis/utils/df_str.py +2 -0
  15. qis-3.0.10/qis/examples/oakmark_analysis.py +0 -21
  16. {qis-3.0.10 → qis-3.1.2}/LICENSE.txt +0 -0
  17. {qis-3.0.10 → qis-3.1.2}/qis/__init__.py +0 -0
  18. {qis-3.0.10 → qis-3.1.2}/qis/examples/best_returns.py +0 -0
  19. {qis-3.0.10 → qis-3.1.2}/qis/examples/bootstrap_analysis.py +0 -0
  20. {qis-3.0.10 → qis-3.1.2}/qis/examples/boxplot_conditional_returns.py +0 -0
  21. {qis-3.0.10 → qis-3.1.2}/qis/examples/btc_asset_corr.py +0 -0
  22. {qis-3.0.10 → qis-3.1.2}/qis/examples/constant_notional.py +0 -0
  23. {qis-3.0.10 → qis-3.1.2}/qis/examples/core/perf_bbg_prices.py +0 -0
  24. {qis-3.0.10 → qis-3.1.2}/qis/examples/core/price_plots.py +0 -0
  25. {qis-3.0.10 → qis-3.1.2}/qis/examples/core/us_election.py +0 -0
  26. {qis-3.0.10 → qis-3.1.2}/qis/examples/credit_spreads.py +0 -0
  27. {qis-3.0.10 → qis-3.1.2}/qis/examples/credit_trackers.py +0 -0
  28. {qis-3.0.10 → qis-3.1.2}/qis/examples/europe_futures.py +0 -0
  29. {qis-3.0.10 → qis-3.1.2}/qis/examples/factsheets/multi_assets.py +0 -0
  30. {qis-3.0.10 → qis-3.1.2}/qis/examples/factsheets/multi_strategy.py +0 -0
  31. {qis-3.0.10 → qis-3.1.2}/qis/examples/factsheets/pyblogs_reports.py +0 -0
  32. {qis-3.0.10 → qis-3.1.2}/qis/examples/factsheets/strategy_benchmark.py +0 -0
  33. {qis-3.0.10 → qis-3.1.2}/qis/examples/generate_option_rolls.py +0 -0
  34. {qis-3.0.10 → qis-3.1.2}/qis/examples/interpolation_infrequent_returns.py +0 -0
  35. {qis-3.0.10 → qis-3.1.2}/qis/examples/long_short.py +0 -0
  36. {qis-3.0.10 → qis-3.1.2}/qis/examples/momentum_indices.py +0 -0
  37. {qis-3.0.10 → qis-3.1.2}/qis/examples/ohlc_vol_analysis.py +0 -0
  38. {qis-3.0.10 → qis-3.1.2}/qis/examples/overnight_returns.py +0 -0
  39. {qis-3.0.10 → qis-3.1.2}/qis/examples/perf_external_assets.py +0 -0
  40. {qis-3.0.10 → qis-3.1.2}/qis/examples/perp_pricing.py +0 -0
  41. {qis-3.0.10 → qis-3.1.2}/qis/examples/readme_performances.py +0 -0
  42. {qis-3.0.10 → qis-3.1.2}/qis/examples/risk_return_frontier.py +0 -0
  43. {qis-3.0.10 → qis-3.1.2}/qis/examples/rolling_performance.py +0 -0
  44. {qis-3.0.10 → qis-3.1.2}/qis/examples/seasonality.py +0 -0
  45. {qis-3.0.10 → qis-3.1.2}/qis/examples/sharpe_vs_sortino.py +0 -0
  46. {qis-3.0.10 → qis-3.1.2}/qis/examples/simulate_quant_strats.py +0 -0
  47. {qis-3.0.10 → qis-3.1.2}/qis/examples/test_ewm.py +0 -0
  48. {qis-3.0.10 → qis-3.1.2}/qis/examples/test_scatter.py +0 -0
  49. {qis-3.0.10 → qis-3.1.2}/qis/examples/try_pybloqs.py +0 -0
  50. {qis-3.0.10 → qis-3.1.2}/qis/examples/universe_corrs.py +0 -0
  51. {qis-3.0.10 → qis-3.1.2}/qis/examples/vix_beta_to_equities_bonds.py +0 -0
  52. {qis-3.0.10 → qis-3.1.2}/qis/examples/vix_conditional_returns.py +0 -0
  53. {qis-3.0.10 → qis-3.1.2}/qis/examples/vix_spy_by_year.py +0 -0
  54. {qis-3.0.10 → qis-3.1.2}/qis/examples/vix_tenor_analysis.py +0 -0
  55. {qis-3.0.10 → qis-3.1.2}/qis/examples/vol_without_weekends.py +0 -0
  56. {qis-3.0.10 → qis-3.1.2}/qis/local_path.py +0 -0
  57. {qis-3.0.10 → qis-3.1.2}/qis/models/README.md +0 -0
  58. {qis-3.0.10 → qis-3.1.2}/qis/models/__init__.py +0 -0
  59. {qis-3.0.10 → qis-3.1.2}/qis/models/linear/__init__.py +0 -0
  60. {qis-3.0.10 → qis-3.1.2}/qis/models/linear/auto_corr.py +0 -0
  61. {qis-3.0.10 → qis-3.1.2}/qis/models/linear/corr_cov_matrix.py +0 -0
  62. {qis-3.0.10 → qis-3.1.2}/qis/models/linear/ewm.py +0 -0
  63. {qis-3.0.10 → qis-3.1.2}/qis/models/linear/ewm_convolution.py +0 -0
  64. {qis-3.0.10 → qis-3.1.2}/qis/models/linear/ewm_factors.py +0 -0
  65. {qis-3.0.10 → qis-3.1.2}/qis/models/linear/ewm_winsor_outliers.py +0 -0
  66. {qis-3.0.10 → qis-3.1.2}/qis/models/linear/pca.py +0 -0
  67. {qis-3.0.10 → qis-3.1.2}/qis/models/linear/plot_correlations.py +0 -0
  68. {qis-3.0.10 → qis-3.1.2}/qis/models/linear/ra_returns.py +0 -0
  69. {qis-3.0.10 → qis-3.1.2}/qis/models/stats/__init__.py +0 -0
  70. {qis-3.0.10 → qis-3.1.2}/qis/models/stats/bootstrap.py +0 -0
  71. {qis-3.0.10 → qis-3.1.2}/qis/models/stats/ohlc_vol.py +0 -0
  72. {qis-3.0.10 → qis-3.1.2}/qis/models/stats/rolling_stats.py +0 -0
  73. {qis-3.0.10 → qis-3.1.2}/qis/models/stats/test_bootstrap.py +0 -0
  74. {qis-3.0.10 → qis-3.1.2}/qis/perfstats/README.md +0 -0
  75. {qis-3.0.10 → qis-3.1.2}/qis/perfstats/__init__.py +0 -0
  76. {qis-3.0.10 → qis-3.1.2}/qis/perfstats/cond_regression.py +0 -0
  77. {qis-3.0.10 → qis-3.1.2}/qis/perfstats/config.py +0 -0
  78. {qis-3.0.10 → qis-3.1.2}/qis/perfstats/desc_table.py +0 -0
  79. {qis-3.0.10 → qis-3.1.2}/qis/perfstats/fx_ops.py +0 -0
  80. {qis-3.0.10 → qis-3.1.2}/qis/perfstats/perf_stats.py +0 -0
  81. {qis-3.0.10 → qis-3.1.2}/qis/perfstats/regime_classifier.py +0 -0
  82. {qis-3.0.10 → qis-3.1.2}/qis/perfstats/returns.py +0 -0
  83. {qis-3.0.10 → qis-3.1.2}/qis/perfstats/timeseries_bfill.py +0 -0
  84. {qis-3.0.10 → qis-3.1.2}/qis/plots/README.md +0 -0
  85. {qis-3.0.10 → qis-3.1.2}/qis/plots/__init__.py +0 -0
  86. {qis-3.0.10 → qis-3.1.2}/qis/plots/bars.py +0 -0
  87. {qis-3.0.10 → qis-3.1.2}/qis/plots/boxplot.py +0 -0
  88. {qis-3.0.10 → qis-3.1.2}/qis/plots/contour.py +0 -0
  89. {qis-3.0.10 → qis-3.1.2}/qis/plots/derived/__init__.py +0 -0
  90. {qis-3.0.10 → qis-3.1.2}/qis/plots/derived/data_timeseries.py +0 -0
  91. {qis-3.0.10 → qis-3.1.2}/qis/plots/derived/desc_table.py +0 -0
  92. {qis-3.0.10 → qis-3.1.2}/qis/plots/derived/drawdowns.py +0 -0
  93. {qis-3.0.10 → qis-3.1.2}/qis/plots/derived/perf_table.py +0 -0
  94. {qis-3.0.10 → qis-3.1.2}/qis/plots/derived/prices.py +0 -0
  95. {qis-3.0.10 → qis-3.1.2}/qis/plots/derived/regime_class_table.py +0 -0
  96. {qis-3.0.10 → qis-3.1.2}/qis/plots/derived/regime_data.py +0 -0
  97. {qis-3.0.10 → qis-3.1.2}/qis/plots/derived/regime_pdf.py +0 -0
  98. {qis-3.0.10 → qis-3.1.2}/qis/plots/derived/regime_scatter.py +0 -0
  99. {qis-3.0.10 → qis-3.1.2}/qis/plots/derived/returns_heatmap.py +0 -0
  100. {qis-3.0.10 → qis-3.1.2}/qis/plots/derived/returns_scatter.py +0 -0
  101. {qis-3.0.10 → qis-3.1.2}/qis/plots/errorbar.py +0 -0
  102. {qis-3.0.10 → qis-3.1.2}/qis/plots/heatmap.py +0 -0
  103. {qis-3.0.10 → qis-3.1.2}/qis/plots/histogram.py +0 -0
  104. {qis-3.0.10 → qis-3.1.2}/qis/plots/histplot2d.py +0 -0
  105. {qis-3.0.10 → qis-3.1.2}/qis/plots/lineplot.py +0 -0
  106. {qis-3.0.10 → qis-3.1.2}/qis/plots/pie.py +0 -0
  107. {qis-3.0.10 → qis-3.1.2}/qis/plots/qqplot.py +0 -0
  108. {qis-3.0.10 → qis-3.1.2}/qis/plots/reports/__init__.py +0 -0
  109. {qis-3.0.10 → qis-3.1.2}/qis/plots/reports/econ_data_single.py +0 -0
  110. {qis-3.0.10 → qis-3.1.2}/qis/plots/reports/gantt_data_history.py +0 -0
  111. {qis-3.0.10 → qis-3.1.2}/qis/plots/reports/price_history.py +0 -0
  112. {qis-3.0.10 → qis-3.1.2}/qis/plots/reports/utils.py +0 -0
  113. {qis-3.0.10 → qis-3.1.2}/qis/plots/scatter.py +0 -0
  114. {qis-3.0.10 → qis-3.1.2}/qis/plots/stackplot.py +0 -0
  115. {qis-3.0.10 → qis-3.1.2}/qis/plots/table.py +0 -0
  116. {qis-3.0.10 → qis-3.1.2}/qis/plots/time_series.py +0 -0
  117. {qis-3.0.10 → qis-3.1.2}/qis/plots/utils.py +0 -0
  118. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/README.md +0 -0
  119. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/ewm_portfolio_risk.py +0 -0
  120. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/reports/__init__.py +0 -0
  121. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/reports/brinson_attribution.py +0 -0
  122. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/reports/config.py +0 -0
  123. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/reports/multi_assets_factsheet.py +0 -0
  124. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/reports/multi_strategy_factseet_pybloqs.py +0 -0
  125. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/reports/multi_strategy_factsheet.py +0 -0
  126. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/reports/strategy_benchmark_factsheet.py +0 -0
  127. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/reports/strategy_benchmark_factsheet_pybloqs.py +0 -0
  128. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/reports/strategy_factsheet.py +0 -0
  129. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/reports/strategy_signal_factsheet.py +0 -0
  130. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/strats/__init__.py +0 -0
  131. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/strats/quant_strats_delta1.py +0 -0
  132. {qis-3.0.10 → qis-3.1.2}/qis/portfolio/strats/seasonal_strats.py +0 -0
  133. {qis-3.0.10 → qis-3.1.2}/qis/sql_engine.py +0 -0
  134. {qis-3.0.10 → qis-3.1.2}/qis/test_data.py +0 -0
  135. {qis-3.0.10 → qis-3.1.2}/qis/utils/README.md +0 -0
  136. {qis-3.0.10 → qis-3.1.2}/qis/utils/__init__.py +0 -0
  137. {qis-3.0.10 → qis-3.1.2}/qis/utils/dates.py +0 -0
  138. {qis-3.0.10 → qis-3.1.2}/qis/utils/df_agg.py +0 -0
  139. {qis-3.0.10 → qis-3.1.2}/qis/utils/df_cut.py +0 -0
  140. {qis-3.0.10 → qis-3.1.2}/qis/utils/df_freq.py +0 -0
  141. {qis-3.0.10 → qis-3.1.2}/qis/utils/df_groups.py +0 -0
  142. {qis-3.0.10 → qis-3.1.2}/qis/utils/df_melt.py +0 -0
  143. {qis-3.0.10 → qis-3.1.2}/qis/utils/df_ops.py +0 -0
  144. {qis-3.0.10 → qis-3.1.2}/qis/utils/df_to_scores.py +0 -0
  145. {qis-3.0.10 → qis-3.1.2}/qis/utils/df_to_weights.py +0 -0
  146. {qis-3.0.10 → qis-3.1.2}/qis/utils/generic.py +0 -0
  147. {qis-3.0.10 → qis-3.1.2}/qis/utils/np_ops.py +0 -0
  148. {qis-3.0.10 → qis-3.1.2}/qis/utils/ols.py +0 -0
  149. {qis-3.0.10 → qis-3.1.2}/qis/utils/sampling.py +0 -0
  150. {qis-3.0.10 → qis-3.1.2}/qis/utils/struct_ops.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: qis
3
- Version: 3.0.10
3
+ Version: 3.1.2
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
@@ -54,7 +54,7 @@ Description-Content-Type: text/markdown
54
54
  qis package implements analytics for visualisation of financial data, performance
55
55
  reporting, analysis of quantitative strategies.
56
56
 
57
- qis package is split into 5 main modules with the
57
+ The package is split into 5 main modules with the
58
58
  dependency path increasing sequentially as follows.
59
59
 
60
60
  1. ```qis.utils``` is module containing low level utilities for operations with pandas, numpy, and datetimes.
@@ -318,4 +318,5 @@ See the [LICENSE.txt](https://github.com/ArturSepp/QuantInvestStrats/blob/master
318
318
 
319
319
  Please report any bugs or suggestions by opening an [issue](https://github.com/ArturSepp/QuantInvestStrats/issues).
320
320
 
321
+ .
321
322
 
@@ -4,7 +4,7 @@
4
4
  qis package implements analytics for visualisation of financial data, performance
5
5
  reporting, analysis of quantitative strategies.
6
6
 
7
- qis package is split into 5 main modules with the
7
+ The package is split into 5 main modules with the
8
8
  dependency path increasing sequentially as follows.
9
9
 
10
10
  1. ```qis.utils``` is module containing low level utilities for operations with pandas, numpy, and datetimes.
@@ -268,3 +268,4 @@ See the [LICENSE.txt](https://github.com/ArturSepp/QuantInvestStrats/blob/master
268
268
 
269
269
  Please report any bugs or suggestions by opening an [issue](https://github.com/ArturSepp/QuantInvestStrats/issues).
270
270
 
271
+ .
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "qis"
3
- version = "3.0.10"
3
+ version = "3.1.2"
4
4
  description = "Implementation of visualisation and reporting analytics for Quantitative Investment Strategies"
5
5
  license = "LICENSE.txt"
6
6
  authors = ["Artur Sepp <artursepp@gmail.com>"]
@@ -27,7 +27,7 @@ qis.plot_prices_with_dd(prices=prices)
27
27
 
28
28
  portfolio_data = qis.backtest_model_portfolio(prices=prices,
29
29
  weights=np.array([-1.0, 2.0, -1.0]),
30
- rebalance_freq='ME',
30
+ rebalancing_freq='ME',
31
31
  ticker='Butterly'
32
32
  )
33
33
 
@@ -11,9 +11,9 @@ tickers = list(tickers_weights.keys())
11
11
  prices = yf.download(tickers, start=None, end=None)['Close'][tickers]
12
12
  prices = prices.asfreq('B', method='ffill').dropna()
13
13
 
14
- balanced_60_40a = qis.backtest_model_portfolio(prices=prices, weights=tickers_weights, rebalance_freq='QE',
14
+ balanced_60_40a = qis.backtest_model_portfolio(prices=prices, weights=tickers_weights, rebalancing_freq='QE',
15
15
  ticker='Zero Cost').get_portfolio_nav()
16
- balanced_60_40b = qis.backtest_model_portfolio(prices=prices, weights=tickers_weights, rebalance_freq='QE',
16
+ balanced_60_40b = qis.backtest_model_portfolio(prices=prices, weights=tickers_weights, rebalancing_freq='QE',
17
17
  ticker='2% Cost',
18
18
  management_fee=0.02).get_portfolio_nav()
19
19
  navs = pd.concat([balanced_60_40a, balanced_60_40b], axis=1)
@@ -163,7 +163,7 @@ def run_unit_test(unit_test: UnitTests):
163
163
  # benchmark_prices = fetch_field_timeseries_per_tickers(tickers={'LQD US Equity': 'LQD'})
164
164
  delta1_portfolio = qis.backtest_model_portfolio(prices=prices,
165
165
  weights=np.array([1.0]),
166
- rebalance_freq='SE', # only at starts
166
+ rebalancing_freq='SE', # only at starts
167
167
  rebalancing_costs=rebalancing_costs,
168
168
  ticker='Delta1')
169
169
  figs = qis.generate_strategy_factsheet(portfolio_data=delta1_portfolio,
@@ -16,13 +16,13 @@ tickers = [benchmark, 'SSO', 'IEF']
16
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
- rebalance_freq = 'B' # each business day
19
+ rebalancing_freq = 'B' # each business day
20
20
  rebalancing_costs = 0.0010 # 10bp for rebalancing
21
21
 
22
22
  # 50/50 SSO/IEF
23
23
  unleveraged_portfolio = qis.backtest_model_portfolio(prices=prices[['SSO', 'IEF']],
24
24
  weights={'SSO': 0.5, 'IEF': 0.5},
25
- rebalance_freq=rebalance_freq,
25
+ rebalancing_freq=rebalancing_freq,
26
26
  rebalancing_costs=rebalancing_costs,
27
27
  ticker='50/50 SSO/IEF').get_portfolio_nav()
28
28
 
@@ -30,7 +30,7 @@ unleveraged_portfolio = qis.backtest_model_portfolio(prices=prices[['SSO', 'IEF'
30
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
- rebalance_freq=rebalance_freq,
33
+ rebalancing_freq=rebalancing_freq,
34
34
  rebalancing_costs=rebalancing_costs,
35
35
  funding_rate=funding_rate,
36
36
  ticker='100/50 SPY/IEF').get_portfolio_nav()
@@ -231,14 +231,14 @@ def save_df_to_excel(data: Union[pd.DataFrame, List[pd.DataFrame], Dict[str, pd.
231
231
  if sheet_names is None:
232
232
  sheet_names = [f"Sheet {n+1}" for n, _ in enumerate(data)]
233
233
  for df, name in zip(data, sheet_names):
234
- if df is not None:
234
+ if df is not None and isinstance(df, pd.DataFrame):
235
235
  df = delocalize_df(df)
236
236
  if transpose:
237
237
  df = df.T
238
238
  df.to_excel(excel_writer=excel_writer, sheet_name=name)
239
239
  elif isinstance(data, dict): # publish with sheet names
240
240
  for key, df in data.items():
241
- if df is not None:
241
+ if df is not None and isinstance(df, pd.DataFrame):
242
242
  df = delocalize_df(df)
243
243
  if transpose:
244
244
  df = df.T
@@ -482,7 +482,7 @@ def save_df_dict_to_csv(datasets: Dict[Union[str, Enum, NamedTuple], pd.DataFram
482
482
  file_name = f"{file_name}_{pd.Timestamp.now().strftime(DATE_FORMAT)}"
483
483
 
484
484
  for key, data in datasets.items():
485
- if data is not None:
485
+ if data is not None and isinstance(data, pd.DataFrame):
486
486
  file_path = get_local_file_path(file_name=file_name,
487
487
  file_type=FileTypes.CSV,
488
488
  local_path=local_path,
@@ -541,7 +541,7 @@ def save_df_dict_to_sql(engine: Engine,
541
541
  save pandas dict to sql engine
542
542
  """
543
543
  for key, df in dfs.items():
544
- if df is not None:
544
+ if df is not None and isinstance(df, pd.DataFrame):
545
545
  if index_col is not None:
546
546
  df = df.reset_index(names=index_col)
547
547
  df.to_sql(f"{table_name}_{key}", engine, schema=schema, if_exists='replace')
@@ -672,7 +672,7 @@ def save_df_dict_to_feather(dfs: Dict[Union[str, Enum, NamedTuple], pd.DataFrame
672
672
  pandas dict to csv files
673
673
  """
674
674
  for key, df in dfs.items():
675
- if df is not None:
675
+ if df is not None and isinstance(df, pd.DataFrame):
676
676
  file_path = get_local_file_path(file_name=file_name,
677
677
  file_type=FileTypes.FEATHER,
678
678
  local_path=local_path,
@@ -35,7 +35,8 @@ from qis.portfolio.reports.multi_assets_factsheet import (MultiAssetsReport, gen
35
35
  from qis.portfolio.reports.strategy_factsheet import generate_strategy_factsheet
36
36
 
37
37
  from qis.portfolio.reports.strategy_benchmark_factsheet import (generate_strategy_benchmark_factsheet_plt,
38
- generate_strategy_benchmark_active_perf_plt)
38
+ generate_strategy_benchmark_active_perf_plt,
39
+ plot_exposures_strategy_vs_benchmark_stack)
39
40
 
40
41
  from qis.portfolio.reports.multi_strategy_factsheet import generate_multi_portfolio_factsheet
41
42
 
@@ -15,7 +15,7 @@ from qis.portfolio.portfolio_data import PortfolioData
15
15
 
16
16
  def backtest_model_portfolio(prices: pd.DataFrame,
17
17
  weights: Union[Dict[str, float], List[float], np.ndarray, pd.DataFrame, pd.Series],
18
- rebalance_freq: Optional[str] = 'QE',
18
+ rebalancing_freq: Optional[str] = 'QE',
19
19
  initial_nav: float = 100,
20
20
  funding_rate: pd.Series = None, # annualised on positive / negative cash balances
21
21
  management_fee: float = None, # annualised
@@ -30,7 +30,7 @@ def backtest_model_portfolio(prices: pd.DataFrame,
30
30
  simulate portfolio given prices and weights
31
31
  include_start_date if index rebalanced at start date
32
32
  the safest weight is to pass weights as Dict or pd.Dataframe - this enforces the alignment with prices
33
- does not rebalance_freq when dates are pd.DataFrame
33
+ does not rebalancing_freq when dates are pd.DataFrame
34
34
  funding_rate is funding rate on cash annualised
35
35
  management_fee is man fee on strategy nav annualised
36
36
  """
@@ -55,7 +55,7 @@ def backtest_model_portfolio(prices: pd.DataFrame,
55
55
  raise ValueError(f"only single aray is allowed")
56
56
 
57
57
  is_rebalancing = qu.generate_rebalancing_indicators(df=prices,
58
- freq=rebalance_freq,
58
+ freq=rebalancing_freq,
59
59
  include_start_date=is_rebalanced_at_first_date)
60
60
 
61
61
  portfolio_rebalance_dates = is_rebalancing[is_rebalancing == True]
@@ -232,15 +232,15 @@ def run_unit_test(unit_test: UnitTests):
232
232
 
233
233
  portfolio_nav_1_0 = backtest_model_portfolio(prices=prices,
234
234
  weights=np.array([1.0, 0.0]),
235
- rebalance_freq='QE').get_portfolio_nav()
235
+ rebalancing_freq='QE').get_portfolio_nav()
236
236
 
237
237
  portfolio_nav_5_5 = backtest_model_portfolio(prices=prices,
238
238
  weights=np.array([1.0, 0.5]),
239
- rebalance_freq='QE').get_portfolio_nav()
239
+ rebalancing_freq='QE').get_portfolio_nav()
240
240
 
241
241
  portfolio_nav_0_1 = backtest_model_portfolio(prices=prices,
242
242
  weights=np.array([1.0, 1.0]),
243
- rebalance_freq='QE').get_portfolio_nav()
243
+ rebalancing_freq='QE').get_portfolio_nav()
244
244
 
245
245
  portfolio_nav = pd.concat([portfolio_nav_1_0, portfolio_nav_5_5, portfolio_nav_0_1], axis=1)
246
246
  portfolio_nav.columns = ['x1=100, x2=0', 'x1=100, x2=50', 'x1=100, x2=100']
@@ -251,7 +251,7 @@ def run_unit_test(unit_test: UnitTests):
251
251
  elif unit_test == UnitTests.COSTS:
252
252
  portfolio_nav = backtest_model_portfolio(prices=prices,
253
253
  weights=np.array([1.0, 1.0]),
254
- rebalance_freq='QE')
254
+ rebalancing_freq='QE')
255
255
 
256
256
  portfolio_nav.plot_pnl()
257
257
 
@@ -80,12 +80,17 @@ class MultiPortfolioData:
80
80
  ) -> pd.DataFrame:
81
81
  """
82
82
  get portfolio navs
83
+ double check that benchmark is not part of portfolio
83
84
  """
84
85
  navs = self.navs
85
86
  if benchmark is not None:
86
- navs = pd.concat([self.benchmark_prices[benchmark].reindex(index=navs.index).ffill(), navs], axis=1)
87
+ if benchmark not in navs.columns:
88
+ navs = pd.concat([self.benchmark_prices[benchmark].reindex(index=navs.index).ffill(), navs], axis=1)
87
89
  elif add_benchmarks_to_navs:
88
- navs = pd.concat([navs, self.benchmark_prices.reindex(index=navs.index).ffill()], axis=1).ffill()
90
+ benchmarks = self.benchmark_prices.reindex(index=navs.index).ffill()
91
+ for benchmark in benchmarks.columns:
92
+ if benchmark not in navs.columns:
93
+ navs = pd.concat([navs, benchmarks[benchmark]], axis=1)
89
94
 
90
95
  if time_period is not None:
91
96
  navs = time_period.locate(navs)
@@ -442,14 +447,14 @@ class MultiPortfolioData:
442
447
  drop_benchmark = False
443
448
  ra_perf_title = f"RA performance table for {perf_params.freq_vol}-freq returns with beta to {benchmark}: " \
444
449
  f"{qis.get_time_period(prices).to_str()}"
445
-
450
+
446
451
  if add_turnover:
447
452
  turnover = self.get_turnover(time_period=time_period, **kwargs)
448
453
  turnover = turnover.mean(axis=0).to_frame('Turnover')
449
454
  df_to_add = qis.df_to_str(turnover, var_format='{:,.0%}')
450
455
  else:
451
456
  df_to_add = None
452
-
457
+
453
458
  fig, ra_perf_table = ppt.plot_ra_perf_table_benchmark(prices=prices,
454
459
  benchmark=benchmark,
455
460
  perf_params=perf_params,
@@ -499,11 +504,13 @@ class MultiPortfolioData:
499
504
  strategy_prices = pd.concat(strategy_prices, axis=1)
500
505
 
501
506
  benchmark_price = benchmark_price.reindex(index=strategy_prices.index, method='ffill')
507
+ if benchmark_price.name not in strategy_prices.columns:
508
+ prices = pd.concat([benchmark_price, strategy_prices], axis=1)
509
+ else:
510
+ prices = strategy_prices
502
511
  if add_ac: # otherwise tables look too bad
503
512
  ac_prices = pd.concat(ac_prices, axis=1)
504
- prices = pd.concat([benchmark_price, strategy_prices, ac_prices], axis=1)
505
- else:
506
- prices = pd.concat([strategy_prices, benchmark_price], axis=1)
513
+ prices = pd.concat([prices, ac_prices], axis=1)
507
514
 
508
515
  ra_perf_title = f"RA performance table for {perf_params.freq_vol}-freq returns with beta to " \
509
516
  f"{benchmark_price.name}: {qis.get_time_period(prices).to_str()}"
@@ -138,7 +138,8 @@ class PortfolioData:
138
138
  NAV level getters
139
139
  """
140
140
 
141
- def get_portfolio_nav(self, time_period: TimePeriod = None, freq: Optional[str] = None) -> pd.Series:
141
+ def get_portfolio_nav(self, time_period: TimePeriod = None, freq: Optional[str] = None,
142
+ ticker: Optional[str] = None) -> pd.Series:
142
143
  """
143
144
  get nav using consistent function for all return computations
144
145
  """
@@ -148,6 +149,8 @@ class PortfolioData:
148
149
  nav_ = self.nav.copy()
149
150
  if freq is not None:
150
151
  nav_ = nav_.asfreq(freq=freq, method='ffill')
152
+ if ticker is not None:
153
+ nav_ = nav_.rename(ticker)
151
154
  return nav_
152
155
 
153
156
  def get_portfolio_nav_with_benchmark_prices(self,
@@ -1714,7 +1717,7 @@ class PortfolioInput:
1714
1717
  prices: pd.DataFrame = None # mandatory but we set none for enumarators
1715
1718
  allocation_type: AllocationType = AllocationType.FIXED_WEIGHTS
1716
1719
  time_period: TimePeriod = None
1717
- rebalance_freq: str = 'QE'
1720
+ rebalancing_freq: str = 'QE'
1718
1721
  freq_regime: str = 'ME'
1719
1722
  returns_freq: str = 'ME'
1720
1723
  ewm_lambda: float = 0.92
@@ -18,4 +18,3 @@ OUTPUT_PATH:
18
18
 
19
19
  AWS_POSTGRES:
20
20
  ""
21
-
@@ -32,6 +32,8 @@ def float_to_str(x: float,
32
32
  elif isinstance(x, str):
33
33
  return x
34
34
  else:
35
+ if not isinstance(x, float):
36
+ raise ValueError(f"x must ne float not type={type(x)}")
35
37
  return var_format.format(x)
36
38
 
37
39
 
@@ -1,21 +0,0 @@
1
- """
2
- analysis of Oakmark Select Fund Investor shareclass (expense ratio=1%)
3
- """
4
- import qis as qis
5
- from bbg_fetch import fetch_field_timeseries_per_tickers
6
-
7
- # Define benchmark and fetch prices
8
- benchmark = 'SPY'
9
- tickers = {'SPY US Equity': benchmark, 'OAKLX US Equity': 'Oakmark Select Inv'}
10
- prices = fetch_field_timeseries_per_tickers(tickers=tickers, field='PX_LAST', CshAdjNormal=True, freq='B')
11
-
12
- # defined time period for analysis and run factcheet
13
- time_period = qis.TimePeriod('01Nov1996', '10Jan2025')
14
- fig = qis.generate_multi_asset_factsheet(prices=prices, benchmark=benchmark,
15
- time_period=time_period,
16
- **qis.fetch_default_report_kwargs(reporting_frequency=qis.ReportingFrequency.DAILY))
17
-
18
- qis.save_fig(fig=fig, file_name=f"oakmark_report", local_path=qis.local_path.get_output_path())
19
- qis.save_figs_to_pdf(figs=[fig],
20
- file_name=f"oakmark_report", orientation='landscape',
21
- local_path=qis.local_path.get_output_path())
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes