29 min read

> "The gambling known as business looks with austere disfavor upon the business known as gambling."

Learning Objectives

  • Understand the historical evolution of sports betting from ancient civilizations to the modern post-PASPA era
  • Navigate the current legal landscape of sports betting across the United States and major international markets
  • Identify and mathematically analyze all common bet types including moneyline, spread, totals, parlays, and props
  • Explain how sportsbooks operate, how they profit through vigorish, and how lines are set and moved
  • Set up a basic quantitative toolkit including Python environments, data sources, and bankroll tracking systems

Chapter 1: Introduction to Sports Betting

"The gambling known as business looks with austere disfavor upon the business known as gambling." --- Ambrose Bierce, The Devil's Dictionary (1911)

Chapter Overview

Every day, millions of dollars change hands in the global sports betting market. The overwhelming majority of that money flows in one direction: from bettors to sportsbooks. This is not because sports outcomes are unknowable, nor because sportsbooks possess some mystical forecasting ability. It is because most bettors approach wagering with intuition, emotion, and tribal loyalty rather than with discipline, mathematics, and systematic analysis. This book exists to place you on the other side of that divide.

Sports betting, at its core, is a problem of probability estimation under uncertainty. The sportsbook publishes a set of prices --- odds --- that encode an implied probability for each outcome. Your task as an analytical bettor is to build independent probability estimates and to wager when your estimate diverges favorably from the market's. This is conceptually identical to what quantitative traders do in financial markets, and the mathematical toolkit is largely the same: probability theory, statistics, optimization, and programming. The difference is that sports betting markets, while increasingly efficient, still contain exploitable inefficiencies that are accessible to individuals with the right skills and discipline.

This opening chapter lays the groundwork for everything that follows. We will trace the long history of wagering on athletic contests, survey the legal landscape that shapes today's market, dissect each major bet type with mathematical precision, peer behind the curtain at how sportsbooks actually operate, and assemble the toolkit you will use throughout this book. Whether you are a complete beginner or an experienced bettor seeking to formalize your approach, this chapter ensures we share a common language and framework before diving into the quantitative deep end.

In this chapter, you will learn to: - Convert between American odds, decimal odds, fractional odds, and implied probabilities fluently - Calculate the vigorish embedded in any set of odds and assess the true cost of each wager - Set up a Python-based betting analysis environment with basic tracking and calculation tools


1.1 A Brief History of Sports Betting

The Ancient Roots of Wagering

The impulse to wager on competitive outcomes is as old as competition itself. Archaeological evidence from Mesopotamia includes six-sided dice carved from bone dating to approximately 3000 BCE, and the Rigveda --- one of humanity's oldest texts --- contains a hymn warning of the dangers of gambling with dice. But it was in the classical civilizations of Greece and Rome that wagering became systematically intertwined with organized sport.

In ancient Greece, the Olympic Games (beginning traditionally in 776 BCE) drew not only athletes but also spectators eager to back their favored competitors. While formal bookmaking did not exist, informal wagering on foot races, wrestling, and the chariot events at Olympia was widespread. The Greeks understood probability at an intuitive level; Aristotle discussed chance and causation, though a formal mathematics of probability would wait nearly two millennia.

Rome expanded the scale dramatically. Chariot racing at the Circus Maximus --- which could seat over 150,000 spectators --- featured organized betting with intermediaries who functioned as proto-bookmakers. Gladiatorial combat, animal fights, and even naval recreations all attracted heavy wagering. Roman law technically prohibited most forms of gambling (alea), but enforcement was lax, and emperors themselves were notorious gamblers. Augustus, Claudius, and Nero all wagered openly and lavishly.

The Birth of Modern Bookmaking

The story of modern sports betting begins with the English thoroughbred. Horse racing became organized in England in the early 17th century, and by the 18th century, the racecourse at Newmarket was the epicenter of a sophisticated wagering culture. The critical innovation came from bookmakers who began offering fixed odds --- a commitment to pay out at a stated price --- rather than the older system of wagering between individuals at mutually agreed terms.

The mathematics followed the practice. In 1654, Blaise Pascal and Pierre de Fermat conducted their famous correspondence on the "problem of points," laying the foundations of probability theory. By the 18th century, Abraham de Moivre's The Doctrine of Chances (1718) gave bettors --- at least the literate ones --- a formal framework for thinking about uncertain outcomes.

The 19th century saw the development of pari-mutuel betting (invented by Parisian entrepreneur Joseph Oller in 1867), in which all bets on an event are pooled and payouts are calculated after the pool is divided among winners, minus a house commission. This system, still used at racetracks worldwide, shifted risk from the bookmaker to the collective pool. Simultaneously, traditional fixed-odds bookmaking flourished in Britain, with licensed betting shops becoming a legal feature of British high streets after the Betting and Gaming Act of 1960.

The American Story: From Wire Rooms to Las Vegas

In the United States, sports betting followed a more turbulent path. Horse racing was legal and popular in the 19th century, with major tracks operating openly. But wagering on other sports existed in a shadowy world of "wire rooms" --- illegal bookmaking operations that received results via telegraph and later telephone. Organized crime became deeply entangled with sports betting in the early 20th century, and high-profile scandals (the 1919 Black Sox, point-shaving schemes in college basketball in the 1950s) cemented the association between sports betting and corruption in the American public mind.

Nevada legalized sports betting in 1949, and Las Vegas sportsbooks became the one legal venue for American bettors. For decades, the Nevada model was a small, regulated island in a vast sea of illegal betting. Estimates of the illegal sports betting market in the United States during the late 20th century range from $80 billion to $380 billion annually --- dwarfing the legal Nevada market.

PASPA and Its Repeal

In 1992, Congress passed the Professional and Amateur Sports Protection Act (PASPA), which effectively prohibited states other than Nevada (and, with limitations, Oregon, Delaware, and Montana) from authorizing sports betting. PASPA froze the legal landscape for 26 years.

The turning point came on May 14, 2018, when the Supreme Court of the United States struck down PASPA in Murphy v. National Collegiate Athletic Association. The Court held that PASPA violated the Tenth Amendment's anti-commandeering principle by forbidding states from modifying their own gambling laws. The decision did not legalize sports betting nationwide; rather, it returned the question to each individual state.

The Modern Explosion

