quantvolt 0.1.0__tar.gz → 0.2.1__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 (121) hide show
  1. {quantvolt-0.1.0 → quantvolt-0.2.1}/Cargo.lock +1 -1
  2. {quantvolt-0.1.0 → quantvolt-0.2.1}/Cargo.toml +1 -2
  3. {quantvolt-0.1.0 → quantvolt-0.2.1}/PKG-INFO +35 -11
  4. {quantvolt-0.1.0 → quantvolt-0.2.1}/README.md +31 -9
  5. {quantvolt-0.1.0 → quantvolt-0.2.1}/pyproject.toml +23 -5
  6. {quantvolt-0.1.0 → quantvolt-0.2.1}/rust/src/paths.rs +2 -2
  7. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/__init__.py +73 -1
  8. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/_validation.py +1 -1
  9. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/assets/dispatch_approx.py +43 -43
  10. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/assets/dispatch_deterministic.py +9 -11
  11. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/assets/dispatch_sdp.py +62 -56
  12. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/assets/long_dated.py +83 -88
  13. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/assets/plant.py +4 -4
  14. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/assets/storage.py +34 -34
  15. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/cli.py +43 -4
  16. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/curvemodels/__init__.py +1 -1
  17. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/curvemodels/multifactor.py +13 -13
  18. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/curvemodels/schwartz_smith.py +35 -38
  19. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/curves/arbitrage.py +8 -10
  20. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/curves/builder.py +10 -11
  21. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/data/__init__.py +2 -2
  22. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/data/base.py +12 -12
  23. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/data/commercial.py +3 -3
  24. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/data/datasets.py +2 -1
  25. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/data/entsoe.py +11 -11
  26. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/data/entsog.py +5 -6
  27. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/data/netztransparenz.py +4 -12
  28. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/data/open_meteo.py +5 -5
  29. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/exceptions.py +1 -1
  30. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/hedging/__init__.py +3 -4
  31. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/hedging/hybrid.py +11 -11
  32. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/hedging/mean_variance.py +5 -5
  33. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/hedging/ppa_nomination.py +4 -12
  34. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/hedging/variance_min.py +11 -11
  35. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/market/outages.py +80 -83
  36. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/market/transmission.py +2 -2
  37. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/market/weather.py +1 -1
  38. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/models/__init__.py +72 -0
  39. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/models/commodity.py +20 -5
  40. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/models/curve.py +4 -4
  41. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/models/discount_curve.py +1 -1
  42. quantvolt-0.2.1/src/quantvolt/models/instruments.py +507 -0
  43. quantvolt-0.2.1/src/quantvolt/models/ppa.py +89 -0
  44. quantvolt-0.2.1/src/quantvolt/models/ppa_terms.py +444 -0
  45. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/models/schedule.py +1 -1
  46. quantvolt-0.2.1/src/quantvolt/models/units.py +224 -0
  47. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/models/vol_surface.py +1 -1
  48. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/numerics/__init__.py +4 -0
  49. quantvolt-0.2.1/src/quantvolt/numerics/_degenerate.py +75 -0
  50. quantvolt-0.2.1/src/quantvolt/numerics/bachelier.py +316 -0
  51. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/numerics/black76.py +8 -21
  52. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/numerics/daycount.py +1 -1
  53. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/numerics/interpolation.py +1 -1
  54. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/numerics/monte_carlo.py +14 -13
  55. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/numerics/risk_adjustment.py +3 -3
  56. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/numerics/rootfind.py +1 -1
  57. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/numerics/spread_models.py +1 -1
  58. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/portfolio/model.py +37 -9
  59. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/portfolio/settlement.py +2 -6
  60. quantvolt-0.2.1/src/quantvolt/portfolio/valuation.py +626 -0
  61. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/pricing/__init__.py +26 -0
  62. quantvolt-0.2.1/src/quantvolt/pricing/bachelier.py +108 -0
  63. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/pricing/exotic.py +44 -46
  64. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/pricing/futures.py +7 -7
  65. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/pricing/implied_vol.py +7 -8
  66. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/pricing/mark_to_market.py +8 -8
  67. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/pricing/power_hedge.py +1 -3
  68. quantvolt-0.2.1/src/quantvolt/pricing/ppa.py +852 -0
  69. quantvolt-0.2.1/src/quantvolt/pricing/ppa_valuation.py +370 -0
  70. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/pricing/spread_option.py +13 -13
  71. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/pricing/spreads.py +85 -90
  72. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/pricing/swap.py +6 -6
  73. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/pricing/tolling.py +52 -55
  74. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/pricing/transmission_right.py +11 -12
  75. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/pricing/vanilla.py +35 -24
  76. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/risk/aggregation.py +3 -3
  77. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/risk/cashflow_metrics.py +1 -3
  78. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/risk/cfar.py +9 -9
  79. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/risk/covariance.py +2 -2
  80. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/risk/credit_var.py +15 -15
  81. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/risk/engine.py +9 -9
  82. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/risk/mc_var.py +60 -61
  83. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/risk/parametric_var.py +56 -59
  84. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/risk/scenarios.py +7 -6
  85. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/stats/correlation.py +2 -2
  86. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/stats/descriptive.py +3 -3
  87. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/stats/mean_reversion.py +1 -1
  88. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/stats/normality.py +1 -1
  89. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/stats/stationarity.py +1 -1
  90. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/testing.py +6 -2
  91. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/workflow/criteria.py +3 -3
  92. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/workflow/modeling.py +18 -19
  93. quantvolt-0.1.0/src/quantvolt/models/instruments.py +0 -221
  94. quantvolt-0.1.0/src/quantvolt/models/ppa.py +0 -54
  95. quantvolt-0.1.0/src/quantvolt/portfolio/valuation.py +0 -231
  96. quantvolt-0.1.0/src/quantvolt/pricing/ppa.py +0 -377
  97. {quantvolt-0.1.0 → quantvolt-0.2.1}/.gitignore +0 -0
  98. {quantvolt-0.1.0 → quantvolt-0.2.1}/LICENSE +0 -0
  99. {quantvolt-0.1.0 → quantvolt-0.2.1}/rust/src/asian_mc.rs +0 -0
  100. {quantvolt-0.1.0 → quantvolt-0.2.1}/rust/src/lib.rs +0 -0
  101. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/_core.pyi +0 -0
  102. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/assets/__init__.py +0 -0
  103. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/assets/_tolerance.py +0 -0
  104. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/curves/__init__.py +0 -0
  105. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/data/dataset_catalog.json +0 -0
  106. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/data/smard.py +0 -0
  107. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/hedging/_conditioning.py +0 -0
  108. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/hedging/ppa_walk_forward.py +0 -0
  109. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/market/__init__.py +0 -0
  110. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/models/greeks.py +0 -0
  111. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/models/interval.py +0 -0
  112. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/models/power_hedge.py +0 -0
  113. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/numerics/_normal.py +0 -0
  114. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/numerics/exotic.py +0 -0
  115. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/portfolio/__init__.py +0 -0
  116. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/pricing/_dates.py +0 -0
  117. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/py.typed +0 -0
  118. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/risk/__init__.py +0 -0
  119. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/risk/_levels.py +0 -0
  120. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/stats/__init__.py +0 -0
  121. {quantvolt-0.1.0 → quantvolt-0.2.1}/src/quantvolt/workflow/__init__.py +0 -0
