Day 46 — External Ingestion: Feeding Price Data to the MockBroker
This lesson covers one of the most misunderstood parts of building a trading system: how price data actually gets in. You will learn why the obvious approach fails, how professional systems handle it, and you will build a working version from scratch.
The “REST Polling” Trap
A junior engineer opens the Alpaca docs, sees a clean REST endpoint, and writes this:
import requests, time
while True:
r = requests.get(
"https://data.alpaca.markets/v2/stocks/SPY/quotes/latest",
headers={"APCA-API-KEY-ID": key, "APCA-API-SECRET-KEY": secret}
)
price = r.json()["quote"]["ap"]
broker.update_price("SPY", price)
time.sleep(1)
This pattern appears in every beginner tutorial. It will ruin a live account. Here is why.
The Failure Mode
Symptom 1 — Rate Limit Collapse. Alpaca’s free tier enforces 200 requests/min per endpoint. That sounds generous — until you are polling 20 symbols at 1-second intervals. You hit the ceiling in 10 seconds. The API returns 429 Too Many Requests. Your loop swallows it silently (no error handling), continues with a stale price, and sends an order at a quote that is now 8 seconds old.
Symptom 2 — Synchronous I/O Blocks Your Event Loop. requests.get() is a blocking call. It holds the GIL for the duration of the network round trip — typically 20–150ms to Alpaca’s servers. While that call is blocking, your order matching logic, risk checks, and P&L recalculation are all frozen. During a volatility spike, when you most need sub-millisecond responsiveness, you are dead in the water.
Symptom 3 — Unbounded Memory Growth. Storing every tick: self.ticks.append(price). For 4 symbols at 10 ticks/second gives 144,000 entries per hour. Over a 6.5-hour session: roughly 936,000 floats. Python’s float is 28 bytes under object model overhead. That is 26MB — manageable today, catastrophic when you scale to 50 symbols.
Symptom 4 — Clock Skew on Staleness Checks. Using datetime.now() for staleness detection breaks under NTP clock adjustments. If your server syncs and the wall clock jumps backward by 50ms, your “5-second stale threshold” logic fires incorrectly. Use time.monotonic() — it is guaranteed to never go backward.
The actual kill shot is that none of these failures throw exceptions. They silently corrupt your price state. An order fills at yesterday’s bid. Your P&L attribution is wrong. You do not know until reconciliation — if you built reconciliation at all.



