Trading costs and market impact

The run charges the spread, commission and slippage declared in the graph. It does not model the market impact of a large order.

Position duration and entry timing

Execution leaves a timing footprint. Inspect holding duration and entry-hour buckets together with the number of trades in each bucket.

Average trade P&L by holding duration and entry hour, from the recorded study. Read each bucket's trade count before comparing averages; these figures do not measure order latency.Open full size
Average trade P&L by holding duration and entry hour, from the recorded study. Read each bucket's trade count before comparing averages; these figures do not measure order latency.

Two quantities that scale differently

The friction on a trade splits into a part that scales with turnover and a part that scales with size. The first covers the spread you cross, the commission you pay and the financing you carry overnight. It attaches to each round trip, so a configuration that trades twice as often pays it twice as often.

The second is market impact: the price moves against an order that consumes a visible share of the available volume. Measured across equities, futures, FX and crypto, it grows with the square root of the size traded and stays close to independent of how the order is sliced.

These two behave differently under a sweep. Turnover-scaled friction is a constant per trade and ranks configurations by how often they trade. Impact depends on the notional you intend to run, which the backtest does not carry, so it changes the answer at the moment you decide how much capital the result deserves.

scales with turnoverspread, commission, slippage per round trip, overnight financing
scales with sizemarket impact, growing with the square root of the order
what the shipped run chargesthe turnover-scaled part, from the nodes you place

A charge the engine adds by itself

The cost the engine applies with no cost node placed is zero. Each run poses model: "declared" in the cost dictionary, which sets the venue model to a zero floor before your nodes add to it. The result then carries a gross-run label, and the label names the spread, commission, slippage and financing that went uncharged.

The zero baseline makes the charge equal to what you declared. It replaces earlier defaults that could shape a result with broker assumptions unrelated to your graph, and the additive keys then add once.

with no cost node placeda gross run, with the uncharged items named in the result
with cost nodes placedthe charge equals the sum your nodes declare

How a node reaches the engine

The cost family ships 26 nodes, of which 17 are validated by executable checks, and 9 report a declared need in place of a figure. They compose into a small set of keys, and each key carries its own unit.

extra_rt_bp holds commissions and slippage in basis points of round-turn notional. sigma_slip_k holds a slippage expressed in multiples of the ATR at entry, for a venue whose spread widens with volatility. extra_drag_R holds a flat drag in R per trade, kept in its own unit because no conversion to basis points exists without a notional. cost_mult multiplies the net friction, for a stress reading.

The replacement keys are separate: round_turn_bp from the venue-profile node, and swap_long_bp, swap_short_bp and crypto_apr_pct from the financing node. A node whose label declares an override replaces the model; the others add to it, because a label reading taker fee describes a commission your venue also charges.

extra_rt_bp     bp of round-turn notional   commissions + slippage
sigma_slip_k    multiples of entry ATR      volatility-scaled slippage
extra_drag_R    R per trade                 flat drag, unconverted
cost_mult       multiplier                  stress on the net friction
round_turn_bp   bp of round-turn notional   venue override (replaces)

The order several nodes compose in

Fees add first, in basis points: commissions, taker fees and the fixed half-spread paid at entry and at exit. Slippages then add in basis points and are multiplied by the exec-efficiency node. A degraded execution degrades the slippage and leaves the commission untouched, since a commission does not slip.

The two sums meet in extra_rt_bp. The stress multiplier applies last, engine-side, on the net friction, so it prices a venue where fees and slippage widen together. A run with a stress node and no slippage node reports that the multiplier had no slippage to degrade.

Two halves of one node stay separate when they carry different units. The vol-scaled spread declares a fixed part in basis points and a volatility coefficient in multiples of ATR, and each half reaches its own key with its own journal line. Declaring the coefficient alone charges the coefficient alone.

feesadded in bp, undegraded by exec efficiency
slippagesadded in bp, then multiplied by the exec-efficiency node
stressapplied last, engine-side, on the net friction

A class resolved by the cost table

Some charges depend on the instrument, so composition runs once per symbol. The per-class commission node declares a line for FX, metals, stocks and crypto, and the line charged is the one matching the class of the symbol being traded. The class comes from the engine cost table; the shape of a ticker string does not determine it.

The funding node charges a perpetual rate on a crypto perp. On any other class the node reports the mismatch and charges zero, because the engine reads that rate inside its crypto branch alone. Charging it on a spot symbol would compose a key that no code path reads, which reads afterwards as a cost that applied.

When the class cannot be resolved, the node reports the failure and charges zero. A line picked at random would bill one class at another class's tariff, and the figure would then carry the appearance of a measurement.

a class the table resolvesthe matching line is charged, per symbol
a class it cannot resolvezero charged, with the reason in the run journal

Charged, and not charged, side by side

A cost node placed produces a journal line, and the run note prints both halves: what was charged with its detail, and what went uncharged with the input that was missing. A parameter left blank contributes zero and appears under the second half.

Blank and zero read differently in that journal. A blank parameter records a silence, and a declared zero records a decision, so a node placed without its numbers does not read as a node that applied. This is what makes a gross run detectable after the fact, including inside an exported paper.

costs DECLARED by your nodes (the engine adds none):
  charged: Taker fee -> commission x 2 fills; Spread (vol-scaled) -> half-spread at entry and exit
  NOT charged: Slippage - volume-share (order quantity unavailable; the run is denominated in R)
a blank parametera silence: zero contributed, named in the journal
a declared zeroa decision: zero contributed, recorded as chosen
P&L bridge in Stochastly, Full desk · 155 nodes, light theme.Open full size
Positive and negative contributions reconcile to the recorded net P&L.

One fill, at the next bar open

