VWAP Execution Algorithm¶
Difficulty expert
Overview¶
VWAP (Volume Weighted Average Price) execution splits orders to match the market's volume profile, minimizing market impact.
vwap calculation¶
where:
Price_itrade price ·Volume_itrade size · summation over every trade in the chosen window. does: VWAP is both the benchmark (the volume-weighted average trade price) and the algo (slice the parent order to match the historical intraday volume curve — heavier near open and close, lighter midday). Pick VWAP for large, non-urgent orders where matching the market's natural participation profile minimizes impact and produces a defensible benchmark — the institutional default for liquidity-driven execution.
def calculate_vwap(prices, volumes):
"""Calculate VWAP."""
cumulative_pv = np.cumsum(prices * volumes)
cumulative_v = np.cumsum(volumes)
return cumulative_pv / cumulative_v
Algorithm Implementation¶
class VWAPExecution:
"""VWAP-based execution algorithm."""
def __init__(self, target_shares, start_time, end_time, volume_profile):
self.target = target_shares
self.remaining = target_shares
self.volume_profile = volume_profile # Expected volume by time interval
self.filled = 0
self.total_cost = 0
def get_target_participation(self, current_time):
"""Calculate target participation rate based on volume profile."""
interval = self.get_time_interval(current_time)
expected_volume_pct = self.volume_profile[interval]
return self.target * expected_volume_pct
def execute_interval(self, market_volume, current_price):
"""Execute for current time interval."""
target_qty = self.get_target_participation(current_time)
# Adjust for participation rate already achieved
if self.remaining > 0:
execute_qty = min(target_qty, self.remaining, market_volume * 0.1) # Cap at 10% of volume
self.filled += execute_qty
self.remaining -= execute_qty
self.total_cost += execute_qty * current_price
return execute_qty
def get_avg_price(self):
"""Calculate average execution price."""
return self.total_cost / self.filled if self.filled > 0 else 0
def get_slippage(self, benchmark_vwap):
"""Calculate VWAP slippage."""
avg_price = self.get_avg_price()
return avg_price - benchmark_vwap
Historical Volume Profile¶
def build_volume_profile(historical_data, n_intervals=390):
"""Build average volume profile from historical data."""
# 390 = minutes in US trading day
profiles = []
for day in historical_data:
profile = day['volume'].resample('1min').sum().values[:n_intervals]
profiles.append(profile / profile.sum()) # Normalize
avg_profile = np.mean(profiles, axis=0)
return avg_profile # Each value = % of daily volume for that minute
Performance Metrics¶
| Metric | Formula | Target |
|---|---|---|
| VWAP Slippage | Avg Exec Price - VWAP | Close to 0 |
| Participation Rate | Our Volume / Market Volume | Match profile |
| Completion Rate | Filled / Target | 100% |
| Market Impact | Price move due to our trading | Minimize |
Practical Guidelines¶
- Use for Large Orders — Best when order > 5% of ADV
- Avoid First/Last 15 Min — Volume profile is noisy
- Monitor Closely — Adjust if volume deviates from profile
- Combine with Passive — Use limit orders within VWAP framework
- Track Slippage — Measure against actual VWAP
Next Steps¶
- TWAP — Time-weighted alternative
- Implementation Shortfall — Cost-aware execution
- Smart Order Routing — Multi-venue execution