balance
 
net pnl
 
wins
 
losses
 
open trades
 
total trades
 
🌍 live city map
open trades
in trading window
outside window
coldhot
cumulative pnl
Cumulative profit/loss chart
recent trades
🌑
Loading…
wallet balance
β€”
no live strategy configured
β—‰
No live strategy yet. Promote one from Training to get started.
⬑
Loading…
🌀
Loading…
πŸ—Ί
Loading…
forecast error distribution
Forecast accuracy chart
recent forecasts vs actual
CityDatePredictedActualErrorStd devMembers
β—‡
Loading…
IDStartedModeCitiesSkippedMarketsTradesSpentDuration
⟳
Loading…
general
logLevel verbosity of bot logs
time guard
minCityLocalHour skip trading each city before this local hour (0–23); 0 = no minimum
maxCityLocalHour stop trading each city after this local hour (0–23)
maxCurrentDataStalenessMinutes skip today's markets if current conditions data is older than this many minutes
YES trades β€” edge detection
minEdgeMultiplier min forecast Γ· price ratio to enter (e.g. 1.15 = 15% edge)
minYesEntryPriceCents skip YES tokens below this price β€” 0–9Β’ bets almost never win (Β’)
maxEntryPriceCents skip YES tokens already above this price (Β’)
minForecastProbability minimum forecast probability to act on (0–1)
maxYesSigmasFromMean don't bet YES on temps more than this many Οƒ from ensemble mean
YES trades
enableYesTrades allow YES entries in edge mode (disable for NO-only strategies)
NO trades
enableNoTrades allow betting on the NO side of markets
minNoTempDistance min Β°C distance from ensemble mean (hard floor)
minNoSigmas min Οƒ from mean to consider a NO bet
minNoEdgeMultiplier min forecast Γ· price ratio for NO entry
minNoYesPriceCents YES must be at least this price before buying NO (Β’)
minNoEntryPriceCents skip NO tokens below this price β€” YES too expensive to fade (Β’)
maxNoEntryPriceCents skip NO tokens above this price β€” tight markets with clustered losses (Β’)
position sizing
positionSizeUsd base stake at minimum forecast probability ($)
maxPositionSizeUsd cap on a single position ($)
adjacentTemps extra temperature brackets to consider per city
spreadMode which buckets spread strategies buy: full = all above minForecastProbability; neighbours = peak Β± maxBucketDistance; hybrid = peak Β± min(ensemble spread, maxBucketDistance)
maxSpendPerCityUsd spend cap per city per bot run ($)
maxSpendPerRunUsd total spend cap per bot run ($)
stop-loss
stopLossPct exit if Gamma price drops to this fraction of entry (0 = disabled)
takeProfitPct exit (or activate trailing) when price rises this fraction above entry (0 = disabled)
trailingStopPct sell if price drops this fraction below peak after takeProfitPct is reached (0 = fixed exit)
trailingStopCents sell if price drops this many cents below peak; activates only once floor exceeds entry (overrides trailingStopPct)
trailingActivationProfitUsd arm the profit-floor trail once unrealized profit reaches this many dollars (0 = disabled)
trailingFloorProfitUsd once armed, sell the instant profit drops below this many dollars; fixed floor, doesn't trail up with peak (overrides trailingStopCents/trailingStopPct)
lock-in
lockinMinHour earliest city-local hour for lock-in entries
lockinHardHour from this local hour the temperature-decline check is skipped
lockinMaxYesEntryCents never buy the locked YES bucket above this price
lockinMarginC required Β°C clearance above the bucket edge (rounding safety)
lockinMinNetYieldPct minimum net yield after fees to enter (fraction)
lockinMaxObsAgeMinutes skip a city if its newest METAR is older than this
lockinTempExit sell YES the moment the station max passes the bucket (salvage busted lock-ins)
lockinStakePct wallet-scaled stake: fraction of balance per trade (0 = flat sizing)
lockinStakeFloorUsd minimum wallet-scaled stake ($)
lockinStakeCapUsd absolute stake backstop ($) β€” book-depth cap does the fine-grained work
lockinCityCapMultiple city budget = stake Γ— this (wallet-scaled strategies)
depthClampFrac clamp every paper stake to this fraction of live ask-book depth (all strategies; 0 = off)
depthClampBandCents depth = ask value within this many cents of the best ask
dashboard
win/loss sound play a sound when a trade settles β€” off by default
training toasts show win/loss notifications for paper strategy runs too
API Tokens
Tokens grant full API access. Use the x-dashboard-token header.
the shared foundation

