-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhaptic-simulation.Rmd
More file actions
468 lines (418 loc) · 26.2 KB
/
Copy pathhaptic-simulation.Rmd
File metadata and controls
468 lines (418 loc) · 26.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
---
title: "Haptic Protocol"
subtitle: "Agent-Based Monte-Carlo Simulation Study under a GBM Regime"
author: "0xAlbert ([0xalbertdefi@gmail.com](mailto:0xalbertdefi@gmail.com))"
date: "v0.1 — June 2026"
fontsize: 11pt
linkcolor: Indigo
citecolor: Teal
urlcolor: Teal
toccolor: Indigo
colorlinks: true
output:
pdf_document:
keep_tex: true
number_sections: true
toc: true
toc_depth: 2
fig_caption: true
latex_engine: pdflatex
includes:
in_header: preamble.tex
abstract: |
We stress-test the Haptic Protocol with a vectorized agent-based Monte-Carlo
simulation. Geometric-Brownian-Motion price paths drive four coupled agent
cohorts --- arbitrageurs, noise traders, option buyers and a compounding LP
vault --- through repeated weekly option cycles in which the risk engine opens
concentrated Uniswap-v4 bands, prices them with the realized-volatility-floor
times utilization-markup pipeline, fills open interest to a demand
equilibrium, and settles digitals from band composition. Across thousands of
paths we find: (i) under full native collateralization the protocol is
\textbf{provably solvent} (0 percent insolvency in every regime), confirming the
collateralization argument; (ii) LP returns are \textbf{EV-positive but
entirely premium-subsidized} --- with no volatility-risk-premium the vault
bleeds about 15 percent per year to loss-versus-rebalancing; (iii) returns are
\textbf{asymmetric in drift}, with Sharpe near 1.5 in up-trends falling to
about 0.6 in down-trends; (iv) band width and over-subscription expose clean
risk--return and capital-efficiency--solvency frontiers; (v) the volatility
floor roughly halves tail loss (CVaR) in a jump regime; (vi) the
depth-derived open-interest cap makes settlement manipulation break-even
exactly at the cap and strictly unprofitable below it, while spot settlement is
always exploitable; and (vii) under Student-t, jump-diffusion,
stochastic-volatility and black-swan stresses, full-collateralization solvency
is \textbf{path-independent} (0 percent insolvency in all of them) while
leverage converts fat tails into frequent shortfalls. This is a research
simulation, not a guarantee of production behavior.
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, message = FALSE, warning = FALSE,
fig.align = "center", fig.pos = "H", out.width = "82%",
dev = "pdf", fig.width = 6.4, fig.height = 3.4)
options(kableExtra.latex.load_packages = FALSE, knitr.kable.NA = "")
library(ggplot2); library(kableExtra); library(scales)
pal <- list(primary = "#4C2A85", accent = "#0E9594", amber = "#B57614",
risk = "#C2410C", stable = "#1D4ED8", gray = "#6B7280",
light = "#F4F2FA", ink = "#1A1A2E")
theme_haptic <- function(base = 10) {
theme_minimal(base_size = base) +
theme(panel.grid.minor = element_blank(),
panel.grid.major = element_line(color = "grey90", linewidth = 0.3),
plot.title = element_text(face = "bold", color = pal$primary, size = base + 1),
axis.title = element_text(color = pal$gray),
legend.position = "top", legend.title = element_text(color = pal$gray, size = base - 1),
legend.key.height = unit(3, "mm"), strip.text = element_text(color = pal$ink, face = "bold"))
}
R <- function(f) read.csv(file.path("results", f))
pc <- function(x, d = 1) sprintf(paste0("%.", d, "f\\%%"), 100 * x) # LaTeX-safe percent
ktab <- function(df, cap, fs = 9, ...) {
kbl(df, format = "latex", booktabs = TRUE, escape = FALSE, caption = cap,
linesep = "", ...) |>
kable_styling(latex_options = c("hold_position", "striped"),
stripe_color = "hapticLight", font_size = fs, full_width = FALSE)
}
```
# Methodology
## Design of the simulation
The simulation is a **vectorized agent-based Monte-Carlo**: arrays of $N$
parallel GBM price paths are advanced jointly, while the protocol logic and four
agent cohorts act on per-path state each weekly cycle. The price follows
$\mathrm{d}S = \mu S\,\mathrm{d}t + \sigma S\,\mathrm{d}W$ (optionally with a
Poisson jump term for stress), discretized hourly. The numeraire is the stable
asset; price $P$ is stable per risky.
```{r agents}
ag <- data.frame(
Agent = c("Arbitrageurs", "Noise traders", "Option buyers", "LP vault"),
Role = c("drag pool price onto the GBM reference each step",
"uninformed two-sided flow",
"value digitals under the \\emph{real} measure $(\\mu,\\sigma)$",
"compounding capital deployed across bands"),
Feedback = c("realize LP inventory P\\&L (LVR) + pay swap fees",
"pure fee income for LPs",
"engine prices at $\\mu=0$ $\\Rightarrow$ trend $\\to$ one-sided flow",
"bear payouts + inventory; earn fees + premia"))
ktab(ag, "Agent cohorts and their feedback couplings.", fs = 8.6) |>
column_spec(1, bold = TRUE, width = "2.4cm") |>
column_spec(2, width = "6.0cm") |> column_spec(3, width = "5.4cm")
```
## Mechanism captured each cycle
Each cycle the engine opens call bands above spot (cash-or-nothing) and put bands
below (asset-or-nothing). For each band it (1) estimates trailing realized
volatility and applies the floor $\sigma=\max(\sigma_{\text{impl}},\sigma_{\min})$;
(2) prices the digital $\mathcal{N}(d_2)$ under $\mu=0$; (3) lets buyers --- whose
reservation is the real-measure fair value times $(1+\text{vrp})$ --- fill open
interest to the utilization equilibrium $\nu(u^\star)\,\Pi_{\text{fair}}(1+m)=R$;
(4) deploys collateral sized to the depth-limited capacity; (5) accrues swap fees
from arbitrage ($\propto\!\sum|\Delta\sqrt P|$ in range) and noise flow; and
(6) settles each digital from the band's realized composition. The key engine
parameters are in Table~\ref{tab:config}.
```{r config}
cfg <- R("config.csv")
keep <- c("sigma","n_paths","n_cycles","cycle_days","delta","sigma_floor",
"markup_m","vol_risk_premium","fee_rate","depth_per_pct","twap_bleed",
"nu_ustar","nu_umax","oversub")
lab <- c(sigma="volatility $\\sigma$ (annual)", n_paths="Monte-Carlo paths",
n_cycles="weekly cycles", cycle_days="cycle tenor (days)",
delta="band width $\\delta$", sigma_floor="vol floor $\\sigma_{\\min}$",
markup_m="EV margin $m$", vol_risk_premium="vol risk premium (vrp)",
fee_rate="swap fee", depth_per_pct="pool depth /1\\%",
twap_bleed="TWAP manip.\\ bleed", nu_ustar="target util.\\ $u^{*}$",
nu_umax="hard util.\\ cap $u_{\\max}$", oversub="over-subscription")
cf <- cfg[match(keep, cfg$param), ]
cf$param <- lab[cf$param]
names(cf) <- c("Parameter", "Baseline value")
ktab(cf, "Baseline configuration of the risk engine.", fs = 8.6) |>
column_spec(1, width = "6.2cm")
```
# Baseline LP performance
Under the baseline (flat $\mu=0$, $\sigma=0.6$, full collateralization) the vault
compounds over 26 weekly cycles. Figure~\ref{fig:fan} shows the vault-value fan
and Figure~\ref{fig:dist} the terminal-return distribution across drift regimes;
headline statistics are in Table~\ref{tab:headline}.
```{r headline}
h <- R("headline.csv"); hv <- setNames(h$value, h$metric)
tab <- data.frame(
Metric = c("Mean annualized return", "Median 6-mo return", "Return volatility (6-mo)",
"Median Sharpe", "5\\% VaR (6-mo)", "5\\% CVaR (6-mo)",
"Prob(loss)", "Insolvency rate", "Mean utilization"),
Value = c(pc(hv["mean_ann_ret"]), pc(hv["median_total_ret"]),
pc(hv["std_total_ret"]), sprintf("%.2f", hv["sharpe"]),
pc(hv["var5"]), pc(hv["cvar5"]),
pc(hv["prob_loss"], 0), pc(hv["insolvency_rate"], 0),
sprintf("%.2f", hv["mean_util"])))
ktab(tab, "Baseline LP statistics (flat market, full collateralization, 6000 paths).", fs = 9) |>
column_spec(1, width = "6.0cm")
```
```{r fig-fan, fig.cap="\\label{fig:fan}Vault-value fan over 26 weekly cycles (flat market). Bands are the 5--95\\% and 25--75\\% path percentiles; line is the median. The vault compounds steadily with a contained left tail.", fig.height=3.0}
fan <- subset(R("vault_fan.csv"), regime == "flat")
ggplot(fan, aes(cycle)) +
geom_ribbon(aes(ymin = p5, ymax = p95), fill = pal$primary, alpha = 0.12) +
geom_ribbon(aes(ymin = p25, ymax = p75), fill = pal$primary, alpha = 0.22) +
geom_line(aes(y = p50), color = pal$primary, linewidth = 1.1) +
geom_hline(yintercept = 1, linetype = "dashed", color = pal$gray, linewidth = 0.3) +
labs(x = "cycle (week)", y = "vault value / initial") + theme_haptic()
```
```{r fig-dist, fig.cap="\\label{fig:dist}Distribution of 6-month LP total return by drift regime. All three are right-of-zero on average; the bear regime carries the fattest left tail.", fig.height=3.0}
d <- R("pnl_dist.csv"); d$regime <- factor(d$regime, levels = c("bear","flat","bull"))
ggplot(d, aes(total_ret, fill = regime, color = regime)) +
geom_density(alpha = 0.22, linewidth = 0.7) +
geom_vline(xintercept = 0, linetype = "dashed", color = pal$gray, linewidth = 0.3) +
scale_x_continuous(labels = percent) +
scale_fill_manual(values = c(bear = pal$risk, flat = pal$primary, bull = pal$accent)) +
scale_color_manual(values = c(bear = pal$risk, flat = pal$primary, bull = pal$accent)) +
labs(x = "6-month LP total return", y = "density", fill = NULL, color = NULL) +
theme_haptic()
```
## Where the P&L comes from
Figure~\ref{fig:decomp} decomposes LP P\&L. Premia minus payouts is the option
edge; it is positive in every regime and is the dominant driver. Fees are minor
(concentrated bands see little volume relative to their notional), and inventory
(loss-versus-rebalancing plus directional exposure) is a persistent drag that the
premium must --- and does --- overcome.
```{r fig-decomp, fig.cap="\\label{fig:decomp}P\\&L decomposition as a fraction of initial vault. Payouts shown negative. The net (lp\\_pnl) is positive in all regimes; the option edge (premia $-$ payouts) carries it against the inventory drag.", fig.height=3.2}
co <- R("components.csv")
co$regime <- factor(co$regime, levels = c("bear (mu=-0.6)","flat (mu=0)","bull (mu=+0.6)"))
co$signed <- ifelse(co$component == "payouts", -co$value, co$value)
co <- subset(co, component %in% c("premia","fees","inventory","payouts","lp_pnl"))
co$component <- factor(co$component, levels = c("premia","fees","inventory","payouts","lp_pnl"),
labels = c("premia","fees","inventory","payouts (−)","net P&L"))
cols <- c("premia" = pal$accent, "fees" = pal$stable, "inventory" = pal$amber,
"payouts (−)" = pal$risk, "net P&L" = pal$primary)
ggplot(co, aes(component, signed, fill = component)) +
geom_col(width = 0.7) +
geom_hline(yintercept = 0, color = pal$gray, linewidth = 0.3) +
facet_wrap(~regime) + scale_fill_manual(values = cols, guide = "none") +
scale_y_continuous(labels = percent) +
labs(x = NULL, y = "share of initial vault") +
theme_haptic() + theme(axis.text.x = element_text(angle = 35, hjust = 1, size = 7))
```
# Drift regime sweep
Figure~\ref{fig:drift} sweeps the annual drift. The protocol is EV-positive
across the whole range, but the profile is **asymmetric**: in down-trends the
risky-funded call bands lose inventory value and put payouts rise, widening the
left tail (VaR), whereas strong up-trends are the most profitable. This is the
short-volatility / inventory signature flagged in the design.
```{r fig-drift, fig.cap="\\label{fig:drift}Annualized return and 5\\% VaR vs.\\ annual drift $\\mu$. Returns stay positive throughout, but downside risk (VaR) deteriorates sharply in bear regimes.", fig.height=3.0}
ds <- R("drift_sweep.csv")
dl <- rbind(data.frame(mu = ds$mu, val = ds$mean_ann_ret, series = "annualized return"),
data.frame(mu = ds$mu, val = ds$var5, series = "5% VaR (6-mo)"))
ggplot(dl, aes(mu, val, color = series)) +
geom_hline(yintercept = 0, linetype = "dashed", color = pal$gray, linewidth = 0.3) +
geom_line(linewidth = 1.1) + geom_point(size = 1.5) +
scale_color_manual(values = c("annualized return" = pal$accent, "5% VaR (6-mo)" = pal$risk)) +
scale_y_continuous(labels = percent) +
labs(x = expression(paste("annual drift ", mu)), y = NULL, color = NULL) +
theme_haptic()
```
# Demand drivers: the premium subsidy is everything
Figure~\ref{fig:demand} isolates the two demand drivers. As the vol-risk-premium
rises, utilization and the LP edge climb together --- and **at vrp $=0$ the vault
is loss-making** ($\approx-15\%$/yr), earning nothing but eating LVR. This is the
quantitative statement of the subsidy thesis: Haptic is only EV-positive because
option buyers persistently overpay relative to realized volatility. Against raw
volatility the relationship is hump-shaped: moderate vol is ideal, while very
high vol overwhelms the fixed premium markup with payouts and inventory loss.
```{r fig-demand, fig.cap="\\label{fig:demand}Left: utilization, LP edge and return vs.\\ the vol-risk-premium --- profitability vanishes without it. Right: return is hump-shaped in volatility (selling vol is unprofitable when realized vol is very high).", fig.width=7.0, fig.height=3.0}
ut <- R("utilization.csv")
uv <- subset(ut, axis == "vrp")
us <- subset(ut, axis == "sigma")
ul <- rbind(
data.frame(x = uv$x, val = uv$util, series = "utilization", panel = "vs vol-risk-premium"),
data.frame(x = uv$x, val = uv$ann_ret, series = "ann. return", panel = "vs vol-risk-premium"),
data.frame(x = uv$x, val = uv$lp_edge, series = "LP edge", panel = "vs vol-risk-premium"),
data.frame(x = us$x, val = us$util, series = "utilization", panel = "vs volatility"),
data.frame(x = us$x, val = us$ann_ret, series = "ann. return", panel = "vs volatility"),
data.frame(x = us$x, val = us$lp_edge, series = "LP edge", panel = "vs volatility"))
ggplot(ul, aes(x, val, color = series)) +
geom_hline(yintercept = 0, linetype = "dashed", color = pal$gray, linewidth = 0.3) +
geom_line(linewidth = 1) + geom_point(size = 1.2) +
facet_wrap(~panel, scales = "free_x") +
scale_color_manual(values = c("utilization" = pal$primary, "ann. return" = pal$accent, "LP edge" = pal$amber)) +
scale_y_continuous(labels = percent) +
labs(x = NULL, y = NULL, color = NULL) + theme_haptic()
```
# Band width: a risk--return frontier
Tightening the band lowers risk and return together; widening it raises both ---
but only up to a point. Figure~\ref{fig:bw} shows the frontier bending back: at
very wide bands ($\delta\!=\!8\%$) inventory risk grows faster than premium income
and the product is no longer meaningfully "binary". The capacity (manipulation
cap) grows linearly in $\delta$, so width also trades against per-contract
sharpness. A width of 1--3\% sits in the efficient region.
```{r fig-bw, fig.cap="\\label{fig:bw}Risk--return frontier over band width $\\delta$ (labelled). Return rises with width then bends back as inventory risk dominates; the dashed marker is the 5\\% VaR scale.", fig.height=3.1}
bw <- R("bandwidth_sweep.csv")
bw$lab <- paste0(bw$delta * 100, "%")
ggplot(bw, aes(std_total_ret, mean_ann_ret)) +
geom_path(color = pal$gray, linewidth = 0.5, linetype = "dotted") +
geom_point(aes(color = delta), size = 3) +
geom_text(aes(label = lab), vjust = -1, size = 2.8, color = pal$ink) +
scale_color_gradient(low = pal$accent, high = pal$risk, guide = "none") +
scale_x_continuous(labels = percent) + scale_y_continuous(labels = percent) +
labs(x = "return volatility (6-mo)", y = "annualized return") +
theme_haptic()
```
# Capital efficiency vs. solvency
Over-subscription (selling more OI than the deployed collateral fully backs)
raises returns but breaks the native-collateralization guarantee.
Figure~\ref{fig:oversub} shows the trade-off cleanly: at full collateralization
(over-subscription $=1.0$) insolvency is **exactly zero** and stays zero, exactly
as the composition argument predicts; every step of leverage buys yield with a
steep rise in the probability of an unpayable shortfall.
```{r fig-oversub, fig.cap="\\label{fig:oversub}Annualized return and insolvency rate vs.\\ over-subscription. Full collateralization (1.0) is provably solvent; leverage trades solvency for yield.", fig.height=3.0}
os <- R("oversub_sweep.csv")
ol <- rbind(data.frame(x = os$oversub, val = os$mean_ann_ret, series = "annualized return"),
data.frame(x = os$oversub, val = os$insolvency_rate, series = "insolvency rate"))
ggplot(ol, aes(x, val, color = series)) +
geom_vline(xintercept = 1, linetype = "dotted", color = pal$accent, linewidth = 0.5) +
geom_line(linewidth = 1.1) + geom_point(size = 1.5) +
scale_color_manual(values = c("annualized return" = pal$accent, "insolvency rate" = pal$risk)) +
scale_y_continuous(labels = percent) +
labs(x = "over-subscription (collateral leverage)", y = NULL, color = NULL) +
theme_haptic()
```
# The volatility floor
Figure~\ref{fig:floor} tests the floor across regimes. Its value concentrates
exactly where intended: in a **calm market punctuated by jumps**, where trailing
realized volatility under-prices tail risk, the floor roughly halves the 5\% CVaR.
In a normal-volatility regime where realized vol already exceeds the floor it is
inert (and in a purely calm market it slightly suppresses demand --- a real cost).
```{r fig-floor, fig.cap="\\label{fig:floor}5\\% CVaR (6-mo) with the volatility floor off vs.\\ on, by regime. The floor materially cuts the tail in the calm-plus-jumps regime; it is inert when realized vol already exceeds it.", fig.height=3.0}
vf <- R("volfloor.csv")
vf$floor <- factor(vf$floor, levels = c("off", "on"))
ggplot(vf, aes(regime, cvar5, fill = floor)) +
geom_col(position = position_dodge(width = 0.7), width = 0.62) +
scale_fill_manual(values = c(off = pal$risk, on = pal$accent)) +
scale_y_continuous(labels = percent) +
labs(x = NULL, y = "5% CVaR (6-mo)", fill = "vol floor") +
theme_haptic() + theme(axis.text.x = element_text(size = 8))
```
# Settlement manipulation and the OI cap
Finally we validate the security invariant directly. For a band of width $\delta$
on a pool of depth $D$, the protocol sets the open-interest ceiling equal to the
cost of moving \emph{and holding} the price across the band for the settlement
window, $\text{OI}_{\max}=C_{\text{manip}}$. Figure~\ref{fig:manip} plots the
attacker's profit (flipped payoff minus cost) against open interest relative to
that cap: under TWAP settlement profit is **negative below the cap and exactly
zero at it**, for every band width --- the cap is tight. Under atomic/spot
settlement the same attack is profitable everywhere (costs are fee-only), which
is why Haptic never settles on spot.
```{r fig-manip, fig.cap="\\label{fig:manip}Attacker profit vs.\\ open interest relative to the manipulation cap, by band width, under TWAP settlement. Profit crosses zero precisely at OI/cap${}=1$ (dotted). Under spot settlement (not shown) profit is positive for all OI --- spot is unsafe.", fig.height=3.1}
mp <- R("manipulation.csv"); mp$lab <- paste0(mp$delta * 100, "%")
ggplot(mp, aes(oi_over_cap, profit_twap_m, color = factor(lab, levels = unique(lab)))) +
annotate("rect", xmin = 0, xmax = 1, ymin = -Inf, ymax = Inf, fill = pal$accent, alpha = 0.06) +
geom_hline(yintercept = 0, color = pal$gray, linewidth = 0.3) +
geom_vline(xintercept = 1, linetype = "dotted", color = pal$risk, linewidth = 0.5) +
geom_line(linewidth = 0.9) + geom_point(size = 1.3) +
annotate("text", x = 0.5, y = max(mp$profit_twap_m) * 0.8, label = "safe", color = pal$accent, size = 3) +
scale_color_manual(values = c(pal$accent, pal$stable, pal$primary, pal$amber, pal$risk)) +
labs(x = "open interest / manipulation cap", y = "attacker profit ($M)", color = expression(delta)) +
theme_haptic()
```
# Heavy-tail stress
GBM understates exactly the risk a short-volatility book is most exposed to: the
sudden left tail. We re-run the protocol under four fat-tailed price processes ---
**Student-t** innovations, **Merton jump-diffusion**, **Heston stochastic
volatility** with leverage ($\rho=-0.7$), and a deterministic **black-swan**
($-40\%$ at mid-horizon) --- at full collateralization and under $1.5\times$
leverage.
The headline result is structural (Figure~\ref{fig:htsolv}, Table~\ref{tab:ht-table}):
\begin{keybox}[Native collateralization is path-independent]
Across \emph{every} heavy-tail scenario, insolvency at full collateralization is
\textbf{exactly zero}. Because each band can only ever owe what its own
composition holds, no price path --- however fat-tailed or discontinuous --- can
make the protocol unable to pay. Heavy tails widen \emph{LP losses} (which are
bounded by deployed capital), not protocol solvency. Leverage is the only thing
that converts a fat tail into insolvency.
\end{keybox}
```{r ht-setup, include=FALSE}
ht <- R("heavytail.csv")
lev <- c("Gaussian GBM","Student-t (df=5)","Student-t (df=3)","Merton jumps",
"Severe jumps","Stochastic vol","Stoch vol + jumps","Black-swan -40%")
ht$scenario <- factor(ht$scenario, levels = lev)
ht1 <- subset(ht, oversub_x == 1.0)
ht$ov <- factor(ht$oversub_x, levels = c(1.0, 1.5),
labels = c("1.0 (full collateral)", "1.5 (levered)"))
```
```{r fig-htdist, fig.cap="\\label{fig:htdist}LP 6-month return distribution under fat-tailed processes (full collateralization). Tails fatten sharply relative to Gaussian; the black-swan and stoch-vol+jumps regimes carry the deepest losses.", fig.height=3.0}
hd <- R("heavytail_dist.csv")
sel <- c("Gaussian GBM","Student-t (df=3)","Severe jumps","Stoch vol + jumps","Black-swan -40%")
hd <- subset(hd, scenario %in% sel); hd$scenario <- factor(hd$scenario, levels = sel)
ggplot(hd, aes(total_ret, color = scenario)) +
geom_vline(xintercept = 0, linetype = "dashed", color = pal$gray, linewidth = 0.3) +
geom_density(linewidth = 0.8) +
scale_x_continuous(labels = percent, limits = c(-0.9, 0.9)) +
scale_color_manual(values = c(pal$gray, pal$primary, pal$amber, pal$accent, pal$risk)) +
labs(x = "6-month LP total return", y = "density", color = NULL) +
theme_haptic() + theme(legend.text = element_text(size = 7))
```
```{r fig-httail, fig.cap="\\label{fig:httail}Tail loss by scenario at full collateralization: 5\\% CVaR (bars) and worst observed path (points). Fat tails roughly double the worst-case LP drawdown versus Gaussian.", fig.height=3.0}
ggplot(ht1, aes(scenario, cvar5)) +
geom_col(fill = pal$risk, alpha = 0.85, width = 0.7) +
geom_point(aes(y = worst_ret), color = pal$ink, size = 1.8) +
geom_text(aes(y = worst_ret, label = "worst"), vjust = 1.6, size = 2.2, color = pal$ink) +
scale_y_continuous(labels = percent) +
labs(x = NULL, y = "6-mo loss (CVaR5 = bar, worst = point)") +
theme_haptic() + theme(axis.text.x = element_text(angle = 35, hjust = 1, size = 7))
```
```{r fig-htsolv, fig.cap="\\label{fig:htsolv}Insolvency rate by scenario: full collateralization (teal) vs.\\ 1.5x leverage (red). Full collateralization is zero everywhere --- solvency is path-independent --- while leverage turns fat tails into frequent shortfalls.", fig.height=3.0}
ggplot(ht, aes(scenario, insolvency_rate, fill = ov)) +
geom_col(position = position_dodge(width = 0.72), width = 0.64) +
scale_fill_manual(values = c("1.0 (full collateral)" = pal$accent, "1.5 (levered)" = pal$risk)) +
scale_y_continuous(labels = percent) +
labs(x = NULL, y = "insolvency rate", fill = "over-subscription") +
theme_haptic() + theme(axis.text.x = element_text(angle = 35, hjust = 1, size = 7))
```
```{r ht-table}
tt <- data.frame(
Scenario = gsub("%", "\\\\%", as.character(ht1$scenario)),
`Ann return` = pc(ht1$mean_ann_ret),
`CVaR 5pct` = pc(ht1$cvar5),
`Worst path` = pc(ht1$worst_ret),
`Insolv 1.0x` = pc(ht1$insolvency_rate, 0),
`Insolv 1.5x` = pc(ht[match(paste(ht1$scenario, 1.5),
paste(ht$scenario, ht$oversub_x)), "insolvency_rate"], 0),
check.names = FALSE)
ktab(tt, "Heavy-tail tail-risk summary (mu=0, full collateralization unless noted; last column at 1.5x leverage).", fs = 8.4) |>
column_spec(1, width = "3.0cm")
```
Two further observations. First, mean return is a \emph{misleading} headline under
fat tails: several heavy-tail regimes post higher average returns than Gaussian
even as their worst-case losses double --- the classic short-volatility trap, and
the reason CVaR and worst-path are the metrics that matter. Second, the
stochastic-volatility regimes are where the volatility floor earns its keep
(\S7), since realized volatility swings far below its long-run level between
bursts.
# Limitations
\begin{warnbox}[Simulation caveats]
\begin{itemize}
\item \textbf{Cohort agents, not individuals.} Arbitrage, noise and buyer demand
are modelled as representative cohorts with calibrated parameters, not thousands
of distinct strategic actors; the vol-risk-premium and demand elasticity are
exogenous inputs, not emergent.
\item \textbf{Heavy tails are probed, not exhausted.} \S9 adds Student-t, jump,
stochastic-volatility and black-swan stresses, but the parameters are illustrative
rather than calibrated to a specific asset's tail; volatility-of-volatility and
jump intensities are exogenous choices.
\item \textbf{Idealized settlement \& fees.} Each band's settlement reads an
end-of-cycle price (TWAP idealized as $S_T$); fee capture assumes the band is the
relevant liquidity at its price; gas, MEV and discrete tick spacing are abstracted.
\item \textbf{No active delta hedge.} The vault is naturally balanced across call
and put bands but is not actively delta-hedged; an explicit hedge would compress
the bear-regime tail at some bleed cost.
\end{itemize}
\end{warnbox}
# Conclusion
The simulation supports the core claims of the design and sharpens the caveats.
Native collateralization makes the protocol solvent by construction (0\%
insolvency at full backing, across every regime), and the deterministic
boundary-composition settlement carries the payouts. LP returns are
EV-positive with a credible risk profile (Sharpe of roughly 1.0--1.5), but they are
**entirely a harvested volatility-risk-premium** --- without persistent buyer
overpayment the vault loses to loss-versus-rebalancing. The principal residual
risks are exactly those identified analytically: a fat left tail in sustained
down-trends (an inventory/short-vol signature), capital-efficiency leverage that
trades away the solvency guarantee, and settlement integrity, which the
depth-derived OI cap is shown to enforce tightly under TWAP and not at all under
spot.
\vspace{1em}
\noindent\textit{Disclaimer: a research simulation, unaudited, not an offer or
recommendation of any financial instrument. Past simulated performance does not
indicate future results.}