The post-PASPA era has transformed American sports betting from a niche Nevada activity into a mainstream national industry. Within five years of the ruling, more than 30 states plus the District of Columbia had authorized some form of legal sports betting. The American Gaming Association reported that Americans legally wagered over $120 billion on sports in 2024 alone.

The industry is now dominated by large, publicly traded operators. Mobile betting accounts for the vast majority of handle (total amount wagered) in most states, and the integration of betting into sports media --- through partnerships, broadcasts, and in-app wagering --- has accelerated cultural normalization.

Historical Timeline

Year Event
~3000 BCE Earliest known dice discovered in Mesopotamia
776 BCE Traditional date of the first Olympic Games; informal wagering common
~100 CE Organized betting on chariot racing and gladiatorial combat in Rome
1654 Pascal and Fermat lay foundations of probability theory
1718 De Moivre publishes The Doctrine of Chances
1867 Joseph Oller invents the pari-mutuel betting system in Paris
1919 Black Sox scandal rocks American baseball
1949 Nevada legalizes sports betting
1960 UK's Betting and Gaming Act legalizes betting shops
1992 PASPA enacted, freezing US sports betting expansion
2018 Supreme Court strikes down PASPA in Murphy v. NCAA
2019--2025 Rapid state-by-state legalization across the US

Real-World Application: Understanding this history is not merely academic. The regulatory patchwork created by state-by-state legalization directly affects where you can bet, what types of bets are available, and what tax obligations you face. The historical entanglement of sports betting with organized crime also explains why regulation is often conservative and why sportsbooks invest heavily in compliance --- factors that shape the market structure you will learn to navigate.


State-by-State Legalization in the United States

The post-PASPA legal landscape is a patchwork. As of early 2026, more than 38 states plus the District of Columbia have legalized sports betting in some form, though the specific rules vary enormously. Some states permit only in-person (retail) wagering at casinos or racetracks. Others allow mobile betting but restrict it to within state borders, enforced through geolocation technology. A few states have legalized but not yet launched markets due to regulatory delays.

Key variables across states include:

  • Licensing model: Some states (e.g., New Jersey, Pennsylvania) allow multiple competing operators. Others (e.g., Washington, D.C.) grant monopoly or near-monopoly rights. Several states use a lottery-operated model.
  • Tax rates: State tax rates on operator gross gaming revenue range from approximately 6.75% (Nevada) to 51% (New York mobile). These rates affect the competitiveness of odds offered to bettors.
  • Permitted bet types: Most states allow standard bets on professional and major college sports. Some restrict or prohibit betting on in-state college teams, player props on college athletes, or certain niche markets.
  • Mobile vs. retail: Mobile betting dominates handle in nearly every state where it is permitted, often accounting for 85--95% of total wagers.

International Markets

The United States, despite its rapid growth, is a relative newcomer to regulated sports betting. Several international markets are far more mature:

  • United Kingdom: The UK has one of the world's most developed betting markets, regulated by the UK Gambling Commission. Licensed bookmakers operate both retail shops and online platforms. The market features vigorous competition, which tends to produce sharper (more efficient) odds. The UK model also includes betting exchanges (notably Betfair), which allow bettors to trade positions directly with each other.
  • Australia: Legal sports betting is well-established, with a strong online market. Australia has imposed advertising restrictions in recent years and prohibits certain bet types (e.g., in-play betting online is technically restricted, though enforcement is complex).
  • Europe: Most Western European countries have regulated markets, though models vary. Some (France, Italy) restrict operators to domestically licensed firms. Others have more open frameworks. Asian handicap betting, originating in markets like Pinnacle and SBO, is widely used in European soccer markets.
  • Asia: The legal landscape is complex. Hong Kong's Jockey Club is the largest single betting operator in the world by turnover. Macau focuses primarily on casino gaming. Much of the enormous Asian betting volume flows through gray- or black-market operators.

Regulatory Frameworks and Consumer Protection

Regulated markets typically require operators to:

  1. Verify bettor identity and age (KYC --- Know Your Customer)
  2. Implement responsible gambling measures (deposit limits, self-exclusion)
  3. Maintain segregated funds to protect bettor balances
  4. Report suspicious betting activity that may indicate match-fixing
  5. Pay licensing fees and taxes

For the analytical bettor, regulation matters because it determines market structure. Highly taxed markets tend to offer worse odds (wider margins), because operators must recover their tax burden. Competitive markets with many operators tend to offer better prices and higher limits.

Types of Operators

Operator Type Description Examples
Retail sportsbook Physical location, typically within a casino Circa Sports (Las Vegas), various state-licensed locations
Online/mobile sportsbook App-based or web-based, geofenced by jurisdiction DraftKings, FanDuel, BetMGM
Betting exchange Platform matching bettors against each other, charging commission Betfair, Betdaq, Prophet Exchange
Market-maker book Offers high limits and sharp lines; welcomes all bettors Circa Sports, Pinnacle
Retail-facing book Consumer-focused; aggressive on promotions, may limit winners Most US mobile operators

Tax Implications for Bettors

In the United States, all gambling winnings are taxable income. The IRS requires that you report net winnings, and sportsbooks issue W-2G forms for certain large payouts. You may deduct gambling losses, but only up to the amount of your winnings, and only if you itemize deductions.

For the serious bettor, tracking every wager is essential --- not just for analytical purposes, but for tax compliance. The following Python function computes your after-tax return on a winning bet, accounting for both federal and state income tax rates.

