Commit Graph

313 Commits

Author SHA1 Message Date
Yijia-Xiao
d8eceb6571 feat(cli): add a backtest command (#1234)
- analysis stays the bare invocation; backtest runs a ticker and date grid
- selections name the models as the config does
- the pending note prints only when a cell is pending
2026-09-16 21:45:08 +00:00
Yijia-Xiao
63738c8f2c docs: drop the analyze subcommand from the CLI examples 2026-09-16 20:19:49 +00:00
Yijia-Xiao
7bf2117491 docs: document the portfolio input and decision evaluation 2026-09-16 20:19:49 +00:00
Yijia-Xiao
8721b92f65 feat: evaluate decisions over a grid of tickers and dates (#1234)
- run_backtest runs the pipeline per cell into its own decision log and resumes by skipping logged cells
- summarize scores settled cells by rating on realized alpha
- settle_pending settles a ticker whose last decision would otherwise stay open
2026-09-16 20:19:49 +00:00
Yijia-Xiao
9794a90734 fix(memory): keep a settled decision from being logged twice
- the duplicate guard matched only pending entries, so re-running a settled ticker and date appended a second one
2026-09-16 20:19:49 +00:00
Yijia-Xiao
6436d1ff30 feat(graph): accept the caller's portfolio as run input (#1304, #1166)
- PortfolioContext input, rendered once into state and read by the trader, risk and portfolio agents
- --portfolio JSON file on the CLI
- a run without it is never treated as a flat book
- the checkpoint signature keys on the portfolio
2026-09-16 20:19:49 +00:00
Yijia-Xiao
dffff22951 fix(dataflows): use configured defaults for omitted Alpha Vantage global-news options (#1326) 2026-09-15 01:33:48 +00:00
Yijia-Xiao
ef52d4452b docs: add uv install and Docker rebuild notes (#1173, #1297)
- uv alternative to conda and pip
- rebuild the Docker image after updating
2026-09-15 01:33:48 +00:00
Yijia-Xiao
2942655f70 fix(dataflows): map Shanghai .SH tickers to Yahoo's .SS (#1260)
- normalize_symbol maps 600519.SH to 600519.SS
- the alpha benchmark is resolved from the normalized symbol
2026-09-15 00:04:25 +00:00
Yijia-Xiao
fadc698e20 fix(dataflows): bound insider filings and prediction markets by the trade date
- insider transactions are filtered to filings on or before the run date
- prediction-market odds are withheld from a historical run
2026-09-15 00:00:26 +00:00
Yijia-Xiao
29e331a9af fix(dataflows): keep one OHLCV cache file per symbol (#1330)
- the cache file is keyed by symbol and serves only on the day it was written
2026-09-14 23:51:48 +00:00
Yijia-Xiao
99313bfdbc fix(dataflows): zero-pad HK ticker codes to Yahoo's 4-digit form (#1342)
- normalize_symbol maps 09992.HK to 9992.HK and 700.HK to 0700.HK (#957)
2026-09-14 23:37:48 +00:00
Yijia-Xiao
d04693a208 fix(agents): bound tool dates by the run's trade date
- dated tools read trade_date from graph state and clamp later or missing dates #1331
- propagate() rejects non-canonical and future trade dates #1319
2026-09-14 23:24:31 +00:00
Yijia-Xiao
4a9f196e92 fix(cli): read and write the decision log on the CLI path
- shared create_run_state and record_decision for propagate() and the CLI #1332 #1347
2026-09-14 23:12:17 +00:00
Yijia-Xiao
2c1ba388d6 chore: remove two unused scripts
- test.py
- scripts/smoke_structured_output.py
2026-09-14 23:12:17 +00:00
Yijia-Xiao
b9666f6e09 docs: use a current date in the usage examples 2026-09-14 22:38:19 +00:00
Yijia-Xiao
34899bd320 fix(cli): save prompted API keys to an owner-only .env
- the key prompt created .env with the default umask, typically readable by
  other local users
- create it 0600 and tighten an existing file before writing the key; a
  read-only file is still updated
2026-09-14 22:38:19 +00:00
Yijia-Xiao
b20c8e60a4 fix(dataflows): keep vendor API keys out of request errors
- FRED and Alpha Vantage authenticate with a query parameter, and requests
  quotes the full URL in HTTP, connection and timeout errors, so the key was
  written into any log or traceback that recorded one
- route both vendors through one request helper that re-raises a requests error
  as the same class with the key removed, carrying no request, response or
  exception chain, each of which still held the URL #1324
2026-09-14 22:38:19 +00:00
Yijia-Xiao
241638da68 fix(dataflows): search all subreddits in one Reddit request
- anonymous RSS allows about one request per minute per IP, so fetching each
  subreddit separately hit a 429 and a 60s back-off on nearly every run
- search the subreddits as one combined feed at Reddit's full page size and
  group posts by the subreddit each entry names; each subreddit keeps its own
  limit, and a full page is not taken as evidence of absence
- drop the unused JSON search path, still blocked with a 403, and the
  per-subreddit pacing; arguments after subreddits are keyword-only
- the sentiment prompt no longer asks for vote and comment counts, which the
  RSS feed does not carry
2026-09-14 22:07:42 +00:00
Yijia-Xiao
2e38b47dca fix(dataflows): report windows a feed cannot observe as unavailable
- Yahoo news and the Reddit and StockTwits feeds serve only recent items, so a
  historical window trimmed to nothing was reported as "no news" or "no
  posts", and the sentiment analyst scored that silence as a neutral signal
- judge each empty window in one shared rule: it is a real absence only when
  the feed's coverage reaches the window's first day and the window ends by
  today; otherwise report it unavailable with where coverage starts
- coverage comes from the returned timestamps, which are newest-first on these
  feeds, plus Reddit's one-week search lookback; merged global-news searches
  prove no continuity and are bounded by the present alone
- state in the sentiment analyst that historical sentiment inputs are not
  guaranteed to be point-in-time
2026-09-14 22:07:41 +00:00
Yijia-Xiao
673abb3c68 feat(llm): add GPT-6 Astra, Gemini 3.8 Flash and the current Claude models
- OpenAI: gpt-6-astra
- Google: gemini-3.8-flash and gemini-3.5-flash-lite
- Anthropic: claude-opus-5 and claude-fable-5-1
- models taken out of the picker stay valid through LEGACY_MODELS, so configs
  that name them run without an unknown-model warning
2026-09-14 18:09:33 +00:00
Yijia-Xiao
9b4c741d33 fix(llm): forward reasoning effort to GPT-6 and gate minimal thinking
- reasoning_effort was forwarded only to IDs matching gpt-5 or the o-series, so
  GPT-6 models silently dropped the configured effort; match GPT-5 and later,
  with a version boundary so unrelated IDs do not match
- Gemini Pro, 3.8+ and the -latest aliases reject thinking_level "minimal"
  with a 400; send it only to numbered Flash models before 3.8 and map it to
  "low" elsewhere, since aliases move between generations
2026-09-14 18:09:33 +00:00
Yijia-Xiao
62d3479217 fix(agents): stop conflict alone from defaulting the verdict to Hold
- both manager prompts and both rating fields listed "materially conflicting"
  as a Hold condition, and every bull/bear and risk debate is conflicting, so
  a modest directional edge collapsed to Hold
- state one rule at all four sites: conflict alone is not a reason to Hold,
  commit to the stronger side sized by how decisively it wins, and Hold only
  when the evidence is still balanced after weighing or too thin for a call
- a modest bull edge now resolves to Overweight; a genuinely balanced debate
  still resolves to Hold #1321
2026-09-14 18:09:33 +00:00
Tauric-Research
be952b8ecc Merge pull request #1310 from TauricResearch/v0.4.2
Point-in-time fixes, honest failure reporting, and housekeeping
2026-09-07 17:30:55 -05:00
Yijia-Xiao
821848bb82 docs: tighten the comments on the point-in-time guards
- keep what the code cannot state itself: which session a closeless bar is,
  where the drop actually happens, and why the trim must stay unguarded
- drop the field-by-field enumeration, the account of what the previous
  behaviour got wrong, and the restatements of adjacent calls
2026-09-07 22:21:20 +00:00
Yijia-Xiao
d6ca23aee5 feat(llm): add the current Kimi models to the picker
- list kimi-k3 (flagship, 1M context) and kimi-k2.6 (256K, thinking modes) on
  both tiers, replacing the custom-only entry
- keep Custom model ID for models newer than the list
- omit the k2.7-code variants: coding specialists, not analysis models
2026-09-07 21:52:29 +00:00
Yijia-Xiao
ef383df8f4 fix(dataflows): don't report a symbol as unavailable over an unsettled bar
- a newest bar with no close made load_ohlcv reject the whole frame, so the
  routing layer answered with its no-data sentinel: the caller lost the entire
  price history and was told the symbol may be invalid, delisted or not
  covered, when only the latest session had not settled
- treat a closeless newest bar as an unsettled session instead. The gap fill
  already drops it, here and mid-series alike, so the frame ends at the last
  settled bar; only a range with no close anywhere is still no data
- the staleness check keeps deciding whether what remains is recent enough, so
  falling back cannot resurrect a long-dead series
- log which bars had no close and which date is being used as the latest close
2026-09-07 21:42:25 +00:00
Yijia-Xiao
d58b838081 test: keep the Ollama endpoint assertions off the terminal colour
- the console highlights numbers and URLs, so with colour enabled the rendered
  output splits asserted substrings with escape codes and two tests fail
- strip the codes before asserting so the result no longer depends on where the
  suite runs
2026-09-07 21:28:52 +00:00
Yijia-Xiao
ffd5d9a180 chore(dataflows): use the module logger and drop dead helpers
- four modules wrote to stdout with print() while ten others use a module
  logger; a warning printed into the rendered CLI output is effectively
  invisible, which is how the trim failure above went unnoticed
- convert the remaining calls to logger.warning with lazy formatting
- remove save_output, SavePathType, decorate_all_methods and get_next_weekday
  from utils, none of which had a caller, along with the pandas and typing
  imports that only they needed
2026-09-07 21:28:52 +00:00
Yijia-Xiao
16f7fd613c fix(dataflows): fail closed when the Alpha Vantage date trim fails
- get_stock requests the full daily series up to today, so trimming to the
  requested window is the only thing keeping bars after end_date out of a
  historical run
- the trim caught every exception, warned, and returned the untrimmed body, so
  a parse failure fed future prices into a backtest with no usable signal that
  it had happened
- let a parse failure propagate instead: the routing layer already logs the
  vendor failure, falls through to the next vendor, and surfaces the real error
  if none can serve
2026-09-07 21:28:52 +00:00
Yijia-Xiao
260c899c72 chore: drop the lint exclude for a directory not in the repo
- ruff's extend-exclude listed a path that is not part of the repository, so
  the entry never matched anything in CI
- keep the generated results/ exclude, which is a real runtime output directory
2026-09-07 20:54:19 +00:00
Yijia-Xiao
94113c8d11 test: stop the sentiment-analyst tests hitting the live network
- create_sentiment_analyst pre-fetches news, StockTwits and Reddit before
  prompting, and TestSentimentAnalystAgent invoked it unstubbed, so every run
  made live requests and a real 429 stalled the suite for minutes
- stub the three sources as the sibling prompt tests already do; the file drops
  from ~84s to under a second, and the suite no longer depends on Reddit or
  Yahoo being reachable
2026-09-07 20:54:19 +00:00
Yijia-Xiao
7cc478ad07 fix(dataflows): report a failed Reddit fetch as unavailable, not silence
- a failed fetch and an empty search both returned [], so a 429 rendered as
  'no posts found' and the sentiment analyst read throttling as real silence;
  when every subreddit was throttled the summary asserted it outright
- a failed fetch now returns None and renders as unavailable, and the summary
  only claims silence for subreddits actually searched
- raise the headerless-429 back-off to 60s, which is where a retry starts
  succeeding; pay it at most once per run so three throttled subreddits do not
  stall the analysis, and match the Retry-After cap to it #1295
2026-09-07 20:54:19 +00:00
Yijia-Xiao
1c44dd1ffc fix(agents): require absolute price levels from the Trader
- asking the Trader for concrete entry/stop levels invited a percentage
  (stop_loss: '15%'), which is not a price and failed the whole structured
  parse, dropping the run to a free-text retry
- state the requirement in the prompt and in both field descriptions
- a percentage now nulls that field instead of failing the proposal; it is
  never salvaged, since 15% must not become a 15 stop. Human-formatted
  prices with a currency symbol or thousands separator parse #1288
2026-09-07 20:54:19 +00:00
Yijia-Xiao
96111aa368 fix(dataflows): withhold the live profile from historical fundamentals
- both fundamentals vendors accepted curr_date and ignored it, serving a
  present-day company profile into a run dated in the past: yfinance via
  Ticker.info, Alpha Vantage via OVERVIEW
- that profile has no historical vintage, not even name/sector/industry (which
  move when a company renames or is reclassified), so a past curr_date now
  withholds it and says why; live runs are unchanged
- the rule lives once in date_window next to the existing look-ahead helpers,
  so switching data_vendors between the two cannot reintroduce the leak, and
  the guard runs before the request rather than discarding a paid-for response
- point-in-time fundamentals for a past date already come from the balance
  sheet, income statement and cash flow tools, which filter on curr_date #1300
2026-09-07 20:54:19 +00:00
Yijia Xiao
9dee508c44 Merge pull request #1285 from TauricResearch/v0.4.1
Post-v0.4.0 fixes: FRED vintage, Reddit 429, debate neutrality, feed bound
2026-09-01 00:38:45 -05:00
Yijia-Xiao
5a26ae17a1 harden(dataflows): bound the Reddit feed read before parsing
- ElementTree does not resolve external entities, so the reported XXE flag
  doesn't apply; the real residual is an unbounded read of untrusted network XML
- cap both the RSS and JSON reads at 5 MiB; overflow degrades to empty / RSS
  fallback through the existing failure paths #1206 #1276
2026-09-01 05:14:23 +00:00
Yijia-Xiao
a4acd8a174 fix(agents): stop the debate managers forcing a direction under ambiguity
- the managers reserved Hold only for "genuinely balanced" evidence and were
  told to "be decisive", pressuring a directional call on ambiguous, conflicting,
  or insufficient inputs; which side it landed on was model-prior-dependent
- allow Hold for balanced, conflicting, ambiguous, or insufficient evidence in
  both manager prompts and both structured rating fields, and weigh cases
  independent of speaking order; rating definitions and debate ordering unchanged #1196
2026-09-01 05:08:49 +00:00
Yijia-Xiao
2322dd9baa fix(dataflows): honour Reddit Retry-After: 0 and jitter 429 backoff
- a valid Retry-After: 0 means retry at once but was treated as absent
  (`or 5.0`) and waited 5s; honour it exactly now
- jitter our own headerless fallback and the inter-subreddit pacing so several
  analyses sharing an IP don't retry in lockstep and re-collide on the limit;
  keep the single-retry ceiling (more retries can't fix an exhausted IP budget) #1193
2026-09-01 05:02:36 +00:00
Yijia-Xiao
70b58c21dc fix(dataflows): clamp the FRED vintage pin to FRED's own clock
- the unconditional realtime pin 400s when curr_date is ahead of FRED's
  US-Central date (a live run's local date), which the router then degrades to
  a silent DATA_UNAVAILABLE — an Asia/Pacific run loses macro data
- clamp realtime_start/end to min(curr_date, FRED-today) via pytz Chicago;
  a past curr_date pins unchanged, so historical look-ahead safety is preserved
- name the vintage in the empty-result message: widening the window can't fix a
  series with no vintage coverage #1275
2026-09-01 04:58:53 +00:00
Yijia Xiao
2448d0a125 Merge pull request #1280 from TauricResearch/v0.4.0
Release v0.4.0
v0.4.0
2026-08-30 22:07:21 -05:00
Yijia-Xiao
c95f83dfaf chore: release v0.4.0
- look-ahead / point-in-time fixes across FRED macro, social sentiment, and the
  decision-log memory; clearer decision signals; CLI checkpoint resume; Trader
  price grounding
- GPT-5.6 / GLM-5.3 models
2026-08-31 03:02:39 +00:00
Yijia-Xiao
ecbe3e3a21 feat(llm): add the GPT-5.6 family and GLM-5.3
- GPT-5.6 (sol/terra/luna) is GA and OpenAI's recommended default; add it and
  make gpt-5.6 (deep) / gpt-5.6-luna (quick) the defaults
- add GLM-5.3 and GLM-5.3-Flash, Zhipu's current flagship line
2026-08-31 02:55:21 +00:00
Yijia-Xiao
e93c5c53c2 fix(agents): ground the Trader in the technical market report
- the Trader received only the digested investment plan, so its entry / stop /
  sizing levels were not anchored to real price structure (ATR, support and
  resistance, current price)
- inject the market report and instruct the Trader to take concrete price levels
  from it and direction/strategy from the plan; when the market analyst was not
  selected the report is empty, so the section and grounding note are omitted #1167
2026-08-31 02:35:50 +00:00
Yijia-Xiao
45c1744b86 fix(llm): apply DeepSeek capabilities to OpenRouter-namespaced models
- OpenRouter exposes DeepSeek as deepseek/<id>, which matched neither the exact
  IDs nor the patterns, so a thinking model like deepseek/deepseek-v4-flash fell
  through to _DEFAULT and had object-form tool_choice forced on it
- strip the official deepseek/ namespace before lookup so it reuses the native
  quirks; deepseek/deepseek-chat still keeps tool_choice, and third-party
  finetunes on other publishers stay on _DEFAULT #1199
2026-08-31 02:15:32 +00:00
Yijia-Xiao
63be7fe7f1 fix(dataflows): don't silently drop the latest OHLCV bar
- the latest in-range bar with a NaN close was dropped before the curr_date
  cutoff, so the previous trading day looked like the latest; dates were also
  compared without timezone normalization
- normalize bar dates and curr_date to naive midnight (per element, so 5-year
  ranges spanning DST and non-US positive-offset markets keep their local date),
  then raise NoMarketDataError on a missing latest close rather than falling back
- split the fill step (_fill_price_gaps) from date/price normalization so the
  latest bar can be inspected before incomplete rows are dropped #1201
2026-08-31 02:08:31 +00:00
Yijia-Xiao
30d42abd5d fix(memory): don't settle a decision before its holding window trades
- _fetch_returns settled on min(holding_days, available), so a rerun a day or
  two after a decision reflected on a 1-2 day partial return as if final
- require the full holding window in both the stock and benchmark series before
  resolving; otherwise leave the entry pending to retry next run
- this also makes the #1251 resolution date the full-window date, not a partial
  bar's #1169
2026-08-31 01:55:31 +00:00
Yijia-Xiao
a2f51da917 chore: remove dead code found in the v0.4.0 review
- inline _in_news_window, a trivial passthrough left from extracting
  dataflows.date_window.in_window; call in_window directly
- drop SignalProcessor's orphaned quick_thinking_llm attribute (unused since
  rating extraction became a deterministic parse)
2026-08-31 01:29:14 +00:00
Yijia-Xiao
b43bc31479 fix(cli): resume a checkpoint without duplicating messages or leaking the saver
- on resume, the CLI and propagate re-passed the initial state to a thread with
  an existing checkpoint; nodes do not re-run, but the message reducer appended
  the initial messages again, duplicating them in the resumed state
- feed None on resume (checkpoint_input) so LangGraph continues the interrupted
  run, and wrap the CLI stream in try/finally so the checkpointer tears down even
  if the stream raises
- correct the _fetch_returns docstring to the 4-tuple return #1249
2026-08-31 01:29:14 +00:00
Yijia-Xiao
8db41f6bca fix(memory): gate past-context lessons to point-in-time in backtests
- get_past_context returned every resolved lesson regardless of the run date, so
  a historical run could learn from an outcome that had not happened yet
- record each resolved entry's resolution date (the last price bar used) and
  filter get_past_context(as_of=trade_date) on it for a historical run; a
  current-date run passes None so live behavior and pre-migration entries (no
  stored resolution date, conservatively excluded from backtests) are unaffected #1251
2026-08-30 07:03:06 +00:00