portpy-quant 0.1.0__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 (85) hide show
  1. portpy_quant-0.1.0/.env.example +3 -0
  2. portpy_quant-0.1.0/.github/workflows/ci.yml +54 -0
  3. portpy_quant-0.1.0/.github/workflows/docs.yml +50 -0
  4. portpy_quant-0.1.0/.github/workflows/publish.yml +28 -0
  5. portpy_quant-0.1.0/.gitignore +63 -0
  6. portpy_quant-0.1.0/CHANGELOG.md +50 -0
  7. portpy_quant-0.1.0/LICENSE +21 -0
  8. portpy_quant-0.1.0/PKG-INFO +190 -0
  9. portpy_quant-0.1.0/README.md +93 -0
  10. portpy_quant-0.1.0/docs/api/core.md +33 -0
  11. portpy_quant-0.1.0/docs/api/explain.md +25 -0
  12. portpy_quant-0.1.0/docs/api/index.md +17 -0
  13. portpy_quant-0.1.0/docs/api/metrics/benchmarks.md +15 -0
  14. portpy_quant-0.1.0/docs/api/metrics/costs.md +11 -0
  15. portpy_quant-0.1.0/docs/api/metrics/covariance.md +16 -0
  16. portpy_quant-0.1.0/docs/api/metrics/distributions.md +17 -0
  17. portpy_quant-0.1.0/docs/api/metrics/drawdowns.md +16 -0
  18. portpy_quant-0.1.0/docs/api/metrics/index.md +20 -0
  19. portpy_quant-0.1.0/docs/api/metrics/performance.md +22 -0
  20. portpy_quant-0.1.0/docs/api/metrics/regressions.md +12 -0
  21. portpy_quant-0.1.0/docs/api/metrics/returns.md +21 -0
  22. portpy_quant-0.1.0/docs/api/metrics/risk.md +23 -0
  23. portpy_quant-0.1.0/docs/api/metrics/rolling.md +14 -0
  24. portpy_quant-0.1.0/docs/api/metrics/summary.md +9 -0
  25. portpy_quant-0.1.0/docs/api/portfolio.md +7 -0
  26. portpy_quant-0.1.0/docs/architecture.md +199 -0
  27. portpy_quant-0.1.0/docs/changelog.md +6 -0
  28. portpy_quant-0.1.0/docs/getting-started.md +101 -0
  29. portpy_quant-0.1.0/docs/guide/calendars-and-currency.md +78 -0
  30. portpy_quant-0.1.0/docs/guide/explainability.md +83 -0
  31. portpy_quant-0.1.0/docs/guide/portfolio.md +90 -0
  32. portpy_quant-0.1.0/docs/guide/weights.md +76 -0
  33. portpy_quant-0.1.0/docs/index.md +53 -0
  34. portpy_quant-0.1.0/docs/roadmap.md +49 -0
  35. portpy_quant-0.1.0/examples/01_yfinance_getting_started.py +120 -0
  36. portpy_quant-0.1.0/examples/02_alpaca_multiasset_calendar.py +201 -0
  37. portpy_quant-0.1.0/examples/debug.ipynb +7735 -0
  38. portpy_quant-0.1.0/examples/tutorial.ipynb +3865 -0
  39. portpy_quant-0.1.0/mkdocs.yml +101 -0
  40. portpy_quant-0.1.0/pyproject.toml +94 -0
  41. portpy_quant-0.1.0/src/portpy/__init__.py +50 -0
  42. portpy_quant-0.1.0/src/portpy/core/__init__.py +19 -0
  43. portpy_quant-0.1.0/src/portpy/core/asset.py +32 -0
  44. portpy_quant-0.1.0/src/portpy/core/calendar.py +110 -0
  45. portpy_quant-0.1.0/src/portpy/core/currency.py +52 -0
  46. portpy_quant-0.1.0/src/portpy/core/weights.py +69 -0
  47. portpy_quant-0.1.0/src/portpy/explain.py +235 -0
  48. portpy_quant-0.1.0/src/portpy/metrics/__init__.py +57 -0
  49. portpy_quant-0.1.0/src/portpy/metrics/benchmarks.py +214 -0
  50. portpy_quant-0.1.0/src/portpy/metrics/costs.py +80 -0
  51. portpy_quant-0.1.0/src/portpy/metrics/covariance.py +189 -0
  52. portpy_quant-0.1.0/src/portpy/metrics/distributions.py +318 -0
  53. portpy_quant-0.1.0/src/portpy/metrics/drawdowns.py +243 -0
  54. portpy_quant-0.1.0/src/portpy/metrics/performance.py +462 -0
  55. portpy_quant-0.1.0/src/portpy/metrics/regressions.py +101 -0
  56. portpy_quant-0.1.0/src/portpy/metrics/returns.py +311 -0
  57. portpy_quant-0.1.0/src/portpy/metrics/risk.py +451 -0
  58. portpy_quant-0.1.0/src/portpy/metrics/rolling.py +193 -0
  59. portpy_quant-0.1.0/src/portpy/metrics/summary.py +130 -0
  60. portpy_quant-0.1.0/src/portpy/models/__init__.py +1 -0
  61. portpy_quant-0.1.0/src/portpy/portfolio.py +244 -0
  62. portpy_quant-0.1.0/src/portpy/py.typed +0 -0
  63. portpy_quant-0.1.0/src/portpy/strategies/__init__.py +1 -0
  64. portpy_quant-0.1.0/src/portpy/utils/__init__.py +1 -0
  65. portpy_quant-0.1.0/src/portpy/utils/constants.py +21 -0
  66. portpy_quant-0.1.0/src/portpy/utils/validation.py +95 -0
  67. portpy_quant-0.1.0/src/portpy/visualization/__init__.py +1 -0
  68. portpy_quant-0.1.0/tests/conftest.py +54 -0
  69. portpy_quant-0.1.0/tests/core/test_calendar.py +73 -0
  70. portpy_quant-0.1.0/tests/core/test_currency.py +51 -0
  71. portpy_quant-0.1.0/tests/core/test_weights.py +52 -0
  72. portpy_quant-0.1.0/tests/metrics/test_benchmarks.py +47 -0
  73. portpy_quant-0.1.0/tests/metrics/test_costs.py +40 -0
  74. portpy_quant-0.1.0/tests/metrics/test_covariance.py +68 -0
  75. portpy_quant-0.1.0/tests/metrics/test_distributions.py +70 -0
  76. portpy_quant-0.1.0/tests/metrics/test_drawdowns.py +81 -0
  77. portpy_quant-0.1.0/tests/metrics/test_performance.py +105 -0
  78. portpy_quant-0.1.0/tests/metrics/test_regressions.py +42 -0
  79. portpy_quant-0.1.0/tests/metrics/test_returns.py +137 -0
  80. portpy_quant-0.1.0/tests/metrics/test_risk.py +104 -0
  81. portpy_quant-0.1.0/tests/metrics/test_rolling.py +50 -0
  82. portpy_quant-0.1.0/tests/metrics/test_summary.py +41 -0
  83. portpy_quant-0.1.0/tests/test_explain.py +94 -0
  84. portpy_quant-0.1.0/tests/test_portfolio.py +131 -0
  85. portpy_quant-0.1.0/tests/validation/test_vs_empyrical_quantstats.py +150 -0
