Transaction Cost Analysis (TCA) System Architecture (2026)

Transaction Cost Analysis (TCA) System Architecture (2026)

Transaction Cost Analysis (TCA) System Architecture (2026)

A trade report that says “you beat VWAP by 1.8 basis points” is a claim about a counterfactual, and counterfactuals are only as good as the plumbing that builds them. Transaction cost analysis (TCA) compares what an execution actually cost against what it would have cost under some reference, and every number it emits depends on three fragile things: which timestamps you trust, which quote you pair with each fill, and which benchmark you let judge the result. Get any of them wrong and the report stays precise, confident, and misleading, because it grades the very algorithms and brokers that route your flow.

This matters more in 2026 than it did five years ago. Best-execution evidence is now a data-engineering deliverable in both the US and the EU, algorithm wheels are replacing relationship-driven broker allocation, and the rules are still moving. You can leave this post with a buildable design: the pipeline, the data contract, a worked slippage decomposition you can reproduce by hand, an impact-model primer, runnable code, and the failure modes that corrupt results.

What this covers: pre-, intra-, and post-trade TCA; benchmarks and their conflicts of interest; implementation shortfall math; square-root and Almgren-Chriss impact models; FIX capture and clock synchronization; kdb+, ClickHouse, and Parquet storage; regulatory status; algo-wheel statistics; and failure modes. This is systems analysis, not investment advice.

Context and Background

Andre Perold introduced implementation shortfall in 1988 as the gap between a paper portfolio that trades instantly at the decision price and the real portfolio that trades over time with friction. Nearly every TCA system still orbits that idea, even when its front-end reports VWAP or participation-weighted slippage instead. The formalization most engineers meet next is the Almgren-Chriss framework (Robert Almgren and Neil Chriss, published in the Journal of Risk, with working papers from 1999), which treats execution as a trade-off between impact cost from trading fast and timing risk from trading slowly.

Regulation turned measurement into obligation. MiFID II requires firms to take “all sufficient steps” to obtain the best possible result for clients and to demonstrate it. In the US, SEC Rules 605 and 606 prescribe execution-quality statistics and order-routing disclosures. For an architect these texts read like data-contract specifications: capture every order event with a trustworthy timestamp, attribute it to a venue, and reconstruct what a fair price was at that moment. Section “Regulation Without the Fog” below gives the status of each regime as of this writing, because several pieces changed recently.

The market splits into two camps. Vendor platforms offer multi-asset coverage, peer universes, and pre-built regulatory reports, but you inherit their benchmark definitions and their data assumptions. In-house builds cost more but give you impact models calibrated to your own flow and attribution you can audit line by line. The deciding question is whether TCA must be a compliance artifact or a feedback loop. A vendor report satisfies the first; only a system you control can satisfy the second, because feedback needs access to raw events, not summary PDFs.

Most desks already operate the upstream pieces, such as an implementation shortfall execution algorithm and a router. TCA is what tells those systems whether they worked. Two properties distinguish a good build from a report generator.

First, raw events are immutable and benchmarks are versioned views. If someone later changes how arrival price is defined, last quarter’s numbers should be reproducible under both the old and the new definition. Second, every number carries its uncertainty: the clock quality of the timestamps involved, the staleness of the paired quote, and whether the order was large enough to contaminate its own benchmark. The rest of this post builds toward those two properties.

Reference Architecture for a TCA Pipeline

A TCA platform answers one question in four time horizons: given what the market looked like around each event, what did this order cost, and why? Structurally it is a linear pipeline in which each stage owns one transformation: ingest and validate, normalize and sessionize, as-of join to market data, compute benchmarks, attribute slippage, aggregate, and report. Correctness at the end depends on timestamps and joins established near the start, which is why the complexity budget belongs in ingestion and the join, not in the formulas.

Transaction cost analysis TCA pipeline from order and fill ingest through as-of join, benchmark engine, slippage attribution, warehouse and impact-model feedback

Figure 1: The TCA pipeline. Four source feeds converge on ingest; sessionized orders are joined to quotes, priced against benchmarks, decomposed, stored, and fed back into impact-model calibration.