def calculate_tax_adjusted_profit(
    stake: float,
    american_odds: int,
    federal_tax_rate: float = 0.24,
    state_tax_rate: float = 0.05,
) -> dict[str, float]:
    """
    Calculate the tax-adjusted profit from a winning bet.

    In the United States, gambling winnings are subject to both federal
    and state income tax. This function computes the gross payout, the
    gross profit, the combined tax liability on the profit, and the
    net (after-tax) profit.

    Args:
        stake: The amount wagered in dollars.
        american_odds: The American odds at which the bet was placed.
            Positive odds (e.g., +150) indicate underdog; negative odds
            (e.g., -110) indicate favorite.
        federal_tax_rate: Federal income tax rate applied to winnings.
            Defaults to 0.24 (24%), the standard withholding rate.
        state_tax_rate: State income tax rate applied to winnings.
            Defaults to 0.05 (5%).

    Returns:
        A dictionary containing:
            - 'gross_payout': Total amount received (stake + profit).
            - 'gross_profit': Profit before taxes.
            - 'federal_tax': Federal tax owed on the profit.
            - 'state_tax': State tax owed on the profit.
            - 'net_profit': Profit after all taxes.
            - 'effective_return': Net profit as a percentage of stake.

    Raises:
        ValueError: If stake is not positive or odds are zero.

    Example:
        >>> result = calculate_tax_adjusted_profit(100, 150)
        >>> print(f"Net profit: ${result['net_profit']:.2f}")
        Net profit: $106.50
    """
    if stake <= 0:
        raise ValueError("Stake must be a positive number.")
    if american_odds == 0:
        raise ValueError("American odds cannot be zero.")

    # Calculate gross profit based on American odds
    if american_odds > 0:
        gross_profit = stake * (american_odds / 100)
    else:
        gross_profit = stake * (100 / abs(american_odds))

    gross_payout = stake + gross_profit

    # Calculate taxes on the profit (not the returned stake)
    federal_tax = gross_profit * federal_tax_rate
    state_tax = gross_profit * state_tax_rate
    total_tax = federal_tax + state_tax

    net_profit = gross_profit - total_tax
    effective_return = (net_profit / stake) * 100

    return {
        "gross_payout": round(gross_payout, 2),
        "gross_profit": round(gross_profit, 2),
        "federal_tax": round(federal_tax, 2),
        "state_tax": round(state_tax, 2),
        "net_profit": round(net_profit, 2),
        "effective_return": round(effective_return, 2),
    }

Common Pitfall: Many bettors fail to account for taxes when evaluating their betting performance. A bet at +150 that you expect to win 40% of the time may appear to have positive expected value before taxes, but once you apply a combined 29% tax rate on winnings, the edge may vanish entirely. Always compute your expected value on an after-tax basis when evaluating long-term profitability.


1.3 Types of Bets

Understanding bet types is fundamental. Each type structures risk differently, and each carries distinct mathematical properties that the analytical bettor must internalize.

Moneyline Bets

A moneyline bet is the simplest form: you pick the winner. The odds are expressed in one of three formats --- American, decimal, or fractional --- all of which encode the same information.

American odds are the standard in the United States: - Positive odds (e.g., +200) indicate the profit on a $100 stake. A $100 bet at +200 returns $300 total ($200 profit + $100 stake). - Negative odds (e.g., -150) indicate how much you must stake to profit $100. A $150 bet at -150 returns $250 total ($100 profit + $150 stake).

Decimal odds are standard in Europe and Australia. They represent the total payout per unit staked. Decimal odds of 3.00 mean a $1 bet returns $3 total ($2 profit + $1 stake). Decimal odds of 1.667 mean a $1 bet returns $1.667 total.

Fractional odds are traditional in the UK. Odds of 2/1 (read "two to one") mean you profit $2 for every $1 staked. Odds of 2/3 mean you profit $2 for every $3 staked.

The critical analytical skill is converting odds to implied probability --- the probability of an outcome that the odds encode:

$$P_{\text{implied}} = \begin{cases} \dfrac{-\text{odds}}{-\text{odds} + 100} & \text{if American odds} < 0 \\[10pt] \dfrac{100}{\text{odds} + 100} & \text{if American odds} > 0 \end{cases}$$

For decimal odds, the conversion is simpler:

$$P_{\text{implied}} = \frac{1}{\text{decimal odds}}$$

Worked Example 1: Moneyline Conversion

Suppose the New York Yankees are listed at -160 and the Boston Red Sox at +140 on the moneyline.

Step 1: Convert each side to implied probability.

For the Yankees at -160:

$$P_{\text{Yankees}} = \frac{160}{160 + 100} = \frac{160}{260} = 0.6154 \approx 61.5\%$$

For the Red Sox at +140:

$$P_{\text{Red Sox}} = \frac{100}{140 + 100} = \frac{100}{240} = 0.4167 \approx 41.7\%$$

Step 2: Sum the implied probabilities.

$$P_{\text{total}} = 0.6154 + 0.4167 = 1.0321$$

Step 3: Interpret the overround.

The sum exceeds 1.0 (100%) by 0.0321, or about 3.2%. This excess is the overround (also called the "vig" or "juice") --- the sportsbook's built-in margin. In a perfectly fair market with no vig, the probabilities would sum to exactly 1.0.

Step 4: Calculate the "fair" (no-vig) probabilities by normalizing.

$$P_{\text{Yankees, fair}} = \frac{0.6154}{1.0321} = 0.5963 \approx 59.6\%$$

$$P_{\text{Red Sox, fair}} = \frac{0.4167}{1.0321} = 0.4037 \approx 40.4\%$$

If your independent analysis concludes that the Yankees' true win probability is 56%, you would conclude that the Yankees are overpriced (the market implies 59.6% but you estimate 56%) and that the Red Sox represent a value bet.

Point Spread Bets

Point spread betting is the dominant form for football and basketball in the United States. The spread is a handicap applied to the favored team. If the Kansas City Chiefs are favored by 7 points (written as Chiefs -7 or Bengals +7), then:

  • A bet on the Chiefs -7 wins if the Chiefs win by 8 or more points.
  • A bet on the Bengals +7 wins if the Bengals win outright or lose by 6 or fewer points.
  • If the Chiefs win by exactly 7, the bet is a "push" (tie), and stakes are returned.

Spread bets are typically offered at standard odds of -110 on each side, meaning you risk $110 to win $100. This -110/-110 structure embeds approximately 4.5% vigorish.

Key numbers are critically important in NFL spread betting. Because football scores frequently involve 3-point and 7-point increments (field goals and touchdowns with extra points), certain margins of victory occur with unusually high frequency. The most common NFL final margins are 3 (approximately 15% of games) and 7 (approximately 9% of games). Spreads that cross these key numbers carry disproportionate significance.

NFL Margin Approximate Frequency
1 4.5%
2 4.0%
3 15.0%
4 5.0%
5 3.5%
6 6.0%
7 9.0%
8 3.0%
10 5.5%
14 5.5%

