A cluster of provider reports traced to one root: every OpenAI-compatible
provider shared a single shim that applied DeepSeek/Kimi/Gemini quirks
globally and silently swallowed stream failures.
- Provider capability layer (capabilities.py): reasoning capture/replay,
Gemini thought signatures, Kimi User-Agent (version from package
metadata), OpenRouter reasoning body — gated per provider, no
cross-contamination
- Optional native DeepSeek adapter via VIBE_TRADING_DEEPSEEK_ADAPTER
(auto/native/openai-compatible) + 'deepseek' extra (langchain-deepseek)
- ChatLLM.stream_chat: silent fallback to chat() removed; failures raise
ProviderStreamError with redacted provider/model context; one retry for
transient errors in AgentLoop + swarm worker (deterministic 4xx fail fast)
- Empty model response diagnosed as empty_model_response instead of
'reached max iterations'
- reasoning_delta SSE event (throttled to 1/s, first chunk immediate) +
frontend 'Reasoning…' indicator; reasoningActive reset on safety timeout
- SSE heartbeats carry no replay id; replay falls back to full buffer on
unknown last-event-id; read-only tools get a hard timeout
(VIBE_TRADING_TOOL_TIMEOUT_SECONDS) instead of hiding behind heartbeats
- 'vibe-trading provider doctor' prints redacted provider/model/package/
proxy diagnostics; preflight includes the same hint
- kimi-k2.x temperature=1 requirement auto-applied; Kimi path verified
end-to-end against the live API (kimi-k2.6 tool calls + strict
multi-turn reasoning replay)
Refs #208#203#195#204
Three interrelated fixes for trace.jsonl field consistency:
1. Field order — every entry now has `iter` immediately after `type`.
This was already the convention for tool_call, tool_result, thinking,
answer, and cancelled. Fixed start, message (user+assistant), and end
to follow the same pattern.
2. Iteration values — start and message (user) previously used
`self._run_iteration` (which is 0 before the loop begins), so the
first turn showed iter=0 while the first loop iteration showed iter=1.
Changed to `self._run_iteration + 1` so they share the same iter as
the first loop iteration. end now carries `iter` in addition to
`iterations` (the per-run count), so it participates in iteration
grouping like every other entry.
3. Resume ordering — the iteration-counter restore logic (reading max
iter from an existing trace to continue a session) was placed AFTER
start/message were written, so resumed sessions had iter=0 on those
entries. Moved the resume block before the write calls.
Tests added (TestTraceFieldConsistency):
- test_tool_result_iter_second — write_tool_result() dict order
- test_generic_write_preserves_type_then_iter — generic write order
- test_all_entry_types_carry_iter — 10 entry types all have iter at
position 1; catches future regressions
Closes the long-standing gap where start/message/end could not be
attributed to a specific turn in multi-turn sessions.
Signed-off-by: zwrong <1003912034@qq.com>
All user-level agent state lives under ~/.vibe-trading in the container
(persistent memory, sessions.db search index, user-created skills,
shadow accounts, hypothesis registry, broker connector config), but
docker-compose only mounted volumes for web sessions and backtest runs.
Every `docker compose up --build` therefore recreated the container and
silently wiped the user's memory store (#197). Swarm run history and
uploads were lost the same way.
- Mount vibe-home -> /home/vibe/.vibe-trading, plus vibe-swarm-runs and
vibe-uploads.
- Pre-create /home/vibe/.vibe-trading owned by the service user in the
Dockerfile so the named volume inherits writable permissions (a named
volume mounted onto a missing path is created root-owned and the
non-root user cannot write).
- README: document what persists across updates.
Verified end-to-end: memory written via PersistentMemory in one
container is intact in a fresh container on the same volume; the
counterfactual run without the volume reproduces the wipe.
Fixes#197.
* fix(docker): reach host Ollama via host.docker.internal
Inside the container, OLLAMA_BASE_URL=http://localhost:11434 resolves to
the container itself, so the agent's preflight check and AgentLoop fail
with "Connection refused" even when Ollama is running on the host.
Override OLLAMA_BASE_URL to http://host.docker.internal:11434 in the
compose service (leaving agent/.env correct for bare-metal runs) and add
an extra_hosts host-gateway mapping so the same file also works on Linux.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(docker): parameterize OLLAMA_BASE_URL and document host networking
Address PR review feedback:
- Make OLLAMA_BASE_URL overridable via Compose variable substitution
(${OLLAMA_BASE_URL:-http://host.docker.internal:11434}) so it can be
changed without editing the compose file when Ollama runs elsewhere.
- Note the Docker Engine >= 20.10 / Compose v2 requirement for the
host-gateway mapping, both inline and in the README Docker section.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Shah Newaz Khan <shah.khan@lexful.ai>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Grounding previously only matched loader-suffixed symbols (NVDA.US),
so an investment_committee run on "long or short on NVDA" got no
pre-fetched ground-truth prices and workers fell back to ad-hoc
scripts (#198, fix direction 5).
Promote bare all-caps tokens to <TOKEN>.US under guards:
- 2-5 uppercase letters only, dotted compounds rejected on both sides
(FOO.USDA promotes neither side; sentence-ending "NVDA." still works)
- stopword list for finance/English acronyms (ETF, CEO, GDP, USD,
bare crypto symbols, index providers like MSCI, ...)
- suffixed matches are blanked before the bare scan, so BTC-USDT can
never leak a bogus BTC.US
- promotions sort after explicit symbols, so explicit symbols win the
max-symbols cap; the per-symbol fetch remains the final validator
Closes the last open item of #198.
PR #199 added the loader-backed get_market_data tool to
investment_committee and equity_research_team. Extend it to the 16
remaining workers (across 11 presets) whose skills include tushare,
yfinance, or okx-market, so every OHLCV-capable worker gets the
loader-first path and the worker prompt policy.
Add a regression test locking the convention: any preset worker with a
market-data skill must whitelist get_market_data.
Part of #198.
The streaming safety timeout guarded on lastEventRef.current being truthy, but
it starts at 0 and only real SSE events (not the keep-alive heartbeat) update
it. A turn whose very first event never arrives — e.g. the LLM provider hangs
before emitting a token — left it at 0, so the guard short-circuited and the UI
stayed on "Agent is working…" indefinitely. Arm the clock on entering the
streaming state so the timeout fires after sse_timeout and resets to idle with a
notice. Turns issue #195's hang into a recoverable timeout.
Two whitelists/maps had drifted as mootdx and futu loaders were added:
1. BacktestConfigSchema._VALID_SOURCES rejected an explicit
source="mootdx"/"futu" even though both are registered loaders (and in
FALLBACK_CHAINS), while src/tools/backtest_tool.py already allowed them.
Fix: one canonical VALID_SOURCES in backtest.loaders.registry, consumed by
both backtest.runner and src.tools.backtest_tool so they cannot diverge.
2. metrics annualisation maps (_TRADING_DAYS / _BARS_PER_DAY) lacked mootdx and
futu, so intraday backtests on them fell back to bars_per_day=1 and
mis-annualised vol/Sharpe. Fix: add both as equity sources (252 days, 240-min
session, mirroring tushare/akshare).
Regression tests: mootdx/futu accepted by the config schema, every registered
loader covered by VALID_SOURCES, and intraday bars_per_year correct for both.
Chat sends from a non-loopback client (another machine, VM host, LAN
device) without API_AUTH_KEY returned 403 on every sensitive endpoint,
but the UI only showed a generic "Failed to send message, please retry."
The send path now surfaces AUTH_REQUIRED_MESSAGE so users know to set
API_AUTH_KEY (and enter it once in Settings) or browse via localhost.
Also document the localhost-vs-LAN rule in the web-UI setup and add a
2026-06-09 news entry across all 5 locale READMEs.
The Path E "pull a pre-built image, no clone" deploy path was open-box
broken: the GHCR package defaults to private, so anonymous `docker pull`
fails, and the path was never verified end-to-end against the main repo
(the contributor tested a self-built image, not ghcr.io/hkuds/vibe-trading).
Removes the publish workflow, docker-compose.prod.yml, the Dockerfile
VERSION arg, and the README Path E section/table row/tree note. The
2026-06-08 news entry is rewritten to cover only the Gemini 3.x
dict-path fix (#184). Also drops the now-orphan edge-build paths-ignore.
If a buildless deploy path is wanted later, do it properly: make the
package public, verify an anonymous pull works, then document it.
Release builds still always fire via the release trigger; only the edge-on-main-push build is skipped when a commit touches nothing but .md / docs/ / wiki/.
#176 added the Gemini thought_signature round-trip but only covered the
in-memory AIMessage path its unit tests exercise. The AgentLoop replays
conversation history as OpenAI-format dicts, stamping the signature into
tool_calls[i].extra_content.google.thought_signature
(loop.py _attach_tool_call_thought_signatures).
LangChain's _convert_dict_to_message discards extra_content during the early
_convert_input step of invoke()/stream() -- before _get_request_payload
re-injects -- so _signature_maps reads an empty additional_kwargs and the
outbound request goes out unsigned. Gemini then rejects the next tool-using
turn with HTTP 400 "Function call is missing a thought_signature in
functionCall parts". This broke every tool-using loop turn on gemini-3.x
(single or parallel), e.g. the parallel load_skill case.
Override ChatOpenAIWithReasoning._convert_input -- the single chokepoint both
invoke and stream call while input is still raw dicts -- to lift signatures
back onto the converted AIMessage in the same
additional_kwargs["tool_call_thought_signatures"] shape the in-memory path
uses, so the existing _signature_maps / _inject_tool_call_thought_signatures
machinery handles both paths identically. Idempotent via an
isinstance(raw, dict) guard. No parallel-specific handling needed: Gemini
signs only the first of N parallel calls and that signature covers the block.
Add regression tests in TestChatOpenAIWithReasoningOutboundPayload that drive
the real dict path (the gap #176's tests missed): red without the fix
(KeyError: 'extra_content'), green with it.
Head-to-head comparison of hand-picked Alpha Zoo alphas, sharing one core
(compare_runner.compare_alphas) across four surfaces:
- CLI: `vibe-trading alpha compare <id1> <id2> ... --sort ir`
- REST: POST /alpha/compare + SSE progress/result stream
- Web UI: Compare view in Alpha Zoo (catalogue multi-select -> ranked table)
- Agent: read-only `alpha_compare` tool (now 47 tools)
Adds a backward-compatible `only=` subset filter to run_bench so comparing
a few alphas no longer benches the whole zoo. Ranks by IC mean/std, IR,
IC-positive ratio or sample count, each with its gap to the leader.
39 new backend tests; frontend tsc + 197 vitest + build green.
Bump react-router-dom ^7.1.0 -> ^7.15.0 (resolves to 7.17.0), clearing 6 react-router advisories (XSS, open redirect, DoS, turbo-stream arbitrary constructor; patched across 7.13.2-7.15.0).
Bump vitest + @vitest/coverage-v8 ^3.2.6 -> ^4.1.0 (resolves to 4.1.8), clearing the critical Vitest UI-server arbitrary file read/exec (patched in 4.1.0) and aligning the coverage peer dependency on the v4 line. Frontend suite (197 tests) green on vitest 4; production build passes.
Cherry-picked the test-infra commit from #175 (sambazhu): vitest setup + 19 frontend test files (197 tests), backend auth/path-traversal/CORS tests, yfinance crypto fallback (BTC-USDT->BTC-USD), and CI coverage + frontend-test steps. Dropped the PR's separate i18n commit to stay consistent with #163.
Fix: align @vitest/coverage-v8 to ^3.2.6 (was ^4.1.8, incompatible with vitest ^3.2.6 - blocked npm install and the new npm ci CI step).
Cherry-picked the factor-module Chinese docstrings from #180 (alpha101 / gtja191 / qlib158 / academic, 452 files, purely additive). Dropped the i18n parts — react-i18next deps + 29 swarm-preset name_zh/description_zh (currently unused) — to stay consistent with the #163 decision to not bundle i18n yet.
Two broker_sdk connectors for the Indian equity and F&O markets (NSE/BSE):
Dhan (dhanhq) and Shoonya / Finvasia (NorenRestApiPy). Read access plus
locally simulated paper-account order placement.
Both brokers expose no sandbox and no runtime paper/live discriminator — a
single token / TOTP login reaches the same real account — so, following the
Longbridge precedent, the order path is structurally capped at paper:
- place_order / cancel_order refuse any non-paper config at the first line, so
a flipped `profile` override can never reach a live order;
- no live order-placing profile is registered (paper-sdk read-only,
paper-trade local-sim, live-sdk-readonly read-only — three profiles each,
mirroring Longbridge);
- DHAN/SHOONYA tool-class maps wired into the live classifier registry;
- order ops pinned WRITE; secrets redacted; pyotp made an optional dependency;
Shoonya sessions cached per user_id to avoid cross-account reuse.
Tests cover profile registration, the non-paper fail-closed guard, paper
simulation, WRITE-pinned order ops, secret redaction, and a regression guard
that these no-discriminator brokers never expose a *-live-trade profile.
Maintainer follow-up to PR #174: the original submission shipped live order
placement with no structural paper/live discriminator (and an invalid asset
class that crashed the live path); this revision caps both connectors to
paper + read-only to keep the paper/live boundary structural, never an
agent-flippable config flag.
Co-authored-by: Haozhe Wu <haozhe_wu@connect.hku.hk>
main.js is browser-cached up to 4h; the fresh HTML still pointed at the unversioned /main.js, so returning visitors kept running the pre-counter JS and the footer never populated. Versioned query forces an immediate fetch of the new module.
The wiki still claimed 'no live trading / does not execute live trades', which contradicts the shipped opt-in bounded Robinhood Agentic channel (and the README). Aligns home, docs, and alpha-library copy with the README's framing. Also fixes a CSS specificity bug where .footer-traffic{display:flex} overrode the [hidden] attribute, showing placeholder dashes before /api/stats data arrived.
pypistats/GitHub calls from the Worker intermittently rate-limit, blanking the footer. PyPI now reads through a D1 last-good cache so it never goes empty; GitHub stars are dropped here since main.js already loads them client-side.
pages deploy <dir> from repo root uploads functions/ as static files and never compiles them, so /api/stats (the agent-vs-human counter) was never live. Run wrangler from wiki/ (workingDirectory) with the wrangler.toml present so functions/ is detected and compiled.
First-party, anonymous analytics for the public wiki — no client-side telemetry in the pip package. Pages Function middleware classifies each page request (AI agent / bot / human) by User-Agent server-side and counts it into D1; /api/stats serves the footer's aggregate counts alongside public PyPI install and GitHub star numbers.
Add an opt-in local market-data cache (VIBE_TRADING_DATA_CACHE) for the backtest
loaders. When enabled, every data source — tushare, okx, ccxt, akshare, mootdx,
yfinance, futu — caches settled historical bars under ~/.vibe-trading/cache
(user home, never the repo tree) so repeated and long-horizon / cross-market
backtests skip the network and avoid provider rate limits.
- Generic loader_cache_get / loader_cache_put / cached_loader_fetch helpers in
the loader base; batch/connection loaders (yfinance, futu) skip the bulk
download / FutuOpenD connection entirely on a full cache hit.
- Staleness guard: ranges ending today or in the future are never cached, since
the key is content-addressed on end_date, not fetch time.
- Cached frames round-trip byte-identical to freshly fetched ones (columns name
and per-level index dtype preserved across the duckdb parquet round-trip).
- pid + uuid tmp filenames for concurrency-safe atomic writes; cache version 2.
- Documented in .env.example; staleness, real-duckdb, and batch-loader tests.
Closes#171.
Signed-off-by: Haozhe Wu <haozhe_wu@connect.hku.hk>
Add AGENT_CONTRIBUTOR_GUIDE.md for AI/automation-assisted contributors: safe
local checks, high-risk broker/MCP/credential surfaces, targeted test hints,
documentation and security rules, PR expectations, and rollback notes. Linked
from CONTRIBUTING.md so agent-assisted contributors see it before changing code.
Adds a guard test (agent/tests/test_agent_guide_paths.py) that parses the guide
for in-repo paths and asserts each still exists, so the guide turns a renamed
test/entry-point into a red CI run instead of silently rotting. pytest --ignore
targets (gitignored e2e suites) and .env do-not-write references are excluded by
design.
Signed-off-by: Haozhe Wu <haozhe_wu@connect.hku.hk>
Adds the 2026-06-03 News entry (trace call_id correlation #168, dead-doc
reference cleanup #166, langchain-community install-warning clarification
#167, Gemini thoughtSignature scoped to help-wanted #170) to the English,
Chinese, Japanese, Korean, and Arabic READMEs, and collapses the 2026-05-31
entry into the "Earlier news" section to keep three latest entries visible.
Adds the tool call_id to the tool_call and tool_result trace records so a
result can be correlated back to its originating call when replaying a trace.
Keeps the existing [:200] truncation on serialized args to avoid bloating
trace files with large tool inputs.
Equivalent to #168 (arg truncation preserved).
Co-authored-by: zwrong <1003912034@qq.com>
The live-trading source files referenced docs/live-trading/SPEC.md in
docstrings, comments, and tests, but docs/ is gitignored (internal planning),
so external contributors hit a dead path when they grep / ls-tree for it.
Genericize the references to "the live-trading SPEC"; docs/ stays internal.
Closes#166
Function-based mandate gate for direct-SDK live orders mirroring the MCP gate:
mandate load + schema/expiry check, kill-switch halt, quantity→notional pricing,
fail-closed pre-trade checks via check_mandate, per-broker audit, and a daily
count consumed only on a confirmed placement. Shared daily counter (order_guard
now delegates to it). AssetClass gains HK_EQUITY/CN_EQUITY and OrderIntent gains
an explicit asset_class so the mandate gate buckets HK/A-share correctly. The 6
connector classification maps are wired into the live registry for fail-closed
WRITE coverage. Covered by two adversarial audit rounds; full suite 2979 passed.
Tiger/Longbridge/Alpaca/OKX/Binance/Futu connectors under a new broker_sdk
transport — read-only account/positions/orders/quote/history + paper-account
order placement, plus bounded mandate-gated order placement for five of them.
Longbridge is paper + read-only only (its API exposes no runtime paper/live
discriminator). Per-broker structural paper/live guards: Tiger 17-digit account,
Alpaca/Binance host separation, OKX flag + uid pin, Futu trd_env via get_acc_list.
New trading_place_order/trading_cancel_order tools; generic period/limit wired
through the tool and CLI. News updated across all 5 READMEs.
Addresses UX feedback from #163: rename systems jargon leaking into chrome
(ev -> evidence, Run bench -> Run benchmark, Decay -> Decay (days), Compact
context -> Summarize conversation), add a hint for Reasoning effort, remove
hardcoded skill/preset/tool counts that go stale under frequent releases, and
show a skeleton on the Compare route while run data loads.
Add MiniMax-M3 as the new default model and retain MiniMax-M2.7 as
the legacy alternative (incl. -highspeed). Update llm_providers.json
registry, cli onboard provider choices, .env.example example, and the
provider-default regression tests.
Signed-off-by: Octopus <liyuan851277048@icloud.com>
Signed-off-by: octo-patch <liyuan851277048@icloud.com>
Refresh the Contributors section in all 5 READMEs from the v0.1.8 cycle
list to the v0.1.9 cycle (PRs #124–#160): @toanalien, @ai7eam-dev,
@shadowinlife, @DoubleSky123, @ArthurXi, @omcdecor-cyber, @Soli22de,
@ruok808, @faizack, @fightZy, @lcwSeven, @Teerapat-Vatpitak.
Roll-up release since 0.1.8 — connector-first broker profiles (IBKR +
Robinhood Agentic Trading), Research Goal runtime, swarm reconcile/MCP
keepalive + worker MCP tools + strict alpha-bench + retry_run, the
agent/cli/ package refactor, the mootdx A-share loader, a robustness pass,
and the session/version fixes (#150#156#157#158 + UI indicator).
Version → 0.1.9 across pyproject, SKILL.md, frontend APP_VERSION, and the
wiki docs version selector. MCP tool count corrected to 36 (adds retry_run)
across all 5 READMEs, with the ar/ko tool lists rebuilt from a stale 22 and
the ar skill count fixed 75→77. CHANGELOG [0.1.9] + 0.1.9 news entry added
to all 5 READMEs. Home backtest card data-source count corrected 3→7.