Commit Graph
103 Commits
Author SHA1 Message Date
Haozhe Wu f917b71960 chore: add local dev workflow 2026-05-06 19:04:43 +08:00
SiMinus ecf1e51915 feat: add swarm preset inspection command (#73) 2026-05-06 19:03:34 +08:00
Haozhe Wu 052277bb56 Harden supplemental security boundaries 2026-05-05 23:30:58 +08:00
Haozhe Wu 7e167ea238 docs: collapse older readme news 2026-05-04 12:15:27 +08:00
Haozhe Wu 688d388551 docs: refresh readme news for cli ux 2026-05-04 12:13:17 +08:00
Haozhe Wu 012a499f74 fix(tests): align CI path expectations 2026-05-04 11:45:55 +08:00
Haozhe Wu 14c97d7c19 Merge pull request #69 from RuifengFu/feat/cli-ux-improvements
feat(cli): add live streaming status indicator and arrow-key navigation to interactive mode
2026-05-04 11:30:58 +08:00
Haozhe Wu 9454d4a27a Harden API and tool security defaults 2026-05-03 22:42:20 +08:00
RuifengFu 5c3d5f98a2 feat(cli): add live status bar and arrow key navigation
Two improvements to the CLI interactive mode:

1. Live status bar: displays provider/model, session duration (real-time
   tick), last run elapsed time, and cumulative tool call stats. Uses
   prompt_toolkit bottom_toolbar with 1s refresh for live updates; falls
   back to static Rich print when prompt_toolkit is unavailable.

2. Arrow key navigation: replaces Rich Prompt.ask() with prompt_toolkit
   PromptSession, enabling up/down history browsing and left/right cursor
   editing. Falls back to Prompt.ask() when prompt_toolkit is not installed
   or stdin is not a tty.

Co-authored-by: RuifengFu <frf2000@tju.edu.cn>
2026-05-02 23:46:47 +08:00
Haozhe Wu fd767f5c29 docs: refresh readme news and roadmap 2026-05-02 18:00:54 +08:00
Haozhe Wu fd7384e472 feat(skills): add dividend analysis skill 2026-05-02 17:51:48 +08:00
Haozhe Wu 3d812ae5ec fix(frontend): proxy correlation endpoint 2026-05-01 19:07:30 +08:00
Haozhe Wu 5bafac3de8 Merge pull request #66 from MB-Ndhlovu/feat/correlation-heatmap
Feat/correlation heatmap
2026-05-01 19:05:50 +08:00
Haozhe Wu 319255c54b docs: sync multilingual news updates 2026-05-01 01:04:13 +08:00
MB-Ndhlovu c3174bacb9 feat: add correlation heatmap dashboard (#64)
Adds the cross-asset correlation endpoint, matrix computation engine, React correlation page, ECharts heatmap, and regression tests for market inference and rolling-window behavior.
2026-05-01 00:44:21 +08:00
ykj@hku 1cbbaab88e feat: add OpenAI Codex OAuth provider option (#65)
Adds the openai-codex OAuth provider, login command, Settings metadata, supported default model, and focused adapter regression tests.
2026-05-01 00:39:15 +08:00
Malibongwe Ndhlovu 72e4488a45 fix correlation PR review blockers 2026-04-30 13:22:11 +00:00
Haozhe Wu 246d5a0dc3 fix(skill): harden ashare penalty relevance filtering 2026-04-30 17:53:16 +08:00
shadowinlife 55972c3e9b feat(skill): add ashare pre-ST filter
Adds the ashare-pre-st-filter skill, Sina regulatory penalty scraper, and regression tests. Follow-up hardening will refine penalty-record relevance filtering.
2026-04-30 17:43:00 +08:00
Malibongwe Ndhlovu b18f22bc94 feat: cross-asset correlation heatmap dashboard
Backend:
- agent/backtest/correlation.py: correlation engine (Pearson/Spearman,
  multi-market loader support, auto market inference from ticker)
- agent/api_server.py: +GET /correlation endpoint
- agent/tests/test_correlation.py: 9 tests (infer_market + core matrix)

Frontend:
- frontend/src/pages/Correlation.tsx: new /correlation page
- frontend/src/components/charts/CorrelationMatrix.tsx: ECharts heatmap
- frontend/src/lib/echarts.ts: register HeatmapChart + VisualMapComponent
- frontend/src/router.tsx: +/correlation route
- frontend/src/components/layout/Layout.tsx: +nav entry
- frontend/src/lib/i18n.tsx: +i18n keys

# Conflicts:
#	frontend/src/components/layout/Layout.tsx
#	frontend/src/router.tsx
2026-04-30 08:00:35 +00:00
Malibongwe Ndhlovu 6512c7ba2c fix(cli): remove broken rich.box import
table.box attribute doesn't exist in this rich version.
table uses default ASCII border instead.
2026-04-30 07:58:30 +00:00
Malibongwe Ndhlovu 27cc7e74a8 fix(cli): remove broken rich.box import 2026-04-30 07:58:30 +00:00
Haozhe Wu cb87c3a37d docs: sync README updates for settings UI and validation CLI 2026-04-30 03:39:17 +08:00
BigNounce f1f3e00c97 fix: validate backtest run_dir CLI input (#60)
Validate the backtest validation CLI run_dir argument and add regression coverage for missing, blank, malformed, missing-path, and non-directory inputs.
2026-04-30 03:26:22 +08:00
TaoMu 6af0a86e2f feat: add model and data source settings UI (#57)
Adds a Settings page for LLM provider configuration and data source credentials, with local/auth protection for settings reads and writes.
2026-04-30 03:22:16 +08:00
warren618 4dcb33034a fix(packaging): bundle swarm presets inside src.swarm (#55) + 0.1.6 release
Move 29 preset YAMLs from agent/config/swarm/ to agent/src/swarm/presets/
so they ship as package-data; loader resolves via
Path(__file__).parent / "presets". Identical behavior under editable
installs and built wheels — fixes "No presets available" reported in #55
after pip install / uv tool install vibe-trading-ai 0.1.5.

Root cause: preset YAMLs were declared via [tool.setuptools.data-files],
which lands them under <install-prefix>/.data/data/config/swarm/ rather
than at the <site-packages>/config/swarm/ path the loader expected.

Pinned by a 6-test packaging regression suite. Bump 0.1.5 -> 0.1.6.
SKILL.md and 5 READMEs synced to reality: 72 skills, 22 MCP tools
(adds 5 shadow-account / journal tool names), 6 data sources
(adds Futu). Adds 2026-04-28 v0.1.6 release news rollup.
v0.1.6
2026-04-28 17:39:19 +08:00
warren618 aae5a7ec1a fix(loader): route ETFs and forex correctly in AKShare; harden registry fallback
Closes #50, closes #54.

akshare_loader._fetch_one previously dispatched every unrecognized symbol to
stock_zh_a_hist. Two consequences:
- Exchange-listed ETFs (518880.SH, 159915.SZ, ...) match the .SH/.SZ tail and
  are misrouted to the A-share endpoint, which can't price them (#50).
- Forex pairs (EURUSD, GBPUSD, ...) match no predicate and fall through to
  the same default branch (#54).

Add two predicates + endpoints:
- _is_etf_listed: 6-digit code with prefix 15/16/50/51/52/56/58 on .SH/.SZ
  -> fund_etf_hist_sina(symbol='sh518880')
- _is_forex: looks up akshare.forex.cons.symbol_market_map (190 pairs)
  -> forex_hist_em(symbol='EURUSD'), with .FX suffix stripped

ETF check runs before _is_a_share so 518880.SH takes the ETF branch.

registry.resolve_loader / get_loader_cls_with_fallback used to construct the
loader before checking is_available(). Tushare's __init__ calls ts.pro_api(token)
and raises 'api init error' on missing token, which broke the fallback chain
(secondary issue from #50). Construction is now wrapped in try/except so init
failures are treated as unavailable and the chain keeps walking.

Tests:
- 21 new unit tests in test_akshare_loader.py (predicates + routing via
  monkeypatched sys.modules['akshare']); no network, no API keys.
- 2 new tests in test_registry.py covering init-error fallback.
- Real-data E2E (manual): loader.fetch returned 242 rows for 518880.SH and
  262 rows for EURUSD over 2024; full runner.main produced metrics for both.

Credit:
- @LaierX for issue #50 root-cause + reference patch
- @yuzawaya for issue #54 root-cause + reference patch
2026-04-27 22:28:14 +08:00
warren618 300f5c2f4f docs: 2026-04-27 news — benchmark panel + upload safety
- Benchmark comparison panel in backtest output (#48, ticker / benchmark return / excess return / information ratio, yfinance-backed)
- /upload streaming + size enforcement (#53) pinned by 4-case regression suite

Across 5 READMEs (en/zh/ja/ko/ar). 04-20 reasoning+swarm entry rolled into 'Earlier news' to keep the 3-latest-visible convention.
2026-04-27 22:01:50 +08:00
warren618 05fc5cac22 test(api): add /upload streaming + size-limit regression tests
Pin PR #53 behavior with 4 cases:
- under-limit upload writes to UPLOADS_DIR
- exactly-at-limit upload succeeds
- over-limit returns 413 and removes the partial file
- blocked extension returns 400 with no file written

Tests shrink MAX_UPLOAD_SIZE to 4 KB via monkeypatch so the streaming
chunk loop and cleanup path run without allocating 50 MB.

Also gitignore AGENTS.md (local mirror of CLAUDE.md for non-Claude
agent tools, identical content, no need to track twice).
2026-04-27 21:53:50 +08:00
Brandy Goodandgenoshide cd999a8d5b fix: stream uploads while enforcing API size limit (#53)
Co-authored-by: genoshide <genoshide@users.noreply.github.com>
2026-04-27 21:37:59 +08:00
warren618 69a4dc69f9 docs: add star history chart to all 5 READMEs 2026-04-27 14:45:26 +08:00
MB-Ndhlovu ffa95a9f51 feat(cli): add benchmark comparison to backtest output
Adds benchmark.py with yfinance-backed ticker resolution (SPY, CSI 300, etc.), injects external benchmark via config['benchmark'] in engines/base.py, and renders a benchmark panel (ticker / benchmark return / excess return / information ratio) in cli.py.

Closes #48
2026-04-27 14:21:50 +08:00
warren618 29fd1f91fa docs: news entries for 0.1.6 hardening + Futu/vnpy integrations across 5 READMEs 2026-04-22 00:03:48 +08:00
warren618 0b9b4d8f64 perf(frontend): route-level lazy loading shrinks initial bundle 688KB → 262KB
Wrap each route in React.lazy() + Suspense so Home/Agent/RunDetail/Compare
download only on first visit. Most importantly, echarts (630KB) now loads
async when a chart-using route is visited instead of eagerly on first
paint. Home and Agent no longer pay the chart import cost.

Measurements (vite build):
  before: index 688KB + vendor-charts 631KB, both eager (~1.3MB blocking)
  after:  index 262KB + vendor-react 101KB + CSS 51KB, ~414KB blocking.
          vendor-charts (630KB) is async, loaded on /runs/:id or /compare.
2026-04-21 09:45:07 +08:00
warren618 5c9488d30b build: add MANIFEST.in so sdist ships .env.example, tests, and Docker files
Previously `pip install vibe-trading-ai --no-binary` missed
`agent/.env.example` and `agent/tests/`, so users who built from
sdist had no template env and no test suite to run. MANIFEST.in also
includes 4 localized READMEs and Docker configs.

Local debug scripts prefixed with `_` in agent/tests/ are explicitly
excluded so untracked files in the working tree never leak to the sdist.
2026-04-21 09:39:55 +08:00
warren618 0a38ba5ef0 fix(security): enforce path containment in safe_path + sandbox journal/shadow tools
- safe_path() now actually raises ValueError on escape / UNC paths
  (existing callers already expected this but never got it).
- New safe_user_path() validates user-supplied broker paths against a
  HOME+CWD envelope, blocking /etc/passwd-style LLM hijack attempts.
- Wire into analyze_trade_journal + 3 shadow account tools.
- 14 new tests in test_path_safety.py cover traversal, UNC, absolute
  escape, and tilde expansion on both helpers.
2026-04-21 09:36:31 +08:00
warren618 8fdf4f8014 chore(loaders): align futu.py with logger convention
Match the existing logger pattern used by akshare_loader, ccxt_loader,
and registry.py. Follow-up to #47 (@hamza-mobeen).
2026-04-21 09:15:56 +08:00
Hamza Mobeen bc62206ad6 feat: add Futu data loader for HK and A-share equities (#47)
Implements FutuLoader using the existing DataLoaderProtocol so users can pull HK and China A-share OHLCV data via Futu OpenAPI instead of yfinance (which frequently rate-limits on HK equity requests).

- agent/backtest/loaders/futu.py: FutuLoader class with symbol mapping (700.HK → HK.00700, 000001.SZ → SZ.000001), interval mapping (1D/1H/4H → KLType), is_available() env-var check, and fetch() that normalises request_history_kline() output to the standard OHLCV schema
- agent/tests/test_futu_loader.py: fully mocked tests — symbol mapping, interval mapping, OHLCV normalisation, is_available() with missing env vars, NoAvailableSourceError on connection failure
- agent/backtest/loaders/registry.py: register futu loader
- agent/.env.example: add FUTU_HOST and FUTU_PORT with comments

Closes #40
2026-04-21 09:13:29 +08:00
Hamza Mobeen 4b28a4d1b4 feat: add vnpy export skill for CtaTemplate strategies (#46)
Adds /vnpy export skill that translates Vibe-Trading backtest strategies into runnable vnpy CtaTemplate Python classes.

- agent/src/skills/vnpy-export/SKILL.md: skill guide with workflow, CtaTemplate structure, ArrayManager indicator mapping, signal→order mapping, multi-timeframe patterns, and quality checklist
- agent/src/skills/vnpy-export/scripts/cta_template.py: complete MA crossover reference implementation (BarGenerator + ArrayManager)
- agent/tests/test_vnpy_export.py: 18 tests covering SKILL.md integrity and template Python syntax/structure (no vnpy install required)

Closes #38
2026-04-21 09:13:16 +08:00
warren618 b76af14d00 docs: news entries for 0.1.5 follow-up patches across 5 READMEs
- 04-21: workspace boundary fix (#43) + README usage examples (#45)
- 04-20: reasoning_content preservation (#39) + swarm streaming (#42)
- 04-18 / 04-17 entries pushed into <details> (top-3 visible convention)
2026-04-21 08:42:26 +08:00
Mothilal M 63ca24867c fix(loop): normalize relative/empty run_dir to memory run_dir (#43)
Hardens against LLMs passing run_dir="." or "" after context compression, which previously resolved against the agent process CWD and exposed repo files (incl. .env) to read_file / write_file and silently mis-routed bash subprocess cwd.
2026-04-20 19:06:57 +08:00
Hamza Mobeen f9022a3ac4 docs: add usage examples to README (#45) 2026-04-20 18:46:00 +08:00
warren618 fc708d15c5 fix(swarm): unblock run_swarm retries and clean Ctrl+C shutdown (#42)
Two follow-ups to b769580 reported by @codewings:

1. "Blocked duplicate call: run_swarm" — loop.py dedups by tool name only,
   and SwarmTool defaulted to repeatable=False. A single successful
   run_swarm per session blocked every subsequent call (retry with a
   different preset, different market, etc). Mark SwarmTool.repeatable=True.

2. threading._shutdown noise on Ctrl+C — `with ThreadPoolExecutor` calls
   shutdown(wait=True) on exit, which blocks indefinitely while kimi
   workers are mid-stream. Switch to manual executor + try/finally with
   shutdown(wait=False, cancel_futures=True); catch KeyboardInterrupt at
   layer boundary to set cancel_event. CLI now returns immediately instead
   of deadlocking on exit.
2026-04-19 22:05:43 +08:00
warren618 a1040bbb71 fix(providers): re-inject reasoning_content on outbound requests (#39)
LangChain's ChatOpenAI strips custom fields across all three serialization
boundaries, but prior commits (73b179a + f866e5f) only patched the two
inbound paths. Assistant messages in context therefore lose reasoning_content
on every ReAct continuation — Moonshot's strict kimi snapshots
(reported as "kimi-k2.5") reject those turns.

Add _get_request_payload override to ChatOpenAIWithReasoning:
  - Copy reasoning_content from additional_kwargs back into the outbound
    messages dict so it echoes across turns.
  - Normalize content=None → "" for assistant messages (LangChain emits
    null when content='' and tool_calls are present, which kimi-k2.5
    rejects).
  - Default missing reasoning_content to "" so strict providers accept
    every assistant turn.

Inbound/outbound symmetry for reasoning_content now covers all three
LangChain serialization boundaries. Verified with real moonshot 2-turn
roundtrip and DeepSeek regression; 22/22 unit tests pass.
2026-04-19 22:05:21 +08:00
warren618 b769580389 fix(swarm): stream LLM calls in workers to unblock moonshot/kimi (#42)
Non-streaming invoke against moonshot is unreliable — kimi-k2 either stalls the
TCP connection (APIConnectionError after 3 min) or exceeds the 120s default
timeout, causing swarm workers to retry-loop forever. Swap worker.py to the
same stream_chat path the main Agent already uses — all other providers are
unaffected. As a bonus the dashboard now receives live worker_text deltas
during kimi's thinking window instead of silent minutes.

Also harden runtime.py: as_completed() now enforces a layer-level deadline
(timeout_seconds * (max_retries+1) + 60s) so any future stuck past the in-loop
check is cancelled cleanly instead of freezing the DAG.

Verified e2e with kimi-k2-0905-preview + equity_research_team:
  - 724 streaming deltas across 3 parallel workers in 8 min
  - 32 tool calls, 0 Request-timed-out retries
  - first dashboard update within 10s (was 10+ min)
2026-04-19 18:51:18 +08:00
warren618 e6ceb5a3b3 refactor(providers): collapse reasoning_content path helpers
Follow-up to f866e5f trimming fallbacks and single-use constants:

  * Collapse _REASONING_FIELDS + _preserve into ChatOpenAIWithReasoning._capture
    (walrus := + or-chained field normalization).
  * Inline extra_body branch in build_llm (kwargs-dict dance removed).
  * Rename _normalize_finish_reason → _dedupe_finish_reason, inline marker
    tuple, switch to endswith() for precision, drop `or "stop"` double default.
  * _parse_response: drop `or []` and `tc.get(..., default)` defenses —
    LangChain BaseMessage contract already guarantees these fields.

No behavior change. Net -44 lines, 64/64 tests green. Smoke re-verified on
Moonshot official and OpenRouter kimi-k2-thinking (effort=medium).
2026-04-19 14:58:06 +08:00
warren618 d4c730213f docs: document LANGCHAIN_REASONING_EFFORT in .env.example 2026-04-19 14:50:30 +08:00
warren618 f866e5fb14 fix(providers): cover streaming path and OpenRouter relay for reasoning_content (#39)
PR #41 only hooked _create_chat_result (non-streaming); swarm workers
use llm.stream() which goes through _convert_chunk_to_generation_chunk
and dropped reasoning_content silently. @codewings' breakpoint never
firing confirmed the miss.

Adds _convert_chunk_to_generation_chunk override. Also handles two
neighbouring quirks found during verification:

  * OpenRouter renames the field to "reasoning" (ships reasoning_details
    alongside). Normalize both variants to the canonical
    additional_kwargs["reasoning_content"].
  * OpenRouter emits finish_reason on every stream chunk;
    AIMessageChunk.__add__ concatenates them into "stopstop" /
    "tool_callstool_calls". Dedupe in _parse_response so ReAct equality
    checks survive.
  * LANGCHAIN_REASONING_EFFORT env forwarded as extra_body.reasoning.effort
    for relays (e.g. OpenRouter) that require explicit thinking activation.

Removed 3-way getattr fallback in _parse_response; downstream reads
single canonical key.

Verified end-to-end (tests/_smoke_reasoning.py + _smoke_swarm_roundtrip.py):
  * Moonshot kimi-k2-thinking (official) — stream + non-stream + 2-round ReAct
  * DeepSeek deepseek-reasoner (official) — stream + non-stream + 2-round ReAct
  * OpenRouter moonshotai/kimi-k2-thinking with effort=medium
  * OpenRouter qwen/qwen3-coder-flash — non-thinking path unaffected

Tests: 66 passed (12 new: 2 field normalization, 7 finish_reason dedupe,
3 extra_body passthrough).
2026-04-19 14:49:51 +08:00
warren618 b6adee5651 fix(tests): patch ChatOpenAIWithReasoning in minimax temperature tests
build_llm() now returns ChatOpenAIWithReasoning (introduced in 73b179a)
instead of ChatOpenAI. The minimax temperature clamping tests were still
patching ChatOpenAI directly, so the fake was never hit and the real
subclass was instantiated — causing KeyError: 'temperature' on assert.
2026-04-19 12:22:38 +08:00
warren618 73b179a28d fix(providers): subclass ChatOpenAI to preserve reasoning_content
PR #41 fixed the protocol layer (LLMResponse / format_assistant_tool_calls)
but langchain-openai 0.3.x's _convert_dict_to_message silently drops
unknown fields — reasoning_content never reached our _parse_response,
so the fix was a no-op end-to-end.

Add ChatOpenAIWithReasoning: subclass that overrides _create_chat_result
to re-read the raw API response and restore reasoning_content into
additional_kwargs. PR #41's 3-location fallback in _parse_response then
picks it up without changes.

Benefits all OpenAI-compat thinking providers: Moonshot K2.5, DeepSeek
reasoner, Qwen thinking (Qwen-QwQ, Qwen3-thinking).

Reported by @codewings on issue #39 after PR #41 shipped.
2026-04-19 12:13:54 +08:00