Entry happens at the open of the bar following the signal, and a stop or target is detected at the wick of the bar, above or below its close. Settlement is immediate, with no delay on cash availability. Placing the nodes that name these behaviours confirms them and changes no figure in the result, which the journal states as a confirmed guarantee.

One consequence follows for the sections below. The run executes a position in a single fill, at one price, with no execution schedule and no child orders. A model of impact needs a trajectory over time, so the shape of the fill sets the boundary of what can be charged.

entrythe open of the bar after the signal
stop and targetdetected at the wick of the bar
the fill itselfa single fill at one price, with no schedule

Why the size axis stays unmodelled

The run is denominated in R, the unit of risk you declared, and carries no order quantity and no currency ledger. Neither the run specification nor the executor transports a lot size. Each charge described above is therefore expressible per round trip or per trade, and each charge that would depend on how much you trade is left out.

The nodes concerned report a declared need in place of a figure, each naming its own missing input. Volume-share slippage requires the order quantity beside the bar volume the engine already holds. Almgren-Chriss market impact requires a fractional execution schedule, since permanent and temporary impact are defined over a trajectory. Delayed settlement requires a currency ledger for a cash delay to constrain anything. Buying power and a margin-call model both require the notional per trade, so a capital figure and a point value. The break-even cost sweep charges per trade, and the trade count travels in no context object today.

The exit condition is one node and the keys it would feed. The account node declares capital and unit; wiring it into composition yields a notional per trade, which opens a volume-share key and the temporary and permanent impact coefficients. Until that wiring exists, an impact figure would be a number with no measurement behind it.

the shared causethe run is denominated in R, with no order size and no currency ledger
the exit conditionthe account node feeding a notional per trade into composition

Costing a result without an impact model

Cost the effect at the first measurement. A gross figure and a net figure read side by side, with the turnover that separates them and the cost per round trip you assumed, give a comparison that survives a later revision of the cost. A cost check deferred to implementation arrives after the research has anchored on the gross number.

Then compute the break-even cost: the gross edge divided by the turnover, which gives the cost per round trip at which the net contribution reaches zero. Compare it against what the instrument costs to trade. A break-even below realistic costs gives a documented no-go carrying the number that would reopen it.

The stress multiplier stands in for the missing size axis. A cost parameter is held fixed for a run, so this sensitivity is built by hand: set a level, rerun, repeat, and read where the verdict changes. That level is a distance to the edge of viability. It prices no impact, and it does tell you how much unmodelled friction the result absorbs.

Last, state the participation rate the result assumes. The square-root law makes impact a function of size over daily volume, so a notional small against the volume of the bars you traded keeps the unmodelled term small. Writing that assumption down beside the verdict keeps it available when the account grows.

the reading to producegross and net side by side, with the turnover between them
the number to computebreak-even cost = gross edge / turnover
the sensitivity to run by handthe stress multiplier, one level per run, reread each time
the assumption to recordthe participation rate the notional implies

Where these results come from

The turnover hurdle and its effect on which effects survive are measured in the anomaly literature. The impact law and the execution frontier come from the optimal-execution literature, and the shipped knowledge pack carries both summaries.

Almgren, R., & Chriss, N. (2000) Optimal Execution of Portfolio Transactions Journal of Risk
Tóth, B., Lempérière, Y., Deremble, C., de Lataillade, J., Kockelkoren, J., & Bouchaud, J.-P. (2011) Anomalous Price Impact and the Critical Nature of Liquidity in Financial Markets Physical Review X doi:10.1103/PhysRevX.1.021006
Kyle, A. S. (1985) Continuous Auctions and Insider Trading Econometrica doi:10.2307/1913210
Novy-Marx, R., & Velikov, M. (2016) A Taxonomy of Anomalies and Their Trading Costs Review of Financial Studies
Frazzini, A., Israel, R., & Moskowitz, T. J. (2018) Trading Costs SSRN Working Paper

What is being claimed, and what is left out

The charge applied to a run comes from the cost nodes placed on the canvas.

Scope
The engine cost floor is set to zero per run, so the figure covers what your nodes declare on the bars that were loaded.
Effect
A graph wiring no cost node produces a gross result, which overstates what an account would have kept.
Control
The run note prints the charged items and the uncharged ones with their missing input, and the gross label travels into the verdict and the exported paper.

Size-dependent market impact is charged at zero.

Scope
Any run, at any notional, since the run is denominated in R and carries no order quantity.
Effect
A result overstates the net edge by the impact its intended size would incur, and the overstatement grows with the square root of that size.
Control
The nodes concerned report a declared need naming the missing input, and the stress multiplier gives a sensitivity across friction levels.

Two cost channels have no equivalent on an installed build.

Scope
A build whose package excludes the kernel engine, where the cross-sectional backend charges the declared round-turn fraction.
Effect
A volatility-scaled slippage and a drag in R are excluded from the charge on that path, since it holds no entry ATR and no R.
Control
The run label names them as excluded from the charge, beside the basis points that were applied.

Production packaging includes the strategy engine and fails when its source is unavailable. The explicit non-deliverable demo configuration can omit it and use a labelled generic cross-sectional fallback. That fallback does not support execution claims.

No figure described above is a statement about future performance, and the harness carries no signal to trade. A result concerns the series that was submitted.

Check a cost assumption in units

A worked example with made-up numbers: 10 units of gross P&L less 3 spread, 2 commission and 4 slippage leaves 1 unit. Keeping the same run and changing only slippage to 8 leaves -3 units. Reproduce that subtraction first; then inspect the run journal to learn whether each real cost was charged, declared zero or left blank. A flat estimate cannot prove market impact for the size you intend to trade.