pnclPULSEGet API access

DEDUPE AND COOLDOWNS

Deduplicating Pinnacle drop alerts.

One falling price can produce several alerts from the Pinnacle odds API: the engine reports each fall that crosses your threshold, live markets move every few seconds, and a restart can hand you a batch you had already handled. Three keys, one cooldown rule and a small state store turn that into one alert per decision.

Why one selection alerts more than once

The stream pushes every fall at or above min_drop inside its own detection window. A home price that goes 2.10, then 1.98, then 1.87 over twenty minutes crosses 5% twice from two different starting points, and both crossings are real. In play, the same selection can reprice on every point of a tennis match, so a 5% rule fires again and again while the favourite runs away with the set.

Restarts add a third source. If your process records its position before it finishes handling a batch, the alerts in that batch are handled again when it comes back. The fix is ordering, not filtering: mark an alert done only after the message that depends on it has gone out.

Three keys, three questions

Each key answers a different question, and a relay usually needs all three.

The cooldown rule

After an alert for a selection goes out, ignore further alerts for that selection until a cooldown ends or the price has fallen by another full threshold from the price you last reported, whichever comes first. The second clause matters: a cooldown alone would hide a 2.10 to 1.70 collapse behind a 2.10 to 1.98 message sent a minute earlier.

state[key] = {last_price, alerted_at}

on alert for key:
    seen = state.get(key)
    if seen is None: send; store; return
    fresh = now - seen.alerted_at < COOLDOWN
    further = (seen.last_price - to_price)
              / seen.last_price >= MIN_DROP
    if fresh and not further: return
    send; store

Ten minutes suits prematch markets, where a line drifts over an afternoon. Sixty seconds suits live markets, where the same rule would otherwise swallow a whole set of tennis. Run live and prematch on separate values, since they arrive on separate streams anyway. To see what a given percentage means in probability terms before you pick a threshold, the dropping odds calculator on pnclDATA turns any pair of prices into the shift the market made.

A store that survives restarts

The state is tiny: one small record per selection you have alerted on, and a set of alert ids. Redis with a 24-hour expiry fits, and so does a SQLite table with an expires_at column swept once an hour. Load it at startup before opening the stream, so the first alerts after a restart are judged against yesterday's messages rather than a blank memory. A prematch slate on a busy day produces low thousands of selections, far below anything that needs tuning.

Batching what arrives together

Alerts arrive in lists, and one event often appears with two or three outcomes in the same list when a market reprices as a whole. Group by the event key inside each batch and send one message that lists every side that moved. Across batches, a five-second buffer collects the stragglers without delaying anyone noticeably. The delivery side of this, including what the chat platforms do when you post too fast, is on the webhook relay page.

Measure the effect

Count alerts read and alerts sent, per stream, per day. A relay that sends a fifth of what it reads is behaving; one that sends nearly everything has a cooldown that never bites, and one that sends almost nothing has a threshold set too high for the sports it watches. Log the two counts once an hour and the right settings show up within a week.

Questions this rule raises

Does the stream deduplicate anything itself?

It does not repeat an alert with the same id, but every new crossing of the threshold is a new alert with a new id. Deduplication by selection is your job.

What about prices that rise?

The drop stream only reports falls. A selection whose price rises back after a drop makes no sound; if you need both directions, poll the market with REST or take the raw WebSocket frames.

Should the cooldown be per sport?

Per phase first, per sport second. Live and prematch differ far more than tennis and soccer do. Start with one live value and one prematch value, then tune a sport only when its log shows a problem.

Get API access

Stream behaviour follows the vendor's published documentation, checked on 26 September 2026. Published .