@@ -237,7 +237,7 @@ dependencies = [
237
237
 
238
238
  [[package]]
239
239
  name = "quantvolt-core"
240
- version = "0.1.0"
240
+ version = "0.2.1"
241
241
  dependencies = [
242
242
  "ndarray 0.17.2",
243
243
  "numpy",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "quantvolt-core"
3
- version = "0.1.0"
3
+ version = "0.2.1"
4
4
  edition = "2021"
5
5
  publish = false
6
6
  readme = "README.md"
@@ -27,4 +27,3 @@ rand_distr = "0.6"
27
27
  # reduction in CI wheel-build time.
28
28
  [profile.release]
29
29
  lto = "thin"
30
-
@@ -1,11 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quantvolt
3
- Version: 0.1.0
3
+ Version: 0.2.1
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Financial and Insurance Industry
6
6
  Classifier: Intended Audience :: Science/Research
7
7
  Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.11
8
9
  Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
9
11
  Classifier: Programming Language :: Rust
10
12
  Classifier: Topic :: Office/Business :: Financial :: Investment
11
13
  Classifier: Topic :: Scientific/Engineering :: Mathematics
@@ -28,7 +30,7 @@ Summary: Quantitative analysis of European power and energy markets
28
30
  Keywords: energy,power,gas,carbon,quant,derivatives,risk-management,pricing,monte-carlo,forward-curve
29
31
  Author-email: Nima Bahrami <nima@skillmix.nl>
30
32
  License-Expression: MIT
31
- Requires-Python: >=3.12
33
+ Requires-Python: >=3.11
32
34
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
33
35
  Project-URL: Documentation, https://nimabahrami.github.io/quantvolt/
34
36
  Project-URL: Homepage, https://nimabahrami.github.io/quantvolt/
@@ -51,9 +53,24 @@ Covered markets: EEX Phelix DE/AT power, EPEX SPOT power (DE, FR, NL, BE, GB), T
51
53
  natural gas (ICE Endex), and EUA carbon allowances — extensible by the caller without editing
52
54
  library source. See [docs/european-markets.md](docs/european-markets.md).
53
55
 
56
+ ## Why QuantVolt?
57
+
58
+ A typical desk assembles a curve spreadsheet, a standalone options pricer, a storage/dispatch
59
+ optimizer and a bolted-on VaR tool, each with its own units, sign conventions and delivery-period
60
+ identity — hand-reconciling between them is slow and a recurring source of quiet errors. QuantVolt
61
+ instead moves the same immutable `ForwardCurve` / `DeliveryPeriod` / `MarketData` / `Greeks` value
62
+ objects through curve construction, pricing, portfolio valuation, risk, realized settlement and
63
+ physical-asset valuation, with determinism, eager validation and serializability enforced
64
+ structurally rather than left to convention. It is equally explicit about scope: a PPA stays
65
+ `unpriced` until you register a forward-looking pricer, storage/dispatch results are brought into
66
+ a portfolio explicitly, no market data ships with the package, and approximations (Kirk,
67
+ Turnbull-Wakeman, `bang_bang`) are labelled, never hidden. See
68
+ [the "Why QuantVolt?" page](https://nimabahrami.github.io/quantvolt/#/guide/why) and the
69
+ [external validation evidence](docs/validation.md) for the checkable version of these claims.
70
+
54
71
  ## Install
55
72
 
56
- Requires Python 3.12+.
73
+ Requires Python 3.11+.
57
74
 
58
75
  ```bash
59
76
  uv add quantvolt # or: pip install quantvolt
@@ -113,10 +130,11 @@ Call premium: 45,663 EUR (delta 5,288 EUR per EUR/MWh)
113
130
  Book NPV: 9,133 EUR, VaR(95): 11,715 EUR
114
131
  ```
115
132
 
116
- Identical inputs give identical outputs: Monte Carlo honours a `seed`, inputs are validated
117
- eagerly (no silent partial results), results round-trip through plain dicts / JSON, and the
118
- library never mutates caller data `quantvolt.testing.assert_input_unchanged` lets you verify
119
- that last invariant in your own tests.
133
+ Repeated calls with the same inputs, seed, QuantVolt version, architecture, and native build
134
+ produce identical results. Monte Carlo honours a `seed`, inputs are validated eagerly (no silent
135
+ partial results), results round-trip through plain dicts / JSON, and the library never mutates
136
+ caller data — `quantvolt.testing.assert_input_unchanged` lets you verify that last invariant in
137
+ your own tests.
120
138
 
121
139
  ### Settle a PPA with your own interval data
122
140
 
@@ -295,7 +313,7 @@ specialised names stay on their sub-package. Full reference with a runnable exam
295
313
  | `quantvolt.stats` | Descriptive statistics, normality tests, ADF/KPSS stationarity with Samuelson-effect detection, correlation, Ornstein-Uhlenbeck mean reversion |
296
314
  | `quantvolt.market` | Transmission cost, temperature/degree-day utilities, generation outage / reliability KPIs |
297
315
  | `quantvolt.workflow` | 7-step model-selection workflow for structured products |
298
- | `quantvolt.portfolio` | `Portfolio`/`Position` book assembly, forward-looking `value_portfolio`, and realized interval `settle_energy_portfolio` aggregation |
316
+ | `quantvolt.portfolio` | `Portfolio`/`Position` book assembly; `value_portfolio` natively prices futures, forwards, swaps, transmission/pipeline rights, vanilla options, spread options and tolling agreements via the caller-extensible `DEFAULT_PRICERS` registry (PPAs and realized hedges are valued via a caller-registered pricer, e.g. `make_ppa_pricer` — deliberately not a default); realized interval `settle_energy_portfolio` aggregation is kept structurally separate from that forward-looking NPV |
299
317
  | `quantvolt.data` | Optional (`quantvolt[data]`): provider adapters behind a `DataSource` protocol — the only package that performs I/O |
300
318
  | `quantvolt.testing` | `assert_input_unchanged` — shipped test utility for the no-mutation invariant |
301
319
  | `quantvolt.exceptions` | The `EnergyQuantError` hierarchy (`ValidationError`, `ArbitrageError`, `MissingTenorError`, …) |
@@ -317,14 +335,20 @@ positions. See [docs/risk-and-assets.md](docs/risk-and-assets.md).
317
335
 
318
336
  ## Documentation
319
337
 
338
+ - [Why QuantVolt?](https://nimabahrami.github.io/quantvolt/#/guide/why) — the integration problem,
339
+ the shared computational model, the extensible pricer registry, and an honest "what QuantVolt is
340
+ not" section
320
341
  - [docs/api.md](docs/api.md) — module-by-module API reference with runnable examples
321
342
  - [docs/risk-and-assets.md](docs/risk-and-assets.md) — the VaR family, the physical-vs-risk-neutral
322
343
  drift rule, incomplete-market hedging, dispatch & storage, and the long-dated / liquidity caveats
323
344
  - [docs/european-markets.md](docs/european-markets.md) — market coverage, power-vs-gas
324
345
  statistics, negative prices, Samuelson effect, carbon costs, data-source policy
325
- - `.kiro/steering/`product, tech, structure, and coding-style standards
326
- - `.kiro/specs/power-energy-quant-analysis/` requirements, design, and task breakdown
327
- (the library is spec-driven; the design document is the source of truth for the maths)
346
+ - [docs/validation.md](docs/validation.md)external validation evidence: QuantVolt's pricers
347
+ checked against QuantLib 1.43 golden fixtures, with honest caveats and regeneration instructions
348
+ - Three complete, runnable end-to-end tutorials (site, each backed by a `site/examples/
349
+ verify_tutorial_*.py` script): [spark spread to hedge](https://nimabahrami.github.io/quantvolt/#/guide/tutorial-spark),
350
+ [renewable PPA to CFaR](https://nimabahrami.github.io/quantvolt/#/guide/tutorial-ppa), and
351
+ [storage intrinsic to hedge](https://nimabahrami.github.io/quantvolt/#/guide/tutorial-storage)
328
352
 
329
353
  ## Development
330
354
 
@@ -14,9 +14,24 @@ Covered markets: EEX Phelix DE/AT power, EPEX SPOT power (DE, FR, NL, BE, GB), T
14
14
  natural gas (ICE Endex), and EUA carbon allowances — extensible by the caller without editing
15
15
  library source. See [docs/european-markets.md](docs/european-markets.md).
16
16
 
17
+ ## Why QuantVolt?
18
+
19
+ A typical desk assembles a curve spreadsheet, a standalone options pricer, a storage/dispatch
20
+ optimizer and a bolted-on VaR tool, each with its own units, sign conventions and delivery-period
21
+ identity — hand-reconciling between them is slow and a recurring source of quiet errors. QuantVolt
22
+ instead moves the same immutable `ForwardCurve` / `DeliveryPeriod` / `MarketData` / `Greeks` value
23
+ objects through curve construction, pricing, portfolio valuation, risk, realized settlement and
24
+ physical-asset valuation, with determinism, eager validation and serializability enforced
25
+ structurally rather than left to convention. It is equally explicit about scope: a PPA stays
26
+ `unpriced` until you register a forward-looking pricer, storage/dispatch results are brought into
27
+ a portfolio explicitly, no market data ships with the package, and approximations (Kirk,
28
+ Turnbull-Wakeman, `bang_bang`) are labelled, never hidden. See
29
+ [the "Why QuantVolt?" page](https://nimabahrami.github.io/quantvolt/#/guide/why) and the
30
+ [external validation evidence](docs/validation.md) for the checkable version of these claims.
31
+
17
32
  ## Install
18
33
 
19
- Requires Python 3.12+.
34
+ Requires Python 3.11+.
20
35
 
21
36
  ```bash
22
37
  uv add quantvolt # or: pip install quantvolt
@@ -76,10 +91,11 @@ Call premium: 45,663 EUR (delta 5,288 EUR per EUR/MWh)
76
91
  Book NPV: 9,133 EUR, VaR(95): 11,715 EUR
77
92
  ```
78
93
 
79
- Identical inputs give identical outputs: Monte Carlo honours a `seed`, inputs are validated
80
- eagerly (no silent partial results), results round-trip through plain dicts / JSON, and the
81
- library never mutates caller data `quantvolt.testing.assert_input_unchanged` lets you verify
82
- that last invariant in your own tests.
94
+ Repeated calls with the same inputs, seed, QuantVolt version, architecture, and native build
95
+ produce identical results. Monte Carlo honours a `seed`, inputs are validated eagerly (no silent
96
+ partial results), results round-trip through plain dicts / JSON, and the library never mutates
97
+ caller data — `quantvolt.testing.assert_input_unchanged` lets you verify that last invariant in
98
+ your own tests.
83
99
 
84
100
  ### Settle a PPA with your own interval data
85
101
 
@@ -258,7 +274,7 @@ specialised names stay on their sub-package. Full reference with a runnable exam
258
274
  | `quantvolt.stats` | Descriptive statistics, normality tests, ADF/KPSS stationarity with Samuelson-effect detection, correlation, Ornstein-Uhlenbeck mean reversion |
259
275
  | `quantvolt.market` | Transmission cost, temperature/degree-day utilities, generation outage / reliability KPIs |
260
276
  | `quantvolt.workflow` | 7-step model-selection workflow for structured products |
261
- | `quantvolt.portfolio` | `Portfolio`/`Position` book assembly, forward-looking `value_portfolio`, and realized interval `settle_energy_portfolio` aggregation |
277
+ | `quantvolt.portfolio` | `Portfolio`/`Position` book assembly; `value_portfolio` natively prices futures, forwards, swaps, transmission/pipeline rights, vanilla options, spread options and tolling agreements via the caller-extensible `DEFAULT_PRICERS` registry (PPAs and realized hedges are valued via a caller-registered pricer, e.g. `make_ppa_pricer` — deliberately not a default); realized interval `settle_energy_portfolio` aggregation is kept structurally separate from that forward-looking NPV |
262
278
  | `quantvolt.data` | Optional (`quantvolt[data]`): provider adapters behind a `DataSource` protocol — the only package that performs I/O |
263
279
  | `quantvolt.testing` | `assert_input_unchanged` — shipped test utility for the no-mutation invariant |
264
280
  | `quantvolt.exceptions` | The `EnergyQuantError` hierarchy (`ValidationError`, `ArbitrageError`, `MissingTenorError`, …) |
@@ -280,14 +296,20 @@ positions. See [docs/risk-and-assets.md](docs/risk-and-assets.md).
280
296
 
281
297
  ## Documentation
282
298
 
299
+ - [Why QuantVolt?](https://nimabahrami.github.io/quantvolt/#/guide/why) — the integration problem,
300
+ the shared computational model, the extensible pricer registry, and an honest "what QuantVolt is
301
+ not" section
283
302
  - [docs/api.md](docs/api.md) — module-by-module API reference with runnable examples
284
303
  - [docs/risk-and-assets.md](docs/risk-and-assets.md) — the VaR family, the physical-vs-risk-neutral
285
304
  drift rule, incomplete-market hedging, dispatch & storage, and the long-dated / liquidity caveats
286
305
  - [docs/european-markets.md](docs/european-markets.md) — market coverage, power-vs-gas
287
306
  statistics, negative prices, Samuelson effect, carbon costs, data-source policy
288
- - `.kiro/steering/`product, tech, structure, and coding-style standards
289
- - `.kiro/specs/power-energy-quant-analysis/` requirements, design, and task breakdown
290
- (the library is spec-driven; the design document is the source of truth for the maths)
307
+ - [docs/validation.md](docs/validation.md)external validation evidence: QuantVolt's pricers
308
+ checked against QuantLib 1.43 golden fixtures, with honest caveats and regeneration instructions
309
+ - Three complete, runnable end-to-end tutorials (site, each backed by a `site/examples/
310
+ verify_tutorial_*.py` script): [spark spread to hedge](https://nimabahrami.github.io/quantvolt/#/guide/tutorial-spark),
311
+ [renewable PPA to CFaR](https://nimabahrami.github.io/quantvolt/#/guide/tutorial-ppa), and
312
+ [storage intrinsic to hedge](https://nimabahrami.github.io/quantvolt/#/guide/tutorial-storage)
291
313
 
292
314
  ## Development
293
315
 
@@ -4,10 +4,10 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "quantvolt"
7
- version = "0.1.0"
7
+ version = "0.2.1"
8
8
  description = "Quantitative analysis of European power and energy markets"
9
9
  readme = "README.md"
10
- requires-python = ">=3.12"
10
+ requires-python = ">=3.11"
11
11
  license = "MIT"
12
12
  license-files = ["LICENSE"]
13
13
  authors = [{ name = "Nima Bahrami", email = "nima@skillmix.nl" }]
@@ -28,7 +28,9 @@ classifiers = [
28
28
  "Intended Audience :: Financial and Insurance Industry",
29
29
  "Intended Audience :: Science/Research",
30
30
  "Programming Language :: Python :: 3",
31
+ "Programming Language :: Python :: 3.11",
31
32
  "Programming Language :: Python :: 3.12",
33
+ "Programming Language :: Python :: 3.13",
32
34
  "Programming Language :: Rust",
33
35
  "Topic :: Office/Business :: Financial :: Investment",
34
36
  "Topic :: Scientific/Engineering :: Mathematics",
@@ -61,12 +63,20 @@ dev = [
61
63
  "maturin>=1.7,<2.0",
62
64
  ]
63
65
 
66
+ # PEP 735 dependency group for OFFLINE external-validation fixture generation only.
67
+ # NOT a [project.optional-dependencies] extra and NOT a core/runtime dependency: the
68
+ # analytics core never imports QuantLib, CI never installs this group, and the generator
69
+ # scripts under scripts/ (wheel-excluded above) are the only consumers. See
70
+ # .kiro/specs/external-validation/ and docs/validation.md for the regeneration workflow.
71
+ [dependency-groups]
72
+ validation = ["QuantLib>=1.34"]
73
+
64
74
  [tool.maturin]
65
75
  python-source = "src"
66
76
  module-name = "quantvolt._core"
67
- # abi3-py312: build a single stable-ABI wheel that works on CPython 3.12+
77
+ # abi3-py311: build a single stable-ABI wheel that works on CPython 3.11+
68
78
  # (one compile per platform instead of one per Python version).
69
- features = ["pyo3/extension-module", "pyo3/abi3-py312"]
79
+ features = ["pyo3/extension-module", "pyo3/abi3-py311"]
70
80
  # Keep the sdist to the buildable package only. The docs site, rendered docs,
71
81
  # research data, helper scripts, and the logo are not part of the installable
72
82
  # library and would only bloat the distribution.
@@ -90,12 +100,20 @@ exclude = [
90
100
  [tool.ruff]
91
101
  line-length = 100
92
102
  src = ["src", "tests"]
103
+ target-version = "py311"
93
104
 
94
105
  [tool.ruff.lint]
95
106
  select = ["E", "F", "I", "UP", "B", "SIM", "RUF"]
96
107
 
108
+ [tool.ruff.lint.per-file-ignores]
109
+ # Authored documentation text (AUTHORED_DOCS) carries long human-readable lines;
110
+ # RUF001 flags the en-dashes used deliberately in that prose.
111
+ "site/build_api.py" = ["E501", "RUF001"]
112
+ # One-off internal refactoring tool, not shipped code.
113
+ "site/tools/refactor_reference_design.py" = ["E501"]
114
+
97
115
  [tool.mypy]
98
- python_version = "3.12"
116
+ python_version = "3.11"
99
117
  strict = true
100
118
  files = ["src/quantvolt"]
101
119
 
@@ -490,11 +490,11 @@ pub fn simulate_correlated_forwards_term_core(
490
490
  if antithetic {
491
491
  write_record(&mut data, p_minus, 0, rec, dim, &minus);
492
492
  }
493
- for step in 0..steps {
493
+ for (step, factor) in factors.iter().enumerate().take(steps) {
494
494
  for e in &mut eps {
495
495
  *e = rng.sample::<f64, _>(StandardNormal);
496
496
  }
497
- matvec_lower(&factors[step], &eps, dim, &mut incr);
497
+ matvec_lower(factor, &eps, dim, &mut incr);
498
498
  let offset = step * dim;
499
499
  for d in 0..dim {
500
500
  if active_steps[offset + d] {
@@ -22,6 +22,7 @@ from .assets import (
22
22
  PhysicalFactorMapping,
23
23
  PlantModel,
24
24
  StorageModel,
25
+ ValuationSource,
25
26
  bang_bang,
26
27
  dispatch_deterministic,
27
28
  dispatch_value,
@@ -70,7 +71,12 @@ from .hedging import (
70
71
  )
71
72
  from .models import (
72
73
  BUILT_IN_COMMODITIES,
74
+ CachedAssetValuation,
75
+ CapFloorStripContract,
76
+ CapFloorType,
77
+ ChangeInLawAllocation,
73
78
  CommodityConfig,
79
+ CurtailmentTreatment,
74
80
  CurveNode,
75
81
  DeliveryPeriod,
76
82
  DeliverySchedule,
@@ -81,24 +87,43 @@ from .models import (
81
87
  Granularity,
82
88
  Greeks,
83
89
  Hub,
90
+ IndexationStep,
84
91
  InstrumentPriceRecord,
85
92
  Moneyness,
93
+ NegativePriceClause,
94
+ NegativePriceTreatment,
95
+ OptionSide,
96
+ OptionType,
86
97
  PipelineRight,
87
98
  PlantConfig,
88
99
  PowerDeliveryInterval,
89
100
  PowerHedgeContract,
90
101
  PowerHedgePosition,
91
102
  PowerHedgeType,
103
+ PpaAvailabilityGuarantee,
92
104
  PpaContract,
105
+ PpaContractMetadata,
106
+ PpaCreditSupportType,
107
+ PpaPriceTerms,
108
+ PpaReconciliationPeriod,
109
+ PpaReconciliationTerms,
93
110
  PpaSettlementType,
111
+ PpaTerms,
112
+ PpaToleranceBand,
94
113
  PpaVolumeBasis,
114
+ PpaVolumeTerms,
115
+ PriceUnit,
95
116
  RiskType,
96
117
  SettlementType,
118
+ SpreadOptionContract,
97
119
  SwapContract,
120
+ TollingAgreement,
98
121
  TransmissionRight,
99
122
  TransportDirection,
123
+ VanillaOptionContract,
100
124
  VolatilitySurface,
101
125
  VolatilityTenor,
126
+ convert_price,
102
127
  )
103
128
  from .portfolio import (
104
129
  Instrument,
@@ -114,6 +139,8 @@ from .portfolio import (
114
139
  )
115
140
  from .pricing import (
116
141
  AsianOptionRequest,
142
+ BachelierOptionRequest,
143
+ BachelierOptionResult,
117
144
  BarrierOptionRequest,
118
145
  CapFloorRequest,
119
146
  CapFloorResult,
@@ -129,6 +156,11 @@ from .pricing import (
129
156
  PowerHedgeSettlement,
130
157
  PpaDataColumns,
131
158
  PpaIntervalSettlement,
159
+ PpaPeriodValuation,
160
+ PpaPeriodVolume,
161
+ PpaReconciliationColumns,
162
+ PpaValuationResult,
163
+ PpaVolumeProfile,
132
164
  SpreadOptionRequest,
133
165
  SpreadOptionResult,
134
166
  SwapPricingResult,
@@ -147,19 +179,23 @@ from .pricing import (
147
179
  futures_delta,
148
180
  implied_heat_rate,
149
181
  implied_vol,
182
+ make_ppa_pricer,
150
183
  mark_to_market,
151
184
  power_cap_payoff,
152
185
  power_floor_payoff,
153
186
  price_asian,
187
+ price_bachelier_option,
154
188
  price_barrier,
155
189
  price_cap_floor,
156
190
  price_futures,
157
191
  price_lookback,
192
+ price_ppa,
158
193
  price_spark_spread_option,
159
194
  price_spread_option,
160
195
  price_swap,
161
196
  price_tolling_agreement,
162
197
  price_vanilla_option,
198
+ reconcile_ppa_ledger,
163
199
  settle_power_hedge_interval,
164
200
  settle_power_hedges_frame,
165
201
  settle_ppa_frame,
@@ -188,7 +224,7 @@ from .risk import (
188
224
  parametric_var,
189
225
  )
190
226
 
191
- __version__ = "0.1.0"
227
+ __version__ = "0.2.1"
192
228
 
193
229
  __all__ = [
194
230
  "BUILT_IN_COMMODITIES",
@@ -197,13 +233,20 @@ __all__ = [
197
233
  "ArbitrageWarning",
198
234
  "AsianOptionRequest",
199
235
  "AuthenticationError",
236
+ "BachelierOptionRequest",
237
+ "BachelierOptionResult",
200
238
  "BangBangHedgeWarning",
201
239
  "BarrierOptionRequest",
240
+ "CachedAssetValuation",
202
241
  "CapFloorRequest",
203
242
  "CapFloorResult",
243
+ "CapFloorStripContract",
244
+ "CapFloorType",
204
245
  "CashflowStrategyComparison",
205
246
  "CashflowStrategyMetrics",
247
+ "ChangeInLawAllocation",
206
248
  "CommodityConfig",
249
+ "CurtailmentTreatment",
207
250
  "CurveBuildResult",
208
251
  "CurveBuilder",
209
252
  "CurveNode",
@@ -229,6 +272,7 @@ __all__ = [
229
272
  "Greeks",
230
273
  "Hub",
231
274
  "ImpliedVolResult",
275
+ "IndexationStep",
232
276
  "Instrument",
233
277
  "InstrumentPriceRecord",
234
278
  "InsufficientDataError",
@@ -243,8 +287,12 @@ __all__ = [
243
287
  "MtMResult",
244
288
  "MultifactorForwardModel",
245
289
  "NativeExtensionError",
290
+ "NegativePriceClause",
291
+ "NegativePriceTreatment",
246
292
  "NoPricingDataError",
247
293
  "NumericalError",
294
+ "OptionSide",
295
+ "OptionType",
248
296
  "PhysicalFactorMapping",
249
297
  "PipelineRight",
250
298
  "PlantConfig",
@@ -259,16 +307,31 @@ __all__ = [
259
307
  "PowerHedgePosition",
260
308
  "PowerHedgeSettlement",
261
309
  "PowerHedgeType",
310
+ "PpaAvailabilityGuarantee",
262
311
  "PpaContract",
312
+ "PpaContractMetadata",
313
+ "PpaCreditSupportType",
263
314
  "PpaDataColumns",
264
315
  "PpaIntervalSettlement",
265
316
  "PpaNominationCandidate",
266
317
  "PpaNominationColumns",
267
318
  "PpaNominationFit",
268
319
  "PpaNominationObjective",
320
+ "PpaPeriodValuation",
321
+ "PpaPeriodVolume",
322
+ "PpaPriceTerms",
323
+ "PpaReconciliationColumns",
324
+ "PpaReconciliationPeriod",
325
+ "PpaReconciliationTerms",
269
326
  "PpaSettlementType",
327
+ "PpaTerms",
328
+ "PpaToleranceBand",
329
+ "PpaValuationResult",
270
330
  "PpaVolumeBasis",
331
+ "PpaVolumeProfile",
332
+ "PpaVolumeTerms",
271
333
  "PpaWalkForwardResult",
334
+ "PriceUnit",
272
335
  "PricedPosition",
273
336
  "RateLimitError",
274
337
  "RiskEngine",
@@ -281,16 +344,20 @@ __all__ = [
281
344
  "SchwartzSmithParams",
282
345
  "SettledPortfolioPosition",
283
346
  "SettlementType",
347
+ "SpreadOptionContract",
284
348
  "SpreadOptionRequest",
285
349
  "SpreadOptionResult",
286
350
  "StorageModel",
287
351
  "SwapContract",
288
352
  "SwapPricingResult",
353
+ "TollingAgreement",
289
354
  "TollingResult",
290
355
  "TransmissionRight",
291
356
  "TransportDirection",
292
357
  "TransportRightResult",
293
358
  "ValidationError",
359
+ "ValuationSource",
360
+ "VanillaOptionContract",
294
361
  "VanillaOptionRequest",
295
362
  "VanillaOptionResult",
296
363
  "VolatilitySurface",
@@ -307,6 +374,7 @@ __all__ = [
307
374
  "classify_moneyness",
308
375
  "clean_spread",
309
376
  "compare_cashflow_strategies",
377
+ "convert_price",
310
378
  "crack_spread",
311
379
  "credit_var",
312
380
  "dark_spread",
@@ -321,21 +389,25 @@ __all__ = [
321
389
  "implied_heat_rate",
322
390
  "implied_vol",
323
391
  "linear_cross_hedge",
392
+ "make_ppa_pricer",
324
393
  "mark_to_market",
325
394
  "monte_carlo_var",
326
395
  "parametric_var",
327
396
  "power_cap_payoff",
328
397
  "power_floor_payoff",
329
398
  "price_asian",
399
+ "price_bachelier_option",
330
400
  "price_barrier",
331
401
  "price_cap_floor",
332
402
  "price_futures",
333
403
  "price_lookback",
404
+ "price_ppa",
334
405
  "price_spark_spread_option",
335
406
  "price_spread_option",
336
407
  "price_swap",
337
408
  "price_tolling_agreement",
338
409
  "price_vanilla_option",
410
+ "reconcile_ppa_ledger",
339
411
  "settle_energy_portfolio",
340
412
  "settle_power_hedge_interval",
341
413
  "settle_power_hedges_frame",
@@ -1,5 +1,5 @@
1
1
  """Reusable boundary-validation guards. Public entry points validate through these before any
2
- computation (fail loudly; see .kiro/steering/coding-style.md). All raise ValidationError."""
2
+ computation, raising ``ValidationError`` on the first violated constraint."""
3
3
 
4
4
  from __future__ import annotations
5
5