Intuition: The reason key numbers matter so much is that moving a spread from -3 to -2.5 (a half-point "buy") captures roughly 7.5% of outcomes (half the ~15% that land on exactly 3), while moving from -4 to -3.5 captures only about 2.5%. The value of half-point line movements is highly non-uniform, and understanding this is one of the first edges available to analytical bettors.

Totals (Over/Under)

A totals bet is a wager on whether the combined score of both teams will be over or under a posted number. If the total for an NBA game is set at 224.5, you bet whether the combined final score will be 225 or more (over) or 224 or fewer (under). Like spreads, totals are typically offered at -110 on each side.

Parlays and Teasers

A parlay combines two or more individual bets ("legs") into a single wager. All legs must win for the parlay to pay. The appeal is a larger payout; the mathematical reality is a compounding disadvantage.

For a two-leg parlay with each leg at -110:

The fair probability of winning each leg is approximately 50% (ignoring vig). The probability of both winning is $0.50 \times 0.50 = 0.25$, or 25%. At true odds, a winning parlay should pay 3-to-1 (returning $4 on a $1 bet). The standard sportsbook payout for a two-team parlay at -110 per leg is approximately 2.64-to-1 (returning $3.64 on a $1 bet). The difference between the fair payout (4.00) and the actual payout (3.64) represents the sportsbook's compounded edge.

This compounding grows with each additional leg:

Legs True Odds (Fair) Typical Payout Sportsbook Edge
2 3-to-1 2.64-to-1 ~10%
3 7-to-1 6.00-to-1 ~12.5%
4 15-to-1 12.28-to-1 ~15%
5 31-to-1 24.35-to-1 ~18%
6 63-to-1 47.41-to-1 ~20%
10 1023-to-1 642.08-to-1 ~30%

A teaser is a special type of parlay (primarily for football) in which the bettor receives additional points on the spread in exchange for a reduced payout. A standard 6-point, two-team NFL teaser allows you to move each spread by 6 points. Teasers that cross key numbers (particularly moving through 3 and 7) can, under specific conditions, represent one of the few systematically profitable bet types --- a topic we will explore in detail in later chapters.

Props, Futures, and Same-Game Parlays

Proposition bets (props) are wagers on specific events within a game that do not necessarily relate to the final outcome. Examples include "Will Patrick Mahomes throw for over 275.5 yards?" or "Which team will score first?" Player props have exploded in popularity and represent one of the most potentially exploitable markets due to softer (less efficient) lines.

Futures bets are long-term wagers on outcomes decided over a season or tournament, such as "Who will win the Super Bowl?" or "Will the Boston Celtics win more than 52.5 regular-season games?" Futures typically carry high vigorish (total overround of 20--40% or more) due to the large number of possible outcomes and the long time horizon during which the sportsbook's capital is at risk.

Same-game parlays (SGPs) combine multiple bets from a single game into a parlay. Unlike traditional parlays where legs are assumed independent, SGP legs are correlated --- for example, a bet on a team to win and their star player to score heavily are positively correlated events. Sportsbooks price SGPs using proprietary correlation models, and the opacity of this pricing creates both risk and opportunity for analytical bettors.

Common Pitfall: Same-game parlays are the highest-margin product for most sportsbooks. The correlation adjustments applied by the book are rarely transparent, and research consistently shows that SGP pricing embeds margins of 15--30% or more. Treat SGPs with extreme caution and always attempt to price the individual components independently before accepting an SGP price.

Bet Type Comparison

Bet Type Typical Vig Complexity Liquidity Analytical Edge Potential
Moneyline 3--5% Low High Moderate
Spread 4--5% Low High Moderate
Total 4--5% Low High Moderate
Parlay (2-leg) ~10% Low High Low (without correlated legs)
Parlay (5+ legs) 18%+ Low High Very Low
Player Prop 5--10% Medium Medium High
Futures 15--40% Medium Low Moderate
Same-Game Parlay 15--30% High Medium Variable (research-dependent)

Python Code: Odds Conversion and Payout Calculation

from dataclasses import dataclass
from enum import Enum


class OddsFormat(Enum):
    """Supported odds formats."""

    AMERICAN = "american"
    DECIMAL = "decimal"
    FRACTIONAL = "fractional"


