A focused maintenance release. Its most urgent fix: a fresh install of the 2.2.0 MCP server crashed on startup, because the mcp dependency had no upper bound and mcp 2.x removed a module the server imports. The local install command on the website therefore failed for everyone. This release caps mcp below 2.0 and fixes two MCP data bugs on top: the rates tool returned no Treasury yields at all, and a benchmark that was also a requested ticker was silently swapped for a different index.
The second theme is daily-first analysis. Risk and Econometrics now default to daily calculations over the whole series instead of quarterly/yearly per-period values, which removes the intraday-data requirement that made period="daily" unusable in Econometrics. Some results change for existing code as a result; see Changed Defaults below.
Underneath, data collection is faster and better behaved. A bounded worker pool replaces one thread per ticker, HTTP connections are reused, and retries back off with jitter. The package now ships type information (py.typed), and the type checker runs in CI.
Every method below hangs off the matching Toolkit sub-module (e.g. toolkit.risk.get_value_at_risk()) or its standalone class (Economics(...), FixedIncome(...), Discovery(...)). Full runnable examples live in the documentation, which now has a dedicated page for every method.
Changed Defaults
- Econometrics defaults to
period="daily"across all methods (previously quarterly or yearly, depending on the Toolkit).within_periodnow defaults toFalseon the five diagnostic tests that take it (ARCH-LM, Jarque-Bera, Ljung-Box, CUSUM, Variance Ratio). Every Econometrics method previously rejectedperiod="daily"outright, even though only the within-period nesting needs intraday data. Intraday data is now only required forperiod="daily"combined withwithin_period=True, and the error message says how to avoid it. Full examples: docs/econometrics. - Risk defaults to
period="daily"andwithin_period=Falseas well, so metrics such as Value at Risk, CVaR and the GARCH family are calculated over the whole daily series by default. Per-period aggregates (variance, volatility, excess volatility, MAD, coefficient of variation) keep their quarterly/yearly default, since a single daily return has no spread to measure. Full examples: docs/risk. - If your code relied on the old defaults, pass
period="quarterly"/"yearly"(andwithin_period=True) explicitly to get the previous behaviour.
Performance
- Bounded worker pool. Every data collection function used to start one bare thread per ticker, with a 0.1-second stagger between them. A 100-ticker universe spent 10 seconds just starting threads, exceptions died silently inside their thread, and the number of concurrent API calls grew with the universe. All 13 collection sites now share one
ThreadPoolExecutorwith 10 workers by default (configurable viaFINANCETOOLKIT_MAX_WORKERS) that re-raises worker exceptions. - Connection reuse. Every request goes through one shared
requestssession with enlarged connection pools, so repeated calls to the same API reuse TCP connections and TLS handshakes instead of paying for a new handshake each time. - Retries back off with jitter, so ten workers hitting a rate limit don't all retry at the same moment.
- Treasury rate windows are fetched in parallel through the same pool, instead of one by one with sleeps in between.
- Memoized slices. Risk (
_get_returns), Econometrics and Performance cache the return and column slices their methods start from, so collecting many metrics, as the MCP server does in one session, no longer recomputes them for every metric.
Bug Fixes
Risk & Econometrics
- Whole-series Risk calculations on resampled periods returned
NaN, because the first resampled return is alwaysNaN. That row is now dropped before the calculation. get_arch_lm_test(within_period=False)produced no values at all under the old yearly default: four yearly observations can't support a 5-lag ARCH test. With the daily default it now returns real results.- Fresh installs now resolve statsmodels 0.15, where the ADF test (
get_augmented_dickey_fuller) and the ARCH-LM test (get_arch_lm_test) printed aFutureWarningabout an upcoming change to their return format. The toolkit is unaffected by that change until statsmodels 0.16, so these two notices are now silenced. Results are identical on statsmodels 0.14 and 0.15.
API key handling
- Passing the documentation placeholder
api_key="FINANCIAL_MODELING_PREP_KEY"used to reach FinancialModelingPrep and come back as a generic "invalid API key", without saying that the example value was never replaced. It's now reported as a placeholder, with a link to get a key, and treated as no key at all (e.g. the Toolkit falls back to Yahoo Finance). This applies toToolkit,Portfolio,Discovery,FixedIncomeand the MCP server.
MCP Server
- Fresh installs crashed on startup.
mcp2.x removedmcp.server.fastmcp, which the server imports, and themcpextra had no upper bound, sopip install financetoolkit[mcp]resolvedmcp2.x and failed immediately. The extra now requiresmcp[cli]>=1.27.0,<2. rates→get_treasury_ratesreturned no data. The server builtFixedIncomewithout the FMP API key, which the Treasury par yield curve needs, and the error was swallowed into "No data available". The key is now passed on, and both the FMP and FRED keys are part of the instance cache key, so instances are never shared between keys.- The benchmark could silently be a different security. When a requested ticker was also the benchmark (e.g.
tickers="AAPL,SPY"withbenchmark_ticker="SPY"), the server replaced the benchmark with the first unused fallback. "Benchmark" was then QQQ (739.77 instead of SPY's 762.63 on 2026-09-30), so every beta, alpha and excess return was measured against the wrong index. The requested benchmark is now kept, matching the library; a fallback is only used when the benchmark is the sole ticker. Either way, the response includes a note explaining where the benchmark's data is. - The startup log reported "Registered router group '…' → True tool(s)" instead of a count; it now names the registered group.
- Daily
periodis no longer blocked for therisktool, matching the new default. - Per-call timing is logged at debug level, so a slow tool can be diagnosed from the logs alone.
Documentation
- Every method has its own documentation page. The 76 README links that pointed to an anchor on a module page (
docs/ratios#collect_profitability_ratios) now point to that method's dedicated page (docs/ratios/all-profitability-ratios). - 21 Econometrics methods, including
get_vif,get_ols, the heteroskedasticity and specification tests, the nested-model tests, Fama-MacBeth, synthetic control and the VAR forecast, previously had a single sentence plus a pointer to an internal function the website doesn't show. Each now explains what it computes, the formula, how to read the result for the tickers involved, and the original reference. - 31 Econometrics docstrings still said
periodfollowed the Toolkit's quarterly/yearly setting; they now document the actual"daily"default. FixedIncome.get_government_bond_yieldopened with the 10-year description even whenshort_term=Truereturns the 3-month rate. Both maturities are now described, including that the OECD short-term figure is the 3-month interbank rate rather than a Treasury bill yield.get_dual_gammareused the gamma description; it now describes the second derivative with respect to the strike price.- The dead Global Macro Database link in
get_gross_domestic_productnow points to the GMD documentation page.
Typing
- The package now ships
py.typed, so downstream type checkers see its annotations. uv check(Astral'sty) runs in pre-commit and in the linting workflow. Five rules are back at their default severity (call-non-callable,not-iterable,not-subscriptable,unsupported-operator,unknown-argument), with the code they flagged fixed. 29 staletype: ignorecomments are gone. The rules that only reflect pandas' very wideSeries | DataFramereturn types stay disabled.
Test Coverage & CI
- Toolkit parameter validation (dates, risk-free rate, source, intraday period, tickers) now lives in its own module,
validation_model, so bad input can be tested without building a Toolkit and fetching data. - CI runs the suite in parallel (
pytest-xdist) with the uv cache. The Economics tests skip cleanly when offline or without a FRED key, instead of erroring. - The recorder gains
--rewrite-failing, which regenerates only the stored outputs that fail comparison, so a regeneration can't churn unrelated files through float noise. - New tests cover the MCP provider (the treasury key and the benchmark handling) and the placeholder API key.
- A version-alignment test makes sure
pyproject.toml,server.json, the MCPB manifest and the MCPB bundle's ownpyproject.tomlall carry the same version. - A Python 3.15 job runs in CI and is skipped until the lock file contains SciPy wheels for 3.15, rather than compiling SciPy from source on the runner.
Dependencies
mcp[cli]>=1.27.0,<2in themcpextra (see MCP Server above).- Lockfile updates: cryptography 50.0.0, httpx2/httpcore2 2.12.0, jupyterlab 4.6.4, notebook 7.6.3, pyjwt 2.15.0, tornado 6.5.9, urllib3 2.8.0.
Full comparison: v2.2.0...v2.2.1