github JerBouma/FinanceToolkit v2.2.1
FinanceToolkit v2.2.1

4 hours ago

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_period now defaults to False on the five diagnostic tests that take it (ARCH-LM, Jarque-Bera, Ljung-Box, CUSUM, Variance Ratio). Every Econometrics method previously rejected period="daily" outright, even though only the within-period nesting needs intraday data. Intraday data is now only required for period="daily" combined with within_period=True, and the error message says how to avoid it. Full examples: docs/econometrics.
  • Risk defaults to period="daily" and within_period=False as 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" (and within_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 ThreadPoolExecutor with 10 workers by default (configurable via FINANCETOOLKIT_MAX_WORKERS) that re-raises worker exceptions.
  • Connection reuse. Every request goes through one shared requests session 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 always NaN. 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 a FutureWarning about 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 to Toolkit, Portfolio, Discovery, FixedIncome and the MCP server.

MCP Server

  • Fresh installs crashed on startup. mcp 2.x removed mcp.server.fastmcp, which the server imports, and the mcp extra had no upper bound, so pip install financetoolkit[mcp] resolved mcp 2.x and failed immediately. The extra now requires mcp[cli]>=1.27.0,<2.
  • rates → get_treasury_rates returned no data. The server built FixedIncome without 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" with benchmark_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 period is no longer blocked for the risk tool, 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 period followed the Toolkit's quarterly/yearly setting; they now document the actual "daily" default.
  • FixedIncome.get_government_bond_yield opened with the 10-year description even when short_term=True returns 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_gamma reused 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_product now points to the GMD documentation page.

Typing

  • The package now ships py.typed, so downstream type checkers see its annotations.
  • uv check (Astral's ty) 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 stale type: ignore comments are gone. The rules that only reflect pandas' very wide Series | DataFrame return 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 own pyproject.toml all 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,<2 in the mcp extra (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

Don't miss a new FinanceToolkit release

NewReleases is sending notifications on new releases.