squarequant 0.2.3__tar.gz → 0.4.0.dev1__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 (46) hide show
  1. squarequant-0.4.0.dev1/CHANGELOG.md +21 -0
  2. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/LICENSE +20 -20
  3. squarequant-0.4.0.dev1/MANIFEST.in +8 -0
  4. squarequant-0.4.0.dev1/PKG-INFO +114 -0
  5. squarequant-0.4.0.dev1/README.md +72 -0
  6. squarequant-0.4.0.dev1/docs/api.md +16 -0
  7. squarequant-0.4.0.dev1/docs/audit.md +67 -0
  8. squarequant-0.4.0.dev1/docs/conf.py +8 -0
  9. squarequant-0.4.0.dev1/docs/index.md +9 -0
  10. squarequant-0.4.0.dev1/docs/usage.md +201 -0
  11. squarequant-0.4.0.dev1/examples/risk_metrics_demo.py +63 -0
  12. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/pyproject.toml +76 -58
  13. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant/__init__.py +60 -60
  14. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant/api.py +927 -893
  15. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant/constants.py +42 -43
  16. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant/core/__init__.py +6 -6
  17. squarequant-0.4.0.dev1/squarequant/core/base.py +132 -0
  18. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant/core/metrics.py +1113 -1254
  19. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant/data/__init__.py +6 -6
  20. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant/data/data.py +245 -258
  21. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant/monte_carlo/__init__.py +6 -6
  22. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant/monte_carlo/montecarlo.py +489 -568
  23. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant/utils/__init__.py +6 -6
  24. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant/utils/helpers.py +91 -85
  25. squarequant-0.4.0.dev1/squarequant/utils/validation.py +28 -0
  26. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant/var/__init__.py +6 -6
  27. squarequant-0.4.0.dev1/squarequant/var/valueatrisk.py +183 -0
  28. squarequant-0.4.0.dev1/squarequant.egg-info/PKG-INFO +114 -0
  29. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant.egg-info/SOURCES.txt +16 -2
  30. squarequant-0.4.0.dev1/squarequant.egg-info/requires.txt +21 -0
  31. squarequant-0.4.0.dev1/tests/conftest.py +11 -0
  32. squarequant-0.4.0.dev1/tests/test_data_adapters.py +89 -0
  33. squarequant-0.4.0.dev1/tests/test_example.py +12 -0
  34. squarequant-0.4.0.dev1/tests/test_numerics.py +210 -0
  35. squarequant-0.4.0.dev1/tests/test_public_api.py +117 -0
  36. squarequant-0.4.0.dev1/tests/test_regressions.py +177 -0
  37. squarequant-0.2.3/PKG-INFO +0 -267
  38. squarequant-0.2.3/README.md +0 -215
  39. squarequant-0.2.3/setup.py +0 -66
  40. squarequant-0.2.3/squarequant/core/base.py +0 -171
  41. squarequant-0.2.3/squarequant/var/valueatrisk.py +0 -495
  42. squarequant-0.2.3/squarequant.egg-info/PKG-INFO +0 -267
  43. squarequant-0.2.3/squarequant.egg-info/requires.txt +0 -21
  44. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/setup.cfg +0 -0
  45. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant.egg-info/dependency_links.txt +0 -0
  46. {squarequant-0.2.3 → squarequant-0.4.0.dev1}/squarequant.egg-info/top_level.txt +0 -0