@dataclass
class BetCalculator:
    """
    A calculator for converting between odds formats, computing implied
    probabilities, and determining payouts for common bet types.

    This class provides the foundational arithmetic that underpins all
    betting analysis. Every odds format encodes the same underlying
    information; this calculator makes conversions seamless.

    Attributes:
        None (all methods are static or class-level utilities).

    Example:
        >>> calc = BetCalculator()
        >>> prob = calc.american_to_implied_probability(-150)
        >>> print(f"Implied probability: {prob:.4f}")
        Implied probability: 0.6000
    """

    @staticmethod
    def american_to_decimal(american_odds: int) -> float:
        """
        Convert American odds to decimal odds.

        Args:
            american_odds: American odds value (e.g., -110, +200).

        Returns:
            Equivalent decimal odds.

        Raises:
            ValueError: If american_odds is zero.
        """
        if american_odds == 0:
            raise ValueError("American odds cannot be zero.")
        if american_odds > 0:
            return (american_odds / 100) + 1
        return (100 / abs(american_odds)) + 1

    @staticmethod
    def decimal_to_american(decimal_odds: float) -> int:
        """
        Convert decimal odds to American odds.

        Args:
            decimal_odds: Decimal odds value (must be > 1.0).

        Returns:
            Equivalent American odds (rounded to nearest integer).

        Raises:
            ValueError: If decimal_odds is not greater than 1.0.
        """
        if decimal_odds <= 1.0:
            raise ValueError("Decimal odds must be greater than 1.0.")
        if decimal_odds >= 2.0:
            return round((decimal_odds - 1) * 100)
        return round(-100 / (decimal_odds - 1))

    @staticmethod
    def american_to_implied_probability(american_odds: int) -> float:
        """
        Convert American odds to implied probability.

        The implied probability is the breakeven win rate --- the
        frequency at which you must win bets at these odds to
        break even (ignoring the vig on the other side).

        Args:
            american_odds: American odds value.

        Returns:
            Implied probability as a float between 0 and 1.
        """
        if american_odds == 0:
            raise ValueError("American odds cannot be zero.")
        if american_odds < 0:
            return abs(american_odds) / (abs(american_odds) + 100)
        return 100 / (american_odds + 100)

    @staticmethod
    def calculate_payout(stake: float, american_odds: int) -> dict[str, float]:
        """
        Calculate the payout for a winning bet.

        Args:
            stake: Amount wagered.
            american_odds: American odds at time of bet.

        Returns:
            Dictionary with 'stake', 'profit', and 'total_payout'.
        """
        if american_odds == 0:
            raise ValueError("American odds cannot be zero.")
        if stake <= 0:
            raise ValueError("Stake must be positive.")

        if american_odds > 0:
            profit = stake * (american_odds / 100)
        else:
            profit = stake * (100 / abs(american_odds))

        return {
            "stake": round(stake, 2),
            "profit": round(profit, 2),
            "total_payout": round(stake + profit, 2),
        }

    @staticmethod
    def calculate_parlay_payout(
        stake: float, odds_list: list[int]
    ) -> dict[str, float]:
        """
        Calculate the payout for a parlay given a list of American odds.

        The parlay payout is computed by converting each leg to decimal
        odds, multiplying them together, and applying the product to
        the stake.

        Args:
            stake: Amount wagered on the parlay.
            odds_list: List of American odds for each leg.

        Returns:
            Dictionary with 'stake', 'combined_decimal_odds',
            'profit', 'total_payout', and 'implied_probability'.
        """
        if not odds_list:
            raise ValueError("Must provide at least one leg.")

        calc = BetCalculator()
        combined_decimal = 1.0
        combined_probability = 1.0

        for odds in odds_list:
            decimal_odds = calc.american_to_decimal(odds)
            implied_prob = calc.american_to_implied_probability(odds)
            combined_decimal *= decimal_odds
            combined_probability *= implied_prob

        total_payout = stake * combined_decimal
        profit = total_payout - stake

        return {
            "stake": round(stake, 2),
            "combined_decimal_odds": round(combined_decimal, 4),
            "profit": round(profit, 2),
            "total_payout": round(total_payout, 2),
            "implied_probability": round(combined_probability, 4),
        }

    @staticmethod
    def calculate_overround(odds_list: list[int]) -> dict[str, float]:
        """
        Calculate the total overround (vig) for a set of odds
        representing all outcomes of an event.

        Args:
            odds_list: American odds for every possible outcome.

        Returns:
            Dictionary with 'implied_probabilities' (list),
            'total_implied', and 'overround_pct'.
        """
        calc = BetCalculator()
        probs = [calc.american_to_implied_probability(o) for o in odds_list]
        total = sum(probs)
        overround = (total - 1.0) * 100

        return {
            "implied_probabilities": [round(p, 4) for p in probs],
            "total_implied": round(total, 4),
            "overround_pct": round(overround, 2),
        }

1.4 How Sportsbooks Operate

To exploit a market, you must understand the market maker. Sportsbooks are businesses, and their operations --- from line-setting to risk management to account restriction --- shape every bet you will ever place.

The Vigorish: The Price of Admission

The vigorish (also called "vig," "juice," or "margin") is the sportsbook's commission. It is embedded in the odds themselves, not charged separately. The standard vig on a two-outcome bet is the amount by which implied probabilities exceed 100%.

For a standard -110/-110 spread bet:

$$P_{\text{side A}} = \frac{110}{110 + 100} = \frac{110}{210} = 0.5238$$

$$P_{\text{side B}} = \frac{110}{110 + 100} = \frac{110}{210} = 0.5238$$

$$\text{Overround} = 0.5238 + 0.5238 - 1.0 = 0.0476 \approx 4.76\%$$

The sportsbook effectively charges each bettor approximately 4.76 cents of expected value per dollar wagered. Another way to express this: at -110, you must win 52.38% of your bets to break even, not the 50% you might naively expect.

The vig can also be expressed as the hold percentage --- the expected fraction of total handle the sportsbook retains:

$$\text{Hold \%} = 1 - \frac{1}{\sum_{i} P_{\text{implied}, i}}$$

For our -110/-110 example:

$$\text{Hold \%} = 1 - \frac{1}{1.0476} = 1 - 0.9546 = 0.0454 \approx 4.5\%$$

Intuition: Think of the vig as a tax on uncertainty. In perfectly efficient markets, the vig would be your maximum possible disadvantage. In practice, inefficiencies in probability estimation can exceed the vig, creating exploitable edges. Your mission is to find spots where your probability estimate differs from the market's by more than the vig.

Risk Management: Balancing the Book

Classical bookmaking theory holds that the ideal sportsbook balances its book --- attracting equal action on both sides so that the vig guarantees a profit regardless of the outcome. If a sportsbook takes $110,000 on each side of a -110/-110 spread bet, it pays out $210,000 to winners (the $110,000 stake plus $100,000 profit) and keeps the losers' $110,000, netting $10,000 on $220,000 in total handle --- a 4.5% hold, regardless of who wins.

In practice, perfect balance is rare. Modern sportsbooks employ two broadly different models:

The market-maker model (exemplified by Pinnacle, Circa Sports) focuses on pricing accuracy. These books set sharp lines, accept large bets, and aim to profit from the vig over high volume. They welcome (or at least tolerate) sophisticated bettors because sharp action helps them improve their lines. Their margins are thin but their handle is enormous.

The retail-facing model (exemplified by most US mobile sportsbooks) focuses on customer acquisition and entertainment. These books often copy opening lines from market-makers, then adjust based on their own liability. They offer generous promotions (sign-up bonuses, profit boosts) to attract recreational bettors, and they aggressively limit or ban bettors who consistently win. Their margins per bet are higher, but they actively exclude their most dangerous customers.

How Lines Are Set