The bot has one informational edge: an ensemble weather forecast from Open-Meteo β€” dozens of model variants aggregated into a probability distribution over tomorrow's maximum temperature. Not a single guess, but something like "26Β°C: 15%, 27Β°C: 32%, 28Β°C: 38%, 29Β°C: 12%…".

Polymarket lists binary YES/NO markets for each degree bracket. A YES token trading at 38Β’ implies the crowd thinks that temperature has a 38% chance. If our forecast says 52%, there's a gap. The question is whether that gap is real, consistent, and exploitable.

Two fundamentally different theses are being tested: Edge (actively hunt for specific mispricings) and Spread (buy the whole distribution and let calibration do the work). A third approach, Scalp, is a timing variant layered on top of Spread β€” same entry logic, different exit.

edge Edge exploit gaps between forecast and market price
the thesis

The prediction market crowd β€” largely individual bettors without access to multi-model ensemble forecasts β€” tends to misprice temperatures in the 10–40Β’ range. The ensemble is especially accurate for near-mean temperatures, which is also where the market's implied probability is most often off. The bet: systematically buying YES tokens where our forecast probability exceeds the market price by at least 5%, restricted to within Β±2Β°C of the ensemble mean, produces positive expectation.

The metric is the edge multiplier = forecastProb Γ· marketPrice. A 20Β’ YES token where we forecast 25% probability gives 1.25Γ—. We only trade at 1.05Γ— or higher. Everything else is filtered out.

the proximity rule β€” the key empirical finding

Not all positive-edge trades are equal. Data from resolved trades shows a hard cliff based on distance from the ensemble mean:

Distance from round(mean)Win rateNotes
0 β€” the mean bucket~20%highest ROI, avg entry ~18Β’
Β±1Β°C~14%still positive
Β±2Β°C~9%positive but thin
3Β°C or more0%never won β€” complete dead zone

Distance-3+ bets look cheap and show high apparent edge. They don't win. The ensemble doesn't genuinely believe in those temperatures β€” it's assigning noise-level probability, and the market prices it more accurately than it appears. Cutting this class entirely via maxBucketDistance = 2 was the single biggest improvement from the May 2026 audit.

NO trades β€” the same thesis, other direction

When a temperature is well above (or below) the ensemble mean, the market sometimes still prices YES too generously. We buy NO instead β€” betting against the market's overconfidence rather than for the forecast.

Example: ensemble mean is 23Β°C for Madrid. The market has "will it reach 28Β°C?" with YES at 18Β’. Our forecast says 3% chance. We buy NO at 82Β’ β€” edge multiplier = 0.97 Γ· 0.82 = 1.18Γ—.

The distance filter works in both directions (above and below mean), with a minimum of max(2Β°C, 1.5Οƒ) from the mean. YES must still be β‰₯ 10Β’ β€” if the market already agrees the temperature is unreachable, there's nothing to exploit. NO token sweet spot is 72–88Β’; outside that range losses cluster historically.

active experiments using Edge
#1 Default edge β€”

The post-audit production configuration β€” our current best-guess at the optimal edge strategy. The May 2026 analysis revealed three fatal flaws in the original setup: (1) distance-3+ bets never win; (2) the 35Β’ price cap was excluding mean-bucket bets which are the most profitable; (3) minEdge=1.30 was too tight, missing good near-mean opportunities. Fixed with a hard 2-bucket distance cap, relaxed edge threshold (1.05Γ—), raised price ceiling to 50Β’, 10Β’ floor on YES, and dynamic position sizing that scales with confidence. NO trades enabled.

#6 Baseline v2 edge β€”

