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.
- {qis-2.0.40 → qis-2.1.40}/PKG-INFO +39 -26
- {qis-2.0.40 → qis-2.1.40}/README.md +31 -20
- {qis-2.0.40 → qis-2.1.40}/pyproject.toml +7 -5
- {qis-2.0.40 → qis-2.1.40}/qis/__init__.py +2 -1
- qis-2.1.40/qis/examples/bond_futures_portfolio.py +49 -0
- {qis-2.0.40 → qis-2.1.40}/qis/examples/bootstrap_analysis.py +3 -3
- qis-2.1.40/qis/examples/btc_asset_corr.py +46 -0
- {qis-2.0.40 → qis-2.1.40}/qis/examples/constant_notional.py +1 -1
- qis-2.1.40/qis/examples/constant_weight_portfolios.py +22 -0
- qis-2.1.40/qis/examples/core/perf_bbg_prices.py +83 -0
- {qis-2.0.40/qis/examples → qis-2.1.40/qis/examples/core}/price_plots.py +16 -11
- qis-2.1.40/qis/examples/core/us_election.py +273 -0
- qis-2.1.40/qis/examples/credit_spreads.py +44 -0
- qis-2.1.40/qis/examples/credit_trackers.py +42 -0
- qis-2.1.40/qis/examples/europe_futures.py +57 -0
- qis-2.1.40/qis/examples/factsheets/multi_assets.py +114 -0
- {qis-2.0.40 → qis-2.1.40}/qis/examples/factsheets/multi_strategy.py +28 -26
- qis-2.1.40/qis/examples/factsheets/pyblogs_reports.py +113 -0
- qis-2.1.40/qis/examples/factsheets/strategy.py +190 -0
- qis-2.1.40/qis/examples/factsheets/strategy_benchmark.py +155 -0
- qis-2.1.40/qis/examples/interpolation_infrequent_returns.py +77 -0
- {qis-2.0.40 → qis-2.1.40}/qis/examples/leveraged_strategies.py +7 -10
- {qis-2.0.40 → qis-2.1.40}/qis/examples/long_short.py +10 -16
- qis-2.1.40/qis/examples/momentum_indices.py +22 -0
- qis-2.1.40/qis/examples/oakmark_analysis.py +21 -0
- qis-2.1.40/qis/examples/overnight_returns.py +62 -0
- qis-2.1.40/qis/examples/perp_pricing.py +56 -0
- qis-2.0.40/qis/examples/performances.py → qis-2.1.40/qis/examples/readme_performances.py +11 -11
- qis-2.1.40/qis/examples/risk_return_frontier.py +59 -0
- qis-2.1.40/qis/examples/rolling_performance.py +41 -0
- qis-2.1.40/qis/examples/seasonality.py +24 -0
- qis-2.1.40/qis/examples/sharpe_vs_sortino.py +36 -0
- {qis-2.0.40 → qis-2.1.40}/qis/examples/simulate_quant_strats.py +6 -6
- {qis-2.0.40 → qis-2.1.40}/qis/examples/test_ewm.py +5 -5
- {qis-2.0.40 → qis-2.1.40}/qis/examples/try_pybloqs.py +3 -4
- {qis-2.0.40 → qis-2.1.40}/qis/examples/universe_corrs.py +0 -1
- {qis-2.0.40 → qis-2.1.40}/qis/examples/vix_beta_to_equities_bonds.py +9 -7
- qis-2.1.40/qis/examples/vix_conditional_returns.py +71 -0
- qis-2.1.40/qis/examples/vix_spy_by_year.py +109 -0
- qis-2.1.40/qis/examples/vix_tenor_analysis.py +57 -0
- {qis-2.0.40 → qis-2.1.40}/qis/examples/vol_without_weekends.py +5 -3
- {qis-2.0.40 → qis-2.1.40}/qis/file_utils.py +33 -32
- {qis-2.0.40 → qis-2.1.40}/qis/models/__init__.py +33 -11
- qis-2.1.40/qis/models/linear/auto_corr.py +278 -0
- {qis-2.0.40 → qis-2.1.40}/qis/models/linear/corr_cov_matrix.py +49 -2
- {qis-2.0.40 → qis-2.1.40}/qis/models/linear/ewm.py +244 -145
- {qis-2.0.40 → qis-2.1.40}/qis/models/linear/ewm_convolution.py +11 -14
- qis-2.1.40/qis/models/linear/ewm_factors.py +315 -0
- qis-2.1.40/qis/models/linear/ewm_winsor_outliers.py +372 -0
- {qis-2.0.40 → qis-2.1.40}/qis/models/linear/pca.py +23 -13
- {qis-2.0.40 → qis-2.1.40}/qis/models/linear/plot_correlations.py +8 -5
- {qis-2.0.40 → qis-2.1.40}/qis/models/linear/ra_returns.py +124 -26
- {qis-2.0.40 → qis-2.1.40}/qis/models/stats/bootstrap.py +1 -1
- qis-2.1.40/qis/models/stats/rolling_stats.py +179 -0
- {qis-2.0.40 → qis-2.1.40}/qis/perfstats/__init__.py +8 -5
- {qis-2.0.40 → qis-2.1.40}/qis/perfstats/config.py +3 -3
- qis-2.1.40/qis/perfstats/fx_ops.py +62 -0
- {qis-2.0.40 → qis-2.1.40}/qis/perfstats/perf_stats.py +28 -12
- {qis-2.0.40 → qis-2.1.40}/qis/perfstats/regime_classifier.py +30 -27
- {qis-2.0.40 → qis-2.1.40}/qis/perfstats/returns.py +45 -51
- {qis-2.0.40 → qis-2.1.40}/qis/perfstats/timeseries_bfill.py +105 -18
- {qis-2.0.40 → qis-2.1.40}/qis/plots/__init__.py +20 -11
- {qis-2.0.40 → qis-2.1.40}/qis/plots/bars.py +74 -33
- {qis-2.0.40 → qis-2.1.40}/qis/plots/boxplot.py +34 -22
- {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/data_timeseries.py +1 -1
- {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/drawdowns.py +23 -10
- {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/perf_table.py +99 -46
- qis-2.1.40/qis/plots/derived/prices.py +391 -0
- {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/regime_data.py +11 -7
- {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/returns_heatmap.py +86 -50
- {qis-2.0.40 → qis-2.1.40}/qis/plots/errorbar.py +24 -13
- {qis-2.0.40 → qis-2.1.40}/qis/plots/heatmap.py +2 -1
- {qis-2.0.40 → qis-2.1.40}/qis/plots/histogram.py +10 -5
- {qis-2.0.40 → qis-2.1.40}/qis/plots/lineplot.py +4 -0
- {qis-2.0.40 → qis-2.1.40}/qis/plots/pie.py +10 -5
- qis-2.1.40/qis/plots/reports/econ_data_single.py +200 -0
- qis-2.1.40/qis/plots/reports/gantt_data_history.py +183 -0
- qis-2.1.40/qis/plots/reports/price_history.py +61 -0
- qis-2.1.40/qis/plots/reports/utils.py +66 -0
- {qis-2.0.40 → qis-2.1.40}/qis/plots/scatter.py +96 -23
- {qis-2.0.40 → qis-2.1.40}/qis/plots/stackplot.py +2 -2
- {qis-2.0.40 → qis-2.1.40}/qis/plots/table.py +5 -3
- {qis-2.0.40 → qis-2.1.40}/qis/plots/time_series.py +15 -15
- {qis-2.0.40 → qis-2.1.40}/qis/plots/utils.py +77 -22
- qis-2.1.40/qis/portfolio/__init__.py +46 -0
- {qis-2.0.40 → qis-2.1.40}/qis/portfolio/backtester.py +56 -28
- qis-2.1.40/qis/portfolio/ewm_portfolio_risk.py +221 -0
- qis-2.1.40/qis/portfolio/multi_portfolio_data.py +986 -0
- qis-2.1.40/qis/portfolio/portfolio_data.py +1719 -0
- qis-2.1.40/qis/portfolio/reports/brinson_attribution.py +185 -0
- qis-2.1.40/qis/portfolio/reports/config.py +241 -0
- qis-2.1.40/qis/portfolio/reports/multi_assets_factsheet.py +540 -0
- qis-2.1.40/qis/portfolio/reports/multi_strategy_factseet_pybloqs.py +363 -0
- qis-2.1.40/qis/portfolio/reports/multi_strategy_factsheet.py +213 -0
- qis-2.1.40/qis/portfolio/reports/strategy_benchmark_factsheet.py +673 -0
- qis-2.1.40/qis/portfolio/reports/strategy_benchmark_factsheet_pybloqs.py +253 -0
- qis-2.1.40/qis/portfolio/reports/strategy_factsheet.py +615 -0
- qis-2.1.40/qis/portfolio/reports/strategy_signal_factsheet.py +199 -0
- qis-2.1.40/qis/portfolio/strats/__init__.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/portfolio/strats/quant_strats_delta1.py +5 -5
- qis-2.1.40/qis/portfolio/strats/seasonal_strats.py +57 -0
- qis-2.1.40/qis/settings.yaml +19 -0
- {qis-2.0.40 → qis-2.1.40}/qis/utils/__init__.py +23 -6
- {qis-2.0.40 → qis-2.1.40}/qis/utils/dates.py +167 -44
- {qis-2.0.40 → qis-2.1.40}/qis/utils/df_agg.py +11 -8
- {qis-2.0.40 → qis-2.1.40}/qis/utils/df_freq.py +24 -12
- {qis-2.0.40 → qis-2.1.40}/qis/utils/df_groups.py +46 -9
- {qis-2.0.40 → qis-2.1.40}/qis/utils/df_melt.py +7 -7
- {qis-2.0.40 → qis-2.1.40}/qis/utils/df_ops.py +21 -18
- {qis-2.0.40 → qis-2.1.40}/qis/utils/df_str.py +16 -8
- qis-2.1.40/qis/utils/df_to_scores.py +83 -0
- {qis-2.0.40 → qis-2.1.40}/qis/utils/df_to_weights.py +97 -49
- {qis-2.0.40 → qis-2.1.40}/qis/utils/np_ops.py +137 -23
- {qis-2.0.40 → qis-2.1.40}/qis/utils/ols.py +63 -13
- {qis-2.0.40 → qis-2.1.40}/qis/utils/sampling.py +7 -3
- {qis-2.0.40 → qis-2.1.40}/qis/utils/struct_ops.py +5 -1
- qis-2.0.40/qis/examples/btc_spy_corr.py +0 -90
- qis-2.0.40/qis/examples/factsheets/multi_assets.py +0 -59
- qis-2.0.40/qis/examples/factsheets/strategy.py +0 -150
- qis-2.0.40/qis/examples/factsheets/strategy_benchmark.py +0 -117
- qis-2.0.40/qis/examples/figures/multi_strategy.PNG +0 -0
- qis-2.0.40/qis/examples/figures/multiassets.PNG +0 -0
- qis-2.0.40/qis/examples/figures/perf1.PNG +0 -0
- qis-2.0.40/qis/examples/figures/perf2.PNG +0 -0
- qis-2.0.40/qis/examples/figures/perf3.PNG +0 -0
- qis-2.0.40/qis/examples/figures/perf4.PNG +0 -0
- qis-2.0.40/qis/examples/figures/strategy.PNG +0 -0
- qis-2.0.40/qis/examples/figures/strategy_benchmark.PNG +0 -0
- qis-2.0.40/qis/examples/test_yf.py +0 -5
- qis-2.0.40/qis/examples/vix_spy.py +0 -76
- qis-2.0.40/qis/models/linear/auto_corr.py +0 -168
- qis-2.0.40/qis/models/linear/ewm_factors.py +0 -189
- qis-2.0.40/qis/plots/derived/prices.py +0 -486
- qis-2.0.40/qis/portfolio/__init__.py +0 -16
- qis-2.0.40/qis/portfolio/multi_portfolio_data.py +0 -550
- qis-2.0.40/qis/portfolio/portfolio_data.py +0 -751
- qis-2.0.40/qis/portfolio/reports/config.py +0 -66
- qis-2.0.40/qis/portfolio/reports/multi_assets_factsheet.py +0 -362
- qis-2.0.40/qis/portfolio/reports/multi_strategy_factsheet.py +0 -158
- qis-2.0.40/qis/portfolio/reports/strategy_benchmark_factsheet.py +0 -212
- qis-2.0.40/qis/portfolio/reports/strategy_factsheet.py +0 -236
- qis-2.0.40/qis/settings.yaml +0 -20
- {qis-2.0.40 → qis-2.1.40}/LICENSE.txt +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/examples/best_returns.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/examples/boxplot_conditional_returns.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/examples/generate_option_rolls.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/examples/ohlc_vol_analysis.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/examples/perf_external_assets.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/examples/test_scatter.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/local_path.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/models/README.md +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/models/linear/__init__.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/models/stats/__init__.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/models/stats/ohlc_vol.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/models/stats/test_bootstrap.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/perfstats/README.md +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/perfstats/cond_regression.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/perfstats/desc_table.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/plots/README.md +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/plots/contour.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/__init__.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/desc_table.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/regime_class_table.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/regime_pdf.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/regime_scatter.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/plots/derived/returns_scatter.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/plots/histplot2d.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/plots/qqplot.py +0 -0
- {qis-2.0.40/qis/portfolio → qis-2.1.40/qis/plots}/reports/__init__.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/portfolio/README.md +0 -0
- {qis-2.0.40/qis/portfolio/strats → qis-2.1.40/qis/portfolio/reports}/__init__.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/sql_engine.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/test_data.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/utils/README.md +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/utils/df_cut.py +0 -0
- {qis-2.0.40 → qis-2.1.40}/qis/utils/generic.py +0 -0
@@ -1,15 +1,14 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.3
|
2
2
|
Name: qis
|
3
|
-
Version: 2.
|
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
|
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 (>=
|
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 (
|
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
|
-
|
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
|
-
|
95
|
+
Install using
|
90
96
|
```python
|
91
97
|
pip install qis
|
92
98
|
```
|
93
|
-
|
99
|
+
Upgrade using
|
94
100
|
```python
|
95
101
|
pip install --upgrade qis
|
96
102
|
```
|
97
103
|
|
98
|
-
|
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
|
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 = ">=
|
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
|

|
@@ -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
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
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
|

|
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
|
-

|
210
|
+

|
211
|
+

|
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
|

|
214
221
|
|
222
|
+
Brinson-Fachler performance attribution (https://en.wikipedia.org/wiki/Performance_attribution)
|
223
|
+

|
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
|
-
|
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
|
-
|
45
|
+
Install using
|
42
46
|
```python
|
43
47
|
pip install qis
|
44
48
|
```
|
45
|
-
|
49
|
+
Upgrade using
|
46
50
|
```python
|
47
51
|
pip install --upgrade qis
|
48
52
|
```
|
49
53
|
|
50
|
-
|
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
|
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 = ">=
|
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
|

|
@@ -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
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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
|

|
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
|
-

|
160
|
+

|
161
|
+

|
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
|

|
166
171
|
|
172
|
+
Brinson-Fachler performance attribution (https://en.wikipedia.org/wiki/Performance_attribution)
|
173
|
+

|
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.
|
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
|
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 = ">=
|
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 = "
|
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.
|
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.
|
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.
|
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
|
-
|
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()
|