# Parlay Calculator Example - JavaScript
URL: https://sockodds.com/docs/examples/parlay-builder/

# Parlay Calculator Example - JavaScript [#parlay-calculator-example---javascript]

Build a multi (parlay) calculator that combines multiple legs and calculates total odds and payout from live Australian prices.

## What you'll build [#what-youll-build]

A script that:

- Fetches live odds from multiple games
- Allows selecting multiple legs
- Calculates combined multi odds
- Shows potential payout
- Prevents legs from the same game

**Perfect for:** Multi calculators, bet-slip builders

## Prerequisites [#prerequisites]

- Node.js 18+
- SockOdds API key ([free](https://sockodds.com/signup/))
- Basic JavaScript

## Complete code [#complete-code]

### Step 1: Setup project [#step-1-setup-project]

```bash
mkdir multi-calc && cd multi-calc && npm init -y
```

### Step 2: Create calculator.js [#step-2-create-calculatorjs]

```javascript
// calculator.js
const API_KEY = process.env.SOCKODDS_KEY, BASE = "https://api.sockodds.com/v2";

async function events(league) {
  const url = new URL(BASE + "/events");
  url.search = new URLSearchParams({ leagueID: league, oddsAvailable: "true", oddID: "points-home-game-ml-home", includeOpposingOdds: "true", limit: "20" });
  return (await (await fetch(url, { headers: { "x-api-key": API_KEY } })).json()).data;
}

// a leg: the best available decimal for one side of one market on one event
function bestLeg(event, oddID) {
  const odd = event.odds[oddID]; if (!odd) return null;
  const [book, q] = Object.entries(odd.byBookmaker).filter(([, q]) => q.available).sort((a, b) => b[1].decimal - a[1].decimal)[0] ?? [];
  return q ? { eventID: event.eventID, label: `${odd.marketName} @ ${book}`, decimal: q.decimal, book } : null;
}

function multi(legs, stake) {
  const eventIDs = new Set(legs.map((l) => l.eventID));
  if (eventIDs.size !== legs.length) throw new Error("Cannot multi two legs from the same game — use the SGM lanes for that");
  const decimal = legs.reduce((p, l) => p * l.decimal, 1);
  const american = decimal >= 2 ? `+${Math.round((decimal - 1) * 100)}` : `${Math.round(-100 / (decimal - 1))}`;
  return { decimal: +decimal.toFixed(2), american, payout: +(stake * decimal).toFixed(2), profit: +(stake * (decimal - 1)).toFixed(2), impliedProbability: +((1 / decimal) * 100).toFixed(1) };
}

const stake = Number(process.argv[2] || 10);
const legs = [];
for (const league of ["AFL", "NRL"]) for (const e of await events(league)) { const leg = bestLeg(e, "points-home-game-ml-home"); if (leg && legs.length < 3) legs.push(leg); }
legs.forEach((l) => console.log(`leg  ${l.decimal.toFixed(2)}  ${l.label}`));
console.log(multi(legs, stake));
```

### Step 3: Run it [#step-3-run-it]

```bash
SOCKODDS_KEY=so_live_… node calculator.js 20
```

## Expected output [#expected-output]

```
leg  1.87  Head to Head — Fremantle @ sportsbet
leg  2.10  Head to Head — Canterbury Bulldogs @ tab
leg  1.65  Head to Head — Collingwood @ pointsbet
{ decimal: 6.48, american: '+548', payout: 129.6, profit: 109.6, impliedProbability: 15.4 }
```

## How it works [#how-it-works]

### 1. Calculate combined odds [#1-calculate-combined-odds]

Multiply the legs' `decimal` prices.

### 2. Calculate payout [#2-calculate-payout]

`stake × decimal`.

### 3. Prevent correlated legs [#3-prevent-correlated-legs]

One leg per `eventID`; same-game legs are priced by the SGM lanes (`betfocus__sgm_only`, …).

### 4. Show implied probability [#4-show-implied-probability]

`1 / decimal`.

## Enhancements [#enhancements]

### Same game multis [#same-game-multis]

```javascript
// use bookmakerID=betfocus__sgm_only,mintbet__sgm_only and take that lane's prices for correlated legs
```

### Calculate true odds [#calculate-true-odds]

```javascript
// on Base: multiply legs' fair decimals (from fairOdds) to see the vig in the multi
```

### Round robin [#round-robin]

```javascript
function combos(arr, k) { return k === 0 ? [[]] : arr.flatMap((v, i) => combos(arr.slice(i + 1), k - 1).map((c) => [v, ...c])); }
```

## Troubleshooting [#troubleshooting]

### "Cannot multi two legs from the same game" [#cannot-multi-two-legs-from-the-same-game]

By design; use an SGM lane.

### Calculated odds don't match the bookmaker [#calculated-odds-dont-match-the-bookmaker]

Books cap multi payouts and price SGMs on correlation.

### Multi odds seem too good [#multi-odds-seem-too-good]

A leg with `available: false` slipped in — the example filters them; make sure yours does.

## Next steps [#next-steps]

### Combine with other examples [#combine-with-other-examples]

- [Live odds tracker](https://sockodds.com/docs/examples/live-odds-tracker/)
- [Arbitrage calculator](https://sockodds.com/docs/examples/arbitrage-calculator/)
- [Odds comparison dashboard](https://sockodds.com/docs/examples/odds-comparison-dashboard/)
- [Player props analyzer](https://sockodds.com/docs/examples/player-props-analyzer/)

### Learn more [#learn-more]

- [The oddID grammar](https://sockodds.com/docs/data-types/odds/)
- [Best practices](https://sockodds.com/docs/info/best-practices/)
- [API reference](https://sockodds.com/docs/reference/)

> Need help?[FAQ](https://sockodds.com/docs/faq/) · [Email](mailto:api@sockodds.com) · [Contact](https://sockodds.com/contact-us/)
