What Is This?

This is an automated scanner that identifies potentially mispriced daily high temperature contracts on Kalshi, a CFTC-regulated prediction market exchange. It compares probability estimates derived from National Weather Service forecasts against live Kalshi market prices to find contracts where the market may disagree with the NWS forecast.

How Kalshi Weather Markets Work

Kalshi offers daily high temperature markets for major US cities. Each market asks a question like "Highest temperature in NYC today?" and offers multiple brackets (e.g., ≤48°F, 49-50°F, 51-52°F, ≥57°F). Each bracket is a YES/NO contract that settles at $1.00 if the actual NWS-reported high falls in that range, or $0.00 if it does not.

Prices range from 1¢ to 99¢ and represent the market's implied probability. Settlement is based on the official NWS Daily Climate Report from that city's designated weather station.

Cities Tracked

The scanner monitors all active Kalshi weather markets. Each city settles on a specific NWS weather station:

CityNWS StationKalshi Series

The Probability Model

Core Concept

We model the actual daily high temperature as a normally distributed random variable centered on the NWS forecast high. The standard deviation is calibrated using historical forecast error data for each station. We then calculate the probability that the temperature falls within each Kalshi bracket by integrating the normal CDF across the bracket bounds.

Step 1: Forecast Mean

mean = NWS_forecast_high + bias_correction + trend_adjustment

Step 2: Standard Deviation (Sigma)

Sigma represents how uncertain the model is. A larger sigma means wider probability spreads and fewer confident signals. The final sigma is computed from four stacked multipliers:

sigma = base_sigma × confidence × stability × time_decay

Base Sigma (Auto-Calibrated)

Instead of hardcoded values, the base sigma is learned from data:

The CLI and dashboard show [cal] when calibrated values are active, or [static] when using defaults.

Confidence Multiplier

ConfidenceMultiplierWhen Used
High×0.45Day-of forecast, stable weather pattern
Medium×0.702-3 days out, some uncertainty
Low×1.04+ days out or unstable pattern

Weather Pattern Stability (×0.70 to ×1.50)

The scanner analyzes three real-time data sources to assess how stable the weather pattern is for each city:

SourceWhat It MeasuresRange
Hourly Forecast Volatility24-hour temperature range and max hour-to-hour swings from the NWS hourly forecast×0.85 to ×1.30
Active Weather AlertsNWS watches, warnings, and advisories for the city's coordinates, scored by severity×1.0 to ×1.50
Forecast Discussion (AFD)NWS meteorologist narrative analyzed for instability keywords ("cold front", "storm", "uncertainty") vs stability keywords ("high pressure", "ridge", "calm")×0.85 to ×1.25

These three factors multiply together and are clamped to a 0.70–1.50 range. The dashboard shows: 🟢 very stable, 🟡 normal, or 🔴 volatile for each city.

Time-of-Day Decay (×0.40 to ×1.0)

As the day progresses and more observations come in, the model becomes more certain about the outcome. Sigma shrinks accordingly:

HIGH MarketsDecayReasoning
Before 8am ET×1.0Full uncertainty — high hasn't been reached
8-11am ET×0.92Morning — temps climbing
11am-2pm ET, near forecast×0.70Observed max approaching forecast
After 2pm, max ≈ forecast×0.50Nearly certain — high is set
After 2pm, max > forecast×0.40Already exceeded — outcome clear
LOW MarketsDecayReasoning
Temps falling, low ahead×1.0Full uncertainty — overnight low hasn't occurred
Rising, min ≈ forecast×0.45Morning low locked in, matches forecast
Rising, min < forecast×0.50Already went below forecast — outcome locked
Midday, stable×0.65Morning low is probably set

This means the later scans (11am, 1pm ET) produce tighter, more confident signals than the early morning scan. This is by design — later scans have more data.

Step 3: Bracket Probabilities

For each Kalshi bracket [low, high], the probability is calculated using the normal CDF:

P(bracket) = Φ(high + 0.5, mean, σ) − Φ(low − 0.5, mean, σ)

The ±0.5 adjustment accounts for integer temperature reporting. For open-ended brackets (≤X or ≥X), only one CDF bound is used.

Step 4: Edge Detection + Liquidity Filter