The figure shows the flow left to right. Orders and fills enter from the OMS/EMS and a FIX drop-copy, market data enters as NBBO quotes and a consolidated trade tape, and reference data (venue identifiers, trading calendars, tick-size regimes, corporate actions) provides context. The dashed line at the end is the feedback edge: realized costs flow back to calibrate the impact model that drives pre-trade forecasts. Without that edge the system is a scorecard.

Ingest, normalize, and sessionize

Ingest validates every record against a schema and quarantines malformed ones instead of letting them poison aggregates. It also runs timestamp sanity checks: a fill stamped before its parent order, a quote dated to a closed session, or a negative latency between two hops is a defect to surface, never to absorb silently. Quarantine rates are themselves a health metric; a sudden jump from 0.01% to 0.5% usually means an upstream FIX dialect change, not a market event.

Normalization maps each venue’s symbology, price conventions, and FIX dialect onto one canonical model, so a fill from any venue is comparable with any other. Prices may be quoted in different increments; quantities may be in lots; currencies need explicit conversion with a rate captured at the right time. Normalization is also where you decide, once, how to represent corporate actions so that a split mid-day does not look like a 50% adverse move.

Sessionization is the subtle stage. It stitches child executions back to the correct parent across partial fills, replaces, cancel-replace chains, and order-ID changes at the venue. Get it wrong and one parent fragments into several phantom orders, each with a bogus arrival price, or two parents merge and share one. The FIX tags that make it tractable are covered below; the failure mode to remember is that cancel-replace chains change ClOrdID (tag 11) while preserving OrigClOrdID (tag 41), so a naive group-by on ClOrdID silently splits orders.

The as-of join is the technical heart

For every fill at time t, the system must find the most recent quote at or before t and never the quote after it. Doing this across billions of rows means time-ordered, symbol-partitioned storage and a join that walks two sorted streams in lockstep. The easy bug is a look-ahead join, which pairs a fill with the next quote. It is dangerous because it is systematically biased: for a buy that lifts the offer, the next quote is typically higher, so the benchmark moves in the algorithm’s favor and every report looks slightly better than reality.

That asymmetry is the main reason clock quality matters, and it is worth quantifying, because the usual story (“50 ms of skew will flip results”) is overstated. Take an illustrative stock with 2% daily volatility over a 23,400-second session. Per-second volatility is about 2% / sqrt(23,400), roughly 1.3 basis points. Over 50 ms the random price move is about 1.3 × sqrt(0.05), or 0.3 basis points. Symmetric clock noise of that size averages out across thousands of orders. What does not average out is a one-directional skew, such as a market-data clock running 50 ms ahead of the order clock, or a look-ahead join, because those shift every benchmark the same way. The architecture’s job is to make bias impossible by construction and to keep noise small enough that per-order outlier analysis still means something.

Pre-trade, intra-trade, and post-trade from one lineage

TCA runs in three regimes that share code but not purpose. Pre-trade TCA is a forecast: given order size relative to average daily volume (ADV), expected spread, and volatility, estimate cost and suggest a strategy. Intra-trade TCA monitors a live order against its schedule and flags drift while the order can still be steered. Post-trade TCA is the forensic record that feeds compliance and strategy review.

All three must draw on one data lineage. If forecasts use a vendor’s volume curves while actuals come from your own tape, the residual between predicted and realized cost mixes model error with data-source mismatch, and you can never tell which one to fix. When all three read the same normalized order and fill records, that residual becomes a clean training signal for the impact model. The three regimes differ only in horizon and in which market data is available: forecasts read historical profiles, intra-trade reads the live tape, and post-trade reads the full realized tape.

Direct answer: a TCA system reconstructs the prevailing market at each fill, prices a set of benchmarks against the actual executions, decomposes the difference into delay, timing, impact, and opportunity costs, and aggregates the result so a desk can see where money leaked and whether the fix worked.

Latency and freshness budgets

Post-trade TCA is a batch workload, but intra-trade is not, and the budgets differ by orders of magnitude. For a daily post-trade run, the SLA is usually “by the next morning,” which leaves hours for backfills and late-arriving corrections. For intra-trade monitoring, the useful horizon is the update cadence of the algorithm itself: a participation algorithm rebalancing every few seconds can be steered by a monitor that refreshes every second or two, but a monitor that lags by a minute is only forensic. A practical split is a streaming path for live parent orders (incremental VWAP, running slippage versus arrival, participation rate) backed by the same functions as the batch path, so the two cannot disagree on definitions. Sharing code is the cheap form of consistency; the expensive form, reconciling two implementations, never ends.

