Skip to content

fix(data): normalize baostock volume to board lots + cross-source consistency guard - #1067

Draft
shadowinlife wants to merge 2 commits into
HKUDS:mainfrom
shadowinlife:fix/volume-normalization-phase2
Draft

fix(data): normalize baostock volume to board lots + cross-source consistency guard#1067
shadowinlife wants to merge 2 commits into
HKUDS:mainfrom
shadowinlife:fix/volume-normalization-phase2

Conversation

@shadowinlife

Copy link
Copy Markdown
Contributor

Summary

Why

#1062 identified baostock as the single A-share source reporting volume in shares while tencent/eastmoney/akshare/mootdx/tushare report board lots (1 lot = 100 shares) — empirically 600519.SH 2026-07-31: baostock 5,512,752 shares vs tencent/eastmoney 55,128 lots, ratio exactly 100.0. Phase 1 (#1065) made the discrepancy visible via _provenance.volume_unit; Phase 2 removes it. Canonical unit = board lots: the native unit of 4 of the 5 A-share sources and the mainstream A-share quote convention.

Changes

  • baostock_loader.py: divide native share volume by 100 at the loader boundary; fractional lots preserved (odd-lot trades are real, so no rounding); volume_units declaration flips to "lots", matching every other A-share source
  • base.py: loader cache version 3 → 4 — entries cached under the pre-normalization unit become unreachable and are never served again
  • tests/test_volume_unit_consistency.py (new): fetches the same settled trading day from every reachable A-share loader (tencent/eastmoney/baostock/mootdx) and asserts pairwise agreement within 1%; network-guarded — unreachable sources skip individually, and the test skips entirely when fewer than two sources are reachable
  • tests/test_baostock_loader.py: normalization unit tests (clean lots + odd-lot fractional case)
  • tests/test_loader_volume_units.py: pins updated to the post-normalization matrix (baostock now declares lots)

Test Plan

  • Existing tests pass: loader/market-data/count-gate sweep — 741 passed, 5 skipped; cache-related suites — 99 passed
  • New tests added: consistency guard + 2 normalization unit tests + updated pins (27 passed in the focused run)
  • Tested manually: 600519.SH 2026-07-31 — tencent 55,128 lots vs baostock 55,127.52 lots after normalization (ratio 0.999991; the 0.48-lot gap is baostock's exact share count including odd lots, within the 1% tolerance); the consistency test passes with three live sources

Checklist

  • No changes to protected areas (src/agent/, src/session/, src/providers/) without prior discussion
  • No hardcoded values (API keys, file paths, magic numbers)
  • Code follows CONTRIBUTING.md guidelines
  • Documentation updated (if user-facing change)

Note for reviewers: this branch stacks on #1065 — it contains the Phase 1 commit until that PR merges, after which this PR's diff narrows to the Phase 2 commit. Behavioral change is bounded: only runs that previously fell back to baostock see different volume values (now consistent with the chain head instead of 100x off).

A-share loaders return volume in each source's native unit with no
metadata: tencent/mootdx/eastmoney/akshare/tushare report board lots
(1 lot = 100 shares) while baostock reports single shares — a silent
100x discrepancy whose value depends on which fallback source served
the request (see HKUDS#1062).

Phase 1 (non-breaking; no data values change):
- Loaders may declare volume_units keyed by market; tencent/eastmoney
  are market-dependent (A-share lots vs HK shares, empirically
  verified 2026-08-11); baostock pinned to shares, the single outlier
- fetch_market_data surfaces _provenance.volume_unit per symbol from
  the serving loader ("lots" / "shares"; null when undeclared)
- get_market_data tool descriptions (agent + MCP) state the contract
  so LLM consumers read the unit instead of assuming one

Undeclared markets (crypto, futures, US via unverified sources) stay
null rather than guessed. Pin tests lock the audit matrix from HKUDS#1062.

Signed-off-by: shadowinlife <shadowinlife@gmail.com>
Phase 2 of HKUDS#1062. BaoStock was the single A-share source reporting
volume in single shares while tencent/eastmoney/akshare/mootdx/tushare
report board lots (1 lot = 100 shares) — a silent 100x discrepancy
whose value depended on which fallback source served the request.

- baostock_loader: divide native share volume by 100 at the loader
  boundary; fractional lots preserved (odd-lot trades are real);
  volume_units declaration now "lots" like every other A-share source
- Loader cache version 3 -> 4 so entries cached under the
  pre-normalization unit are never served again
- New empirical cross-source consistency test: the same settled
  trading day fetched from every reachable A-share loader must agree
  within 1% (network-guarded — skips when fewer than two sources are
  reachable); this is the runtime lock against future unit drift
- Pin tests updated to the post-normalization matrix

Verified end-to-end: 600519.SH 2026-07-31 — tencent 55,128 lots vs
baostock 55,127.52 lots (ratio 0.999991; the 0.48-lot gap is baostock's
exact share count including odd lots, within the 1% tolerance).

Signed-off-by: shadowinlife <shadowinlife@gmail.com>
shadowinlife added a commit to shadowinlife/Vibe-Trading that referenced this pull request Aug 11, 2026
- Merge 14 upstream commits (swarm/backtest/agent/RSI fixes) with zero
  conflicts; all gates unchanged (memory 321/2, CH 13/8, counts 54, env 0)
- Update HKUDS#1062 tracking with Phase 1/2 draft PRs (HKUDS#1065/HKUDS#1067) and the
  canonical-unit decision (board lots)
- Add section 4.6 pending research: R1 ClickHouse semantic-layer
  deep-dive (DB-side comments/DDL, semantic views, explicit valuation
  tool for the SELECT * leak path, metric dictionary, MCP preconditions)

Signed-off-by: shadowinlife <shadowinlife@gmail.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.

1 participant