A late or repeated aggregate bar on the WebSocket is deliberate. Trades do not all arrive at the instant they happen: an off-exchange execution is reported to a FINRA facility rather than matched on an exchange, within 10 seconds during facility hours, and that is long enough at market speed for a print to land after the bar it belongs to was published. Rather than publish a bar we already know may be revised, Massive holds a late-trade window and rebroadcasts a bar when a late trade changes it. Your consumer should treat a bar with a timestamp it has already seen as a replacement for the earlier one.
Applies to
- Plans: every paid subscription with WebSocket access.
- Feeds: the per-second (A) and per-minute (AM) aggregate channels on the stocks cluster.
- Asset classes: US stocks. The rebroadcast behavior of the other clusters is not documented, and forex bars are built from quotes rather than trades, so the mechanism below does not apply to them in the same way.
How long each bar can be revised
How long each bar can be revised depends on its size, because each has its own late-trade window, and the window is what decides how long you should expect a repeat:
| Bar | When it is first sent | How long it stays open to late trades |
|---|---|---|
| Per-second | Two seconds after the second closes, to let late messages arrive | 15 minutes from the second it covers |
| Per-minute | As the trades arrive and the bar is calculated | 15 minutes from the minute it covers |
| Daily | Continuously recalculated through the session | The whole session, however late the trade |
A trade that arrives after the 15-minute window closes is not folded into the second or minute bar on the stream. It reaches the end-of-day data instead, which is why a minute bar pulled from REST the next morning can differ from the one you received live.
The window is for late trades only. A trade that is canceled or corrected afterwards is not taken out of the live bar: a cancellation is a separate message that usually arrives well after 15 minutes, and the WebSocket trade message has no field to carry it. REST bars leave canceled and corrected trades out once the cancellation reaches REST, which happens a few days after the session, and that is the second reason a REST bar can differ from the live one. How do Massive's feeds handle canceled trades? has the details.
How to handle a rebroadcast
- Key your store on the bar's start timestamp, not on arrival order.
- When a bar arrives whose timestamp you already hold, overwrite it. Do not append, and do not sum the volume.
- If you compute anything downstream from a bar, such as an indicator, recompute it when a bar is replaced, or accept that the first value was provisional.
- Expect a per-second bar about two seconds after the wall clock leaves that second. That delay is the late-trade window, not latency.
Why we rebroadcast rather than drop
We rebroadcast rather than drop late trades because a bar that omits a real trade is wrong in a way you cannot detect, while a bar that arrives twice is wrong in a way you can handle in three lines of code. The alternative, publishing the first version and discarding everything that arrives afterwards, would leave your history permanently different from ours and from the exchange's. There is more on the reasoning in the Aggregate Bar Delays post.
If you see an error
A bar you have already stored arriving again is expected behavior, not a duplicate-delivery bug. Overwrite on timestamp.
A minute bar from REST that does not match the one you received live, more than 15 minutes after the fact, means either a trade arrived after the late-trade window and was folded into the end-of-day data, or a trade in that minute was later canceled or corrected. REST is the version to trust in both cases, though a cancellation only shows up there a few days after the session, so a mismatch caused by one appears days later rather than the next morning.
A per-second bar that seems two seconds behind the clock is the late-trade window. A delay that grows over time is not, and it is usually a slow consumer; Why is my Massive WebSocket data running behind, and is it my client or Massive? shows how to tell.

