24 September update: Read the fixed ₹60L / ₹5L comparison of all 132 opportunities. It separates closed profit, open marks, ordering sensitivity and missing-stop assumptions. Earlier figures below use different experiments and must not be treated as interchangeable.

Equity book · data audit · 24 September 2026

Counting Defects Is Not Diagnosing Them

An independent audit of the 132-trade sample reported three numbers: eight entry prices outside their daily range, three rows needing corporate-action checks, and a ₹61,474.80 gap between the broker statement and the trade log. All three are real. None of them is a cause. This resolves each into one.

132 trades2,971 symbols in the panel 58 corporate actions found0 corrupted published setups
of the ₹61,475 gap
97%
is two symbols
entry prices that are a date error
5 of 8
the price exists one session away
unadjusted splits in the panel
58
18 invisible to the existing guard
actions now adjusted
58
0 residual, verified
One

The ₹61,475 gap is two symbols, and each has its own fault

symbolbrokertrade log gapwhat is actually wrong
JUNIORBEES₹42,582.84₹-172.64 ₹42,755Two trades in the log against a year of broker activity. The log did not record it
AVALON₹67,228.15₹50,157.00 ₹17,071A third round trip sits in the database marked cancelled with ₹0 realised
everything else24 symbols₹1,648 Rounding and charge attribution. Ordinary
This is the difference a diagnosis makes. "A ₹61,475 reconciliation gap" sounds like a systemic accounting problem across 134 trades. It is two broken records, and they are broken in two different ways: one position never entered the log, and another was closed in the book as cancelled while the broker shows it round-tripped.
Two

The entry prices are not impossible. The dates are wrong.

Eight entry prices sit outside the range of the session they are recorded against. The useful question is not how many, but whether the price exists anywhere nearby. For five of the eight it exists exactly one session away.

symbolrecorded date entrythat day's rangeprice actually exists verdict
NIFTYBEES2026-04-02273.45251.70–259.2913 days away (15 Apr)date or price wrong
SAKAR2026-04-17633.14580.10–614.352 sessions (15 Apr)date attribution
NATIONALUM2026-04-28433.00436.05–444.201 session (29 Apr)date attribution
SGMART2026-04-28561.59543.00–556.801 session (27 Apr)date attribution
JUNIORBEES2026-05-05778.94750.55–763.50no nearby sessionunexplained
HONASA2026-05-07360.87343.00–357.001 session (8 May)date attribution
WELCORP2026-05-221,348.811252.20–1306.801 session (21 May)duplicate of the 21 May row
GOLDBEES2026-09-10125.30126.05–126.931 session (9 Sep)date attribution
Consistent with entry_time holding the booking or reconciliation moment rather than the fill, which is what you would expect given 28 of these rows say they were backfilled from broker fills. Two are worse than a date slip: WELCORP carries the entry price of its own 21 May row, and NIFTYBEES carries a price that belongs to mid-April and is duplicated onto a September row with the identical quantity. JUNIORBEES — already the largest reconciliation gap — has an entry price that appears in no session within fifteen days either way.
Three

The panel carries 58 unadjusted corporate actions

The audit flagged three rows for corporate-action checks. Two are real: POCL falls 1401.80 to 561.10 on 21 July, a ratio of 2.498 against a 2.5:1 action, and TDPOWERSYS falls 1534.80 to 780.10 on 24 August, 1.967 against a 2:1. The third, KIRLOSENG at +20%, is a day in the market.

The right question was the one behind it: how many are there? Scanning all 2,971 symbols the backtests read gives 58. Nothing in the pipeline detects or adjusts for a single one of them.

corporate actions in the panelcount why
caught by the per-symbol move guard40 A 2:1 or larger shows as a >35% one-day fall, and the guard drops the symbol whole
missed by it18 Every one a 1.5:1, showing as a ~33% fall — just inside the band. TRENT, OLAELEC and sixteen others

So the guard that protects these studies is a filter written to drop garbage ticks, which catches a 2:1 split by accident and a 1.5:1 not at all. That is luck, not design.

The published results survive, and it was checked rather than assumed. Across the G3 population (46,404 setups) and the stop-rules population (2,876), the number holding a position across one of the eighteen missed actions is zero. A different window would not have been so lucky.

Fixed

The panel is adjusted, and the G3 verdict survives it

All 58 actions are now back-adjusted: every bar before an action is divided by its factor, open, high, low and close together so ranges stay internally consistent. Volume is left alone, because multiplying it would invent liquidity that did not exist. Re-running the detector on the adjusted panel finds zero remaining discontinuities, and the script refuses to write the file if any survive.

Adjusting the panel is not a free change: forty symbols the move guard used to discard for having had a split now stay in, so the population grows. The test that matters is whether the published conclusion moves with them.

G3 measurementraw panel (published) adjusted panel
setups after G1, G2 and G432,00732,706
Spearman, momentum vs return−0.079−0.078
G3 at 25, kept minus refused+0.34pp (p=0.43)+0.37pp (p=0.38)
Mar–May / Jun–Jul+2.21 / −2.76+2.15 / −2.64
threshold 15−0.89pp (p=0.0005)−0.91pp (p=0.0003)
Seven hundred more setups and nothing moves: the gate still admits about 4% of the population for a difference indistinguishable from zero, the relationship is still a U, and the effect still flips sign between periods. A finding that survives a correction to its own inputs is worth more than it was before the correction.
What follows

Three fixes, in the order they matter

Adjust the panel for corporate actions, or detect them. Fifty-eight known events and a guard that sees forty of them by accident. A ratio near a simple fraction on a single session is a reliable signature — this page found all 58 with six lines of arithmetic.

Record the fill, not the booking. done Reconciliation stamps entry_time with the moment it noticed a holding, because the broker exposes no purchase date. It now also sets entry_time_estimated — the counterpart of the exit_price_estimated flag this model already carried — so research can tell an observed fill from an adopted one. Not set on the parking sweep, which really does buy in that moment.

Repair the two broken records rather than excluding them. The AVALON round trip marked cancelled and the missing JUNIORBEES history are 97% of the reconciliation gap, and both are recoverable from broker data. Exclusion moves the modeled total by about ₹24,000 and teaches nothing.

Not fixed here, and worth saying: 39 of the 132 trades still have no recorded entry stop, and no backup contains one. That stays unknown. See the correction on the six-month replay.

← Trading book · 2026-09-24_data_quality.py · 24 September 2026