Browse sections

root@kaus.com:~/blog$ cat mmg-exact-horizon-audit.md

Experiment Logs5 min read

When 48 rows stopped meaning four hours

A time-series label used row distance as a substitute for elapsed time. Missing market bars broke that equivalence, and the repair required more than changing one shift operation.

Updated

The key idea

Validate a duration with timestamps, then validate features against information available at prediction time. Those are separate contracts.

Kbit · page guide
  1. 1 / 3

    The article is the primary path.

    Read top to bottom; figures and code-linked examples stay inside the narrative.

  2. 2 / 3

    Continue through related work.

    Use related writing or project links to keep the context connected.

  3. 3 / 3

    Return to the index at any point.

    Open the full Writing index to browse more topics.

The arithmetic was right only on a complete series

Forty-eight five-minute intervals make four hours. Forty-eight observed rows do not necessarily do the same. MMG's intraday label builder used a row shift to locate the future price. When market bars were missing, the label could silently refer to a later time than the four-hour forecast named in the interface.

This is the sort of bug that survives plausible model output. The target is still a future price, the rows remain ordered, and a classifier can learn from them. What changes is the question the model is answering. An evaluation can be internally consistent with its labels while those labels violate the product's intended horizon.

One missing bucket is enough to break the equivalence

Consider a simpler illustrative series observed at 12:00, 12:05, and 12:15. Moving two rows forward from noon lands at 12:15. Looking up a price exactly ten minutes ahead requires a 12:10 observation, which is absent. A row shift returns a value where an exact-time join should report missing data.

The actual continuity audit covered the top 100 items by volume. It inspected 839,687 rows and found 22,830 interior gaps. Of 834,887 labels produced by the 48-row rule, 580,076 landed exactly 14,400 seconds later. That is 69.48% correct. The remaining labels failed the intended elapsed-time contract.

Those counts diagnose the horizon definition. They do not show how much of a previous model score was caused by the error. Answering that would require a matched evaluation under corrected labels. The immediate conclusion was sufficient: a field called four-hour return had to identify a price four real hours away.

Illustrative label construction, with item identity includedtext
row-based target:
  future_price = price.shift(-48) within each item

exact-time target:
  target_timestamp = timestamp + 14_400 seconds
  future_price = lookup(item_id, target_timestamp)
  if absent: no exact-horizon label

12:00 → 12:05 → [12:10 missing] → 12:15
Two observed rows ahead is fifteen minutes, not ten.

The target was not the only time-dependent object

The replacement builder joins each observation to the same item's price at the exact future timestamp. Rolling feature windows also restart at gaps. Otherwise, a feature described as covering a recent continuous interval could quietly span missing time even after the target itself was repaired.

This repair deliberately gives up examples that cannot support the requested meaning. Filling a missing future observation with a later trade would restore a row count while reintroducing the horizon problem. Inventing a price would introduce a different assumption. The corrected research pipeline instead preserves the gap as a limit on what can be labeled.

The resulting feature dataset is frozen separately from the operational archive, so the evaluation reads a stable artifact. That makes a metric traceable to particular data and feature rules. It also prevents a later collector update from silently changing the examples behind a recorded result.

The dataset sizes are not a before-and-after pair

The continuity audit and the rebuilt research dataset have different scopes. The audit's 834,887 shifted labels came from its top-100-item sample. The v2 feature dataset contains 802,443 exact-horizon rows across 789 items. Subtracting those counts would not measure how many examples the repair removed.

This is a second, independent reporting trap. Two precise numbers beside the words before and after can imply a controlled comparison that the source never made. The proper denominator for the 69.48% result is the audit's own set of shifted labels. The broader v2 dataset needs its own item count, window, and feature eligibility rules.

Within that v2 dataset, 22.88% of examples met the price-rise target of at least 1%, while 2.19% met the conservative after-tax-positive target. Those rates describe different targets on the corrected research cohort. They should not be borrowed to characterize the earlier daily experiment or the top-100 continuity sample.

The July 4 report contains two distinct cohorts
ArtifactScopeCount
Continuity auditTop 100 items by volume834,887 row-shift labels; 580,076 exact four-hour matches
Corrected feature dataset789 items, June 4 to July 4802,443 exact-horizon feature rows

September update: a correct horizon is not a clean feature pipeline

A later source audit found a separate problem in the corrected pipeline. It joined future bars before calculating cross-item rank features. Whether an item had a future observation could therefore change another item's present-time feature.

Filtering before ranking is the link between future data and a current input. If Item B lacks a future bar, the old builder drops B from the set of current items used for ranking. In the retained synthetic fixture, Item A's momentum feature percentile changes from 1.0 to 0.0 even though its current history does not change. The repaired builder keeps both current rows and preserves A's 1.0 value.

The September 20 local repair constructs current features before attaching future labels and writes separate training and unlabelled inference artifacts. In the tested synthetic cases, changing future observations no longer changes present inputs or cohort membership. A missing future bar leaves the training target unknown while preserving the current row. Unknown metadata is not filled from later snapshots.

That separation changes the model contract. Historical weights are refused, and no compatible dataset or model has been rebuilt. The public application is unchanged, availability times still involve explicit assumptions, and historical performance has not been recomputed. Fixing the target answered what four hours meant. Fixing the inputs addresses what the model could have known, with separate evidence still needed for how well it predicts.

A future bar should not change a present feature

Both versions receive the same present histories. Change only whether Item B has a future price bar in the dataset.

Item A’s future bar stays included in both measured cases.

Present-time feature percentile · B future bar removed

Before repair

Future bars decide the current cohort

Item A
0.0
Item B
Excluded

Source a9b99f4

After local repair

Current features are built before labels

Item A
1.0
Item B
0.0

Source dd7b361

Item B’s training target is unknown. Its repaired current features stay the same in both cases.

The old builder lets a missing future target change what another item appears to know now. Separating inputs from labels removes that dependency in this fixture.

Feature percentiles only, not predicted rankings. This short synthetic history includes missing longer-window inputs and does not establish model readiness or improved historical scores. The repair is not deployed to MMG.

Measured before and after · isolated synthetic fixture · 2026-09-20

Feature: momentum_1h_rank

Download measured comparison (JSON)

Turn the target's name into an invariant

The transferable check is small: for every retained label, assert that the target timestamp minus the observation timestamp equals the declared horizon, within the same entity. Check feature-window continuity separately. These tests specify meaning rather than merely reproduce the implementation's chosen shift.

The broader lesson is to preserve the unit being measured all the way from data construction to the result paragraph. Rows are observations. Hours are elapsed time. Dataset size belongs to a cohort. Making those distinctions explicit improved the question the experiment could answer before any model architecture changed.

Sources & further reading

  1. September methodology audit

    Future-availability leakage confirmed in source and an isolated reproduction. Historical metric impact remains unmeasured.

  2. Measured feature comparison

    The same synthetic current histories before and after repair, including missing future bars and later volume observations.

  3. Local feature-pipeline repair

    Pinned source identity, synthetic contract checks, incompatible legacy weights, and remaining readiness limits.

  4. Horizon audit report extract

    Separate continuity-audit and corrected-dataset cohorts with primary-report provenance.

  5. MMG exact-horizon experiment

    July 4 continuity audit and timestamp-based label repair.