
Tagging 8-K disclosures with AI: corporate events, labelled by what actually happened
A SEC 8-K API that labels what actually happened. Query filings by event type (CEO departures, cybersecurity incidents, M&A), with source-cited tags.

rian

Introducing
Jun 18, 2026
TL;DR: Massive's LULD WebSocket channel streams Limit Up/Limit Down price band updates, halts, and resumptions for every NMS stock in real time. In this post, we walk through what LULD is and why it matters, the event schema, and two runnable Python examples: a full firehose that prints every band update, and a smart monitor that narrows the stream down to the Magnificent 7 plus market-wide halt and resumption alerts. Clone the repo, plug in your API key, and start monitoring the market's volatility guardrails in minutes.
In this post, we'll cover what Limit Up/Limit Down price bands are and why they are one of the most useful real-time inputs for a risk system or volatility dashboard, what a LULD WebSocket event looks like field by field, and how to build a practical monitor that separates routine band recalculations from the rare, high-signal halt events.
Who this is for: Quantitative traders, algorithmic trading teams, risk-system engineers, and fintech builders who need real-time awareness of intraday volatility guardrails across U.S. equities.
Plan requirement: LULD data requires a Stocks Advanced plan or the LULD Expansion add-on.
New to WebSockets? If you haven't connected to Massive's WebSocket API before, start with the WebSocket quickstart. It walks through the connect, authenticate, and subscribe pattern, the Python SDK installation, and the shape of a minimal client. The rest of this post assumes familiarity with those basics.
Limit Up/Limit Down is a regulatory mechanism designed to prevent excessive volatility by establishing dynamic price bands for individual securities. Think of them as guardrails: for every NMS stock, the exchanges calculate an upper and lower price boundary that updates continuously throughout the trading day. When a stock's price approaches or breaches those boundaries, the system responds with escalating intervention. First, the stock enters a limit state. If the price doesn't recover within 15 seconds, a five-minute trading pause is triggered. If volatility persists after the pause, the stock can be halted entirely.
LULD bands are recalculated throughout regular trading hours (9:30 AM to 4:00 PM ET). The width of each band depends on the stock's reference price tier and the time of day. Bands are wider during the first 15 minutes and last 25 minutes of the session, when volatility tends to be highest.
By subscribing to Massive's LULD WebSocket feed, you can monitor these events across NYSE, Nasdaq, and other U.S. exchanges in real time. For quantitative traders and risk systems, LULD bands define the boundary conditions of intraday price movement. Knowing exactly where the ceiling and floor are, and how fast a stock is approaching them, is a direct input to position sizing and risk management. For anyone building monitoring dashboards or alerting tools, LULD events are the earliest signal that a stock is approaching a halt, giving you time to react before the pause hits.
The LULD channel is available on
A sample LULD message looks like this:
{ "ev": "LULD", "T": "MSFT", "h": 492.99, "l": 446.04, "i": [16], "z": 3, "t": 1764086430905642800, "q": 5925769 }
Here is what each field contains:
| Field | Description |
|---|---|
| ev | Event type (always "LULD") |
| T | Ticker symbol |
| h | Upper price band (limit up) |
| l | Lower price band (limit down) |
| i | Array of indicator codes |
| z | Tape (1 = NYSE, 2 = AMEX, 3 = Nasdaq) |
| t | Timestamp (nanoseconds) |
| q | Sequence number for message ordering |
The indicator codes carry the most signal:
| Indicator | Meaning |
|---|---|
| 15, 16 | Price band update (routine recalculation) |
| 17 | Trading halt or pause (NASDAQ-listed tickers only) |
| 18 | Trading resumption or reopening (NASDAQ-listed tickers only) |
Indicators 15 and 16 fire continuously as bands are recalculated throughout the day. They are high volume but low urgency. Indicators 17 and 18 are the rare, high-signal events: actual halts and resumptions. A practical monitoring setup watches the routine updates for your specific watchlist while alerting on halts and resumptions across the entire market. For the full list of indicator and condition codes, see the Massive conditions and indicators glossary.
Both example scripts are published in Massive's community GitHub repo. You'll need Python 3.10 or later, uv for dependency management, and a Massive API key.
git clone https://github.com/massive-com/community.git cd community/examples/websocket/luld-monitor uv sync cp .env.example .env # Add your MASSIVE_API_KEY to .env
From there, run any script with
The simplest approach is to subscribe to
from massive import WebSocketClient from massive.websocket.models import Feed, Market import os from dotenv import load_dotenv load_dotenv() API_KEY = os.getenv("MASSIVE_API_KEY") client = WebSocketClient( api_key=API_KEY, feed=Feed.RealTime, market=Market.Stocks ) client.subscribe("LULD.*") def handle_msg(msgs): for m in msgs: print(m) print("LULD Basic Firehose Started") print("Streaming ALL LULD price-band updates in real time") print("Press Ctrl+C to stop\n") try: client.run(handle_msg) except KeyboardInterrupt: pass
This outputs hundreds of messages per minute during market hours. Every price band recalculation for every ticker crosses the wire:
LimitUpLimitDown(event_type='LULD', symbol='NVDA', high_price=200.95, low_price=181.81, indicators=[16], tape=3, ...) LimitUpLimitDown(event_type='LULD', symbol='ZOOZ', high_price=0.46, low_price=0.16, indicators=[16], tape=3, ...)
The firehose is useful for understanding the volume and shape of the data, but not what you'd run as a monitoring tool. For that, you need filtering.
The smart monitor narrows the firehose down to what's actually actionable: price bands for the Magnificent 7 (AAPL, AMZN, GOOGL, META, MSFT, NVDA, TSLA) and any trading halts or resumptions across the full market.
from massive import WebSocketClient from massive.websocket.models import Feed, Market, LimitUpLimitDown import os import time from dotenv import load_dotenv load_dotenv() API_KEY = os.getenv("MASSIVE_API_KEY") MAG7 = {"AAPL", "AMZN", "GOOGL", "META", "MSFT", "NVDA", "TSLA"} client = WebSocketClient( api_key=API_KEY, feed=Feed.RealTime, market=Market.Stocks ) client.subscribe("LULD.*") def format_ts(ts: int) -> str: seconds = ts / 1_000_000_000 return time.strftime("%H:%M:%S", time.localtime(seconds)) def handle_msg(msgs): for m in msgs: if not isinstance(m, LimitUpLimitDown): continue symbol = m.symbol indicators = m.indicators or [] high = getattr(m, "high_price", None) low = getattr(m, "low_price", None) ts_str = format_ts(m.timestamp) alert = "" if 17 in indicators: alert = " HALT / SUSPENDED PAUSE (Indicator 17)" elif 18 in indicators: alert = " RESUMPTION / REOPENING (Indicator 18)" if symbol in MAG7 or alert: print(f"{ts_str} | {symbol:6} | " f"Upper: {high:>8.2f} | Lower: {low:>8.2f} | " f"Ind: {indicators}{alert}") try: client.run(handle_msg) except KeyboardInterrupt: pass
The filtering logic is a two-tier approach. First, it checks whether the ticker is in the Mag 7 set; if so, the routine band update is worth displaying. Second, regardless of ticker, it checks whether the event contains indicator 17 (halt) or 18 (resumption), because those are always worth surfacing no matter the stock.
The result is a clean, focused output:
07:01:00 | NVDA | Upper: 200.95 | Lower: 181.81 | Ind: [16] 07:01:00 | MSFT | Upper: 411.48 | Lower: 372.29 | Ind: [16] 07:01:20 | AFJK | Upper: 0.00 | Lower: 0.00 | Ind: [17] HALT / SUSPENDED PAUSE (Indicator 17) 07:06:20 | AFJK | Upper: 52.34 | Lower: 42.82 | Ind: [18] RESUMPTION / REOPENING (Indicator 18)
The
cd community/examples/websocket/luld-monitor uv sync cp .env.example .env # Add your MASSIVE_API_KEY to .env uv run basic_firehose.py # raw stream, all tickers uv run luld_monitor.py # smart monitor
Press Ctrl+C to stop either script.
The smart monitor is a starting point. A few directions that work well on top of it:
This content is for educational purposes only. Nothing in this post constitutes investment advice or a recommendation to buy or sell any securities or other financial instruments. Massive is a market data provider, not a broker-dealer, exchange, or investment adviser. Market data accessed through Massive may originate from third-party exchanges and data providers or may be derived or calculated by Massive; in either case, it is subject to the applicable terms of your Massive subscription agreement. The data and code samples provided by Massive are offered on an "as-is" basis without any warranty of accuracy, completeness, or timeliness. You are solely responsible for your use of the data provided by Massive and for compliance with all applicable terms and conditions, laws, and data licensing requirements.
Justin
editor
See what's happening at Massive

A SEC 8-K API that labels what actually happened. Query filings by event type (CEO departures, cybersecurity incidents, M&A), with source-cited tags.

rian

In this post, we'll walk through how Earnings Watcher built its analytics layer on Massive's options chains and snapshots, why Fair Market Value anchors its scanner, and how the same API pattern extended from options into post-earnings drift analysis on the underlying equity.

alexnovotny

Massive's LULD WebSocket channel streams Limit Up/Limit Down price band updates, halts, and resumptions for every NMS stock in real time.
editor