Benchmarks, Slippage Math, and Impact Models

A TCA system never picks one benchmark. It computes several and lets the user choose by context, because each measures something different and each can be gamed in its own way. The deeper point, and the thesis of this post, is that a benchmark is an assignment of blame. Arrival price blames everything after the order reached the desk on the execution. Interval VWAP blames only the deviation from the average over the order’s own life, which quietly forgives the cost of the order’s own footprint when the order was large enough to move that average.

The benchmark menu

Benchmark What it measures Reference price Best for Main weakness
Arrival price / implementation shortfall Total cost versus the decision Mid at order arrival or decision Alpha-sensitive, urgent orders Punishes bad timestamps; ignores unfilled shares unless opportunity cost is added
Full-day VWAP Adherence to the volume profile Volume-weighted price over the session Large, schedule-driven orders Large orders move their own benchmark
Interval VWAP Cost over the execution window VWAP during the order’s life Sub-day passive execution Window must be defined honestly; contaminated by own volume
TWAP Even-pacing adherence Time-weighted price Orders on a fixed clock Ignores liquidity shape
PWP (participation-weighted price) Cost at a target participation rate Price over the volume the order would have participated in Liquidity-constrained orders Flattered by under-trading
Close Tracking-error relevance Official closing print Index and benchmark-tracking funds Concentrates risk in the closing auction

Arrival price underpins implementation shortfall and is the most decision-relevant, but it is also the most sensitive to timestamp quality. VWAP is forgiving and intuitive; its structural flaw is self-reference. An order that is 20% of the day’s volume is a fifth of the VWAP it is being compared with, so the benchmark is partly the order grading itself. A reliable VWAP execution algorithm will track that benchmark closely by construction, which says little about whether it was a good idea to track it. The mature approach is to compute both an arrival-based and an interval-based cost and treat divergence between them as a signal: a small VWAP slippage with a large arrival slippage means the algorithm was punctual but the market ran away before it started.

A useful refinement is to exclude the order’s own volume from the interval VWAP when computing the benchmark (“VWAP ex-own”). It is a one-line change in the engine and it removes most of the self-reference problem, at the price of needing child-fill volume tagged to the parent on the tape. This is another reason sessionization has to be correct before anything else.

Worked example: implementation shortfall by hand

The numbers below are illustrative, chosen so the arithmetic closes exactly. A manager decides to buy 100,000 shares at a decision price of 100.00, so the paper notional is 10,000,000. The order reaches the desk when the mid is 100.05 (arrival price). The algorithm fills 90,000 shares at an average price of 100.20 while the interval VWAP, over the order’s life, is 100.12. The remaining 10,000 shares go unfilled and the stock closes at 100.40.

Implementation shortfall slippage decomposition from decision price 100.00 through arrival 100.05, interval VWAP 100.12 and average fill 100.20 plus opportunity cost to close 100.40

Figure 2: Slippage decomposition for the worked example. Delay, timing, and impact cost sum over the executed shares; opportunity cost marks the unfilled remainder against the close; the total is 22.0 basis points of paper notional.

Total shortfall comes from comparing portfolios. The paper portfolio buys all 100,000 at 100.00 and is marked at the close of 100.40, a gain of 0.40 × 100,000 = 40,000. The real portfolio owns 90,000 shares bought at 100.20 and marked at 100.40, a gain of 0.20 × 90,000 = 18,000. The shortfall is 40,000 − 18,000 = 22,000, or 22.0 basis points of 10,000,000. The components on the executed 90,000 shares are:

  • Delay: (100.05 − 100.00) × 90,000 = 4,500, or 4.5 bps. The move between decision and desk arrival.
  • Timing: (100.12 − 100.05) × 90,000 = 6,300, or 6.3 bps. Drift from arrival to the interval VWAP, which includes market-wide movement during the execution window.
  • Market impact: (100.20 − 100.12) × 90,000 = 7,200, or 7.2 bps. The average fill versus the interval VWAP.
  • Opportunity: (100.40 − 100.00) × 10,000 = 4,000, or 4.0 bps. The notional gain forgone on the unfilled shares.

