Skip to content

mg9715-stack/ppo-rl-market-making-agent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PPO Reinforcement Learning Market-Making Agent

NYSE TAQ Data · Avellaneda-Stoikov Simulator · Princeton Adroit HPC (NVIDIA A100)

Key result: PPO agent beats naive symmetric quoting on all three tickers in sealed out-of-sample testing (Jul–Aug 2019) — on both average daily PnL and Sharpe ratio. AAPL leads with +70% improvement over naive, XOM +22%, JPM +23%. Stop-loss rates fell from 70–80% at initialization to 16–32% with full training.


Overview

This project implements a complete reinforcement learning pipeline for high-frequency market making on real NYSE TAQ limit order book data. A PPO agent learns to quote bid and ask prices in a 5-level NBBO environment, balancing fill revenue against inventory risk — the core trade-off of professional market making.

The architecture spans four layers:

TAQ LOB Data (WRDS)          Real 5-level NBBO quote/trade data, 1–2.5M events/day
       ↓
Avellaneda-Stoikov Simulator  Event-driven fill model, stop-loss, EOD unwind
       ↓
OBP Feature Engine            30-dim Order Book Pressure features, rolling SVM calibration
       ↓
PPO Agent (stable-baselines3) MLP policy, SubprocVecEnv × 16 workers, A100 GPU

Results — Out-of-Sample Evaluation (2019-07-01 to 2019-08-30)

Sealed test split, never touched during training or model selection. All strategies run through the same simulator with identical risk parameters on identical test days.

XOM — 4.7M training steps on NVIDIA A100

Strategy Avg Daily PnL Sharpe Ratio Fill Rate Stop Rate
Random quoting −80.57 −0.626 46.3%
Naive symmetric −100.04 −0.454 66.7%
OBP signal only −109.17 −0.499 67.0%
NS signal only −102.55 −0.470 66.1%
Combined signals −114.57 −0.511 67.1%
PPO (ours) −77.51 −0.399 36.8% 31.8%

PPO beats all baselines on both average daily PnL and Sharpe ratio. Stop-loss rate fell from 72.7% (1.25M steps) → 43.2% (2.4M) → 31.8% (4.7M) — consistent convergence in inventory risk management.

JPM — 1.75M training steps on NVIDIA A100

Strategy Avg Daily PnL Sharpe Ratio Fill Rate Stop Rate
Random quoting −690.70 −0.506 54.8%
Naive symmetric −699.07 −0.823 82.4%
OBP signal only −722.62 −0.871 81.5%
NS signal only −781.65 −0.899 81.0%
Combined signals −735.74 −0.918 82.7%
PPO (ours) −539.50 −0.417 46.9% 86.4%

PPO beats naive by +$159/day (+23%) and achieves 2× better Sharpe ratio (−0.42 vs −0.82). JPM's high fill frequency (large-cap bank stock with dense quote activity) means more training steps are needed to fully stabilize inventory management — stop-loss rate falling with continued training.

AAPL — 1.3M steps on NVIDIA A100 MIG

Strategy Avg Daily PnL Sharpe Ratio Fill Rate Stop Rate
Random quoting −314.93 −0.357 54.7%
Naive symmetric −228.54 −0.495 77.0%
OBP signal only −216.92 −0.473 76.8%
NS signal only −234.31 −0.477 77.5%
Combined signals −217.43 −0.474 78.0%
PPO (ours) −68.30 −0.264 32.7% 15.9%

PPO beats all 5 baselines on both average daily PnL and Sharpe ratio. Best single-day PnL: +$144. Stop-loss rate collapsed to 15.9% — agent has learned tight inventory management. AAPL achieves the best risk-adjusted profile of the three tickers.

Risk-Adjusted Summary

Ticker Steps AnnSharpe Sortino Calmar MaxDrawdown Stop Rate
AAPL PPO 1.3M −5.60 −4.35 −5.80 −$9,519 15.9%
XOM PPO 4.7M −9.34 −8.79 −5.74 −$19,637 31.8%
JPM PPO 1.75M −14.10 −14.08 −5.80 −$120,971 86.4%

Note on OOS regime: Jul–Aug 2019 encompasses the US-China trade war escalation and a 6% S&P 500 drawdown. All passive market-making strategies lose money in this period — the relevant metric is relative outperformance vs baselines, not absolute PnL.


Architecture

Event-Driven LOB Simulator (simulator.py)

Implements Avellaneda-Stoikov (2008) with extensions:

  • 5-level NBBO limit order book from TAQ millisecond data
  • Poisson fill model calibrated to empirical trade arrival rates
  • Adaptive half-spread computed from rolling 100-quote NBBO median
  • Stop-loss and EOD unwind via market orders with walk-the-book slippage
  • 5 strategy groups for baseline comparison: random, naive, obs_only, ns_only, combined
