The project's README from github.com/HKUDS/Vibe-Trading. As one markdown file: README.md.
English | 中文 | 日本語 | 한국어 | العربية | Español
One Command to Empower Your Agent with Comprehensive Trading Capabilities
Website · Docs · News · Features · Shadow Account · Demo · Quick Start · Examples · API / MCP · Roadmap · Contributing
⚠️ Security warning: The X account
VibeTrading_HKU, Virtuals project101845, and token contract0x640BDBF77b6447E8b7DB7894cED84BD1c40571f4are not official Vibe-Trading assets. We have never launched or endorsed any token or memecoin. Do not buy, connect a wallet, or sign anything. Details.
2026-09-10 🧾 A remediation message that named a variable nothing reads, and an adapted strategy that wore its parent's sign-off: when stooq answers with its anti-bot challenge page, the loader tells the operator how to route around it — and both halves of that advice were unreachable as printed. VIBE_TRADING_MARKET_DATA_ORDER_* is read nowhere: the registry builds MARKET_DATA_ORDER_<MARKET> and the config layer passes it to a bare os.getenv, so exporting the name as printed was a silent no-op and stooq stayed at position 2 of the US-equity chain. The other half, "drop stooq from it", is rejected outright — an override must be a permutation of the default chain, so a source can be reordered but not removed. The message now names the variable the registry actually reads and states the constraint, pinned to the registry rather than to a literal so it cannot drift again (#1402). Separately, the first half of description-driven strategy adaptation landed: an adapted strategy is a new artifact pointing at its parent through derived_from, the description may change universe, name and position sizing while the signal is copied verbatim, and the child starts with empty evidence so its numbers come from its own backtest instead of inheriting the parent's (#1391, refs #1149). Two gaps were closed on the way in, both one field over from the rule that PR was written to enforce. dataclasses.replace copies every field the call does not name, so the child came out UNVALIDATED while still carrying the parent's validator and approver — an independent sign-off worn by something that never earned it — along with an artifact_version and a model_version for code no model had generated yet. And the parent-evidence gate was reachable only from tests: register_adaptation never consulted it, so a stale or insufficient parent was written anyway, against what the substrate advertised. Its inputs are keyword-only and required now, so the check cannot be skipped by forgetting it. Thanks @chiww and @modelpath-dev!
2026-09-09 🚀 v0.1.15 released (Release notes, pip install -U vibe-trading-ai): 551 commits and 162 merged pull requests since 0.1.14, from 35 contributors. The theme of this cycle is data that says what it is — and three of the biggest fixes turned out to be the same bug wearing different clothes: a default that quietly substitutes a plausible value for a missing one, which is indistinguishable downstream from a real observation. The Alpha Zoo's NaN contract moved out of the individual alphas and into the registry: an alpha that branches on an np.where comparison never sees a NaN — the comparison is False — so blanking every declared input on one bar left 84 of 462 alphas returning a number anyway, each consumed as a real signal because dropna() is what keeps a gap out of an IC. The registry now masks output wherever a declared dependency is missing on that bar, and the same sweep afterwards finds none (#1377, closes #1376). The warmup half stays open and stated rather than quietly carried: 52 alphas still emit a value inside their own declared min_warmup_bars, and a registry-level warmup mask would also blank rows for alphas that merely over-declare, so that half needs a direction call rather than 52 PRs. Same class, second instance: pandas still defaults pct_change() to forward-filling, so a missing close becomes a 0.0% return that never happened; four PRs fixed one call site each, and an exhaustive sweep found 24 sites across 10 files — two of them inside the very files those PRs were editing. Third instance: the futures loader chain named tushare and akshare, and neither implemented a futures endpoint at all, so every contract fell through to the A-share equity endpoint. AKShare now serves Chinese contracts off the keyless Sina daily endpoints — dated and main-continuous, verified live across SHFE, CFFEX, GFEX and ZCE — while a global contract returns empty and reaches local instead of being priced as an equity (#1395). Prices themselves now carry the adjustment caliber they were served under, and a source whose caliber was never measured against a live payload reports unknown rather than guessing one (#1317). Live trading fails closed in five more places: a broker position read returning an API error, error envelopes arriving mid halt-sweep, the flatten latch across runner restarts, unresolved Alpaca submissions recovered by exact client ID across a restart, and buy-limit orders sized at the worse of quote and limit on both transports. In the backtest: options signals fill on the next bar rather than the date they were computed from, short option legs hold margin and gate opens on buying power, perpetual funding settles by bar span on 8h+ intervals, an over-committed rebalance basket scales instead of aborting, and a halted position marks at the last traded close instead of entry cost. In the agent loop: a session that had already fetched fundamentals, fund flow, margin and research data had those results cleared to free context, and the de-duplication ledger went on refusing to re-fetch them — 44 blocked retries in one run, ending in "fundamental data not retrieved" for data the model had already received; results reconcile by exact call identity now, and the no-progress budget counts observations rather than activity. The final batch to land before the tag is those same three themes in miniature. CL2412.NYMEX matched no symbol pattern — the bare dated form (CL2412) matched and the venue form (ES.CME) matched, but the way a contract is actually written matched neither — so a US crude contract ran under T+1, with no shorting, settled in CNY; Tushare's own exchange spellings had the mirror-image problem on the China side, and both halves classify now (#1396, closes #1394). The pct_change() call sites were being fixed one at a time (#1397, #1398) before the exhaustive sweep found the rest, and the same default sat behind TopN selection ranking assets that had no factor observations (#1399) and sentiment orthogonalization regressing on zeros it had filled in itself (#1400). China-futures runs now state which products were priced on a generic default instead of a table entry — rb's real margin rate is 0.10, the same number as the default, and "looked up" must not read like "assumed" (closes #1393). And the ML walk-forward example purges future labels (#1392). New this cycle: UK equities (LSE .L/.IL, with SDRT modelled as a purchase-side-only duty, because charging both sides overstated every round trip by half), Zerodha Kite Connect as the fourteenth broker (capped at paper plus read-only, since Kite exposes no runtime live discriminator), a read-only multi-broker portfolio across Web, REST, CLI and agent tool where a source that fails to refresh is an error excluded from the totals rather than a stale cache, fifteen Quant Library additions (Heston, Hierarchical Risk Parity, copulas, VPIN/Roll/Amihud/Kyle microstructure, barrier options with finite-difference Greeks, ISDA CDS, Key Rate Duration, Vasicek, factor IC, Ornstein-Uhlenbeck, Ulcer/Pain, event-study tests, group-purged CV), Brazilian Portuguese as the eighth locale, read-only Nobitex and Wallex, calendar-triggered partial rebalancing, swarm replay and retry, and an offline evals harness that emits NOT_EVALUABLE rather than passing when the instrumentation it needs is absent. Thanks to all 35 contributors of this cycle!
2026-09-08 🧪 An alpha that printed a number for a bar whose inputs were blank, and a spot-gold search that answered with a Swedish Bitcoin ETP: the Alpha Zoo contract preserves NaN through warmup and missing data, but an alpha that branches on an np.where comparison never sees a NaN — the comparison is False, so the ternary falls through to its constant. Blanking every declared input on one bar of a 462-alpha sweep, 84 alphas returned a number anyway, and since dropna() is what keeps a gap out of an IC, each of those constants was consumed as a real signal. The guard now sits in the registry rather than in each alpha — the output is masked to NaN wherever a declared dependency is missing on that bar — and the same sweep afterwards finds none (#1377, closes #1376). Eleven alphas were fixed for the other half of the contract, the warmup window (#1379, #1380, #1381, #1382, #1383) — and measuring that half the same way leaves 52 alphas still emitting a value inside their own declared min_warmup_bars. A registry-level warmup mask would also blank rows for alphas whose declaration is merely conservative, so that half stays open and stated rather than quietly carried. Separately, search_symbol("XAUUSD") returned exactly one candidate — VALOUR-BTC-0-SEK.ST, a Swedish Bitcoin ETP — which then locked the run's instrument identity; a metal or FX pair query is an exact instrument assertion now, keeping only candidates that name the same two legs in any spelling (#1282). Fixed: an announcement date carries no time of day, so on an intraday frame a filing became visible from the first bar of its own announcement day — a full session of lookahead. Sub-daily frames are refused now, with fundamental_subdaily="next_day" as the explicit opt-in (closes #1387). A metric claim formatted as a generic | Metric | Value | table escaped the analysis gate that rejects the same claim in prose (#1375), while the mirror-image error rejected a price word used as a formula variable — close/SMA50 > 1 was read as an asserted price (#1378, closes #1354). A pasted broker code (HK.00700, US.AAPL, SH.600519) scanned as no symbol at all, so every market tool answered identity_required (#1384). The three Archimedean copula CDFs returned wrong extreme values under strong dependence — Clayton 0.0, Gumbel 1.0, Frank inf — and are computed in log space now, verified against a 2500-digit reference across θ ∈ [-5000, 5000] (#1386, closes #1385). An MT5 order above the symbol's volume cap was silently clamped and sent, an eToro limit order with no limit price rested untriggered while reading as accepted, futu K-lines inherited an undeclared adjustment caliber, and BTCUSDT was routed through A-share rules — T+1 and no shorting, on a perpetual (#1388). Thanks @cgycorey, @Shizoqua, @he-yufeng, @Robin1987China, @laiyierjiangsu, @aminak58, and @nandofmike!
apply_slippage refreshed that sub-engine's own active symbol — so round_size and calc_commission used whichever symbol had synced it last. A 50 oz gold position sized against FX's 1,000-unit micro lot rounded to 0, gold futures were charged the index future's rate-based fee (6.90) instead of their own flat one (20.00), and swap used the 100,000-unit standard lot rather than the 100 oz metal lot (#1370). Separately, _extract_product could not read a symbol whose product code contains a digit or ends in a letter that doubles as a month code: 6EZ4, 6JH25, M2K, MYM2503 and FESX2503 all fell through to the default multiplier of 50 — for 6E the true figure is 125,000 (#1369). Symbols now resolve against the multiplier table itself, longest match first, which also covers the dated forms (M2KZ4) and prefix collisions (SILZ4 vs SI). Fixed: four Alpha Zoo factors printed a hard ±1 or 0 through a trading halt and inside their own declared warmup, because a NaN comparison is False rather than NaN and the ternary fell through to its constant. min_warmup_bars is metadata that nothing trims, and what keeps a gap out of an IC is dropna() — so those constants were consumed as real signals (#1371, #1372, #1373, #1374). Measuring the whole zoo the same way puts 82 more alphas in the same shape; rather than one PR per alpha, that is now tracked as a registry-level guard in #1376. Strategy-discovery evidence also resolved position size once for the whole run instead of per regime, and a single unparseable benchmark cell could erase every evidence row that run produced (#1368). New: a run that keeps issuing tool calls without gaining information now stops after eight iterations with a visible recovery request, instead of spending its whole budget on path discovery — the reported case burned ~35 iterations re-guessing the same rejected paths. An identical call is refused from its second failure rather than its first, so one honest retry after a rate limit or a timeout still runs (#1363, closes #1353). Thanks @Shizoqua, @be-student, and @nandofmike!Analysts say TSLA.US last traded at 412.35 USD. with no tool call behind it (#1338, closes #1336); a cash-dividend row killed the whole trade-journal parse (#1356); compac