The sum, 4,500 + 6,300 + 7,200 + 4,000, is 22,000 exactly. Explicit costs (commissions, fees, taxes) add as a separate line; at an illustrative 0.3 cents per share on 90,000 shares that would be 270, or 0.27 bps. Two caveats belong in the report footnotes. First, “impact” measured against interval VWAP understates true impact when the order is a large share of interval volume, because its own prints raised that VWAP. Second, delay depends on what you call the decision time, which many firms cannot timestamp precisely because the portfolio manager’s decision happens in a conversation, not in a system. A system that cannot produce a decision timestamp should report shortfall from arrival and label it so, instead of inventing a decision price.

Because attribution is additive, roll-ups are honest sums. A desk’s quarterly shortfall is the notional-weighted sum of component basis points across orders, and the breakdown by delay, timing, impact, and opportunity points to different owners: delay to the workflow between PM and desk, timing to scheduling decisions, impact to algorithm and venue choices, opportunity to limit-price discipline.

Market impact models: square-root law and Almgren-Chriss

Pre-trade TCA needs a cost forecast, and two models do most of the work. The empirical workhorse is the square-root law: the average price move caused by executing a large order scales roughly with volatility times the square root of the order size relative to volume. In symbols, impact ≈ Y × σ × sqrt(Q / V), where σ is daily volatility, Q is the order size, V is the daily volume, and Y is a constant of order one that you calibrate from your own fills. Tóth, Lemperiere, Deremble, de Lataillade, Kockelkoren, and Bouchaud (Physical Review X, 2011) discuss why this concave shape arises, attributing it to low local liquidity near the current price. For a purely illustrative case, take σ = 2%, Q/V = 5%, and a calibrated Y of 0.5. Then impact ≈ 0.5 × 2% × sqrt(0.05) = 0.5 × 2% × 0.2236 ≈ 0.22%, or about 22 basis points. Doubling the order to 10% of volume does not double the cost; it multiplies it by sqrt(2), about 1.41, which is why splitting across days helps less than intuition suggests and why per-order cost curves are concave.

The Almgren-Chriss model is a structural alternative that yields a schedule, not just a cost. It models execution of X shares by deadline T with a permanent impact that moves the equilibrium price and a temporary impact (linear in trading rate, with coefficient η in the simple form) that affects only the fill price. The trader minimizes expected cost plus λ times variance of cost. With temporary impact only, the optimal remaining-holdings trajectory in continuous time is x(t) = X × sinh(κ(T − t)) / sinh(κT), with κ = sqrt(λσ² / η). When risk aversion λ is zero, κ goes to zero and the schedule becomes linear, which is TWAP. As λ grows, κ grows and the schedule front-loads, trading faster to cut variance at the price of more temporary impact. Tuning λ is therefore a statement about how much timing risk the desk tolerates.

The practical point for TCA architecture is that both models are fitted and checked against the same attribution rows the system already produces. The square-root model needs (Q/V, σ, realized impact) triples per order; Almgren-Chriss needs temporary-impact estimates from child-slice behavior, which requires child-level timestamps. A pre-trade model that cannot be back-tested against post-trade actuals will drift, so store the model’s prediction in the warehouse next to the realized outcome and publish the residual distribution by size bucket.

Here is a compact Python reference for the decomposition and the Almgren-Chriss schedule. It reproduces the worked example above and runs with only NumPy.

import numpy as np

def shortfall_components(side, decision, arrival, interval_vwap,
                         avg_fill, close, order_qty, filled_qty):
    """Perold-style implementation shortfall decomposition, in currency.
    side = +1 buy, -1 sell. Returns dict of components and bps of paper notional."""
    s = side
    unfilled = order_qty - filled_qty
    delay = s * (arrival - decision) * filled_qty
    timing = s * (interval_vwap - arrival) * filled_qty
    impact = s * (avg_fill - interval_vwap) * filled_qty
    opportunity = s * (close - decision) * unfilled
    total = delay + timing + impact + opportunity
    paper = decision * order_qty
    comp = dict(delay=delay, timing=timing, impact=impact,
                opportunity=opportunity, total=total)
    comp_bps = {k: 1e4 * v / paper for k, v in comp.items()}
    return comp, comp_bps

c, b = shortfall_components(+1, 100.00, 100.05, 100.12, 100.20, 100.40,
                            100_000, 90_000)
print(c)   # delay 4500, timing 6300, impact 7200, opportunity 4000, total 22000
print({k: round(v, 2) for k, v in b.items()})  # total 22.0 bps

def almgren_chriss_holdings(X, T, sigma, eta, lam, n=10):
    """Remaining holdings x(t) at n+1 grid points, temporary-impact-only,
    continuous-time solution. sigma per sqrt(time), eta per share per time."""
    kappa = np.sqrt(lam * sigma**2 / eta)
    t = np.linspace(0, T, n + 1)
    if kappa * T < 1e-9:                      # lam -> 0: TWAP
        return t, X * (1 - t / T)
    return t, X * np.sinh(kappa * (T - t)) / np.sinh(kappa * T)

t, x = almgren_chriss_holdings(X=100_000, T=1.0, sigma=0.02, eta=2.5e-6, lam=1e-4)
print(np.round(x).astype(int))

Units must be consistent between σ, η, and T when you use the schedule in production; the snippet is a reference for the shape, not a calibrated model.

Data Capture, Clocks, and Storage

None of the above is computable without a strict data contract. The inputs are parent and child orders with full lifecycle events, fills from a drop-copy independent of the trading path, market data fine enough to reconstruct the book around each fill, venue tags, and timestamps from synchronized clocks at every hop.

FIX capture and the tags that matter

Drop-copy is a FIX session that mirrors execution reports to a second consumer, so the audit record cannot be altered by the EMS. An execution report (MsgType 8) carries most of what TCA needs. The following tags are the working set for sessionization and attribution:

Tag Name Why TCA needs it
11 / 41 ClOrdID / OrigClOrdID Chain replaces and cancel-replaces back to one parent
37 / 17 OrderID / ExecID Deduplicate and order execution reports
150 / 39 ExecType / OrdStatus Distinguish fills, partials, cancels, rejects, busts
31 / 32 LastPx / LastQty The fill itself
30 LastMkt Venue attribution (MIC)
29 / 851 LastCapacity / LastLiquidityInd Agency versus principal; add versus remove liquidity
60 TransactTime Venue or broker event time
52 SendingTime Session-layer time, used to estimate latency, not as event time

Two disciplines make this data usable. First, store both TransactTime and your own capture time, and record which clock produced each. The difference between them is a latency estimate and a data-quality indicator. Second, treat trade busts and corrections (ExecType changes, ExecRestatement) as first-class events; a TCA system that ignores busts reports fills that legally never happened. FIX timestamps support millisecond, microsecond, and nanosecond precision variants, but precision is not accuracy: a nanosecond-formatted timestamp from an unsynchronized host is just a precisely formatted guess.

Clock synchronization is the foundation

If the OMS stamps fills from one clock and the market-data capture stamps quotes from another, the as-of join compares prices across an unknown offset. The infrastructure answer is Precision Time Protocol (IEEE 1588) disciplining clocks at every capture point from a GNSS-referenced grandmaster, with the clock source and estimated error recorded alongside each timestamp. In Europe, MiFID II’s RTS 25 (Commission Delegated Regulation (EU) 2017/574) sets maximum divergence from UTC by activity type. As summarized by a vendor source I could not cross-check against the regulation text, the tiers are 100 microseconds for high-frequency algorithmic trading, 1 millisecond for other algorithmic activity, and 1 second for voice and manual trading; confirm the figures against the regulation before relying on them. In the US, the Consolidated Audit Trail clock requirement is reported at 50 milliseconds from NIST UTC for most order events, again from a secondary source, so verify against current CAT rules.

Event timeline sequence of a parent order from decision through arrival, child slice, venue fill and drop-copy to the TCA capture and as-of join

Figure 3: Event timeline for one order. Every cost component is defined by the gap between two events, so skewed or misordered timestamps corrupt attribution directly.

The sequence shows why. Delay is t1 − t0, impact is measured at fills t3 against quotes prevailing at t2 and t3, and the TCA capture joins on the drop-copy’s TransactTime rather than on its own arrival time. A practical rule: any record whose clock source cannot be traced to a synchronized reference gets a quality flag, and reports surface the share of flagged notional instead of hiding it.

Storage: tick store plus columnar warehouse

