Reconstruct the National Best Bid and Offer from WRDS millisecond TAQ, measure the cost of demanding liquidity across a panel of stocks spanning liquidity tiers and across a calm month (July 2019) versus a stressed month (March 2020), decompose the spread into adverse selection and order processing, fit a market-impact model, and use it to backtest optimal-execution schedules against the real tape with a queue-aware fill simulator.
Research question. How large is the cost of demanding liquidity in US equities, how does it decompose into adverse selection versus inventory/processing cost, how does it scale with order size and market stress, and how much of the implementation shortfall of a large order can a schedule (TWAP / VWAP / Almgren–Chriss) actually save versus a naive market order?
WRDS Daily TAQ (millisecond), regular trading hours only. Licensed — nothing
under data/ is committed.
| Block | WRDS table | Content |
|---|---|---|
| Trades | taqm_YYYY.ctm_YYYYMMDD |
consolidated trades: time, price, size, exchange, sale condition |
| NBBO | taqm_YYYY.complete_nbbo_YYYYMMDD |
best bid/ask and sizes (last per 1-second bucket; full tick for three check days) |
| Master | taqm_YYYY.mastm_YYYYMMDD |
round-lot size, primary exchange |
Universe: 20 names, five per liquidity tier (mega / large / mid / small-cap). Periods: July 2019 (VIX ~13) and March 2020 (VIX to 82).
Time is carried through the whole pipeline as integer nanoseconds since
09:30 ET — no timezone handling, no datetime64 in the hot path.
config/config.yaml universe, dates, filter thresholds, model parameters
scripts/
taq_pull.py Stage A: the one online step (separate environment)
build_results.py regenerates every table and figure
make_notebooks.py emits notebooks/01..10 (executed)
src/
data_loader.py offline readers, the ns time axis
clean.py trade & quote filters, NBBO series, as-of match to trades
classify.py Lee-Ready / EMO / tick-test trade signing
liquidity.py quoted / effective / realised spread, impact, Roll, Amihud
decomposition.py Glosten-Harris (1988), Madhavan-Richardson-Roomans (1997)
impact.py Kyle's lambda, metaorder reconstruction, sqrt-impact fit
execution.py Almgren-Chriss trajectory, TWAP, VWAP volume profile
fills.py queue-position-aware limit-order fill simulator
backtest.py schedule x size x tape -> implementation shortfall
evaluation.py cross-sectional regressions, bootstrap, hypothesis verdicts
viz.py, pipeline.py
tests/ one module per src module, synthetic fixtures, no network
notebooks/ 01 data+clean .. 10 execution backtest
report/ report.md, defense.md, audit.md
Two environments — the wrds client pins an old pandas that the offline
pipeline cannot use, and on CPython 3.14 that old pandas additionally segfaults
on datetime columns, so the pull is isolated and writes integer timestamps.
python -m venv venv
venv/Scripts/pip install -r requirements.txt
python -m venv venv-pull
venv-pull/Scripts/pip install -r requirements-pull.txt
venv-pull/Scripts/python scripts/taq_probe.py
venv-pull/Scripts/python scripts/taq_pull.py
venv/Scripts/python -m pytest -q
venv/Scripts/python scripts/build_results.py
venv/Scripts/python scripts/make_notebooks.py- Effective spread (value-weighted, bps, calm → March 2020): mega 1.1 → 3.1, large 1.5 → 5.3, mid 3.5 → 10.1, small 9.8 → 21.5. Below quoted on 90% of stock-days.
- MRR adverse-selection share (calm): 0.17 mega → 0.30 small — monotone in illiquidity, but flat-to-down under stress.
- Realised spread turns negative for small-caps in March 2020 (−2.6 bps): market making lost money.
- Impact concavity: fitted exponent 0.11–0.23, every fit t > 6.
- Implementation shortfall (bps, 0.5% → 10% ADV): market 81 → 101, TWAP 24 → 35, passive-at-touch 17 → 31. TWAP beats the market order by −65 bps (paired bootstrap, p < 0.001).
- Cross-sectional: +26 bps small-cap premium, +39 bps stress premium on the shortfall — cost is first-order about what you trade.
See report/report.md.
Complete: 12 source modules, 59 tests, 8 executed notebooks, graduate report with 22 defence questions and a 15-category self-audit.