edge = (model_probability − market_price) / market_price
expected_value = model_prob × (1 − market_price) − (1 − model_prob) × market_price

A signal is generated when all of the following are met:

The liquidity filter is critical — without it, the model would generate phantom signals on contracts that look mispriced but have no real market to trade against.

Step 5: Position Sizing (Quarter-Kelly)

kelly_fraction = max(0, (p × b − (1 − p)) / b) × 0.25
position_size = min(kelly_fraction × daily_budget, max_per_trade)

Where p = model probability, b = payout odds = (1 − price) / price. The 0.25 multiplier is a conservative quarter-Kelly to reduce volatility. Positions are sorted by expected value and allocated until the $100 daily budget is exhausted.

Trading Parameters

ParameterValue
Daily Budget$100
Max Per Trade$20
Min Edge Threshold15%
Min Expected Value3¢ per contract
Kelly FractionQuarter-Kelly (0.25)
YES Price Floor≥15¢ (NO stays at ≥3¢)
Min Bid Required2¢ (ensures exit liquidity)
Max Bid/Ask Spread10¢
Min Volume100 contracts
Portfolio Split70% NO / 30% YES
MAE SuppressionSignals disabled when MAE > 5°F

Understanding MAE (Mean Absolute Error)

MAE measures the average of how far off our forecast is from what actually happened, ignoring direction. If we forecast 52°F and the actual was 55°F, the error is 3°F. If the next day we forecast 60°F and actual was 57°F, the error is also 3°F. The MAE across both days is 3°F.

Why MAE Matters for Bracket Markets

Kalshi brackets are typically 2°F wide (e.g., 52-53°F). The model needs to estimate which bracket the temperature will land in. If our MAE is 2°F, we're usually within one bracket of being right — the probability estimates are trustworthy. If our MAE is 10°F, we're off by 5 brackets on average — the model's probabilities are essentially guesses.

MAE RangeQualityAction
< 3°FStrongTrade with confidence — model is within one bracket
3-5°FModerateTrade selectively — signals have some uncertainty
> 5°FToo inaccurateSignals automatically suppressed

Automatic Signal Suppression

The scanner tracks a rolling MAE for each city and market type (HIGH/LOW) based on the last 14 days of settlement data. When a city's MAE exceeds 5°F with at least 5 settlement samples, signal generation for that market is automatically suppressed.

What this means in practice:

This prevents the model from recommending trades in markets where it doesn't yet have enough accuracy to be profitable. As the calibration system learns from more settlements, the MAE naturally decreases and markets re-enable themselves.

Current suppression status is visible on the Roadmap page or via the CLI: node cli.js --calibration. The /api/calibration endpoint also returns the suppressed flag and rollingMAE for each city.

Auto-Calibration

The model improves itself automatically over time through three mechanisms:

On first setup, a 90-day historical backtest seeds the calibration using data from NOAA's RCC-ACIS climate database. This gives the model a strong baseline immediately rather than waiting weeks for settlements to accumulate. Run node cli.js --backtest to re-run the backtest at any time.

View the current calibration state with node cli.js --calibration or via the API at /api/calibration.

Data Sources

National Weather Service API

The NWS API is the sole authoritative source for settlement data on Kalshi weather markets. We use five endpoints per city:

Additionally, the Area Forecast Discussion (AFD) from each NWS office is analyzed for weather pattern keywords.

The NWS API is free, requires no API key, and is rate-limited. We add 300ms delays between requests and implement retry logic with exponential backoff.

NOAA RCC-ACIS (Historical Data)

For the 90-day backtest, we use the Regional Climate Centers' Applied Climate Information System. This provides daily max/min temperature records for each station going back years. No API key required.

Kalshi Public API

Market prices, bracket definitions, and order book data are fetched from Kalshi's public API. No authentication is required for reading market data. Prices are parsed from live bid/ask spreads and last-traded prices.

Polymarket Cross-Reference

In addition to Kalshi, Polymarket offers daily high temperature markets for 36+ cities worldwide, including all 17 US cities we track on Kalshi. The scanner pulls Polymarket prices on every scan and computes a consensus score for each city.

How Consensus Works

For each signal the model generates, we check whether Polymarket's pricing agrees or disagrees:

The consensus score ranges from -1 (strong disagreement) to +1 (strong consensus):