@@ -0,0 +1,21 @@
1
+ # Changes
2
+
3
+ ## 0.4.0.dev1
4
+
5
+ This edition retains the original risk statistics, VaR, simulations, data adapters
6
+ and utilities. Contract definitions, trade capture, persistence, payment
7
+ schedules, valuation/scenario layers, their tests, examples, benchmarks and
8
+ associated documentation have been removed from the source distribution.
9
+
10
+ Retained numerical corrections and new consistency/validation improvements are
11
+ described in `docs/audit.md`.
12
+ The portfolio VaR column remains part of the statistical API.
13
+
14
+ The package requires Python 3.11+. The function-level default VaR horizon remains
15
+ 10 observations; users should pass their chosen horizon explicitly. Supplied
16
+ risk-free rates remain daily unless the annual convention is selected.
17
+ Delta-gamma VaR requires an explicit Hessian and retains the documented
18
+ moment-matching approximation. Invalid numerical/grid inputs now fail clearly.
19
+
20
+ Data-provider dependencies remain optional. Complete install/build/API checks
21
+ and their actual outcomes are recorded in `VALIDATION_REPORT.md`.
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Gabriel Bosch
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Gabriel Bosch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
@@ -0,0 +1,8 @@
1
+ recursive-include tests *.py
2
+ recursive-include docs *.md *.py
3
+ prune docs/_build
4
+ include examples/risk_metrics_demo.py
5
+ include requirements-validation-py312.txt
6
+ include VALIDATION_REPORT.md
7
+ include CHANGELOG.md
8
+ include START_HERE.txt
@@ -0,0 +1,114 @@
1
+ Metadata-Version: 2.4
2
+ Name: squarequant
3
+ Version: 0.4.0.dev1
4
+ Summary: Financial risk metrics, Monte Carlo simulations, and optional market-data adapters
5
+ Author-email: Gabriel Bosch <contact@squarequant.org>
6
+ License-Expression: MIT
7
+ Project-URL: Documentation, https://www.squarequant.org
8
+ Project-URL: Bug Tracker, https://github.com/SquareQuant/squarequant-package/issues
9
+ Project-URL: Homepage, https://github.com/SquareQuant/squarequant-package
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Topic :: Office/Business :: Financial :: Investment
16
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
17
+ Classifier: Intended Audience :: Financial and Insurance Industry
18
+ Classifier: Intended Audience :: Science/Research
19
+ Classifier: Development Status :: 4 - Beta
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: pandas>=2.0
24
+ Requires-Dist: numpy>=1.24
25
+ Requires-Dist: scipy>=1.10
26
+ Provides-Extra: data
27
+ Requires-Dist: yfinance>=0.2.0; extra == "data"
28
+ Requires-Dist: requests>=2.25.0; extra == "data"
29
+ Provides-Extra: optimization
30
+ Requires-Dist: cvxpy>=1.0.0; extra == "optimization"
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest>=8.0; extra == "dev"
33
+ Requires-Dist: ruff>=0.11; extra == "dev"
34
+ Requires-Dist: mypy>=1.15; extra == "dev"
35
+ Requires-Dist: hypothesis>=6; extra == "dev"
36
+ Requires-Dist: build>=1; extra == "dev"
37
+ Requires-Dist: requests>=2.25.0; extra == "dev"
38
+ Requires-Dist: sphinx>=4.0.0; extra == "dev"
39
+ Requires-Dist: myst-parser>=1.0.0; extra == "dev"
40
+ Requires-Dist: pytest-cov>=2.12.0; extra == "dev"
41
+ Dynamic: license-file
42
+
43
+ # SquareQuant — financial risk statistics
44
+
45
+ Version `0.4.0.dev1` provides rolling financial risk metrics, historical/parametric
46
+ VaR, Monte Carlo simulations, and optional market-data downloads. Python 3.11+
47
+ is required.
48
+
49
+ ## Install and run
50
+
51
+ ```bash
52
+ python -m venv .venv
53
+ ```
54
+
55
+ On Windows:
56
+
57
+ ```powershell
58
+ .\.venv\Scripts\python.exe -m pip install -e ".[dev]"
59
+ .\.venv\Scripts\python.exe -m pytest -q
60
+ .\.venv\Scripts\python.exe examples\risk_metrics_demo.py
61
+ ```
62
+
63
+ On macOS/Linux:
64
+
65
+ ```bash
66
+ .venv/bin/python -m pip install -e '.[dev]'
67
+ .venv/bin/python -m pytest -q
68
+ .venv/bin/python examples/risk_metrics_demo.py
69
+ ```
70
+
71
+ ## Example
72
+
73
+ ```python
74
+ import pandas as pd
75
+ import squarequant as sq
76
+
77
+ returns = pd.DataFrame({'Asset': [-0.04, -0.02, 0.01, 0.02]})
78
+ var = sq.var(returns, ['Asset'], window=4, holding_period=1)
79
+ es = sq.cvar(returns, ['Asset'], window=4, holding_period=1, confidence=0.625)
80
+ print(var['Portfolio'].iloc[-1]) # 0.037
81
+ print(es['Asset'].iloc[-1]) # 0.03333333333333333
82
+ ```
83
+
84
+ The public API contains `DownloadConfig`, `download_tickers`, `sharpe`, `sortino`,
85
+ `vol`, `mdd`, `calmar`, `var`, `cvar`, `semidev`, `avgdd`, `ulcer`, `mad`, `erm`,
86
+ `evar`, `cdar`, `simplemc`, `brownian`, `correlated_brownian`, `gbm_paths`, and
87
+ `gbm_correlated_paths`.
88
+
89
+ The `data` extra installs yfinance/requests for optional downloads. It is not
90
+ needed to import SquareQuant or run the offline statistics example. The
91
+ `optimization` extra is retained for installation compatibility.
92
+
93
+ ## Documentation and validation
94
+
95
+ - [Usage, assumptions and numerical examples](docs/usage.md)
96
+ - [Public API reference](docs/api.md)
97
+ - [Audit and corrections](docs/audit.md)
98
+ - [Release changes](CHANGELOG.md)
99
+ - [Executed verification results](VALIDATION_REPORT.md)
100
+
101
+ ```bash
102
+ python -m ruff check squarequant tests examples/risk_metrics_demo.py
103
+ python -m ruff format --check squarequant tests examples/risk_metrics_demo.py
104
+ python -m mypy --no-incremental
105
+ python -m sphinx -W -b html docs docs/_build/html
106
+ python -m build
107
+ ```
108
+
109
+ CI is configured for Python 3.11/3.12/3.13. Actual local validation versions and
110
+ results are recorded in `VALIDATION_REPORT.md`. Legacy `examples/*_test.py` files
111
+ are vendor/plotting scripts; the isolated offline suite is under `tests/`.
112
+ Those legacy scripts require optional provider packages and matplotlib.
113
+
114
+ Code is licensed under MIT. See [CONTRIBUTING.md](CONTRIBUTING.md).
@@ -0,0 +1,72 @@
1
+ # SquareQuant — financial risk statistics
2
+
3
+ Version `0.4.0.dev1` provides rolling financial risk metrics, historical/parametric
4
+ VaR, Monte Carlo simulations, and optional market-data downloads. Python 3.11+
5
+ is required.
6
+
7
+ ## Install and run
8
+
9
+ ```bash
10
+ python -m venv .venv
11
+ ```
12
+
13
+ On Windows:
14
+
15
+ ```powershell
16
+ .\.venv\Scripts\python.exe -m pip install -e ".[dev]"
17
+ .\.venv\Scripts\python.exe -m pytest -q
18
+ .\.venv\Scripts\python.exe examples\risk_metrics_demo.py
19
+ ```
20
+
21
+ On macOS/Linux:
22
+
23
+ ```bash
24
+ .venv/bin/python -m pip install -e '.[dev]'
25
+ .venv/bin/python -m pytest -q
26
+ .venv/bin/python examples/risk_metrics_demo.py
27
+ ```
28
+
29
+ ## Example
30
+
31
+ ```python
32
+ import pandas as pd
33
+ import squarequant as sq
34
+
35
+ returns = pd.DataFrame({'Asset': [-0.04, -0.02, 0.01, 0.02]})
36
+ var = sq.var(returns, ['Asset'], window=4, holding_period=1)
37
+ es = sq.cvar(returns, ['Asset'], window=4, holding_period=1, confidence=0.625)
38
+ print(var['Portfolio'].iloc[-1]) # 0.037
39
+ print(es['Asset'].iloc[-1]) # 0.03333333333333333
40
+ ```
41
+
42
+ The public API contains `DownloadConfig`, `download_tickers`, `sharpe`, `sortino`,
43
+ `vol`, `mdd`, `calmar`, `var`, `cvar`, `semidev`, `avgdd`, `ulcer`, `mad`, `erm`,
44
+ `evar`, `cdar`, `simplemc`, `brownian`, `correlated_brownian`, `gbm_paths`, and
45
+ `gbm_correlated_paths`.
46
+
47
+ The `data` extra installs yfinance/requests for optional downloads. It is not
48
+ needed to import SquareQuant or run the offline statistics example. The
49
+ `optimization` extra is retained for installation compatibility.
50
+
51
+ ## Documentation and validation
52
+
53
+ - [Usage, assumptions and numerical examples](docs/usage.md)
54
+ - [Public API reference](docs/api.md)
55
+ - [Audit and corrections](docs/audit.md)
56
+ - [Release changes](CHANGELOG.md)
57
+ - [Executed verification results](VALIDATION_REPORT.md)
58
+
59
+ ```bash
60
+ python -m ruff check squarequant tests examples/risk_metrics_demo.py
61
+ python -m ruff format --check squarequant tests examples/risk_metrics_demo.py
62
+ python -m mypy --no-incremental
63
+ python -m sphinx -W -b html docs docs/_build/html
64
+ python -m build
65
+ ```
66
+
67
+ CI is configured for Python 3.11/3.12/3.13. Actual local validation versions and
68
+ results are recorded in `VALIDATION_REPORT.md`. Legacy `examples/*_test.py` files
69
+ are vendor/plotting scripts; the isolated offline suite is under `tests/`.
70
+ Those legacy scripts require optional provider packages and matplotlib.
71
+
72
+ Code is licensed under MIT. See [CONTRIBUTING.md](CONTRIBUTING.md).
@@ -0,0 +1,16 @@
1
+ # Public API reference
2
+
3
+ ## Risk statistics and simulations
4
+
5
+ ```{automodule} squarequant.api
6
+ :members:
7
+ ```
8
+
9
+ ## Optional data configuration and retrieval
10
+
11
+ ```{autoclass} squarequant.data.data.DownloadConfig
12
+ :members:
13
+ ```
14
+
15
+ ```{autofunction} squarequant.data.data.download_tickers
16
+ ```
@@ -0,0 +1,67 @@
1
+ # Statistics audit and corrections
2
+
3
+ This source edition contains the financial statistics, Monte Carlo and optional
4
+ data-download components. The upstream audit baseline is commit
5
+ `024363059e7af6a23e0e95167a6294b5d449e33a`.
6
+
7
+ ## Retained corrections
8
+
9
+ | Component | Confirmed issue | Correction |
10
+ |---|---|---|
11
+ | Drawdowns | A boolean was compared with the string `absolute`; double rolling leaked an earlier peak outside the window; return inputs omitted initial losses. | Explicit return conventions and a within-window path with an initial wealth baseline. |
12
+ | Shared preprocessing | Whole-frame missing-value removal discarded unrelated valid observations; forward filling manufactured zero price returns. | Process selected columns, retain dates/per-asset missing values, use price conversion without imputation. |
13
+ | Risk-free rates | The risk-free column could be price-converted; daily versus annual inputs were implicit. | Preserve the rate series; add explicit annual effective conversion while retaining daily explicit-input defaults. |
14
+ | Delta-gamma VaR | Squared returns/correlations did not identify instrument price curvature, and the expected gamma term was added to volatility. | Explicit supplied portfolio Hessian, Gaussian quadratic P&L moments and covariance-level horizon scaling. |
15
+ | Historical VaR | Multiple-period results relied on square-root scaling. | Preserve/document the approximation and add overlapping empirical horizon returns. |
16
+ | Calmar | Log annual returns and simple annual returns were inconsistent. | Convert annualized log returns with `expm1`. |
17
+ | CVaR/ES | Tail counts were rounded up; parametric mean was scaled with square-root time. | Fractional empirical tail mass; separate mean/volatility horizon scaling. |
18
+ | Entropic statistics | The fixed-z formula mixed z and its reciprocal, could overflow and normal EVaR ignored the horizon. | Stable log-sum-exp bound with documented legacy naming; normal EVaR horizon scaling. |
19
+ | Monte Carlo | Default multi-factor GBM reused one factor; fitted log drift was presented as price drift; parameter checks were incomplete. | Correct default correlation dimension, half-variance drift correction and validation. |
20
+ | Data/packaging | Date defaults were fixed at import; single-ticker MultiIndexes were mishandled; optional provider imports blocked numerical use; Python/version metadata disagreed. | Per-instance defaults, response normalization, lazy provider imports and one version source with an explicit Python baseline. |
21
+
22
+ ## Additional corrections in 0.4.0.dev1
23
+
24
+ - Calmar now calculates both numerator and drawdown denominator from the same
25
+ return window. Equivalent price and return inputs are regression-tested.
26
+ - Relative/log drawdowns use log-wealth differences to avoid overflowing a long
27
+ compounded wealth path; a -100% simple return remains a valid total loss.
28
+ - Confidence, z, grids and holding periods reject non-finite values, booleans and
29
+ inappropriate non-integer counts. NumPy integer counts are accepted.
30
+ - VaR aligns labelled Series weights by asset name and retains zero-net
31
+ exposures. Invalid/missing gamma labels have explicit errors. The reserved
32
+ aggregate result label cannot overwrite an input factor.
33
+ - Nullable pandas numeric data are supported; complex observations are rejected.
34
+ - Monte Carlo instance methods delegate to their static implementations,
35
+ reducing duplicate code and making API equivalence directly testable.
36
+ - Date helpers enforce ISO format and date ordering. Downloaded symbols retain
37
+ requested order; `Count` is accepted, `Adj Close` is retrieved coherently, and
38
+ intraday requests are rejected by the EOD-only adapter.
39
+ - Optional annotations, public VaR documentation, supported-method constants,
40
+ fixtures, build manifests and CI targets are aligned with the retained API.
41
+ Static checks cover all package source modules rather than a selected subset.
42
+
43
+ ## Validation approach and remaining assumptions
44
+
45
+ The suite exercises every public export with offline inputs. It includes
46
+ analytical VaR/ES/ratio examples, price-versus-return consistency, drawdown
47
+ properties, simulation moments/correlations, seeded API equivalence, bad-input
48
+ checks, and provider-response fixtures. A network guard forbids outbound
49
+ connections. Provider tests use deterministic doubles rather than credentials or
50
+ live subscriptions. The complete example is an executable numerical regression.
51
+
52
+ Delta-gamma VaR uses Gaussian moment matching, so it is an approximate quantile
53
+ of generally non-Gaussian quadratic P&L. Delta-normal uses a zero-mean convention.
54
+ Square-root historical risk scaling is an approximation. The `erm` legacy name
55
+ refers to a fixed-z tail bound; it is not an optimized empirical EVaR or the
56
+ confidence-independent entropic risk measure. The assumptions and units are
57
+ specified in [usage.md](usage.md).
58
+
59
+ Minimum observations and window sizes remain metric-specific. Strict rolling
60
+ drawdown calculation costs O(observations × window × assets), while historical
61
+ quantiles and standard moments use pandas rolling methods. No universal
62
+ performance claim is made for arbitrary data sizes.
63
+
64
+ Python 3.11/3.12/3.13 are configured in CI. Executed interpreter/dependency
65
+ versions, test counts, coverage, build and installed-artifact results are
66
+ recorded in the accompanying validation report. CI configuration is distinct
67
+ from having executed every interpreter locally.
@@ -0,0 +1,8 @@
1
+ project = "SquareQuant"
2
+ author = "SquareQuant contributors"
3
+ extensions = ["myst_parser", "sphinx.ext.autodoc"]
4
+ source_suffix = {".md": "markdown"}
5
+ master_doc = "index"
6
+ exclude_patterns = ["_build"]
7
+ html_theme = "alabaster"
8
+ myst_enable_extensions = ["colon_fence"]
@@ -0,0 +1,9 @@
1
+ # SquareQuant financial statistics
2
+
3
+ ```{toctree}
4
+ :maxdepth: 2
5
+
6
+ usage
7
+ api
8
+ audit
9
+ ```
@@ -0,0 +1,201 @@
1
+ # Using SquareQuant
2
+
3
+ ## Inputs and output conventions
4
+
5
+ Risk functions accept a pandas DataFrame and an explicit sequence of asset column
6
+ names. Inputs must be real numeric observations with a unique, increasing index.
7
+ Missing observations are retained per asset; unrelated columns are not processed.
8
+ A requested missing asset produces a warning and a NaN output column. Date filters
9
+ require a DatetimeIndex. Calculations return DataFrames indexed by observation
10
+ date; output columns retain the requested names. VaR adds a `Portfolio` column,
11
+ so that name is reserved as an input asset name for VaR.
12
+
13
+ Set `use_returns=True` for returns/P&L and `False` for prices. Return conventions:
14
+
15
+ | `returns_type` | Price conversion | Multi-period aggregation |
16
+ |---|---|---|
17
+ | `relative` | `P_t / P_(t-1) - 1` | Compound `(1+r)` |
18
+ | `log` | `log(P_t/P_(t-1))`, requiring positive prices | Sum |
19
+ | `absolute` | `P_t-P_(t-1)` | Sum |
20
+
21
+ Relative price returns use no forward filling. A missing price leaves adjacent
22
+ returns unknown. Nullable pandas numeric columns are supported. Use integer
23
+ observation windows/horizons; booleans, fractions, NaN and infinite counts/risk
24
+ parameters are rejected where applicable. Annualization uses 252 observations
25
+ per year. Change data frequency before interpreting annualized outputs.
26
+
27
+ ## Core metrics
28
+
29
+ ```python
30
+ import pandas as pd
31
+ import squarequant as sq
32
+
33
+ returns = pd.DataFrame({'A': [-0.04, -0.02, 0.01, 0.02]})
34
+ vol = sq.vol(returns, ['A'], window=4)
35
+ sharpe = sq.sharpe(returns, ['A'], window=4, freerate_value=0)
36
+ var = sq.var(returns, ['A'], window=4, holding_period=1)
37
+ es = sq.cvar(returns, ['A'], window=4, holding_period=1, confidence=0.625)
38
+ assert abs(var['Portfolio'].iloc[-1] - 0.037) < 1e-12
39
+ assert abs(es['A'].iloc[-1] - 1/30) < 1e-12
40
+ ```
41
+
42
+ Volatility uses sample standard deviation and square-root annualization. Sharpe
43
+ uses annualized mean excess return divided by annualized standard deviation.
44
+ Sortino/downside deviation count all observations in the denominator and set
45
+ positive excess returns to zero. Semi-deviation's default target is estimated
46
+ inside each rolling window; an explicit target stays constant.
47
+
48
+ Explicit `freerate_value` or `freerate` columns are **daily** by default. Select
49
+ `risk_free_period='annual'` for annual effective rates. Relative returns subtract
50
+ `(1+r_annual)^(1/252)-1`; log returns subtract `log(1+r_annual)/252`. A risk-free
51
+ column is not price-converted. With no explicit risk-free input, the default is
52
+ 2% annual effective. Absolute P&L needs a compatible daily currency risk-free
53
+ amount, such as an explicitly supplied zero, rather than an annual percentage.
54
+
55
+ Drawdowns are calculated entirely within the rolling window. Return inputs
56
+ include initial wealth of one (zero for absolute P&L), so a first-period loss is
57
+ captured. Relative/log drawdowns use stable log-wealth differences. MDD and
58
+ average drawdown are nonpositive; ulcer and CDaR are nonnegative. CDaR is the
59
+ conditional mean of observed drawdowns at/below the empirical lower-tail
60
+ quantile, with observations tied at the threshold included.
61
+
62
+ A price-input drawdown window contains that many price observations. Calmar
63
+ instead uses a window of converted returns for both its annualized numerator and
64
+ its maximum drawdown denominator, requiring the preceding price observation.
65
+ Relative and log inputs yield equivalent Calmar ratios. Absolute inputs define
66
+ annualized P&L divided by absolute-currency maximum drawdown. Zero drawdown or
67
+ zero standard/downside deviation yields NaN where a ratio is undefined.
68
+
69
+ MAD/downside square-root annualization is a retained daily-return convention;
70
+ it is not a distribution-free identity for multi-period P&L.
71
+
72
+ ## VaR and expected shortfall
73
+
74
+ VaR is reported in input return/P&L units; positive numbers denote losses. A VaR
75
+ quantile may be negative when the relevant outcome is a gain; it is not floored
76
+ at zero. The package-level `sq.var` default horizon is **10 observations**, while
77
+ `ValueAtRisk` directly defaults to **1**. Specify `holding_period` explicitly in
78
+ research code.
79
+
80
+ - `historical`: negative empirical lower-tail return quantile.
81
+ - `delta-normal` (`parametric` alias): zero-mean Gaussian linear approximation.
82
+ - `delta-gamma-normal`: supplied-Hessian quadratic P&L with moment-matched normal
83
+ quantiles.
84
+
85
+ VaR supports `sqrt_time`, `linear`, `none`, and historical `overlapping` scaling.
86
+ `sqrt_time` is a modelling approximation and warns when applied to historical
87
+ VaR for multiple periods. `overlapping` first constructs the observed horizon
88
+ returns, then uses the requested rolling number of those horizon observations.
89
+ Adjacent horizon observations overlap and are dependent. No false independence
90
+ assumption is needed to calculate their empirical quantile.
91
+
92
+ Weights may be a sequence, dictionary or labelled pandas Series. Series are
93
+ aligned to asset labels; positive-net weights normalize to sum to one. Zero-net
94
+ or negative-net vectors are retained as supplied. Missing active-factor values
95
+ make that portfolio observation unknown; a zero-weight factor does not remove
96
+ it. For log/absolute inputs, aggregation is a linear factor combination, not a
97
+ claim about an exact discrete buy-and-hold wealth process.
98
+
99
+ For zero-mean Gaussian factors and quadratic P&L:
100
+
101
+ ```{math}
102
+ X\sim N(0,\Sigma),\quad P=w^\top X+\tfrac12 X^\top G X,
103
+ ```
104
+
105
+ ```{math}
106
+ m=\tfrac12\operatorname{tr}(G\Sigma),\quad
107
+ v=w^\top\Sigma w+\tfrac12\operatorname{tr}(G\Sigma G\Sigma),\quad
108
+ \operatorname{VaR}_\alpha\approx z_\alpha\sqrt{v}-m.
109
+ ```
110
+
111
+ `gamma_matrix` must be finite, symmetric, labelled/dimensioned for the selected
112
+ factors, in the same input units and already position-weighted. It cannot be
113
+ inferred from squared historical factor returns. Per-asset delta-gamma columns
114
+ are unit-delta single-factor diagnostics using the supplied `G_ii`; the
115
+ `Portfolio` result uses the full weights and Hessian. These columns are not an
116
+ additive risk allocation. The quadratic P&L generally has a non-Gaussian
117
+ distribution, so this is an approximation. Under default horizon scaling, use
118
+ `Sigma_h=h*Sigma`; gamma effects are calculated after this covariance change.
119
+ `linear` scales factor shocks by h and therefore covariance by h squared.
120
+
121
+ The old `market_regime_threshold` and `vol_scaling_factor` arguments remain for
122
+ call compatibility. They do not modify the result and nondefault values emit a
123
+ deprecation warning. Missing gamma emits a deprecation warning and raises an
124
+ error when delta-gamma is selected.
125
+
126
+ Historical CVaR/ES integrates the fractional empirical tail mass, avoiding a
127
+ rounded-up count bias. Parametric ES/normal EVaR scale the mean by h and volatility
128
+ by square-root h (or by h under linear scaling). Historical ES square-root
129
+ scaling remains an approximation. The legacy `erm` function is a fixed-z
130
+ Chernoff tail bound `(log mean exp(-z*R) - log(1-confidence))/z`; it is not the
131
+ confidence-independent conventional entropic risk measure. `evar` implements
132
+ normal-distribution EVaR. No optimized empirical EVaR or alternate distribution
133
+ calibration is implied.
134
+
135
+ ## Simulations and historical GBM fitting
136
+
137
+ ```python
138
+ import numpy as np
139
+ from squarequant.monte_carlo.montecarlo import MonteCarloSimulator
140
+ import squarequant as sq
141
+
142
+ w = sq.brownian(T=1, N=32, nsims=100, seed=42)
143
+ gbm = sq.gbm_paths(S0=100, mu=0.05, sigma=0.2, N=32, nsims=100, seed=42)
144
+ correlated = sq.gbm_correlated_paths(
145
+ np.array([100., 100.]), np.array([0.05, 0.03]), np.array([0.2, 0.15]),
146
+ N=32, nsims=100, seed=42, correlation_matrix=np.array([[1., 0.5], [0.5, 1.]]),
147
+ )
148
+ ```
149
+
150
+ Paths include time zero. N is the number of grid points, so `dt=T/(N-1)`.
151
+ Correlation matrices must be finite, symmetric, positive definite and have unit
152
+ diagonals. The default multi-asset correlation is the identity matrix. Instance
153
+ and static entry points share the same implementations and preserve seeded
154
+ reproducibility. Zero volatility gives deterministic exponential growth.
155
+
156
+ `MonteCarloSimulator.fit_params` estimates historical GBM parameters from one
157
+ positive price series using population-variance MLE:
158
+ `sigma=std(log_returns, ddof=0)/sqrt(dt)` and
159
+ `mu=mean(log_returns)/dt + sigma**2/2`. This estimates the drift of price under
160
+ the historical sampling measure; it does not impose a risk-neutral drift or
161
+ perform a change of measure. A user may supply another drift to the path
162
+ simulator for another specified modelling measure.
163
+
164
+ `simplemc` reports the sample mean, unbiased sample variance and an interval
165
+ formed using the selected distribution's quantile times the estimated standard
166
+ error. The normal interval is an asymptotic approximation with estimated
167
+ variance; the t option uses the supplied degrees of freedom. The Laplace option
168
+ is a distribution-quantile heuristic, not an exact finite-sample confidence
169
+ interval for a mean. Invalid samples and degrees of freedom are rejected.
170
+
171
+ ## Optional data downloads
172
+
173
+ Install `python -m pip install -e '.[data]'` from source. Both a ticker string and
174
+ a list of unique, nonempty ticker strings are accepted. Results use names such
175
+ as `AAPL_Close`.
176
+
177
+ The yfinance adapter handles single-level columns and both ticker/field and
178
+ field/ticker MultiIndexes. It requests adjusted data by default; explicitly
179
+ requesting `Adj Close` disables automatic adjustment so the named adjusted-close
180
+ field can be retrieved. If `Close` is requested alongside `Adj Close`, `Close`
181
+ is then the unadjusted field. Missing symbols/fields are logged; reconcile your
182
+ requested output when completeness is required.
183
+
184
+ The Theta adapter targets the existing v2 EOD endpoint and supports `interval='1d'`
185
+ only; it requires a compatible local Theta terminal. It normalizes fields,
186
+ including `Count`, and keeps requested ticker order even with parallel fetching.
187
+ Partial failures are logged and successful symbols remain available. Live
188
+ terminal compatibility, subscriptions and option-specific request parameters
189
+ are outside the offline adapter checks; no live-vendor validation is claimed.
190
+
191
+ `DownloadConfig` date defaults are evaluated per instance. Explicit dates require
192
+ zero-padded ISO format and ordered endpoints. Provider dependencies are lazy
193
+ imports, so the numerical API remains usable without them.
194
+
195
+ ## Executable example
196
+
197
+ `python examples/risk_metrics_demo.py` uses no network or credentials. Its JSON
198
+ output exercises all metric groups and all simulation wrappers. Key expected
199
+ values are historical VaR `0.037`, 62.5% historical ES `0.03333333333333333`,
200
+ sample mean `2.5`, Brownian shape `[3, 8]`, and correlated Brownian/GBM shapes
201
+ `[2, 3, 8]`.
@@ -0,0 +1,63 @@
1
+ """Deterministic, offline demonstration of every statistics/simulation API group.
2
+
3
+ Install from the project root with: python -m pip install -e '.[dev]'
4
+ Then run: python examples/risk_metrics_demo.py
5
+ """
6
+
7
+ import json
8
+
9
+ import numpy as np
10
+ import pandas as pd
11
+
12
+ import squarequant as sq
13
+
14
+
15
+ def run() -> dict:
16
+ returns = pd.DataFrame({"A": [-0.04, -0.02, 0.01, 0.02], "B": [0.01, -0.01, 0.03, -0.02]})
17
+ result = {}
18
+ for name in [
19
+ "sharpe",
20
+ "sortino",
21
+ "vol",
22
+ "mdd",
23
+ "calmar",
24
+ "cvar",
25
+ "semidev",
26
+ "avgdd",
27
+ "ulcer",
28
+ "mad",
29
+ "erm",
30
+ "evar",
31
+ "cdar",
32
+ ]:
33
+ kwargs = dict(window=4)
34
+ if name in {"sharpe", "sortino"}:
35
+ kwargs["freerate_value"] = 0
36
+ if name == "cvar":
37
+ kwargs.update(holding_period=1, confidence=0.625)
38
+ frame = getattr(sq, name)(returns, ["A"], **kwargs)
39
+ result[name] = float(frame.iloc[-1, 0])
40
+ for method in ["historical", "delta-normal", "delta-gamma-normal"]:
41
+ kwargs = {"gamma_matrix": np.array([[2.0]])} if method == "delta-gamma-normal" else {}
42
+ frame = sq.var(returns, ["A"], window=4, holding_period=1, method=method, **kwargs)
43
+ result["var_" + method] = float(frame.Portfolio.iloc[-1])
44
+ prices = pd.DataFrame({"A": [100.0, 80.0, 90.0, 95.0]})
45
+ result["calmar_from_prices"] = float(sq.calmar(prices, ["A"], window=3, use_returns=False).iloc[-1, 0])
46
+ result["sample_mean"] = sq.simplemc([1.0, 2.0, 3.0, 4.0])["sample_mean"]
47
+ result["brownian_shape"] = list(sq.brownian(N=8, nsims=3, seed=42).shape)
48
+ result["correlated_brownian_shape"] = list(
49
+ sq.correlated_brownian(N=8, nsims=3, seed=42, correlation_matrix=np.eye(2)).shape
50
+ )
51
+ result["gbm_shape"] = list(sq.gbm_paths(N=8, nsims=3, seed=42).shape)
52
+ result["correlated_gbm_shape"] = list(
53
+ sq.gbm_correlated_paths(np.ones(2), np.zeros(2), np.ones(2) * 0.2, N=8, nsims=3, seed=42).shape
54
+ )
55
+ result["version"] = sq.__version__
56
+ assert abs(result["var_historical"] - 0.037) < 1e-12
57
+ assert abs(result["cvar"] - 1 / 30) < 1e-12
58
+ print(json.dumps(result, indent=2, allow_nan=False))
59
+ return result
60
+
61
+
62
+ if __name__ == "__main__":
63
+ run()