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 probability P(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 relationshipTrue joint vs productIf priced as independent
Positive correlationjoint higherodds too long, the vault overpays
Negative correlationjoint lowerodds too short, taker underpaid (safe)
Mutually exclusivejoint = 0a bet that can never win is sold a multiple
The dangerous direction is positive correlation: a sharp stacks co-moving legs (two BTC markets, a team across rounds) and gets paid as if they were independent long shots. The correlation engine closes exactly that.

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:
  1. 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 (or duplicate_leg for repeated markets).
  2. Crypto, shared factor. All crypto-underlying legs load on one common factor; rho is 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.
  3. Everything else (one-shot events, unrelated markets) is independent, rho = 0.
Joint probability is the product across clusters and the copula within each cluster.

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:
"correlation": {
  "independentOddsBps": "182400",
  "adjustedOddsBps": "156200",
  "jointProbBps": 640,
  "reductionBps": "26200",
  "clusters": [
    {
      "key": "u:BTC",
      "driver": "Bitcoin",
      "rho": 0.72,
      "marketIds": [101, 118],
      "independentProbBps": 1520,
      "jointProbBps": 2210
    },
    {
      "key": "solo:205",
      "driver": "Independent",
      "rho": 0,
      "marketIds": [205],
      "independentProbBps": 2900,
      "jointProbBps": 2900
    }
  ]
}
independentOddsBps
string
The naive product odds (what an independence-assuming engine would pay), bps.
adjustedOddsBps
string
The correlation-aware odds actually charged, bps.
jointProbBps
number
Overall adjusted joint win probability, bps.
reductionBps
string
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.
clusters
ClusterBreakdown[]
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):
ComponentDefaultApplied to
Spread200 bpsOdds (vault edge)
Protocol fee100 bpsStake
Builder fee25 bpsStake
Fees come out of the stake side (netStake in the quote is stake minus fees); the payout in the quote is already net of everything, so display it as-is.