ScoreBadgeMeaning
> 0.5PM ✓Polymarket agrees with our signals — higher confidence
0 to 0.5PM ~Mixed agreement — some signals confirmed, some not
< 0PM ✗Polymarket disagrees — the mispricing may be on our end, not Kalshi's

Why This Matters

A single market can be wrong. But when two independent markets (Kalshi and Polymarket) agree with our model that a bracket is mispriced, the probability of real edge increases significantly. Conversely, if Polymarket's price is close to Kalshi's and far from our model, it suggests our model may be the one that's wrong — two markets independently arrived at a similar price.

Polymarket also offers 11 brackets per city (vs Kalshi's 6), providing finer resolution on market expectations. The scanner matches brackets with overlapping temperature ranges for comparison.

Quick-Reference Trading Guide

Reading the Dashboard Indicators

IndicatorWhat It MeansTrade with confidence when...
Signal (BUY NO / BUY YES)Model thinks Kalshi mispriced this bracketEdge > 20% and EV > 5¢
Bid/AskWhat you'd actually pay. Bid = sell price, Ask = buy priceSpread ≤ 5¢ (tight = liquid market)
PM ✓Polymarket prices confirm the mispricingTwo independent markets agree against Kalshi
PM ✗Polymarket disagrees — maybe Kalshi is rightBe cautious, consider skipping
σ [cal]Sigma computed from real settlement data, not static guessesGreen [cal] = more trustworthy model
Ensemble spreadHow much the 6 weather models disagreeLow spread (< 5°) = models agree = more certain forecast
decayHow settled the temperature is based on time of day and observationsStrong decay (×0.4-0.5) = outcome nearly locked in
MAE (via calibration)How accurate our forecast has been for this city historicallyBelow 3° = strong accuracy

Strongest Trades

Your highest-confidence trades are when multiple indicators align:

When to Skip

Recommended Workflow


How Settlement Works

Each morning, the scanner fetches the previous day's actual high temperature from NWS observation data for each station. It then:

Historical performance, win rates, and cumulative P&L are tracked on the History page.

Real Example: Reading the Dashboard

Let's walk through an actual scan to show how to interpret everything the scanner produces. We'll look at Miami HIGH from a recent run, then explain the portfolio allocation.

Step-by-Step: Miami High Temperature

Here's what the scanner showed for Miami's daily high temperature market:

Miami (MIA) HIGH

Forecast High: 83°F  |  Model Mean: 83.0°F  |  Sigma: ±1.7°F

Bracket Model % Market Bid/Ask Signal
≥86°7.2%-/10¢-
84-85°31.3%-/47¢BUY NO
82-83°42.5%-/39¢-
80-81°17.0%-/3¢BUY YES
78-79°2.0%-/5¢-
≤77°0.1%-/1¢-

How to Read Each Column

Why BUY NO on 84-85°?

The market is asking 47¢ for a YES contract on 84-85°, implying a 47% chance. But our model estimates only a 31.3% chance. The market is overpricing this bracket — it thinks 84-85° is more likely than our forecast supports.

When a bracket is overpriced, we buy the NO side. A NO contract costs 100¢ − 47¢ = 53¢ (approximately). If the high does NOT land in 84-85° (which our model says is a ~69% chance), we collect $1.00, profiting ~47¢ per contract.

Why BUY YES on 80-81°?

The market is asking only 3¢ for a YES contract on 80-81°, implying just a 3% chance. But our model estimates a 17% chance — more than 5x what the market thinks. This bracket is significantly underpriced.

At 3¢ per contract, you risk 3¢ to potentially win $1.00. With a 17% model probability, the expected value is strongly positive. Even though this bracket is less likely than 82-83°, the price is so cheap relative to the probability that it's a good bet.

Why No Signal on 82-83°?

The model says 82-83° has a 42.5% chance, and the market is asking 39¢. That's close enough — the market and our model roughly agree. The edge (42.5% vs 39%) doesn't clear our 15% minimum threshold, so no signal is generated. Not every bracket needs a trade — we only act when the market is significantly mispriced.


Reading the Portfolio Allocation

After scanning all cities, the scanner combines every signal into a single portfolio. Here's how to read it:

SIGNALS FOUND

31

TOTAL COST

$90.82

EXPECTED RETURN

$632.21

POSITIONS

10

Ticker Bracket Side Contracts Cost Exp. Profit
KXHIGHNY-26MAR24-B48.5High 48-49NO22$12.76$4.80
KXHIGHMIA-26MAR24-B84.5High 84-85NO14$7.56$2.06
KXHIGHMIA-26MAR24-B80.5High 80-81YES120$3.60$16.80
KXLOWTMIA-26MAR24-T63Low ≥64YES131$14.41$67.47
KXLOWTPHIL-26MAR24-T33Low ≥34YES83$19.09$49.22

(Showing 5 of 10 positions for brevity)

Understanding Each Column

Why So Many Contracts for Miami 80-81° (120) vs 84-85° (14)?

Position size is determined by two things: the edge (how mispriced the contract is) and the price (how cheap each contract is).

What Does "$90.82 Total Cost" Mean?

This is the total amount of money you'd need to place all 10 positions. The scanner targets a $100 daily budget and allocates by expected value — the best opportunities get funded first, and it stops when the budget is used up. You don't have to take every position — you can cherry-pick the ones you're most confident in.

About Expected Profit

The expected profit numbers assume the model's probability estimates are correct. In reality:

Tips for Placing Trades on Kalshi


Schedule

Time (PT / ET)Action
6:45 AM PT / 9:45 AM ETFirst scan — early morning forecast, initial market prices
8:00 AM PT / 11:00 AM ETSecond scan — updated forecast, more market liquidity
10:00 AM PT / 1:00 PM ETThird scan — midday update, strongest observation data
7:00 AM PT / 10:00 AM ETSettlement check — score yesterday's signals as wins/losses

You can also trigger a scan at any time using the "Run Scan" button on the dashboard. Each run is saved separately so you can compare how prices changed throughout the day.

FAQ

Is this financial advice?

No. This is an experimental quantitative model for educational and research purposes. The signals generated are based on statistical analysis of NWS forecasts and should not be considered financial advice. All prediction market trading involves risk of loss. Past performance does not guarantee future results.

Why does the model sometimes show large edges?

Large apparent edges can occur when: (1) markets are thinly traded with wide bid/ask spreads, (2) the scan runs during off-hours when prices are stale, or (3) the model's normal distribution assumption doesn't capture weather pattern nuances like frontal passages or storm systems. The portfolio builder filters out contracts priced at ≤2¢ or ≥98¢ to avoid illusory edges on near-certain outcomes.

Why use the NWS forecast instead of other weather sources?

Kalshi settles exclusively on NWS Daily Climate Reports. While AccuWeather, Weather.com, or other sources may sometimes be more accurate, the NWS report is the only source that determines contract outcomes. Using the same data source for prediction and settlement eliminates basis risk.

What is Quarter-Kelly sizing?

The Kelly Criterion is a formula for optimal bet sizing that maximizes long-term growth rate. Full Kelly is mathematically optimal but extremely volatile. Quarter-Kelly (betting 25% of the Kelly-recommended amount) sacrifices some growth rate for significantly lower variance and drawdown risk. It's a common approach in professional trading and gambling.

How accurate is the normal distribution assumption?

NWS forecast errors are approximately normally distributed for day-of and next-day forecasts, which is the primary use case. The model is less reliable for multi-day forecasts or during extreme weather events (cold fronts, heat waves) where the error distribution may be skewed. The historical sigma values are calibrated per-city to account for local climate variability.

What does the scanner NOT account for?

The current model does not account for: (1) sudden forecast revisions or weather advisories, (2) microclimate effects at specific stations, (3) instrument measurement biases, (4) order book depth and liquidity constraints, or (5) transaction costs and fees. These are areas for future improvement.

When is the best time to trade?

The scanner runs at 9:45 AM ET, shortly before Kalshi weather markets typically see the most trading activity. Day-of forecasts have the highest confidence and lowest sigma, making edge detection most reliable. Signals generated from overnight or multi-day forecasts carry more uncertainty.

How can I track model performance?

Visit the History page to see forecast accuracy (mean absolute error, bias), signal win rate, and cumulative P&L over time. Click any settlement row with signals to see the per-signal WIN/LOSS breakdown. You can also filter by city to analyze performance for specific markets.