Optimal Execution Algorithms: TWAP, VWAP & Market Impact › Putting It All Together: An Execution Simulator

Michael BrenndoerferJanuary 20, 20254 min read
Quantitative FinanceAll chapters
Part 12 of 15Market Making and High-Frequency Trading
Chapter 68Optimal Execution Algorithms: TWAP, VWAP & Market Impact › Putting It All Together: An Execution Simulator
Reading section 7 of 10 · Putting It All Together: An Execution Simulator · 4 minSection 7 of 10 · 4 min

Book contents

Section 7 of 10 · 4 min read

Optimal Execution Algorithms: TWAP, VWAP & Market Impact › Putting It All Together: An Execution SimulatorOne section · Sections in this chapter7 / 10 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

To solidify these concepts, let's build a complete execution simulation that compares algorithm performance across different market scenarios.

In[21]:
Code
class ExecutionSimulator:
    """
    Simulates execution of different algorithms against
    realistic price dynamics with market impact.
    """

    def __init__(
        self,
        initial_price: float,
        volatility: float,
        permanent_impact: float,
        temporary_impact: float,
        num_periods: int,
    ):
        self.S0 = initial_price
        self.sigma = volatility
        self.gamma = permanent_impact
        self.eta = temporary_impact
        self.num_periods = num_periods

    def simulate_execution(
        self, schedule: np.ndarray, seed: int = None
    ) -> dict:
        """
        Simulate execution and compute performance metrics.

        Returns execution prices, costs, and statistics.
        """
        if seed is not None:
            np.random.seed(seed)

        n = len(schedule)
        prices = [self.S0]
        execution_prices = []

        # Price follows Brownian motion with impact
        for i in range(n):
            shares = schedule[i]

            # Permanent impact shifts fundamental price
            permanent_shift = self.gamma * shares

            # Random price movement
            random_shock = self.sigma * np.random.randn() / np.sqrt(n)

            # New fundamental price
            new_price = prices[-1] + permanent_shift + random_shock
            prices.append(new_price)

            # Execution occurs at fundamental + temporary impact
            if shares > 0:
                temp_impact = self.eta * shares
                exec_price = new_price + temp_impact
                execution_prices.append((shares, exec_price))

        # Compute metrics
        total_shares = sum(s for s, _ in execution_prices)
        if total_shares > 0:
            vwap_exec = sum(s * p for s, p in execution_prices) / total_shares
        else:
            vwap_exec = self.S0

        implementation_shortfall = (vwap_exec - self.S0) * total_shares

        return {
            "prices": np.array(prices),
            "execution_prices": execution_prices,
            "average_price": vwap_exec,
            "implementation_shortfall": implementation_shortfall,
            "arrival_price": self.S0,
            "final_price": prices[-1],
        }


# Set up simulation parameters
sim = ExecutionSimulator(
    initial_price=100.0,
    volatility=2.0,  # $2 volatility over execution period
    permanent_impact=1e-5,  # Price moves $0.01 per 1000 shares permanently
    temporary_impact=2e-5,  # Additional $0.02 per 1000 shares temporarily
    num_periods=40,
)

# Generate different schedules
order_size = 100_000
num_periods = 40

schedules = {
    "TWAP": generate_twap_schedule(order_size, num_periods),
    "VWAP": generate_vwap_schedule(
        order_size, create_intraday_volume_profile(num_periods)
    ),
    "IS-Patient": implementation_shortfall_schedule(
        order_size, num_periods, 0.3
    ),
    "IS-Urgent": implementation_shortfall_schedule(
        order_size, num_periods, 0.8
    ),
}

import numpy as np

# Run multiple simulations for each schedule
num_simulations = 500
results = {name: [] for name in schedules}

for name, schedule in schedules.items():
    for seed in range(num_simulations):
        result = sim.simulate_execution(schedule, seed=seed)
        results[name].append(result["implementation_shortfall"])
Out[22]:
Visualization
Box plot comparing implementation shortfall distributions for TWAP, VWAP, IS-Patient, and IS-Urgent algorithms.
Distribution of implementation shortfall across 500 Monte Carlo simulations comparing four execution algorithms. Median shortfalls are similar in this parameterization, while the dispersion narrows from the clock-based schedules toward IS-Urgent. The comparison shows that algorithm ranking depends on the assumed impact, volatility, and urgency parameters rather than on a universal winner.
In[23]:
Code
import numpy as np

performance_metrics = []
for name in schedules.keys():
    mean_is = np.mean(results[name])
    std_is = np.std(results[name])
    # Negative because lower IS is better
    sharpe_like = -mean_is / std_is if std_is > 0 else 0
    performance_metrics.append((name, mean_is, std_is, sharpe_like))
Out[24]:
Console
Algorithm Performance Summary:
------------------------------------------------------------
Algorithm       Mean IS ($)     Std Dev ($)     Sharpe-like    
------------------------------------------------------------
TWAP            58,271.74       119,247.10      -0.489         
VWAP            58,184.59       118,062.57      -0.493         
IS-Patient      58,616.04       107,360.20      -0.546         
IS-Urgent       60,791.98       88,313.35       -0.688

The simulation reveals the trade-offs inherent in execution algorithm selection. TWAP provides stable, predictable performance but misses opportunities to reduce timing risk. The urgent IS algorithm achieves the lowest average shortfall by completing execution quickly, but exhibits higher variance because it's more sensitive to the initial price movements. The "Sharpe-like" ratio (negative mean shortfall divided by standard deviation) provides a risk-adjusted measure of execution quality.