Repository navigation
fix(trade-journal): only claim the Futu format when its value columns exist - #1766
Merged
warren618 merged 1 commit intoOct 10, 2026
Conversation
… exist detect_format() returned "futu" for any frame carrying the exact-case columns Date, Symbol and Side (or Direction). That is an ordinary English journal header, not a Futu signature, and parse_futu() reads Quantity, Price, Amount, Commission and Platform Fee by exact spelling while _to_float(None) defaults to 0.0. A journal headed Date,Symbol,Side,Qty,Price therefore parsed to records with zero quantity and zero amount, and parse_file() still reported success, so analyze_trade_journal answered status ok with total_pnl 0.0 and format_detected "futu" over real fills. Require a side column and one of the value columns parse_futu() actually reads before claiming the format. Frames without one now reach parse_generic(), which already matches headers case-insensitively and documents the qty/size/volume aliases. A genuine Futu export always carries Quantity, so it is unaffected; a re-routed frame cannot regress, because parse_futu() could only ever return zero quantity and zero amount for it. Signed-off-by: Steven Huang <134844631+remote-controlled-man@users.noreply.github.com>
remote-controlled-man
deleted the
codex/vibe-trading-trade-journal-header-aliasing
branch
October 13, 2026 01:27
warren618
added a commit
that referenced
this pull request
Oct 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
detect_format()returned"futu"for any frame whose columns include the exact-case setDate,SymbolandSide(orDirection). That is an ordinary English journal header, not a Futu-specific signature.parse_futu()then reads its value columns by exact spelling (Quantity,Price,Amount,Commission,Platform Fee), and_to_float(None)returns its0.0default. A journal headedDate,Symbol,Side,Qty,Pricetherefore parsed into records with zero quantity and zero amount, whileparse_file()still returned success.parse_futu()actually reads before claiming the Futu format. Everything else now reachesparse_generic(), which already matches headers case-insensitively and documents theqty/size/volumealiases.Why
Self-discovered while reading the parser; no existing issue.
analyze_trade_journalis documented in the agent context (src/agent/context.py) as the entry point for "parse + profile + behavior diagnostics". Over an affected journal it returnsstatus: okwithtotal_roundtrips: 0,win_rate: 0.0,total_pnl: 0.0andformat_detected: "futu", so the agent reports an empty but successful analysis of a journal that contains real fills.shadow_account/extractor.pyandshadow_account/backtester.pyreach the same code and raiseNo complete buy→sell roundtrips found in journal.Identical rows, only the header spelling differs — measured on
mainat2620a39:The capitalised form is the one that breaks, because the Futu branch is tested before the case-insensitive generic branch. This is exactly the failure
_dividend_amount's docstring says the module avoids: a zero is meant to be "surfaced downstream, never silently treated as a real zero payout".Existing tests did not catch it because every end-to-end fixture in
test_trade_journal.pyuses lowercase headers, which misses the case-sensitive Futu branch.Changes
agent/src/tools/trade_journal_parsers.py(+13/−1): add_FUTU_SIDE_COLUMNSand_FUTU_VALUE_COLUMNS, and require both in the Futu branch ofdetect_format().agent/tests/test_trade_journal.py(+79): three regression tests — the detection signature,parse_file()economics across three alias spellings, and theanalyze_trade_journalenvelope.Deliberately out of scope:
parse_futu()is unchanged, so a genuine Futu export — which always carriesQuantity— parses exactly as before. Both pinned detection tests use that shape and still pass.parse_futu(). That alternative duplicates logicparse_generic()already documents, and would push a 703-line file toward the 800-line hard cap inCONTRIBUTING.md; this fix takes it to 715.black --checkalready fails on both pristine files under black 26.10.0, andCONTRIBUTING.mdasks not to mix unrelated formatting cleanup into a focused PR.ruff checkpasses on both changed files, and black's proposed changes to the patched files touch only pre-existing lines.A re-routed frame cannot regress: if it carries neither
QuantitynorAmount,parse_futu()could only ever return zero quantity and zero amount for it, so handing it to the alias-aware generic parser cannot do worse. The returned format name has no behavioural consumers —trade_journal_tool.pyonly reports it asformat_detected(now accurate),extractor.pyonly interpolates it into an error string, andbacktester.pydiscards it. Nothing branches onfmt == "futu"outsideparse_file()'s ownfmt == "unknown"check.Test Plan
pytest --ignore=agent/tests/e2e_backtest --tb=short -q)Environment: isolated venv,
pip install -e ".[dev]",pandas 2.3.3/numpy 2.4.6perrequirements-lock.txt, Python 3.11.7 on Windows.main—5 failed, 118 deselected, every failureassert 'futu' == 'generic'.pytest agent/tests/test_trade_journal.py -q→ 123 passed (118 baseline + 5 new).trade_journal_parsers/trade_journal_tool/shadow_accountor referencedetect_format/parse_file→ 521 passed.ruff checkon both changed files → clean.parse_file()before and after.pytest --ignore=agent/tests/e2e_backtest --ignore=agent/tests/test_e2e_harness_v2.py): 45 failed, 16975 passed, 168 skipped, 4 errors on this Windows host. Every one of those failures is pre-existing: re-running the 25 affected files with both changed files reverted toorigin/maingives 47 failed / 393 passed / 4 errors, and the patched failure set is a strict subset of that baseline (node-ID diff). The two extra baseline failures aretest_channel_authoring_contract.py[slack]/[telegram], which also fail when that file is run alone on the patched source, so they are a test-isolation artifact. The failures are Windows symlink-privilege guards, a Unix-domain-socket test, packaging-metadata assertions against an editableuvinstall, and console-encoding collection errors on very large unicode parametrizations — none touches the trade journal.desktop-electron-windows/ Windows background-lifecycle CI jobs, and CI on other operating systems. No live broker, MCP, network, credential or deployment path is exercised — this is pure offline CSV parsing, andpytest-socketis in the dev extra.Risk: none beyond the parse routing described above. Rollback:
git revert.Checklist
src/agent/,src/session/,src/providers/) without prior discussionAGENT_CONTRIBUTOR_GUIDE.mdscopes the README rule to user-facing CLI/Web/MCP/connector/provider/safety-boundary changes.