The line-setting process is one of the most misunderstood aspects of sports betting. Lines do not simply reflect one analyst's opinion. The process typically works as follows:

  1. Model-based opening lines: Market-making sportsbooks develop proprietary quantitative models that generate probability estimates for each game. These models incorporate historical data, team and player statistics, injury information, weather, rest days, travel, and dozens of other variables. The model output is converted to an opening line.

  2. Soft opening / limit testing: The line is first posted with low limits (perhaps $500--$2,000 in the NFL). This "soft open" allows the book to test the line against the sharpest bettors in the market. If one side attracts heavy action from known sharp accounts, the line moves.

  3. Steam moves and market consensus: As multiple books post lines and take action, a consensus forms. A "steam move" occurs when a line moves rapidly across multiple books, typically triggered by sharp money at a respected sportsbook (like Pinnacle or Circa) that cascading into the broader market.

  4. Public money and late adjustments: As game time approaches, larger recreational bettors enter the market. Public money tends to favor favorites, home teams, overs, and popular franchises. Books may adjust lines to balance liability, particularly on high-profile events.

  5. Closing line: The final line before a game begins is called the "closing line." Research consistently shows that closing lines are the most efficient point in the market --- they are better predictors of outcomes than any publicly known model. The ability to consistently beat the closing line (closing line value, or CLV) is the single best indicator of long-term betting profitability.

Worked Example 2: Understanding Vig and True Probabilities

Suppose an NFL game is listed as follows:

  • Buffalo Bills -3 (-108)
  • Miami Dolphins +3 (-112)

Step 1: Convert each side to implied probability.

Bills -3 at -108:

$$P_{\text{Bills}} = \frac{108}{108 + 100} = \frac{108}{208} = 0.5192$$

Dolphins +3 at -112:

$$P_{\text{Dolphins}} = \frac{112}{112 + 100} = \frac{112}{212} = 0.5283$$

Step 2: Calculate the overround.

$$\text{Overround} = 0.5192 + 0.5283 - 1.0 = 0.0475 \approx 4.75\%$$

Step 3: Calculate the no-vig (fair) probabilities.

$$P_{\text{Bills, fair}} = \frac{0.5192}{1.0475} = 0.4956 \approx 49.6\%$$

$$P_{\text{Dolphins, fair}} = \frac{0.5283}{1.0475} = 0.5044 \approx 50.4\%$$

Step 4: Interpret.

The sportsbook's no-vig line slightly favors the Dolphins covering. Note that the vig is not evenly distributed: the Dolphins side carries slightly more juice (-112 vs. -108), suggesting the book has seen more action or expects more action on the Dolphins +3 side and has shaded the line accordingly.

Step 5: Evaluate a potential bet.

If your model estimates the Bills cover probability at 53%, the expected value of a bet on the Bills at -108 is:

$$\text{EV} = (0.53 \times \frac{100}{108}) - (0.47 \times 1) = (0.53 \times 0.9259) - 0.47 = 0.4907 - 0.47 = +0.0207$$

This represents a positive expected value of about +2.1% per dollar wagered --- a worthwhile bet if your model is well-calibrated.

Account Management and Limiting Winners

A reality of modern sports betting that every serious bettor must confront: most sportsbooks will limit or ban you if you win consistently. This practice, euphemistically called "account management" or "promo-banning," is legal in most jurisdictions and standard industry practice for retail-facing books.

Signs of account restriction include: - Maximum bet amounts reduced (from thousands of dollars to single digits) - Exclusion from promotional offers - Delayed bet acceptance - Outright account closure

Strategies for managing this reality include maintaining accounts at multiple books, using market-maker books (Pinnacle, Circa) that do not limit winners, varying bet sizes and timing, and avoiding obviously sharp behaviors (e.g., always betting steam moves immediately). We will discuss these strategies in detail in later chapters.

Real-World Application: The existence of bettor limiting is actually an important market signal. If a sportsbook limits you, it is strong evidence that you are finding genuine edges. Many professional bettors view their first limitation as a milestone rather than a setback. The key is to have diversified access to multiple sportsbooks and exchanges before limitations restrict your activity.


1.5 The Bettor's Toolkit

The analytical bettor needs more than just knowledge --- you need infrastructure. This section outlines the essential tools, accounts, and technical setup that will support your work throughout this book.

Essential Accounts and Platforms

Sportsbook accounts: Open accounts at as many legal sportsbooks as are available in your jurisdiction. The reasons are threefold:

  1. Line shopping: Different books offer different odds on the same event. The ability to compare and select the best available price is the single easiest way to improve your bottom line. Even small differences (e.g., -108 vs. -112) compound over hundreds of bets.
  2. Access to different markets: Some books offer player props that others do not. Exchange markets behave differently from traditional bookmaker markets.
  3. Redundancy against limitations: If one book limits you, others remain available.

Odds comparison tools: Services such as OddsJam, OddsBoom, and DonBest aggregate real-time odds across dozens of sportsbooks, making line shopping efficient. For the serious bettor, a subscription to one of these tools pays for itself quickly.

Data sources and APIs: Quantitative analysis requires data. Key sources include:

Data Type Sources
Historical odds Covers.com archives, Sports Insights, Kaggle datasets
Team/player statistics Basketball Reference, Pro Football Reference, FanGraphs (baseball)
Real-time odds feeds The Odds API, OddsJam API, Pinnacle API
Play-by-play data NFL's official API, nflfastR (R package), NBA Stats API
Weather data OpenWeatherMap, Visual Crossing
Injury reports Official league injury reports, Rotoworld/NBC Sports Edge

Basic Python Setup for This Book

All code in this book uses Python 3.11 or later. We recommend setting up a dedicated virtual environment:

# Create and activate a virtual environment
python -m venv sports-betting-env
source sports-betting-env/bin/activate  # On Windows: sports-betting-env\Scripts\activate

# Install core packages used throughout this book
pip install numpy pandas scipy matplotlib seaborn requests
pip install scikit-learn statsmodels jupyter
pip install sqlalchemy  # For database storage of bet history

Throughout this book, we will build a growing library of tools. The following class represents the beginning of your personal bet-tracking system --- a simple but extensible framework for recording, analyzing, and learning from every wager you make.

Python Code: Basic Betting Tracker

import json
from dataclasses import dataclass, field, asdict
from datetime import datetime
from pathlib import Path


