{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# What does the inflation market price beyond the last release?\n", "Compare quoted CPI threshold contracts with the latest calculated year-on-year inflation from BLS. Preserve each bid and ask.\n", "Nearest returned open event: November 2026 CPI, closing 2026-12-10. BLS baseline: September 2026, calculated from unadjusted indexes. The rules use the twelve-month CPI increase rounded to one decimal. Quotes show a percentage of the $1 payout, not a calibrated probability distribution." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\"\"\"Compare quoted CPI threshold contracts with the latest calculated year-on-year inflation from BLS. Preserve each bid and ask.\n", "\n", "Research example. Live observations can change.\n", "\"\"\"\n", "\n", "from datetime import UTC, datetime\n", "from decimal import Decimal\n", "from textwrap import fill\n", "\n", "import matplotlib.pyplot as plt\n", "import pandas as pd\n", "\n", "AS_OF = datetime.now(UTC).date()\n", "NAVY, BLUE, PINK = \"#132244\", \"#8fb9ff\", \"#e7a2cc\"\n", "\n", "\n", "def fetch(qj):\n", " return {\n", " \"kalshi_cpi\": qj.domains.call(\n", " \"predictions.markets.search_markets\", provider=\"kalshi\", series_ticker=\"KXCPIYOY\", status=\"open\", limit=1000\n", " ),\n", " \"bls_latest\": qj.domains.call(\n", " \"macro.us.get_cpi\",\n", " provider=\"bls\",\n", " category=\"all\",\n", " seasonally_adjusted=False,\n", " start_year=AS_OF.year - 1,\n", " end_year=AS_OF.year,\n", " ),\n", " }\n", "\n", "\n", "def analyze(raw):\n", " market_page = payload(raw[\"kalshi_cpi\"])\n", " if market_page.get(\"cursor\"):\n", " raise ValueError(\"Paginate the market search before comparing event thresholds\")\n", " markets = frame(market_page[\"markets\"], \"close_time\")\n", " eligible = markets.loc[(markets[\"close_time\"].dt.date > AS_OF) & (markets[\"strike_type\"] == \"greater\")].copy()\n", " if eligible.empty:\n", " raise ValueError(\"No open future CPI threshold contracts\")\n", " event = eligible.sort_values(\"close_time\").iloc[0][\"event_ticker\"]\n", " cohort = eligible.loc[eligible[\"event_ticker\"] == event]\n", " rows = []\n", " for contract in cohort.to_dict(\"records\"):\n", " bid = decimal(contract[\"yes_bid_dollars\"])\n", " ask = decimal(contract[\"yes_ask_dollars\"])\n", " payout = decimal(contract[\"notional_value_dollars\"])\n", " if (\n", " payout <= 0\n", " or not Decimal(0) <= bid <= ask <= payout\n", " or decimal(contract[\"yes_bid_size_fp\"]) <= 0\n", " or decimal(contract[\"yes_ask_size_fp\"]) <= 0\n", " ):\n", " continue\n", " rows.append(\n", " {\n", " \"Threshold %\": float(contract[\"floor_strike\"]),\n", " \"Yes bid / payout %\": 100 * ratio(bid, payout),\n", " \"Yes ask / payout %\": 100 * ratio(ask, payout),\n", " \"Event\": event,\n", " \"Quote updated\": contract[\"updated_time\"],\n", " }\n", " )\n", " result = pd.DataFrame(rows).sort_values(\"Threshold %\") if rows else pd.DataFrame()\n", " if result.empty:\n", " raise ValueError(\"No valid two-sided CPI quotes with displayed size\")\n", " cpi = bls_cpi(raw[\"bls_latest\"]).resample(pd.offsets.MonthEnd()).last()\n", " # Reindex the calendar so a missing CPI month cannot shift the annual comparison.\n", " annual = (100 * (cpi / cpi.shift(12) - 1)).dropna()\n", " if annual.empty:\n", " raise ValueError(\"No exact prior-year CPI observation for a current baseline\")\n", " baseline = annual.iloc[-1]\n", " fig, (top, bottom) = canvas(\"Inflation \u00b7 reported data and market quotes\", \"Calculated CPI YoY %\", panels=2)\n", " observation_months = pd.DatetimeIndex([day.replace(day=1) for day in annual.index])\n", " top.plot(observation_months, annual, marker=\"o\", color=BLUE)\n", " top.set_xlabel(\"BLS observation month \u00b7 unadjusted CPI\")\n", " bottom.plot(result[\"Threshold %\"], result[\"Yes bid / payout %\"], marker=\".\", label=\"Yes bid\")\n", " bottom.plot(result[\"Threshold %\"], result[\"Yes ask / payout %\"], marker=\".\", label=\"Yes ask\")\n", " bottom.fill_between(\n", " result[\"Threshold %\"], result[\"Yes bid / payout %\"], result[\"Yes ask / payout %\"], alpha=0.14, color=BLUE\n", " )\n", " bottom.axvline(baseline, color=\"#afc1dc\", linestyle=\"--\", label=f\"BLS {annual.index[-1]:%Y-%m} \u00b7 {baseline:.2f}%\")\n", " bottom.set_xlabel(\"Annual CPI threshold % \u00b7 \" + event)\n", " bottom.set_ylabel(\"Quote / $1 payout %\")\n", " bottom.set_ylim(0, 100)\n", " legend(bottom)\n", " result[\"Latest calculated CPI YoY %\"] = baseline\n", " return result, fig\n", "\n", "\n", "def payload(response):\n", " if hasattr(response, \"to_dict\"):\n", " response = response.to_dict()\n", " while isinstance(response, dict):\n", " if response.get(\"error\") or response.get(\"error_code\") or response.get(\"success\") is False:\n", " raise ValueError(\"Provider request failed\")\n", " if \"data\" in response:\n", " response = response[\"data\"]\n", " elif \"value\" in response:\n", " response = response[\"value\"]\n", " else:\n", " break\n", " return response\n", "\n", "\n", "def frame(response, date=\"date\"):\n", " rows = payload(response)\n", " if not isinstance(rows, list) or not rows:\n", " raise ValueError(\"No provider observations returned\")\n", " result = pd.DataFrame(rows)\n", " if date:\n", " result[date] = pd.to_datetime(result[date], utc=True)\n", " result = result.sort_values(date)\n", " return result\n", "\n", "\n", "def decimal(value):\n", " if pd.isna(value):\n", " raise ValueError(\"Required monetary value or quantity is missing\")\n", " return Decimal(str(value))\n", "\n", "\n", "def ratio(numerator, denominator):\n", " denominator = decimal(denominator)\n", " if denominator == 0:\n", " raise ValueError(\"Required denominator is zero\")\n", " return float(decimal(numerator) / denominator)\n", "\n", "\n", "def macro(response, column):\n", " values = frame(response).set_index(\"date\")[column]\n", " values = pd.to_numeric(values, errors=\"coerce\").dropna()\n", " if values.empty:\n", " raise ValueError(f\"No numeric observations for {column}\")\n", " return values\n", "\n", "\n", "def canvas(title, ylabel, panels=1):\n", " plt.rcParams.update(\n", " {\n", " \"figure.facecolor\": NAVY,\n", " \"axes.facecolor\": NAVY,\n", " \"savefig.facecolor\": NAVY,\n", " \"text.color\": \"#e9eef8\",\n", " \"axes.labelcolor\": \"#afc1dc\",\n", " \"xtick.color\": \"#afc1dc\",\n", " \"ytick.color\": \"#afc1dc\",\n", " \"axes.edgecolor\": \"#34486b\",\n", " \"grid.color\": \"#34486b\",\n", " \"font.size\": 10,\n", " \"axes.prop_cycle\": plt.cycler(color=[BLUE, PINK, \"#ba9dea\"]),\n", " }\n", " )\n", " fig, axes = plt.subplots(panels, 1, figsize=(9, 6.1), squeeze=False, layout=\"constrained\")\n", " fig.suptitle(fill(title, width=58), fontsize=17, x=0.04, ha=\"left\", weight=\"medium\")\n", " for ax in axes.flat:\n", " ax.grid(axis=\"y\", alpha=0.45)\n", " ax.set_axisbelow(True)\n", " ax.spines[[\"top\", \"right\"]].set_visible(False)\n", " axes.flat[0].set_ylabel(ylabel)\n", " return fig, list(axes.flat)\n", "\n", "\n", "def legend(ax):\n", " ax.legend(frameon=False, labelcolor=\"#e9eef8\", fontsize=9)\n", "\n", "\n", "def run():\n", " from quantjourney.sdk import QuantJourneyAPI\n", "\n", " qj = QuantJourneyAPI.from_env()\n", " raw = fetch(qj)\n", " table, fig = analyze(raw)\n", " print(table.to_string(index=False))\n", " fig.savefig(OUTPUT, dpi=180)\n", " plt.show()\n", "\n", "\n", "def bls_cpi(response):\n", " return macro(response, \"cpi_all\")\n", "\n", "\n", "OUTPUT = \"sdk_inflation_market.png\"\n", "\n", "if __name__ == \"__main__\":\n", " run()\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }