qis 2.0.40__tar.gz → 2.1.40__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 (176) hide show
  1. {qis-2.0.40 → qis-2.1.40}/PKG-INFO +39 -26
  2. {qis-2.0.40 → qis-2.1.40}/README.md +31 -20
  3. {qis-2.0.40 → qis-2.1.40}/pyproject.toml +7 -5
  4. {qis-2.0.40 → qis-2.1.40}/qis/__init__.py +2 -1
  5. qis-2.1.40/qis/examples/bond_futures_portfolio.py +49 -0
  6. {qis-2.0.40 → qis-2.1.40}/qis/examples/bootstrap_analysis.py +3 -3
  7. qis-2.1.40/qis/examples/btc_asset_corr.py +46 -0
  8. {qis-2.0.40 → qis-2.1.40}/qis/examples/constant_notional.py +1 -1
  9. qis-2.1.40/qis/examples/constant_weight_portfolios.py +22 -0
  10. qis-2.1.40/qis/examples/core/perf_bbg_prices.py +83 -0
  11. {qis-2.0.40/qis/examples → qis-2.1.40/qis/examples/core}/price_plots.py +16 -11
  12. qis-2.1.40/qis/examples/core/us_election.py +273 -0
  13. qis-2.1.40/qis/examples/credit_spreads.py +44 -0
  14. qis-2.1.40/qis/examples/credit_trackers.py +42 -0
  15. qis-2.1.40/qis/examples/europe_futures.py +57 -0
  16. qis-2.1.40/qis/examples/factsheets/multi_assets.py +114 -0
  17. {qis-2.0.40 → qis-2.1.40}/qis/examples/factsheets/multi_strategy.py +28 -26
  18. qis-2.1.40/qis/examples/factsheets/pyblogs_reports.py +113 -0
  19. qis-2.1.40/qis/examples/factsheets/strategy.py +190 -0
  20. qis-2.1.40/qis/examples/factsheets/strategy_benchmark.py +155 -0
  21. qis-2.1.40/qis/examples/interpolation_infrequent_returns.py +77 -0
  22. {qis-2.0.40 → qis-2.1.40}/qis/examples/leveraged_strategies.py +7 -10
  23. {qis-2.0.40 → qis-2.1.40}/qis/examples/long_short.py +10 -16
  24. qis-2.1.40/qis/examples/momentum_indices.py +22 -0
  25. qis-2.1.40/qis/examples/oakmark_analysis.py +21 -0
  26. qis-2.1.40/qis/examples/overnight_returns.py +62 -0
  27. qis-2.1.40/qis/examples/perp_pricing.py +56 -0
  28. qis-2.0.40/qis/examples/performances.py → qis-2.1.40/qis/examples/readme_performances.py +11 -11
  29. qis-2.1.40/qis/examples/risk_return_frontier.py +59 -0
  30. qis-2.1.40/qis/examples/rolling_performance.py +41 -0
  31. qis-2.1.40/qis/examples/seasonality.py +24 -0
  32. qis-2.1.40/qis/examples/sharpe_vs_sortino.py +36 -0
  33. {qis-2.0.40 → qis-2.1.40}/qis/examples/simulate_quant_strats.py +6 -6
  34. {qis-2.0.40 → qis-2.1.40}/qis/examples/test_ewm.py +5 -5
  35. {qis-2.0.40 → qis-2.1.40}/qis/examples/try_pybloqs.py +3 -4
  36. {qis-2.0.40 → qis-2.1.40}/qis/examples/universe_corrs.py +0 -1
  37. {qis-2.0.40 → qis-2.1.40}/qis/examples/vix_beta_to_equities_bonds.py +9 -7
  38. qis-2.1.40/qis/examples/vix_conditional_returns.py +71 -0
  39. qis-2.1.40/qis/examples/vix_spy_by_year.py +109 -0
  40. qis-2.1.40/qis/examples/vix_tenor_analysis.py +57 -0
  41. {qis-2.0.40 → qis-2.1.40}/qis/examples/vol_without_weekends.py +5 -3
  42. {qis-2.0.40 → qis-2.1.40}/qis/file_utils.py +33 -32
  43. {qis-2.0.40 → qis-2.1.40}/qis/models/__init__.py +33 -11
  44. qis-2.1.40/qis/models/linear/auto_corr.py +278 -0
  45. {qis-2.0.40 → qis-2.1.40}/qis/models/linear/corr_cov_matrix.py +49 -2
  46. {qis-2.0.40 → qis-2.1.40}/qis/models/linear/ewm.py +244 -145
  47. {qis-2.0.40 → qis-2.1.40}/qis/models/linear/ewm_convolution.py +11 -14
  48. qis-2.1.40/qis/models/linear/ewm_factors.py +315 -0
  49. qis-2.1.40/qis/models/linear/ewm_winsor_outliers.py +372 -0
  50. {qis-2.0.40 → qis-2.1.40}/qis/models/linear/pca.py +23 -13
  51. {qis-2.0.40 → qis-2.1.40}/qis/models/linear/plot_correlations.py +8 -5
  52. {qis-2.0.40 → qis-2.1.40}/qis/models/linear/ra_returns.py +124 -26
  53. {qis-2.0.40 → qis-2.1.40}/qis/models/stats/bootstrap.py +1 -1
  54. qis-2.1.40/qis/models/stats/rolling_stats.py +179 -0
  55. {qis-2.0.40 → qis-2.1.40}/qis/perfstats/__init__.py +8 -5
  56. {qis-2.0.40 → qis-2.1.40}/qis/perfstats/config.py +3 -3
  57. qis-2.1.40/qis/perfstats/fx_ops.py +62 -0
  58. {qis-2.0.40 → qis-2.1.40}/qis/perfstats/perf_stats.py +28 -12
  59. {qis-2.0.40 → qis-2.1.40}/qis/perfstats/regime_classifier.py +30 -27
  60. {qis-2.0.40 → qis-2.1.40}/qis/perfstats/returns.py +45 -51
  61. {qis-2.0.40 → qis-2.1.40}/qis/perfstats/timeseries_bfill.py +105 -18
  62. {qis-2.0.40 → qis-2.1.40}/qis/plots/__init__.py +20 -11
  63. {qis-2.0.40 → qis-2.1.40}/qis/plots/bars.py +74 -33
  64. {qis-2.0.40 → qis-2.1.40}/qis/plots/boxplot.py +34 -22
  65. {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/data_timeseries.py +1 -1
  66. {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/drawdowns.py +23 -10
  67. {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/perf_table.py +99 -46
  68. qis-2.1.40/qis/plots/derived/prices.py +391 -0
  69. {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/regime_data.py +11 -7
  70. {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/returns_heatmap.py +86 -50
  71. {qis-2.0.40 → qis-2.1.40}/qis/plots/errorbar.py +24 -13
  72. {qis-2.0.40 → qis-2.1.40}/qis/plots/heatmap.py +2 -1
  73. {qis-2.0.40 → qis-2.1.40}/qis/plots/histogram.py +10 -5
  74. {qis-2.0.40 → qis-2.1.40}/qis/plots/lineplot.py +4 -0
  75. {qis-2.0.40 → qis-2.1.40}/qis/plots/pie.py +10 -5
  76. qis-2.1.40/qis/plots/reports/econ_data_single.py +200 -0
  77. qis-2.1.40/qis/plots/reports/gantt_data_history.py +183 -0
  78. qis-2.1.40/qis/plots/reports/price_history.py +61 -0
  79. qis-2.1.40/qis/plots/reports/utils.py +66 -0
  80. {qis-2.0.40 → qis-2.1.40}/qis/plots/scatter.py +96 -23
  81. {qis-2.0.40 → qis-2.1.40}/qis/plots/stackplot.py +2 -2
  82. {qis-2.0.40 → qis-2.1.40}/qis/plots/table.py +5 -3
  83. {qis-2.0.40 → qis-2.1.40}/qis/plots/time_series.py +15 -15
  84. {qis-2.0.40 → qis-2.1.40}/qis/plots/utils.py +77 -22
  85. qis-2.1.40/qis/portfolio/__init__.py +46 -0
  86. {qis-2.0.40 → qis-2.1.40}/qis/portfolio/backtester.py +56 -28
  87. qis-2.1.40/qis/portfolio/ewm_portfolio_risk.py +221 -0
  88. qis-2.1.40/qis/portfolio/multi_portfolio_data.py +986 -0
  89. qis-2.1.40/qis/portfolio/portfolio_data.py +1719 -0
  90. qis-2.1.40/qis/portfolio/reports/brinson_attribution.py +185 -0
  91. qis-2.1.40/qis/portfolio/reports/config.py +241 -0
  92. qis-2.1.40/qis/portfolio/reports/multi_assets_factsheet.py +540 -0
  93. qis-2.1.40/qis/portfolio/reports/multi_strategy_factseet_pybloqs.py +363 -0
  94. qis-2.1.40/qis/portfolio/reports/multi_strategy_factsheet.py +213 -0
  95. qis-2.1.40/qis/portfolio/reports/strategy_benchmark_factsheet.py +673 -0
  96. qis-2.1.40/qis/portfolio/reports/strategy_benchmark_factsheet_pybloqs.py +253 -0
  97. qis-2.1.40/qis/portfolio/reports/strategy_factsheet.py +615 -0
  98. qis-2.1.40/qis/portfolio/reports/strategy_signal_factsheet.py +199 -0
  99. qis-2.1.40/qis/portfolio/strats/__init__.py +0 -0
  100. {qis-2.0.40 → qis-2.1.40}/qis/portfolio/strats/quant_strats_delta1.py +5 -5
  101. qis-2.1.40/qis/portfolio/strats/seasonal_strats.py +57 -0
  102. qis-2.1.40/qis/settings.yaml +19 -0
  103. {qis-2.0.40 → qis-2.1.40}/qis/utils/__init__.py +23 -6
  104. {qis-2.0.40 → qis-2.1.40}/qis/utils/dates.py +167 -44
  105. {qis-2.0.40 → qis-2.1.40}/qis/utils/df_agg.py +11 -8
  106. {qis-2.0.40 → qis-2.1.40}/qis/utils/df_freq.py +24 -12
  107. {qis-2.0.40 → qis-2.1.40}/qis/utils/df_groups.py +46 -9
  108. {qis-2.0.40 → qis-2.1.40}/qis/utils/df_melt.py +7 -7
  109. {qis-2.0.40 → qis-2.1.40}/qis/utils/df_ops.py +21 -18
  110. {qis-2.0.40 → qis-2.1.40}/qis/utils/df_str.py +16 -8
  111. qis-2.1.40/qis/utils/df_to_scores.py +83 -0
  112. {qis-2.0.40 → qis-2.1.40}/qis/utils/df_to_weights.py +97 -49
  113. {qis-2.0.40 → qis-2.1.40}/qis/utils/np_ops.py +137 -23
  114. {qis-2.0.40 → qis-2.1.40}/qis/utils/ols.py +63 -13
  115. {qis-2.0.40 → qis-2.1.40}/qis/utils/sampling.py +7 -3
  116. {qis-2.0.40 → qis-2.1.40}/qis/utils/struct_ops.py +5 -1
  117. qis-2.0.40/qis/examples/btc_spy_corr.py +0 -90
  118. qis-2.0.40/qis/examples/factsheets/multi_assets.py +0 -59
  119. qis-2.0.40/qis/examples/factsheets/strategy.py +0 -150
  120. qis-2.0.40/qis/examples/factsheets/strategy_benchmark.py +0 -117
  121. qis-2.0.40/qis/examples/figures/multi_strategy.PNG +0 -0
  122. qis-2.0.40/qis/examples/figures/multiassets.PNG +0 -0
  123. qis-2.0.40/qis/examples/figures/perf1.PNG +0 -0
  124. qis-2.0.40/qis/examples/figures/perf2.PNG +0 -0
  125. qis-2.0.40/qis/examples/figures/perf3.PNG +0 -0
  126. qis-2.0.40/qis/examples/figures/perf4.PNG +0 -0
  127. qis-2.0.40/qis/examples/figures/strategy.PNG +0 -0
  128. qis-2.0.40/qis/examples/figures/strategy_benchmark.PNG +0 -0
  129. qis-2.0.40/qis/examples/test_yf.py +0 -5
  130. qis-2.0.40/qis/examples/vix_spy.py +0 -76
  131. qis-2.0.40/qis/models/linear/auto_corr.py +0 -168
  132. qis-2.0.40/qis/models/linear/ewm_factors.py +0 -189
  133. qis-2.0.40/qis/plots/derived/prices.py +0 -486
  134. qis-2.0.40/qis/portfolio/__init__.py +0 -16
  135. qis-2.0.40/qis/portfolio/multi_portfolio_data.py +0 -550
  136. qis-2.0.40/qis/portfolio/portfolio_data.py +0 -751
  137. qis-2.0.40/qis/portfolio/reports/config.py +0 -66
  138. qis-2.0.40/qis/portfolio/reports/multi_assets_factsheet.py +0 -362
  139. qis-2.0.40/qis/portfolio/reports/multi_strategy_factsheet.py +0 -158
  140. qis-2.0.40/qis/portfolio/reports/strategy_benchmark_factsheet.py +0 -212
  141. qis-2.0.40/qis/portfolio/reports/strategy_factsheet.py +0 -236
  142. qis-2.0.40/qis/settings.yaml +0 -20
  143. {qis-2.0.40 → qis-2.1.40}/LICENSE.txt +0 -0
  144. {qis-2.0.40 → qis-2.1.40}/qis/examples/best_returns.py +0 -0
  145. {qis-2.0.40 → qis-2.1.40}/qis/examples/boxplot_conditional_returns.py +0 -0
  146. {qis-2.0.40 → qis-2.1.40}/qis/examples/generate_option_rolls.py +0 -0
  147. {qis-2.0.40 → qis-2.1.40}/qis/examples/ohlc_vol_analysis.py +0 -0
  148. {qis-2.0.40 → qis-2.1.40}/qis/examples/perf_external_assets.py +0 -0
  149. {qis-2.0.40 → qis-2.1.40}/qis/examples/test_scatter.py +0 -0
  150. {qis-2.0.40 → qis-2.1.40}/qis/local_path.py +0 -0
  151. {qis-2.0.40 → qis-2.1.40}/qis/models/README.md +0 -0
  152. {qis-2.0.40 → qis-2.1.40}/qis/models/linear/__init__.py +0 -0
  153. {qis-2.0.40 → qis-2.1.40}/qis/models/stats/__init__.py +0 -0
  154. {qis-2.0.40 → qis-2.1.40}/qis/models/stats/ohlc_vol.py +0 -0
  155. {qis-2.0.40 → qis-2.1.40}/qis/models/stats/test_bootstrap.py +0 -0
  156. {qis-2.0.40 → qis-2.1.40}/qis/perfstats/README.md +0 -0
  157. {qis-2.0.40 → qis-2.1.40}/qis/perfstats/cond_regression.py +0 -0
  158. {qis-2.0.40 → qis-2.1.40}/qis/perfstats/desc_table.py +0 -0
  159. {qis-2.0.40 → qis-2.1.40}/qis/plots/README.md +0 -0
  160. {qis-2.0.40 → qis-2.1.40}/qis/plots/contour.py +0 -0
  161. {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/__init__.py +0 -0
  162. {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/desc_table.py +0 -0
  163. {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/regime_class_table.py +0 -0
  164. {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/regime_pdf.py +0 -0
  165. {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/regime_scatter.py +0 -0
  166. {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/returns_scatter.py +0 -0
  167. {qis-2.0.40 → qis-2.1.40}/qis/plots/histplot2d.py +0 -0
  168. {qis-2.0.40 → qis-2.1.40}/qis/plots/qqplot.py +0 -0
  169. {qis-2.0.40/qis/portfolio → qis-2.1.40/qis/plots}/reports/__init__.py +0 -0
  170. {qis-2.0.40 → qis-2.1.40}/qis/portfolio/README.md +0 -0
  171. {qis-2.0.40/qis/portfolio/strats → qis-2.1.40/qis/portfolio/reports}/__init__.py +0 -0
  172. {qis-2.0.40 → qis-2.1.40}/qis/sql_engine.py +0 -0
  173. {qis-2.0.40 → qis-2.1.40}/qis/test_data.py +0 -0
  174. {qis-2.0.40 → qis-2.1.40}/qis/utils/README.md +0 -0
  175. {qis-2.0.40 → qis-2.1.40}/qis/utils/df_cut.py +0 -0
  176. {qis-2.0.40 → qis-2.1.40}/qis/utils/generic.py +0 -0
@@ -1,15 +1,14 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: qis
3
- Version: 2.0.40
3
+ Version: 2.1.40
4
4
  Summary: Implementation of visualisation and reporting analytics for Quantitative Investment Strategies
5
- Home-page: https://github.com/ArturSepp/QuantInvestStrats
6
5
  License: LICENSE.txt
7
6
  Keywords: quantitative,investing,portfolio optimization,systematic strategies,volatility
8
7
  Author: Artur Sepp
9
8
  Author-email: artursepp@gmail.com
10
9
  Maintainer: Artur Sepp
11
10
  Maintainer-email: artursepp@gmail.com
12
- Requires-Python: >=3.8,<3.11
11
+ Requires-Python: >=3.8
13
12
  Classifier: Development Status :: 4 - Beta
14
13
  Classifier: Environment :: Console
15
14
  Classifier: Intended Audience :: Financial and Insurance Industry
@@ -22,6 +21,9 @@ Classifier: Programming Language :: Python :: 3
22
21
  Classifier: Programming Language :: Python :: 3.8
23
22
  Classifier: Programming Language :: Python :: 3.9
24
23
  Classifier: Programming Language :: Python :: 3.10
24
+ Classifier: Programming Language :: Python :: 3.11
25
+ Classifier: Programming Language :: Python :: 3.12
26
+ Classifier: Programming Language :: Python :: 3.13
25
27
  Classifier: Programming Language :: Python :: 3 :: Only
26
28
  Classifier: Topic :: Office/Business :: Financial :: Investment
27
29
  Requires-Dist: PyYAML (>=6.0)
@@ -32,14 +34,14 @@ Requires-Dist: matplotlib (>=3.2.2)
32
34
  Requires-Dist: numba (>=0.56.4)
33
35
  Requires-Dist: numpy (>=1.22.4)
34
36
  Requires-Dist: openpyxl (>=3.0.10)
35
- Requires-Dist: pandas (>=1.5.2)
37
+ Requires-Dist: pandas (>=2.2.0)
36
38
  Requires-Dist: psycopg2 (>=2.9.5)
37
39
  Requires-Dist: pyarrow (>=10.0.1)
38
40
  Requires-Dist: scipy (>=1.10)
39
41
  Requires-Dist: seaborn (>=0.12.2)
40
42
  Requires-Dist: statsmodels (>=0.13.5)
41
43
  Requires-Dist: tabulate (>=0.9.0)
42
- Requires-Dist: yfinance (>0.1.38)
44
+ Requires-Dist: yfinance (>=0.1.38)
43
45
  Project-URL: Documentation, https://github.com/ArturSepp/QuantInvestStrats
44
46
  Project-URL: Issues, https://github.com/ArturSepp/QuantInvestStrats/issues
45
47
  Project-URL: Personal website, https://artursepp.com
@@ -53,7 +55,7 @@ qis package implements analytics for visualisation of financial data, performanc
53
55
  reporting, analysis of quantitative strategies.
54
56
 
55
57
  qis package is split into 5 main modules with the
56
- dependecy path increasing sequentially as follows.
58
+ dependency path increasing sequentially as follows.
57
59
 
58
60
  1. ```qis.utils``` is module containing low level utilities for operations with pandas, numpy, and datetimes.
59
61
 
@@ -64,9 +66,14 @@ dependecy path increasing sequentially as follows.
64
66
  4. ```qis.models``` is module containing statistical models including filtering and regressions.
65
67
 
66
68
  5. ```qis.portfolio``` is high level module for analysis, simulation, backtesting, and reporting of quant strategies.
69
+ Function ```backtest_model_portfolio()``` in ```qis.portfolio.backtester.py``` takes instrument prices
70
+ and simulated weights from a generic strategy and compute the total return, performance attribution, and risk analysis
67
71
 
68
72
  ```qis.examples``` contains scripts with illustrations of QIS analytics.
69
73
 
74
+ ```qis.examples.factheets``` contains scripts with examples of factsheets for simulated and actual strategies,
75
+ and cross-sectional analysis of backtests.
76
+
70
77
 
71
78
  # Table of contents
72
79
  1. [Analytics](#analytics)
@@ -83,30 +90,29 @@ dependecy path increasing sequentially as follows.
83
90
  6. [ToDos](#todos)
84
91
  7. [Disclaimer](#disclaimer)
85
92
 
86
- ## **Updates** <a name="updates"></a>
87
93
 
88
94
  ## **Installation** <a name="installation"></a>
89
- install using
95
+ Install using
90
96
  ```python
91
97
  pip install qis
92
98
  ```
93
- upgrade using
99
+ Upgrade using
94
100
  ```python
95
101
  pip install --upgrade qis
96
102
  ```
97
103
 
98
- close using
104
+ Close using
99
105
  ```python
100
106
  git clone https://github.com/ArturSepp/QuantInvestStrats.git
101
107
  ```
102
108
 
103
109
  Core dependencies:
104
- python = ">=3.8,<3.11",
110
+ python = ">=3.8",
105
111
  numba = ">=0.56.4",
106
112
  numpy = ">=1.22.4",
107
113
  scipy = ">=1.10",
108
114
  statsmodels = ">=0.13.5",
109
- pandas = ">=1.5.2",
115
+ pandas = ">=2.2.0",
110
116
  matplotlib = ">=3.2.2",
111
117
  seaborn = ">=0.12.2"
112
118
 
@@ -140,7 +146,7 @@ with sns.axes_style("darkgrid"):
140
146
  ```python
141
147
  # 2-axis plot with drawdowns using sns styles
142
148
  with sns.axes_style("darkgrid"):
143
- fig, axs = plt.subplots(2, 1, figsize=(10, 7))
149
+ fig, axs = plt.subplots(2, 1, figsize=(10, 7), tight_layout=True)
144
150
  qis.plot_prices_with_dd(prices=prices, x_date_freq='YE', axs=axs)
145
151
  ```
146
152
  ![image info](qis/examples/figures/perf2.PNG)
@@ -168,16 +174,16 @@ fig = qis.plot_ra_perf_table(prices=prices,
168
174
  # add benchmark regression using excess returns for linear beta
169
175
  # regression frequency is specified using perf_params.freq_reg
170
176
  # regression alpha is multiplied using perf_params.alpha_an_factor
171
- fig = qis.plot_ra_perf_table_benchmark(prices=prices,
172
- benchmark='SPY',
173
- perf_columns=[PerfStat.TOTAL_RETURN, PerfStat.PA_RETURN, PerfStat.PA_EXCESS_RETURN,
174
- PerfStat.VOL, PerfStat.SHARPE_RF0,
175
- PerfStat.SHARPE_EXCESS, PerfStat.SORTINO_RATIO, PerfStat.CALMAR_RATIO,
176
- PerfStat.MAX_DD, PerfStat.MAX_DD_VOL,
177
- PerfStat.SKEWNESS, PerfStat.KURTOSIS,
178
- PerfStat.ALPHA_AN, PerfStat.BETA, PerfStat.R2],
179
- title=f"Risk-adjusted performance: {qis.get_time_period_label(prices, date_separator='-')} benchmarked with SPY",
180
- perf_params=perf_params)
177
+ fig, _ = qis.plot_ra_perf_table_benchmark(prices=prices,
178
+ benchmark='SPY',
179
+ perf_columns=[PerfStat.TOTAL_RETURN, PerfStat.PA_RETURN, PerfStat.PA_EXCESS_RETURN,
180
+ PerfStat.VOL, PerfStat.SHARPE_RF0,
181
+ PerfStat.SHARPE_EXCESS, PerfStat.SORTINO_RATIO, PerfStat.CALMAR_RATIO,
182
+ PerfStat.MAX_DD, PerfStat.MAX_DD_VOL,
183
+ PerfStat.SKEWNESS, PerfStat.KURTOSIS,
184
+ PerfStat.ALPHA_AN, PerfStat.BETA, PerfStat.R2],
185
+ title=f"Risk-adjusted performance: {qis.get_time_period_label(prices, date_separator='-')} benchmarked with SPY",
186
+ perf_params=perf_params)
181
187
  ```
182
188
  ![image info](qis/examples/figures/perf4.PNG)
183
189
 
@@ -200,8 +206,9 @@ for either backtested or actual strategy
200
206
 
201
207
  Run example in ```qis.examples.factsheets.strategy.py``` https://github.com/ArturSepp/QuantInvestStrats/blob/master/qis/examples/factsheets/strategy.py
202
208
 
203
- ![image info](qis/examples/figures/strategy.PNG)
204
-
209
+ ![image info](qis/examples/figures/strategy1.PNG)
210
+ ![image info](qis/examples/figures/strategy2.PNG)
211
+ ![image info](qis/examples/figures/strategy3.PNG)
205
212
 
206
213
  ### 4. Strategy benchmark factsheet <a name="strategybenchmark"></a>
207
214
  This report is adopted for report performance and marginal comparison
@@ -212,6 +219,9 @@ Run example in ```qis.examples.factsheets.strategy_benchmark.py``` https://githu
212
219
 
213
220
  ![image info](qis/examples/figures/strategy_benchmark.PNG)
214
221
 
222
+ Brinson-Fachler performance attribution (https://en.wikipedia.org/wiki/Performance_attribution)
223
+ ![image info](qis/examples/figures/brinson_attribution.PNG)
224
+
215
225
 
216
226
  ### 5. Multi strategy factsheet <a name="multistrategy"></a>
217
227
  This report is adopted to examine the sensitivity of
@@ -233,6 +243,9 @@ Starting local server
233
243
  jupyter notebook
234
244
  ```
235
245
 
246
+ Examples of using qis analytics jupyter notebooks are located here
247
+ https://github.com/ArturSepp/QuantInvestStrats/blob/master/qis/examples/notebooks
248
+
236
249
 
237
250
  ## **Contributions** <a name="contributions"></a>
238
251
  If you are interested in extending and improving QIS analytics,
@@ -5,7 +5,7 @@ qis package implements analytics for visualisation of financial data, performanc
5
5
  reporting, analysis of quantitative strategies.
6
6
 
7
7
  qis package is split into 5 main modules with the
8
- dependecy path increasing sequentially as follows.
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.
11
11
 
@@ -16,9 +16,14 @@ dependecy path increasing sequentially as follows.
16
16
  4. ```qis.models``` is module containing statistical models including filtering and regressions.
17
17
 
18
18
  5. ```qis.portfolio``` is high level module for analysis, simulation, backtesting, and reporting of quant strategies.
19
+ Function ```backtest_model_portfolio()``` in ```qis.portfolio.backtester.py``` takes instrument prices
20
+ and simulated weights from a generic strategy and compute the total return, performance attribution, and risk analysis
19
21
 
20
22
  ```qis.examples``` contains scripts with illustrations of QIS analytics.
21
23
 
24
+ ```qis.examples.factheets``` contains scripts with examples of factsheets for simulated and actual strategies,
25
+ and cross-sectional analysis of backtests.
26
+
22
27
 
23
28
  # Table of contents
24
29
  1. [Analytics](#analytics)
@@ -35,30 +40,29 @@ dependecy path increasing sequentially as follows.
35
40
  6. [ToDos](#todos)
36
41
  7. [Disclaimer](#disclaimer)
37
42
 
38
- ## **Updates** <a name="updates"></a>
39
43
 
40
44
  ## **Installation** <a name="installation"></a>
41
- install using
45
+ Install using
42
46
  ```python
43
47
  pip install qis
44
48
  ```
45
- upgrade using
49
+ Upgrade using
46
50
  ```python
47
51
  pip install --upgrade qis
48
52
  ```
49
53
 
50
- close using
54
+ Close using
51
55
  ```python
52
56
  git clone https://github.com/ArturSepp/QuantInvestStrats.git
53
57
  ```
54
58
 
55
59
  Core dependencies:
56
- python = ">=3.8,<3.11",
60
+ python = ">=3.8",
57
61
  numba = ">=0.56.4",
58
62
  numpy = ">=1.22.4",
59
63
  scipy = ">=1.10",
60
64
  statsmodels = ">=0.13.5",
61
- pandas = ">=1.5.2",
65
+ pandas = ">=2.2.0",
62
66
  matplotlib = ">=3.2.2",
63
67
  seaborn = ">=0.12.2"
64
68
 
@@ -92,7 +96,7 @@ with sns.axes_style("darkgrid"):
92
96
  ```python
93
97
  # 2-axis plot with drawdowns using sns styles
94
98
  with sns.axes_style("darkgrid"):
95
- fig, axs = plt.subplots(2, 1, figsize=(10, 7))
99
+ fig, axs = plt.subplots(2, 1, figsize=(10, 7), tight_layout=True)
96
100
  qis.plot_prices_with_dd(prices=prices, x_date_freq='YE', axs=axs)
97
101
  ```
98
102
  ![image info](qis/examples/figures/perf2.PNG)
@@ -120,16 +124,16 @@ fig = qis.plot_ra_perf_table(prices=prices,
120
124
  # add benchmark regression using excess returns for linear beta
121
125
  # regression frequency is specified using perf_params.freq_reg
122
126
  # regression alpha is multiplied using perf_params.alpha_an_factor
123
- fig = qis.plot_ra_perf_table_benchmark(prices=prices,
124
- benchmark='SPY',
125
- perf_columns=[PerfStat.TOTAL_RETURN, PerfStat.PA_RETURN, PerfStat.PA_EXCESS_RETURN,
126
- PerfStat.VOL, PerfStat.SHARPE_RF0,
127
- PerfStat.SHARPE_EXCESS, PerfStat.SORTINO_RATIO, PerfStat.CALMAR_RATIO,
128
- PerfStat.MAX_DD, PerfStat.MAX_DD_VOL,
129
- PerfStat.SKEWNESS, PerfStat.KURTOSIS,
130
- PerfStat.ALPHA_AN, PerfStat.BETA, PerfStat.R2],
131
- title=f"Risk-adjusted performance: {qis.get_time_period_label(prices, date_separator='-')} benchmarked with SPY",
132
- perf_params=perf_params)
127
+ fig, _ = qis.plot_ra_perf_table_benchmark(prices=prices,
128
+ benchmark='SPY',
129
+ perf_columns=[PerfStat.TOTAL_RETURN, PerfStat.PA_RETURN, PerfStat.PA_EXCESS_RETURN,
130
+ PerfStat.VOL, PerfStat.SHARPE_RF0,
131
+ PerfStat.SHARPE_EXCESS, PerfStat.SORTINO_RATIO, PerfStat.CALMAR_RATIO,
132
+ PerfStat.MAX_DD, PerfStat.MAX_DD_VOL,
133
+ PerfStat.SKEWNESS, PerfStat.KURTOSIS,
134
+ PerfStat.ALPHA_AN, PerfStat.BETA, PerfStat.R2],
135
+ title=f"Risk-adjusted performance: {qis.get_time_period_label(prices, date_separator='-')} benchmarked with SPY",
136
+ perf_params=perf_params)
133
137
  ```
134
138
  ![image info](qis/examples/figures/perf4.PNG)
135
139
 
@@ -152,8 +156,9 @@ for either backtested or actual strategy
152
156
 
153
157
  Run example in ```qis.examples.factsheets.strategy.py``` https://github.com/ArturSepp/QuantInvestStrats/blob/master/qis/examples/factsheets/strategy.py
154
158
 
155
- ![image info](qis/examples/figures/strategy.PNG)
156
-
159
+ ![image info](qis/examples/figures/strategy1.PNG)
160
+ ![image info](qis/examples/figures/strategy2.PNG)
161
+ ![image info](qis/examples/figures/strategy3.PNG)
157
162
 
158
163
  ### 4. Strategy benchmark factsheet <a name="strategybenchmark"></a>
159
164
  This report is adopted for report performance and marginal comparison
@@ -164,6 +169,9 @@ Run example in ```qis.examples.factsheets.strategy_benchmark.py``` https://githu
164
169
 
165
170
  ![image info](qis/examples/figures/strategy_benchmark.PNG)
166
171
 
172
+ Brinson-Fachler performance attribution (https://en.wikipedia.org/wiki/Performance_attribution)
173
+ ![image info](qis/examples/figures/brinson_attribution.PNG)
174
+
167
175
 
168
176
  ### 5. Multi strategy factsheet <a name="multistrategy"></a>
169
177
  This report is adopted to examine the sensitivity of
@@ -185,6 +193,9 @@ Starting local server
185
193
  jupyter notebook
186
194
  ```
187
195
 
196
+ Examples of using qis analytics jupyter notebooks are located here
197
+ https://github.com/ArturSepp/QuantInvestStrats/blob/master/qis/examples/notebooks
198
+
188
199
 
189
200
  ## **Contributions** <a name="contributions"></a>
190
201
  If you are interested in extending and improving QIS analytics,
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "qis"
3
- version = "2.0.40"
3
+ version = "2.1.40"
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>"]
@@ -23,19 +23,21 @@ classifiers=[
23
23
  "Programming Language :: Python :: 3 :: Only",
24
24
  "Topic :: Office/Business :: Financial :: Investment",
25
25
  ]
26
- packages = [ {include = "qis"} ]
26
+ packages = [ {include = "qis"}]
27
+ exclude = ["qis/examples/figures/",
28
+ "qis/examples/notebooks/"]
27
29
 
28
30
  [tool.poetry.urls]
29
31
  "Issues" = "https://github.com/ArturSepp/QuantInvestStrats/issues"
30
32
  "Personal website" = "https://artursepp.com"
31
33
 
32
34
  [tool.poetry.dependencies]
33
- python = ">=3.8,<3.11"
35
+ python = ">=3.8"
34
36
  numba = ">=0.56.4"
35
37
  numpy = ">=1.22.4"
36
38
  scipy = ">=1.10"
37
39
  statsmodels = ">=0.13.5"
38
- pandas = ">=1.5.2"
40
+ pandas = ">=2.2.0"
39
41
  matplotlib = ">=3.2.2"
40
42
  seaborn = ">=0.12.2"
41
43
  openpyxl = ">=3.0.10"
@@ -46,7 +48,7 @@ psycopg2 = ">=2.9.5"
46
48
  SQLAlchemy = ">=1.4.46"
47
49
  pyarrow = ">=10.0.1"
48
50
  fsspec = ">=2022.11.0"
49
- yfinance = "> 0.1.38"
51
+ yfinance = ">=0.1.38"
50
52
 
51
53
  [build-system]
52
54
  requires = ["poetry-core>=1.0.0"]
@@ -9,7 +9,6 @@ from qis.file_utils import (
9
9
  get_all_folder_files,
10
10
  get_local_file_path,
11
11
  get_output_path,
12
- get_param_file_path,
13
12
  get_paths,
14
13
  get_pdf_path,
15
14
  get_resource_path,
@@ -46,3 +45,5 @@ from qis.plots.__init__ import *
46
45
  from qis.models.__init__ import *
47
46
 
48
47
  from qis.portfolio.__init__ import *
48
+
49
+
@@ -0,0 +1,49 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ from enum import Enum
5
+ import qis as qis
6
+
7
+ import quant_strats.local_path as lp
8
+
9
+ output_path = lp.OUTPUT_PATH
10
+ local_path = lp.LOCAL_PATH
11
+ # quant_strats
12
+ from quant_strats import (compute_strategy_portfolio_data_with_costs,
13
+ compute_multi_strategy_data_from_blocks,
14
+ compute_marginal_perfs_for_strategy)
15
+ from quant_strats.data.universes.futures.bbg_futures import Universes
16
+ from quant_strats.data.set.assets_bbg import AssetsBBG
17
+ from quant_strats.research_strategies.futures.cross_trend.csft_strategies import (CSTF_RB_TRACKER,
18
+ CSTF_EXACT_TRACKER,
19
+ CSTF_RB_AC_TRACKER)
20
+ from quant_strats.research_strategies.futures.trackers.run_trackers_backtest import (CSTF_GOLDMAN_TRACKER_AC,
21
+ GOLDMAN_UNIVERSE)
22
+
23
+ strategy_universe = Universes.BBG_FUTURES_INVESTABLE.load_universe_data(local_path=local_path)
24
+ prices = strategy_universe.get_prices(freq='B')[['UXY1 Comdty', 'US1 Comdty', 'WN1 Comdty']].ffill().dropna()
25
+ prices = prices[prices.columns[::-1]]
26
+ qis.plot_prices_with_dd(prices=prices)
27
+
28
+ portfolio_data = qis.backtest_model_portfolio(prices=prices,
29
+ weights=np.array([-1.0, 2.0, -1.0]),
30
+ rebalance_freq='ME',
31
+ ticker='Butterly'
32
+ )
33
+
34
+ time_period = qis.TimePeriod('31Dec2015', None)
35
+ figs = qis.generate_strategy_factsheet(portfolio_data=portfolio_data,
36
+ benchmark_prices=prices.iloc[:, -1],
37
+ is_grouped=False,
38
+ add_current_position_var_risk_sheet=False,
39
+ add_weights_turnover_sheet=False,
40
+ add_grouped_exposures=False,
41
+ add_grouped_cum_pnl=False,
42
+ time_period=time_period,
43
+ **qis.fetch_default_report_kwargs(time_period=time_period,
44
+ add_rates_data=False))
45
+
46
+ qis.save_figs_to_pdf(figs=figs, file_name=f"butterfly", orientation='landscape',
47
+ local_path=output_path)
48
+
49
+ plt.show()
@@ -63,7 +63,7 @@ def plot_bootsrap_paths(prices: pd.Series,
63
63
  ax=ax,
64
64
  **kwargs)
65
65
 
66
- acfs, m_acf, std_acf = qis.estimate_path_acf(log_returns, is_pacf=True, nlags=nlags)
66
+ acfs, m_acf, std_acf = qis.estimate_acf_from_paths(log_returns, is_pacf=True, nlags=nlags)
67
67
  with sns.axes_style("darkgrid"):
68
68
  fig, ax = plt.subplots(1, 1, figsize=(10, 7))
69
69
  qis.set_suptitle(fig, title=f"Auto-correlation of returns of realized (red) and bootsrapped paths (grey)")
@@ -73,7 +73,7 @@ def plot_bootsrap_paths(prices: pd.Series,
73
73
  ax=ax,
74
74
  **kwargs)
75
75
 
76
- acfs, m_acf, std_acf = qis.estimate_path_acf(log_returns2, is_pacf=True, nlags=nlags)
76
+ acfs, m_acf, std_acf = qis.estimate_acf_from_paths(log_returns2, is_pacf=True, nlags=nlags)
77
77
  with sns.axes_style("darkgrid"):
78
78
  fig, ax = plt.subplots(1, 1, figsize=(10, 7))
79
79
  qis.set_suptitle(fig, title=f"Auto-correlation of squared returns of realized (red) and bootsrapped paths (grey)")
@@ -129,7 +129,7 @@ def plot_autocorr_in_block_size(prices: pd.Series,
129
129
  prices1 = pd.concat([bootstrap_prices, prices], axis=1)
130
130
  log_returns = qis.to_returns(prices1, is_log_returns=True, is_first_zero=True)
131
131
  log_returns2 = np.square(log_returns)
132
- acfs, m_acf, std_acf = qis.estimate_path_acf(log_returns2, is_pacf=True, nlags=nlags)
132
+ acfs, m_acf, std_acf = qis.estimate_acf_from_paths(log_returns2, is_pacf=True, nlags=nlags)
133
133
 
134
134
  with sns.axes_style("darkgrid"):
135
135
  fig, ax = plt.subplots(1, 1, figsize=(14, 10))
@@ -0,0 +1,46 @@
1
+ """
2
+ compute rolling correlations between crypto and asset
3
+ """
4
+ import pandas as pd
5
+ import numpy as np
6
+ import matplotlib.pyplot as plt
7
+ import seaborn as sns
8
+ import qis as qis
9
+ import yfinance as yf
10
+
11
+ # define asset and cryptocurrency
12
+ ASSET = 'QQQ'
13
+ CRYPTO = 'BTC-USD'
14
+ tickers = [ASSET, CRYPTO]
15
+ # fetch yahoo data
16
+ prices = yf.download(tickers, start=None, end=None)['Adj Close'][tickers]
17
+ # resample to business days
18
+ prices = prices.asfreq('B', method='ffill').dropna()
19
+ # % returns
20
+ returns = prices.pct_change()
21
+ # returns = np.log(prices).diff()
22
+
23
+ # compute rolling correlations
24
+ corr_3m = returns[CRYPTO].rolling(63).corr(returns[ASSET]).rename('3m')
25
+ corr_1y = returns[CRYPTO].rolling(252).corr(returns[ASSET]).rename('1y')
26
+ corr_2y = returns[CRYPTO].rolling(2*252).corr(returns[ASSET]).rename('2y')
27
+ corrs = pd.concat([corr_3m, corr_1y, corr_2y], axis=1).dropna()
28
+
29
+ # select period
30
+ time_period = qis.TimePeriod('01Jan2016', None)
31
+ corrs = time_period.locate(corrs)
32
+ # qis.save_df_to_excel(data=corrs, file_name='btc_spy_corr')
33
+
34
+ with sns.axes_style("darkgrid"):
35
+ fig, ax = plt.subplots(1, 1, figsize=(15, 8), tight_layout=True)
36
+
37
+ qis.plot_time_series(df=corrs,
38
+ trend_line=qis.TrendLine.ZERO_SHADOWS,
39
+ legend_stats=qis.LegendStats.AVG_LAST_SCORE,
40
+ var_format='{:.0%}',
41
+ fontsize=14,
42
+ title=f"Rolling correlation of daily returns between {CRYPTO} and {ASSET} as function of rolling window",
43
+ ax=ax)
44
+ # qis.save_fig(fig=fig, file_name='btc_all_corr')
45
+
46
+ plt.show()
@@ -70,7 +70,7 @@ with sns.axes_style("darkgrid"):
70
70
 
71
71
  # plot performance
72
72
  qis.plot_prices_with_dd(prices=prices,
73
- performance_label=qis.PerformanceLabel.ARITHMETIC,
73
+ perf_stats_labels=qis.PerfStatsLabels.TOTAL.value,
74
74
  title=f"Realized performance of strategies with short exposure to {ticker}",
75
75
  axs=axs)
76
76
 
@@ -0,0 +1,22 @@
1
+
2
+ import pandas as pd
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+ import seaborn as sns
6
+ import qis as qis
7
+ import yfinance as yf
8
+
9
+ tickers_weights = dict(SPY=0.6, IEF=0.4)
10
+ tickers = list(tickers_weights.keys())
11
+ prices = yf.download(tickers, start=None, end=None)['Adj Close'][tickers]
12
+ prices = prices.asfreq('B', method='ffill').dropna()
13
+
14
+ balanced_60_40a = qis.backtest_model_portfolio(prices=prices, weights=tickers_weights, rebalance_freq='QE',
15
+ ticker='Zero Cost').get_portfolio_nav()
16
+ balanced_60_40b = qis.backtest_model_portfolio(prices=prices, weights=tickers_weights, rebalance_freq='QE',
17
+ ticker='2% Cost',
18
+ management_fee=0.02).get_portfolio_nav()
19
+ navs = pd.concat([balanced_60_40a, balanced_60_40b], axis=1)
20
+ qis.plot_prices_with_dd(prices=navs)
21
+
22
+ plt.show()
@@ -0,0 +1,83 @@
1
+ # packages
2
+ import matplotlib.pyplot as plt
3
+ import qis as qis
4
+ from enum import Enum
5
+ from bbg_fetch import fetch_field_timeseries_per_tickers
6
+
7
+
8
+ def run_report():
9
+ # tickers = {'TY1 Comdty': '10y', 'UXY1 Comdty': '10y Ultra'}
10
+ tickers = {
11
+ 'SPTR Index': 'SPTR Index',
12
+ 'CIEQVEHG Index': 'Citi SPX 0D Vol Carry',
13
+ 'CIEQVRUG Index': 'Citi SX5E 1W Vol Carry',
14
+ 'CICXCOSE Index': 'Citi Brent Vol Carry',
15
+ 'GSISXC07 Index': 'GS Multi Asset Carry',
16
+ 'GSISXC11 Index': 'GS Macro Carry',
17
+ 'XUBSPGRA Index': 'UBS Gold Strangles',
18
+ 'XUBSU1D1 Index': 'UBS Short Vol Daily',
19
+ 'BCKTARU2 Index': 'BNP Call on Short-vol Carry',
20
+ 'BNPXAUUS Index': 'BNP Intraday SPX Vol Carry',
21
+ 'BNPXAUTS Index': 'BNP Intraday NDX Vol Carry',
22
+ 'BNPXOV3U Index': 'BNP 3M Long DHhedged Puts'
23
+ }
24
+
25
+ prices = fetch_field_timeseries_per_tickers(tickers=tickers, freq='B', field='PX_LAST').ffill()
26
+ print(prices)
27
+ qis.save_df_to_csv(df=prices, file_name='qis_vol_indices', local_path=qis.get_output_path())
28
+
29
+ time_period = qis.TimePeriod('31Dec2019', '15Nov2024')
30
+ kwargs = qis.fetch_default_report_kwargs(time_period=time_period, add_rates_data=False)
31
+
32
+ fig = qis.generate_multi_asset_factsheet(prices=prices,
33
+ benchmark='SPTR Index',
34
+ time_period=time_period,
35
+ **kwargs)
36
+ qis.save_figs_to_pdf(figs=[fig],
37
+ file_name=f"bbg_multiasset_report", orientation='landscape',
38
+ # local_path='C://Users//uarts//outputs//',
39
+ local_path=qis.get_output_path()
40
+ )
41
+
42
+
43
+ def run_price():
44
+ tickers = {'CL1 Comdty': 'WTI'}
45
+ prices = fetch_field_timeseries_per_tickers(tickers=tickers, freq='B', field='PX_LAST').ffill()
46
+ print(prices)
47
+
48
+ time_period = qis.TimePeriod('31Dec1989', '08Nov2024')
49
+ prices = time_period.locate(prices)
50
+
51
+ qis.plot_prices_with_dd(prices,
52
+ start_to_one=False)
53
+
54
+
55
+ class UnitTests(Enum):
56
+ REPORT = 1
57
+ PRICE = 2
58
+
59
+
60
+ def run_unit_test(unit_test: UnitTests):
61
+
62
+ if unit_test == UnitTests.REPORT:
63
+ run_report()
64
+
65
+ elif unit_test == UnitTests.PRICE:
66
+ run_price()
67
+
68
+ plt.show()
69
+
70
+
71
+ if __name__ == '__main__':
72
+
73
+ unit_test = UnitTests.PRICE
74
+
75
+ is_run_all_tests = False
76
+ if is_run_all_tests:
77
+ for unit_test in UnitTests:
78
+ run_unit_test(unit_test=unit_test)
79
+ else:
80
+ run_unit_test(unit_test=unit_test)
81
+
82
+
83
+ plt.show()