The benchmark engine and the reporting layer have opposite access patterns. Benchmark computation needs fine-grained, time-ordered access: reconstruct the NBBO at a microsecond, scan every print in an interval to build VWAP, replay the book around a fill. That is a tick store, partitioned by symbol and date, optimized for as-of joins and range scans. Reporting asks wide analytical questions: average shortfall by desk this quarter, cost by venue and algorithm, outliers above a threshold. That is a columnar analytics warehouse.

Three realistic stack choices fit these needs. kdb+ remains the incumbent for tick data, with an as-of join operator (aj) that is native to the language and very fast on sorted, partitioned tables, but it carries licensing cost and a specialist skill requirement. ClickHouse supports ASOF JOIN in SQL and delivers strong columnar compression and aggregation at lower cost, with the usual caveat that you design the sort key (symbol, time) deliberately. A lakehouse approach stores Parquet with zstd compression and queries via DuckDB or a Spark/Trino engine, which is cheapest and most open, and DuckDB also supports ASOF JOIN; the trade-off is slower interactive point lookups than a purpose-built tick database.

A back-of-envelope sizing, labeled illustrative: assume 8,000 symbols averaging 60,000 NBBO updates a day. That is 480 million quote rows daily. At roughly 40 bytes of raw payload per row, that is about 19 GB a day raw; columnar compression of around 5x brings it near 4 GB a day, roughly 1 TB a year for quotes alone, before trades and depth. Actual quote counts vary widely by symbol and by market-data tier, so measure your own feed before sizing.

-- ClickHouse: pair every fill with the prevailing quote, never a later one.
SELECT
    f.parent_id, f.exec_id, f.symbol, f.ts AS fill_ts,
    f.side, f.px AS fill_px, f.qty,
    q.ts AS quote_ts,
    (q.bid + q.ask) / 2 AS mid,
    f.ts - q.ts AS quote_age_us,
    if(f.side = 'B', f.px - mid, mid - f.px) / mid * 1e4 AS slippage_vs_mid_bps
FROM fills AS f
ASOF LEFT JOIN quotes AS q
    ON f.symbol = q.symbol AND f.ts >= q.ts;

The f.ts >= q.ts condition is the entire anti-look-ahead guarantee, and quote_age_us is the uncertainty column: a fill paired with a quote 800 milliseconds old is evidence of a stale feed or a thinly traded name, and should be flagged. The same query in kdb+ is a single aj call keyed on symbol and time. In pandas, merge_asof(..., direction="backward") gives the equivalent for small experiments; never use direction="nearest" for TCA, since it will happily pick a future quote.

Regulation Without the Fog

Best-execution regulation is useful to an architect because it reads as a free requirements document. The status of each regime matters, though, and several changed recently, so here is what I could verify and what to re-check.

EU, MiFID II. The duty to take all sufficient steps to obtain the best possible result for clients remains in force. The periodic publications are what changed. The RTS 28 report, in which investment firms disclosed their top five execution venues and quality obtained, was effectively dropped: ESMA stated on 13 February 2024 that it no longer expected national competent authorities to prioritize supervision of the obligation, ahead of the MiFID/MiFIR review that deletes it once transposed into national law (trade press reported an 18-month transposition window). The venue-side RTS 27 execution-quality reports were suspended earlier and the review removes them as well, though I did not verify the current legal text for each member state, so check your jurisdiction’s status. The practical lesson: do not tear out the data capture. The evidence duty persists, regulators can still ask for it, and the same data feeds your own algo-wheel analytics.

US, Rule 605 and Rule 606. Rule 605 requires market centers (and, under the 2024 amendments, larger broker-dealers) to publish standardized execution-quality statistics such as effective spread and price improvement. Rule 606 requires broker-dealers to disclose order-routing practices and, on request, customer-specific routing reports. The compliance date for the amended Rule 605 was originally 14 December 2025; per an SEC order of 30 September 2025 it moved to 1 August 2026, and a further SEC order dated 1 April 2026 (Release No. 34-105136) adjusted several exemptions while leaving that date as referenced. Because compliance dates in this area have shifted more than once, confirm the live status before you commit an engineering schedule to it. Whichever way the date falls, the data contract does not change.

Designing the warehouse so these reports are queries over existing tables, not bespoke pipelines, is the difference between a system that survives an audit cheaply and one that needs a fire drill every reporting period. Concretely: keep a venue dimension keyed on MIC, a capacity column from LastCapacity, an order-type classification, and the effective-spread columns (twice the signed distance of fill price from the mid prevailing at order receipt). Those four elements cover most of what execution-quality disclosures ask for.

Aggregation, Algo Wheels, and Closing the Loop

Per-order attribution rows are raw material; the reporting layer turns them into decisions. Aggregation rolls cost along several dimensions at once, such as desk, trader, algorithm, broker, venue, symbol, and order-size bucket relative to ADV. Because attribution is additive, those roll-ups are notional-weighted sums, not re-estimates. The reporting layer should also expose outlier analysis, because averages hide the orders that matter: a desk’s mean cost can look healthy while twenty large, badly timed orders account for most of the drag.

Algo wheels: a statistics problem, not a scorecard

An algo wheel randomly assigns eligible orders among a set of brokers or algorithms within a bucket of similar orders, so that performance differences can be attributed to the algorithm instead of to which orders happened to be given to whom. Randomization is the point. If a trader hand-picks Broker A for easy names and Broker B for difficult ones, a TCA comparison simply measures the trader’s selection.

Algo wheel analytics flow from order stream through bucketing and random assignment to three brokers, per-order slippage, statistical test and wheel weight updates

Figure 4: Algo wheel analytics. Orders are bucketed by size, liquidity, and style, randomly assigned among brokers, measured by the attribution pipeline, and tested statistically before weights change.

The hard part is sample size. Per-order slippage is noisy. Suppose, for illustration, the standard deviation of per-order shortfall in a bucket is 30 basis points and you want to detect a true difference of 2 basis points between two brokers with 95% confidence and 80% power. The standard two-sample formula gives n per group = 2 × (1.96 + 0.84)² × σ² / Δ² = 2 × 7.84 × 900 / 4, which is about 3,500 orders per broker. A desk sending 100 orders a day into that bucket needs roughly 35 trading days per group, per bucket, even with perfect randomization. The consequences are design constraints. Use few buckets, normalize slippage by expected cost from the pre-trade model so variance shrinks (a variance-reduction step called control-variate adjustment), and use sequential testing or Bayesian updating instead of calling winners after a week. Many wheel programs fail because someone reads a 10-day dashboard as truth.

A related trap is Goodhart’s law. Once brokers know the wheel scores them on arrival slippage, an algorithm can optimize for the measured metric by, for example, leaving hard shares unfilled. That is why the attribution always carries opportunity cost and fill rate next to impact: an algorithm that scores 3 basis points of impact and leaves 40% unfilled is not cheaper than one scoring 8 basis points and finishing the job.

Feeding the model back

Finally, the loop closes. Realized post-trade slippage becomes the calibration set for the pre-trade impact model: regress realized impact on sqrt(Q/V) × σ by bucket, publish the fitted Y and its confidence interval, and compare the pre-trade prediction with the realized outcome every day. A system whose outputs never reach its inputs is a report generator, not an architecture. Where the pre-trade model feeds a router or scheduler, such as a smart order routing engine, version the model and store the version ID on each order so that you can attribute a change in cost to a change in model, not to market conditions.

Trade-offs, Gotchas, and What Goes Wrong

TCA fails in predictable ways, and almost all the failures are silent, because the report still renders a number.

Clock and join bias. A look-ahead join or a systematic clock offset shifts every benchmark in one direction. Symptom: the desk’s average arrival slippage is suspiciously small, or even negative on aggressive orders. Test: shift the quote stream by plus and minus a few milliseconds and see how much the aggregate changes; a robust system moves by a fraction of a basis point.

Benchmark self-reference. Large orders contaminate VWAP and interval benchmarks. Mitigation: compute ex-own VWAP and report participation rate next to every VWAP-based figure.

Survivorship in parent definitions. Cancelled orders and orders that expired unfilled vanish from reports that only list fills, which is exactly where opportunity cost lives. Always start from parent orders, never from fills.

Mixing order types. Marketable limit orders, pegged orders, and auctions have different benchmarks; compare an opening-auction fill with an intraday arrival price and you create cost out of nothing. Classify at ingest and route to the correct benchmark set.

Corporate actions and halts. A split or a trading halt mid-order breaks price continuity. Adjust prices on a stored event, and exclude halt windows from interval benchmarks, noting the exclusion in the output.

