Most of the almost-surely-profitable pipeline now treats non-finite floats as invalid before they reach JSON. The TradingAgent decision history was one of the last boundaries still using raw json.dump.
The gap
In src/llm/trading_agent.py, save_decision() wrote the decision list with
json.dump(decisions, f, indent=2)
json.dump defaults to allow_nan=True, so any NaN or Infinity value in a parsed LLM response would be persisted as the non-standard NaN/Infinity tokens. Downstream consumers that expect strict JSON then fail when they reload the history. The __main__ demo had the same problem when pretty-printing the decision.
The fix
I reused the shared serialization helpers already present in the repo:
save_decisionusesdump_json_safe. Whenutils.dump_json_safeis available, the decision list is serialized through it; otherwise the code falls back to the originaljson.dumpso the module still runs on incomplete installs.- Demo output is sanitized. The
__main__block now runs the decision throughsanitize_for_jsonbeforejson.dumpsso the printed summary is also strict JSON. - Regression test.
test_save_decision_sanitizes_non_finite_valuescreates a decision withNaNandInfinityaction percentages, saves it, asserts the file contains neitherNaNnorInfinity, and checks that the reloaded values areNone. - Benchmark. A new benchmark compares
json.dumpsbaseline, a cleansave_decision, and a non-finitesave_decision, validating that every saved file is strict JSON.
Why this matters
The decision history is the record the strategy uses to reflect on its own past. If that record contains NaN, later analysis, reports, or LLM prompts built from the history become unreliable. Centralizing the fix in the shared utils module keeps the boundary behavior consistent with the rest of the codebase.
Verification
- 1 regression test added in
tests/test_trading_agent.py. benchmarks/benchmark_trading_agent_json_boundary.pymeasures the clean and non-finite save paths.- Full suite: 1013 passed under
pytest tests/ -q -W error::RuntimeWarning. - Import sorting verified via
ruff check --select Ion changed/new files. - Daily dry-run completed successfully with strict JSON output.