Reconcile stock backtest commissions, sell-side charges and slippage
To reconcile transaction costs, start with executed quantities and the price actually used by the simulation. Apply commissions and direction-specific charges to that notional, then bridge each fill to cash. If slippage already changed the execution price, do not subtract the same slippage amount again as a cash fee.
This guide produces a complete round-trip ledger with a minimum commission, a fictional sell-only charge and adverse price slippage. All rates are invented teaching assumptions, not current broker quotes or a historical tax schedule. The example validates accounting mechanics. CoTrading fee-audit features remain planned or unverified, and no live account or real trading engine was exercised.
Freeze the fee model before calculating
Record market, account currency, fee-model version, effective period, commission rate, minimum per order, side-specific charges, slippage definition and rounding convention. A default in reference code is not evidence of today’s applicable charge. If studying real transactions, compare the chosen model with the broker’s own schedule and the relevant market’s dated rules.
Keep the unit of each parameter explicit. One tenth of one percent is 0.001 as a decimal multiplier; a minimum of one currency unit is an amount, not a rate. A minimum can dominate small orders while the proportional component dominates large ones. Also distinguish minimum per order from minimum per day or per execution, because splitting an order changes costs under different conventions.
The fixture starts with 2,000 currency units and trades one hundred shares. Commission is the larger of one unit and 0.0003 times executed notional. A sell-only charge is 0.001 times sell notional. Slippage is adverse by 0.001 of the raw reference price: purchases cost more and sales receive less. The calculation retains full decimal precision until display, rather than silently assuming a real broker’s rounding.
Calculate the purchase from its execution price
The raw buy reference is ten. Adverse slippage multiplies it by 1.001, giving 10.010. One hundred shares therefore have executed notional 1,001. The proportional commission would be 0.3003, so the minimum of one applies. There is no sell-only charge on this purchase.
The purchase cash change is minus 1,001 minus one, or minus 1,002. Cash after purchase is 998, with one hundred shares held. Save reference price, execution price, quantity, notional, each charge and net cash change as separate columns. A single field called total cost makes it harder to discover whether the price difference was included twice.
The difference between reference notional 1,000 and executed notional 1,001 is one of entry slippage. It is already contained in the 1,002 cash outflow. Recording that one separately for analysis is useful, but deducting it again would produce an account that no longer reconciles to its stated executed price.
Calculate the sale and directional charge
The raw sale reference is 10.5. Adverse slippage multiplies it by 0.999, giving 10.4895. One hundred shares have executed notional 1,048.95. The proportional commission is below one, so the minimum applies again. The fictional sell charge is 1.04895, calculated only on the sale’s executed notional.
Net sale cash inflow is 1,048.95 minus one minus 1.04895, or 1,046.90105. Add that to the remaining 998 cash to obtain 2,044.90105. There are no shares left, so terminal account value equals cash. Net round-trip profit is 44.90105.
Do not copy this sell-only charge into another market or date simply because the formula is convenient. A/H/US models require their own actual assumptions, and a US zero-commission setting would still not imply zero spread, slippage or every other possible charge. This article makes no claim about current fee levels in any market.
Build a second independent profit bridge
At raw reference prices, the price gain would be fifty: one hundred times 10.5 minus ten. Entry slippage is one; exit slippage is 1.05, the difference between raw sale proceeds 1,050 and executed proceeds 1,048.95. Total slippage is therefore 2.05.
Subtract 2.05, two commissions and 1.04895 sell charge from fifty. The result is 44.90105, matching the cash bridge. This agreement is a meaningful invariant because the two calculations start from different columns. It also explains the entire difference between a raw-price gross result and the account’s final profit.
The boundary case subtracts the same 2.05 slippage from that already net result, producing 42.85105. It is wrong because the executed buy and sale prices have already absorbed slippage. A second boundary increases purchase quantity to ten thousand shares; proportional commission then exceeds the minimum. The model must support both branches rather than hard-code a one-unit fee for every trade.
Use decimal arithmetic with an explicit policy
Python’s decimal documentation explains decimal representation and selectable precision and rounding. Construct monetary inputs from strings in this fixture so that a binary floating-point approximation is not imported unintentionally. Decimal arithmetic does not choose the correct economic rounding policy for you; that remains a declared model input.
The full standard-library artifact is docs/growth/research/round-5/examples/R600.py. Its central cash formula is:
commission = max(minimum, notional * commission_rate)
sell_charge = notional * sell_rate if side == 'sell' else Decimal(0)
cash_delta = (-notional - commission if side == 'buy'
else notional - commission - sell_charge)
Run python docs/growth/research/round-5/examples/R600.py. Assertions check both execution prices, minimum commissions, zero buy-side sell charge, the sell-side amount, exact ending cash, equality of the two profit bridges and the incorrect doubled-slippage result. The script also executes the large-order branch to verify that the minimum is not the only active commission rule.
Reconcile actual engine output field by field
For a genuine run, join each fill to its fee-model version and independently recalculate notional. Inspect whether an engine’s price field already contains slippage, whether commission is reported positive or signed and whether tax-like charges are separate. A field named fee may include several components; do not add its subcomponents again without checking its definition.
Use Investor.gov’s order reference when distinguishing observed reference prices from actual execution conditions. Preserve unfilled and partially filled quantities if the engine exposes them. An unfilled target should not incur a fictional executed-notional fee unless the chosen model explicitly defines an order-level charge.
Close the ledger only when initial cash plus all signed cash changes equals final cash, final shares equal initial shares plus executed buys minus sells, and realized profit agrees with the independent reference-price bridge. Investigate differences before comparing strategy returns. Update the study when fee schedules, fill conventions, order splitting or rounding change, keeping old configuration and output so the cost effect can be isolated from signal changes.
Download the complete example. Save it as R600.py and run python R600.py with Python 3.12 or later. It uses only the standard library and synthetic data.
Define where rounding enters the ledger
The fixture retains sub-cent amounts to expose the formulas. A real fee schedule may round each component separately, round the total at each fill or aggregate before rounding. Those conventions can produce different cash balances even with identical nominal rates. Write the selected rule beside the fee-model version and apply it consistently to the independent profit bridge. Display formatting is not a rounding policy: showing two decimals must not silently replace the stored calculation.
When reconciling a discrepancy, compare raw reference notional, executed notional, unrounded charges, charged amounts and final signed cash movement in that order. A difference confined to rounding should be classified separately from a wrong rate or a duplicated price adjustment. Do not tune the commission rate to make one statement match. First obtain the actual computation convention, then rerun all small-order and large-order cases. Archive the original mismatch and the corrected output so a later schedule change cannot be confused with a strategy-performance improvement.
Validate an executed-fill request before charging anything. side must be exactly buy or sell; uppercase BUY is rejected rather than interpreted as a sale. Quantity must be a positive integer, with Boolean values excluded. The raw price must convert to a finite positive decimal. Zero or negative quantities, fractional quantities, zero or negative prices, NaN, infinity and malformed price text all raise ValueError. The downloadable file asserts those rejections. A zero-share target is not a fill and must never incur the minimum commission through this function.
FAQ
Are these rates applicable to my broker?
No. They are fictional parameters for a reproducible accounting exercise; use the actual dated schedule for a real study.
Is slippage always a separate fee?
No. Here it changes the execution price. It is measured separately for explanation and deducted through the price only once.