quantvolt 0.2.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 (117) hide show
  1. {quantvolt-0.2.0 → quantvolt-0.2.1}/Cargo.lock +1 -1
  2. {quantvolt-0.2.0 → quantvolt-0.2.1}/Cargo.toml +1 -1
  3. {quantvolt-0.2.0 → quantvolt-0.2.1}/PKG-INFO +6 -8
  4. {quantvolt-0.2.0 → quantvolt-0.2.1}/README.md +5 -7
  5. {quantvolt-0.2.0 → quantvolt-0.2.1}/pyproject.toml +1 -1
  6. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/__init__.py +1 -1
  7. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/_validation.py +1 -1
  8. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/assets/dispatch_approx.py +43 -43
  9. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/assets/dispatch_deterministic.py +9 -11
  10. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/assets/dispatch_sdp.py +54 -54
  11. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/assets/long_dated.py +81 -85
  12. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/assets/plant.py +4 -4
  13. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/assets/storage.py +34 -34
  14. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/cli.py +2 -4
  15. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/curvemodels/__init__.py +1 -1
  16. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/curvemodels/multifactor.py +13 -13
  17. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/curvemodels/schwartz_smith.py +35 -38
  18. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/curves/arbitrage.py +8 -10
  19. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/curves/builder.py +10 -11
  20. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/data/__init__.py +2 -2
  21. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/data/base.py +12 -12
  22. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/data/commercial.py +3 -3
  23. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/data/entsoe.py +11 -11
  24. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/data/entsog.py +5 -6
  25. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/data/open_meteo.py +5 -5
  26. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/exceptions.py +1 -1
  27. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/hedging/__init__.py +3 -4
  28. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/hedging/hybrid.py +11 -11
  29. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/hedging/mean_variance.py +5 -5
  30. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/hedging/variance_min.py +11 -11
  31. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/market/outages.py +80 -83
  32. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/market/transmission.py +2 -2
  33. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/market/weather.py +1 -1
  34. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/models/commodity.py +2 -2
  35. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/models/curve.py +4 -4
  36. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/models/discount_curve.py +1 -1
  37. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/models/instruments.py +112 -119
  38. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/models/ppa_terms.py +24 -24
  39. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/models/schedule.py +1 -1
  40. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/models/units.py +9 -9
  41. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/models/vol_surface.py +1 -1
  42. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/numerics/black76.py +2 -3
  43. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/numerics/daycount.py +1 -1
  44. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/numerics/interpolation.py +1 -1
  45. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/numerics/monte_carlo.py +14 -13
  46. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/numerics/risk_adjustment.py +3 -3
  47. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/numerics/rootfind.py +1 -1
  48. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/numerics/spread_models.py +1 -1
  49. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/portfolio/model.py +6 -6
  50. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/portfolio/valuation.py +81 -96
  51. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/bachelier.py +3 -0
  52. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/exotic.py +44 -46
  53. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/futures.py +7 -7
  54. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/implied_vol.py +7 -8
  55. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/mark_to_market.py +8 -8
  56. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/ppa.py +13 -16
  57. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/ppa_valuation.py +16 -18
  58. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/spread_option.py +13 -13
  59. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/spreads.py +85 -90
  60. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/swap.py +6 -6
  61. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/tolling.py +52 -55
  62. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/transmission_right.py +11 -12
  63. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/vanilla.py +35 -24
  64. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/risk/aggregation.py +3 -3
  65. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/risk/cfar.py +9 -9
  66. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/risk/covariance.py +2 -2
  67. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/risk/credit_var.py +15 -15
  68. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/risk/engine.py +9 -9
  69. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/risk/mc_var.py +60 -61
  70. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/risk/parametric_var.py +56 -59
  71. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/risk/scenarios.py +5 -5
  72. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/stats/correlation.py +2 -2
  73. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/stats/descriptive.py +3 -3
  74. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/stats/mean_reversion.py +1 -1
  75. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/stats/normality.py +1 -1
  76. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/stats/stationarity.py +1 -1
  77. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/testing.py +1 -1
  78. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/workflow/criteria.py +3 -3
  79. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/workflow/modeling.py +10 -11
  80. {quantvolt-0.2.0 → quantvolt-0.2.1}/.gitignore +0 -0
  81. {quantvolt-0.2.0 → quantvolt-0.2.1}/LICENSE +0 -0
  82. {quantvolt-0.2.0 → quantvolt-0.2.1}/rust/src/asian_mc.rs +0 -0
  83. {quantvolt-0.2.0 → quantvolt-0.2.1}/rust/src/lib.rs +0 -0
  84. {quantvolt-0.2.0 → quantvolt-0.2.1}/rust/src/paths.rs +0 -0
  85. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/_core.pyi +0 -0
  86. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/assets/__init__.py +0 -0
  87. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/assets/_tolerance.py +0 -0
  88. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/curves/__init__.py +0 -0
  89. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/data/dataset_catalog.json +0 -0
  90. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/data/datasets.py +0 -0
  91. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/data/netztransparenz.py +0 -0
  92. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/data/smard.py +0 -0
  93. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/hedging/_conditioning.py +0 -0
  94. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/hedging/ppa_nomination.py +0 -0
  95. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/hedging/ppa_walk_forward.py +0 -0
  96. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/market/__init__.py +0 -0
  97. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/models/__init__.py +0 -0
  98. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/models/greeks.py +0 -0
  99. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/models/interval.py +0 -0
  100. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/models/power_hedge.py +0 -0
  101. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/models/ppa.py +0 -0
  102. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/numerics/__init__.py +0 -0
  103. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/numerics/_degenerate.py +0 -0
  104. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/numerics/_normal.py +0 -0
  105. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/numerics/bachelier.py +0 -0
  106. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/numerics/exotic.py +0 -0
  107. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/portfolio/__init__.py +0 -0
  108. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/portfolio/settlement.py +0 -0
  109. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/__init__.py +0 -0
  110. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/_dates.py +0 -0
  111. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/pricing/power_hedge.py +0 -0
  112. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/py.typed +0 -0
  113. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/risk/__init__.py +0 -0
  114. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/risk/_levels.py +0 -0
  115. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/risk/cashflow_metrics.py +0 -0
  116. {quantvolt-0.2.0 → quantvolt-0.2.1}/src/quantvolt/stats/__init__.py +0 -0
  117. {quantvolt-0.2.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.2.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.2.0"
3
+ version = "0.2.1"
4
4
  edition = "2021"
5
5
  publish = false
6
6
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quantvolt
3
- Version: 0.2.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
@@ -130,10 +130,11 @@ Call premium: 45,663 EUR (delta 5,288 EUR per EUR/MWh)
130
130
  Book NPV: 9,133 EUR, VaR(95): 11,715 EUR
131
131
  ```
132
132
 
133
- Identical inputs give identical outputs: Monte Carlo honours a `seed`, inputs are validated
134
- eagerly (no silent partial results), results round-trip through plain dicts / JSON, and the
135
- library never mutates caller data `quantvolt.testing.assert_input_unchanged` lets you verify
136
- 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.
137
138
 
138
139
  ### Settle a PPA with your own interval data
139
140
 
@@ -348,9 +349,6 @@ positions. See [docs/risk-and-assets.md](docs/risk-and-assets.md).
348
349
  verify_tutorial_*.py` script): [spark spread to hedge](https://nimabahrami.github.io/quantvolt/#/guide/tutorial-spark),
349
350
  [renewable PPA to CFaR](https://nimabahrami.github.io/quantvolt/#/guide/tutorial-ppa), and
350
351
  [storage intrinsic to hedge](https://nimabahrami.github.io/quantvolt/#/guide/tutorial-storage)
351
- - `.kiro/steering/` — product, tech, structure, and coding-style standards
352
- - `.kiro/specs/power-energy-quant-analysis/` — requirements, design, and task breakdown
353
- (the library is spec-driven; the design document is the source of truth for the maths)
354
352
 
355
353
  ## Development
356
354
 
@@ -91,10 +91,11 @@ Call premium: 45,663 EUR (delta 5,288 EUR per EUR/MWh)
91
91
  Book NPV: 9,133 EUR, VaR(95): 11,715 EUR
92
92
  ```
93
93
 
94
- Identical inputs give identical outputs: Monte Carlo honours a `seed`, inputs are validated
95
- eagerly (no silent partial results), results round-trip through plain dicts / JSON, and the
96
- library never mutates caller data `quantvolt.testing.assert_input_unchanged` lets you verify
97
- 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.
98
99
 
99
100
  ### Settle a PPA with your own interval data
100
101
 
@@ -309,9 +310,6 @@ positions. See [docs/risk-and-assets.md](docs/risk-and-assets.md).
309
310
  verify_tutorial_*.py` script): [spark spread to hedge](https://nimabahrami.github.io/quantvolt/#/guide/tutorial-spark),
310
311
  [renewable PPA to CFaR](https://nimabahrami.github.io/quantvolt/#/guide/tutorial-ppa), and
311
312
  [storage intrinsic to hedge](https://nimabahrami.github.io/quantvolt/#/guide/tutorial-storage)
312
- - `.kiro/steering/` — product, tech, structure, and coding-style standards
313
- - `.kiro/specs/power-energy-quant-analysis/` — requirements, design, and task breakdown
314
- (the library is spec-driven; the design document is the source of truth for the maths)
315
313
 
316
314
  ## Development
317
315
 
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "quantvolt"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Quantitative analysis of European power and energy markets"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -224,7 +224,7 @@ from .risk import (
224
224
  parametric_var,
225
225
  )
226
226
 
227
- __version__ = "0.2.0"
227
+ __version__ = "0.2.1"
228
228
 
229
229
  __all__ = [
230
230
  "BUILT_IN_COMMODITIES",
@@ -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
 
@@ -1,42 +1,42 @@
1
- """Dispatch heuristics and approximations (Task 74; Req 21.3).
2
-
3
- Req 21.3 requires three *caller-selectable* approximations to the dispatch
4
- optimisation, each trading fidelity for tractability, plus a specific warning when
5
- the state-aggregation ("bang-bang") one is used. This module implements them as
6
- **documented input/problem transformations around the exact deterministic solver**
7
- :func:`quantvolt.assets.dispatch_deterministic.dispatch_deterministic` (eq. B.1) --
8
- the perfect-foresight optimum that is itself the upper bound the stochastic value
9
- is measured against (Property 62). Wrapping the deterministic DP (rather than the
10
- Monte-Carlo :func:`~quantvolt.assets.dispatch_sdp.dispatch_value`) keeps every
11
- approximation's error *exactly* comparable to a noise-free benchmark: the
1
+ """Dispatch heuristics and approximations.
2
+
3
+ Three caller-selectable approximations to the dispatch optimisation, each trading
4
+ fidelity for tractability, plus a specific warning when the state-aggregation
5
+ ("bang-bang") one is used. This module implements them as documented input/problem
6
+ transformations around the exact deterministic solver
7
+ ``quantvolt.assets.dispatch_deterministic.dispatch_deterministic``: the
8
+ perfect-foresight optimum that is itself the upper bound the stochastic value
9
+ is measured against. Wrapping the deterministic DP (rather than the
10
+ Monte-Carlo ``quantvolt.assets.dispatch_sdp.dispatch_value``) keeps every
11
+ approximation's error exactly comparable to a noise-free benchmark: the
12
12
  block-constant, decoupling, and bias identities below are equalities up to float
13
13
  tolerance, not statistical statements. The same three transformations apply
14
- unchanged to the stochastic solver -- a caller may block-average the factor
14
+ unchanged to the stochastic solver: a caller may block-average the factor
15
15
  model's per-step structure, divide the simulated horizon, or feed a bang-bang
16
- plant to :func:`dispatch_value` -- but the *heuristic itself* is the transform, and
16
+ plant to ``dispatch_value``, but the heuristic itself is the transform, and
17
17
  it is cleanest to specify and test against the deterministic optimum.
18
18
 
19
19
  The three approximations
20
20
  ------------------------
21
- - :func:`time_aggregate` -- **time aggregation** (hourly -> 4/8/16-hour blocks).
21
+ - ``time_aggregate``: time aggregation (hourly -> 4/8/16-hour blocks).
22
22
  Averages the price/temperature series into equal blocks, solves the (much
23
23
  shorter) coarse dispatch, and rescales the coarse value back to the original
24
24
  number of hours. Exact on block-constant prices for a unit that incurs no start
25
25
  within the horizon; the intra-block ramp/commitment freedom it drops is the
26
26
  approximation.
27
- - :func:`horizon_divide` -- **horizon division** (weekly / monthly sub-horizons).
28
- Splits the horizon into consecutive sub-horizons solved *independently* and sums
29
- their values. The boundary-state approximation -- each sub-horizon after the
27
+ - ``horizon_divide``: horizon division (weekly / monthly sub-horizons).
28
+ Splits the horizon into consecutive sub-horizons solved independently and sums
29
+ their values. The boundary-state approximation: each sub-horizon after the
30
30
  first restarts from a cold, restart-ready offline state, dropping the true
31
- carried-over commitment state -- decouples the subproblems. Exact when the
31
+ carried-over commitment state, decouples the subproblems. Exact when the
32
32
  periods decouple (zero start costs and non-binding min-run / min-down / ramp).
33
- - :func:`bang_bang` -- **state aggregation** for steep heat-rate curves. Collapses
33
+ - ``bang_bang``: state aggregation for steep heat-rate curves. Collapses
34
34
  the online output grid to the single full-load level, so the unit is either off
35
- or running at ``c_max`` ({0, c_max}). Exact for a *linear* (constant marginal)
35
+ or running at ``c_max`` ({0, c_max}). Exact for a linear (constant marginal)
36
36
  heat rate, where the optimal load is always a corner; a downward-biased lower
37
37
  bound for a steep (rising-marginal) curve, where the exact optimum runs at an
38
38
  efficient part load the approximation cannot represent. Emits
39
- :class:`BangBangHedgeWarning` (Req 21.3): hedges are far more sensitive than
39
+ ``BangBangHedgeWarning``: hedges are far more sensitive than
40
40
  values to the heat-curve approximation.
41
41
 
42
42
  Direction-of-bias summary (the "documented direction" the tests assert)
@@ -70,17 +70,17 @@ from .plant import MaxCapacityCurve, PlantModel
70
70
 
71
71
 
72
72
  class BangBangHedgeWarning(UserWarning):
73
- """Advisory that bang-bang state aggregation biases hedges far more than values (Req 21.3).
73
+ """Advisory that bang-bang state aggregation biases hedges far more than values.
74
74
 
75
75
  Collapsing the output grid to {0, c_max} pins the operating point to full load,
76
76
  so the plant value loses only the (often small) part-load optionality. The
77
- *sensitivities* the deltas and critical-dispatch surfaces used to hedge are
77
+ sensitivities (the deltas and critical-dispatch surfaces used to hedge) are
78
78
  a different matter: they read the slope of value against price, and the
79
79
  approximation replaces the true, curved heat-rate response with a single kink
80
80
  at the on/off boundary. A hedge derived from a bang-bang model can therefore be
81
81
  badly wrong even when the headline value looks reasonable. The approximation is
82
- not rejected for a sufficiently steep heat curve the value error is genuinely
83
- negligible but the caller is warned so the choice is deliberate.
82
+ not rejected: for a sufficiently steep heat curve the value error is genuinely
83
+ negligible, but the caller is warned so the choice is deliberate.
84
84
  """
85
85
 
86
86
 
@@ -109,7 +109,7 @@ def time_aggregate(
109
109
  output_step: float | None = None,
110
110
  discount_factors: Sequence[float] | None = None,
111
111
  ) -> DispatchSchedule:
112
- """Time-aggregation heuristic: solve on ``block_hours``-hour blocks, rescale (Req 21.3).
112
+ """Time-aggregation heuristic: solve on ``block_hours``-hour blocks, rescale.
113
113
 
114
114
  Each consecutive run of ``block_hours`` periods is averaged into one coarse
115
115
  period (power, fuel, temperature, and — if supplied — discount factor), the
@@ -229,21 +229,21 @@ def horizon_divide(
229
229
  output_step: float | None = None,
230
230
  discount_factors: Sequence[float] | None = None,
231
231
  ) -> DispatchSchedule:
232
- """Horizon-division heuristic: solve independent sub-horizons and sum (Req 21.3).
232
+ """Horizon-division heuristic: solve independent sub-horizons and sum.
233
233
 
234
234
  The horizon is cut into consecutive sub-horizons of length ``sub_horizon`` (the
235
- last may be shorter) that are solved *independently* by the deterministic DP and
235
+ last may be shorter) that are solved independently by the deterministic DP and
236
236
  concatenated; ``total_value`` is the sum of the sub-values. This is the weekly /
237
237
  monthly sub-period heuristic that keeps each solve small.
238
238
 
239
- Boundary-state approximation. Only the **first** sub-horizon sees the caller's
239
+ Boundary-state approximation. Only the first sub-horizon sees the caller's
240
240
  initial condition; every later sub-horizon restarts from a cold, restart-ready
241
- *offline* state. Dropping the true carried-over commitment state is exactly what
242
- decouples the subproblems and is the approximation. It is **exact** when the
241
+ offline state. Dropping the true carried-over commitment state is exactly what
242
+ decouples the subproblems, and is the approximation. It is exact when the
243
243
  periods decouple (zero start costs and non-binding min-run / min-down / ramp),
244
244
  because the dispatch is then myopic and the start-up state is immaterial. It
245
- **typically understates** value otherwise (each boundary can pay a spurious
246
- restart cost), but it *can* overstate value when a binding min-run / min-down
245
+ typically understates value otherwise (each boundary can pay a spurious
246
+ restart cost), but it can overstate value when a binding min-run / min-down
247
247
  constraint that would span a boundary in the full solve is artificially relaxed
248
248
  by the cut.
249
249
 
@@ -335,34 +335,34 @@ def bang_bang(
335
335
  output_step: float | None = None,
336
336
  discount_factors: Sequence[float] | None = None,
337
337
  ) -> DispatchSchedule:
338
- """Bang-bang state aggregation: run at full load or off, {0, c_max} (Req 21.3).
338
+ """Bang-bang state aggregation: run at full load or off, {0, c_max}.
339
339
 
340
340
  Collapses the online output grid to a single full-load level so the unit is
341
- either off or running at ``c_max`` the state aggregation Appendix B recommends
342
- for a *sufficiently steep* heat-rate curve, where the optimal load is (almost)
341
+ either off or running at ``c_max``, recommended for a sufficiently steep
342
+ heat-rate curve, where the optimal load is (almost)
343
343
  always a corner. Implemented by solving the deterministic DP on a derived plant
344
344
  whose ``c_min`` and ``c_max`` are both pinned to that full-load level; every
345
345
  other operating characteristic (heat-rate curve, start costs, ramp, durations)
346
- is unchanged. The input plant is **not** mutated a new :class:`PlantModel` is
346
+ is unchanged. The input plant is not mutated: a new ``PlantModel`` is
347
347
  derived.
348
348
 
349
- Full-load level. With a constant capacity the pinned level *is* ``c_max``. With a
349
+ Full-load level. With a constant capacity the pinned level is ``c_max``. With a
350
350
  temperature-dependent ``c_max(temp)`` the level is the horizon-minimum ``c_max``
351
351
  (the largest constant full-output level feasible in every period); a period
352
352
  whose capacity falls below the plant's own ``c_min`` makes the plant infeasible
353
353
  there and is rejected, mirroring the deterministic solver's curve check.
354
354
 
355
- Exactness and bias. For a **linear** (constant-marginal) heat rate the per-MWh
355
+ Exactness and bias. For a linear (constant-marginal) heat rate the per-MWh
356
356
  margin is output-independent, so the exact optimum is a corner and bang-bang is
357
- exact. For a **steep** (rising-marginal) curve the exact optimum may run at an
357
+ exact. For a steep (rising-marginal) curve the exact optimum may run at an
358
358
  efficient part load that bang-bang cannot represent, so the value is a
359
- **downward-biased lower bound**: whenever the full-load level is reachable in one
359
+ downward-biased lower bound: whenever the full-load level is reachable in one
360
360
  step (ramp rate >= operating range) the bang-bang on/off policy set is a strict
361
361
  subset of the exact one, and restricting the load choice can only lose value.
362
362
 
363
- Warning (Req 21.3). Emits :class:`BangBangHedgeWarning`: hedges (sensitivities
363
+ Warning. Emits ``BangBangHedgeWarning``: hedges (sensitivities
364
364
  and critical-dispatch surfaces) are far more sensitive than the value itself to
365
- the heat-rate-curve approximation, because they read the *slope* of value
365
+ the heat-rate-curve approximation, because they read the slope of value
366
366
  against price, which the single on/off kink distorts even where the value error
367
367
  is negligible.
368
368
 
@@ -1,13 +1,12 @@
1
- """Perfect-foresight thermal dispatch (Task 72; Req 21.1 partial, 21.4; Property 62).
1
+ """Perfect-foresight thermal dispatch.
2
2
 
3
3
  ``dispatch_deterministic`` solves the deterministic optimization of Appendix B
4
4
  eq. B.1: with all prices, fuel costs and temperatures known in advance, it finds
5
5
  the commitment-and-output schedule that maximizes total margin subject to the
6
6
  operational and state-transition constraints. Because it optimizes over the
7
- *fully known* path, its value is the **upper bound** that any non-anticipating
8
- (stochastic) policy is tested against the ``dispatch_value
9
- dispatch_deterministic`` guarantee of Property 62 (validated with the Task-73
10
- stochastic engine). ``total_value`` is therefore the headline field.
7
+ fully known path, its value is the upper bound that any non-anticipating
8
+ (stochastic) policy is tested against: the ``dispatch_value <=
9
+ dispatch_deterministic`` guarantee. ``total_value`` is therefore the headline field.
11
10
 
12
11
  Objective (eq. B.1), per period ``t`` when producing ``q_t``::
13
12
 
@@ -61,8 +60,8 @@ Conventions (documented, standard unit-commitment)
61
60
  lag is not itself a "start".
62
61
  - **Full availability.** The deterministic perfect-foresight benchmark assumes
63
62
  no forced outages (derate multiplier ``M ≡ 1``); the plant's ``outage_rate``
64
- ``λ`` is consumed only by the stochastic model (Task 73). Perfect foresight
65
- *and* no outages is exactly the upper bound Property 62 needs.
63
+ ``λ`` is consumed only by the stochastic model. Perfect foresight
64
+ and no outages is exactly what the upper-bound property needs.
66
65
  - **Terminal condition.** No salvage or penalty at the horizon end: the unit may
67
66
  finish online without having completed its min-run (finite-window truncation).
68
67
  - **Discounting.** ``discount_factors`` (default all 1.0, matching eq. B.1's
@@ -112,8 +111,7 @@ class DispatchSchedule:
112
111
  stopped: Whether the unit shuts down in the period.
113
112
  margins: Per-period contribution to ``total_value`` (discounted cash
114
113
  flow, net of any start cost); ``sum(margins) == total_value``.
115
- total_value: The optimal total value the perfect-foresight upper bound
116
- (Property 62).
114
+ total_value: The optimal total value, the perfect-foresight upper bound.
117
115
  """
118
116
 
119
117
  outputs: tuple[float, ...]
@@ -335,8 +333,8 @@ def dispatch_deterministic(
335
333
  discount_factors: Per-period discount factors (default all 1.0).
336
334
 
337
335
  Returns:
338
- The optimal :class:`DispatchSchedule`; ``total_value`` is the
339
- perfect-foresight upper bound of Property 62.
336
+ The optimal ``DispatchSchedule``; ``total_value`` is the
337
+ perfect-foresight upper bound.
340
338
 
341
339
  Raises:
342
340
  ValidationError: If the series are empty or of unequal length; if a
@@ -1,27 +1,27 @@
1
- """Stochastic dispatch valuation (Task 73; Req 21.1, 21.2, 21.5, 21.6; Properties 62-63).
1
+ """Stochastic dispatch valuation.
2
2
 
3
3
  ``dispatch_value`` computes the risk-adjusted expected value of a thermal unit by
4
- stochastic dynamic programming -- the Bellman recursion of Appendix B eqs. B.2-B.3
5
- solved by backward induction over the *same* commitment state machine as
6
- :mod:`quantvolt.assets.dispatch_deterministic`. Two solution methods are offered
7
- (Req 21.2): ``method="tree"`` (recombining lattice / binomial-forest backward
4
+ stochastic dynamic programming: the Bellman recursion of Appendix B eqs. B.2-B.3
5
+ solved by backward induction over the same commitment state machine as
6
+ ``quantvolt.assets.dispatch_deterministic``. Two solution methods are offered:
7
+ ``method="tree"`` (recombining lattice / binomial-forest backward
8
8
  induction) and ``method="lsm"`` (least-squares Monte Carlo, Longstaff-Schwartz).
9
- Both return a :class:`DispatchResult` carrying the value and the four critical
9
+ Both return a ``DispatchResult`` carrying the value and the four critical
10
10
  exercise surfaces of eq. B.5 (start-up / shutdown / ramp-up / ramp-down).
11
11
 
12
12
  Relationship to the deterministic benchmark
13
13
  --------------------------------------------
14
- The perfect-foresight optimum (:func:`dispatch_deterministic`, eq. B.1) is the
14
+ The perfect-foresight optimum (``dispatch_deterministic``, eq. B.1) is the
15
15
  upper bound of any non-anticipating policy: valuing each realised path with
16
- perfect foresight and averaging bounds the stochastic value from above
17
- (Property 62). This module reuses the deterministic module's *private* state
16
+ perfect foresight and averaging bounds the stochastic value from above.
17
+ This module reuses the deterministic module's private state
18
18
  machine (the status codes, output grid, initial-state translation and the
19
19
  feasible-transition enumerator ``_transitions``) so the two solvers optimise over
20
- an identical feasible set -- the reuse, not a re-implementation, is what makes
21
- Property 62 hold exactly on shared conventions. Only the immediate-cash-flow
22
- arithmetic is recomputed here (vectorised over scenarios); the intricate
23
- feasibility logic is never duplicated. Should ``dispatch_deterministic`` ever
24
- need to change those helpers, this module must be revisited (documented coupling).
20
+ an identical feasible set: the reuse, not a re-implementation, is what makes
21
+ the upper-bound property hold exactly on shared conventions. Only the
22
+ immediate-cash-flow arithmetic is recomputed here (vectorised over scenarios);
23
+ the intricate feasibility logic is never duplicated. Should ``dispatch_deterministic``
24
+ ever need to change those helpers, this module must be revisited (documented coupling).
25
25
 
26
26
  Reused state machine (see ``dispatch_deterministic`` for the full account)
27
27
  --------------------------------------------------------------------------
@@ -35,42 +35,42 @@ so the Bellman continuation is the plain (undiscounted-further) sum of future
35
35
  discounted cash flows -- equivalent to eq. B.2's stepwise ``e^{-r dt}`` when the
36
36
  factors are ``e^{-r t dt}``.
37
37
 
38
- The Markov state and the on-peak / off-peak split (Req 21.6, eq. B.4)
39
- ---------------------------------------------------------------------
40
- Appendix B stresses that an hourly power-price *sequence* is **not** Markovian:
38
+ The Markov state and the on-peak / off-peak split (eq. B.4)
39
+ -------------------------------------------------------------
40
+ Appendix B stresses that an hourly power-price sequence is not Markovian:
41
41
  tomorrow's on-peak price depends on recent on-peak prices, only weakly on the
42
- current off-peak price. The fix (eq. B.4) is to carry **two** power spot processes
43
- -- one on-peak, one off-peak -- as separate state coordinates, restoring the
44
- Markov property. :class:`DispatchFactorModel` implements the split *in the factor
45
- definition*: ``power_on_index`` and ``power_off_index`` name two coordinates of the
42
+ current off-peak price. The fix (eq. B.4) is to carry two power spot processes,
43
+ one on-peak, one off-peak, as separate state coordinates, restoring the
44
+ Markov property. ``DispatchFactorModel`` implements the split in the factor
45
+ definition: ``power_on_index`` and ``power_off_index`` name two coordinates of the
46
46
  simulated log-forward state (they may coincide when every period shares a peak
47
- kind), and ``peak_kinds[t]`` selects which coordinate is the *active* spot price
47
+ kind), and ``peak_kinds[t]`` selects which coordinate is the active spot price
48
48
  for period ``t``. Both coordinates evolve on every step and both enter the LSM
49
49
  regression basis / the lattice, so the conditioning information is the full
50
- Markov state ``(P_on, P_off, G, ...)`` -- exactly the eq. B.4 construction.
50
+ Markov state ``(P_on, P_off, G, ...)``, exactly the eq. B.4 construction.
51
51
 
52
- Risk-adjusted expectation (Req 21.5, consistent with Req 19)
53
- ------------------------------------------------------------
54
- eq. B.2 optimises under a *risk-adjusted* expectation ``E*``. The factor model's
52
+ Risk-adjusted expectation
53
+ --------------------------
54
+ eq. B.2 optimises under a risk-adjusted expectation ``E*``. The factor model's
55
55
  per-step drift ``mu`` is therefore tagged with
56
- :class:`~quantvolt.numerics.risk_adjustment.DriftKind`, and ``dispatch_value``
57
- requires :attr:`~quantvolt.numerics.risk_adjustment.DriftKind.RISK_NEUTRAL` -- the
56
+ ``quantvolt.numerics.risk_adjustment.DriftKind``, and ``dispatch_value``
57
+ requires ``DriftKind.RISK_NEUTRAL``: the
58
58
  pricing / valuation measure in this library's vocabulary ("arbitrage-free
59
- valuation only"). A :attr:`DriftKind.PHYSICAL` drift is **rejected**: valuing a
59
+ valuation only"). A ``DriftKind.PHYSICAL`` drift is rejected: valuing a
60
60
  not-fully-hedgeable asset under the real-world measure would overstate its value,
61
61
  the very failure this optimiser guards against. The caller forms the risk-adjusted
62
62
  drift ``mu* = mu - lambda_S * sigma`` (eq. 10.5, via
63
- :func:`~quantvolt.numerics.risk_adjustment.risk_adjusted_drift`) -- for a fully
64
- tradable factor this collapses to the risk-neutral ``r - y`` -- and tags it
63
+ ``quantvolt.numerics.risk_adjustment.risk_adjusted_drift``): for a fully
64
+ tradable factor this collapses to the risk-neutral ``r - y``, and tags it
65
65
  ``RISK_NEUTRAL`` to signal "this drift is under the pricing measure ``E*``".
66
66
 
67
67
  Forced outages (the outage seam)
68
68
  --------------------------------
69
69
  An optional per-period ``availability`` vector ``M`` in ``(0, 1]`` derates the
70
70
  capacity ceiling to ``M_t * c_max(S_t)`` for period ``t`` (eq. B.1's ``M_ti``). A
71
- caller with an :class:`~quantvolt.market.outages.OutageDataset` supplies
72
- ``M_t = dataset.forced_outage_multiplier(period_hours, installed_capacity_mw)``
73
- (design 2.23). Online outputs above the derated ceiling become infeasible that
71
+ caller with an ``quantvolt.market.outages.OutageDataset`` supplies
72
+ ``M_t = dataset.forced_outage_multiplier(period_hours, installed_capacity_mw)``.
73
+ Online outputs above the derated ceiling become infeasible that
74
74
  period; if the derated ceiling falls below ``c_min`` the unit is forced offline
75
75
  (eq. B.1 constraint 5).
76
76
 
@@ -234,8 +234,8 @@ class DispatchDiagnostics:
234
234
  class DispatchFactorModel:
235
235
  """Risk-adjusted factor dynamics driving stochastic dispatch (eqs. B.2-B.4).
236
236
 
237
- The stochastic factors are correlated log-forwards evolved by the Task-62
238
- engine ``ΔZ = mu + L·ε`` (GBM). At least a power and a gas coordinate are
237
+ The stochastic factors are correlated log-forwards evolved by the shared
238
+ simulation engine ``ΔZ = mu + L·ε`` (GBM). At least a power and a gas coordinate are
239
239
  required; the on-peak / off-peak Markov split (eq. B.4) is expressed by naming
240
240
  two power coordinates (which may coincide) and a per-period peak label.
241
241
  Temperature is supplied deterministically (``temperatures``) -- it drives
@@ -245,13 +245,13 @@ class DispatchFactorModel:
245
245
  Attributes:
246
246
  log_forward0: Initial log-forward vector ``z0 = log F(0, ·)`` over the
247
247
  flattened factor state, dimension ``D`` (``>= 2``).
248
- drift: Per-step drift ``mu`` (length ``D``). Must be the **risk-adjusted**
248
+ drift: Per-step drift ``mu`` (length ``D``). Must be the risk-adjusted
249
249
  (pricing-measure) drift and is tagged by ``drift_kind``.
250
250
  covariance: Per-step covariance ``C`` (``D x D``), assembled by
251
- :func:`~quantvolt.numerics.monte_carlo.build_covariance`.
251
+ ``quantvolt.numerics.monte_carlo.build_covariance``.
252
252
  drift_kind: Measure tag on ``drift``; ``dispatch_value`` requires
253
- :attr:`DriftKind.RISK_NEUTRAL` (Req 21.5).
254
- peak_kinds: Per-period :class:`PeakKind` selecting the active power
253
+ ``DriftKind.RISK_NEUTRAL``.
254
+ peak_kinds: Per-period ``PeakKind`` selecting the active power
255
255
  coordinate; its length is the dispatch horizon ``H`` (``>= 1``).
256
256
  temperatures: Per-period ambient temperature ``S_t`` (length ``H``).
257
257
  power_on_index: Coordinate of ``z0`` used as the on-peak power spot.
@@ -335,11 +335,11 @@ class DispatchFactorModel:
335
335
  return self.power_off_index
336
336
 
337
337
  def simulate(self, seed: int, path_count: int, *, antithetic: bool = True) -> Vector:
338
- """Simulate ``(n_paths, H + 1, D)`` log-forward paths (Task-62 engine).
338
+ """Simulate ``(n_paths, H + 1, D)`` log-forward paths.
339
339
 
340
340
  Record 0 is ``log_forward0``; record ``t + 1`` holds the prices realised in
341
- dispatch period ``t``. Deterministic under ``seed`` (Req 11.2), so a caller
342
- (or a Property-62 test) can reproduce the exact scenario set.
341
+ dispatch period ``t``. Deterministic under ``seed``, so a caller can
342
+ reproduce the exact scenario set.
343
343
  """
344
344
  if self.drift.ndim == 1 and self.covariance.ndim == 2:
345
345
  return simulate_correlated_forwards(
@@ -431,8 +431,8 @@ def _moves(
431
431
  for eq. B.1 constraint 5): a running unit whose min-run timer has not yet reached
432
432
  ``d_min`` cannot use ``_transitions``'s own shutdown transition, so if the derate
433
433
  also drops every ramp target below ``eff_capacity`` the state machine would offer
434
- *no* feasible action at all -- a dead end the reused deterministic state space
435
- never has to consider (it assumes full availability, Req 21.4). A physical
434
+ no feasible action at all, a dead end the reused deterministic state space
435
+ never has to consider (it assumes full availability). A physical
436
436
  derating below ``c_min`` overrides the min-run rule (the unit cannot keep
437
437
  producing regardless of the timer), so when no ordinary move survives the filter
438
438
  a forced shutdown at zero cash is appended, mirroring the already-documented
@@ -1107,7 +1107,7 @@ _SOLVERS: dict[str, _Solver] = {"lsm": _dispatch_lsm, "tree": _dispatch_tree}
1107
1107
 
1108
1108
 
1109
1109
  def _require_risk_adjusted_drift(drift_kind: DriftKind) -> None:
1110
- """Reject a physical drift for valuation (Req 21.5; mirror of the VaR guard).
1110
+ """Reject a physical drift for valuation (mirror of the VaR guard).
1111
1111
 
1112
1112
  Valuation optimises under the risk-adjusted expectation ``E*`` (eq. B.2), whose
1113
1113
  drift is ``RISK_NEUTRAL`` in this library's ``DriftKind`` vocabulary
@@ -1141,12 +1141,12 @@ def dispatch_value(
1141
1141
  antithetic: bool = True,
1142
1142
  regression_basis_degree: int = 2,
1143
1143
  ) -> DispatchResult:
1144
- """Value ``plant`` under uncertainty by stochastic DP (eqs. B.2-B.3; Req 21.1-21.2).
1144
+ """Value ``plant`` under uncertainty by stochastic DP (eqs. B.2-B.3).
1145
1145
 
1146
1146
  Solves the Bellman recursion over the deterministic module's commitment state
1147
- machine, under the risk-adjusted expectation carried by ``factor_model``
1148
- (Req 21.5). See the module docstring for the state machine, the on-peak /
1149
- off-peak Markov split (Req 21.6), the risk-adjusted-drift requirement, the
1147
+ machine, under the risk-adjusted expectation carried by ``factor_model``.
1148
+ See the module docstring for the state machine, the on-peak /
1149
+ off-peak Markov split, the risk-adjusted-drift requirement, the
1150
1150
  forced-outage seam, and the critical-surface representation. All inputs are
1151
1151
  validated before any computation and never mutated.
1152
1152
 
@@ -1157,7 +1157,7 @@ def dispatch_value(
1157
1157
  temperature context (also fixes the horizon ``H``).
1158
1158
  method: ``"lsm"`` (least-squares Monte Carlo) or ``"tree"`` (recombining
1159
1159
  lattice); selected via a dispatch table.
1160
- seed: Monte Carlo / lattice seed (Req 11.2 determinism); ``>= 0``.
1160
+ seed: Monte Carlo / lattice seed (determinism); ``>= 0``.
1161
1161
  path_count: Simulated paths for ``"lsm"`` (ignored by ``"tree"``); ``>= 1``.
1162
1162
  output_step: Output-grid spacing (MW); defaults to the ramp rate.
1163
1163
  availability: Optional per-period forced-outage multiplier ``M`` in
@@ -1170,7 +1170,7 @@ def dispatch_value(
1170
1170
  initial_uptime: Producing periods already accrued (min-run).
1171
1171
  initial_downtime: Periods already offline (start bucket / min-down); must be
1172
1172
  ``>= 1`` when not ``initial_online`` (delegated to the reused
1173
- :func:`~quantvolt.assets.dispatch_deterministic._initial_state`).
1173
+ ``quantvolt.assets.dispatch_deterministic._initial_state``).
1174
1174
  evaluation: Independent LSM policy-evaluation sample. By default, the fitted
1175
1175
  policy is evaluated on ``path_count`` fresh paths using ``seed + 1``.
1176
1176
  antithetic: Whether ``"lsm"``'s simulated paths use antithetic variates
@@ -1184,11 +1184,11 @@ def dispatch_value(
1184
1184
  ``"tree"``. Must be ``>= 1``.
1185
1185
 
1186
1186
  Returns:
1187
- The :class:`DispatchResult` (value + eq. B.5 critical surfaces).
1187
+ The ``DispatchResult`` (value + eq. B.5 critical surfaces).
1188
1188
 
1189
1189
  Raises:
1190
1190
  ValidationError: If ``method`` is unknown; if ``factor_model.drift`` is not
1191
- risk-adjusted (Req 21.5); if ``path_count`` / ``seed`` /
1191
+ risk-adjusted; if ``path_count`` / ``seed`` /
1192
1192
  ``discount_factors`` / ``availability`` / ``regression_basis_degree`` are
1193
1193
  out of range or mis-sized; if a plant curve is infeasible at a supplied
1194
1194
  temperature; if ``initial_downtime < 1`` while offline; or if the initial