@@ -0,0 +1,3 @@
1
+ ALPACA_ENDPOINT=https://paper-api.alpaca.markets/v2
2
+ ALPACA_KEY=your_alpaca_api_key_id
3
+ ALPACA_SECRET=your_alpaca_api_secret_key
@@ -0,0 +1,54 @@
1
+ name: CI
2
+
3
+ # If a commit introduces a bug, breaks a Python version, # or violates formatting
4
+ # rules, GitHub marks the commit.
5
+
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ pull_request:
10
+ branches: [main]
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ matrix:
17
+ python-version: ["3.10", "3.11", "3.12"]
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Set up Python ${{ matrix.python-version }}
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+
26
+ - name: Install portpy with dev extras
27
+ run: pip install -e ".[dev]"
28
+
29
+ - name: Lint
30
+ run: ruff check src tests
31
+
32
+ - name: Type check
33
+ run: mypy src/portpy
34
+
35
+ - name: Run tests (excluding network-dependent tests)
36
+ run: pytest -q -m "not network" --cov=portpy --cov-report=term-missing
37
+
38
+ build:
39
+ runs-on: ubuntu-latest
40
+ needs: test
41
+ steps:
42
+ - uses: actions/checkout@v4
43
+ - uses: actions/setup-python@v5
44
+ with:
45
+ python-version: "3.12"
46
+ - name: Install build tool
47
+ run: pip install build
48
+ - name: Build sdist and wheel
49
+ run: python -m build
50
+ - name: Upload build artifacts
51
+ uses: actions/upload-artifact@v4
52
+ with:
53
+ name: dist
54
+ path: dist/
@@ -0,0 +1,50 @@
1
+ name: Docs
2
+
3
+ # Builds and publishes the mkdocs site to GitHub Pages on every push to main.
4
+ # Requires GitHub Pages to be enabled for this repo with source = GitHub Actions
5
+ # (Settings -> Pages -> Build and deployment -> Source). --- Already DONE
6
+
7
+ on:
8
+ push:
9
+ branches: [main]
10
+ paths:
11
+ - "docs/**"
12
+ - "mkdocs.yml"
13
+ - "src/**"
14
+ - ".github/workflows/docs.yml"
15
+ workflow_dispatch:
16
+
17
+ permissions:
18
+ contents: read
19
+ pages: write
20
+ id-token: write
21
+
22
+ concurrency:
23
+ group: pages
24
+ cancel-in-progress: true
25
+
26
+ jobs:
27
+ build:
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - uses: actions/setup-python@v5
32
+ with:
33
+ python-version: "3.12"
34
+ - name: Install portpy with dev extras
35
+ run: pip install -e ".[dev]"
36
+ - name: Build site
37
+ run: mkdocs build --strict
38
+ - uses: actions/upload-pages-artifact@v3
39
+ with:
40
+ path: site
41
+
42
+ deploy:
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ environment:
46
+ name: github-pages
47
+ url: ${{ steps.deployment.outputs.page_url }}
48
+ steps:
49
+ - id: deployment
50
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,28 @@
1
+ name: Publish to PyPI
2
+
3
+ # Only runs when a version tag (e.g. v1.0.0) is pushed, never on normal commits.
4
+ # Requires a PYPI_API_TOKEN secret to be configured in the repository settings.
5
+
6
+ on:
7
+ workflow_dispatch:
8
+ push:
9
+ tags:
10
+ - "v*.*.*"
11
+
12
+ jobs:
13
+ publish:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: "3.12"
20
+ - name: Install build tool
21
+ run: pip install build twine
22
+ - name: Build sdist and wheel
23
+ run: python -m build
24
+ - name: Publish to PyPI
25
+ env:
26
+ TWINE_USERNAME: __token__
27
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
28
+ run: twine upload dist/*
@@ -0,0 +1,63 @@
1
+ # Environment variables
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+
6
+ # Python
7
+ __pycache__/
8
+ *.py[cod]
9
+ *$py.class
10
+ *.so
11
+ .Python
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+
28
+ # Virtual environments
29
+ venv/
30
+ .venv/
31
+ ENV/
32
+ env/
33
+
34
+ # Testing, coverage, linting
35
+ .pytest_cache/
36
+ .coverage
37
+ .coverage.*
38
+ htmlcov/
39
+ .mypy_cache/
40
+ .ruff_cache/
41
+
42
+ # Jupyter
43
+ .ipynb_checkpoints/
44
+
45
+ # Docs build
46
+ /site/
47
+
48
+ # Personal working notes
49
+ /planning.md
50
+ /debug.ipynb
51
+
52
+ # Example outputs
53
+ examples/output/
54
+
55
+ # IDE, Editor
56
+ .vscode/
57
+ .idea/
58
+ *.swp
59
+ *.swo
60
+
61
+ # OS specific
62
+ .DS_Store
63
+ Thumbs.db
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Pre-Release] - 2026-08-04
8
+
9
+ ## [0.1.0] - 2026-08-04
10
+
11
+ First release. Metrics and core are implemented and tested; visualization, models, and
12
+ strategies are designed but not yet built — see [docs/roadmap.md](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/roadmap.md).
13
+
14
+ ### Added
15
+
16
+ - **`Portfolio`**: the single entry point over price/return data — weights (negative/short
17
+ positions supported), asset-class tags, and a `.metrics` namespace that auto-fills
18
+ `returns`/`prices`/`weights`/`rf`/`periods_per_year` from the portfolio's own state on
19
+ keyword-only calls.
20
+ - **`portpy.metrics`**: `returns`, `risk`, `performance`, `drawdowns`, `rolling`,
21
+ `distributions`, `benchmarks` (alpha/beta/capture ratios), `regressions`
22
+ (OLS via statsmodels), `covariance`(portfolio-level risk decomposition: variance,
23
+ diversification ratio, marginal/component contribution to risk), one-shot
24
+ `summary` tables, and transaction-`costs` helpers. Most scalar metrics accept
25
+ `as_result=True` to get a self-explaining `MetricResult` back.
26
+ - **`portpy.core`**: `calendars` for combining assets that trade on different calendars
27
+ (e.g. 24/7 crypto with Mon-Fri equities); `currency` for multi-currency portfolios;
28
+ `weights` for weight validation (including long/short books); and `asset` to represent
29
+ individual assets.
30
+ - **`portpy.explain`**: the explainability layer — `Explanation` knowledge cards registered
31
+ per metric (what it is, formula, how to read it, good vs. bad, caveats, a value-specific
32
+ verdict), `MetricResult` (a `float` subclass carrying its own name/unit/interpretation),
33
+ and `portpy.explain(name_or_result)` as the single dispatch point.
34
+ - **Examples**: `01_yfinance_getting_started.py`, `02_alpaca_multiasset_calendar.py`, and
35
+ `tutorial.ipynb` — a full walkthrough of every metric function against three real
36
+ long/short, multi-asset portfolios built from live Alpaca (stocks/ETFs/crypto) and FRED
37
+ (risk-free rate) data.
38
+ - **Tests**: unit coverage for every metric module, core of portpy, plus
39
+ `tests/validation/test_vs_empyrical_quantstats.py`, cross-checking PortPy's numbers
40
+ against `empyrical` and `quantstats` on synthetic and real (yfinance) market data.
41
+ - **Docs**: a full guide + API reference site (`mkdocs` + `mkdocstrings`) covering the
42
+ `Portfolio` object, weights/shorts, calendar & currency alignment, the explainability
43
+ layer, and every function's API docs.
44
+ - Project scaffolding: `pyproject.toml`, `src/` layout, CI (lint/type-check/test/build) and
45
+ PyPI-publish GitHub Actions workflows, MIT license.
46
+
47
+ ### Known limitations
48
+
49
+ - `portpy.visualization`, `portpy.models`, and `portpy.strategies` are designed (see
50
+ [docs/architecture.md](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/architecture.md)) but not implemented in this release.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arthur Lino Faugeron Jacono
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
+ SOFTWARE.
@@ -0,0 +1,190 @@
1
+ Metadata-Version: 2.4
2
+ Name: portpy-quant
3
+ Version: 0.1.0
4
+ Summary: Portfolio analysis, optimization, and management with built-in, human-readable explanations of every metric, chart, model, and strategy.
5
+ Project-URL: Homepage, https://github.com/Arthur-Faugeron/PortPy
6
+ Project-URL: Documentation, https://Arthur-Faugeron.github.io/PortPy/
7
+ Project-URL: Repository, https://github.com/Arthur-Faugeron/PortPy
8
+ Project-URL: Issues, https://github.com/Arthur-Faugeron/PortPy/issues
9
+ Project-URL: Changelog, https://github.com/Arthur-Faugeron/PortPy/blob/main/CHANGELOG.md
10
+ Author-email: Arthur Lino Faugeron Jacono <faugeronarthur@gmail.com>
11
+ License: MIT License
12
+
13
+ Copyright (c) 2026 Arthur Lino Faugeron Jacono
14
+
15
+ Permission is hereby granted, free of charge, to any person obtaining a copy
16
+ of this software and associated documentation files (the "Software"), to deal
17
+ in the Software without restriction, including without limitation the rights
18
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+ copies of the Software, and to permit persons to whom the Software is
20
+ furnished to do so, subject to the following conditions:
21
+
22
+ The above copyright notice and this permission notice shall be included in all
23
+ copies or substantial portions of the Software.
24
+
25
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31
+ SOFTWARE.
32
+ License-File: LICENSE
33
+ Keywords: backtesting,finance,optimization,portfolio,quant,risk
34
+ Classifier: Development Status :: 4 - Beta
35
+ Classifier: Intended Audience :: Financial and Insurance Industry
36
+ Classifier: Intended Audience :: Science/Research
37
+ Classifier: License :: OSI Approved :: MIT License
38
+ Classifier: Programming Language :: Python :: 3
39
+ Classifier: Programming Language :: Python :: 3.10
40
+ Classifier: Programming Language :: Python :: 3.11
41
+ Classifier: Programming Language :: Python :: 3.12
42
+ Classifier: Topic :: Office/Business :: Financial :: Investment
43
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
44
+ Requires-Python: >=3.10
45
+ Requires-Dist: numpy>=1.24
46
+ Requires-Dist: pandas>=2.0
47
+ Requires-Dist: scipy>=1.10
48
+ Requires-Dist: statsmodels>=0.14
49
+ Provides-Extra: all
50
+ Requires-Dist: alpaca-py>=0.30; extra == 'all'
51
+ Requires-Dist: arch>=6.3; extra == 'all'
52
+ Requires-Dist: hmmlearn>=0.3.2; extra == 'all'
53
+ Requires-Dist: kaleido>=0.2.1; extra == 'all'
54
+ Requires-Dist: matplotlib>=3.7; extra == 'all'
55
+ Requires-Dist: plotly>=5.18; extra == 'all'
56
+ Requires-Dist: python-dotenv>=1.0; extra == 'all'
57
+ Requires-Dist: scikit-learn>=1.3; extra == 'all'
58
+ Requires-Dist: seaborn>=0.13; extra == 'all'
59
+ Requires-Dist: yfinance>=0.2.40; extra == 'all'
60
+ Provides-Extra: data
61
+ Requires-Dist: alpaca-py>=0.30; extra == 'data'
62
+ Requires-Dist: python-dotenv>=1.0; extra == 'data'
63
+ Requires-Dist: yfinance>=0.2.40; extra == 'data'
64
+ Provides-Extra: dev
65
+ Requires-Dist: alpaca-py>=0.30; extra == 'dev'
66
+ Requires-Dist: arch>=6.3; extra == 'dev'
67
+ Requires-Dist: build>=1.0; extra == 'dev'
68
+ Requires-Dist: empyrical-reloaded>=0.5.7; extra == 'dev'
69
+ Requires-Dist: hmmlearn>=0.3.2; extra == 'dev'
70
+ Requires-Dist: jupyter>=1.0; extra == 'dev'
71
+ Requires-Dist: kaleido>=0.2.1; extra == 'dev'
72
+ Requires-Dist: matplotlib>=3.7; extra == 'dev'
73
+ Requires-Dist: mkdocs-material>=9.5; extra == 'dev'
74
+ Requires-Dist: mkdocs>=1.5; extra == 'dev'
75
+ Requires-Dist: mkdocstrings[python]>=0.24; extra == 'dev'
76
+ Requires-Dist: mypy>=1.8; extra == 'dev'
77
+ Requires-Dist: nbformat>=5.9; extra == 'dev'
78
+ Requires-Dist: plotly>=5.18; extra == 'dev'
79
+ Requires-Dist: pytest-cov>=4.1; extra == 'dev'
80
+ Requires-Dist: pytest>=7.4; extra == 'dev'
81
+ Requires-Dist: python-dotenv>=1.0; extra == 'dev'
82
+ Requires-Dist: quantstats>=0.0.62; extra == 'dev'
83
+ Requires-Dist: ruff>=0.4; extra == 'dev'
84
+ Requires-Dist: scikit-learn>=1.3; extra == 'dev'
85
+ Requires-Dist: seaborn>=0.13; extra == 'dev'
86
+ Requires-Dist: yfinance>=0.2.40; extra == 'dev'
87
+ Provides-Extra: models
88
+ Requires-Dist: arch>=6.3; extra == 'models'
89
+ Requires-Dist: hmmlearn>=0.3.2; extra == 'models'
90
+ Requires-Dist: scikit-learn>=1.3; extra == 'models'
91
+ Provides-Extra: viz
92
+ Requires-Dist: kaleido>=0.2.1; extra == 'viz'
93
+ Requires-Dist: matplotlib>=3.7; extra == 'viz'
94
+ Requires-Dist: plotly>=5.18; extra == 'viz'
95
+ Requires-Dist: seaborn>=0.13; extra == 'viz'
96
+ Description-Content-Type: text/markdown
97
+
98
+ # PortPy
99
+
100
+ **Portfolio analysis that explains itself.**
101
+
102
+ PortPy is a Python library for portfolio performance measurement and risk analysis, built around one object — `Portfolio` — and one idea: every number it gives you can explain, in plain language, what it is, how to read it, and whether it's good or bad.
103
+
104
+ [![CI](https://github.com/Arthur-Faugeron/PortPy/actions/workflows/ci.yml/badge.svg)](https://github.com/Arthur-Faugeron/PortPy/actions/workflows/ci.yml)
105
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
106
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml)
107
+
108
+ ```python
109
+ import pandas as pd
110
+ from portpy import Portfolio
111
+
112
+ prices = pd.read_csv("prices.csv", index_col=0, parse_dates=True) # columns = tickers
113
+
114
+ portfolio = Portfolio(
115
+ prices,
116
+ weights={"AAPL": 0.4, "MSFT": 0.35, "GOOGL": 0.25},
117
+ name="Tech Portfolio",
118
+ risk_free_rate=0.04,
119
+ )
120
+
121
+ portfolio.metrics.sharpe_ratio(as_result=True).explain()
122
+ ```
123
+
124
+ ```text
125
+ sharpe_ratio (metric)
126
+ =====================
127
+
128
+ What it is:
129
+ The most widely used risk-adjusted return measure: excess return earned per unit of total volatility taken on.
130
+
131
+ Formula:
132
+ mean(r - rf) / std(r - rf, ddof=1) * sqrt(periods_per_year)
133
+
134
+ How to read it:
135
+ A Sharpe of 1.0 means you earned, on average, one standard deviation of excess return for the volatility you took on.
136
+
137
+ Good vs. bad:
138
+ Rules of thumb: <0 poor (lost money net of the risk-free rate), 0-1 sub-par, 1-2 good, 2-3 very good, >3 excellent (and worth double-checking for overfitting or a very short sample).
139
+
140
+ Caveats:
141
+ Assumes returns are roughly symmetric - it penalizes upside volatility just as much as downside, and can be misleadingly high for strategies with rare, large negative tail events (e.g. option-selling). Pair with sortino_ratio and max_drawdown.
142
+
143
+ This result:
144
+ sub-par
145
+ ```
146
+
147
+ ## Why PortPy
148
+
149
+ - **One object, one namespace.** `portfolio.metrics.<name>()` auto-fills returns, weights,
150
+ risk-free rate, and annualization frequency from the portfolio itself — no re-threading
151
+ the same five arguments through every call.
152
+ - **A real portfolio, not just a return stream.** Weights (negative/short included), asset-class
153
+ tags, and explicit calendar-alignment / currency-conversion helpers for combining assets that
154
+ don't trade on the same schedule (crypto, equities, bonds) or in the same currency.
155
+ - **Explains itself.** `.explain()` on any result — a plain-language card covering what it is,
156
+ how to read it, good vs. bad, and known caveats.
157
+ - **Numerically validated.** Cross-checked against [`empyrical`](https://github.com/stefan-jansen/empyrical-reloaded)
158
+ and [`quantstats`](https://github.com/ranaroussi/quantstats) on real market data — see
159
+ `tests/validation/`.
160
+
161
+ ## Install
162
+
163
+ ```bash
164
+ pip install portpy-quant # core: numpy, pandas, scipy, statsmodels
165
+ pip install "portpy-quant[viz]" # + plotly, matplotlib, seaborn
166
+ pip install "portpy-quant[data]" # + yfinance, alpaca-py (for the examples)
167
+ pip install "portpy-quant[all]" # everything
168
+ ```
169
+
170
+ ## Documentation
171
+
172
+ - [Getting Started](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/getting-started.md)
173
+ - [User Guide](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/index.md) — the `Portfolio` object, weights & shorts, calendar/currency
174
+ alignment, the explainability layer
175
+ - [API Reference](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/api/index.md) — every function, by module
176
+ - [Roadmap](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/roadmap.md) — what's implemented today vs. planned (`models`, `strategies`,
177
+ `visualization` are not yet built)
178
+ - [`examples/`](https://github.com/Arthur-Faugeron/PortPy/blob/main/examples/) — runnable scripts and a full tutorial notebook exercising every
179
+ metric against live Alpaca + Fed (FRED) data
180
+
181
+ ## Status
182
+
183
+ PortPy is pre-1.0 (`Development Status :: 4 - Beta`). **Metrics and core** (calendar/currency
184
+ alignment, weights, the explainability layer) are implemented and tested. **Visualization,
185
+ models/optimization, and strategies/backtesting** are designed but not yet built — see the
186
+ [Roadmap](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/roadmap.md).
187
+
188
+ ## License
189
+
190
+ [MIT](LICENSE) © Arthur Faugeron
@@ -0,0 +1,93 @@
1
+ # PortPy
2
+
3
+ **Portfolio analysis that explains itself.**
4
+
5
+ PortPy is a Python library for portfolio performance measurement and risk analysis, built around one object — `Portfolio` — and one idea: every number it gives you can explain, in plain language, what it is, how to read it, and whether it's good or bad.
6
+
7
+ [![CI](https://github.com/Arthur-Faugeron/PortPy/actions/workflows/ci.yml/badge.svg)](https://github.com/Arthur-Faugeron/PortPy/actions/workflows/ci.yml)
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml)
10
+
11
+ ```python
12
+ import pandas as pd
13
+ from portpy import Portfolio
14
+
15
+ prices = pd.read_csv("prices.csv", index_col=0, parse_dates=True) # columns = tickers
16
+
17
+ portfolio = Portfolio(
18
+ prices,
19
+ weights={"AAPL": 0.4, "MSFT": 0.35, "GOOGL": 0.25},
20
+ name="Tech Portfolio",
21
+ risk_free_rate=0.04,
22
+ )
23
+
24
+ portfolio.metrics.sharpe_ratio(as_result=True).explain()
25
+ ```
26
+
27
+ ```text
28
+ sharpe_ratio (metric)
29
+ =====================
30
+
31
+ What it is:
32
+ The most widely used risk-adjusted return measure: excess return earned per unit of total volatility taken on.
33
+
34
+ Formula:
35
+ mean(r - rf) / std(r - rf, ddof=1) * sqrt(periods_per_year)
36
+
37
+ How to read it:
38
+ A Sharpe of 1.0 means you earned, on average, one standard deviation of excess return for the volatility you took on.
39
+
40
+ Good vs. bad:
41
+ Rules of thumb: <0 poor (lost money net of the risk-free rate), 0-1 sub-par, 1-2 good, 2-3 very good, >3 excellent (and worth double-checking for overfitting or a very short sample).
42
+
43
+ Caveats:
44
+ Assumes returns are roughly symmetric - it penalizes upside volatility just as much as downside, and can be misleadingly high for strategies with rare, large negative tail events (e.g. option-selling). Pair with sortino_ratio and max_drawdown.
45
+
46
+ This result:
47
+ sub-par
48
+ ```
49
+
50
+ ## Why PortPy
51
+
52
+ - **One object, one namespace.** `portfolio.metrics.<name>()` auto-fills returns, weights,
53
+ risk-free rate, and annualization frequency from the portfolio itself — no re-threading
54
+ the same five arguments through every call.
55
+ - **A real portfolio, not just a return stream.** Weights (negative/short included), asset-class
56
+ tags, and explicit calendar-alignment / currency-conversion helpers for combining assets that
57
+ don't trade on the same schedule (crypto, equities, bonds) or in the same currency.
58
+ - **Explains itself.** `.explain()` on any result — a plain-language card covering what it is,
59
+ how to read it, good vs. bad, and known caveats.
60
+ - **Numerically validated.** Cross-checked against [`empyrical`](https://github.com/stefan-jansen/empyrical-reloaded)
61
+ and [`quantstats`](https://github.com/ranaroussi/quantstats) on real market data — see
62
+ `tests/validation/`.
63
+
64
+ ## Install
65
+
66
+ ```bash
67
+ pip install portpy-quant # core: numpy, pandas, scipy, statsmodels
68
+ pip install "portpy-quant[viz]" # + plotly, matplotlib, seaborn
69
+ pip install "portpy-quant[data]" # + yfinance, alpaca-py (for the examples)
70
+ pip install "portpy-quant[all]" # everything
71
+ ```
72
+
73
+ ## Documentation
74
+
75
+ - [Getting Started](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/getting-started.md)
76
+ - [User Guide](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/index.md) — the `Portfolio` object, weights & shorts, calendar/currency
77
+ alignment, the explainability layer
78
+ - [API Reference](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/api/index.md) — every function, by module
79
+ - [Roadmap](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/roadmap.md) — what's implemented today vs. planned (`models`, `strategies`,
80
+ `visualization` are not yet built)
81
+ - [`examples/`](https://github.com/Arthur-Faugeron/PortPy/blob/main/examples/) — runnable scripts and a full tutorial notebook exercising every
82
+ metric against live Alpaca + Fed (FRED) data
83
+
84
+ ## Status
85
+
86
+ PortPy is pre-1.0 (`Development Status :: 4 - Beta`). **Metrics and core** (calendar/currency
87
+ alignment, weights, the explainability layer) are implemented and tested. **Visualization,
88
+ models/optimization, and strategies/backtesting** are designed but not yet built — see the
89
+ [Roadmap](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/roadmap.md).
90
+
91
+ ## License
92
+
93
+ [MIT](LICENSE) © Arthur Faugeron
@@ -0,0 +1,33 @@
1
+ # core
2
+
3
+ Asset-class tagging, calendar alignment, currency conversion, and weight
4
+ normalization — all opt-in helpers you call before constructing a Portfolio.
5
+ See the
6
+ [Calendars & currencies](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/guide/calendars-and-currency.md)
7
+ and
8
+ [Weights & short positions](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/guide/weights.md)
9
+ guides for worked examples.
10
+
11
+ ## Asset tagging
12
+
13
+ ::: portpy.core.asset.AssetClass
14
+
15
+ ::: portpy.core.asset.ALWAYS_ON_CLASSES
16
+
17
+ ## Calendar alignment
18
+
19
+ ::: portpy.core.calendar.detect_frequency
20
+
21
+ ::: portpy.core.calendar.calendar_coverage_report
22
+
23
+ ::: portpy.core.calendar.align_calendars
24
+
25
+ ## Currency conversion
26
+
27
+ ::: portpy.core.currency.convert_to_base_currency
28
+
29
+ ## Weights
30
+
31
+ ::: portpy.core.weights.equal_weights
32
+
33
+ ::: portpy.core.weights.normalize_weights
@@ -0,0 +1,25 @@
1
+ # explain
2
+
3
+ See
4
+ [Explainability](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/guide/explainability.md)
5
+ for how the pieces below fit together, and
6
+ where the explanation text itself actually lives (hint: not in this module).
7
+
8
+ !!! note
9
+
10
+ from portpy import Portfolio (or any import of the portpy package) binds the package
11
+ attribute portpy.explain to the function below, not this module. Import the other
12
+ names (available, get, register, Explanation, MetricResult) directly from
13
+ portpy.explain as shown in each signature.
14
+
15
+ ::: portpy.explain.Explanation
16
+
17
+ ::: portpy.explain.MetricResult
18
+
19
+ ::: portpy.explain.explain
20
+
21
+ ::: portpy.explain.register
22
+
23
+ ::: portpy.explain.get
24
+
25
+ ::: portpy.explain.available
@@ -0,0 +1,17 @@
1
+ # API Reference
2
+
3
+ Generated from docstrings. Every function also documents its own parameters, return type,
4
+ and — where relevant — the exact formula it computes.
5
+
6
+ | Submodule | Covers |
7
+ |---|---|
8
+ | [Portfolio](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/api/portfolio.md) | the main entry point. |
9
+ | [core](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/api/core.md) | asset-class tagging, calendar alignment, currency conversion, weight
10
+ normalization. |
11
+ | [explain](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/api/explain.md) | Explanation, MetricResult, and the explain() dispatcher. |
12
+ | [metrics](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/api/metrics/index.md) | every metric function, grouped by submodule: returns,
13
+ risk, performance, drawdowns, rolling, distributions, benchmarks,
14
+ regressions, covariance, summary, costs.
15
+
16
+ Not yet implemented (see the [Roadmap](https://github.com/Arthur-Faugeron/PortPy/blob/main/docs/roadmap.md)): portpy.visualization,
17
+ portpy.models, portpy.strategies.
@@ -0,0 +1,15 @@
1
+ # benchmarks
2
+
3
+ Functions for comparing portfolio performance against a benchmark. These metrics
4
+ measure active return, market sensitivity, and relative performance.
5
+
6
+ ::: portpy.metrics.benchmarks
7
+ options:
8
+ members:
9
+ - alpha
10
+ - correlation
11
+ - r_squared
12
+ - up_capture_ratio
13
+ - down_capture_ratio
14
+ - capture_ratio
15
+ - batting_average
@@ -0,0 +1,11 @@
1
+ # costs
2
+
3
+ PortPy assumes frictionless trading by default. These helpers layer transaction costs back
4
+ in, either from an explicit weight-history (real turnover) or an assumed constant per-period
5
+ turnover.
6
+
7
+ ::: portpy.metrics.costs
8
+ options:
9
+ members:
10
+ - turnover_from_weights
11
+ - net_of_costs_returns