oddsmith
v0.3.0
Published
Sports-betting odds math: format conversion, no-vig pricing, parlays, EV, and Kelly. Zero dependencies, fully typed.
Downloads
25
Maintainers
Readme
Oddsmith
Sports-betting odds math, done right. Convert between odds formats, strip the vig to find the fair price, build a multi-book consensus line, spot arbitrage, track closing line value, combine parlays, and size bets by expected value and Kelly.
Zero dependencies. Fully typed. Every function is pure.
Install
npm install oddsmithQuick start
import { americanToDecimal, fairOdds, vig, parlay, expectedValue, kelly } from 'oddsmith';
// Normalize any book's price to a common format
americanToDecimal(150); // 2.5
// How much margin is baked into this two-way market?
vig([1.91, 1.91]); // 0.0471 → 4.71%
// Strip the vig to recover the fair price
fairOdds([1.91, 1.91]); // [2, 2]
// Combine parlay legs into one price
parlay([1.5, 2.0, 1.8]); // 5.4
// Is this bet +EV given your own probability estimate?
expectedValue(2.5, 0.45, 10); // 1.25 (per $10 staked)
// What fraction of your bankroll does Kelly suggest?
kelly(2.5, 0.45); // 0.0833API
Conversion
| Function | Description |
| ---------------------------------------- | ------------------------------------ |
| americanToDecimal(american) | American → decimal odds |
| decimalToAmerican(decimal) | Decimal → American odds |
| fractionalToDecimal(str) | "3/2" → decimal odds |
| decimalToFractional(decimal) | Decimal → simplified fraction string |
| decimalToImpliedProbability(decimal) | Decimal → implied probability (0–1) |
| americanToImpliedProbability(american) | American → implied probability |
| impliedProbabilityToDecimal(p) | Probability → decimal odds |
| impliedProbabilityToAmerican(p) | Probability → American odds |
Vig & fair pricing
| Function | Description |
| -------------------------------------- | --------------------------------------- |
| overround(decimalOdds[]) | Total book probability (the "book sum") |
| vig(decimalOdds[]) | Sportsbook margin as a fraction |
| removeVig(decimalOdds[], { method }) | Fair probabilities that sum to 1 |
| fairOdds(decimalOdds[], { method }) | Fair (no-vig) decimal odds per outcome |
method may be multiplicative (default), additive, power, or shin. Shin's method corrects favorite–longshot bias.
Consensus, line shopping & arbitrage
| Function | Description |
| ---------------------------------------------------- | ------------------------------------------------------- |
| consensusProbabilities(books, { method, weights }) | Blend de-vigged books into one sharp fair line |
| consensusFairOdds(books, opts) | Consensus fair odds per outcome |
| bestPrice(decimalOdds[]) | Best (highest) payout across books, with its index |
| arbitrage(bestOdds[], totalStake?) | Detect guaranteed profit and the stake split to lock it |
Hedging & parlays
| Function | Description |
| ----------------------------------- | -------------------------------------------------- |
| hedge(stake, yourOdds, otherOdds) | Opposite-side stake that locks an equal return |
| parlayValue(legs, stake?) | Combined true probability, edge, and +EV check |
| hold(decimalOdds[]) | The book's theoretical hold (share of handle kept) |
| rankByHold(books) | Rank books friendliest-first by hold |
kelly(odds, trueProb, { fraction }) supports fractional Kelly (e.g. half-Kelly).
Closing Line Value
| Function | Description |
| ----------------------------------------- | ----------------------------------------------- |
| closingLineValue(yourOdds, closingOdds) | How your price compares to the close |
| averageClosingLineValue(records) | Average CLV across bets — a sharpness scorecard |
Parlays
| Function | Description |
| ----------------------------- | ---------------------------------- |
| parlay(legs[]) | Combined decimal price of all legs |
| parlayPayout(stake, legs[]) | Total return on a winning parlay |
Value
| Function | Description |
| ------------------------------------------ | ---------------------------------------------------- |
| payout(stake, decimal) | Total return, stake included |
| profit(stake, decimal) | Net profit on a win |
| expectedValue(decimal, trueProb, stake?) | EV given your probability estimate |
| edge(decimal, trueProb) | EV per unit staked, as a fraction |
| kelly(decimal, trueProb) | Kelly stake as a fraction of bankroll (0 if no edge) |
All inputs are validated; invalid values throw an OddsError.
Conventions
- Decimal odds are the internal lingua franca — convert to decimal first when comparing books.
- Probabilities are expressed in the range
(0, 1]. removeViguses proportional (normalization) vig removal.
License
MIT © Brandon Sullivan
