English | 中文 | 日本語 | 한국어 | العربية | Español | Bahasa Indonesia
Vibe-Trading: Your Personal Trading Agent
One Command to Empower Your Agent with Comprehensive Trading Capabilities
Website · Docs · News · Features · Shadow Account · Demo · Quick Start · Examples · API / MCP · Roadmap · Contributing
📰 News
⚠️ Security warning: The X accountVibeTrading_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-10-03 🛠️ Research, reports and data reliability: CJK session search, channel setup, broker exposure pricing and file writes now handle cases that blocked everyday use. PDF delivery embeds CJK fonts, Swarm validates preset inputs and separates task artifacts, and replayed tool results survive context compaction (#1683, #1455, #1635). Backtests keep one adjustment basis, local caches distinguish sources, single-asset caps and weekly/monthly risk use the declared settings, audits retain loss signs, grounding checks the current engine output and exact list references, and Stooq retries after a denial cooldown (#1684, #1650, #1685, #1640).
-
2026-10-02 🛠️ Backtests and report checks: strategy-file writes retain model provenance without crashing (#1673), and Monte Carlo drawdown and Sharpe include starting capital (#1664). Report audits preserve accounting negatives and units (#1663); grounding artifacts record fired declared checks (#1661); public loader-health reports include sanitized failure reasons (#1643). Indonesian tool documentation matches the registry (#1671).
-
2026-10-01 ✅ Data correctness and reproducible backtests: A-share adjustment conversion now refuses ambiguous one-bar edge cases (#1551); Southbound Eastmoney amounts are scaled from million HKD and rejected responses no longer look empty (#1486); Northbound fallback data distinguishes post-2024-08-19 turnover from net flow (#1484); Binance-only unpriced positions are marked incomplete without penalising other brokers (#1505); and backtest run cards record model provenance and warn when the training cutoff is unknown or outside the test window (#1618, closes #1613).
Earlier news
-
2026-09-30 🛠️ Feishu in the Web UI, a momentum factor that read today's price, and a profit factor for runs that never lost: Feishu joins the guided channel setup, with a standalone connection test and a hot reload that closes the old WebSocket (#1572).
academic_carhart_momsubtracted the 1-month return from the 12-month one, so it moved with today's close; it is now the return from 12 months ago to 1 month ago (#1578). A run with no losing trade reports its profit factor as undefined instead of 0.0, which ranked it last (#1602). Stooq's anti-bot page now stops every later request in the process, not just the log line (#1637); MCP tool results reach the agent once instead of up to four times (#1634); and the first piece ofloop.pymoves into its own module (#1636). -
2026-09-29 🚀 v0.1.16 released (Release notes,
pip install -U vibe-trading-ai): 492 commits and 116 merged pull requests since 0.1.15, from 16 contributors. The theme of this cycle is a number that can show where it came from. The grounding gate no longer guesses what a number is from the words around it: the model declares each figure's role (observed, derived, proposed, cited or count), the gate checks it against the session's tool evidence, and a figure that fails is cut instead of the whole answer being refused. A backtest's own output — Sortino, turnover, weights and Monte Carlo p-values, not only Sharpe — now grounds the report written about it, and run cards cite a metric only where the saved CSV agrees. Long research runs keep what they fetched: compaction follows each model's real context window instead of a fixed 40K estimate. The missing-data sweep reached 59 more alphas. Fixed in this release, from a user's report: failed runs keep their steps and the stop message names the call that produced nothing;read_filesays where it may read instead of letting the model guess paths until the no-progress stop; on Codex, mid-run instructions no longer replace the system prompt, reasoning carries between turns and the default model isgpt-6-sol; andget_market_dataasks forAAPL.USrather than returning empty data for a bare US ticker. New: Argentina (BYMA), weekly and monthly bars, Bahasa Indonesia, Gildata, OpenCode, Email and WebSocket channels, IM channel setup in the Web UI, and KIS, Upbit, Toss Securities and Scalable Capital connectors, 18 brokers in all. Thanks @Shizoqua, @zeus229, @cgycorey, @shadowinlife, @lorenzozanee, @he-yufeng, @chiww, @as950118, @woshi77777stars, @Yoruxyv, @sambazhu, @0xouzm, @tingkk, @tonydo, @alanwilhelm and @modelpath-dev! -
2026-09-28 🛠️ Source health and connector transparency: scheduled public-source canaries report outages and data drift with bounded, credential-free probes (#1627); BaoStock socket requests now have deadlines and serialize concurrent sessions (#1615). Copilot credential lookups refresh after a short cache TTL (#1619). A generated broker matrix preserves each profile’s paper/live permissions and labels declared capabilities separately from runtime verification (#1629). Grounding and MT5 follow-up: explicit evidence symbols and exact field references are preserved; localized grouped figures retain the existing numeric tolerance (#1584, #1586, #1588). Text-only correction attempts are bounded (#1600). MT5 search uses the selected terminal and refuses ambiguous broker aliases; backtest sandboxes receive only validated attachment settings (#1597, #1598).
-
2026-09-27 🛠️ Verifiable run cards and safer research: Run cards now show hashed backtest execution records and verified metric-to-CSV references in JSON, Markdown and Run Detail (#1612). GTJA high/low recency factors use the correct day count and consistent tie handling (#1604); India short covers check the buy-side circuit band (#1608). Token usage remains readable without exempting arbitrary credential strings (#1606), malformed MCP schemas retain their object properties (#1607), and immediate-order goal checks preserve research questions (#1605). Setup documentation now correctly identifies OpenRouter as the shipped default (#1609).
-
2026-09-26 🛠️ More reliable indicators and trading limits: Technical indicators retain their observation date and can reuse results lost during conversation compaction (#1590, #1601). All five Qlib158 WVMA windows now use absolute returns in the numerator (#1594); Benford checks preserve the correct leading digit at numeric boundaries (#1591). An explicitly zero exposure limit stays zero (#1593), and Dhan paper orders reject fractional or invalid quantities without rounding away input precision (#1595). Invalid optional numeric environment variables fall back to defaults while explicit configuration constraints remain enforced (#1592). Runtime triggers keep the correct field defaults and subclass factory behavior (#1599).
-
2026-09-25 🛠️ More reliable research sessions and channel diagnostics: A text column in a backtest CSV no longer discards the numeric metrics (#1579), and conversation compaction now counts reasoning content in its retained-message budget (#1582). Failed channel loads now log the actual exception (#1580); the Telegram guide clarifies that CLI and Web controls share the same API runtime (#1583). Robinhood portfolio reads reject malformed buying-power objects while keeping omitted or null values unknown (#1526). Leaving a chat also cancels pending history-scroll timers. Thanks @Shizoqua and @lorenzozanee!
-
2026-09-24 💱 A dollar line priced as pesos, Email and WebSocket in the Web UI, and a CI break from an SDK update: BYMA and the TSX list US-dollar lines beside their home-currency ones (GGALD.BA, DLR-U.TO), and an Argentine or Canadian backtest priced them in its single peso or Canadian-dollar pool. The loaders now admit a line only in its market's currency, as LSE lines already were, and every other declared quote currency travels with the data, so an answer names the right one (#1576). Hong Kong's RMB and USD counters (80700.HK, 9834.HK), which HKEX numbers in their own code ranges, are refused in a Hong Kong backtest for the same reason and quoted in their own currency. Stock profiles carry the listing's own venue and currency beside the issuer's fundamentals (#1577), grounding and symbol search know every market the data layer routes (#1575), and
technical_indicatorsreports volume with the unit its source declares — board lots or shares, 100× apart (#1571). Email and WebSocket join the guided channel setup, with mail-server certificates now verified and settings writes closed to cross-site pages, and a plain IMAP login is upgraded with STARTTLS before the password goes out (#1544). Fixed: an explicit provider header lost to an ambient twin under openai 3.19.2, which had turned every PR's CI red (#1568); a backtest from a microsecond-resolution source was dated 1970 (#1560); a research goal that is plainly an order is refused again (#1562); a strict-bench OOS split could leave one side empty (#1559); HRP label alignment, cross-validation label order and non-finite quant inputs (#1555, #1556, #1557, #1558); the shadow-account overtrading window (#1563); and asset growth was read day over day (#1564). Thanks @Shizoqua, @zeus229 and @shadowinlife! -
2026-09-23 🇦🇷 Argentine listings, a short book sized upside down, and the last half of the tail-risk gate:
.BAsymbols — BYMA equities and CEDEARs — route to Yahoo as their own ARS market, and the Web UI groups and labels them like any other; an Argentine backtest still fails closed until BYMA execution rules are modelled (#1543). The mean-variance and turnover-aware optimizers scored every position as if it were held long, so the strongest shorts got the least capital. Both now size on position returns, where a long and a short of two correlated names hedge each other instead of reading as correlated — on a +0.92 correlated pair the old objective put the whole book on the long, the new one splits it 0.51 / -0.49 (#1548). And a tail-risk figure now has to name its own field once a session holds more than one measure or confidence level, so an ES 95% can no longer quote the VaR 95% value under a call id (#1425). Fixed: Monte-Carlo validation annualises at the venue's bars per year instead of a hardcoded 252, so its Sharpe agrees with the rest of the same report (#1546); a futures order on a negative-price bar is no longer rejected outright (#1547); a factor's IC ratio is left unset for a negative baseline instead of reading real decay as healthy improvement (#1549); and two memories sharing a title under different types keep their own index rows and link targets (#1545). Thanks @zeus229, @Shizoqua and @he-yufeng! -
2026-09-22 📅 Weekly and monthly bars, and a compaction replay that could lose evidence:
get_market_dataand backtests take1Wand1M(rejected since 09-20 so1Mwould not fetch minute bars). Every source still serves daily bars and the period bar is built from them, dated on the last trading day of its week or month, so a 21-month review is one call of 21 bars (#1479). A price index is stamped unadjusted instead of by what its source does to a stock (#1541). A read-only tool result that context compaction removed is restored from the run's own memory instead of fetched again, capped per run, and a write empties that memory so a regenerated file is read afresh (#1488). A tail-risk figure declared with a field ref is checked against that field, so a VaR 99% quoting the 95% value is caught (#1444). QVeris refuses stock and ETF bars, whose adjustment its search-ranked pick cannot state, and quotes priced per result (#1494). Thanks @sambazhu, @zeus229, @he-yufeng, @jw232 and @cgycorey! -
2026-09-22 💸 A backtest that died when funding pushed cash below zero, and three more tools that read a rejected query as no data: a crypto funding debit can leave free cash below zero, and the next position the strategy wanted then found no size that fit, not even zero, so the run aborted with "planned order … exceeds available capital" (or "insufficient capital for position rebalance"). That open is now skipped and reported once as
insufficient_capital, the bar's reductions still run, and a close whose loss exceeds its margin still aborts the bar as before (#1542). Block trades, margin trading and financial statements now tell Eastmoney rejecting a stale query apart from a genuinely empty answer, through the same check shareholder count, dragon-tiger and lockup expiry use. Answers in Spanish, German and other decimal-comma locales now ground17,93 %or1.410,00 CNYagainst the tool results, while two unspaced numbers like1400,1777stay two numbers, and a figure has to match its evidence to the precision it is written at (#1517). Thanks @turtle696966969696 for a trace reconciled to the cent, and @zeus229! -
2026-09-21 🔌 IM channels configured from the Web UI, and 24 alphas that emitted a value computed from a missing bar: Settings now edits each IM channel with masked secrets, a connection test on the unsaved form, and a per-channel hot apply with no restart; DingTalk and QQ get guided setups (#1520, #1529), and every adapter logs through stdlib again, so diagnostics that a
{}placeholder used to swallow reach the log (#1533). In the Alpha Zoo, a comparison whose operand is NaN because its window holds a missing bar reads False and emits a constant; the #1463 sweep, which nudged the bar by a tick, could not see it. A gap-versus-gap-free check found 24 more alphas, which now mask on their inputs' reach while a correlation undefined on complete data keeps its old verdict (#1523, #1534, #1463). Data: a lockup'sfree_sharesis the unlocking quantity, not the float it was read from (#1513); dragon-tiger seats are ranked within each listing reason (#1512); shareholder-count history returns real periods (#1518); Tencent, Eastmoney and AKShare A-share prices are labelled dividend-additive (#1497); Gildata joins the A-share chain as a token-gated source (#1474); portfolios value any ISO-4217 currency and fail one source, not the snapshot, on a missing rate (#1510);connector accountshows Binance, Futu and Trading 212 balances (#1539). New: Bahasa Indonesia UI and README (#1482). Removed: the Requesty provider — pointopenrouterat its base URL instead. Thanks @shadowinlife, @he-yufeng, @Shizoqua, @lorenzozanee, @cgycorey, @zeus229, @sambazhu, @Yoruxyv and @jastincheis! -
2026-09-20 🛠️ Clearer data requests and everyday fixes: monthly
1Mrequests are now explicitly rejected instead of silently fetching one-minute bars (#1487); weekly/monthly support remains tracked in #1479. Welcome quick actions highlight the selected chip (#1500), DingTalk sanitizes downloaded filenames (#1506), Signal preserves text around mentions after emoji (#1478), and credit-spread tables merge equivalent5/5.0tenor columns (#1507). -
2026-09-19 🛠️ Data errors that looked like missing disclosures or the wrong provider: A-share shareholder counts work again after an upstream column rename; rejected queries now report the provider's error instead of claiming the stock has no disclosure (#1490). Market-data provenance now names the loader that actually served each symbol, with the matching fallback flag and adjustment label, even when the requested SDK is unavailable (#1491). QVeris no longer overwrites
indicatorswith an end date (#1496); adjustment selection, adjusted-response parsing and billing remain tracked in #1494. Thanks @cgycorey and @lorenzozanee! -
2026-09-18 ✂️ A redacted answer that lost its row numbers, and one starved order counted as twenty-six: when the grounding gate released an answer with its unverified figures cut, a ranked table came back with
(omitted※)where 1, 2, 3, the12min its headers and "3 names" in the prose had been. Every integer in a table cell counted as a measurement, and each cut figure's digits were then swept off the rest of the page. A column that numbers its rows is now structure, a plain integer beside words in a cell is read as it would be in a sentence, and the sweep never keys on a single digit; invented returns and multiples are still cut (#1471). Inholdmode, the search that scales a basket down to the available cash booked azero_sizerejection on every step, so one contract the cash could not hold read as 26 lot-rounding failures; it is now oneinsufficient_capital(#1470). Fixed: alocal:code in a backtest is served from your own dataset or refused, never filled from a network source (#1467); the event-study CAR standard error now includes the covariance between daily forecasts that share estimated parameters — with a 30-day estimation window the standardised CAR's variance on null events was 1.41–1.46 and is now 1.07–1.08 (#1466); a realhistorical_var/parametric_varresult grounds the VaR it returned (#1464); and twenty alphas no longer fill a missing bar with a constant, while recursive smoothers skip the gap and continue under a rule that is now written down and tested (#1463). Later the same day: a plainLANGCHAIN_PROVIDER=openaisetup withgpt-5.6-terrafailed on its first tool call, because Chat Completions refuses function tools for thegpt-5.6models unless the reasoning effort isnone. The adapter now retries such a request once on/v1/responses, where the reasoning is kept, and remembers that route for the model (#1473). New: OpenCode Go / Zen as a built-inopencodeprovider that sends thex-opencode-sessionid the relay requires, in the compaction thread too (#1416, closes #1415). Merged: an entry memory GC archived is no longer handed to compression as well (#1450); the Patell z uses each normal-return model's own degrees of freedom (#1449); the microstructure estimators reject NaN and infinite inputs instead of returning a zero spread (#1451); a source that must never fall back to the network keeps that promise per symbol too (#1441); and a backtest checks the declared interval against the served bar spacing before annualising, so a daily file declared1His no longer annualised at the hourly rate and a weekly file annualises at 52 (#1432). Thanks @5gaLbt and @turtle696966969696 for the reports, and @tonydo, @Shizoqua and @chiww for the fixes! -
2026-09-17 🔐 A Robinhood login that reaches several accounts, and a live gate that refused every real order while its tests passed: Robinhood can now be a read-only portfolio source that reads exactly one account, picked from Robinhood's own list with nothing preselected (#1428), and live trading binds each mandate to one account the same way (#1442). The runner and the pre-trade gate had been calling Robinhood with no account and reading its replies one level too shallow, so every reconciliation aborted and every order was refused, while the tests stayed green because their fixtures used a reply shape Robinhood never sends. Positions show unpriced until the quote reply is mapped. Fixed: a swarm worker saves its message log on every exit path that writes a summary (#1439); qlib158's up- and down-day counts no longer count a missing close as a flat day (#1459); a strategy's decay check no longer reads an infinite Sharpe as healthy (#1447); and a custom alpha-zoo root no longer replaces a bundled alpha of the same name process-wide (#1468). Thanks @balu1866 for the reply shapes, and @cgycorey and @0xouzm!
-
2026-09-16 🧪 Four numerical slips that each returned a plausible number: "low" sat in the sentiment lexicon as both a bullish and a bearish word, cancelling itself out (#1448); head-and-shoulders detection divided by a signed shoulder average, so lopsided shoulders on a negative-valued series passed the symmetry check (#1456); the options engine annualised over one bar fewer than the shared metrics, exaggerating short runs (#1458); and copula pseudo-observations ranked a missing value as the largest and gave ties arbitrary distinct ranks (#1453). Thanks @Shizoqua.
-
2026-09-15 🧮 A grounding gate that decided what a number was from the words around it: the check every market answer passes before release used to infer whether a figure was a price, a target or a metric from a catalogue of phrasings — 65 regular expressions that grew a fix commit per wording and could give a sentence and its translation different verdicts. The model now declares what each figure is in a
figuresblock the reader never sees (observed,derived,proposed,cited,count); the gate reads only a number's shape and checks every declaration against the session's tool results. A rejected answer gets one correction that names each failing figure with the nearest observed prints, and if it still fails it is released with only those figures cut ((略※)/(omitted※)) instead of being replaced wholesale by a canned refusal, which remains only for an identity problem or a run that observed no price. While it waits, the chat shows "Checking the figures in this answer" in all eight languages. Before release it went through three rounds of adversarial review and real model runs, which added decimal commas, year-shaped prices, untagged code fences, integer prices of instruments quoted in the thousands, prices declared as counts and unchecked numbers reaching the stream. Metadata counts and metric-name tokens no longer ground a metric, and a sentence with several metrics is no longer rejected (#1418, #1420, #1421, #1426, #1433). Thanks @zeus229, @he-yufeng and @5gaLbt! -
2026-09-14 🇰🇷 A-shares backtested as perpetual futures, and a swarm argument the model could set but never change: every branchless data source that serves A-shares —
local,tencent,eastmoney,baostock,mootdx,sina— routed a pure A-share basket to the crypto engine, which applies none of the A-share rules (stamp tax, T+1, price limits, 100-share lots) and does charge an 8-hourly perpetual funding fee against the position. The run succeeded and its metrics were internally consistent, which is what made it dangerous; a pure A-share basket now reachesChinaAEnginefrom every source (#1431). Separately, the swarm worker overwrote every tool call'srun_dirwith the agent workspace, so for the tools that declare it —backtestabove all — the model could pass any directory and read none of it, while the error named no path. A declaredrun_diris now honoured inside the workspace and refused outside it with the boundary named, and the backtest tool says which directory it checked (#1430, #1429). New: four connectors land together and the count reaches 18 — KIS (한국투자증권; a genuine 모의투자 paper sandbox on its own host, plus read-only live), Upbit (KRW crypto; paper + read-only live, capped because the exchange exposes no paper/live discriminator), Toss Securities (read-only live; the closed-order history is paged to completion and refused rather than truncated past the cap; read paths validated against a real account) and Scalable Capital's Agentic MCP as a fully read-only profile with an explicit tool allowlist and no order capability (#1407, #1408, #1409, #1417). Fixed: the shared SDK diagnostics told every broker's user to check Longbridge; they now name the affected broker's SDK or gateway in all eight languages (#1436). Thanks @chiww, @cgycorey, @as950118, @dudco and @tingkk! -
2026-09-13 🗂️ A broker reply in the wrong shape that counted as an empty account, and run-card diagnostics that never reached the card: the portfolio layer read positions with an empty list as the default, so a reply that never carried a positions list — an MCP response with no mapping yet, or IBKR's official MCP answering without structured positions — became a source holding nothing, and the snapshot was stored as complete. That is exactly the quietly smaller portfolio the aggregation exists to rule out, so a read without a positions list is now refused: the source shows as an error and the snapshot is marked incomplete, while a genuinely empty account (
"positions": []) still reads as empty. It surfaced while reviewing a new MCP connector whose holdings are not mapped yet. Separately, the run card kept only scalar metrics, so every dict- or list-shaped diagnostic the engine writes for card readers was dropped — a reader could see how many plans were rejected, but not for which symbol or why. Those metrics now travel in astructured_metricsblock, capped at 4 KiB of UTF-8 JSON per metric with anything larger named under_omitted, and render in the Run Card tab in all eight languages (#1424). Thanks @cgycorey! -
2026-09-12 📥 A dividend row that looks like a holding, and a number format where one misplaced separator is a thousandfold error: extraETF's CSV exports can now be read into the portfolio layer's position shape — read-only, standard library only, and failing closed on anything it cannot read without guessing (#1406, toward #1170). The Investments export becomes positions and the Transaktionen export becomes typed movements, so a
Dividenderow, which carries a quantity and a price of its own, is never read as a position, and a transactions file cannot pose as an empty portfolio. Numbers follow the export's German grammar exactly:1.386,608is 1386.608, a US-ordered1,234.56is refused rather than read a thousandfold off, and a value the eight-decimal wire format cannot hold is refused rather than rounded —0,000000004shares would otherwise have become zero. An unknown header (the error names both known column sets), an unknown transaction label, a movement with no instrument, and one instrument listed twice under a single portfolio ID are all refused, while the same ISIN under two portfolio IDs — the export's normal multi-portfolio case — stays two positions. The export carries no snapshot date, so the file's modification time is reported assource_mtimeand no position claims an observation time. Nothing calls the reader yet; wiring it into the portfolio container is next. Thanks @cgycorey and @KaiLuettmann! -
2026-09-11 📉 Eight years of Tencent history that came back as the last two, and an evidence gate that turned away every backtest the agent ran itself: Tencent's
fqklineendpoint serves the last 500 bars of a window, not the first — a CSI 300 request for2018-01-01..2026-06-30returns2024-06-06..2026-06-30— while the loader paginated forward, stepped past the window after one page and returned a tail-only series with exit code 0 and no warning. It now walks backward from the end date, re-requests an empty page once before believing it, treats an error-shaped reply as a failure, and bumps the loader cache version so series cached by the old walk are never served again (#1411, closes #1410).refresh_strategy_evidencerejected every run produced by thebacktesttool, because only the agent runtime wrote thestate.jsonits ingestion gate reads; the tool now records success, failure and timeout itself, and the gate stays fail-closed for runs nobody vouches for (#1413, closes #1412). Concurrent cold-start data requests could come back_unresolvedfor symbols that were perfectly available: the loader registry marked itself initialised before its imports ran, so simultaneous first callers saw it empty — seven of eight in the reporter's run (#1405, closes #1403). A swarm worker'sskills:list, documented as its allowlist, is now enforced byload_skillinstead of only by the prompt (#1414), and the correlation skill's Engle-Granger example, which crashed on an unnamed series, now calls the tested quantlib functions instead of its own OLS fit (#1404). One more was ours: thirteen broker config files that hold API keys —alpaca.json,zerodha.jsonand the rest — were missing from.gitignorethough its comment claimed them, so anyone whoseVIBE_TRADING_HOMEis the checkout was onegit add -Aaway from committing credentials; a test now derives the list from the code. So was a paper cancel that answered for orders it never placed: the Zerodha, Dhan and Shoonya paper connectors simulate orders locally but read the real account, and acknowledged a cancel for any order id — a live one included, which would have kept working; they now accept only the ids their simulator issued. Thanks @shadowinlife, @alanwilhelm and @Shizoqua! -
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 buildsMARKET_DATA_ORDER_<MARKET>and the config layer passes it to a bareos.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 throughderived_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.replacecopies every field the call does not name, so the child came outUNVALIDATEDwhile still carrying the parent'svalidatorandapprover— an independent sign-off worn by something that never earned it — along with anartifact_versionand amodel_versionfor code no model had generated yet. And the parent-evidence gate was reachable only from tests:register_adaptationnever 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 annp.wherecomparison never sees a NaN — the comparison isFalse— so blanking every declared input on one bar left 84 of 462 alphas returning a number anyway, each consumed as a real signal becausedropna()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 declaredmin_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 defaultspct_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: thefuturesloader chain namedtushareandakshare, 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 reacheslocalinstead 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 reportsunknownrather 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.NYMEXmatched 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). Thepct_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 emitsNOT_EVALUABLErather 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.wherecomparison never sees a NaN — the comparison isFalse, 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 sincedropna()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 declaredmin_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, withfundamental_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 > 1was 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 answeredidentity_required(#1384). The three Archimedean copula CDFs returned wrong extreme values under strong dependence — Clayton0.0, Gumbel1.0, Frankinf— 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, andBTCUSDTwas routed through A-share rules — T+1 and no shorting, on a perpetual (#1388). Thanks @cgycorey, @Shizoqua, @he-yufeng, @Robin1987China, @laiyierjiangsu, @aminak58, and @nandofmike! -
2026-09-07 🪙 A 50-ounce gold order rounded down to nothing, and a currency future priced at 1/2500th of its contract size: in a cross-market backtest the composite engine dispatches every symbol through one shared sub-engine per market, but only
apply_slippagerefreshed that sub-engine's own active symbol — soround_sizeandcalc_commissionused 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_productcould not read a symbol whose product code contains a digit or ends in a letter that doubles as a month code:6EZ4,6JH25,M2K,MYM2503andFESX2503all fell through to the default multiplier of 50 — for6Ethe 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 (SILZ4vsSI). 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 isFalserather than NaN and the ternary fell through to its constant.min_warmup_barsis metadata that nothing trims, and what keeps a gap out of an IC isdropna()— 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! -
2026-09-06 🛡️ A buy limit sized at a price it would never fill at, and an audit that returned PASS having verified nothing: the MCP order gate priced a quantity order from the live quote alone, so a limit at twice the market cleared a cap sized for the quote and could fill at twice the authorized notional. It is sized at the worse of quote and limit now — the rule the direct-SDK path already used — and an unparseable limit price is denied rather than waved through (#1361). The report auditor skipped every point with no fetched value and then reported PASS because nothing had failed, certifying a report backed by no evidence at all; that fails closed now (#1362). Fixed: metrics survived a failed analysis tool, and the new exemption for attributed figures — right for a paper's Sharpe, wrong for a price this run is meant to observe — would have passed
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); compaction re-opened the de-duplication ledger by tool name, so clearing one argument variant left the rest gated (#1358); the compliance ledger refused its first append on Windows (#1261). New: Zerodha Kite Connect makes 14 brokers — read and paper only, since Kite exposes no runtime paper/live discriminator ((README truncated)