{
  "openapi": "3.0.1",
  "info": {
    "title": "NHL Playoff Odds API — Monte Carlo Simulator & Value Bets",
    "description": "Replays every remaining NHL game thousands of times for playoff, division, wild card and Presidents' Trophy probabilities for all 32 teams, then prices them against live Kalshi contracts with fee-adjusted EV and Kelly sizing.",
    "version": "0.0",
    "x-build-id": "hJHWEl1ZgDWECJcxS"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/commodus67~nhl-playoff-odds-monte-carlo/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-commodus67-nhl-playoff-odds-monte-carlo",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/commodus67~nhl-playoff-odds-monte-carlo/runs": {
      "post": {
        "operationId": "runs-sync-commodus67-nhl-playoff-odds-monte-carlo",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/commodus67~nhl-playoff-odds-monte-carlo/run-sync": {
      "post": {
        "operationId": "run-sync-commodus67-nhl-playoff-odds-monte-carlo",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "iterations": {
            "title": "Monte Carlo iterations",
            "type": "integer",
            "description": "How many full seasons to simulate. More iterations give smoother probabilities and cost a little more time. 20000 is plenty for stable numbers; the run takes a few seconds either way.",
            "default": 20000
          },
          "season": {
            "title": "Season year (optional)",
            "type": "integer",
            "description": "An NHL season is named by the year it ends, so the 2026-27 season is 2027. Leave empty to use the current season. Set a year to project a past season from where it stood, which is useful for backtesting."
          },
          "archiveToNamedDataset": {
            "title": "Archive to a named dataset (optional)",
            "type": "string",
            "description": "Leave empty for a normal run. If you put a name here, every run also appends its results to a named dataset in your account, which Apify keeps indefinitely instead of deleting after a few days. Run it on a schedule and you build your own history of how the probabilities moved across the season, which is something you cannot reconstruct later. Use the same name every time, for example nhl-playoff-odds-history."
          },
          "regressionGames": {
            "title": "Regression to the mean, in games",
            "type": "integer",
            "description": "How strongly a partial season is pulled towards the team's baseline. Think of it as adding this many imaginary average games. Hockey is a high-variance sport over a long season, so the default of 25 is deliberately larger than the NFL setting and smaller than baseball's. Lower it to trust the current record more, raise it to trust it less.",
            "default": 25
          },
          "pythagoreanWeight": {
            "title": "Weight on goal differential",
            "type": "number",
            "description": "How much of the strength estimate comes from goals scored and allowed rather than the raw record. Goal differential predicts future results better than the record itself, which in hockey is badly distorted by shootouts and the loser point, so the default leans on it.",
            "default": 0.6
          },
          "homeIceAdvantage": {
            "title": "Home ice advantage (log-odds)",
            "type": "number",
            "description": "Added to the home team's strength in every simulated game. The default of 0.12 corresponds to roughly a 53 percent win rate for the home side between evenly matched teams, in line with recent NHL seasons. Home ice is worth noticeably less in hockey than in basketball or football. Set to 0 to ignore venue.",
            "default": 0.12
          },
          "strengthUncertainty": {
            "title": "Strength uncertainty",
            "type": "number",
            "description": "How unsure the model is about each team's true talent. Every simulated season draws a slightly different version of each team. Setting it to zero makes the output far too confident.",
            "default": 0.1
          },
          "overtimeProbability": {
            "title": "Probability a game goes past regulation",
            "type": "number",
            "description": "Chance that any single simulated game is still tied after sixty minutes and goes to overtime or a shootout. Those games award three points instead of two: two to the winner and one to the loser. Around 23 percent of NHL games end this way, which is where the default comes from, and ignoring it would understate every team's points total by about nine.",
            "default": 0.23
          },
          "overtimeDamping": {
            "title": "How much talent matters in overtime",
            "type": "number",
            "description": "Three-on-three overtime and the shootout are far closer to a coin flip than sixty minutes of hockey are. This shrinks the strength gap in those games: 0 makes overtime a pure coin flip, 1 treats it exactly like regulation. The default of 0.5 halves the edge a stronger team carries into the extra period.",
            "default": 0.5
          },
          "priorCarryover": {
            "title": "How much of last season carries over",
            "type": "number",
            "description": "Before the season starts, every team's record is 0-0-0 and a model with no memory would return the same number for all 32 clubs. Last season's goal differential is shrunk towards average by this amount and used as the starting point. 0.6 keeps 60 percent of last season's separation. Set to 0 to start every team dead level.",
            "default": 0.6
          },
          "minGamesPlayedForValue": {
            "title": "Games needed before a VALUE call",
            "type": "integer",
            "description": "In September this model knows one thing: how each club finished last season. It does not know who was traded, who got hurt or who signed in July, and the market knows all of it. Until the league has played this many games per team, edges are still reported in full but no row is allowed to claim VALUE. Set to 0 to override that, deliberately.",
            "default": 10
          },
          "includeDivisionMarkets": {
            "title": "Also price the four division winner markets",
            "type": "boolean",
            "description": "Adds Kalshi's Atlantic, Metropolitan, Central and Pacific division winner races alongside the qualification market. These are mutually exclusive races, so the overround is stripped out before the comparison. They are usually much thinner than the qualification market, so they are reported as a probability and an edge and never sized.",
            "default": true
          },
          "edgeThreshold": {
            "title": "Minimum edge to flag value",
            "type": "number",
            "description": "A team is flagged when the model probability beats the de-vigged market probability by at least this much. 0.05 means five percentage points.",
            "default": 0.05
          },
          "bankroll": {
            "title": "Bankroll for position sizing",
            "type": "integer",
            "description": "Your total capital, in dollars. It only scales the suggestedStake column. The suggestedFractionOfBankroll column is independent of this number, so it stays meaningful whatever capital you run.",
            "default": 1000
          },
          "maxPerPositionPct": {
            "title": "Max stake per position",
            "type": "number",
            "description": "Hard ceiling on any single position, 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.",
            "default": 0.02
          },
          "maxTotalExposurePct": {
            "title": "Max total portfolio exposure",
            "type": "number",
            "description": "Ceiling on the sum of all open positions, as a fraction of bankroll. This divided by the per-position ceiling is how many positions actually fit: at the defaults, 6% over 2% means three. Qualifying rows beyond that keep their verdict but come back trimmed, with cappedByPortfolioLimit set to true.",
            "default": 0.06
          },
          "marketProbabilities": {
            "title": "Market probabilities (optional override)",
            "type": "array",
            "description": "Supply your own prices instead of reading Kalshi. Each entry needs a team name, a market of either playoff or division, and a probability between 0 and 1. Example: [{ \"team\": \"Colorado Avalanche\", \"market\": \"playoff\", \"impliedProbability\": 0.9 }]. When this is filled in, no Kalshi call is made, no de-vig is applied and no stake is sized.",
            "default": []
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}