# MLB Playoff Odds Monte Carlo Simulator (`commodus67/mlb-playoff-odds-monte-carlo`) Actor

MLB baseball betting odds model. Monte Carlo replays every remaining game on the real schedule for fair division, wild card, playoff and top-seed probabilities for all 30 teams, plus value bets vs sportsbook odds. Keyless ESPN data, no account needed.

- **URL**: https://apify.com/commodus67/mlb-playoff-odds-monte-carlo.md
- **Developed by:** [ELIO LIBERATORE](https://apify.com/commodus67) (community)
- **Categories:** Agents, AI, Developer tools
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 team projections

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## MLB Playoff Odds Monte Carlo Simulator

Plays out the rest of the Major League Baseball regular season thousands of times and returns, for every one of the 30 teams, the fair probability of winning its division, taking a wild card, reaching the postseason at all, and finishing with the best record in its league. If you supply bookmaker implied probabilities, it also reports the gap between the model and the market.

Standings and the remaining schedule come from the public ESPN API. No account, no API key, no configuration.

### What makes it different

Almost every baseball tool on Apify Store copies numbers out of a website. This one computes something that is not published anywhere: a full distribution of season outcomes built from the actual games each team still has to play.

Three design choices matter.

**It simulates the real schedule, not an average one.** MLB schedules are unbalanced. A team fighting for a wild card while facing six games against the best team in its division has a harder path than its record suggests. Every remaining fixture is loaded individually, with the correct home and away side, and played out.

**It leans on run differential, not just the record.** Runs scored and runs allowed predict future wins better than the win-loss column does, because one-run wins and blowout losses carry the same weight in the standings but not in reality. Strength is estimated from Pythagorean expectation blended with the raw record, and you control the mix.

**It admits it does not know how good the teams are.** A partial season is a noisy sample. The model regresses every team towards .500 by a configurable amount, and then, in each simulated season, redraws each team's true talent from a distribution around that estimate. Without that second step the output is wildly overconfident: the better team wins nearly every simulated race and a 60 percent favourite comes out looking like a 95 percent certainty.

### How it works

1. Pull the current standings, split by league and division, including runs scored and runs allowed.
2. Convert each team's record into a strength rating on a log-odds scale: blend Pythagorean win percentage with actual win percentage, regress the result towards .500 in proportion to how much season is left, then take the log-odds.
3. Load every team's remaining fixtures and deduplicate them into one list of games.
4. For each simulated season, redraw each team's rating with Gaussian noise, then play every remaining game. A home team's win probability is the logistic of the rating gap plus a home field term.
5. At the end of each simulated season, award the three division titles in each league to the best record in each division, then the three wild cards to the best remaining records, and record who got the top seed.
6. Average across all simulations to get probabilities and projected win totals.

### Sanity check

On a mid-August slate the output lands close to the well-known public playoff-odds models for the clear contenders and clear also-rans, and diverges by ten to twenty points on a handful of teams whose run differential disagrees with their record. That divergence is the point: it is where the model thinks the standings are lying.

### Input

Everything is optional. Run it with an empty input and it projects the current season.

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `iterations` | integer | 10000 | Seasons to simulate. 10000 gives probabilities stable to about half a point. |
| `season` | integer | current | Set a past year to project that season from where it stood. Useful for backtesting. |
| `regressionGames` | integer | 70 | How hard to pull a partial season towards .500. Equivalent to adding this many .500 games. |
| `pythagoreanWeight` | number | 0.6 | Share of the strength estimate that comes from run differential rather than the record. |
| `homeFieldAdvantage` | number | 0.16 | Home edge in log-odds. 0.16 is roughly a 54 percent home win rate between equals. |
| `strengthUncertainty` | number | 0.1 | Spread of the per-season talent redraw. Set to 0 to see how overconfident the model becomes. |
| `edgeThreshold` | number | 0.05 | How far the model must beat the market before a team is flagged as value. |
| `marketProbabilities` | array | `[]` | Bookmaker implied probabilities to compare against. |

Market entries look like this:

```json
[
  { "team": "New York Yankees", "market": "playoff", "impliedProbability": 0.98 },
  { "team": "Houston Astros", "market": "division", "impliedProbability": 0.45 }
]
```

Team names are matched loosely, so "yankees" and "New York Yankees" both work. `market` accepts `playoff` or `division`.

### Output

One row per team, sorted by playoff probability. Three ready-made views are included in the dataset: a playoff overview, a market edge table, and the records and run differential the model was built from.

| Field | Meaning |
| --- | --- |
| `league`, `division`, `team`, `abbreviation` | Where the team sits |
| `gamesPlayed`, `wins`, `losses`, `gamesRemaining` | Current record |
| `runsScored`, `runsAllowed`, `runDifferential` | Scoring profile |
| `actualWinPct` | Wins divided by games played |
| `pythagoreanWinPct` | Win percentage implied by run differential |
| `trueTalentWinPct` | The blended, regressed estimate the simulation actually uses |
| `projectedWins`, `projectedLosses` | Mean final record across all simulations |
| `fairDivisionProbability` | Chance of winning the division |
| `fairWildCardProbability` | Chance of taking one of the three wild cards |
| `fairPlayoffProbability` | Chance of reaching the postseason by either route |
| `fairTopSeedProbability` | Chance of the best record among the division winners in its league |
| `marketPlayoffProbability`, `marketDivisionProbability` | What you supplied, if anything |
| `playoffEdge`, `divisionEdge` | Model probability minus market probability |
| `hasValue` | True when the larger edge clears your threshold |
| `simulations` | Iterations actually run |

### Running it once a day

Playoff races move every night. Schedule the Actor to run each morning and you get a fresh table before the day's games. A full 30-team run with 10000 iterations finishes in about three seconds, so a daily schedule costs very little.

### Pricing

Pay per result. One result is one team, so a full-league run produces 30 results. There is no subscription and no minimum.

### Limitations and honest caveats

**It knows records, not rosters.** The model has no idea who is on the injured list, who was traded at the deadline, or who is starting tomorrow. A team that just lost its best two starting pitchers still looks as strong as its run differential says it is. On a fast-moving roster this is the model's biggest blind spot.

**Tiebreakers are simplified.** Real MLB tiebreakers use head-to-head records and other criteria. Ties in a simulated season are broken at random, which is fine for probabilities across thousands of runs but means no single simulated season should be read as a forecast of the actual tiebreak.

**Postseason series are not simulated.** The output stops at who qualifies and who gets the top seed. It says nothing about who wins the World Series.

**It depends on ESPN.** If ESPN changes or delays its feed, results change or the run fails. Standings are also as fresh as ESPN makes them, which is usually within hours of the last game.

**Probabilities are not predictions.** A 30 percent chance happens three times in ten. The value of this tool is comparing its number with a price, not treating it as a forecast.

**On betting.** If you use this to inform wagers, treat the output as one noisy opinion among many, and never stake money you cannot afford to lose. No model, including this one, turns a losing bet into a winning one.

# Actor input Schema

## `iterations` (type: `integer`):

How many full seasons to simulate. More iterations give smoother probabilities and cost a little more time. 10000 is plenty for stable numbers; the run takes a few seconds either way.

## `season` (type: `integer`):

Leave empty to use the current season. Set a year to project a past season from where it stood, which is useful for backtesting the model.

## `regressionGames` (type: `integer`):

How strongly a partial season is pulled towards .500. Think of it as adding this many imaginary games at a .500 record. Around 70 is the usual choice for baseball. Lower it to trust the current record more, raise it to trust it less.

## `pythagoreanWeight` (type: `number`):

How much of the strength estimate comes from runs scored and allowed (Pythagorean expectation) rather than the raw win-loss record. Run differential predicts future wins better than the record itself, so the default leans on it.

## `homeFieldAdvantage` (type: `number`):

Added to the home team's strength in every simulated game. The default of 0.16 corresponds to roughly a 54 percent win rate for the home side between evenly matched teams, which is the long-run MLB average.

## `strengthUncertainty` (type: `number`):

How unsure the model is about each team's true talent. Every simulated season draws a slightly different version of each team. Setting this to zero makes the output far too confident, because the favourite then wins almost every time.

## `edgeThreshold` (type: `number`):

A team is flagged in the hasValue column when the model probability beats the market probability you supplied by at least this much. 0.05 means five percentage points.

## `bankroll` (type: `integer`):

Your total betting capital, in dollars. It only scales the sizing columns: suggestedContracts and suggested stake grow with it. The suggestedFractionOfBankroll column is independent of this number, so it stays meaningful whatever capital you run.

## `maxPerPositionPct` (type: `number`):

Hard ceiling on any single bet, as a fraction of bankroll. 0.02 means no position ever exceeds 2% of your capital. Quarter-Kelly sizing is clipped to this ceiling, and the kellyFractionUncapped column shows what the model wanted before the clip.

## `maxTotalExposurePct` (type: `number`):

Ceiling on the sum of all open positions, as a fraction of bankroll. This divided by the per-position ceiling is how many bets actually fit: at the defaults, 6% over 2% means three. Qualifying bets beyond that are still marked VALUE but come back with zero contracts and cappedByPortfolioLimit set to true.

## `marketProbabilities` (type: `array`):

Bookmaker implied probabilities to compare against, so the output shows where the model disagrees with the market. Each entry needs a team name, a market of either playoff or division, and a probability between 0 and 1. Example: \[{ "team": "New York Yankees", "market": "playoff", "impliedProbability": 0.78 }]

## Actor input object example

```json
{
  "iterations": 10000,
  "regressionGames": 70,
  "pythagoreanWeight": 0.6,
  "homeFieldAdvantage": 0.16,
  "strengthUncertainty": 0.1,
  "edgeThreshold": 0.05,
  "bankroll": 1000,
  "maxPerPositionPct": 0.02,
  "maxTotalExposurePct": 0.06,
  "marketProbabilities": []
}
```

# Actor output Schema

## `projections` (type: `string`):

Full projection table. Switch between the Playoff overview, Market edge and Records views for a narrower set of columns.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("commodus67/mlb-playoff-odds-monte-carlo").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("commodus67/mlb-playoff-odds-monte-carlo").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call commodus67/mlb-playoff-odds-monte-carlo --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,commodus67/mlb-playoff-odds-monte-carlo"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/YS2iYRcccbwctL5QQ/builds/g2oNrhz5zuiFbDFSK/openapi.json
