The client never supplies odds
A quote request carries only which legs ({marketId, outcome}[]) and the stake. The relayer fetches live Hyperliquid mids (fetchAllMids), reads each backed side’s implied probability in bps, and computes the payout server-side. A taker cannot inflate their own payout, and there is no basis risk: a HIP-4 side’s mid is its implied probability, and it is the same venue the legs settle against.
Why independence is wrong
A parlay pays only if every leg wins, so the fair price is the joint probabilityP(L1 and L2 and ... and Ln), not the product of the marginals. Pricing the product is wrong whenever legs move together, and the error is asymmetric:
| Leg relationship | True joint vs product | If priced as independent |
|---|---|---|
| Positive correlation | joint higher | odds too long, the vault overpays |
| Negative correlation | joint lower | odds too short, taker underpaid (safe) |
| Mutually exclusive | joint = 0 | a bet that can never win is sold a multiple |
The model: one-factor Gaussian copula
Per-leg marginals stay as HL mids; dependence is overlaid with a one-factor Gaussian copula. Each binary leg loads on a shared latent factor; the joint “all win” probability is a one-dimensional integral evaluated numerically (jointAllWin in @parlays-live/sdk). rho = 0 recovers the independent product; rho = 1 collapses to the least likely leg; the result is always bounded in [product of p_i, min p_i].
Conservative by construction: rho is floored at 0, so the adjustment can only ever shorten odds versus independence. Hedged combos (a long against a short) fall back to independent pricing rather than being widened.
Structural clustering, not an estimated matrix
Legs are clustered from HL metadata (buildMarketGraph), never from a pairwise correlation matrix that data cannot support:
- Same question, mutually exclusive. Two outcomes of the same HL question (two BTC price buckets, two World Cup champions) cannot both win. The combo is refused with HTTP 422 and code
mutually_exclusive(orduplicate_legfor repeated markets). - Crypto, shared factor. All crypto-underlying legs load on one common factor;
rhois measured live from HL candle returns. Loadings are signed by bull/bear direction, so BTC-up plus BTC-down reads as a hedge and stays at independent odds. - Everything else (one-shot events, unrelated markets) is independent,
rho = 0.
The correlation block in every quote
Every successful quote returns the full transparency breakdown so your UI can show the taker exactly what the guard did:
The naive product odds (what an independence-assuming engine would pay), bps.
The correlation-aware odds actually charged, bps.
Overall adjusted joint win probability, bps.
How much payout multiple the guard removed:
independent - adjusted. Render this as a “correlation adjustment” row when it is greater than zero. See the drawer guide.One entry per cluster:
key ("u:BTC", "solo:123"), human driver label, applied rho, member marketIds, and the independent vs joint probability inside the cluster.Spread and fees
On top of the joint probability, the quote applies (relayer defaults, all configurable per deployment):| Component | Default | Applied to |
|---|---|---|
| Spread | 200 bps | Odds (vault edge) |
| Protocol fee | 100 bps | Stake |
| Builder fee | 25 bps | Stake |
netStake in the quote is stake minus fees); the payout in the quote is already net of everything, so display it as-is.