A frozen snapshot of the May 17 configuration β€” the "before" picture. Flat $3/trade regardless of confidence, 1.30Γ— edge required, 35Β’ price cap, no bucket-distance cap, 35% stop-loss. Running as a clean control group to quantify how much the audit improvements are worth. Started June 12 2026 after a stop-loss bug was patched (the original Baseline, #3, was running with broken stop-losses and has been retired). Expected to underperform Default because without the distance cap it keeps placing distance-3+ bets that empirically never win.

#13 Baseline v3 (No Stop) edge β€”

Identical to Baseline v2 β€” flat $3/trade, 1.30Γ— edge, 35Β’ price cap, no bucket-distance cap β€” but with the 35% stop-loss removed. Analysis of 184 stopped trades in v2 found that 59% of classifiable stopped trades went on to win: the stop was firing on temporary price dips in thin markets (slippage meant exits at 0.1–1Β’ instead of the intended βˆ’35%), and the temperature target was still hit regardless. The 10 confirmed wrong stops cost βˆ’$414 in missed profits while the 7 correct stops saved only +$5. Baseline v3 lets bets run to resolution to test whether holding is structurally better than cutting.

spread Spread skip the edge hunt β€” buy the probability distribution
the thesis

Edge-hunting requires accurately identifying which markets are mispriced β€” which is its own source of error. The spread strategy asks: what if we skip that step entirely? If the ensemble forecast is well-calibrated overall, then buying every temperature bucket proportional to its probability mass should work. The correct bucket wins proportionally to how much probability was assigned to it. We capture the calibration advantage without needing to pick winners.

Every city gets a budget distributed across all qualifying buckets. The 38% bucket gets 38% of the bucket-weighted budget. The 12% bucket gets 12%. No edge filter. No proximity cap.

cityBudget = numQualifyingBuckets Γ— positionSizeUsd
bucketStake = cityBudget Γ— (bucketProb Γ· totalQualifyingProb)
why it might beat Edge β€” and why it might not

The case for Spread: Edge implicitly assumes we can identify which markets are mispriced, but that selection adds noise. If the forecast is the real alpha, buying proportional to the forecast strips out the noise and gets there more directly. Spread also naturally avoids the "lottery ticket" problem β€” it buys the likely outcomes heavily and the long shots lightly, by design.

⚠
The case against: Spread buys overpriced buckets. If the mean bucket has YES at 75Β’ and we forecast 60% probability, Spread still buys it β€” at negative edge. Edge would skip it. The question is whether aggregate calibration covers those overpayments. This is exactly what Spread v1 is testing.
active experiments using Spread
#2 Spread v1 spread β€”

The core comparison experiment running against Default. No edge filter, no proximity cap β€” just the forecast distribution. The central question it's answering: does the edge-hunting in Default add real value, or is the forecast distribution itself the alpha? If Spread v1 approaches Default's risk-adjusted returns, the implication is that the edge filter is mostly filtering signal, not noise β€” and a simpler approach works just as well. If it significantly underperforms, it validates active price selection as worth the complexity.

scalp Scalp timing variant on Spread enter early in the day, take profit on intraday repricing
the insight

Temperature markets go through a price discovery cycle each day. Early in the morning, when little current temperature data exists, uncertainty is high and tokens are cheap. As the day progresses and the actual trajectory becomes clear, the market reprices β€” the likely outcome's token appreciates toward certainty. By afternoon the winner is often obvious at 70–80Β’.

The scalp thesis: we can capture that intraday repricing without needing to hold to resolution. Buy early when tokens are cheap (below 35–40Β’), take profit when they've moved +30% in our favour, and recycle capital to the next city. The forecast is still doing the work of picking which buckets to buy β€” but the exit is timing-based, not resolution-based.

how it differs from Spread

Entry is identical to Spread β€” buy all qualifying buckets proportional to forecast probability. The two additions are:

Entry price cap: only enter tokens below 35–40Β’. This ensures we're buying before the repricing has happened β€” a token already at 60Β’ has had most of its intraday run. The cap also means we skip the mean bucket if it's already expensive, favouring the Β±1–2Β°C neighbours instead.

Intraday exit: take profit fires at +30% above entry price. The scheduler checks every few minutes via the Gamma API. A 20Β’ entry sells at 26Β’. If neither exit fires, the trade resolves normally at end of day.

active experiments using Scalp
#4 Scalp v1 scalp β€”

The conservative variant. Take-profit at +30%, stop-loss at 50% of entry, higher probability threshold (min 20%), entry capped at 40Β’. The stop-loss is the key variable being tested here β€” Polymarket CLOB bids are always near-zero, so stop-losses rely on the Gamma API price which can lag. The hypothesis is that the combination of spread entry + intraday exit generates alpha vs holding to resolution. Does cutting losers early via stop-loss actually help, or does it cut positions that would have recovered?

#5 Scalp v2 (Spread) scalp β€”

The aggressive variant: no stop-loss, default 8% probability floor (wide net vs v1's 20%), tighter entry cap at 35Β’, wider trading window extended to 19:00 local. Tests two things simultaneously: (a) whether removing the stop-loss improves returns by not cutting positions that would have recovered, and (b) whether casting a wider net β€” more buckets, lower confidence bar β€” improves aggregate probability capture. Directly comparable to v1 to isolate the stop-loss effect and probability threshold effect.

#7 Scalp v3 (Trailing) scalp β€”

Trailing take-profit variant of v1. Same entry (stop-loss at 50%, min prob 20%, entry cap 40Β’) but instead of selling immediately at +30%, the position is held. Once the +30% threshold is crossed, a high-water mark is tracked every 30 seconds β€” the position rides the move until the price drops 5% below peak, then exits. Directly comparable to v1 to test whether riding the profit wave outperforms locking in a fixed gain.

#8 Scalp v4 (Trailing) scalp β€”

Trailing take-profit variant of v2. Same entry (no stop-loss, 8% probability floor, entry cap 35Β’, window until 19:00 local) but with trailing exits. The combination of no stop-loss and trailing take-profit maximises upside capture β€” but without a floor. A sharp intraday reversal between the 30-second checks can turn a winning position negative. Directly comparable to v2 to isolate the trailing effect in the more aggressive setup.

#10 Scalp v5 (Hybrid Trailing) scalp β€”

Hybrid trailing stop β€” the intended design of v4, now correctly implemented. Entry is identical to v2 (spread mode, no stop-loss, 8% probability floor, window until 19:00 local). The exit is a two-part trailing stop: activation triggers at +30% above entry (same threshold as v2's fixed take-profit) using a percentage so cheap tokens can reach it, then the position trails 5Β’ below peak rather than exiting immediately. On winning markets this captures far more upside than v2's fixed +30% exit. v4's flaw was absolute-cents activation (entry + 8Β’) β€” a 5Β’ token had to reach 13Β’ to arm, which almost never happened. v5 replaces that with entry Γ— 1.3, so a 5Β’ token arms at 6.5Β’, same as v2 would have exited. Floor: once armed, stop never falls below activation price β€” any dip below +30% triggers an exit. This protects cheap-token gains but causes premature exits when price dips briefly below activation then recovers. See v6 for a looser floor.

#11 Scalp v6 (Entry Floor) scalp β€”

Hybrid trailing stop with entry-price floor β€” fixes the over-eager exit problem observed in v5. Entry is identical to v2/v5 (spread mode, no stop-loss, 8% probability floor, window until 19:00 local). Exit: activation at +30% above entry, then trails 5Β’ below peak. The key difference from v5 is the floor: instead of clamping the stop at activation price (which fires on any dip below +30%), the floor is the entry price β€” the stop can never push the position into a loss. This means a brief dip below +30% does not trigger an exit; only a 5Β’ pullback from the trailing high does. The entry floor still protects cheap tokens where a 5Β’ trailing distance would produce a negative stop (e.g. a 2Β’ token with trailingHigh 3Β’ gives rawStop = βˆ’2Β’; the entry floor catches this at 2Β’). On normal tokens the trailing stop runs freely above activation and only exits on a genuine 5Β’ pullback from peak.

#12 Scalp v7 (Peak Window) scalp β€”

Peak-window scalp β€” systematically exploits the high-certainty temperature window every day. Identical to v6 (entry-price floor hybrid trailing stop: activates at +30%, trails 5Β’, floor at entry) but adds minCityLocalHour: 12: each city is only traded after noon local time. The insight: when a city is 1–5 hours from its temperature peak, the day's outcome is nearly certain β€” the observed max is already near or above the market threshold β€” yet Polymarket prices still reflect the uncertainty from when the market opened in the morning. The bot's ensemble forecast confirms the near-certain outcome; participants who set prices overnight had less information. By reserving the city budget until noon, v7 avoids spending it on low-certainty morning trades and concentrates the full budget on the afternoon window where the edge is sharpest. The trailing stop then captures price appreciation as participants discover the near-certain outcome. Unlike v5/v6 which only exploit this window accidentally on their first run (when all city budgets are fresh), v7 does it systematically every day for every city.

position sizing

For edge YES and NO trades, size scales linearly with forecast confidence:

size = min( baseSize Γ— (forecastProb Γ· minForecastProbability),  maxPositionSizeUsd )

At the minimum probability threshold you get the base stake ($3). Higher confidence = proportionally larger stake, capped at the maximum ($8). The bot bets bigger when it's most confident.

For Spread and Scalp, position size is pre-calculated by the distribution formula β€” each bucket's size is cityBudget Γ— (bucketProb Γ· totalQualifyingProb). The confidence scaling doesn't apply on top; the proportional allocation does the same job.

🩷
hey, emma.
you built something genuinely cool.
the map lights up because of you.

πŸ©·πŸ€πŸ©΅πŸ³οΈβ€βš§οΈ