@dataclass
class Bet:
    """
    Represents a single sports bet with all relevant metadata.

    Attributes:
        timestamp: When the bet was placed (ISO format string).
        sport: The sport (e.g., 'NFL', 'NBA', 'MLB').
        event: Description of the event (e.g., 'Bills vs Dolphins').
        bet_type: Type of bet ('moneyline', 'spread', 'total', 'prop',
            'parlay', 'future').
        selection: What was bet on (e.g., 'Bills -3', 'Over 47.5').
        american_odds: The American odds at the time of the bet.
        stake: The dollar amount wagered.
        sportsbook: The sportsbook where the bet was placed.
        result: Outcome --- 'win', 'loss', 'push', or 'pending'.
        profit: Net profit or loss (positive for wins, negative for
            losses, zero for pushes). Set to None while pending.
        notes: Optional notes about the rationale for the bet.
    """

    timestamp: str
    sport: str
    event: str
    bet_type: str
    selection: str
    american_odds: int
    stake: float
    sportsbook: str
    result: str = "pending"
    profit: float | None = None
    notes: str = ""


@dataclass
class BettingTracker:
    """
    A simple bet tracking system that persists data to a JSON file.

    This tracker records every bet placed, calculates running statistics,
    and provides basic performance analysis. It is intentionally simple
    to serve as a foundation; we will extend it with database storage,
    statistical analysis, and visualization in later chapters.

    Attributes:
        filepath: Path to the JSON file where bet data is stored.
        bets: List of all recorded bets.

    Example:
        >>> tracker = BettingTracker("my_bets.json")
        >>> tracker.add_bet(
        ...     sport="NFL",
        ...     event="Bills vs Dolphins",
        ...     bet_type="spread",
        ...     selection="Bills -3",
        ...     american_odds=-108,
        ...     stake=100.00,
        ...     sportsbook="DraftKings",
        ...     notes="Model gives Bills 53% cover probability"
        ... )
        >>> tracker.resolve_bet(0, result="win")
        >>> print(tracker.summary())
    """

    filepath: str = "betting_history.json"
    bets: list[Bet] = field(default_factory=list)

    def __post_init__(self) -> None:
        """Load existing bets from file if it exists."""
        path = Path(self.filepath)
        if path.exists():
            with open(path, "r", encoding="utf-8") as f:
                data = json.load(f)
                self.bets = [Bet(**record) for record in data]

    def _save(self) -> None:
        """Persist the current bet list to the JSON file."""
        with open(self.filepath, "w", encoding="utf-8") as f:
            json.dump([asdict(b) for b in self.bets], f, indent=2)

    def add_bet(
        self,
        sport: str,
        event: str,
        bet_type: str,
        selection: str,
        american_odds: int,
        stake: float,
        sportsbook: str,
        notes: str = "",
    ) -> int:
        """
        Record a new bet.

        Args:
            sport: The sport being bet on.
            event: Description of the event.
            bet_type: Type of bet placed.
            selection: The specific selection made.
            american_odds: American odds at time of bet.
            stake: Dollar amount wagered.
            sportsbook: Where the bet was placed.
            notes: Optional analysis notes.

        Returns:
            The index of the newly added bet.
        """
        bet = Bet(
            timestamp=datetime.now().isoformat(),
            sport=sport,
            event=event,
            bet_type=bet_type,
            selection=selection,
            american_odds=american_odds,
            stake=stake,
            sportsbook=sportsbook,
            notes=notes,
        )
        self.bets.append(bet)
        self._save()
        return len(self.bets) - 1

    def resolve_bet(self, index: int, result: str) -> None:
        """
        Record the outcome of a previously placed bet.

        Args:
            index: The index of the bet to resolve.
            result: One of 'win', 'loss', or 'push'.

        Raises:
            IndexError: If index is out of range.
            ValueError: If result is not valid.
        """
        if index < 0 or index >= len(self.bets):
            raise IndexError(f"Bet index {index} is out of range.")
        if result not in ("win", "loss", "push"):
            raise ValueError("Result must be 'win', 'loss', or 'push'.")

        bet = self.bets[index]
        bet.result = result

        if result == "push":
            bet.profit = 0.0
        elif result == "loss":
            bet.profit = -bet.stake
        else:  # win
            if bet.american_odds > 0:
                bet.profit = bet.stake * (bet.american_odds / 100)
            else:
                bet.profit = bet.stake * (100 / abs(bet.american_odds))

        self._save()

    def summary(self) -> dict[str, float | int]:
        """
        Calculate summary statistics across all resolved bets.

        Returns:
            Dictionary containing:
                - total_bets: Number of bets placed.
                - resolved_bets: Number of bets with outcomes.
                - wins, losses, pushes: Counts of each outcome.
                - win_rate: Wins divided by (wins + losses).
                - total_staked: Sum of all stakes on resolved bets.
                - total_profit: Net profit across all resolved bets.
                - roi: Return on investment (profit / staked * 100).
        """
        resolved = [b for b in self.bets if b.result != "pending"]
        wins = sum(1 for b in resolved if b.result == "win")
        losses = sum(1 for b in resolved if b.result == "loss")
        pushes = sum(1 for b in resolved if b.result == "push")

        total_staked = sum(b.stake for b in resolved)
        total_profit = sum(b.profit for b in resolved if b.profit is not None)

        win_rate = wins / (wins + losses) if (wins + losses) > 0 else 0.0
        roi = (total_profit / total_staked * 100) if total_staked > 0 else 0.0

        return {
            "total_bets": len(self.bets),
            "resolved_bets": len(resolved),
            "wins": wins,
            "losses": losses,
            "pushes": pushes,
            "win_rate": round(win_rate, 4),
            "total_staked": round(total_staked, 2),
            "total_profit": round(total_profit, 2),
            "roi": round(roi, 2),
        }

    def profit_by_sport(self) -> dict[str, float]:
        """
        Break down total profit by sport.

        Returns:
            Dictionary mapping sport names to net profit.
        """
        profits: dict[str, float] = {}
        for bet in self.bets:
            if bet.profit is not None and bet.result != "pending":
                profits[bet.sport] = profits.get(bet.sport, 0.0) + bet.profit
        return {k: round(v, 2) for k, v in profits.items()}

    def profit_by_sportsbook(self) -> dict[str, float]:
        """
        Break down total profit by sportsbook.

        Useful for identifying which books offer the best results,
        which may correlate with line quality or market efficiency.

        Returns:
            Dictionary mapping sportsbook names to net profit.
        """
        profits: dict[str, float] = {}
        for bet in self.bets:
            if bet.profit is not None and bet.result != "pending":
                profits[bet.sportsbook] = (
                    profits.get(bet.sportsbook, 0.0) + bet.profit
                )
        return {k: round(v, 2) for k, v in profits.items()}

