Skip to content

Cross Validation Of Commission Schedules Over Time

cross-validation-of-commission-schedules-over-timesource

Use when a multi-year US equity backtest applies today's zero commissions to trades from 2016; models the schedule that actually applied on each date. Volume-tiered rates belong to exchange-fee-tier-and-rebate-structure-analysis.

Version
2.0.0
Reading
4 min
Hands off to
4
Handed off from
0
License
Apache-2.0
CoversHistorical Commission ModelerPython

When to Use

Invoke this skill when running historical backtests spanning multiple years (e.g. 2016–2024) on US cash equities. Retail commission rates moved repeatedly over short windows — one major US broker's standard online equity rate went $8.95 → $6.95 → $4.95 → $0.00 between February 2017 and October 2019 — so a single flat commission figure is wrong for most of a multi-year sample. Applying today's zero-commission structure retroactively inflates backtested P&L for any strategy that trades frequently. This skill models date-effective fee schedules, adds the US regulatory pass-through fees that survived the move to zero commission, and quantifies the P&L difference against the naive assumption.

When NOT to Use

Prerequisites

  • Trade execution log with timestamps, symbol, positive share quantity, price, and side (BUY/SELL).
  • Your broker's published historical commission schedule with effective dates. The shipped DEFAULT_SCHWAB_RETAIL_SCHEDULE is a worked reference example, not a substitute.
  • For US regulatory fees: SEC Section 31 rates and FINRA TAF rates covering the backtest window (references/standards.md lists the primary sources).

Workflow

  1. Construct the Time-Varying Fee Schedule: One CommissionTier per period during which the published rate did not change, with inclusive effective dates. Match the broker's actual structure — flat ticket, per-share with floor/cap, or percent-of-value. Do not blend a ticket fee with a per-share fee unless the broker charges both. Overlapping tiers are rejected at construction; gaps are logged and raise at lookup.
  2. Resolve the Tier for Each Trade Date: Parse the timestamp to a calendar date and select the covering tier. If the date is unparseable or uncovered, fail loudly — never fall back to the latest tier, because the latest tier is $0.00 and that silently reintroduces the exact bias being corrected. Convert timestamps to the schedule's timezone first; a UTC timestamp near a session boundary can land on the wrong calendar day.
  3. Compute the Trade Commission: max(raw, min_trade_fee) then, when the broker publishes one, min(..., trade_value * max_pct_of_value) — the cap is applied after the floor and dominates it. Pass share quantity as a positive number with an explicit side; a signed quantity would silently reduce a per-share fee.
  4. Add Regulatory Pass-Through Fees: SEC Section 31 and FINRA TAF apply to sales only and both changed rates repeatedly. Supply a RegulatoryFeeTier list; when omitted, results carry regulatory_fees_modeled=False so the report states the cost was excluded rather than measured as zero.
  5. Audit Fee Schedule Impact: Price every trade under the historical schedule and under a flat modern_baseline, and report the delta in dollars and as a percentage of starting capital. That delta is the P&L a naive backtest would have fabricated.

Full procedure: see references/workflows.md. Standards reference: see references/standards.md. Printable pre-flight checklist: see assets/checklist.md.

Common Pitfalls

  • Retroactive Zero-Commission: Charging $0.00 back to 2015. US retail zero commission began in October 2019 — and on different dates per broker (TD Ameritrade Oct 3, Schwab and E*TRADE Oct 7, 2019). Using one broker's cutover date for another mis-costs the intervening days.
  • Treating "Zero Commission" as Zero Cost: The SEC Section 31 fee and the FINRA Trading Activity Fee still apply to every sale and are passed through to the seller. A backtest that charges nothing to a post-2019 exit under-costs every exit in the sample.
  • Silent Fallback on an Unresolvable Date: An unparseable timestamp, a date before the schedule starts, or a schedule gap must raise. If the modeler quietly applies its most recent tier instead, a whole pre-2019 backtest can be priced at $0.00 and still look like it modelled commissions.
  • Modelling a Structure the Broker Never Charged: A flat-ticket broker charges a ticket fee and no per-share fee. Adding a per-share component "for realism" invents costs; conversely, dropping a per-share broker's floor or cap mis-prices both ends of the size distribution.
  • Ignoring Minimum Ticket Charges: Applying $0.005/share without the $1.00 per-order minimum under-costs a 10-share trade by 20×.
  • Ignoring the Percent-of-Value Cap: 10,000 shares at $0.10 is $1,000 of notional; an uncapped $0.005/share fee charges $50 — 5% of the trade — where the broker's 1% cap charges $10.

Verification

  • Price the same trade on 2019-10-06 and 2019-10-07 against the reference schedule and confirm $4.95 vs $0.00 — an off-by-one at the cutover is the most common schedule bug.
  • Submit a trade dated before the schedule's coverage and confirm it raises rather than returning $0.00.
  • Price a 1,000-share $200 sell under a Section 31 + TAF schedule and confirm the total cost is non-zero even in the zero-commission era.
  • Run python -m unittest discover -s skills/cross-validation-of-commission-schedules-over-time/scripts and confirm a 100% pass rate.

Verify it, from the repository root

python -m unittest discover -s skills/cross-validation-of-commission-schedules-over-time/scripts

Hands off to 4

Skills this document names, usually in When NOT to Use, as the owner of a case it excludes.

Handed off from 0

Skills that name this one as the place a case belongs. The reverse edges of the graph.

No other skill hands off to this one yet.