Skip to content

Fix STATISTICS decode corruption and related decoder bugs (#32-#35)#36

Merged
tbeason merged 2 commits into
mainfrom
fix/issues-32-35
Jun 10, 2026
Merged

Fix STATISTICS decode corruption and related decoder bugs (#32-#35)#36
tbeason merged 2 commits into
mainfrom
fix/issues-32-35

Conversation

@tbeason

@tbeason tbeason commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the four issues filed against v0.1.3, all uncovered while pulling Schema.STATISTICS from GLBX.MDP3:

Tests

test/test_issues_32_35.jl adds regressions for each issue (77 assertions):

  • the shipped test_data.statistics.v1/v2.dbn.zst files — which previously desynced after the first record — now decode field-for-field identical to the v3 file, with headers upgraded to the v3 size
  • a synthetic stream of two hand-encoded 64-byte v1-layout StatMsg records, proving the record after a pre-v3 record decodes intact (the desync regression), including v1 UNDEF_STAT_QUANTITY sentinel mapping
  • StatMsgrecords_to_dataframe round-trip, including UNDEF_PRICENaN
  • a 3-interval roll-history Metadata encode/decode round-trip plus a second symbol to check grouping boundaries
  • corrupted side/action bytes on disk decode to NONE with a warning while the following record decodes correctly

Full suite passes on Julia 1.12.

Fixes #32, #33, #34, #35

🤖 Generated with Claude Code

- read_stat_msg: size the body from hd.length; decode 64-byte pre-v3
  StatMsg records (Int32 quantity) and upgrade them to the v3 shape
  instead of overrunning the record and desyncing the stream (#32)
- stat_to_dataframe: use real StatMsg fields (price, quantity,
  stat_flags, ts_ref, stat_type, channel_id, update_action) instead of
  nonexistent stat_value/flags (#33)
- metadata: decode every symbol-mapping interval, one mappings tuple
  per interval; encoder groups consecutive same-symbol tuples back into
  one entry so round-trips are lossless (#34)
- enums: uniform warn-and-default policy for Side/Action/InstrumentClass
  via lookup tables; invalid bytes no longer throw, and unknown Action
  defaults to NONE rather than TRADE (#35)

Adds regression tests for all four issues, including v1/v2 statistics
test-data decoding and a desync regression on a synthetic stream.

Fixes #32, #33, #34, #35

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c53b84600f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/decode.jl
Comment on lines +633 to +635
@warn "Unexpected StatMsg body size $body_size; skipping record" maxlog = 1
skip(decoder.io, body_size)
return nothing

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip malformed statistics records in the typed reader

When a STATISTICS file contains a record whose body is between 49 and 63 bytes, this returns nothing, but read_dbn selects the Vector{StatMsg} typed path for Schema.STATISTICS; that path assigns or pushes the result without checking for nothing, causing a conversion MethodError rather than skipping the record as intended. The typed loops in read_dbn_typed need to recognize this skip result, or this function must signal it in a way those loops handle.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in aa023c9 — all four typed call sites (read_dbn_typed exact/dynamic loops, _foreach_record_impl, _foreach_record_with_control_impl) now skip a nothing result and continue, with a regression test decoding a STATISTICS file containing a 56-byte-body record through the typed read_dbn path.

Comment thread src/decode.jl Outdated
Comment on lines +657 to +660
# Upgrade pre-v3 records to the v3 size so re-encoding stays consistent
out_hd = body_size == 48 ?
RecordHeader(UInt8(80 ÷ LENGTH_MULTIPLIER), hd.rtype, hd.publisher_id, hd.instrument_id, hd.ts_event) :
hd

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize headers after discarding extended StatMsg tails

For a future or vendor-extended StatMsg with a body larger than 64 bytes, the decoder deliberately skips the extra tail but returns the original header here. Re-encoding that value writes only the fixed 64-byte v3 body in write_record_complex, while preserving a header length that advertises more bytes, so the next record becomes misaligned for downstream readers. Normalize every accepted 64+-byte record to the 80-byte header size, or preserve and re-emit the discarded extension bytes.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in aa023c9 — the header is now normalized to the v3 record size (length 20) whenever the on-disk size differs, covering extended 64+-byte bodies with discarded tails as well as pre-v3 upgrades. Regression test included (88-byte record with an 8-byte vendor tail, followed by a record that must stay aligned).

- Typed read paths (read_dbn_typed, _foreach_record_impl,
  _foreach_record_with_control_impl) now skip a nothing returned by a
  record reader for a malformed record instead of erroring on assignment
- read_stat_msg normalizes the header to the v3 record size for any
  on-disk size that differs (not just pre-v3 upgrades), so extended
  records with discarded vendor tails re-encode without misaligning
  downstream readers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

read_stat_msg assumes 80-byte v3 StatMsg layout, ignoring hd.length — corrupts STATISTICS streams served as v1/v2

1 participant