Building Your Environment

Beyond the code itself, the analytical bettor needs a disciplined working environment:

  1. Dedicated workspace: Keep your betting analysis separate from other projects. A clean directory structure --- mirroring the topics in this book --- helps you stay organized as your codebase grows.

  2. Version control: Use Git to track changes to your models and analysis scripts. When a model's performance degrades, you want the ability to review what changed and when.

  3. Documentation discipline: Comment your code, record your rationale for each bet (the notes field in our tracker), and maintain a betting journal. The bettors who improve fastest are those who systematically review their decisions.

  4. Security awareness: Your sportsbook login credentials, API keys, and financial data are sensitive. Use environment variables or a secrets manager rather than hardcoding credentials. Never commit API keys to a Git repository.

Intuition: Think of your betting operation as a small quantitative fund. The infrastructure may seem like overhead now, but as your volume grows and your strategies become more sophisticated, the bettors who invested early in solid tooling will be far more productive than those who are still managing bets in a spreadsheet with no version history and no systematic record of their analytical rationale.


1.6 Chapter Summary

Key Concepts

  1. Sports betting is fundamentally a problem of probability estimation: you profit when your probability estimates are more accurate than the market's, after accounting for the vig.

  2. The vigorish (vig/juice) is the sportsbook's built-in commission, embedded in the odds as an overround beyond 100% implied probability. Standard vig on a -110/-110 bet is approximately 4.76%.

  3. Implied probability is the probability of an outcome encoded in a set of odds. It is the breakeven win rate at those odds.

  4. American odds express payout in terms of a $100 reference: positive odds state the profit on a $100 bet; negative odds state the stake required to profit $100.

  5. Point spreads handicap the favorite and are sensitive to key numbers (3 and 7 in football), creating non-uniform value in half-point line movements.

  6. Parlays compound the sportsbook's edge multiplicatively, making them poor-value bets in the absence of exploitable correlations.

  7. Market-maker sportsbooks profit through volume and sharp pricing; retail-facing sportsbooks profit through wider margins and limiting winning bettors.

  8. Closing line value (CLV) --- the ability to consistently beat the closing line --- is the strongest known predictor of long-term betting profitability.

  9. Line shopping across multiple sportsbooks is the simplest and most reliable way to improve your results.

  10. Systematic tracking of every bet, including rationale and outcome, is essential for both tax compliance and analytical improvement.

Key Formulas

Formula Description
$P = \frac{\lvert \text{odds} \rvert}{\lvert \text{odds} \rvert + 100}$ Implied probability from negative American odds
$P = \frac{100}{\text{odds} + 100}$ Implied probability from positive American odds
$P = \frac{1}{\text{decimal odds}}$ Implied probability from decimal odds
$\text{Overround} = \sum P_i - 1$ Vigorish as total overround
$\text{Hold \%} = 1 - \frac{1}{\sum P_i}$ Expected sportsbook hold percentage
$\text{EV} = (P_{\text{win}} \times \text{profit}) - (P_{\text{loss}} \times \text{stake})$ Expected value of a bet
$\text{Decimal odds} = \frac{\text{American}}{100} + 1$ American (positive) to decimal conversion
$\text{Decimal odds} = \frac{100}{\lvert \text{American} \rvert} + 1$ American (negative) to decimal conversion

Key Code Patterns

In this chapter, we introduced three code patterns that will recur throughout the book:

  1. Odds conversion utilities (BetCalculator): A stateless calculator class with static methods for converting between odds formats and computing probabilities. This pattern --- small, focused utility classes --- will be our standard approach for mathematical building blocks.

  2. Financial calculation with tax awareness (calculate_tax_adjusted_profit): A standalone function that computes after-tax returns. This illustrates the principle that all financial calculations should account for real-world costs (taxes, fees, opportunity cost) from the start.

  3. Persistent bet tracking (BettingTracker): A data class with JSON persistence for recording and analyzing bets. This pattern --- structured records with file-based storage --- will evolve into database-backed systems as our needs grow.

Decision Framework: Should You Place This Bet?

The following decision process summarizes the analytical approach introduced in this chapter. It is deliberately simple; we will refine and formalize it throughout the book.

START: You are considering a bet.

1. Do you have an independent probability estimate for this outcome?
   - NO  --> Do not bet. (Never bet without an edge thesis.)
   - YES --> Proceed to step 2.

2. Convert the offered odds to implied probability.
   - What is the no-vig implied probability?

3. Does your estimated probability exceed the no-vig implied probability?
   - NO  --> No edge exists. Do not bet.
   - YES --> Proceed to step 4.

4. Is the edge larger than the vig on your side?
   - NO  --> The edge does not overcome the cost of the bet. Do not bet.
   - YES --> Proceed to step 5.

5. Does the bet fit within your bankroll management rules?
   (We will formalize these rules in later chapters.)
   - NO  --> The bet is too large relative to your bankroll. Do not bet.
   - YES --> Proceed to step 6.

6. Have you checked for the best available odds across all your accounts?
   - NO  --> Line shop. Find the best price.
   - YES --> Place the bet. Record it in your tracker with full notes.

Common Pitfall: The most dangerous step to skip is step 1. The vast majority of losing bettors place wagers based on "feel," fandom, or tips rather than independent quantitative analysis. If you cannot articulate a specific, quantifiable reason why you believe the true probability differs from the market price, you do not have an edge --- you are gambling, not betting analytically.


What's Next

In Chapter 2: Probability and Odds, we will build the mathematical foundation that underpins everything in this book. We will start with the axioms of probability theory, work through Bayes' theorem and its applications to updating beliefs as new information arrives, derive the relationships between probability and various odds formats rigorously, and introduce the concept of expected value --- the single most important number in all of betting. We will also begin building our first simple predictive model: a naive probability estimator based on historical win rates.

If this chapter gave you the vocabulary and context of sports betting, Chapter 2 will give you the grammar --- the rules for reasoning under uncertainty that separate the analytical bettor from the casual gambler. The mathematics is accessible, the examples are concrete, and the payoff is immediate: by the end of Chapter 2, you will be able to evaluate any bet and determine whether it has positive expected value.


This chapter is part of The Sports Betting Textbook, a comprehensive guide to quantitative sports betting. All code examples use Python 3.11+ and are available in the companion repository.