Insights

Intraday fills and VWAP: chasing a perfect-execution ceiling

·Vectopian · algolab, vwap, execution, backtesting, intraday, ruby

Most backtests quietly assume a convenient fill. Same clock every day. Same polite price. The equity curve looks calm because the execution model never had to fight the session.

This work sat in the early days of Ruby, our flagship algorithm for the Nasdaq-100 — expressed as a mix of TQQQ, SQQQ, and QQQ. Liquid, leveraged, and noisy enough that “fill at the convenient minute” was never a small assumption.

On names like that, the calm of a fixed fill is fake. Price can travel a long way between the open and the close — further, some days, than the edge your signal claims to have found. You can polish the daily decision forever and still leave money on the table if every order is stamped at the convenient minute on the chart.

So before we argued about tricks and overlays, we asked a colder question: what is the best case if execution inside the day were perfect?

Sizing the prize

The perfect-execution benchmark was built the blunt way — and only because it is not meant to be traded. On days Ruby was a buyer, we marked the fill at that day’s lowest price. On days it was a seller, we marked the fill at that day’s highest price. Same daily decisions as the normal backtest; only the fill print changed. Same cost assumptions either way.

That is an oracle, not a strategy. You only know the day’s low and high after the session is over. Nobody can systematically buy every low and sell every high. We used it as a ceiling: how much timing could matter if execution never missed inside the day.

We held the daily trading decisions fixed and compared that oracle fill to a normal fixed-time fill. On the paths we study, the gap was not a rounding error. It was large enough that execution stopped feeling like a footnote and started feeling like its own research problem.

When we put numbers on it, the shape was stark. In one sample window, normal fills sat around ~28% CAGR and ~1.2 Sharpe; perfect intraday timing on the same daily signals sat near ~66–68% CAGR and ~2.4–2.5 Sharpe — on the order of +38 percentage points of CAGR, with Sharpe roughly doubling. Per fill, the average edge versus the fixed-time print was about 1.6%, against an average window range near 3.5%. On a longer sample the ceiling only grew: normal fills near ~28% CAGR / ~1.0 Sharpe, perfect timing near ~88% CAGR / ~2.4 Sharpe — roughly +60 percentage points of CAGR sitting in timing alone.

Those oracle figures are not tradable. They exist to size the prize. And the prize was big enough that we stopped treating “fill at noon and move on” as good enough.

That is what pushed us to build an intraday optimization layer on top of early Ruby: the daily engine still decides what to hold in the TQQQ / SQQQ / QQQ mix; a separate layer researches when inside the day to act — aiming for an honest slice of the ceiling, not the whole fantasy.

Two reminders we kept:

  1. Closing a small slice of the ceiling can still matter — and is the only honest ambition.
  2. Closing a huge slice of the ceiling is usually a bug — lookahead, a leaked future bar, a fill that could not have been observed.

A volume-weighted average to hang timing on

With the ceiling known, fixed-time fills looked like leaving a large room empty. We needed something the session itself publishes as it unfolds — a path we could compare price against without peeking at the close.

Volume-weighted average price (VWAP) is exactly that object. From the open through any minute t in the day, take every trade (or every bar) so far, multiply its price by its volume, sum those products, and divide by total volume so far. The result is the average price of the auction weighted by how much actually traded, not a simple mid of high and low.

In symbols, if Pᵢ is the price of the i-th print (or bar) and Vᵢ its volume:

VWAPt  =  i=1t Pi Vi i=1t Vi

Cumulative from the session open: at 10:00 it only knows morning volume; by the close it is the full-day VWAP. Institutions often use it as a fair-value yardstick for a day’s work. For us it was a natural reference for an intraday layer — where is the last print relative to the volume-weighted path so far?

One session  price, VWAP, fixed-range volume profileIllustrative intraday window (e.g. TQQQ). Volume histogram on the left; candles and VWAP on the right.HighVWAPLowPOCVolume VWAPFixed-time fillBuy side of VWAPDay low (oracle buy)OpenSessionCloseVolume at price (profile)Session VWAPFixed clock fillOracle extreme
Figure 1. Illustrative session: candles and VWAP on the right; a fixed-range volume profile on the left — each horizontal bar is volume traded at that price. The longest bar is the point of control (POC), usually near VWAP when the auction is balanced. Day high / low mark the oracle extremes; a fixed clock fill ignores where the tape actually spent its volume.

So we explored families of timing ideas built around session VWAP — while Ruby’s daily engine kept deciding what to hold in the TQQQ / SQQQ / QQQ mix. The temptation is to treat VWAP as wisdom: fade extremes back to the average, wait for price to respect it, or somehow mark fills nearer to it. We ran those temptations through the same walk-forward discipline we use elsewhere.

What we observed along the way

Mean-reversion stories around VWAP did not earn their keep. Buying weakness toward the session average felt patient and classical. Out of sample it was mostly pain. In a wide screen of intraday techniques, that family sat among the losers. “Buy the dip to VWAP” was a narrative. The data did not sponsor it.

Going with the tape relative to VWAP was the posture that survived. When price and the session average agreed that the day had chosen a direction, waiting for that agreement before acting looked less foolish than fading it. This did not solve perfect execution. It was simply the VWAP-shaped idea that did not fall apart under walk-forward the way mean-reversion did.

What the 1-minute layer actually returned

After the screens, we kept a 1-minute price path and the VWAP posture that survived, as an execution layer on the same daily decisions. Costs matched the fixed-fill baseline. Only when inside the day changed.

Waiting for a better print only works if you still get the book done. If the VWAP condition never fires — or only fills part of the size — we do not leave the day with the wrong mix of TQQQ / SQQQ / QQQ. X minutes before the market close, the layer stops hunting and markets into the required portfolio weights at whatever the tape is offering. Timing is optional; ending the session at the target weights is not.

The lift showed up gradually depending where you look. On a clean hold-out window it was clearest: roughly +6 pp CAGR and about +0.17 Sharpe versus normal fixed-time fills (about 28% → 34% CAGR, Sharpe about 1.0 → 1.2). Full-sample and walk-forward stitches were smaller but still positive — on the order of +2 to +3.5 pp CAGR and about +0.07 to +0.10 Sharpe, depending on the date range.

Against the perfect-timing ceiling, that is only a modest capture — often low single digits to low teens of the oracle gap. We treat that as a feature. A few points of CAGR and a notch of Sharpe, earned without claiming the whole day. The remaining tens of points of oracle CAGR stay on the table on purpose: they remind us that timing still has room, and that “most of the ceiling” would be a red flag.

A dumb control stays honest. Sometimes “just fill earlier in the window” got close to the VWAP timing on hold-out. Say so when it happens — adaptation is not magic.

Upstream signal tweaks were small next to the timing gap. Polishing the daily signal moved results by pennies compared with the room between fixed fills and perfect fills.

What stuck

For early Ruby, start with the calm lie of fixed fills. Measure the perfect-day ceiling so you know the size of the room. Then try to close a slice — VWAP was one toolbox. Mean-reversion failed; respecting the tape relative to VWAP held up better; the 1-minute layer paid something real but modest. If the better print never comes, X minutes before the close you still force the target weights. And whatever you invent, if it suddenly owns most of that ceiling, check your timestamps before you check your champagne.