Skip to content
C.W.K.
Stream
Lesson 05 of 05 · published

The Fast Half and the Slow Half

~12 min · caching, performance, decomposition, affordability

Level 0Raw Ore
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"A market cap has a fast half and a slow half."

The cost problem, and the wrong ways out

To rank the largest US companies you need a market capitalization for each one, across a universe of over twelve thousand listings. Market cap is price times shares outstanding. The naive implementation asks a provider for both, per company, every day — twelve thousand requests for a number that gets used once.

The usual escapes all degrade the answer. Sample a subset: now the ranking is approximate, and approximate rankings have exactly the failure mode this whole track is about. Refresh weekly: the prices go stale, and prices are the half that actually moves. Restrict to a pre-known list of large companies: you have assumed the answer you were trying to compute.

The decomposition

The escape is not to approximate — it is to notice that the two factors move on completely different clocks.

Price moves every session, and the provider offers a single grouped call that returns the day's bars for every listing at once. One request, twelve thousand prices.

Shares outstanding moves on filings — quarterly, in practice. It must be fetched per company, but it is nearly static, so it can be cached hard with a validity window measured in weeks.

So a daily pass costs one grouped call plus arithmetic. The expensive half is fetched rarely, the cheap half is fetched in bulk, and no company is dropped by random sampling. But be precise about what that buys: the ranking is exact arithmetic over the candidate pool, and the pool is a turnover-selected 1,500 out of roughly 5,100 priced common stocks. The step from there to "exact over the universe" rests on an assumption — that no top-500 company by market cap sits outside the top 1,500 by dollar turnover — which is very probably true and is nowhere verified. That is a much better position than sampling. It is not the same as exact, and a lesson in this quest of all places should not round it up.

Before you approximate, check whether your inputs share a clock. Expensive computations are often expensive because a fast-changing factor and a slow-changing one are being refreshed at the same rate. Splitting them by their natural cadence frequently converts an unaffordable exact answer into an affordable one — and that is strictly better than an affordable approximate answer, which you can never fully trust again.

The cheap net before the expensive catch

One more layer. Even cached, fetching share counts for twelve thousand companies is more work than needed for a top-500 ranking. So candidates are first narrowed by dollar turnover — price times volume — which arrives free in the same grouped call.

The reasoning is stated carefully in the code, and the care is the point: this is a wide net for a small catch, but a net, not a guess. It only has to be large enough that no top-500 company can fall outside it. Large companies dominate turnover by an enormous margin, so a candidate pool three times the size of the target is a very safe margin — and the safety is what makes it legitimate. A narrower pool chosen for speed would have been a sampling decision in disguise.

Concurrency, sized by the honest state. The per-pass fetch limit covers the whole candidate pool in one pass, because a cold start that dribbles in over days leaves the measure unpublished for days. Sequential detail calls blew a client timeout on the first real run; a modest thread pool turned minutes into seconds. Note the ordering: the honest "publish nothing until complete" rule came first, and it is what made the performance work necessary and obvious.

Code

Two clocks, two strategies, and one honest limit·python
# THE FAST HALF -- price. One grouped call, every US listing.
bars = market.polygon_grouped_bars(day)

# THE SLOW HALF -- shares outstanding. Moves on filings, so cache
# hard and refresh on a long window.
SHARES_TTL_DAYS = 30

# A cheap, wide net BEFORE the expensive per-company fetch:
# dollar turnover, which no megacap escapes and which arrives
# free in the grouped call above.
priced.sort(key=lambda b: b["volume"] * b["vwap"], reverse=True)
candidates = priced[:CANDIDATE_POOL]     # 1500, for a top-500 answer

# Sized to cover the WHOLE pool in one pass: a cold start that
# dribbles in over days leaves the measure unpublished for days.
SHARES_PER_PASS = 1500

with ThreadPoolExecutor(max_workers=8) as pool:
    for row in pool.map(_safe_shares, batch):
        ...

# Daily cost: 1 grouped call + arithmetic.
# Answer quality: exact arithmetic over the CANDIDATE POOL --
#   which is a stated assumption, not a proof. Live coverage:
#   ~5,100 priced common stocks narrowed to 1,500 by turnover.
#   The claim is that no top-500 company by market cap can fall
#   outside the top 1,500 by dollar turnover. That is very
#   probably true and it is NOT verified by the code.
#   Say which of those two you have.

External links

Exercise

Find an expensive periodic computation in your systems and list its inputs with how often each genuinely changes. If any input is being refreshed far more often than it changes, you have found the cost. Work out what an exact answer would cost with each input refreshed at its own natural rate — often the exact version turns out to be affordable, and the approximation was never necessary.
Hint
The classic pairs are price and quantity, rate and volume, config and traffic, schema and rows. One side moves per event, the other per release or per quarter. Systems usually refresh both at the fast side's cadence because that is what the loop's period was set to.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.