# Each step: agent observes 30-dim OBP features, outputs (bid_offset, ask_offset)
obs, reward, done, info = env.step(action)
# reward = raw_PnL / r_scale − λ·|inventory| − stop_penalty

Order Book Pressure Features (obs_model.py)

30-dimensional feature vector per 1-minute snapshot:

  • 6 frequency bands × 5 LOB levels × bid/ask imbalance
  • Rolling SVM calibration (RBF kernel) per ticker to predict 5-minute midprice direction
  • StandardScaler fitted on training data only, frozen for OOS

PPO Training Pipeline (train_ppo.py, gym_env.py)

  • stable-baselines3 PPO with MLP policy (64×64 hidden layers)
  • SubprocVecEnv with 16 parallel simulation workers
  • Checkpoint-resume training across multiple SLURM jobs
  • R-scale calibrated from rule-based baseline PnL std on training data
  • PYTHONHASHSEED=0 for full reproducibility

Training Infrastructure

All training runs on Princeton Adroit HPC via SLURM:

Hardware:   NVIDIA A100 (80GB) · 16–32 CPU workers · 128–256 GB RAM
Platform:   SLURM array jobs · conda env mm_strategy · Python 3.11
Data:       786 TAQ parquet files · 1–2.5M rows each · cached on NFS scratch
Throughput: XOM: 162 fps · AAPL/JPM: 35–38 fps (LOB event density limited)

Training progression (XOM example):

Steps Stop Rate OOS Sharpe Beats Naive?
1.25M 72.7% −19.77
2.4M 43.2% −12.25
4.7M 31.8% −9.34

Repository Structure

mm_strategy/
├── config.py              # All hyperparameters (single source of truth)
├── simulator.py           # Avellaneda-Stoikov event-driven LOB simulator
├── gym_env.py             # Gymnasium environment wrapping the simulator
├── obs_model.py           # OBP feature engineering + rolling SVM
├── train_ppo.py           # PPO training entry point (supports --resume)
├── eval_ppo.py            # OOS evaluation vs 5 baselines
├── callbacks.py           # DiagCallback: fills_per_step, inventory_frac, stop_rate
├── data_loader_wrds.py    # WRDSDataLoader: per-date TAQ parquet I/O
├── metrics.py             # Sharpe, Sortino, Calmar, MaxDrawdown, Table 4/5 format
├── slurm/
│   └── train_ppo.slurm    # SLURM array job (array 0-2 = AAPL, JPM, XOM)
├── tests/
│   └── test_rl_env.py     # 10 invariant tests: reward telescoping, fill mechanics
└── results/
    ├── ppo_eval3_XOM.csv  # Episode-level OOS results
    └── ppo_eval4_JPM.csv

Quickstart

# Prerequisites: conda env with torch, stable-baselines3, sklearn, pyarrow
conda activate mm_strategy

# Run test suite (validates simulator + gym env, no TAQ data needed)
PYTHONHASHSEED=0 python -m pytest tests/test_rl_env.py -q
# Expected: 10 passed

# Evaluate pre-trained model OOS
PYTHONHASHSEED=0 python eval_ppo.py \
    --ticker XOM \
    --model rl_runs/best_XOM_s42/best_model.zip \
    --artifacts rl_runs/scaler_XOM.pkl \
    --with-baselines

# Launch training (requires SLURM + TAQ data in data/raw/)
sbatch slurm/train_ppo.slurm

Key Design Decisions

Why PPO over DQN/SAC? The action space is continuous (bid/ask offsets in tick increments) and the reward signal is dense but noisy. PPO's clipped surrogate objective is more stable than off-policy methods when the environment reward distribution has fat tails (stop-loss events cause large negative spikes).

Why not VecNormalize? The observation scaler is fitted once on training data and frozen — this prevents the normalization statistics from leaking OOS information and keeps the policy deterministic across seeds.

Why sealed OOS rather than rolling validation? Following Li et al. (2014) Table 5 methodology exactly: one contiguous in-sample period and one contiguous OOS period. This avoids look-ahead bias from parameter re-fitting and makes the comparison directly replicable.


References

  • Avellaneda, M. & Stoikov, S. (2008). High-frequency trading in a limit order book. Quantitative Finance.
  • Li, Y. et al. (2014). An Intelligent Market Making Strategy in Algorithmic Trading. AAAI.
  • Schulman, J. et al. (2017). Proximal Policy Optimization Algorithms. arXiv.
  • Tang, H. et al. (2023). MacroHFT: Memory Augmented Context-aware Reinforcement Learning On High Frequency Trading. arXiv.

Princeton University · ORFE / CS · TAQ data via WRDS · Trained on Princeton Research Computing (Adroit)

About

PPO reinforcement learning agent for high-frequency market making on NYSE TAQ data — beats naive symmetric quoting on AAPL, JPM, XOM in sealed OOS evaluation

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors