Changelog
June 5, 2026 · View on GitHub
4.5.47 - 2026-06-05
Deploy marker: e51a0478
Fixed
- Explicit broker cancel/modify calls no longer get suppressed by local order state. Schwab and Tradier now send
cancel_order()and_modify_order()requests to the broker even when Lumibot's local order status is alreadyCANCELLING, filled, canceled, expired, or errored, preventing a strategy-side cancel-pending state from blocking the actual broker API call. - Rejected broker-status checks now have a compatibility alias.
Order.OrderStatus.REJECTEDmaps to Lumibot's existingERRORstatus so generated strategies that check rejected outcomes do not crash while new strategy guidance moves towardERROR.
Tests
- Schwab and Tradier regression tests now cover local terminal/cancel-pending states. The targeted tests include the exact lifecycle where strategy code marks an order
CANCELLINGbefore broker cancel and assert the broker API is still called.
4.5.46 - Unreleased
Added
- Schwab cancels now emit order-level diagnostic telemetry.
Schwab.cancel_order()logs request metadata, Schwab response status and elapsed time, and a best-effort direct order read after cancel acceptance so support can distinguish missing cancel requests, broker rejection, delayed broker state, and strategy-loop lockups.
Tests
- Rob-owned Schwab live cancel diagnostics now cover Titus-style fast option cancels. Local smoke helpers can load the existing gitignored Schwab token and app metadata, submit a nonmarketable option order, cancel by exact order id, confirm broker state, and optionally compare broad order-list timing outside the hot path.
4.5.45 - Unreleased
Added
- AI agent model calls now have provider-level request timeouts. Native Gemini requests pass a Google GenAI HTTP timeout and LiteLLM-backed providers pass a LiteLLM completion timeout, preventing one stalled model request from freezing a strategy for the full agent-run budget.
- AI agent timeout settings are configurable from strategy code.
self.agents.create(...)andagent.run(...)now acceptmodel_request_timeout_secondsandrun_timeout_seconds, with documented defaults of 600 seconds per model request and 1800 seconds for the whole agent run.
Fixed
- Agent runtime traces now log the effective timeout settings and first ADK event latency. This makes it clear whether a run is stuck waiting on the model provider or actively making progress through ADK/tool events.
4.5.44 - Unreleased
Added
- Exact BotManager scheduled runs now wait locally for
targetRunAt. Scheduled one-shot live runs can initialize early, wait immediately beforeon_trading_iteration(), write timing telemetry, skip late iterations that exceed the drift budget, and drain post-iteration broker events before exit.
Fixed
- Schwab advanced-order cancels now terminalize local child legs. When Schwab accepts canceling an OTO, OCO, or bracket parent order, LumiBot marks the local parent and child orders canceled so strategy active-order guards do not keep seeing canceled advanced orders as active.
- Scheduled timing setup now works with lightweight strategy stubs.
StrategyExecutorfalls back to the module logger when a minimal strategy object has nologger, restoring existing unit/backtest test compatibility.
Tests
- Rob-owned Schwab live smoke now mirrors Titus-style option cancel and broker-native hedge structures. The smoke submits a TSLL option limit order, waits four seconds, cancels and verifies the canceled broker state, then submits/read/cancels OTO and bracket option structures against Schwab.
4.5.43 - 2026-06-01
Canceled release candidate. Do not deploy.
4.5.42 - 2026-06-01
Deploy marker: deploy 4.5.42
Fixed
- Tradier OAuth deployments now refresh after retry-layer 401 failures. The Tradier broker catches retry-library errors such as
too many 401 error responses, forces one OAuth refresh, and retries the broker read instead of failing before the refresh hook can run.
4.5.41 - 2026-05-30
Deploy marker: deploy 4.5.41
Added
- Schwab OTO trigger orders now support explicit parent/child orders.
Order(order_class=Order.OrderClass.OTO, child_orders=[...])can now represent a single cross-asset child order, such as an option entry that triggers a stock hedge order, without requiring synthetic secondary limit/stop fields. - Schwab OTO, OCO, and bracket submissions now build Schwab advanced-order specs. The Schwab broker uses
schwab-pytrigger and one-cancels-other builders for single stock/ETF/options parent and child orders. Multi-leg option spreads remain intentionally unsupported until separately implemented and live-tested. - AI trading team examples now cover six distinct agent workflows. Public docs and example files include bull/bear leveraged ETFs, bull/bear large-cap stocks, Ray Dalio-style idea meritocracy, Warren Buffett-style value, Bill Ackman-style concentrated investing, and Citadel-style sector pods with matching workflow diagrams.
Fixed
- Schwab trigger-order reads now preserve parent and child order structure. Broker responses with
orderStrategyType=TRIGGERparse back into an OTO parent with child orders instead of flattening the response to the first active child. - Schwab stop and stop-limit order builders now use the installed
schwab-pybuilder API. This fixes advanced orders with stop exits on currentschwab-pyversions where directorder_specmutation is not available. - AI agent order submission now requires fresh account, position, and price context. Trading agents must check cash, portfolio value, positions, and the ordered asset's last price before
orders_submit_order; missing context returns a structured error instead of submitting an impossible order or silently resizing it. - Long AI agent runs no longer inherit ADK's 500-call default cap. LumiBot now owns timeout/retry behavior for agent runs instead of letting Google ADK stop complex multi-tool runs with
LlmCallsLimitExceededError.
Tests
- Advanced-order unit coverage now validates OTO, OCO, and bracket spec generation plus Schwab trigger-response parsing. Live Schwab API smoke tests were also added to submit, read, and cancel nonmarketable OTO, OCO, and bracket orders when credentials are available and the market is closed.
- Agent order-readiness tests cover missing account context, missing last price, valid submissions, and comma-separated symbol rejection.
4.5.40 - 2026-05-29
Deploy marker: deploy 4.5.40
Fixed
- Schwab market-order reconciliation no longer leaves missing live hedge orders active forever. Active local market orders missing from Schwab's broad order list now go through direct broker lookup like other active orders; if the broker still returns no order after the existing new-order grace window, Lumibot marks the local order
UNKNOWNand non-active instead of letting it block future active-order checks. - Schwab simple stock and option order replacement now builds broker specs correctly.
modify_order()now reuses the same Schwab stock/option order builders used for new submissions instead of calling missing replacement helper methods, and it safely tracks previous order IDs when Schwab returns a replacement ID. - Known Schwab account-history rows are quieter. Schwab
MUTUAL_FUNDposition rows andEXERCISEorder-history rows are preserved without noisy unknown-warning spam, while truly new/unknown asset and order types still warn and preserve raw broker data.
Tests
- Schwab live option replace smoke coverage now exercises the Titus failure path. The live API test submits a TSLL option limit order, reads it directly and from the active order list, replaces it through Schwab's order-replace endpoint, reads the replacement, cancels it, and confirms the canceled state.
- Market-order broad-list miss regression coverage was added. Unit tests cover active market orders missing from non-empty and empty broker order lists, including direct filled reconciliation and unresolved missing-order terminalization to non-active
UNKNOWN.
4.5.39 - 2026-05-29
Canceled release candidate. Do not deploy.
4.5.38 - 2026-05-29
Deploy marker: 4.5.38 release commit (Bump LumiBot to 4.5.38)
4.5.37 - Unreleased
4.5.36 - Unreleased
4.5.35 - 2026-05-28
Deploy marker: 4.5.35 release commit (deploy 4.5.35)
Fixed
- Schwab order refresh now skips unsupported mutual-fund and bond order-history legs. Schwab accounts with sweep-fund activity such as
MUTUAL_FUNDorder legs no longer poison order parsing for supported stock, option, future, forex, and index orders. - Live order accessors now support active-order filtering with broker refresh. Strategies can call
get_orders(statuses=Order.ACTIVE_STATUSES)orget_order(order_id)to refresh live broker order state before making cancel/hedge decisions. - Broad broker order-list misses no longer create fake local cancels. If an active local order is missing from a broad broker list, Lumibot now performs a direct broker lookup by order id before updating local state; if that lookup fails, the order remains active with a warning instead of being marked canceled locally.
Docs
- Live order and position refresh guidance documents active-order filters and broker reconciliation safety. The new docs explain when generated/refined strategies should use
Order.ACTIVE_STATUSESand how Lumibot handles incomplete broker order-list responses.
4.5.34 - 2026-05-26
Deploy marker: 4.5.34 release commit (deploy 4.5.34)
Changed
- AI Investment Committee agents now use explicit role-specific tool surfaces. Strategies can opt out of automatically adding every built-in tool by passing
include_builtin_tools=False, letting examples keep research, debate, and trading responsibilities separate. - The AI Investment Committee example now keeps Bull and Bear agents argumentative by default. The Evidence Researcher gathers the bounded evidence pack, Bull and Bear reason over that handoff without reopening the tool loop, and the Portfolio Manager keeps only portfolio/order/price/memory/notification tools.
Fixed
- Schwab live order cancellation now calls the broker API.
Schwab.cancel_order()now sends the order ID and account hash toschwab-pyinstead of logging an unimplemented-method error, validates missing IDs/setup failures, and dispatches a cancel event after Schwab accepts the cancel. - Cached agent tools preserve their callable signatures. Provider function declarations for cached read-only tools now retain required arguments, preventing missing-argument failures such as
get_fred_snapshotwithoutseries_idsorget_company_factswithoutsymbol. - AI order tool results are JSON-safe after broker side effects. UUID-like order identifiers are serialized before reaching the provider runtime, preventing duplicate-order retry paths caused by post-submit JSON serialization errors.
- Agent memory search includes append-only event history.
search_memorynow finds prior thesis, decision, and lesson text even after the current memory projection changes status or text, keeping live observability searchable after updates and closes. - SEC normalized statements avoid mixing stale facts across filings. Fundamental statement helpers now keep fields anchored to a coherent filing/period when possible, preventing old revenue or cash-flow facts from leaking into fresh analysis.
- Backtest look-ahead warnings no longer flag future SEC proxy meeting dates as future data.
get_filingsstill gates filings by acceptance/filing availability, but a public filing's futurereport_datedoes not create a false warning.
Operations
- AI committee observability validation now has real Gemini artifact proof. A one-day
gemini-3.5-flashpaid backtest completed end-to-end with four agent traces, role-level tool-call counts, raw trace artifacts, andstats_agent_detail.parquetfor post-run inspection. - TQQQ SMA200 ThetaData acceptance baseline was refreshed for the canonical CI cache result. The 4.5.34 GitHub Actions S3-backed acceptance run produced
858500centipercent total return, replacing the stale older baseline while preserving the queue-free ThetaData contract. - MELI Deep Drawdown acceptance runtime cap now allows normal CI runner variance. The metric contract is unchanged, but the runtime ceiling was raised from 600s to 660s after GitHub Actions missed the old cap by 3.6s.
4.5.32 - 2026-05-23
Deploy marker: 4.5.32 release commit (deploy 4.5.32)
Changed
- Lumibot README and docs now frame the agent examples as AI trading teams. The public docs refresh the BotSpot CTAs, favicon/logo assets, and agent-flow visuals while keeping the existing examples and links intact.
Fixed
- Schwab position sync now skips unsupported mutual-fund and bond positions instead of crashing. Accounts that contain asset classes LumiBot does not model can still sync supported stocks, ETFs, options, futures, and cash-like positions.
- SEC normalized statements now pick the freshest fact across equivalent tags. Revenue and similar mapped fields no longer get stuck on an older taxonomy tag when a newer filing reports the same field under another supported SEC company-facts tag.
4.5.31 - 2026-05-23
Deploy marker: 4.5.31 release commit (deploy 4.5.31)
Changed
- AI Investment Committee example now supports explicit cash parking. The example keeps growth equities and defensive cash-parking ETFs separate, defaults cash-parking symbols to
SGOV,BIL, andSHV, and asks the Portfolio Manager to consider short-duration Treasury/cash ETFs when no growth-equity candidate clears the risk/reward bar.
Fixed
- Live broker position sync now refreshes broker mark fields on existing positions. When a broker refresh updates an already-tracked asset, Lumibot now copies current price, market value, P&L, side, and related broker fields instead of only updating quantity, preventing impossible BotSpot position snapshots such as hundreds of shares with a stale two-share market value.
- Live FRED macro requests no longer use tomorrow's real-time date after UTC midnight. Default FRED/ALFRED requests clamp the live as-of date to FRED's current Central-time date, preventing after-hours agents from receiving 400 errors while preserving explicit historical
as_ofbacktest requests.
Operations
- AI committee benchmark notes now record the context-budget follow-up plan. The investigation doc captures the recommended runtime input-budget layer, affected provider context limits, and the rerun sequence after context-failure review.
4.5.30 - Unreleased
Changed
- AI agent runs now default to a 30-minute timeout.
LUMIBOT_AGENT_RUN_TIMEOUT_SECONDSstill overrides the full ADK/model/tool-call timeout, and non-positive values still disable it, but the unset default is now 1800 seconds so slower multi-tool agents are not treated as failed after only five minutes. - Trading-enabled AI agent runs no longer retry the whole run by default when mutating order tools are available. This prevents duplicate broker-side effects when a provider/runtime error happens after
orders_submit_order,orders_cancel_order, ororders_modify_orderhas already executed. Read-only research agents keep the larger retry budget, andLUMIBOT_AGENT_MAX_RUN_ATTEMPTScan still explicitly override the default.
Fixed
- AI agent tool outputs now coerce UUIDs and other non-JSON scalars before they reach Google ADK/provider serialization. Submitted order identifiers and similar broker payload fields are converted to JSON-safe values instead of causing
Object of type UUID is not JSON serializablefailures after an order side effect.
4.5.29 - 2026-05-20
Deploy marker: 4.5.29 release commit (deploy 4.5.29)
Fixed
- AI agent runtime no longer blocks tool calls with hidden runtime budgets. The runtime-enforced tool-call budget introduced in 4.5.28 has been removed because it could block execution tools such as
orders_submit_orderand invalidate agent trading results. - AI agent tool results and committee handoffs are no longer silently token-truncated by the benchmark path. Large handoffs/tool results now preserve the model-visible evidence instead of replacing it with middle-truncated excerpts.
- AI benchmark usage accounting now aggregates raw traces across all committee roles. The provider benchmark runner no longer relies on a last-writer detail artifact that undercounted multi-agent token usage and cost.
- Multi-agent detail parquet files now include all agent rows for stats-file backtests. Observability artifacts for committee-style strategies include every role instead of only the most recent writer.
Changed
- The AI Investment Committee example uses prompt guidance instead of numeric tool-call caps. The example still asks agents to be concise and targeted, but it does not impose hidden research/follow-up/portfolio tool budgets.
- The AI committee provider benchmark plan marks the enforced 4.5.28 results invalid for trading conclusions. The investigation doc records why hidden tool-call and truncation controls polluted the prior 14-day and three-month results, and documents the safer one-model smoke-test plan for Cerebras, direct DeepSeek Flash, and Together after billing propagation.
4.5.28 - 2026-05-20
Deploy marker: 4.5.28 release commit (deploy 4.5.28)
Fixed
- Broker cash-event polling now requests full available history on first successful cloud poll. Strategies no longer limit the first cloud cash-event fetch to the last seven days, so restarted BotSpot live bots can replay available broker deposit/withdrawal history into the listener.
- Alpaca journal classifications now match the broker activity contract used for cash-flow adjustment.
JNLCcash journals are classified as external deposits/withdrawals by sign, whileJNLSstock journals remain internal non-cash-flow activity. - Tradier transfer/journal classifications now distinguish external funding from internal bookkeeping. ACH, wire, and check funding activity is treated as deposits/withdrawals, while fee, subscription, and transfer bookkeeping descriptions stay internal unless they match cash/account movement patterns.
Operations
- AI committee benchmark notes now record the enforced-qualifier and three-month launch results. The benchmark plan documents the latest qualifier outcomes and launch artifacts for the provider-comparison work.
4.5.27 - 2026-05-20
Deploy marker: 4.5.27 release commit (deploy 4.5.27)
Fixed
- AI agent runs now have a hard wall-clock timeout.
LUMIBOT_AGENT_RUN_TIMEOUT_SECONDSbounds the full ADK/model/tool call, defaulting to 300 seconds, so a live or backtest strategy skips a stalled agent iteration instead of hanging indefinitely inside a provider or built-in tool call. - Large AI agent tool results are now token-budgeted before re-entering model context. Tool responses that would consume too much context are replaced with an explicit bounded excerpt and a truncation notice, preventing raw SEC/fundamental/news payloads from blowing up committee-style agent turns.
Changed
- AI agent tool-result budgeting is configurable.
LUMIBOT_AGENT_TOOL_RESULT_MAX_TOKENSoverrides the default 4,000-token per-result budget for benchmark or large-context-provider runs. - The AI Investment Committee example now includes runtime-enforced tool-call discipline. Research, follow-up, and portfolio-manager agents receive explicit tool-call budgets, and the runtime returns a budget-exceeded notice after the configured call count so benchmark runs measure targeted research instead of uncontrolled tool spraying.
Operations
- AI committee provider benchmark tooling was expanded. The benchmark runner can set per-agent timeout overrides and emit fixed-budget provider reruns, and
scripts/summarize_ai_committee_provider_benchmarks.pysummarizes per-modelresult.jsonartifacts into compact JSON/Markdown reports. - AI committee benchmark documentation now records provider pricing sources, updated cost estimates, observed context-window failures, and the new tool-result/tool-call controls.
4.5.26 - 2026-05-19
Deploy marker: 4.5.26 release commit (deploy 4.5.26)
Operations
- PyPI project storage was cleaned up for wheel-only releases. Historical source distributions that had matching wheels were removed from PyPI so new LumiBot releases can publish again under the project storage quota. The package release remains wheel-only.
- 4.5.26 republishes the safe dotenv and lazy-startup release path after the 4.5.25 PyPI quota blocker. This keeps the deployment path moving without force-moving the stale
v4.5.25tag. - Google ADK is pinned below 2.0 for release stability. CI exposed a
google-adk2.0 function-declaration schema change that drops the expectedparametersobject for the built-in indicator tool. LumiBot keeps the previously passing 1.x range until the 2.x schema contract is reviewed deliberately.
4.5.25 - 2026-05-15
Deploy marker: 4.5.25 release commit (deploy 4.5.25)
Changed
- Package namespace imports are now lazy-loaded. Top-level namespaces such as
lumibot,lumibot.brokers,lumibot.data_sources,lumibot.entities,lumibot.tools, andlumibot.tradersdefer heavy broker/data/tool imports until first use while preserving common star imports and legacyentities.*aliases. - Dotenv loading is now safer for runtime-secret deployments. LumiBot searches upward for the nearest
.envinstead of recursively scanning nested directories, supports sibling.env.localoverrides for local development, and supportsLUMIBOT_DISABLE_DOTENV/LUMIBOT_DISABLE_DOTENV_LOCALfor injected-env production jobs.
Fixed
- CI Ruff checks now point at the current data downloader queue client path.
- IBKR REST stock/index conid lookup now filters secdef results by asset type. Ambiguous tickers such as
MHOno longer resolve to a futures contract when the strategy requested a stock, preventing false “Chart data unavailable” failures in AlphaPicks backtests. - Symbol parsing now uses a single conservative implementation.
lumibot.tools.parse_symbolandlumibot.tools.helpers.parse_symbolboth normalize whitespace/case, require full OCC option-symbol matches, reject empty input, and parse OCC years as 2000-based.
4.5.24 - 2026-05-15
Deploy marker: 4.5.24 release commit (deploy 4.5.24)
Fixed
- IBKR REST backtests now negative-cache unresolvable stock/index conid lookups. Symbols that IBKR cannot resolve, such as defunct AlphaPicks holdings, are treated as terminal no-data and persisted in the conid negative cache so long BotSpot backtests skip them deterministically instead of hammering secdef/history endpoints until the task times out.
- PyPI releases now build wheel-only artifacts. This avoids source-distribution uploads hitting PyPI project storage limits while preserving the artifact BotManager installs for backtest workers.
4.5.22 - Unreleased
Fixed
- ThetaData option backtests no longer exhaustively download minute quote/OHLC data during delta strike selection.
OptionsHelper.find_strike_for_delta()now uses the existing model-based strike selector for ThetaData-routed option backtests, including intraday-cadence strategies, and leaves real historical price validation tofind_next_valid_option()/evaluate_option_market(). This prevents sparse option chains from spending minutes probing empty strikes before any trade is placed.
4.5.21 - Unreleased
4.5.20 - 2026-05-15
Deploy marker: 4.5.20 release commit (deploy 4.5.20)
Fixed
- Crypto futures and perpetual backtests can use spot crypto price history through routed backtesting.
Asset.AssetType.CRYPTO_FUTURErequests now route through explicitcrypto_futureprovider settings, falling back tocryptowhen omitted, and USDT contracts such asBTCUSDT,ETHUSDT, andSOLUSDTcan price from the matching USD spot proxy while preserving the original futures asset for strategy-facing orders and positions. - Routed backtesting now preserves day-level stock/index data in mixed option workflows. Stock/index lookups that support option strategies avoid stale minute-frame fallbacks when daily bars are requested.
Changed
- Release docs now require prompt review for platform capability changes. Deployment review includes a token-efficient BotSpot agent prompt/shared-example check alongside the existing documentation and visual asset gates.
4.5.19 - 2026-05-14
Deploy marker: 4.5.19 release commit (deploy 4.5.19)
Fixed
- Alpaca backtests now support multi-timeframe history requests such as
15min.AlpacaBacktesting.get_historical_prices()now requests native Alpaca minute/hour multiples where supported and only falls back to local aggregation for unsupported aliases such as multi-day bars, so strategy calls likeget_historical_prices(asset, 100, "15min")match the public strategy API contract instead of failing validation.
Changed
- LumiBot release docs now include a Codex-safe remote-first release path for shared checkouts. AI agents can avoid switching the canonical local checkout when unrelated dirty files are present while preserving the same version-branch, PR, tag, PyPI, and BotManager deployment invariants.
4.5.18 - 2026-05-14
Deploy marker: 4.5.18 release commit (deploy 4.5.18)
Fixed
- IBKR-routed stock/index lookups in option backtests now stay on native daily bars. Implicit stock/index
get_last_price()andget_quote()calls no longer fall through to IBKR minute history after an option backtest has observed intraday cadence. - ThetaData option OHLC downloader waits are now bounded separately. One stuck sparse option contract/day no longer inherits the broad OHLC history timeout used for larger history downloads.
4.5.17 - 2026-05-14
Deploy marker: 4.5.17 release commit (deploy 4.5.17)
Fixed
- ThetaData quote-history requests now use a bounded quote-specific timeout. Point-in-time option quote lookups no longer inherit the larger OHLC history timeout, so one stuck quote request fails visibly instead of making BotSpot option backtests appear frozen early in the run.
4.5.16 - 2026-05-14
Deploy marker: 4.5.16 release commit (deploy 4.5.16)
Fixed
- Routed daily stock/index backtests no longer satisfy day lookups from stale minute frames.
RoutedBacktestingPandasnow pins stock/index day requests to native daily bars, andThetaDataBacktestingPandas.get_last_price()/get_quote()preserve daily cadence instead of falling back through basePandasDatalookups that can resolve May-only minute caches during April AlphaPicks option backtests.
4.5.15 - 2026-05-13
Deploy marker: 4.5.15 release commit (deploy 4.5.15)
Fixed
- IBKR REST daily stock/index price lookups now treat raw ticker strings as stock assets. Strategies that call
get_last_price("MU")orget_quote("MU")now use the same daily stock/index refresh path asAsset("MU", "stock"), preventing AlphaPicks option backtests from falling back to end-anchored IBKR1mindata when the strategy passes string symbols.
4.5.14 - 2026-05-13
Deploy marker: 4.5.14 release commit (deploy 4.5.14)
Fixed
- IBKR REST daily stock/index backtests no longer fall back to minute data after a stale daily slice. When daily stock/index data is already loaded but missing the current simulation timestamp,
get_last_price()andget_quote()now refresh a bounded daily slice around the simulation date and return empty/None if that still fails. This prevents daily AlphaPicks option backtests from making unnecessary IBKR1minrequests anchored near the backtest end date and then trying to use May-only data for April entries.
4.5.13 - 2026-05-13
Deploy marker: 4.5.13 release commit (deploy 4.5.13)
Fixed
- IBKR REST backtesting point-in-time option price lookups now fetch bounded minute slices around the simulation time.
get_last_price()andget_quote()no longer prefetch the full backtest window for point lookups, which avoids IBKR returning only a later tail segment and then mispricing earlier AlphaPicks option entries.
4.5.12 - 2026-05-13
Deploy marker: 4.5.12 release commit (deploy 4.5.12)
Fixed
- IBKR REST backtesting no longer treats underfilled minute history as full-window coverage. If a full backtest-window prefetch returns only a later tail slice,
get_last_price()/get_quote()now leave the series unmarked as fully loaded, fetch a bounded slice around the current simulation datetime, and retry. This fixes BotSpot option backtests where an Apr-09 Alpha Picks lookup was incorrectly attempted against a May-only minute cache and logged “outside data range.” Disjoint recovery slices are normalized before merging so timezone object differences cannot leave the stale slice in place. - ThetaData option quote caches now recover from stale provider-expiry placeholders. Placeholder-only option quote caches now record the Theta provider expiration used for Friday/Saturday expiry mapping, and LumiBot refetches once when a legacy placeholder was written before the correct provider mapping was learned. This prevents a no-data placeholder from blocking later valid option quotes for symbols whose Theta history endpoint expects the exact Friday expiry.
4.5.11 - 2026-05-12
Deploy marker: 4.5.11 release commit (deploy 4.5.11)
Added
- Scheduled live execution can run one iteration and exit for BotManager scheduled runs. When BotManager sets
LUMIBOT_SCHEDULED_EXECUTION, LumiBot restores persistedself.vars, runs one live iteration, persists the updated state file, and exits cleanly. - AI agent strategies now support richer multi-agent workflows. Added per-agent model selection,
allow_trading=Falsetool restrictions for research-only agents, and an AI investment committee example that runs researcher, bull-case, bear-case, and portfolio-manager agents inside the normalon_trading_iteration()flow. - Built-in SEC fundamentals and filing tools for agents and strategies. Added direct SEC EDGAR access with local caching, rate limiting, SEC user-agent support, company facts, normalized income statement / balance sheet / cash-flow helpers, filing lists, filing document retrieval, and keyword search over cached filings. Backtests gate filings by filed / accepted timestamps so agents do not see future disclosures.
- Built-in FRED macro tools. Added official FRED/ALFRED API helpers for series lookup, latest values, historical observations, and point-in-time snapshots using
realtime_start/realtime_end. FRED data fetches requireFRED_API_KEYand cache under the Lumibot cache directory. - Built-in technical-indicator tools for agents. Agents can list indicators and request current-bar indicator values through the existing fast indicator subsystem, keeping backtests aligned with the simulated datetime.
- Local AI-agent memory and notifications. Added JSONL-backed memory tools for decisions, lessons, and thesis lifecycle tracking, plus a Telegram notification provider and
notify_usertool with backtest notifications disabled unless explicitly enabled. - AI-agent smoke and verification tooling. Added deterministic and real-model AI committee runners, built-in-tool integration coverage, SEC/FRED tests, notification/memory tests, and agent-tool permission tests.
- New AI-agent documentation and branded visuals. Added RST and Markdown docs for agent flows, built-in tools, SEC fundamentals, FRED macro data, memory, notifications, observability, and the investment committee example, plus Nano Banana / Spot-branded README and documentation diagrams.
- Individual CCXT broker documentation pages. Added separate docs pages for the CCXT brokers Lumibot supports directly, including Binance, BitMEX, Bybit, Coinbase, Kraken, KuCoin, OKX, and WEEX.
Changed
- README and public docs now position Lumibot as deterministic Python strategies plus AI-agent strategies on the same backtest / paper / live execution path. The AI investment committee is presented as one example workflow, not the only possible agent structure.
- Agent built-in tools are included by default when available. Account, positions, open orders, history, docs search, indicators, SEC, FRED, memory, notifications, and order tools are available by default;
allow_trading=Falseremoves only mutating order tools. - Alpaca news tool availability now depends on credentials. The built-in Alpaca news tool is hidden when no Alpaca credentials are configured and uses the standard Alpaca credential environment variables when present.
- FRED no longer uses public CSV fallbacks. Revised/no-key CSV access was removed from examples, docs, tests, and implementation; official FRED/ALFRED API access is the only supported macro-data fetch path.
- AI-agent docs and deployment guidance now require high-quality generated visuals. Lumibot/BotSpot/Lumiwealth documentation visuals must use Nano Banana/GPT Image 2 quality and the canonical Spot brand reference when a mascot is helpful.
Fixed
- BotSpot cloud account snapshots are marked verified before use. This prevents unverified broker/account reads from being treated as trusted performance data.
- AI committee example trades are constrained to reviewed symbols and risk limits. The example no longer places trades outside the researcher/portfolio-manager scope.
- Filing, macro, indicator, and news examples now document point-in-time behavior more explicitly. Docs and prompts emphasize using the current strategy datetime during backtests.
4.5.10 - 2026-04-30
Deploy marker: 9e73736f (deploy 4.5.10)
Fixed
- Bitunix futures order cancellation now sends the exchange-required symbol with the order id.
Bitunix.cancel_order()now passes bothorder_idandsymbolinto the Bitunix REST client, andBitUnixClient.cancel_order()accepts either the broker's keyword shape or a Lumibot order object. This fixes cancellation failures caused by the client method expecting an order object while the broker passed only an identifier.
4.5.9 - 2026-04-30
Deploy marker: b0da6a50 (deploy 4.5.9)
Fixed
- IBKR continuous futures now tolerate IBKR-listed holiday expiration dates for the same contract month. MNQ June 2026 (
MNQM26) was computed from the synthetic equity-index anchor as2026-06-19, but IBKR lists the contract withexpirationDate/ltd=20260618; the exact-date miss was then persisted in the negative conid cache and BotSpot backtests saw no MNQ bars. LumiBot now keeps the strict no-front-month-fallback invariant, but when IBKR returns exactly one contract in the requestedYYYYMM, it resolves that conid, records both the listed date and computed-date alias, clears stale target negative-cache entries, and uses the IBKR-listed date for cont-future history fetches.
4.5.8 - 2026-04-29
Deploy marker: eefe772f (deploy 4.5.8)
Fixed
- Broker cash-event polling is hardened for real Alpaca and Tradier activity-history responses. Alpaca cash-event polling now requests non-fill account-activity categories separately (
TRANS,DIV,MISC) instead of paging through trade fills or letting dividend pages hide funding rows, safely handles wrapped/model/invalid activity rows, and was verified against real Alpaca live API-key/OAuth accounts with actualCSDdeposits andCSWwithdrawals. Tradier cash-event polling now parses the raw history response inside LumiBot so the publishedlumiwealth_tradierpackage's"history": "null"bug cannot emit live warnings likestring indices must be integers, not 'str'; direct-request pagination, wrapper fallback, null-history payloads, the exact TypeError path, and real Tradier live transfer/wire/dividend history were verified. - Cash-event warnings now identify the broker and sanitized fetch context. Strategy warnings now include the broker name/class, and Alpaca/Tradier broker adapters log sanitized activity type, page/limit, pagination, and raw-type counts so future production warnings can be diagnosed without exposing API keys.
4.5.7 - 2026-04-29
Deploy marker: c850cd66 (deploy 4.5.7)
Fixed
- AI agents no longer send unsupported
temperature=0to LiteLLM-routed providers. The ADK runtime had hardcodedtemperature=0.0for every model. OpenAI GPT-5/reasoning-class models reject custom temperature values and only allow the provider default, causing backtests to fail before the first agent call. LumiBot now keeps explicittemperature=0.0only on the Gemini-native ADK path and omits temperature for OpenAI/xAI/Anthropic/etc. LiteLLM providers. Verified with a realopenai/gpt-5.4agent call. - Tradier live cash-event polling no longer passes unsupported
page=toAccount.get_history(). The installedlumiwealth-tradierclient supportsstart_date,end_date,limit,activity_type, andsymbol, but notpage. LumiBot now detects whether the client supports pagination and falls back to a single bounded per-activity request when it does not, preventing live warnings likeAccount.get_history() got an unexpected keyword argument 'page'and allowing broker cash events to load.
4.5.6 - 2026-04-29
Deploy marker: 61b75f24 (deploy 4.5.6)
Added
- Canonical AI agent detail artifact is now Parquet-only and token-first. AI agent backtests now write one canonical
*_agent_detail.parquetartifact beside the normal backtest outputs. The file contains onecall_summaryrow per AI call plus event rows forthinking,text,tool_call,tool_result, andusage, with exact prompt/context fields, model output text, tool args/results, event payload JSON, input/output/total tokens, thinking tokens, cached/uncached input tokens, cache-write tokens, tool-use prompt tokens, full-call latency, and first-event latency. Pricing is intentionally excluded; the artifact records raw token facts only. - Built-in BYOK Alpaca news tool for AI agents. Added
BuiltinTools.news.alpaca_news(), which uses the user's ownALPACA_API_KEY/ALPACA_API_SECRETorAPCA_API_KEY_ID/APCA_API_SECRET_KEY, defaults the newsendtimestamp to the current simulated datetime during backtests, and returns normalized Alpaca news headlines/summaries/sources/URLs/symbols. Full content is opt-in withinclude_content=True. - Alpaca news proof script, example strategy, and live quality test. Added
scripts/run_alpaca_news_ai_proof.py, which runs a tiny real AI-agent backtest, forces the scan-first/full-article workflow, and verifies the resulting*_agent_detail.parquetcontainsalpaca_newstool calls, full article content, thinking rows when exposed by the model, and token/latency fields. Addedlumibot/example_strategies/agent_alpaca_news_builtin.pyas the recommended built-in-tool news example. Added opt-inapitestcoverage that checks Alpaca/Benzinga historical relevance and real pagination on known broad-market event windows. - Real provider prompt-cache probe. Added
scripts/run_agent_prompt_cache_probe.py, which bypasses LumiBot's replay cache and sends repeated real provider calls with the same long static prefix. The probe prints input, cached input, uncached input, output, thinking, total token counts, first-event latency, and full-call latency so provider prompt caching can be verified before deployment.
Changed
- Alpaca news tool now supports scan-first / full-read workflow without hidden truncation.
BuiltinTools.news.alpaca_news()now defaultslimit=30, supportspage_token, returnscontent_available_countandsummary_available_count, exposesrequested_end/effective_end/lookahead_clamped, clamps future backtestendtimestamps to the current simulated datetime, and returns the full article body wheninclude_content=Trueunlesscontent_max_charsis explicitly set. The tool description now teaches agents that Alpaca news is symbol/date-window retrieval rather than keyword search, recommends focused scans atlimit=10-20, broad scans atlimit=30-50, explains pagination, and lists broad-market/sector ETF proxies for less sparse historical news queries. - AI agent prompt layout is more provider-cache friendly. Runtime-specific fields such as current datetime, timezone, positions, orders, task prompt, user context, and persistent memory now stay in dynamic user-context sections instead of the static system instruction prefix. This keeps the large base prompt and tool surface stable across bars so OpenAI/xAI/Gemini provider prompt caches can reuse more of the prefix during cold backtests and live trading.
Fixed
- AI agent prompt caching telemetry and routing keys. Provider cache routing is now stable for OpenAI (
prompt_cache_key+ 24h retention) and xAI (x-grok-conv-id), and LumiBot normalizes cached-token fields from provider usage payloads intocached_input_tokens/uncached_input_tokensso backtests can prove whether provider prompt caching is actually being used.
4.5.5 - 2026-04-24
Added
- Anthropic Claude M2 Liquidity demo. Added
lumibot/example_strategies/agent_m2_liquidity_anthropic.py, matching the Gemini/OpenAI/Grok variants withdefault_model="anthropic/claude-sonnet-4-6"andANTHROPIC_API_KEYsetup instructions.
Fixed
- AI agent provider-key naming now matches product-facing env vars. Gemini examples/docs/error guidance now use
GEMINI_API_KEY, and Grok examples accept eitherXAI_API_KEYorGROK_API_KEY. LumiBot mirrorsGROK_API_KEYintoXAI_API_KEYfor LiteLLM's xAI provider when the canonical xAI env var is absent. Added regression coverage intests/test_agent_runtime_provider_keys.py.
4.5.4 - 2026-04-24
Added
- Multi-provider LLM support for AI agents via LiteLLM bridge. Agents can now use any LLM provider LiteLLM supports (OpenAI GPT, xAI Grok, Anthropic Claude, Groq, Mistral, Cohere, and ~100 others) by passing a provider-prefixed
default_modeltoself.agents.create(...). Gemini ids still take Google ADK's native fast path; anything else is transparently wrapped viagoogle.adk.models.lite_llm.LiteLlm. New_resolve_model_for_adk()helper inlumibot/components/agents/runtime.pydoes the routing. Addedlitellm>=1.77.0tosetup.pyinstall_requires. Replay cache already keyed on the model id (manager.py:516), so swapping providers on the same backtest produces fresh runs rather than stale cross-model replays. Three provider-specific M2 demo strategies mirror the existing Gemini demo with only thedefault_model+ provider key changed:lumibot/example_strategies/agent_m2_liquidity_openai.py(openai/gpt-5.4-mini),agent_m2_liquidity_grok.py(xai/grok-4.20-0309-reasoning = Grok 4.2), andagent_m2_liquidity_anthropic.py(anthropic/claude-sonnet-4-6). Public docs updated indocsrc/agents.rst,docsrc/agents_quickstart.rst, anddocsrc/environment_variables.rstto document supported providers and required env vars (GEMINI_API_KEY/OPENAI_API_KEY/XAI_API_KEYorGROK_API_KEY/ANTHROPIC_API_KEY). New unit testsTestResolveModelForAdkintests/test_agent_runtime_cache.pycover Gemini passthrough (6 ids incl. case and whitespace variants), LiteLlm wrapping (OpenAI, xAI, Anthropic, Groq, Mistral prefixes), and non-string passthrough. - Automatic retry on transient provider errors for AI agents.
GoogleADKRuntime.run()now wraps the ADK runner in a 3-attempt retry loop with exponential backoff (2s/4s/8s). Catches transient failures (network blips, 429 rate limits, 500/503/529 server errors, brief timeouts) and retries silently; real configuration errors (auth failures, invalid model ids, context-length-exceeded) still fail fast via a non-retryable error classifier. For the LiteLLM path,litellm.num_retries = 3is also set so the LiteLLM layer gets its own provider-aware retry (HTTP-level, 429 Retry-After aware) inside each attempt. Production agents now survive normal cloud-provider hiccups without strategy-level error handling. LiteLLM also configured withdrop_params = True(silently drops provider-unsupported params instead of raising; needed for xAI rejectingmax_completion_tokens) andsuppress_debug_info = True(mutes cosmetic "Provider List" banner for current-generation models not yet in LiteLLM's static cost registry likegpt-5.4-*andgrok-4.20-*). - Discretionary Trader demo + multi-provider parallel runner. New
lumibot/example_strategies/agent_discretionary.pyis the first max-discretion AI agent demo: one-sentence user prompt ("Make as much money as you possibly can."), no asset whitelist, shorts allowed, readsAGENT_MODELenv var so the same file runs against any supported provider (Gemini, OpenAI, xAI, Anthropic). Tools: FRED macro + Alpaca news (graceful-degrade if keys unset) + yfinance fundamentals snapshot (P/E, forward P/E, market cap, profit margins, earnings dates, analyst targets, short interest, 52W range, sector, industry) + all built-ins. Companion scriptscripts/run_discretionary_3way.pylaunches three providers in parallel with a memory watchdog (aborts all if combined RSS > 10 GB or any single process > 5 GB), per-provider log capture, and a summary JSON of tearsheet paths + metrics for post-run comparison. Docs updated indocsrc/agents_canonical_demos.rstwith the full "5 demos" list (v1 discretionary + 4 existing constrained allocators). - AI agent error classifier + live-vs-backtest error-handling semantics (AI-agent-scoped only; zero changes to main Lumibot error handling). New
_classify_agent_errorhelper inlumibot/components/agents/runtime.pybuckets every exception from an agent call intoauth/config/billing/transient/unknown. Classification uses class name match (litellm/openai/anthropic/google-genai), HTTP status code when the provider SDK attaches one, and message-substring keywords (insufficient_quota,credits,billing,payment,no credits,team doesn't have any credits) so e.g. xAI's 403 "your team doesn't have any credits yet" is correctly bucketed asbillingnotauth.GoogleADKRuntime.run()now only retriestransient+unknown; auth/config/billing errors propagate immediately instead of burning the 10-attempt 5-minute retry budget on errors that cannot self-heal.AgentHandle.run()wraps the runtime call with a classifier-aware safety net: in backtest mode,auth/config/billingre-raise loudly with a clean error banner that points at the env-var to fix and the provider billing URL (so users fix and re-run instead of getting a silent +0% tearsheet); in live mode, all categories log + skip the iteration so the bot never stops. Transient and unknown errors always skip gracefully in both modes. This is fully scoped insidelumibot/components/agents/*— the mainstrategy_executor.py,_strategy.py, broker/trader files, and_on_bot_crash/gracefully_exitbehavior are unchanged. 22 new regression tests intests/test_agent_runtime_cache.py(TestClassifyAgentError,TestAgentHandleRunClassifiedErrorBehavior) cover classifier buckets, backtest crash semantics, and live-never-crash invariants. Retry schedule retuned from 3 attempts × 14s budget (old) to 10 attempts × ~5-min budget with no single backoff exceeding 60s (2, 3, 5, 10, 20, 30, 45, 60, 60, 60). Rich error handling documentation added todocsrc/agents.rstin a new "Error Handling and Reliability" section. - AI agent model auto-populates into
strategy.parameters. Every call toself.agents.create(name=..., default_model=...)now auto-setsstrategy.parameters[f"agent_{name}_model"]to the resolved model id, which surfaces in the tearsheet's "Parameters Used" panel. Multi-agent strategies get one key per agent. Zero per-strategy code changes required — works for any existing or future agent strategy. Three regression tests inTestAgentManagerParametersAutoPopulate. - AI token usage summary + detailed audit artifacts for backtests. Every AI agent run now updates cumulative token totals directly in
strategy.parameters, so the tearsheet's "Parameters Used" panel showsagent_<name>_calls,agent_<name>_input_tokens,agent_<name>_output_tokens,agent_<name>_total_tokens,agent_<name>_thinking_tokens,agent_<name>_tool_calls,agent_<name>_cache_hits,agent_<name>_detail_csv, andagent_<name>_detail_parquetautomatically. In addition, LumiBot writes*_agent_detail.csvand*_agent_detail.parquetbeside the normal backtest artifacts, following the existing CSV-compatible / Parquet-fast contract used by trades, stats, trade_events, and indicators. The detail artifact contains one row per model event (thinking,text,tool_call,tool_result,usage) with the effective prompt, task prompt, user context, runtime context, event text, flattened tool details, warnings, trace path, and token counts for the call. This is intentionally token-only: no pricing logic, no stale model-cost tables. Gemini requests thought summaries when the SDK supports them; other providers still record thinking token counts even if they do not expose thought text. Regression coverage added intests/test_agent_runtime_cache.py.
4.5.3 - 2026-04-21
Changed
- Agent prompt now instructs strategies on risk and drawdown discipline. New "RISK AND DRAWDOWN DISCIPLINE" block in
AgentHandle's base prompt (lumibot/components/agents/manager.py) tells generated strategies to optimize for risk-adjusted return, not headline return; explains recovery math (20% DD needs 25% gain, 50% needs 100%, 80% needs 400%); sizes positions by conviction + volatility, not just cash; cuts broken theses instead of averaging down; does not chase losses with more size; and thinks in Sharpe/Sortino/Calmar terms. Zero code-path changes; purely a behavioral shift in what agent-generated strategies do.
4.5.2 - 2026-04-21
Added
- WEEX broker support via CCXT. New
WEEX_CONFIGinlumibot/credentials.pywires WEEX (CCXT exchange idweex) into the broker auto-detect chain, the explicit-name branch (TRADING_BROKER=weex), and the data-source aliases (DATA_SOURCE=weex). WEEX requires three credentials (WEEX_API_KEY,WEEX_API_SECRET,WEEX_API_PASSPHRASE) and has no API sandbox, sosandboxis hard-wired toFalse. Added"weex"to the exchange-id whitelists inlumibot/brokers/ccxt.py(balance parsing at line 104 and open-orders routing at line 224) so the generic Ccxt broker accepts WEEX without raisingNotImplementedError. Initial scope is spot trading only; WEEX's primary business is USDT-margined perpetual swap, but swap support is not wired into the shared Ccxt broker today. Note that WEEX's Terms of Use exclude US and Canadian residents, documented indocsrc/brokers.ccxt.rst. New unit testtest_initialize_weex_brokerintests/test_ccxt.py.
Changed
- Docs: Coinbase section now documents CDP (Cloud Developer Platform) private-key auth.
COINBASE_CONFIGalready supported PEM private keys via thesecretfield (CCXT auto-detects PEM vs HMAC), but the docs still described the legacy HMAC + passphrase flow. Updateddocsrc/brokers.ccxt.rstto explain thatCOINBASE_API_KEY_NAMEholds the org-qualified identifier from the CDP JSON andCOINBASE_PRIVATE_KEYholds the full PEM block (multi-line,-----BEGIN EC PRIVATE KEY-----...-----END EC PRIVATE KEY-----).COINBASE_API_PASSPHRASEis now documented as legacy-only.
Fixed
Data.iter_count()with a microsecond-precision index returned incorrect cursor positions._index_values_nsassumed the pandasDatetimeIndexwas nanosecond-precision, but pandas 2.x can build microsecond-precision indexes from ISO strings. Normalize to nanoseconds explicitly so thenp.searchsorted(index_ns, now_ns)cursor math works regardless of pandas' chosen unit. Regression test intests/test_data_iter_count_microsecond_index.py.
4.5.1 - 2026-04-20
Fixed
Strategy.get_last_price()returnedNoneat sim_time=00:00 on 24/7 markets, breaking strategy rebalance logic. When a strategy's iteration fires at midnight (e.g.MARKET="24/7"plus daily sleeptime), the daily-shortcut'slength=1, timeshift=0slice returns empty because no day bar exists exactly at sim_time=00:00. Without a retry the shortcut falls through tobroker.get_last_price, which on the IBKR REST backtest path also fails at non-market-hours timestamps. Net result: strategies seeNoneon every iteration and the rebalance path'sif last_price is None: continueskip means no orders are ever placed. Reported by Rob on the 2026-04-19 local IBKR backtest: Alpha Picks ran for 31 simulated days atVal: \$100,000with zero trades. Fix: when the length=1 slice comes back empty, retryget_historical_prices(length=5, timestep="day")and return the close of the last row withindex <= sim_time. No guard, no rejection of other slices — this ONLY triggers when the length=1 result is honestly empty. Matches Rob's stated semantics: "if we have no bar at sim_time, the price hasn't changed since the last known bar." New regression testtest_forward_fill_returns_last_prior_bar_when_length1_emptyintests/test_get_last_price_sim_time_safety.pycovers the 24/7 midnight case.- Tearsheet cumulative-returns plot started the Strategy line below the Benchmark line at the left edge. QuantStats computes cumulative returns as
(1 + returns).cumprod() - 1, so the first plotted point equals the first day's return. When the strategy bought at open and closed with a small drawdown on day 1, the Strategy line began at e.g.-4%while the Benchmark's firstpct_changewas NaN→0 and started at0%. Rob reported this as a recurring visual artifact on Alpha Picks tearsheets (multiple sightings over months). Two-part fix: (1)lumibot/tools/indicators.py::_prepare_tearsheet_returnsnow prepends a day-0 anchor row (first_index − 1 day) with both returns = 0 whenever the first real row has a non-zero return; (2) passmatch_dates=Falsetoqs.reports.htmlat both call sites increate_tearsheet. Without the second part, QuantStats'_match_datesexplicitly drops leading zero-return rows (loc = max(returns.ne(0).idxmax(), benchmark.ne(0).idxmax()), truncates both series toloc:) and deletes our anchor before it reaches the plot. With both parts, the Strategy and Benchmark lines share a common 0% starting point on the left edge. Regression testtest_tearsheet_prepends_anchor_when_first_day_has_non_zero_returnintests/test_tearsheet.pypins the anchor prepend; thematch_dates=Falsechange is validated by visual tearsheet inspection. - IBKR helper:
get_price_data()crashed with'str' object has no attribute 'symbol'when callers passed a bare symbol string instead of anAsset. Observed in the 2026-04-19 local Alpha Picks IBKR backtest: upstream path leaked a string through, producing misleading log spam"IBKR history fetch failed for None/USD timestep=minute ..."that obscured the real bug during investigation. Fix: defensive coercion at the top oflumibot/tools/ibkr_helper.py::get_price_data— ifassetis a string, wrap it inAsset(symbol=asset)before proceeding. One-line safety net at a system boundary; the log message becomes accurate (IBKR history fetch failed for COP/USD ...) and the rest of the pipeline works.
4.5.0 - 2026-04-19
Note: the in-flight
4.4.63work was never published to PyPI (nov4.4.63tag). Those entries are consolidated into this4.5.0release alongside theStrategy.indicatorssubsystem.
Added
Strategy.backtest()now honoursBACKTESTING_START/BACKTESTING_ENDenv vars as overrides for hardcodedbacktesting_start/backtesting_endargs. Strategies typically pin dates in theirif __name__ == "__main__":block for local dev convenience. When the same code runs in a managed container (bot_manager backtest ECS task, BotSpot MCPstart_backtest, etc.) the orchestrator sets these env vars from the caller's requested date range — previously those env vars did nothing, the hardcoded datetimes won, and MCP callers had no runtime control over the window. New precedence: env var (if present and parseable) > passed arg. Accepts ISO date (YYYY-MM-DD) or full ISO datetime; malformed values are logged and the passed arg is preserved (no silent fallback to the epoch ornow). Unit tests intests/test_strategy_backtest_env_override.pycover both shapes, empty-string handling, parse failures, and the no-env-set baseline.Strategy.indicatorssubsystem. New per-strategy indicator accessor that computes any indicator over the full bar series once, then hands back the value at the current bar in O(1) on every subsequent call. Eliminates the per-iterationdf.rolling(...).apply(...)full-history recompute pattern that dominated wall-time on indicator-heavy strategies. Exposes the entire pandas-ta-classic surface (~130 indicators) viaself.indicators.<indicator>(asset, **kwargs)passthrough plusself.indicators.custom(name, fn, asset, **kwargs)for user-defined indicators. Same API for backtest and live. Seedocsrc/indicators.rstfor the full reference and migration guide.LUMIBOT_CACHE_MISS_DEBUGenv var gates opt-in[CACHE_MISS]/[FETCH]warning traces inibkr_helper.pyfor auditing why the cache layer decided to hit the network. Documented indocs/ENV_VARS.mdanddocsrc/environment_variables.rst.
Changed
Strategy.indicators: per-bar lookup now O(log N) viasearchsorted._latest_scalar/_latest_rowno longer allocate a.loc[:now]slice on every call; they compute the integer position of the most-recent bar ≤ now withIndex.searchsorted(now, side="right") - 1andilocdirectly. Collapses the per-iteration cost of a memoized indicator from a full label-compare slice to a binary search + one integer index. Observed impact ontqqq_medianbenchmark (13-year daily): 54.71s → 5.59s end-to-end (~9.8x faster per-iteration lookup — the memoized compute was already one-shot; this fixes the hot-path indexing cost).Strategy.indicators: cache keyed on full-history fingerprint._dispatchnow tags each memoized result with(len(df), df.index[-1])and recomputes transparently when the underlying bar series grows (routed/live prefetch). Prevents stale indicator output when the data source extends the frame after the first call.Strategy.indicators: prefetch-aware full-history fallback._full_historynow tries_data_storefirst, then — if empty — callsget_historical_prices(length=self._fallback_length)purely to trigger the routed/live adapter's prefetch path, and re-reads the now-populated store. This makes the indicator subsystem work correctly on routed/IBKR backtests where_data_storeis populated lazily on first access._fallback_lengthreduced from 100_000 to 10_000 to avoid pandas ns-timestamp overflow (100k daily bars ≈ year 1573, below the pandas epoch).lumibot/backtesting/routed_backtesting.py: narrow short-circuit in_IbkrRoutingAdapter.update_pandas_dataskips IBKR's 1-minute prefetch forcont_future/futurequote-lookup calls when coarser non-day cadence (15-minute, 30-minute, hour, …) is already loaded. Affectsget_quote(asset)default-timestep lookups during backtests. Observed impact onmes_ema_15mbenchmark: 26.6s → 7.03s (3.78x) on warm cache; results byte-identical.lumibot/data_sources/pandas_data.py: module-level_USD_FOREX = Asset("USD", "forex")singleton reused byfind_asset_in_data_store()to avoid constructing a fresh default-quoteAsseton every call (~200K calls/backtest). Missing-asset warnings now gate to first-encounter per(asset, timestep)to avoid flooding 1-minute backtest logs with redundant warnings.
Fixed
- CRITICAL:
Strategy.get_last_price()daily-cadence shortcut had a look-ahead read that returned future bars on IBKR / ThetaData / routed backtests. The shortcut atlumibot/strategies/_strategy.py::get_last_pricecalledself.get_historical_prices(asset, length=2, timestep="day", timeshift=-1, ...)and then tookfloat(bars.df["close"].iloc[-1]).Data.get_bars(length=2, timestep="day", timeshift=-1)computesend_row = iter_count + 2,start_row = iter_countand returns rows[iter_count, iter_count + 1]— i.e. the bar AT sim-time AND the next bar after it — soiloc[-1]picked up the future bar. In a backtest the full history exists ahead of the sim clock, so when the underlying frame also contained bars past the backtest window (e.g. a shared S3 cache row stamped at wall-clock "today") the shortcut returned today's close for a 2022 sim-time. Observed incident 2026-04-17 on BotSpot Stock Alpha Picks (backtestf2d9ca86, window 2022-07-01 → 2022-08-01):get_last_price(COP)returned $97.43 (real-now close) instead of the 2022-07-01 close of $90.98, polluting position sizing across every symbol. Fill prices were unaffected (historical-bars fill path is separately sim-time-safe); only theget_last_priceshortcut leaked. Fix: change the shortcut tolength=1with no timeshift soData.get_barsreturns exactly the single bar atiter_count— guaranteed to be at or before sim_time regardless of what the frame contains past it. New regression coverage intests/test_get_last_price_sim_time_safety.py(7 cases): polluted-frame reproduction replaying the exact Alpha Picks COP/NUE symptom, dense-frame sanity, sim-time-before-any-bar fall-through, and a whitebox guard on thelength=1, timeshift in (None, 0)call signature so a future edit can't silently restorelength=2, timeshift=-1. The existingtest_strategy_methods.py::test_get_last_price_prefers_day_bars_for_routed_backtesting_daily_cadencetest — which had been pinning the buggyiloc[-1]return of a two-row frame as "correct" — is rewritten to assert the correct sim-time-safe behaviour. Operationally, the shared IBKR daily stock/index S3 cache was wiped on dev + prod (stock/index day + minute; futures/crypto/option preserved per theibkr/future/rule) to clear any lingering wall-clock-stamped rows that may have been written by earlier buggy code paths. - CRITICAL: IBKR stock/index daily pagination cap was too tight, producing flat-price backtests over multi-year windows.
IBKR_STOCK_INDEX_DAILY_MAX_PERIODwas hardcoded to"180d", which made_fetch_history_between_datesrequest only ~125 bars per call against an endpoint that caps responses at 1000. In practice the backward-walking pagination loop completed in a single iteration: the initial fetch near real-now filled the cache with 180 days of recent data, and the coverage check for every subsequent simulation date declared that "real bars exist" and stopped re-fetching. The strategy then received today's prices for every historical simulation date, producing flat-VIX / flat-SPY / flat-RUT series across the entire backtest window. Observed incident: Peter Credit Spreads backtesta83663bd-...(2018-01-01 → 2026-04-16) hadVIXconstant at14.2for all 2,010 simulated days (stddev=0) and max drawdown of-94%. Production verification against IBKR Client Portal REST on 2026-04-17:period=5yreturns the full 1000-bar cap (~4 trading years of daily data),period=2yreturns 323 bars,period=180dreturned only ~125 bars. Fix: raiseIBKR_STOCK_INDEX_DAILY_MAX_PERIODto"5y"so each call gets the maximum usable response and the backward-pagination loop covers 8-year windows in ~2 iterations per symbol/source. Regression tests intests/test_ibkr_helper_unit.pylock the new value in place. End-to-end verified locally against Peter Credit Spreads: VIX now has 497 distinct values (9.15–82.69) over 2018-01 → 2020-06 with the March 2020 COVID spike to 82.69 captured correctly; strategy max drawdown collapses from-94%(broken) to-5%(working, regime filter actually firing). - Backtesting: persist placeholder markers for IBKR terminal pagination-empty responses.
_fetch_history_between_datesraises"IBKR history pagination returned empty data before covering the requested window"when IBKR serves partial-then-empty responses (common for entitlement/stitching gaps like CONT_FUTURE 1-minute Trades). The error was caught but_is_terminal_no_data_errordid not match the message, so no placeholder marker was written and every backtest refetched the same ~23s empty-result call. Fix: extend_is_terminal_no_data_errorinlumibot/tools/ibkr_helper.pyto match the pagination-empty tokens, so_record_missing_windowpersists amissing=Truemarker and_window_is_placeholder_coveredshort-circuits on subsequent runs. Observed impact onvband_mnq_mes_1mbenchmark: 55.8s → 2.94s (18.98x) on warm cache; all results byte-identical. - CRITICAL: IBKR backward pagination discarded all earlier chunks on a single empty mid-walk page, breaking CME futures backtests.
_fetch_history_between_dateswalks backwards fromend_dtinperiod-sized windows (default1000minfor 1-min bars). CME futures markets have a weekly weekend close (Fri 4pm CT → Sun 5pm CT) and a nightly maintenance break (4–5pm CT); when a1000minwindow lands entirely in a no-trading range, IBKR correctly returns{"data": []}even though the surrounding bars are valid. A previous fix (3c5cbb5c, 2026-03-05 "ibkr: keep paged history chunks when later page is empty") changed the empty-page handler toif chunks: breakso earlier chunks survived, but the WIP checkpointaf8df88b(2026-03-30, pre-4.4.57 branch reconcile) silently reverted it back toraise RuntimeError("IBKR history pagination returned empty data before covering the requested window ..."). The regression was then released onversion/4.4.57+ and carried intoversion/4.5.0. Impact: everyget_historical_prices()call for CME futures (MES/MNQ/ES/NQ/…) that needed to walk past a weekend would raise, and the resulting empty-frame return meant the strategy saw no bars for every subsequent iteration. Observed inmes_ema_15mbenchmark onversion/4.5.0: 209 pagination-empty errors in a single 6-month backtest, strategy unable to trade after bar 1, backtest runs to 40-min wall-clock cap without completing. Fix: restore theif chunks: breakbehaviour in both theif not data:andif df.empty:branches of the backward-pagination loop, exactly matching3c5cbb5c. New regression testtest_ibkr_fetch_history_keeps_chunks_when_mid_paging_page_is_emptyintests/test_ibkr_crypto_daily_series.pyforces three mocked history calls where call 3 returns empty, and asserts that the two prior chunks are returned (verified to raiseRuntimeErrorwithout the fix, pass with it).
4.4.62 - 2026-04-15
Fixed
- CRITICAL: Flat-price fabrication bug in backtesting fills.
BacktestingBroker._pandas_fill_pathpreviously pickeddf_original.iloc[-1:]when the "bars at or after self.datetime" filter came back empty, ORdf_original.iloc[0]when the filter caught rows far in the future. Both paths could silently price every fill at a single bar's OPEN that had nothing to do with the simulation time, producing deterministic constant prices across multi-year backtests. Observed incident: BotSpot "Alpha Picks" backtest where 72 of 84 stocks froze at the OPEN of 2026-04-10 for every fill across 2022-2024. The fix rejects future bars outside a narrow window (2 days for daily, 2 hours for hourly, 5 minutes for minute), prefers the most recent bar at or before the simulation time, and refuses to fabricate a fill when no bar exists within a reasonable window ofself.datetime. A second-layermax_fill_distancesanity check (7 days for daily, 1 day for intraday) hard-rejects any fill price derived from a bar too far from the simulation clock, cancelling the offending iteration rather than producing a garbage fill. Provider-agnostic; applies to every data source that flows throughPANDAS/InteractiveBrokersRESTfill paths (ThetaData, IBKR, Polygon, etc.). - IBKR history loading now fails open by returning available real bars instead of synthesizing an empty dataset when the cache refresh leaves the requested window underfilled (commit
5de1362a, April 14). - Interactive Brokers REST backtesting data source now prefers an already-loaded daily stock/index series for
get_last_price/get_quotebefore triggering a separate intraday minute fetch, which avoids unnecessary VIX/USD midpoint history requests during daily-cadence backtests (commit5de1362a, April 14). Order.to_dict()now emitsidentifierandavg_fill_priceconsistently so downstream consumers that rely on these fields on serialized orders no longer see missing keys after backtest-time fills (commite3cb48fe, April 15).- Release the IBKR downloader fail-closed hotfix from the corrected commit so CI and PyPI ship the same behavior validated locally (merged from dev).
Changed
- README restructured for visitor conversion: Quick Start code block, competitor comparison table (LumiBot is the only open-source Python trading library with options support), supported brokers & data sources matrix, migration guide from Backtrader (merged from dev).
- PyPI metadata overhaul: added 20 search keywords and 12 classifiers (Financial, Investment, AI, Python 3.10-3.12) after being previously empty. Updated package description for search discoverability (merged from dev).
- Author email updated to rob@botspot.trade; added project_urls for documentation, Discord, and BotSpot platform (merged from dev).
Added
docs/MIGRATING_FROM_BACKTRADER.md: concept mapping and side-by-side code examples for users switching from the now-unmaintained Backtrader library (merged from dev).- Release readiness script now verifies that every referenced artifact file actually exists on disk (not just that
artifact_pathis non-empty), preventing stale pointer references from passing the deployment gate (commit323b6488, April 12). - LumiBot matrix runner emits
lumibot_runner_state.jsoncheckpoint files during long runs so in-flight case state is inspectable during multi-hour acceptance runs (commit6944e22c, April 12).
4.4.61 - 2026-04-01
Release bookkeeping only. No functional code changes from 4.4.60.
4.4.60 - 2026-04-01
Fixed
- Data Downloader queue client now uses a dedicated configurable connect-timeout budget instead of a hardcoded
5s, which prevents IBKR/VIX history refreshes from failing closed on slow downloader connections. - IBKR history loading now fails closed when a refresh leaves the requested window underfilled, so stale cached slices are no longer returned as if they were complete history.
4.4.58 - 2026-04-01
Added
@agent_tooldecorator now auto-includes function source code in tool descriptions, giving AI agents full visibility into parameters, defaults, and implementation details without manual documentation.AgentHandlenow always merges built-in tools with custom user tools (previously custom tools replaced built-ins).- Four new canonical agent demo strategies: M2 Liquidity (FRED data), Macro Risk (Alpaca bars), Momentum Allocator (Alpaca bars + news), and News Sentiment (Alpaca news). These replace the previous stress-test examples with production-quality patterns.
- Version logged at startup (
LumiBot v{version} starting) vialogger.infofor CloudWatch/backtest/live log visibility. - Version included in backtest
settings.jsonartifacts (lumibot_versionfield) for post-deploy verification. - Auto-create next version branch job in release workflow to prevent team-blocking delays after a release.
- Post-deployment verification steps documented in
DEPLOYMENT.md.
Changed
- Improved lookahead-bias guardrails in agent system prompts: agents must now explicitly set end-date bounds on ALL temporal tool parameters, not just known ones.
- Major documentation refresh: agents quickstart, canonical demos, observability, FAQ, getting started page with agent framework introduction.
Fixed
BACKTESTING_QUIET_LOGSenv var parsing was broken (comparing string toNone); now correctly parses boolean-like strings (true,1,yes,on).- Removed contradictory
set_console_log_level("ERROR")call whenquiet_logs=Falseintrader.py. - IBKR pagination test assertions updated to match current behavior.
.gitignorefix for deployment reliability.
4.4.57 - 2026-03-30
Changed
- Bump
quantstats-lumidependency to>=1.1.3,<1.2.0so tearsheet consumers require the renamedWorst 1-Month Returnrow and the latest machine-readable contract.
Fixed
- Backtest console print settings no longer get silently overwritten when
lumibot_loggerre-applies log levels during a backtest run. (PR #981 — @davidlatte) - Tearsheet summary artifact compatibility with
quantstats-lumimachine-readable metric contract (typed scalar values, no%string leakage in JSON scalar values). - Removed the duplicate
cash_financing_rates()strategy hook so cash financing now uses a single public interface centered onset_cash_financing_rates(...). - Backtest stats, plots, and tearsheet inputs now subtract external cashflows from returns, so deposits and withdrawals no longer distort
total_return, CAGR, or other performance metrics. - Backtest runners now honor caller-provided
plot_file_htmlandtrades_filepaths instead of silently writing trade artifacts to the defaultlogs/directory.
Added
- Tradier stock shorting support:
sell_shortandbuy_to_coverorder sides now map correctly so short-selling equities works on Tradier. (PR #976 — @brettelliot) - AI trading agent framework:
self.agents.create(...)inside strategies with DuckDB query tools, agentic backtesting with replay cache, and external MCP server mounting. New modules underlumibot/components/agents/. - End-to-end tearsheet custom-metrics proof coverage for real backtest runs that generate both
tearsheet.htmlandtearsheet_metrics.json. - Backtest cash-accounting coverage for
adjust_cash,deposit_cash,withdraw_cash, and strategy-managed financing-rate updates. - Normalized
cash_eventslive payload support in LumiBot for Alpaca and Tradier, including stable event IDs, retry-safe pending emission, and bounded payload serialization. - Period-delta cash columns in
stats.csv(cash_*_period) for manual inspection of deposits, withdrawals, financing accruals, and cashflow-adjusted return math. - Cash-event rows in
trades.csv/ parquet and cash-event markers intrades.html, including deposits, withdrawals, and financing credits/debits.
Docs
- Expanded public documentation for
tearsheet_custom_metrics(...), including parameter structure, full examples, literal-scalar unit behavior, and release-order guidance for QuantStats/LumiBot metric changes. - Added public documentation for strategy cash accounting, financing lifecycle usage, broker cash-event normalization, and broker-specific limitations for Alpaca and Tradier.
4.4.55 - 2026-03-15
Added
BACKTESTING_PARAMETERSenvironment variable support for parameter injection in backtest runs.- Machine-readable
*_tearsheet_metrics.jsonartifacts (summary-first) with placeholder output on insufficient/degenerate returns. - New strategy lifecycle hook
tearsheet_custom_metrics(...)for appending custom metrics to tearsheet HTML and JSON artifacts. - Regression coverage for multi-timeframe day-timestep stock lookup and tearsheet metrics/custom-hook passthrough.
Changed
- Backtest analysis and trader APIs now accept
tearsheet_metrics_file; default output filename is*_tearsheet_metrics.json. - QuantStats
metrics_jsongeneration now runs insummary_onlymode and forwards custom metrics to both HTML and JSON outputs. - Documentation updates for tearsheet metrics/lifecycle hooks and TradingFee guidance (
per_contract_feeusage).
Fixed
- Day-timestep asset lookup regression for multi-timeframe stock/index backtests (including minute->day fallback paths where appropriate).
- IBKR stale no-data cache reuse now forces refresh when requested windows extend beyond cached coverage.
- ProjectX order processing race-condition and tracking hardening merged from
dev.
Deploy marker: 15e8e268 ("deploy 4.4.55")
4.4.54 - 2026-03-08
Added
TradingFeenow supportsper_contract_feefor broker-style option commissions charged per contract.- Regression tests for
per_contract_feeinitialization and trade-cost calculations in backtesting.
Changed
TradingFeefee fields now coerce throughDecimal(str(...))for stable decimal handling across float inputs.
Fixed
- Backtesting trade-cost calculations now apply
per_contract_fee * quantityfor taker and maker fee paths (market,stop,limit,stop_limit,smart_limit).
4.4.53 - 2026-03-06
Added
- Regression tests for daily-cadence datasource seeding in
StrategyExecutor, routed1Dtimestep normalization, put-delta normalization/model-path strike selection, and IBKR equity corporate-action cache reuse. - Regression tests for IBKR paged-history retention when later pages are empty, plus option valuation fallback coverage for off-session stale mark scenarios.
Changed
- Daily-cadence backtests now seed datasource cadence to
dayduring strategy initialization to avoid first-lookup minute prefetch blowups. Strategy.get_last_price()now consistently prefers daily bars for stock/index assets in daily backtest cadence, including routed IBKR stock/index paths.- Routed backtesting now treats day-like timestep aliases (
1D,1day, etc.) as daily cadence for non-Theta last-price/quote reads. - ThetaData daily option fetches now prefetch forward in bounded chunks (capped by expiration/end) to reduce repeated downloader round-trips during long runs.
- Option helper strike selection now normalizes absolute delta inputs by option side and uses a fast model-based strike pick for Theta daily option backtests.
- IBKR equity corporate-action enrichment now uses Yahoo history with coverage hints (
last_needed_datetime) and date-bucket cache keys for stable reuse. - Backtest artifact export now always writes CSV/parquet outputs for trades/stats/indicators/trade-events regardless of
show_plotmode.
Fixed
- Guarded option MTM valuation against off-session stale marks that could cause transient portfolio-value drops in backtests.
- Fixed IBKR history pagination to preserve already-fetched chunks when a subsequent page returns empty.
- Refreshed acceptance baseline metrics for
aapl_deep_dip_callsandleaps_alpha_picks_shortto match current deterministic CI outputs. - Updated
test_classic_60_40drift-rebalancer expectations to the corrected daily-cadence fill quantities.
4.4.52 - 2026-03-03
Added
- Regression tests for Yahoo corporate-actions helpers (
get_symbol_actions,get_symbols_actions) and IBKR daily equity action enrichment. - Regression test for routed IBKR daily stock prefetch to guarantee full lookback warmup coverage.
Changed
- Production-readiness harness (
scripts/ibkr_theta_prod_readiness.py) now defaults SPX stress windows to 3 months (2025-01-01through2025-03-31) with a longer timeout. - Prod-like runner (
scripts/run_backtest_prodlike.py) now supports--perf-modefor cleaner runtime benchmarking without plot/indicator/progress noise. - Routed IBKR daily stock/index prefetch now uses the computed bar lookback window (
start_datetime) instead of a short calendar cap from backtest start. - Acceptance performance history records were refreshed for ongoing regression tracking.
- Deployment runbook now documents local-timeout fallback and explicit review of local-only commit ranges before release.
Fixed
- Yahoo helper typo in corporate-actions paths (
get_symbol_actions/get_symbols_actions) that prevented IBKR equity split/dividend enrichment from loading actions. - Acceptance gate hardening: apply a bounded, case-scoped tolerance override for
ibkr_crypto_acceptance_btc_usdmetric jitter (CI/provider-data drift) to reduce false negatives. - Router benchmark stats now prefer routed datasource bars for stock benchmarks and only fall back to Yahoo on router fetch failure (removes flaky Yahoo-first behavior in CI).
4.4.51 - 2026-02-26
Added
- Option lifecycle event support in backtesting for option expiration outcomes:
assigned,exercised, andexpired(in addition tocash_settled). - Regression coverage for equity/ETF physical settlement and index cash settlement paths at expiration.
- Opt-in early-assignment heuristic model for short ITM, physically-settled options (
strategy.parameters:option_early_assignment_enabled,option_early_assignment_max_dte_days,option_early_assignment_max_extrinsic).
Changed
- Options expiration behavior now follows broker-style settlement defaults:
- Equity/ETF options settle physically at expiration (short ITM -> assignment, long ITM -> exercise when account constraints allow).
- Index options settle to cash at intrinsic value.
- Trade artifacts now preserve option-expiration lifecycle statuses in
trades.csv/trades.parquetandtrade_eventsexports so downstream consumers can render assignment/exercise/cash-settlement explicitly.
Fixed
- ThetaData daily options MTM: prefer snapshot quote marks over stale day marks, and allow forward-fill when snapshot data is unavailable.
- ThetaData backtesting: keep intraday index minute/hour fetch bounds aligned to the simulation timestamp instead of forcing full-window end coverage.
- Long ITM equity option expirations now avoid unrealistic forced delivery when account constraints are not met; these contracts expire unexercised in backtests.
- Acceptance baselines: refresh
aapl_deep_dip_callsandleaps_alpha_picks_shortmetrics to match current option settlement behavior.
4.4.50 - 2026-02-19
Changed
- Indicators HTML: improve subplot scaling so indicator panels render with sane proportions across mixed plots.
- Indicators export: make HTML export non-fatal so backtests still complete if HTML rendering fails.
Fixed
- Acceptance baselines: refresh 0DTE backdoor baseline metrics and timing metadata to match current provider data revisions.
- Acceptance CI: allow a bounded queue-fill threshold for
spx_short_straddle_reprowhile keeping strict queue-free checks for other ThetaData acceptance cases.
4.4.49 - 2026-02-10
Added
- Backtesting artifacts: add
LUMIBOT_BACKTEST_PARQUET_MODEwithrequiredcontract mode (fail-fast on parquet export failures) and structured parquet export logs (rows/cols/bytes/duration, coerced columns).
Changed
- Indicators: always emit
*_indicators.csv+*_indicators.parquet, even when a strategy produced no markers/lines/OHLC (empty indicators = valid artifact). - Trade events: always emit
*_trade_events.csv+*_trade_events.parquet(empty events = valid artifact).
Fixed
- Stats: stop embedding raw
Assetobjects in thepositionsstats snapshot; sanitize object-ish stats columns before parquet export to preventConversion failed for column positions with type object.
4.4.48 - 2026-02-10
Added
- Backtesting artifacts: emit Parquet siblings for
*_indicators.csv,*_trades.csv,*_stats.csv, and*_trade_events.csv(zstd + PyArrow). CSV remains the compatibility layer.
Changed
- Tradier: support OAuth payload + access token refresh; add runtime notes for the refresh flow.
- Tests: mark DataBento backtest coverage as
apitestso the default CI suite stays deterministic without vendor credentials. - Docs: clarify auto-expiry futures behavior and IBKR crypto roots.
Fixed
- Data: handle
timeshift=Nonein Data bars. - Futures (auto-expiry): make selection roll-aware and harden IBKR conid negative cache behavior.
4.4.47 - 2026-02-07
Added
- Backtesting: support
BACKTESTING_BUDGETenvironment override for strategy backtest cash/budget.
Changed
- Downloader: rename the downloader queue client module from
thetadata_queue_clienttodata_downloader_queue_client(provider-agnostic naming).
Fixed
- IBKR: parse seconds-style timesteps (e.g.
20S) for history requests where supported. - IBKR crypto futures: harden continuous futures expiration selection in backtesting.
- Logging: avoid stale env-driven logger levels by re-applying Lumibot logging configuration on each
get_logger()call (reduces test flakiness when env vars toggle).
4.4.46 - 2026-02-04
Fixed
- Backtesting routing: when
futures/futureis configured, defaultcont_futureto the same provider soAssetType.CONT_FUTUREdoes not fall back todefault. - Backtesting performance: default per-asset fetch throttling (
sleep_time) to 0 for backtesting data sources (keeps live default throttling unchanged). - Backtesting performance: bound
get_trading_days()calendar initialization to the backtest date window to avoid building decades of unused schedules.
4.4.45 - 2026-01-30
Fixed
- Release: include
lumibot/resources/ThetaTerminal.jarin the PyPI wheel/sdist (required by BotManager and ThetaData setup). - Backtesting:
BacktestingBroker.process_pending_orders()now accepts both iterable order buckets and legacy buckets that exposeget_list().
4.4.44 - 2026-01-30
Added
- Charting:
Strategy.add_ohlc()andStrategy.get_ohlc_df()for exporting OHLC (candlestick) indicator series. - Indicators:
plot_indicators()now supports OHLC series in*_indicators.htmland exportstype=ohlcrows in*_indicators.csv. - Docs: add seconds-level backtesting guidance and expand seconds-mode notes.
Changed
- Charting:
Strategy.add_line()now returns the appended dict (consistent with other chart helpers). - Docs: recommend
add_ohlc()for plotting price bars andadd_line()for single-value indicators.
Fixed
- Release: correct PyPI packaging so
lumibot==4.4.44includesStrategy.add_ohlc()(the published4.4.43wheel was missing it).
4.4.43 - 2026-01-30
NOTE: The PyPI lumibot==4.4.43 artifact was published from an older commit and does not include the changes
listed below. Upgrade to lumibot==4.4.44.
Added
- Charting:
Strategy.add_ohlc()andStrategy.get_ohlc_df()for exporting OHLC (candlestick) indicator series. - Indicators:
plot_indicators()now supports OHLC series in*_indicators.htmland exportstype=ohlcrows in*_indicators.csv. - Docs: add seconds-level backtesting guidance and expand seconds-mode notes.
Changed
- Charting:
Strategy.add_line()now returns the appended dict (consistent with other chart helpers). - Docs: recommend
add_ohlc()for plotting price bars andadd_line()for single-value indicators.
Fixed
- Backtest executor safe-sleep overload now applies only in backtests and uses real sleep outside backtesting.
4.4.42 - 2026-01-30
NOTE: The PyPI lumibot==4.4.42 artifact was published from an older commit and does not include the changes
listed below. Upgrade to lumibot==4.4.43.
Added
- Charting:
Strategy.add_ohlc()andStrategy.get_ohlc_df()for exporting OHLC (candlestick) indicator series. - Indicators:
plot_indicators()now supports OHLC series in*_indicators.htmland exportstype=ohlcrows in*_indicators.csv. - Docs: add seconds-level backtesting guidance and expand seconds-mode notes.
Changed
- Charting:
Strategy.add_line()now returns the appended dict (consistent with other chart helpers). - Docs: recommend
add_ohlc()for plotting price bars andadd_line()for single-value indicators.
Fixed
- Backtest executor safe-sleep overload now applies only in backtests and uses real sleep outside backtesting.
4.4.41 - 2026-01-28
Added
- Tests: add regression coverage for futures calendar spreads (same root symbol, different expirations) to prevent margin/PnL ledger collisions.
- Docs: add investigation notes for ThetaData stale-loop behavior and futures “ghost PnL” equity spikes.
Changed
- Backtesting helpers: cache trading calendar schedules by year and slice to the requested window to reduce repeated schedule computations.
- ThetaData: avoid eager debug string building in hot paths unless debug logging is enabled.
Fixed
- ThetaData backtesting: normalize legacy/externally-warmed
prefetch_completemetadata before cache validation to prevent per-bar STALE/REFRESH thrash. - ThetaData backtesting (day): treat
tail_missing_permanent=Trueas satisfying end-coverage validation to prevent per-bar STALE→REFRESH loops on warm caches. - Backtesting futures: include expiration in futures margin/PnL ledger keys so calendar spreads (same root, different expiries) don't incorrectly net margin/realized PnL, preventing “ghost PnL” equity spikes.
4.4.40 - 2026-01-27
Added
- ThetaData backtesting: coverage-based
prefetch_completecomputation + tests to prevent per-bar STALE/REFRESH thrash when cached datasets are incomplete.
Changed
- Yahoo helper: when S3 backtest cache is enabled, hydrate cached pickles before falling back to live Yahoo fetches; upload pickles to the cache on write.
Fixed
- ThetaData EOD: enforce the provider's 365-day window limit per request and keep progress tracking consistent with chunked downloads.
4.4.39 - 2026-01-27
Added
Changed
Fixed
- Backtesting router (IBKR futures/cont_future/crypto): prefetch full backtest window once per series and slice from memory to avoid per-iteration history fetches (major warm-cache speedup).
- Indicators: prevent
plot_indicators()hovertext generation from crashing whendetail_textis missing/NaN/NA (e.g., mixed indicator points with and withoutdetail_text).
4.4.38 - 2026-01-26
Added
- IBKR futures: automatic exchange resolution for futures and continuous futures (via downloader secdef search) with persisted root→exchange cache.
- IBKR futures: regression/unit coverage for exchange routing, per-call exchange overrides, and conid registry bulk updates.
Changed
- Backtesting router: accept
futures/cont_futuresroute-key aliases for convenience (maps tofuture/cont_future).
Fixed
- IBKR futures: honor call-time
exchange=overrides consistently forget_historical_prices,get_last_price, andget_quote(live + backtesting), and include exchange in cache keys to avoid cross-exchange contamination. - IBKR futures conid registry: bulk-ingest
trsrv/futuresresponses and harden S3 persistence with merge-before-upload retry to avoid lost updates under concurrent backtests. - Continuous futures: add roll rules for COMEX micro gold (
MGC) and NYMEX crude oil (CL/MCL) and fix monthly roll selection to avoid hanging on already-rolled contract months.
4.4.37 - 2026-01-24
Deploy marker: 174875a8 ("chore: start 4.4.37")
Added
Changed
Fixed
- Backtesting: support
timestep="hour"in pandas-backed history requests (Data.get_bars()), used by routed backtesting (e.g., IBKR futures/crypto). - ThetaData backtesting: proxy missing NDX underlying/index bars/quotes via scaled
QQQso NDX options strategies have a usable underlying series. - ThetaData (downloader): normalize v3 row-style and nested option-history payloads so option quotes/chains parse correctly and caches hydrate instead of looping.
- ThetaData: stop incorrectly scaling legitimate high strikes (e.g., NDX ~ 18,000) during chain-building; only de-scale clearly thousandths-encoded payloads.
- Backtesting progress: fix progress-bar throttling keying for non-terminal sinks (prevents intermittent missing output under test runners/log capture).
- Backtesting stats: fix
cagr()/volatility()crash during end-of-run stats generation when returns index uses non-nanosecond datetime dtypes (e.g.,datetime64[us]/datetime64[s]).
4.4.36 - 2026-01-24
Changed
- IBKR futures backtesting: accelerate intraday resampling paths to avoid repeated per-iteration recomputation for timesteps like 5minute/15minute/30minute.
Fixed
- ThetaData EOD: treat all-zero OHLC rows as missing placeholders to prevent one-day portfolio valuation cliffs.
4.4.35 - 2026-01-19
Changed
- IBKR futures backtesting: cut downloader roundtrips by caching history windows across iterations and preferring native bar sizes (e.g., 15-min) when available.
4.4.34 - 2026-01-19
Added
- IBKR futures: add acceptance backtest strategy covering market/limit/stop/stop-limit/trailing/smart-limit and OCO/OTO/bracket semantics.
- IBKR futures: add parity/apitest helpers + scripts to compare IBKR runs against stored DataBento artifact baselines.
- Docs: add IBKR futures backtesting notes and DataBento parity guidance.
Changed
- IBKR futures backtesting: interpret
get_last_price(dt)as the last completed bar close (avoid lookahead bias). - Continuous futures (IBKR): stitch rolled segments with a 1-minute overlap and deterministic de-duplication.
- US futures gap handling (IBKR): replace flaky calendar logic with a simple rule-based “closed interval” detector to reduce repeated downloader fetches.
4.4.33 - 2026-01-12
Fixed
- SMART_LIMIT (live): avoid scanning full tracked order history in the background loop by using the broker’s active-order fast path, preventing high RSS growth in accounts with large historical order lists.
- Backtesting (router): make dataset lookup timestep-aware so minute requests don’t accidentally resolve to daily Data objects, and routed crypto assets passed as
(base, quote)work reliably. - Backtesting (router): refactor multi-provider routing to a provider registry + adapters (no hard-coded branching), add
alpaca/ccxtsupport, and allow CCXT exchange-id aliases likecoinbase/kraken(case/sep-insensitive). - IBKR (crypto): normalize daily timestep handling (
day/1d/1day) so crypto daily bars consistently use the derived-daily path. - ThetaData: prevent acceptance backtests from hitting the downloader queue by enforcing CI-only warm-cache guardrails consistently (local runs behave like GitHub CI).
- ThetaData: treat session close as “complete coverage” for index minute OHLC to avoid perpetual STALE→REFRESH loops when backtest end dates are represented as midnight.
- Backtest cache (S3): speed up warm-cache hydration by streaming small objects via
get_objectinstead ofdownload_filetransfer manager overhead.
4.4.32 - 2026-01-10
Added
- Runtime telemetry: lightweight memory/health JSON lines (
LUMIBOT_TELEMETRY ...) for diagnosing OOMs in long-running live workers. - Broker API smoke apitests: basic Alpaca and Tradier connectivity + order lifecycle checks (paper/live as available).
Fixed
- Live (Tradier): treat
submitted/open/newas equivalent to reduce repeated NEW events under polling; bound live trade-event history to avoid unbounded memory growth in long-running workers. - Live (Tradier): avoid heavy DataFrame copy chains when cleaning orders; skip ingesting large historical closed order lists on the first poll to prevent startup memory spikes in accounts with long histories.
4.4.31 - 2026-01-09
Deploy marker: d5c6b730 ("deploy 4.4.31")
Added
- SMART_LIMIT: live matrix apitests + runner scripts; expanded unit coverage for edge cases.
- Investigations/docs: production endpoint breakdown notes and an expanded backtesting performance playbook.
- ThetaData: per-asset download progress reporting for option-chain strike scans (exposed via
download_status).
Changed
- Acceptance backtests now run in CI (no longer marked
apitest); baselines were refreshed for LEAPS + MELI; CI caps were raised for long full-year strategies due to runner variability. - CI policy: use pytest markers (not env vars) for opt-in/slow tests; some slow ThetaData backtest tests were made opt-in, then re-enabled once bounded.
- Backtests under pytest no longer auto-open HTML artifacts (plots/indicators/tearsheets) in a browser.
- Strategy collaboration workflow: clarified “shared version branch” conventions.
Fixed
- ThetaData: reduced option-chain fanout and improved warm-cache parity (reuse chain cache under constraints; prefetch strikes only for head+tail expirations when unconstrained; bounded intraday chain defaults).
- ThetaData: improved intraday cache coverage and corrected daily option MTM behavior.
- Polygon: reduced split-cache rate limit thrash.
- SMART_LIMIT: hardened behavior for quote/stream failures.
- Backtesting progress: improved per-asset
download_statusfor clearer “what is downloading” diagnostics.
Removed
- ⚠️ Removed ThetaData chain default-horizon env vars (
THETADATA_CHAIN_DEFAULT_MAX_DAYS_OUT*). Chain default horizons are now fixed and covered by tests. - Removed the short-lived
LUMIBOT_DISABLE_UIenv var (useSHOW_PLOT/SHOW_INDICATORS/SHOW_TEARSHEET+ pytest non-interactive behavior instead).
4.4.30 - 2026-01-06
Version bump marker: 76b31467 ("Docs/tests: normalize artifacts + bump version")
Added
- Backtesting performance playbook and production/local parity notes.
LUMIBOT_DISABLE_DOTENVto disable recursive.envscanning in prod-like runs.
Fixed
- ThetaData: filtered intraday parquet loads to reduce memory footprint; daily option MTM fixes.
4.4.29 - 2026-01-06
Deploy marker: b8c6a839 ("deploy 4.4.29")
Fixed
- Prevent production backtests from OOM-like hard exits (
ERROR_CODE_CRASH) when refreshing multi-year intraday ThetaData caches by avoiding deep copies during cache load/write and trimming non-option intraday frames in-memory.
4.4.28 - 2026-01-05
Added
- Production backtest runner script (
scripts/run_backtest_prod.py) plus investigation docs for NVDA/SPX accuracy, parity, and startup latency.
Fixed
- ThetaData missing-day detection for intraday caches across UTC midnights (prevents “every other trading day missing” forward-fill storms).
- Backtesting: improved intraday fills and cache end handling; deterministic drift ordering for rebalances.
4.4.27 - 2026-01-05
Fixed
- Reduced peak memory usage for ThetaData backtests and tear sheet generation to avoid OOM crashes in production.
4.4.26 - 2026-01-05
Changed
- ThetaData: cache snapshot quotes per session and fetch full-session option quote snapshots to reduce downloader fanout.
Fixed
- Clamp future backtest end dates instead of failing.
4.4.25 - 2026-01-04
Deploy marker: b7f83088 ("Deploy 4.4.25")
Added
- Public documentation page for environment variables (
docsrc/environment_variables.rst) plus engineering notes (docs/ENV_VARS.md). - Backtest audit telemetry can be preserved in a separate
*_trade_events.csvartifact (seeLUMIBOT_BACKTEST_AUDIT). - Investigation docs for ThetaData corporate actions and performance.
Changed
- ThetaData option chain defaults are now bounded to reduce cold-cache request fanout (configurable via
THETADATA_CHAIN_DEFAULT_MAX_DAYS_OUT*).
Fixed
- OptionsHelper delta-to-strike selection fast path to prevent per-strike quote storms (SPX Copy2/Copy3 slowness).
- Prevent backtest tear sheet generation from crashing on degenerate/flat returns (NVDA end-of-run failures).
- Reduce ThetaData corporate action request thrash via memoization/negative caching.
- Normalize ThetaData intraday bars for corporate actions in backtests so option strikes and underlying prices stay in the same split-adjusted space (NVDA split issues).
- Improve ThetaData snapshot quote selection near the session open to avoid missing NBBO due to end-of-minute timestamps.
4.3.6 - 2024-11-16
- Fixed ThetaData EOD corrections by fetching a real 09:30–09:31 minute window for each trading day, preventing zero-length requests and the resulting terminal hangs.
- Logged the active downloader base URL whenever remote mode is enabled to make it obvious in backtest logs which data path is being used.
- Added regression tests covering the custom session window override plus the fallback path when Theta rejects an invalid minute range.