Separate Runtime Errors from Strategy Losses
A strategy run can end badly for two completely different reasons. Either the code never got far enough to trade, or it traded and the simulated positions lost money. These look similar in a results table (both show a disappointing number) but they mean opposite things. One is a bug or a data gap. The other is a signal about your edge. If you delete the inconclusive runs and keep only the clean ones, you throw away the operational record that tells you whether the financial record can be trusted at all.
Two failure modes that get confused
Call the first one a runtime error: the worker could not produce a valid, completed experiment. The second is a strategy loss: the worker ran to completion and the marked value of the simulated book went down. Keep them in separate mental buckets, because the fixes are unrelated.
Runtime errors usually come from one of these:
- Initialization failures: a missing import, a bad config value, or a venue adapter that could not resolve the market universe before the first tick arrived.
- Missing or partial data: absent bid/ask quotes, a gap in the tick stream, or a market that stopped updating mid-run. In the public Banger 0.1.7 paper context, tick prices are Decimal and bid or ask can be absent, and a missing quote can trigger a price fallback rather than a clean fill.
- Rejected inputs: an order the risk layer refused, an out-of-range size, or a price the simulator could not interpret.
- Worker errors: an unhandled exception in your on_market_tick callback that halted the run partway through.
None of those tell you anything about whether the strategy is profitable. They tell you the experiment did not actually test the hypothesis. A run that crashed at tick 40 of an intended 10,000 is not a small loss. It is no result.
Why a strategy loss is different
A strategy loss is a completed path: inputs arrived, orders were risk-gated, fills were recorded, and the book was marked to a valuation model. That outcome is evidence, under a particular data, fill, fee, and valuation model, not proof of profitable or real venue execution. It is the thing you came to measure. A losing completed run is often more useful than a winning crashed one, because you can actually reason about why it lost.
The catch is that "lost money" is not one number. Distinguish what the simulator is reporting: marked value under the valuation model, a settlement claim on a resolved binary, credited cash, and hypothetical executable proceeds are all different quantities. A paper fill is not a venue fill. In the public 0.1.7 forward-paper context, a non-crossing requested limit can be filled at that limit and explicitly logged as paper.fill.resting-assumed, and the reported-volume quantity cap is not full order-book depth or passive queue reconstruction. If your "loss" depends on fills that a real book would not have given you, the loss is an artifact of the model, not a property of the strategy.
Fees change the financial number, not the operational one
Costs belong on the strategy-loss side of the ledger, and they are easy to get wrong. For Kalshi, the official fee schedule (last updated effective July 7, 2026) gives the standard taker fee formula as fees = round up(M x 0.07 x C x P x (1-P)), where C is the number of contracts, P is the price in dollars, and M is the per-series multiplier (default 1 for taker orders). Because of the P x (1-P) term, the fee peaks near a 50-cent price and shrinks toward both ends. The schedule lists no settlement fee. Taker fees apply when your order matches immediately; resting orders pay nothing on most markets, though some series charge a maker fee using 0.0175 in place of 0.07. Some products carry their own separate fee schedule.
Modeling detail matters here. Banger's packaged standard Kalshi fee helper rounds up to a whole cent, while the current official venue PDF describes finer fee-plus-position-cost rounding. So a paper loss computed with the packaged helper may differ from what a real invoice would show. That is a modeling gap to document, not a bug to delete. Inspect the actual result model rather than assuming a single fee number applies everywhere.
Preserve both outcomes instead of deleting runs
The habit worth building: never discard an inconclusive run. A crash, a data gap, and a rejected order are each a finding about your pipeline. Record them next to the completed runs so you can tell, months later, whether a backtest was clean or quietly degraded.
- Tag every run with an operational status (completed, crashed, data-incomplete) that is separate from its financial result (P&L under the stated model).
- Pin the exact market ticker and the data window. Do not assume that taking the first discovery result or parsing a ticker makes a run reproducible. Select and preserve the specific ticker.
- Log the model assumptions: fill policy, fee policy, and valuation basis. A loss is only comparable to another loss under the same assumptions.
- Keep the paper.fill.resting-assumed and price-fallback log lines. They flag exactly the fills that may not survive contact with a real book.
- Write down what was missing. Preserve unknown rules and absent data as unknown rather than silently filling them with defaults.
In practice this maps onto how you run things. pip install bangertrades installs the package. banger run strategy.py --paper runs an existing file locally after account setup and creates a local paper record; banger deploy strategy.py --paper is a separate hosted path subject to admission and capacity. For a local check, run it under supervision and stop it with Ctrl+C when you have seen enough. Note that a log count bounds output, not the lifetime of the run, and stopping a hosted deployment needs its own confirmation and a completed resource cleanup before you can call it done. A stopped badge can appear before cancellation is fully complete. Public market observation here does not require venue trading keys, and hosted historical backtesting currently supports Kalshi binary markets.
A triage checklist
When a run ends badly, ask these in order before you conclude anything about edge:
- Did it complete? If it crashed or stalled, you have a runtime error. Fix the pipeline and rerun. Do not read the P&L.
- Did it have data the whole way? If quotes were absent or the stream gapped, the fills after the gap are suspect.
- Were orders accepted? A wall of rejected inputs means the risk envelope or sizing is wrong, not that the strategy is unprofitable.
- Only if all three pass: now read the financial result, and read it under its stated fill, fee, and valuation model, not as a venue outcome.
Keep the two ledgers separate and keep every run. The operational record tells you whether to believe the financial record, and a backtest you cannot trust is worse than no backtest, because it looks like evidence.