Data revisions. Late market-data corrections and trade busts change history. Model data as bitemporal: store when a fact was true and when you learned it, so a rerun reproduces last quarter’s reported numbers and a separate rerun shows the corrected numbers.

Over-aggregation. Averaging across bucket types hides Simpson’s paradox: Broker A can look worse overall yet beat Broker B in every bucket simply because it was handed harder orders. Report at bucket level first.

Compliance-only thinking. The costliest anti-pattern is building TCA for the auditor and then never reading it. If no one has a monthly meeting where a number changes an allocation, the system is paying rent and returning nothing.

Practical Recommendations

Start with the data contract, not the dashboard. Capture parent and child orders, drop-copy fills with both venue and capture timestamps, and a quote stream with a recorded clock source, and keep the raw events immutable. Version every benchmark definition as code and store the version on each result row. Make the as-of join backward-only by construction, and publish quote age and clock quality alongside the cost.

Pick the store to match your scale and skills: ClickHouse or a Parquet lakehouse with DuckDB for most firms starting fresh, kdb+ where it is already the tick standard. Compute implementation shortfall from arrival price with the four-way decomposition, and add ex-own interval VWAP as a second lens. Run an impact model (square-root law is a sound default) calibrated per bucket, and log predictions beside outcomes. If you introduce an algo wheel, size the experiment before launch and refuse to read results early.

A short checklist:

  • Raw order, fill, and quote events are immutable and bitemporal.
  • Every timestamp has a recorded source and an estimated error.
  • The as-of join is backward-only and emits quote age.
  • Reports start from parent orders and include opportunity cost and fill rate.
  • Benchmarks are versioned code, with ex-own VWAP available.
  • Pre-trade predictions are stored next to realized outcomes.
  • Regulatory reports are queries on existing tables; compliance dates are tracked from primary sources.
  • Wheel experiments have a pre-computed sample size and a stopping rule.

Disclaimer: this article describes system design and measurement methodology. It is not investment, legal, or compliance advice.

Frequently Asked Questions

What is transaction cost analysis (TCA)?

Transaction cost analysis measures how much an execution cost relative to a reference price, then explains why. It splits total cost into components such as delay, timing, market impact, and opportunity cost, so traders, brokers, and compliance teams can see where value leaked. It runs before trading as a forecast, during trading as monitoring, and afterwards as the forensic record used for best-execution evidence and for improving algorithms and venue choice.

What is the difference between implementation shortfall and VWAP slippage?

Implementation shortfall compares your real execution with a paper trade at the decision price (or arrival price), so it captures delay, impact, and the cost of shares you failed to buy. VWAP slippage compares your average fill with the volume-weighted average over a window, so it ignores what happened before the window and can be flattered when your own volume moves the average. They answer different questions and are best reported together.

How accurate do TCA timestamps need to be?

Accurate enough that one-directional bias disappears. In an illustrative stock with 2% daily volatility, a random 50 ms error is only about 0.3 bps of noise, which averages out, but a systematic skew or a look-ahead join biases every order. Regulatory tiers such as MiFID II RTS 25 range from 100 microseconds to one second by activity; the architecture should record clock source and error for every timestamp.

Should we build or buy a TCA system?

Buy when you mainly need standardized reports, peer comparisons, and multi-asset coverage quickly. Build when TCA must feed a pre-trade model, an algo wheel, or custom attribution that you can audit. A hybrid works well: keep raw events and the data contract in-house, so you can switch vendors or add your own analytics without losing history. The expensive mistake is outsourcing the data capture and then trying to bolt on feedback later.

Which database is best for TCA?

There is no single best answer. kdb+ is the incumbent for tick data and has native as-of joins but carries licensing and skill costs. ClickHouse offers SQL ASOF JOIN, strong compression, and lower cost. A Parquet lakehouse with DuckDB, Spark, or Trino is the cheapest and most open. Choose based on data volume, latency needs, and your team’s skills, and validate on your own join workload before committing.

Is RTS 28 best execution reporting still required?

Largely no, but verify locally. ESMA said in February 2024 that national authorities need not prioritize supervising RTS 28 reports, and the MiFID/MiFIR review deletes the obligation once transposed into national law. The underlying best-execution duty and the need to evidence it remain, so keep capturing the data. Check your member state’s transposition status and any FCA or other local rules that differ.

Further Reading

Internal:

External primary sources:

By Riju — about

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *