Module G · Alpha: Finding & Validating an Edge - Chapter 31

Market Making & Optimal Execution

The other side of the trade - quoting both sides, managing inventory, and slicing big orders to hide impact.

NSE
What you'll learn
  • ·Providing liquidity
  • ·Inventory management
  • ·Adverse selection
  • ·VWAP & TWAP
  • ·Implementation shortfall
  • ·Transaction cost analysis

Every strategy in this module has been a liquidity taker - crossing the spread to get filled. This final strategy flips the perspective entirely: the market maker provides liquidity, quoting both sides and earning the spread as their fee for standing ready to trade. It's the other side of every order you've ever sent. And even if you never make markets, this chapter holds a lesson you cannot escape: how you execute a trade can matter as much as whether the trade was right. A great signal, badly executed, is a losing strategy.

The market maker's business

A market maker continuously posts a bid and an ask on an instrument, capturing the spread each time someone trades against them. Do this across thousands of instruments, millions of times a day, and tiny spreads add up to a real business - the professional, automated form of the liquidity provision we met in Chapter 5. The market maker isn't predicting direction; they're renting out immediacy to everyone who wants to trade now.

Inventory and adverse selection

But it's not free money - it's a tightrope over two pits. First, inventory risk: every fill leaves the maker holding a position they didn't want, accumulating directional exposure that the market can move against. Second, adverse selection (Chapter 9): the orders most eager to hit their quote are often the informed ones who know something - so the maker systematically trades against smarter money. The entire craft of market making is quoting wide enough to survive these two risks, yet tight enough to win the flow. It's a constant, automated balancing act.

Why execution matters for everyone

Here's why this concerns you even as a taker. When you trade size, you pay the spread and push the price (the market impact of Chapter 7). A clumsy execution - dumping a big order into a thin book - can leak far more than brokerage and STT combined. So every serious trader, market maker or not, needs to execute well: get filled close to a fair price without telegraphing their hand. And to know if you executed well, you need a benchmark.

VWAP and TWAP

The two standard benchmarks are VWAP and TWAP. Let's compute them on a real day of Reliance:

EX 1VWAP and TWAP benchmarksNSEch31/01_vwap_twap.py
# VWAP and TWAP - the execution benchmarks every trading desk is measured against.
import os
from datetime import datetime, timedelta

from openalgo import api

client = api(
    api_key=os.getenv("OPENALGO_API_KEY", "your_api_key_here"),
    host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)

end = datetime.now().strftime("%Y-%m-%d")
start = (datetime.now() - timedelta(days=6)).strftime("%Y-%m-%d")
df = client.history(symbol="RELIANCE", exchange="NSE", interval="5m",
                    start_date=start, end_date=end)
df = df[df.index.date == df.index[-1].date()]          # keep just the last trading day

df["typical"] = (df["high"] + df["low"] + df["close"]) / 3
vwap = (df["typical"] * df["volume"]).sum() / df["volume"].sum()
twap = df["close"].mean()

print(f"RELIANCE intraday, {len(df)} five-minute bars on {df.index[-1].date()}\n")
print(f"VWAP  : {vwap:.2f}   volume-weighted - more trades at a price, more weight (the benchmark)")
print(f"TWAP  : {twap:.2f}   time-weighted  - every bar counts equally")
print(f"Open  : {df['close'].iloc[0]:.2f}    Close: {df['close'].iloc[-1]:.2f}")
print("\nBeat VWAP and your execution desk earned its keep; lag it and you leaked money to the market.")
Live output
RELIANCE intraday, 75 five-minute bars on 2026-06-24

VWAP  : 1312.30   volume-weighted - more trades at a price, more weight (the benchmark)
TWAP  : 1313.22   time-weighted  - every bar counts equally
Open  : 1306.30    Close: 1313.40

Beat VWAP and your execution desk earned its keep; lag it and you leaked money to the market.

VWAP - the volume-weighted average price - weights each price by how much volume traded there, so it reflects where the bulk of the day's business actually happened. It's the benchmark execution desks are judged against: beat VWAP (buy below it) and you added value; lag it and you leaked money. TWAP - time-weighted - simply averages over time, treating every moment equally. The two differ here by about a rupee, the gap between "where most trading happened" and "the simple average".

Plot the running VWAP against the intraday price, with volume below, and you see exactly the line an execution algo is chasing all day:

EX 2Intraday price tracking VWAPNSEch31/02_execution_chart.py
# Intraday price with its VWAP - the line a good execution algo tries to match.
import os
from datetime import datetime, timedelta
from pathlib import Path

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import seaborn as sns
from openalgo import api

client = api(
    api_key=os.getenv("OPENALGO_API_KEY", "your_api_key_here"),
    host=os.getenv("OPENALGO_HOST", "http://127.0.0.1:5000"),
)

end = datetime.now().strftime("%Y-%m-%d")
start = (datetime.now() - timedelta(days=6)).strftime("%Y-%m-%d")
df = client.history(symbol="RELIANCE", exchange="NSE", interval="5m",
                    start_date=start, end_date=end)
df = df[df.index.date == df.index[-1].date()].copy()

df["typical"] = (df["high"] + df["low"] + df["close"]) / 3
df["vwap"] = (df["typical"] * df["volume"]).cumsum() / df["volume"].cumsum()

sns.set_theme(style="whitegrid")
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 6), sharex=True,
                               gridspec_kw={"height_ratios": [3, 1]})
ax1.plot(range(len(df)), df["close"], color="#7c83ff", lw=1.3, label="price")
ax1.plot(range(len(df)), df["vwap"], color="#dc2626", lw=2, label="running VWAP")
ax1.set_title(f"RELIANCE intraday vs VWAP ({df.index[-1].date()})")
ax1.set_ylabel("Price")
ax1.legend()
ax2.bar(range(len(df)), df["volume"], color="#16a34a", alpha=0.5)
ax2.set_ylabel("Volume")
ax2.set_xlabel("5-minute bar of the day")

out = Path(__file__).with_suffix(".png")
plt.savefig(out, dpi=110, bbox_inches="tight")
print(f"Final VWAP {df['vwap'].iloc[-1]:.2f}, close {df['close'].iloc[-1]:.2f}. Saved {out.name}")
Live output
Final VWAP 1312.30, close 1313.40. Saved 02_execution_chart.png
Intraday price tracking VWAP chart

The VWAP line is smooth and anchored - it barely moves on low-volume bars and shifts on the busy ones. An algo trying to "achieve VWAP" simply trades a little more whenever volume is high, so its own fills land right on that line.

Slicing the order

So how do you actually achieve a price near VWAP on a large order? You don't send it all at once - that would walk straight up the book (Chapter 7). You slice it into many small child orders dribbled out across the day:

Parent order big & impatient small child slices, trickled across the day average fill ≈ VWAP, impact hidden
Trickle it out - don't dump it in

A VWAP algo sizes its slices to match the day's volume profile (more when the market is busy); a TWAP algo spreads them evenly over time. Both hide a large order inside the natural flow, so it gets absorbed rather than announcing itself and moving the price against you.

Implementation shortfall

VWAP measures you against the day, but there's a stricter, more honest metric: implementation shortfall - the gap between the price when you decided to trade and the price you actually achieved, including everything: spread, impact, and the cost of delay (the market drifting away while you slice slowly). It captures the real, all-in cost of turning a decision into a position - and it forces the hard trade-off at the heart of execution: trade fast and pay impact, or trade slow and risk the price running away.

Transaction cost analysis

Finally, you close the loop with transaction cost analysis (TCA) - systematically measuring every execution against VWAP and implementation shortfall to see where you're leaking money, and feeding that back to improve. TCA is to execution what backtesting is to strategy: the rigorous measurement that turns a guess into a process. For a quant trading at any size, good execution isn't an afterthought - it's a measurable, improvable edge in its own right.

Key idea

A strategy's paper returns assume perfect fills; real returns are paper returns minus execution cost. Benchmark against VWAP, measure true cost with implementation shortfall, slice large orders to hide impact, and run TCA to improve. Many a "profitable" backtest dies entirely in the gap between the decision price and the fill.

Try it yourself

  • Compute your own slippage: if you'd bought Reliance at the open of each 5-minute bar, how far from VWAP would your average fill have landed?
  • Compare VWAP on a calm day versus a trending day. On which is it harder for a VWAP algo to do well?
  • Estimate implementation shortfall for a hypothetical order placed at the open and filled evenly through the day - how much did delay cost as the price drifted?

Recap

  • Market makers provide liquidity, quoting both sides to earn the spread - balancing inventory risk and adverse selection on a tightrope.
  • Even as a taker, execution matters: trading size leaks money through spread and impact, often more than explicit costs.
  • VWAP (volume-weighted) is the standard execution benchmark; TWAP (time-weighted) is its simpler cousin - beat VWAP and you added value.
  • Achieve a good price by slicing a large parent order into small child slices that hide inside the natural flow.
  • Implementation shortfall measures the true all-in cost from decision to fill, and TCA is the feedback loop that makes execution a measurable edge.

That completes Module G - the full toolkit of edges, from stat-arb to execution. All that remains is the discipline that decides whether any of it survives contact with reality. Module H is about rigor: backtesting without fooling yourself, using machine learning safely, the infrastructure behind it all, and the path to actually becoming a quant.