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
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
- Node.js 18+
- SockOdds API key (free)
- Basic JavaScript
Complete code
Step 1: Setup project
mkdir multi-calc && cd multi-calc && npm init -yStep 2: Create calculator.js
// 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
SOCKODDS_KEY=so_live_… node calculator.js 20Expected 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
1. Calculate combined odds
Multiply the legs' decimal prices.
2. Calculate payout
stake × decimal.
3. Prevent correlated legs
One leg per eventID; same-game legs are priced by the SGM lanes (betfocus__sgm_only, …).
4. Show implied probability
1 / decimal.
Enhancements
Same game multis
// use bookmakerID=betfocus__sgm_only,mintbet__sgm_only and take that lane's prices for correlated legsCalculate true odds
// on Base: multiply legs' fair decimals (from fairOdds) to see the vig in the multiRound robin
function combos(arr, k) { return k === 0 ? [[]] : arr.flatMap((v, i) => combos(arr.slice(i + 1), k - 1).map((c) => [v, ...c])); }Troubleshooting
"Cannot multi two legs from the same game"
By design; use an SGM lane.
Calculated odds don't match the bookmaker
Books cap multi payouts and price SGMs on correlation.
Multi odds seem too good
A leg with available: false slipped in — the example filters them; make sure yours does.