quantstack-lab 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yousuf Hossain Shafeen
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,321 @@
1
+ Metadata-Version: 2.4
2
+ Name: quantstack-lab
3
+ Version: 0.1.0
4
+ Summary: A connected quantitative finance research stack: structured product pricing, volatility surfaces, honest backtesting, market microstructure and risk, linked into end-to-end studies.
5
+ License-Expression: MIT
6
+ Project-URL: Repository, https://github.com/hossainyousuf751/quant-stack
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: sppricer<0.3,>=0.2.1
11
+ Requires-Dist: volsurface-lab<0.3,>=0.2.1
12
+ Requires-Dist: btlab<0.5,>=0.4.0
13
+ Requires-Dist: lobkit<0.4,>=0.3.0
14
+ Requires-Dist: marketrisk-lab<0.5,>=0.4.1
15
+ Requires-Dist: numpy>=1.24
16
+ Requires-Dist: scipy>=1.10
17
+ Requires-Dist: pandas>=2.0
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=7; extra == "dev"
20
+ Requires-Dist: matplotlib>=3.7; extra == "dev"
21
+ Dynamic: license-file
22
+
23
+ # quant-stack
24
+
25
+ > A connected quantitative finance research stack: structured product pricing, volatility
26
+ > surfaces, honest backtesting, market microstructure and risk, linked into end-to-end studies.
27
+
28
+ quant-stack holds five independent libraries as git submodules, pinned to tagged releases. It
29
+ connects them through small adapters and runs cross-project studies on top. No library imports
30
+ another, and none imports quant-stack. Each still installs, tests and releases on its own.
31
+
32
+ | Project | `pip install` (import) | What it does |
33
+ |---|---|---|
34
+ | [sp-pricer](https://github.com/hossainyousuf751/sp-pricer) | `sppricer` | Monte Carlo pricing of Phoenix autocallables and reverse convertibles, Greeks, barrier monitoring; Dupire local vol and term structure from v0.2 |
35
+ | [volsurface](https://github.com/hossainyousuf751/volsurface) | `volsurface-lab` (`volsurface`) | Arbitrage-free implied vol surfaces (SVI, SSVI, eSSVI, arbitrage-free SVI) from raw quotes, Dupire local vol, scenario shifts |
36
+ | [backtest-lab](https://github.com/hossainyousuf751/backtest-lab) | `btlab` | Backtesting with look-ahead tests, Deflated Sharpe, PBO, walk-forward, pluggable cost models, covariance strategies |
37
+ | [microstructure-lab](https://github.com/hossainyousuf751/microstructure-lab) | `lobkit` | Order-book reconstruction, order-flow imbalance, micro-price, Avellaneda–Stoikov; cost calibration and bars |
38
+ | [risk-lab](https://github.com/hossainyousuf751/risk-lab) | `marketrisk-lab` (`risklab`) | VaR/ES models and backtests, DCC-GARCH, shrinkage, HRP; scenario full-revaluation VaR and stress tests |
39
+
40
+ ## How the pieces connect
41
+
42
+ ```mermaid
43
+ flowchart LR
44
+ VS[volsurface] -->|"local_vol(K, T), curve<br/>adapters/surface.py"| SP[sp-pricer]
45
+ SP -->|"value(scenario)<br/>adapters/notes.py"| RL[risk-lab]
46
+ VS -->|"shifted surfaces, vol_fn"| RL
47
+ RL -->|"estimators and allocators<br/>(plain callables)"| BT[backtest-lab]
48
+ MS[microstructure-lab] -->|"calibrate_costs to SpreadImpactCost<br/>adapters/costs.py"| BT
49
+ subgraph quant-stack
50
+ A[adapters, studies S1-S7, integration tests]
51
+ end
52
+ ```
53
+
54
+ Every arrow is a **protocol, not an import**. The library on the receiving end of an arrow defines a
55
+ small interface:
56
+
57
+ - sp-pricer's `LocalVolSource` is anything with `local_vol(K, T)` and a `curve`.
58
+ - risk-lab's `Position` is anything with `value(scenario)`.
59
+ - btlab's `CostModel` is anything with `cost(trade, date, context)`.
60
+
61
+ quant-stack wires real objects into those interfaces in [`src/quantstack/adapters/`](https://github.com/hossainyousuf751/quant-stack/tree/main/src/quantstack/adapters), and
62
+ nowhere else.
63
+
64
+ The libraries also duplicate a few small pieces on purpose instead of sharing a core package: three
65
+ Black-Scholes implementations and two yfinance loaders. [`tests/test_consistency.py`](https://github.com/hossainyousuf751/quant-stack/blob/main/tests/test_consistency.py)
66
+ asserts that the duplicates agree to 1e-12 on a grid of inputs and return the same data format.
67
+
68
+ ## Studies
69
+
70
+ | Study | Question | Headline result |
71
+ |---|---|---|
72
+ | [S1](https://github.com/hossainyousuf751/quant-stack/blob/main/studies/s1_skew_autocallable.py) skew and the autocallable | What does pricing off the smile do to a Phoenix? | On the 2026-10-30 SPX/NDX close, local vol lowers the worst-of note from 100.44 to 96.44 per 100 (−4.00 ± 0.04 SE). The synthetic notes fall by 3.4 and 5.8 |
73
+ | [S2](https://github.com/hossainyousuf751/quant-stack/blob/main/studies/s2_note_book_risk.py) risk of a structured-note book | Where do linear VaR approximations break? | Delta-normal VaR of the delta-hedged book is 0, against 90 under full revaluation. Delta-gamma-vega overstates it at 152 |
74
+ | [S3](https://github.com/hossainyousuf751/quant-stack/blob/main/studies/s3_risk_portfolios_backtest.py) risk-based portfolios, honestly backtested | Do risk-lab's rankings survive costs, lag and selection? | Long-only minimum variance keeps the lowest true risk (9.5% vs 15.1% for 1/N) after lag and costs. Best of 24 configurations: Deflated Sharpe 0.94, walk-forward out-of-sample Sharpe 0.59. HRP's turnover barely falls with the window on synthetic data but falls 4× on real ETFs |
75
+ | [S4](https://github.com/hossainyousuf751/quant-stack/blob/main/studies/s4_costs_from_order_book.py) costs from the order book | Does a strong same-second order-flow relationship survive as a trade? | OFI (R² 0.59) loses after the spread alone. Queue imbalance survives spread, 1–5 s latency and the recorded book at small size, but covers at most 0.24 bp per unit traded, so any exchange taker fee kills it |
76
+ | [S5](https://github.com/hossainyousuf751/quant-stack/blob/main/studies/s5_skew_option_var.py) skew in option VaR | How much does the smile's behaviour under spot moves change option VaR? | Sticky moneyness more than doubles a short straddle's 1-day VaR against sticky strike (0.82 vs 0.38 synthetic, 0.84 vs 0.32 SPX) |
77
+ | [S6](https://github.com/hossainyousuf751/quant-stack/blob/main/studies/s6_autocallable_investors.py) how did autocallable investors do? | A SPY Phoenix issued monthly since 2005 at a model fair value of 98: what did investors actually receive? | Realised cash flows were worth 101.20 at the issue rate, +3.20 ± 0.72 (Newey-West) above the model. 94% autocalled, none lost capital, and the excess return over the 3-year rate was +1.1% ± 0.7% a year |
78
+ | [S7](https://github.com/hossainyousuf751/quant-stack/blob/main/studies/s7_market_maker_risk.py) market-maker inventory risk | How bad is a bad session for an Avellaneda–Stoikov quoter in the order book, and do standard VaR models know? | Symmetric quotes earn 46–65% more but carry 5–13× the 97.5% ES. Normal VaR fails 4 of 6 books (32–47 exceptions against 15), and RiskMetrics EWMA finds none on the inventory books because it assumes zero-mean P&L. Historical and FHS pass |
79
+
80
+ Each study prints its tables to [`docs/`](https://github.com/hossainyousuf751/quant-stack/tree/main/docs) (`s*_output.txt`, plus plots). All seeds are fixed.
81
+
82
+ ### S1: skew and the autocallable
83
+
84
+ ![Price vs barrier](https://raw.githubusercontent.com/hossainyousuf751/quant-stack/main/docs/s1_price_vs_barrier_spx.png)
85
+
86
+ The note is sp-pricer's README note: 3 years, quarterly, a 2% memory coupon, autocall at 100% from
87
+ year 1, and 60% European protection. Every product here is short a down-and-in put, so the put skew
88
+ drives its value. Three models price the same note on the same forward curves and random numbers:
89
+
90
+ | SPX/NDX worst-of, ρ = 0.9 | Fair value | vs flat ATM | P(autocall at year 1) | P(knocked in) |
91
+ |---|---|---|---|---|
92
+ | flat ATM vol (18.2% / 23.8%) | 100.44 | | 47.0% | 7.7% |
93
+ | flat vol at the 60% strike | 94.15 | −6.29 ± 0.04 | 42.4% | 17.7% |
94
+ | Dupire local vol (arbitrage-free SVI) | **96.44** | **−4.00 ± 0.04** | 57.4% | 11.2% |
95
+
96
+ The local-vol pipeline is validated first, in [`tests/test_link_a.py`](https://github.com/hossainyousuf751/quant-stack/blob/main/tests/test_link_a.py). Monte
97
+ Carlo prices of 20 vanillas under sp-pricer's `LocalVolModel` imply back the volsurface surface's
98
+ own vols within 2.5 standard errors. Flat ATM vol misses the same targets by up to 940.
99
+
100
+ The S1 numbers also reproduce volsurface's earlier, independent simulator: 100.41, 96.14 and 96.42.
101
+ Halving or doubling the time step moves the worst-of note by no more than its standard error (96.23,
102
+ 96.30 and 96.15, each ± 0.07).
103
+
104
+ ### S2: risk of a structured-note book
105
+
106
+ ![P&L profile](https://raw.githubusercontent.com/hossainyousuf751/quant-stack/main/docs/s2_pnl_profile.png)
107
+
108
+ A dealer is short four notes on two underlyings and may delta-hedge with shares. Each method gives a
109
+ 10-day 99% VaR (lognormal scenarios, with implied vol falling 0.8 points per 1% rise in A):
110
+
111
+ | Book | Delta-normal | Delta-gamma-vega | Full revaluation (price grid) |
112
+ |---|---|---|---|
113
+ | unhedged | 89.9 | 222.4 | 154.4 |
114
+ | delta-hedged | **0.0** | 152.2 | **90.0** |
115
+
116
+ Full revaluation reprices every note in every scenario. To make 20,000 scenarios affordable, it
117
+ interpolates on a price grid validated against direct repricing at random points: the mean error is
118
+ 0.02 per 100 and the largest is 2.2 Monte Carlo standard errors.
119
+ The same pattern holds under fat-tailed DCC-t scenarios.
120
+
121
+ ### S5: skew in option VaR
122
+
123
+ risk-lab Study 3's books, priced off a fitted smile instead of one flat vol, with full revaluation only:
124
+
125
+ | 1-day 99% VaR | flat ATM | sticky strike | sticky moneyness |
126
+ |---|---|---|---|
127
+ | covered call | 1.97 | 2.05 | 1.92 |
128
+ | protective put | 1.86 | 1.83 | 2.04 |
129
+ | short ATM straddle | 0.38 | 0.38 | **0.82** |
130
+
131
+ (Synthetic SSVI surface; the SPX snapshot gives the same ordering.)
132
+
133
+ ### S6: how did autocallable investors actually do?
134
+
135
+ ![Realised vs fair](https://raw.githubusercontent.com/hossainyousuf751/quant-stack/main/docs/s6_fair_vs_realised.png)
136
+
137
+ A 3-year SPY Phoenix is issued on the first trading day of every month from 2005 to 2025: quarterly
138
+ observations, a memory coupon above 70%, autocall at 100% from year 1, and 60% European protection.
139
+ The coupon is set at issue, from that day's VIX and Treasury yields only, so that sp-pricer's fair value
140
+ is 98 (a 2-point margin). The realised SPY path, from btlab's loader, then gives each note's actual cash
141
+ flows, read off by the product's own payoff code.
142
+
143
+ | 250 notes, 2005-01 to 2025-10 | Value |
144
+ |---|---|
145
+ | coupon set at issue, per year | mean 3.23%, from −0.32% (2013) to 28.4% (Nov 2008) |
146
+ | model fair value at issue | 98.00 |
147
+ | realised cash flows, PV at the issue rate | **101.20** (+3.20 ± 0.72) |
148
+ | realised IRR minus the 3-year rate | +1.13% ± 0.69% a year |
149
+ | outcomes | 94% autocalled (83% at year 1), 6% repaid at par, 0 losses |
150
+ | VIX at issue vs realised vol over the note's life | 19.3% vs 17.0% |
151
+
152
+ VIX stands in for a 3-year ATM vol, with no skew or term structure. The note is on SPY total return with
153
+ zero dividend yield, and the rate carries no issuer funding spread. The 21 years of overlapping notes hold
154
+ only about seven independent 3-year windows.
155
+
156
+ ### S7: market-maker inventory risk
157
+
158
+ ![Return vs ES](https://raw.githubusercontent.com/hossainyousuf751/quant-stack/main/docs/s7_mm_return_vs_es.png)
159
+
160
+ microstructure-lab's Avellaneda–Stoikov quoter runs inside its order-book simulator for 2,500 independent
161
+ 5-minute sessions per setting. It uses the paper's inputs calibrated on the book, as in that repo's Study 4.
162
+ risk-lab measures each P&L series and backtests its four VaR models over the last 1,500 sessions.
163
+
164
+ | Session P&L (ticks) | Mean | Skew | Excess kurtosis | P(loss) | 99% VaR | 97.5% ES | Mean / ES |
165
+ |---|---|---|---|---|---|---|---|
166
+ | γ = 0.001, inventory | 31.6 | −0.57 | 0.8 | 9.9% | 37.0 | 37.7 | 0.84 |
167
+ | γ = 0.001, symmetric | 46.2 | −1.29 | 6.3 | 16.7% | 169.0 | 182.9 | 0.25 |
168
+ | **γ = 0.005, inventory** | **22.6** | −0.25 | 0.6 | **4.3%** | **10.0** | **11.9** | **1.90** |
169
+ | γ = 0.005, symmetric | 36.8 | −1.03 | 4.9 | 17.8% | 143.0 | 149.3 | 0.25 |
170
+ | γ = 0.02, inventory | 8.5 | −0.27 | 0.5 | 10.1% | 10.5 | 10.0 | 0.85 |
171
+ | γ = 0.02, symmetric | 14.0 | −1.00 | 5.9 | 21.6% | 70.0 | 72.8 | 0.19 |
172
+
173
+ | 99% VaR backtest, 6 books | Kupiec passes (p > 0.05) | Exceptions (15 expected) | 97.5% ES (Z2) |
174
+ |---|---|---|---|
175
+ | historical, 500 sessions | 6 of 6 | 13–21 | passes everywhere |
176
+ | filtered historical (FHS), 1,000 | 5 of 6 | 11–24 | passes everywhere |
177
+ | normal, 500 | 2 of 6, both at p = 0.054 | 23–47 | rejected on 4 books (−0.96 to −1.35) |
178
+ | EWMA (RiskMetrics) | 3 of 6: symmetric books only | 0 on every inventory book | far too conservative on inventory books |
179
+
180
+ In the paper's own fill model, not one of 2,500 sessions loses money (the worst is +43), so a loss VaR
181
+ there is meaningless. Losses come from the order book's queue position and adverse selection.
182
+
183
+ ## What was surprising, what went wrong, and how it was diagnosed
184
+
185
+ **S1: a "skew-aware" flat vol overshoots.** Pricing the note with flat vol taken at the barrier
186
+ strike sounds like a cheap way to respect the skew, so I expected it to land near local vol. It lands
187
+ well below it instead: −6.3 against −4.0 on SPX/NDX, and −8.1 against −5.8 on the synthetic worst-of.
188
+ The autocall probabilities show why. A barrier-strike vol applies put-wing volatility everywhere,
189
+ including near and above 100%, where local vol is low. So it understates the chance of an early
190
+ call at year 1 (42% vs 57%) and overstates the chance of ending knocked in (18% vs 11%). Local vol
191
+ is not "flat vol at the right strike"; it is a different vol in each region the path visits. The
192
+ local-vol pipeline was validated before any of this: Monte Carlo vanillas reprice the surface within
193
+ 2.5 SE, and two independent simulators agree within error.
194
+
195
+ **S2: delta-gamma-vega was worse than delta-normal, in the other direction.** I expected the
196
+ second-order expansion to land between delta-normal and full revaluation. Instead it overstates the
197
+ unhedged book's VaR by 44% (222 vs 154). To find out why, I repriced each note under parallel vol
198
+ shifts. The notes' value is concave in vol: an 8-point fall in vol raises the single-asset Phoenix by
199
+ 3.8 per 100, while vega × shift says 4.9. The scenarios move vol against spot (−0.8 points per 1%),
200
+ so the 10-day tail combines a rally with a large vol fall, exactly where linear vega overstates the
201
+ dealer's loss. Delta-normal misses the other way: it reports zero risk for the delta-hedged book,
202
+ which full revaluation puts at 90. Getting the full-revaluation number cheaply also took a second
203
+ attempt. The first price grid missed direct repricing by up to 5.5 SE. That pointed at the vol axis
204
+ rather than the spot axis, because tightening the spot spacing made no difference. Halving the vol
205
+ step brought the worst point to 2.2 SE, and the VaR itself moved by only 2%.
206
+
207
+ **S3: one finding confirmed, one refuted, one bug of my own.** On risk-lab's synthetic data, HRP's
208
+ turnover barely falls as the estimation window grows (4.4 to 3.3 times a year, while minimum variance
209
+ falls from 5.6 to 1.0), confirming risk-lab's result under a real backtest. On the 10 ETFs it falls
210
+ 4×, from 3.6 to 0.9. The block covariance's equal within-block correlations make HRP's ordering flip
211
+ between near-ties, while stocks, bonds and commodities form stable clusters. FHS VaR, which passes
212
+ every backtest on synthetic data, takes 44–53 exceptions against 37 expected on the ETF portfolios.
213
+ A first run also reported "true" volatility well below realised volatility for every method. The
214
+ cause was in my study code: it averaged in the warm-up rebalances that hold nothing. 24 empty
215
+ rebalances out of 144 explained the gap exactly (12.6% vs 15.1% for 1/N).
216
+
217
+ **S4: the edge that should not have survived, and what finally killed it.** The expected lesson was
218
+ that a strong same-second relationship (OFI explains 59% of BTC's one-second mid changes) does not
219
+ survive latency and spread. For OFI itself that holds: it trades about 2,000 times an hour, and the
220
+ spread alone costs more than its gross edge, so its net Sharpe is negative at every lag. Queue
221
+ imbalance did survive: positive net of spread and calibrated impact at lags of 1, 2 and 5 seconds,
222
+ right on 90% of the mid's moves. I treated that as a bug until each candidate was ruled out:
223
+
224
+ - The look-ahead check rebuilds features from quotes truncated at each cutoff, and it passes.
225
+ - The spread at the trades' execution bars equals the median spread, so the edge is not a wide
226
+ spread being ignored.
227
+ - Walking each trade through the recorded 10-level book at $100,000 cuts BTC's edge by a third but
228
+ leaves it positive. On the thinner ETH book the same size turns every variant negative; at $1,000
229
+ both survive.
230
+
231
+ What kills it is the cost an order book does not show: exchange fees. The signal's gross edge
232
+ covers at most about 0.24 bp per unit traded on BTC and on ETH, against Binance's
233
+ published base taker fee of 10 bp. At any taker fee, every variant loses. A queue-imbalance edge
234
+ belongs to whoever is paid to provide liquidity, not to whoever takes it. Two hours of data is one
235
+ sample, so this is an illustration of the method rather than a result to trade on.
236
+
237
+ **S6: investors beat the model, and the sample is why to be careful.** Priced at 98, the notes
238
+ delivered 101.20 on average. Only 2% of issues came in below 98, and the gap survives dropping the 2008,
239
+ 2009 and 2020 vintages (+2.28 ± 0.38). The reason is in the outcomes: the fair value charges for a
240
+ 3-year fall of more than 40%, and since 2005 the lowest SPY level at any 3-year maturity was 72%. VIX
241
+ also exceeded the volatility that followed for 65% of issues. Two results surprised me. First, for 13
242
+ issues between 2013 and 2016 the coupon that prices the note at 98 is negative: with VIX near 12 and
243
+ 3-year rates under 1%, a note with no coupon is already worth more than 98. Real notes paid positive
244
+ coupons then because issuers fund above Treasuries and the note references the price index, which has
245
+ a lower forward; both are left out here and stated. Second, the first version used a fixed 8% coupon,
246
+ which put fair values as high as 112, terms no issuer would sell; setting the coupon at issue fixed that.
247
+ The study begins in 2005, as specified, so it leaves out 2000–2002, when SPY fell 48% peak to trough.
248
+ I first wrote here that a note issued in 2000 would very likely have lost capital, then checked the same
249
+ total-return series. It would not: the 2000 issues end at 60–85% of their initial level. The April 2000
250
+ note matures at 60.4%, just 0.4 points above the barrier. None of them autocalls, so investors get par
251
+ back after three years with most coupons missed. Seven independent windows cannot show that the model
252
+ overprices the crash; they show that a crash deep enough to breach 60% at a maturity date did not occur.
253
+
254
+ **S7: the model that failed for the opposite reason, and a library number that moved.** I expected
255
+ normal VaR to fail on the symmetric quoter, whose P&L has excess kurtosis near 6 and a fat left tail, and it
256
+ did, with 32–47 exceptions against 15. The surprise was RiskMetrics EWMA, which recorded zero exceptions on
257
+ every inventory book. It assumes zero-mean returns, but these books earn 1.2–1.7 standard deviations per
258
+ session on average. EWMA therefore reads the mean as volatility and sets VaR 2.6–5.7× too high (61 against 11
259
+ at γ = 0.005). Fed into a limit framework, that would cap the safest quoter hardest. Sessions are
260
+ independent by construction, so FHS has no clustering to exploit; it passes because its residuals are
261
+ empirical. Its one Kupiec rejection (p = 0.032) is in line with 24 tests at a 5% level.
262
+
263
+ Running 2,500 sessions instead of 200 also moved a number in microstructure-lab's README. Its γ = 0.005
264
+ inventory row shows a mean P&L of 19.4. The first 200 sessions here reproduce that exactly, but the full
265
+ 2,500 average 22.6 ± 0.26, and five fresh blocks on unused seeds give 21.3–24.1. Seeds 0–199 were a
266
+ low draw, about 3.5 standard errors under the mean. Inventory control costs 32–40% of mean P&L rather than
267
+ the published 33–45%; the conclusion is unchanged. The library README is left as published until the
268
+ owner approves a change.
269
+
270
+ **Testing across versions.** The libraries promise numpy ≥ 1.24, pandas ≥ 2.0 and scipy ≥ 1.10. I
271
+ ran every suite in an environment with exactly those versions. Every test passed except my new
272
+ regression pins: optimiser-fitted values move by up to 1e-5 relative between scipy 1.10 and 1.17,
273
+ and one penalised SVI fit settles on a different optimum (0.1815 vs 0.1790, both printed as 0.18).
274
+ The pins now hold each figure to the precision the README actually prints.
275
+
276
+ ## Install
277
+
278
+ ```bash
279
+ pip install quantstack-lab # the adapters (import quantstack) and all five libraries from PyPI
280
+ ```
281
+
282
+ `volsurface` and `risk-lab` are published as `volsurface-lab` and `marketrisk-lab`, because the names
283
+ `volsurface` and `risklab` belong to unrelated projects on PyPI; their import names are unchanged. The
284
+ studies, tests and plots are not part of the package. To run them, clone the repository as below.
285
+
286
+ ## Reproduce
287
+
288
+ ```bash
289
+ git clone --recurse-submodules https://github.com/hossainyousuf751/quant-stack.git
290
+ cd quant-stack
291
+ for p in sp-pricer volsurface backtest-lab microstructure-lab risk-lab; do pip install -e "projects/$p"; done
292
+ pip install -e ".[dev]"
293
+ pytest # consistency and integration tests, offline, about 1 minute
294
+ python studies/s1_skew_autocallable.py # each study writes docs/s*_output.txt-style tables and plots
295
+ ```
296
+
297
+ What the studies need:
298
+
299
+ - **S1 and S5** use volsurface's saved CBOE snapshot (`projects/volsurface/data/`, git-ignored; create it with
300
+ `projects/volsurface/examples/snapshot.py`) for their real-data parts, and skip those parts without it.
301
+ - **S4** uses microstructure-lab's Binance recordings, likewise git-ignored and created with its recorder.
302
+ - **S7** is fully offline (simulated order book), about 7 minutes.
303
+ - **S3, S4 and S6** download Yahoo Finance prices on first run and cache them in `data/`. They skip those
304
+ parts when offline.
305
+
306
+ Each library's own tests still run without the others installed: `cd projects/<repo> && pytest`.
307
+
308
+ ## Layout
309
+
310
+ ```
311
+ quant-stack/
312
+ ├── projects/ the five libraries (git submodules, tagged releases)
313
+ ├── src/quantstack/
314
+ │ └── adapters/ surface.py (Link A), notes.py (Link B), costs.py (Link D)
315
+ ├── studies/ S1-S7
316
+ ├── tests/ consistency and integration tests
317
+ └── docs/ study outputs and plots; profile_README.md
318
+ ```
319
+
320
+ Data is public or synthetic only: Yahoo Finance, CBOE delayed quotes, Binance public market data,
321
+ and simulators.
@@ -0,0 +1,299 @@
1
+ # quant-stack
2
+
3
+ > A connected quantitative finance research stack: structured product pricing, volatility
4
+ > surfaces, honest backtesting, market microstructure and risk, linked into end-to-end studies.
5
+
6
+ quant-stack holds five independent libraries as git submodules, pinned to tagged releases. It
7
+ connects them through small adapters and runs cross-project studies on top. No library imports
8
+ another, and none imports quant-stack. Each still installs, tests and releases on its own.
9
+
10
+ | Project | `pip install` (import) | What it does |
11
+ |---|---|---|
12
+ | [sp-pricer](https://github.com/hossainyousuf751/sp-pricer) | `sppricer` | Monte Carlo pricing of Phoenix autocallables and reverse convertibles, Greeks, barrier monitoring; Dupire local vol and term structure from v0.2 |
13
+ | [volsurface](https://github.com/hossainyousuf751/volsurface) | `volsurface-lab` (`volsurface`) | Arbitrage-free implied vol surfaces (SVI, SSVI, eSSVI, arbitrage-free SVI) from raw quotes, Dupire local vol, scenario shifts |
14
+ | [backtest-lab](https://github.com/hossainyousuf751/backtest-lab) | `btlab` | Backtesting with look-ahead tests, Deflated Sharpe, PBO, walk-forward, pluggable cost models, covariance strategies |
15
+ | [microstructure-lab](https://github.com/hossainyousuf751/microstructure-lab) | `lobkit` | Order-book reconstruction, order-flow imbalance, micro-price, Avellaneda–Stoikov; cost calibration and bars |
16
+ | [risk-lab](https://github.com/hossainyousuf751/risk-lab) | `marketrisk-lab` (`risklab`) | VaR/ES models and backtests, DCC-GARCH, shrinkage, HRP; scenario full-revaluation VaR and stress tests |
17
+
18
+ ## How the pieces connect
19
+
20
+ ```mermaid
21
+ flowchart LR
22
+ VS[volsurface] -->|"local_vol(K, T), curve<br/>adapters/surface.py"| SP[sp-pricer]
23
+ SP -->|"value(scenario)<br/>adapters/notes.py"| RL[risk-lab]
24
+ VS -->|"shifted surfaces, vol_fn"| RL
25
+ RL -->|"estimators and allocators<br/>(plain callables)"| BT[backtest-lab]
26
+ MS[microstructure-lab] -->|"calibrate_costs to SpreadImpactCost<br/>adapters/costs.py"| BT
27
+ subgraph quant-stack
28
+ A[adapters, studies S1-S7, integration tests]
29
+ end
30
+ ```
31
+
32
+ Every arrow is a **protocol, not an import**. The library on the receiving end of an arrow defines a
33
+ small interface:
34
+
35
+ - sp-pricer's `LocalVolSource` is anything with `local_vol(K, T)` and a `curve`.
36
+ - risk-lab's `Position` is anything with `value(scenario)`.
37
+ - btlab's `CostModel` is anything with `cost(trade, date, context)`.
38
+
39
+ quant-stack wires real objects into those interfaces in [`src/quantstack/adapters/`](https://github.com/hossainyousuf751/quant-stack/tree/main/src/quantstack/adapters), and
40
+ nowhere else.
41
+
42
+ The libraries also duplicate a few small pieces on purpose instead of sharing a core package: three
43
+ Black-Scholes implementations and two yfinance loaders. [`tests/test_consistency.py`](https://github.com/hossainyousuf751/quant-stack/blob/main/tests/test_consistency.py)
44
+ asserts that the duplicates agree to 1e-12 on a grid of inputs and return the same data format.
45
+
46
+ ## Studies
47
+
48
+ | Study | Question | Headline result |
49
+ |---|---|---|
50
+ | [S1](https://github.com/hossainyousuf751/quant-stack/blob/main/studies/s1_skew_autocallable.py) skew and the autocallable | What does pricing off the smile do to a Phoenix? | On the 2026-10-30 SPX/NDX close, local vol lowers the worst-of note from 100.44 to 96.44 per 100 (−4.00 ± 0.04 SE). The synthetic notes fall by 3.4 and 5.8 |
51
+ | [S2](https://github.com/hossainyousuf751/quant-stack/blob/main/studies/s2_note_book_risk.py) risk of a structured-note book | Where do linear VaR approximations break? | Delta-normal VaR of the delta-hedged book is 0, against 90 under full revaluation. Delta-gamma-vega overstates it at 152 |
52
+ | [S3](https://github.com/hossainyousuf751/quant-stack/blob/main/studies/s3_risk_portfolios_backtest.py) risk-based portfolios, honestly backtested | Do risk-lab's rankings survive costs, lag and selection? | Long-only minimum variance keeps the lowest true risk (9.5% vs 15.1% for 1/N) after lag and costs. Best of 24 configurations: Deflated Sharpe 0.94, walk-forward out-of-sample Sharpe 0.59. HRP's turnover barely falls with the window on synthetic data but falls 4× on real ETFs |
53
+ | [S4](https://github.com/hossainyousuf751/quant-stack/blob/main/studies/s4_costs_from_order_book.py) costs from the order book | Does a strong same-second order-flow relationship survive as a trade? | OFI (R² 0.59) loses after the spread alone. Queue imbalance survives spread, 1–5 s latency and the recorded book at small size, but covers at most 0.24 bp per unit traded, so any exchange taker fee kills it |
54
+ | [S5](https://github.com/hossainyousuf751/quant-stack/blob/main/studies/s5_skew_option_var.py) skew in option VaR | How much does the smile's behaviour under spot moves change option VaR? | Sticky moneyness more than doubles a short straddle's 1-day VaR against sticky strike (0.82 vs 0.38 synthetic, 0.84 vs 0.32 SPX) |
55
+ | [S6](https://github.com/hossainyousuf751/quant-stack/blob/main/studies/s6_autocallable_investors.py) how did autocallable investors do? | A SPY Phoenix issued monthly since 2005 at a model fair value of 98: what did investors actually receive? | Realised cash flows were worth 101.20 at the issue rate, +3.20 ± 0.72 (Newey-West) above the model. 94% autocalled, none lost capital, and the excess return over the 3-year rate was +1.1% ± 0.7% a year |
56
+ | [S7](https://github.com/hossainyousuf751/quant-stack/blob/main/studies/s7_market_maker_risk.py) market-maker inventory risk | How bad is a bad session for an Avellaneda–Stoikov quoter in the order book, and do standard VaR models know? | Symmetric quotes earn 46–65% more but carry 5–13× the 97.5% ES. Normal VaR fails 4 of 6 books (32–47 exceptions against 15), and RiskMetrics EWMA finds none on the inventory books because it assumes zero-mean P&L. Historical and FHS pass |
57
+
58
+ Each study prints its tables to [`docs/`](https://github.com/hossainyousuf751/quant-stack/tree/main/docs) (`s*_output.txt`, plus plots). All seeds are fixed.
59
+
60
+ ### S1: skew and the autocallable
61
+
62
+ ![Price vs barrier](https://raw.githubusercontent.com/hossainyousuf751/quant-stack/main/docs/s1_price_vs_barrier_spx.png)
63
+
64
+ The note is sp-pricer's README note: 3 years, quarterly, a 2% memory coupon, autocall at 100% from
65
+ year 1, and 60% European protection. Every product here is short a down-and-in put, so the put skew
66
+ drives its value. Three models price the same note on the same forward curves and random numbers:
67
+
68
+ | SPX/NDX worst-of, ρ = 0.9 | Fair value | vs flat ATM | P(autocall at year 1) | P(knocked in) |
69
+ |---|---|---|---|---|
70
+ | flat ATM vol (18.2% / 23.8%) | 100.44 | | 47.0% | 7.7% |
71
+ | flat vol at the 60% strike | 94.15 | −6.29 ± 0.04 | 42.4% | 17.7% |
72
+ | Dupire local vol (arbitrage-free SVI) | **96.44** | **−4.00 ± 0.04** | 57.4% | 11.2% |
73
+
74
+ The local-vol pipeline is validated first, in [`tests/test_link_a.py`](https://github.com/hossainyousuf751/quant-stack/blob/main/tests/test_link_a.py). Monte
75
+ Carlo prices of 20 vanillas under sp-pricer's `LocalVolModel` imply back the volsurface surface's
76
+ own vols within 2.5 standard errors. Flat ATM vol misses the same targets by up to 940.
77
+
78
+ The S1 numbers also reproduce volsurface's earlier, independent simulator: 100.41, 96.14 and 96.42.
79
+ Halving or doubling the time step moves the worst-of note by no more than its standard error (96.23,
80
+ 96.30 and 96.15, each ± 0.07).
81
+
82
+ ### S2: risk of a structured-note book
83
+
84
+ ![P&L profile](https://raw.githubusercontent.com/hossainyousuf751/quant-stack/main/docs/s2_pnl_profile.png)
85
+
86
+ A dealer is short four notes on two underlyings and may delta-hedge with shares. Each method gives a
87
+ 10-day 99% VaR (lognormal scenarios, with implied vol falling 0.8 points per 1% rise in A):
88
+
89
+ | Book | Delta-normal | Delta-gamma-vega | Full revaluation (price grid) |
90
+ |---|---|---|---|
91
+ | unhedged | 89.9 | 222.4 | 154.4 |
92
+ | delta-hedged | **0.0** | 152.2 | **90.0** |
93
+
94
+ Full revaluation reprices every note in every scenario. To make 20,000 scenarios affordable, it
95
+ interpolates on a price grid validated against direct repricing at random points: the mean error is
96
+ 0.02 per 100 and the largest is 2.2 Monte Carlo standard errors.
97
+ The same pattern holds under fat-tailed DCC-t scenarios.
98
+
99
+ ### S5: skew in option VaR
100
+
101
+ risk-lab Study 3's books, priced off a fitted smile instead of one flat vol, with full revaluation only:
102
+
103
+ | 1-day 99% VaR | flat ATM | sticky strike | sticky moneyness |
104
+ |---|---|---|---|
105
+ | covered call | 1.97 | 2.05 | 1.92 |
106
+ | protective put | 1.86 | 1.83 | 2.04 |
107
+ | short ATM straddle | 0.38 | 0.38 | **0.82** |
108
+
109
+ (Synthetic SSVI surface; the SPX snapshot gives the same ordering.)
110
+
111
+ ### S6: how did autocallable investors actually do?
112
+
113
+ ![Realised vs fair](https://raw.githubusercontent.com/hossainyousuf751/quant-stack/main/docs/s6_fair_vs_realised.png)
114
+
115
+ A 3-year SPY Phoenix is issued on the first trading day of every month from 2005 to 2025: quarterly
116
+ observations, a memory coupon above 70%, autocall at 100% from year 1, and 60% European protection.
117
+ The coupon is set at issue, from that day's VIX and Treasury yields only, so that sp-pricer's fair value
118
+ is 98 (a 2-point margin). The realised SPY path, from btlab's loader, then gives each note's actual cash
119
+ flows, read off by the product's own payoff code.
120
+
121
+ | 250 notes, 2005-01 to 2025-10 | Value |
122
+ |---|---|
123
+ | coupon set at issue, per year | mean 3.23%, from −0.32% (2013) to 28.4% (Nov 2008) |
124
+ | model fair value at issue | 98.00 |
125
+ | realised cash flows, PV at the issue rate | **101.20** (+3.20 ± 0.72) |
126
+ | realised IRR minus the 3-year rate | +1.13% ± 0.69% a year |
127
+ | outcomes | 94% autocalled (83% at year 1), 6% repaid at par, 0 losses |
128
+ | VIX at issue vs realised vol over the note's life | 19.3% vs 17.0% |
129
+
130
+ VIX stands in for a 3-year ATM vol, with no skew or term structure. The note is on SPY total return with
131
+ zero dividend yield, and the rate carries no issuer funding spread. The 21 years of overlapping notes hold
132
+ only about seven independent 3-year windows.
133
+
134
+ ### S7: market-maker inventory risk
135
+
136
+ ![Return vs ES](https://raw.githubusercontent.com/hossainyousuf751/quant-stack/main/docs/s7_mm_return_vs_es.png)
137
+
138
+ microstructure-lab's Avellaneda–Stoikov quoter runs inside its order-book simulator for 2,500 independent
139
+ 5-minute sessions per setting. It uses the paper's inputs calibrated on the book, as in that repo's Study 4.
140
+ risk-lab measures each P&L series and backtests its four VaR models over the last 1,500 sessions.
141
+
142
+ | Session P&L (ticks) | Mean | Skew | Excess kurtosis | P(loss) | 99% VaR | 97.5% ES | Mean / ES |
143
+ |---|---|---|---|---|---|---|---|
144
+ | γ = 0.001, inventory | 31.6 | −0.57 | 0.8 | 9.9% | 37.0 | 37.7 | 0.84 |
145
+ | γ = 0.001, symmetric | 46.2 | −1.29 | 6.3 | 16.7% | 169.0 | 182.9 | 0.25 |
146
+ | **γ = 0.005, inventory** | **22.6** | −0.25 | 0.6 | **4.3%** | **10.0** | **11.9** | **1.90** |
147
+ | γ = 0.005, symmetric | 36.8 | −1.03 | 4.9 | 17.8% | 143.0 | 149.3 | 0.25 |
148
+ | γ = 0.02, inventory | 8.5 | −0.27 | 0.5 | 10.1% | 10.5 | 10.0 | 0.85 |
149
+ | γ = 0.02, symmetric | 14.0 | −1.00 | 5.9 | 21.6% | 70.0 | 72.8 | 0.19 |
150
+
151
+ | 99% VaR backtest, 6 books | Kupiec passes (p > 0.05) | Exceptions (15 expected) | 97.5% ES (Z2) |
152
+ |---|---|---|---|
153
+ | historical, 500 sessions | 6 of 6 | 13–21 | passes everywhere |
154
+ | filtered historical (FHS), 1,000 | 5 of 6 | 11–24 | passes everywhere |
155
+ | normal, 500 | 2 of 6, both at p = 0.054 | 23–47 | rejected on 4 books (−0.96 to −1.35) |
156
+ | EWMA (RiskMetrics) | 3 of 6: symmetric books only | 0 on every inventory book | far too conservative on inventory books |
157
+
158
+ In the paper's own fill model, not one of 2,500 sessions loses money (the worst is +43), so a loss VaR
159
+ there is meaningless. Losses come from the order book's queue position and adverse selection.
160
+
161
+ ## What was surprising, what went wrong, and how it was diagnosed
162
+
163
+ **S1: a "skew-aware" flat vol overshoots.** Pricing the note with flat vol taken at the barrier
164
+ strike sounds like a cheap way to respect the skew, so I expected it to land near local vol. It lands
165
+ well below it instead: −6.3 against −4.0 on SPX/NDX, and −8.1 against −5.8 on the synthetic worst-of.
166
+ The autocall probabilities show why. A barrier-strike vol applies put-wing volatility everywhere,
167
+ including near and above 100%, where local vol is low. So it understates the chance of an early
168
+ call at year 1 (42% vs 57%) and overstates the chance of ending knocked in (18% vs 11%). Local vol
169
+ is not "flat vol at the right strike"; it is a different vol in each region the path visits. The
170
+ local-vol pipeline was validated before any of this: Monte Carlo vanillas reprice the surface within
171
+ 2.5 SE, and two independent simulators agree within error.
172
+
173
+ **S2: delta-gamma-vega was worse than delta-normal, in the other direction.** I expected the
174
+ second-order expansion to land between delta-normal and full revaluation. Instead it overstates the
175
+ unhedged book's VaR by 44% (222 vs 154). To find out why, I repriced each note under parallel vol
176
+ shifts. The notes' value is concave in vol: an 8-point fall in vol raises the single-asset Phoenix by
177
+ 3.8 per 100, while vega × shift says 4.9. The scenarios move vol against spot (−0.8 points per 1%),
178
+ so the 10-day tail combines a rally with a large vol fall, exactly where linear vega overstates the
179
+ dealer's loss. Delta-normal misses the other way: it reports zero risk for the delta-hedged book,
180
+ which full revaluation puts at 90. Getting the full-revaluation number cheaply also took a second
181
+ attempt. The first price grid missed direct repricing by up to 5.5 SE. That pointed at the vol axis
182
+ rather than the spot axis, because tightening the spot spacing made no difference. Halving the vol
183
+ step brought the worst point to 2.2 SE, and the VaR itself moved by only 2%.
184
+
185
+ **S3: one finding confirmed, one refuted, one bug of my own.** On risk-lab's synthetic data, HRP's
186
+ turnover barely falls as the estimation window grows (4.4 to 3.3 times a year, while minimum variance
187
+ falls from 5.6 to 1.0), confirming risk-lab's result under a real backtest. On the 10 ETFs it falls
188
+ 4×, from 3.6 to 0.9. The block covariance's equal within-block correlations make HRP's ordering flip
189
+ between near-ties, while stocks, bonds and commodities form stable clusters. FHS VaR, which passes
190
+ every backtest on synthetic data, takes 44–53 exceptions against 37 expected on the ETF portfolios.
191
+ A first run also reported "true" volatility well below realised volatility for every method. The
192
+ cause was in my study code: it averaged in the warm-up rebalances that hold nothing. 24 empty
193
+ rebalances out of 144 explained the gap exactly (12.6% vs 15.1% for 1/N).
194
+
195
+ **S4: the edge that should not have survived, and what finally killed it.** The expected lesson was
196
+ that a strong same-second relationship (OFI explains 59% of BTC's one-second mid changes) does not
197
+ survive latency and spread. For OFI itself that holds: it trades about 2,000 times an hour, and the
198
+ spread alone costs more than its gross edge, so its net Sharpe is negative at every lag. Queue
199
+ imbalance did survive: positive net of spread and calibrated impact at lags of 1, 2 and 5 seconds,
200
+ right on 90% of the mid's moves. I treated that as a bug until each candidate was ruled out:
201
+
202
+ - The look-ahead check rebuilds features from quotes truncated at each cutoff, and it passes.
203
+ - The spread at the trades' execution bars equals the median spread, so the edge is not a wide
204
+ spread being ignored.
205
+ - Walking each trade through the recorded 10-level book at $100,000 cuts BTC's edge by a third but
206
+ leaves it positive. On the thinner ETH book the same size turns every variant negative; at $1,000
207
+ both survive.
208
+
209
+ What kills it is the cost an order book does not show: exchange fees. The signal's gross edge
210
+ covers at most about 0.24 bp per unit traded on BTC and on ETH, against Binance's
211
+ published base taker fee of 10 bp. At any taker fee, every variant loses. A queue-imbalance edge
212
+ belongs to whoever is paid to provide liquidity, not to whoever takes it. Two hours of data is one
213
+ sample, so this is an illustration of the method rather than a result to trade on.
214
+
215
+ **S6: investors beat the model, and the sample is why to be careful.** Priced at 98, the notes
216
+ delivered 101.20 on average. Only 2% of issues came in below 98, and the gap survives dropping the 2008,
217
+ 2009 and 2020 vintages (+2.28 ± 0.38). The reason is in the outcomes: the fair value charges for a
218
+ 3-year fall of more than 40%, and since 2005 the lowest SPY level at any 3-year maturity was 72%. VIX
219
+ also exceeded the volatility that followed for 65% of issues. Two results surprised me. First, for 13
220
+ issues between 2013 and 2016 the coupon that prices the note at 98 is negative: with VIX near 12 and
221
+ 3-year rates under 1%, a note with no coupon is already worth more than 98. Real notes paid positive
222
+ coupons then because issuers fund above Treasuries and the note references the price index, which has
223
+ a lower forward; both are left out here and stated. Second, the first version used a fixed 8% coupon,
224
+ which put fair values as high as 112, terms no issuer would sell; setting the coupon at issue fixed that.
225
+ The study begins in 2005, as specified, so it leaves out 2000–2002, when SPY fell 48% peak to trough.
226
+ I first wrote here that a note issued in 2000 would very likely have lost capital, then checked the same
227
+ total-return series. It would not: the 2000 issues end at 60–85% of their initial level. The April 2000
228
+ note matures at 60.4%, just 0.4 points above the barrier. None of them autocalls, so investors get par
229
+ back after three years with most coupons missed. Seven independent windows cannot show that the model
230
+ overprices the crash; they show that a crash deep enough to breach 60% at a maturity date did not occur.
231
+
232
+ **S7: the model that failed for the opposite reason, and a library number that moved.** I expected
233
+ normal VaR to fail on the symmetric quoter, whose P&L has excess kurtosis near 6 and a fat left tail, and it
234
+ did, with 32–47 exceptions against 15. The surprise was RiskMetrics EWMA, which recorded zero exceptions on
235
+ every inventory book. It assumes zero-mean returns, but these books earn 1.2–1.7 standard deviations per
236
+ session on average. EWMA therefore reads the mean as volatility and sets VaR 2.6–5.7× too high (61 against 11
237
+ at γ = 0.005). Fed into a limit framework, that would cap the safest quoter hardest. Sessions are
238
+ independent by construction, so FHS has no clustering to exploit; it passes because its residuals are
239
+ empirical. Its one Kupiec rejection (p = 0.032) is in line with 24 tests at a 5% level.
240
+
241
+ Running 2,500 sessions instead of 200 also moved a number in microstructure-lab's README. Its γ = 0.005
242
+ inventory row shows a mean P&L of 19.4. The first 200 sessions here reproduce that exactly, but the full
243
+ 2,500 average 22.6 ± 0.26, and five fresh blocks on unused seeds give 21.3–24.1. Seeds 0–199 were a
244
+ low draw, about 3.5 standard errors under the mean. Inventory control costs 32–40% of mean P&L rather than
245
+ the published 33–45%; the conclusion is unchanged. The library README is left as published until the
246
+ owner approves a change.
247
+
248
+ **Testing across versions.** The libraries promise numpy ≥ 1.24, pandas ≥ 2.0 and scipy ≥ 1.10. I
249
+ ran every suite in an environment with exactly those versions. Every test passed except my new
250
+ regression pins: optimiser-fitted values move by up to 1e-5 relative between scipy 1.10 and 1.17,
251
+ and one penalised SVI fit settles on a different optimum (0.1815 vs 0.1790, both printed as 0.18).
252
+ The pins now hold each figure to the precision the README actually prints.
253
+
254
+ ## Install
255
+
256
+ ```bash
257
+ pip install quantstack-lab # the adapters (import quantstack) and all five libraries from PyPI
258
+ ```
259
+
260
+ `volsurface` and `risk-lab` are published as `volsurface-lab` and `marketrisk-lab`, because the names
261
+ `volsurface` and `risklab` belong to unrelated projects on PyPI; their import names are unchanged. The
262
+ studies, tests and plots are not part of the package. To run them, clone the repository as below.
263
+
264
+ ## Reproduce
265
+
266
+ ```bash
267
+ git clone --recurse-submodules https://github.com/hossainyousuf751/quant-stack.git
268
+ cd quant-stack
269
+ for p in sp-pricer volsurface backtest-lab microstructure-lab risk-lab; do pip install -e "projects/$p"; done
270
+ pip install -e ".[dev]"
271
+ pytest # consistency and integration tests, offline, about 1 minute
272
+ python studies/s1_skew_autocallable.py # each study writes docs/s*_output.txt-style tables and plots
273
+ ```
274
+
275
+ What the studies need:
276
+
277
+ - **S1 and S5** use volsurface's saved CBOE snapshot (`projects/volsurface/data/`, git-ignored; create it with
278
+ `projects/volsurface/examples/snapshot.py`) for their real-data parts, and skip those parts without it.
279
+ - **S4** uses microstructure-lab's Binance recordings, likewise git-ignored and created with its recorder.
280
+ - **S7** is fully offline (simulated order book), about 7 minutes.
281
+ - **S3, S4 and S6** download Yahoo Finance prices on first run and cache them in `data/`. They skip those
282
+ parts when offline.
283
+
284
+ Each library's own tests still run without the others installed: `cd projects/<repo> && pytest`.
285
+
286
+ ## Layout
287
+
288
+ ```
289
+ quant-stack/
290
+ ├── projects/ the five libraries (git submodules, tagged releases)
291
+ ├── src/quantstack/
292
+ │ └── adapters/ surface.py (Link A), notes.py (Link B), costs.py (Link D)
293
+ ├── studies/ S1-S7
294
+ ├── tests/ consistency and integration tests
295
+ └── docs/ study outputs and plots; profile_README.md
296
+ ```
297
+
298
+ Data is public or synthetic only: Yahoo Finance, CBOE delayed quotes, Binance public market data,
299
+ and simulators.