Does order-flow imbalance predict short-horizon returns after costs? A research and execution framework for market microstructure.
This is a research project, not a trading bot. We're testing whether order-flow imbalance (OFI) predicts 10–60s mid-price returns after controlling for spread, volatility and momentum, and after transaction costs. A clean negative result counts as a finding.
| Role | Owner | Spec |
|---|---|---|
| Quant Research (QR) | Derek (@dryhour) | QUANT_RESEARCH.md |
| Quant Engineering (QE) | Josh | QUANT_ENGINEERING.md |
Timeline: 8 weeks, Wed Sep 30, 2026 → Tue Nov 24, 2026. Each week runs Wednesday to Tuesday. Deadlines are end of day Tuesday, and we do a short sync that day to check the gate before starting the next week.
| ID | Hypothesis |
|---|---|
| H1 | OFI predicts the sign and size of the next h-second mid-price return |
| H2 | OFI adds information beyond spread, volatility, momentum and top-of-book imbalance |
| H3 | Predictive power decays with horizon, fastest in liquid names |
| H4 | The signal is still profitable after realistic costs |
| H5 | The signal is regime-dependent and decays over time |
Full tests and methodology are in QUANT_RESEARCH.md §1 and §4.
microprice/
├── common/ # shared: paths, version metadata, frozen universe + holdout guard
├── ingest/ # QE: WebSocket collector, historical loaders
├── book/ # QE: order-book reconstruction
├── features/ # QE implements, QR specs (feature code)
├── backtest/ # QE: event-driven engine, execution sim, costs, portfolio
├── api/ # QE: FastAPI service
├── dashboard/ # QE: Streamlit/React dashboard
├── research/
│ ├── notebooks/ # QR: exploration (EDA, IC studies)
│ ├── studies/ # QR: scripts that produce paper results; trials.csv lives in research/
│ ├── hand_calcs/ # QR: hand-computed feature examples for QE's unit tests
│ └── output/ # generated tables (IC, data quality)
├── paper/
│ └── figures/ # generated by scripts only
├── configs/ # strategy.yaml, cost scenarios, run configs
├── scripts/ # pipeline runners (week2_pipeline.sh)
├── tests/ # unit, golden, property, leakage, determinism tests; fixtures/ = raw samples
├── docs/ # architecture.md, data.md, performance.md
├── data/ # git-ignored, regenerated from raw
│ ├── raw/ # raw/{venue}/{symbol}/{date}/ (immutable, append-only)
│ ├── book/ # reconstructed L2 snapshots
│ ├── features/ # features/{symbol}/{date}.parquet (QE → QR)
│ └── signals/ # signals/{run_id}.parquet (QR → QE)
└── results/ # git-ignored: results/{run_id}/ (QE → QR)
Planned later (not created yet): Makefile (make data | features | backtest | walkforward | paper), docker-compose.yml.
python3 -m venv .venv && .venv/bin/pip install uv
UV_PROJECT_ENVIRONMENT=.venv .venv/bin/uv sync --all-extras --group dev
.venv/bin/uv run pre-commit install # ruff, ruff format, mypy on commit
.venv/bin/python -m pytest -q # tests marked `data` skip without data/
.venv/bin/python -m ingest.tardis --wait-for-quota # historical raw data (universe v1)
.venv/bin/python -m book.build # raw → data/book/ (1 s grid L2)
.venv/bin/python -m features.build # book → data/features/
.venv/bin/python -m book.golden tardis BTC-USD 2024-11-01 --max-rows 300000
.venv/bin/python -m ingest.coinbase_ws # live collector (runs until stopped)
.venv/bin/python -m ingest.capture_report # live capture summary + golden testData notes are in docs/data.md. Tardis's free tier has a daily transfer quota (~18 GB). The full universe needs about two quota windows, and the loader waits for the reset by itself.
The role docs write the contract paths as
features/…andsignals/…. Here they live underdata/so they don't collide with thefeatures/code package. The formats are unchanged.
Change it only by agreement, and bump the version.
| Artifact | Producer → Consumer | Format |
|---|---|---|
data/features/{symbol}/{date}.parquet |
QE → QR | ts_ns, symbol, one column per feature, labels y_10s, y_30s, y_60s |
data/signals/{run_id}.parquet |
QR → QE | ts_ns, symbol, signal (target position in [-1, 1]), model_id |
configs/strategy.yaml |
QR → QE | thresholds, holding period, max position, cost scenario |
results/{run_id}/ |
QE → QR | trades.parquet, pnl.parquet, metrics.json, config.yaml, git SHA |
ts_nsis the local receive time, int64 nanoseconds UTC. Exchange time is stored separately.- Every result must be reproducible from
(git SHA, config, data version).
| Week | Dates | Focus | QR (Derek) | QE (Josh) |
|---|---|---|---|---|
| 1 | Sep 30 – Oct 6 | Research setup | Hypothesis + literature review | Data architecture + ingestion |
| 2 | Oct 7 – Oct 13 | Data | Explore market data + identify features | Historical data pipeline |
| 3 | Oct 14 – Oct 20 | Signal | Order-book / microstructure signals | Feature computation infrastructure |
| 4 | Oct 21 – Oct 27 | Modeling | Statistical / predictive models | Initial backtesting engine |
| 5 | Oct 28 – Nov 3 | Strategy | Combine signals + portfolio logic | Execution + slippage / fees |
| 6 | Nov 4 – Nov 10 | Validation | Walk-forward + out-of-sample testing | Optimize bottlenecks |
| 7 | Nov 11 – Nov 17 | Robustness | Statistical significance + stress tests | Productionize / reproducibility |
| 8 | Nov 18 – Nov 24 | Finalization | Research paper + conclusions | Dashboard + documentation |
Tick a box when it's done and merged. A week's Gate must be fully ticked before either of us starts the next week's work that depends on it. Handoffs with an earlier date are what the other person is blocked on, so hit those first.
QR — Derek
- Finalize research question and H1–H5, with the economic rationale for each → research/hypotheses.md (Fri Oct 2)
- Literature review notes in
research/: Cont, Kukanov & Stoikov (2014); Stoikov (2018); Cartea, Jaimungal & Penalva; Bailey & López de Prado (deflated Sharpe) → research/literature_review.md (Tue Oct 6; done Oct 11) - Sign off feature spec and label spec: horizons, clock vs. event time, mid vs. microprice → research/feature_label_spec.md (Fri Oct 2)
- Choose symbol and date universe before looking at any results, and write it down → research/universe.md, configs/universe.yaml (Tue Oct 6; done Oct 11)
- Define the final holdout dates (last ~20%) and record them → 5 of 24 dates, 2026-06-01 … 2026-10-01, enforced in code by
common.universe(Tue Oct 6; done Oct 11) - Create
research/trials.csvwith its column headers → research/trials.csv, columns in research/trials.md (Tue Oct 6; done Oct 11)
QE — Josh
- Choose a data source (crypto L2 recommended) and document the choice in
docs/data.md→ Coinbase: live Advanced Trade WS + historical Tardis.dev (docs/data.md §1) (Fri Oct 2; done Oct 11) - Write the raw message schema doc (
exchange_ts,recv_ts_ns, sequence numbers) → docs/data.md §2 (Fri Oct 2; done Oct 11) - Send QR a raw data sample → tests/fixtures/raw/ (live + Tardis), described in docs/data.md §7 (Fri Oct 2; done Oct 11)
- Collector running with reconnect/backoff, sequence-gap detection, snapshot resync → ingest/coinbase_ws.py, first 1 h run on 2026-10-11 with 0 gaps (Tue Oct 6; done Oct 11)
- Raw append-only Parquet archive at
data/raw/{venue}/{symbol}/{date}/→ ingest/archive.py, ingest/tardis.py (Tue Oct 6; done Oct 11) - Repo tooling:
pyproject.toml(uv/poetry), ruff, mypy, pre-commit, CI skeleton →pyproject.toml+uv.lock,.pre-commit-config.yaml,.github/workflows/ci.yml(Tue Oct 6; done Oct 11)
Gate
- Both: interface contract v1 agreed (Fri Oct 2). Implemented as written (paths under
data/); needs Derek and Josh to confirm at the sync. - QE: 24h capture completed with gaps detected and resynced. Not yet run: a 1 h trial ran Oct 11 (0 gaps, 18 audits passed). Start the 24 h run with
caffeinate -i .venv/bin/python -m ingest.coinbase_ws --duration 93600(the Mac must stay awake), then runpython -m ingest.capture_reportand fill in docs/data.md §8. Gap → resync is tested end to end against a fake exchange. - QR: symbols, dates and holdout frozen
QR — Derek
- EDA notebook: spread and volatility profiles, intraday seasonality, message rates (Tue Oct 13). Written (research/notebooks/01_eda.ipynb); run
scripts/week2_pipeline.shafter the Tardis quota resets (~22:00 UTC daily) to download the rest of the in-sample data and execute it. - Data-quality review of the reconstructed book: gaps, crossed books, outliers (Fri Oct 9). BTC-USD and ETH-USD reviewed in research/data_quality.md;
scripts/week2_pipeline.shgenerates the tables for the other four symbols, which then need a read. - Univariate ICs of v1 features vs.
y_10s,y_30s,y_60s, logged intrials.csv(Tue Oct 13). research/studies/univariate_ic.py is ready;scripts/week2_pipeline.shruns it after the liquidity buckets are frozen; it refuses to run before. - Final list of features to build for Week 3, with exact definitions → research/week3_features.md (Fri Oct 9; done Oct 11)
- Hand-computed examples for the v1 features → research/hand_calcs/v1_features.md (spec §6, Fri Oct 9; done Oct 11)
QE — Josh
- L2 book reconstruction (snapshot + deltas) with invariant checks → book/ (Fri Oct 9; done Oct 11)
- Historical pipeline: raw → book → features, partitioned Parquet with version metadata (
code_git_sha,source_version,created_at) →ingest.tardis→book.build→features.build(Tue Oct 13; done Oct 11; the full-universe run waits on the Tardis quota) - v1 features: queue imbalance, spread, mid, realized vol, momentum → features/v1.py, labels in features/labels.py (Fri Oct 9; done Oct 11)
- Point-in-time query path via DuckDB or polars lazy scans → features/query.py (Tue Oct 13; done Oct 11)
Gate
- QE: golden-snapshot test passes with 0 mismatches. Tardis top-25: 299,559 snapshots / 15.0M levels; live exchange audits: 18 / 233,764 levels; both 0 mismatches (docs/data.md §6).
- QE: v1 features match QR's hand calculations (
tests/test_features_v1.py) - QR: EDA reviewed together at the sync
QR — Derek
- OFI, multi-level OFI and microprice signal study (Tue Oct 20)
- HAC (Newey-West) regressions for H1 (Fri Oct 16)
- IC by horizon and liquidity for H3; nested-model tests for H2 (Tue Oct 20)
- Hand-computed examples for each new feature, for QE's unit tests (Wed Oct 14)
QE — Josh
- Full feature set: OFI, multi-level OFI, depth imbalance, trade-flow imbalance, microprice, activity, time of day (Fri Oct 16)
- Streaming/incremental feature engine; batch mode = replay through it (Tue Oct 20)
- Trailing-only normalization; labels computed in a separate, clearly marked step (Fri Oct 16)
- Multi-symbol batch runs (Tue Oct 20)
Gate
- QE: leakage test passes (perturbing future events changes nothing at
t) - QE: throughput benchmark recorded (events/sec per stage)
- QR: H1–H3 preliminary results written up
QR — Derek
- Model ladder rungs 1–4: univariate IC → OLS/HAC → Ridge/Lasso/Elastic Net → logistic (Fri Oct 23)
- Gradient boosting (LightGBM), kept only if it beats the regularized linear model out of sample (Tue Oct 27)
- Walk-forward split design with purge and embargo, written as a spec for QE (Fri Oct 23)
- Backtester acceptance tests, including a toy scenario with a known P&L (Wed Oct 21)
QE — Josh
- Event-driven backtester v1: event queue, strategy interface, order management, portfolio (Fri Oct 23)
- Explicit latency model (decision + order-to-exchange) (Tue Oct 27)
- Execution Level 1: market orders cross the spread at the touch (Tue Oct 27)
- Sanity strategies: random signal, always-long, "cheating" future-label strategy (Tue Oct 27)
Gate
- QE: determinism test passes (same inputs → byte-identical outputs)
- QE: toy scenario gives QR's expected P&L
- QR: first
data/signals/{run_id}.parquetruns end to end through the backtester
QR — Derek
- Combine signals and define portfolio logic: thresholds, holding period, max position (Fri Oct 30)
-
configs/strategy.yamlv1 handed to QE (Fri Oct 30) - Gross vs. net analysis, break-even cost, cost sensitivity grid (H4) (Tue Nov 3)
QE — Josh
- Execution Level 2: walk the book for size (Fri Oct 30)
- Execution Level 3: limit orders with queue-position modeling; Level 4 adverse selection if time allows (Tue Nov 3)
- Pluggable cost model with
optimistic/realistic/pessimisticscenarios and square-root impact (Fri Oct 30) - Cost-sweep mode (0 → N bps) (Tue Nov 3)
Gate
- QE: queue-fill unit tests pass
- QE + QR: cost sweep produces a break-even curve
- QR: H4 preliminary result written up
QR — Derek
- Full walk-forward and out-of-sample results, holdout still untouched (Tue Nov 10)
- Model ladder comparison with Diebold-Mariano tests (Tue Nov 10)
- Run the bias checklist (QR §6) on every result so far (Tue Nov 10)
QE — Josh
- Profile with cProfile / py-spy / scalene and identify the real hotspots (Fri Nov 6)
- Optimize them (polars → NumPy → Numba → C++ only if still needed) (Tue Nov 10)
- Parallel walk-forward fold runner with purge/embargo support, plus a results store (Fri Nov 6)
-
docs/performance.mdwith before/after throughput (Tue Nov 10)
Gate
- QE: folds reproduce identically; N× speedup documented
- QR: bias checklist clean for all walk-forward results
QR — Derek
- Evaluate the final holdout exactly once, and record that in the paper (Fri Nov 13)
- Deflated Sharpe using the true trial count from
trials.csv; White's Reality Check / SPA (Tue Nov 17) - Stationary block bootstrap CIs on IC and Sharpe (Tue Nov 17)
- Regime splits (volatility, spread, time of day) and structural-break tests (CUSUM, Chow, Bai-Perron) for H5 (Tue Nov 17)
- Decay analysis: IC vs. horizon, rolling 20-day IC (Tue Nov 17)
QE — Josh
- Dockerfile + Docker Compose (Fri Nov 13)
- CI runs unit, golden, leakage, determinism and sanity-strategy tests (Fri Nov 13)
- Data versioning and config-driven runs; parameter-sweep API (Tue Nov 17)
-
Makefiletargets:data,features,backtest,walkforward,paper(Tue Nov 17)
Gate
- QE: CI green on a fresh clone
- QR: H1–H5 each have a result, test statistic, and p-value or CI
QR — Derek
- Paper draft (
paper/), 8–15 pages, following QR §8 (Fri Nov 20) - Every paper figure generated by a script in
research/studies/(Fri Nov 20) - Final paper, conclusions, and the README research section (Tue Nov 24)
- Review QE's engineering sections of the paper (Mon Nov 23)
QE — Josh
- FastAPI:
/runs,/runs/{id},/runs/{id}/trades,/compare,/features/{symbol}/{date},/health(Fri Nov 20) - Dashboard: gross vs. net equity, drawdown, rolling IC/Sharpe, cost sweep, trade scatter, run comparison, data health (Tue Nov 24)
-
docs/architecture.md,docs/data.mdfinalized; engineering sections of the paper (data, execution model, limitations) (Fri Nov 20)
Gate: Definition of Done
- Fresh clone →
docker compose up→make paperreproduces every result - Book reconstruction golden test: 0 mismatches
- Leakage, determinism and sanity-strategy tests pass in CI
- Execution Levels 1–3 and the cost sweep are implemented and documented
- Net-of-cost results reported with break-even cost
- Holdout evaluated exactly once; deflated Sharpe reported
- Bias checklist fully checked