Repository navigation
Releases
FinanceToolkit v2.3.3
A release on correctness, robustness and security, with bonds as a new asset class. Each ticker is now measured on its own trading days and calculated on unrounded data, so the Python package and the MCP server give the same figures. Controller methods work without arguments, bond calculators work on real bonds and market curves, the portfolio optimizers gain new objectives, constraints and a walk-forward backtest, and the MCP server, the cache, data retrieval and the release pipeline are hardened.
Package
Defaults from the data
- Controller methods work without arguments: the valuation models default to each company's cost of equity (CAPM with its country's equity risk premium) or WACC, the dividend CAGR or the reinvestment growth, with the stable growth kept within 0% and 2.5%, all measured without look-ahead. Rates also take a list or a dict per ticker (#359).
- With
trailingset, the beta of the WACC and residual income covers the trailing periods, so quarterly defaults agree with annual ones (#359). - Defaults for
get_tail_dependence_coefficientandget_covar(against the benchmark),get_style_analysis(Sharpe's asset classes as ETFs),get_commodity_forward_curve(all eight commodities),get_return_excluding(the largest holding),get_strategy_payoff(an at-the-money straddle) and the Discovery news searches (#359).
Bonds and rates
get_treasury_securitieslists every marketable US Treasury with price, yield and duration from FedInvest, without an API key.get_bond_universelists about 9,000 US corporate bonds from bond ETF holdings, with the G-Spread over the Treasury par curve (#356).- The bond calculators (
collect_bond_statistics,get_present_value,get_duration,get_yield_to_maturity,get_z_spread,get_key_rate_duration,get_taylor_price_change) run on real bonds: by default the benchmark Treasuries, or bonds chosen withbonds=,issuer=,etfs=and a maturity range. The rate calculators read a country's government bond curves on a day (#358). get_implied_default_probabilitytakesratings=and uses the latest ICE BofA spread per rating (#358).- Removed:
get_option_adjusted_spread,get_derivative_priceand the parameters for typing in a bond's terms by hand (#358).
Economic data without a FRED key
- Initial jobless claims, retail sales, housing starts, real personal income, nonfarm payrolls and the HQM corporate bond curve and spread come straight from the Department of Labor, Census Bureau, BEA, BLS and Treasury when no FRED key is set, identical to FRED over their full history (#357).
Portfolio optimization
get_maximum_diversification_weights,get_minimum_tracking_error_weights(withobjective="information_ratio") andget_growth_optimal_weights(Kelly, withfraction=), andcovariance="denoised"(López de Prado) in every optimizer and inget_risk_contributions, (#362).- Per-asset weight bounds (one number or a dict per ticker),
sector_bounds=andcountry_bounds=from the company profiles, andnet_exposureandgross_exposurefor long-short portfolios in every optimizer; risk parity takes weight, sector and country bounds (constrained risk budgeting) and HRP weight bounds. Infeasible constraints are refused with the reason (#378). get_hierarchical_equal_risk_contribution_weights(HERC, Raffinot),get_nested_clustered_optimization_weights(NCO, López de Prado),get_minimum_drawdown_weights(CDaR, maximum or average drawdown),get_maximum_omega_weightsandget_maximum_cvar_ratio_weights(#385, merged through #386).get_optimization_backtest: a walk-forward backtest that re-optimizes on each monthly, quarterly or yearly date from the trailing returns, without look-ahead, against equal weights and the benchmark, with turnover and transaction costs (#386, #391).current_weights=withmaximum_turnover=andtransaction_cost=in the optimizers, andexpected_returns=('mean', 'exponential', 'capm' or Jorion's 'james_stein') (#386, #391).get_resampled_efficient_weights(Michaud resampling),get_diversification_metrics(concentration, effective number of assets and of bets, diversification ratio) andget_risk_contributions(risk_measure="cvar")for the contributions to expected shortfall. Every CVaR uses the exact Rockafellar-Uryasev tail (#386, #391).covariance=gains the Ledoit-Wolf constant-correlation and single-factor targets,codependence=('pearson', 'spearman' or 'kendall') sets the correlation behind HRP, HERC and NCO,get_mean_variance_weightstakestarget_volatility=for the highest expected return at a volatility, and Black-Litterman takesrisk_aversion="market"(implied by the benchmark) and reports the posterior volatility (#394).get_discrete_allocationturns weights into whole shares for a portfolio value (integer programme or greedy),get_portfolio_risk_metricsgives the return, volatility, Sharpe, Sortino, maximum drawdown, VaR and CVaR of given weights next to the benchmark, andget_risk_contributionsadds semideviation, mean absolute deviation and CDaR (#393).- The portfolio docstrings describe every optimizer by its paper and formula (#392).
- Removed:
optimization_model.get_black_litterman_returnsreturning only the returns; it now returns the returns and the posterior covariance (#394).
Ratios
- Gross profitability (Novy-Marx), operating profitability (Fama-French), cash-based operating profitability, cash return on invested capital, free cash flow conversion, free cash flow to EBITDA, capex intensity and capex to depreciation (#361).
- EBITDA, EBITDA minus capex and cash interest coverage, FFO to debt and discretionary cash flow to debt (S&P, Moody's), the degrees of operating, financial and total leverage, the free cash flow payout ratio, EV to invested capital and price to tangible book (#373).
- The investment and issuance anomalies: asset growth (Cooper, Gulen and Schill), net operating assets (Hirshleifer et al.), percent accruals (Hafzalla, Lundholm and Van Winkle), net share issuance (Pontiff and Woodgate), composite equity issuance (Daniel and Titman) and net external financing (Bradshaw, Richardson and Sloan) (#380).
Models
get_altman_z_score(model=...): the original Z, Z' (private firms), Z'' (non-manufacturers) and Z''-EM (emerging markets), each with Altman's own coefficients and zones, andget_taffler_z_score, the UK model of Taffler (1983) (#369).get_campbell_hilscher_szilagyi_score: the failure probability of Campbell, Hilscher and Szilagyi (2008), with its components (#390).- On quarterly data the distress scores (Altman, Taffler, Ohlson, Zmijewski, Springate, Grover, Fulmer, Piotroski, Beneish) cover a trailing year and compare with the same quarter a year earlier (#387).
- The valuation models warn when the required return is less than two percentage points above the growth kept forever (#387).
- Removed: the model function parameter
market_value_of_equity_to_book_value_of_total_liabilities_ratioofaltman_model.get_altman_z_score, nowequity_to_total_liabilities_ratio(#369).
Risk and performance
- Each ticker uses its own trading days, and measures against the benchmark pair returns only on dates both traded (#353).
- The drawdown table gains calendar-day and year durations, and
get_drawdown_seriesadeepest_since=date (#354). - Calculations run on unrounded prices, returns and Treasury yields; only the returned tables are rounded (#367).
- Lo's autocorrelation-adjusted Sharpe ratio (
method="lo", with Moran's bias correction and at least 30 returns),get_sharpe_ratio_standard_error(iid, non-normal and GMM),get_minimum_track_record_length, andget_coskewnessandget_cokurtosiswith the benchmark (#379). - Drawdown and tail ratios no longer drop a period for every ticker when one ticker has no value; a zero denominator gives NaN instead of ±inf; returns that do not vary have no skewness or kurtosis; and a bad OHLC bar only leaves out that day in the range-based volatility estimators (#364).
Robustness
- Supplied
historical=data is checked and normalised (dates of any type, sorted, duplicates removed, returns calculated when missing), and supplied statements without a ticker level or with repeated line items are refused with a clear message (#371). - All Options methods carry
@handle_errors; a ticker without a price on the pricing date is left out instead of failing the others, and astart_dateon a non-trading day uses the last trading day before it (#371). - The package no longer silences RuntimeWarnings for the whole process (#371).
- Symbols are percent-encoded in every FMP request, so
M&M.NSis no longer read as Macy's (M); FMP errors sent with status 200 are classified, and one failing ticker no longer discards the results of the others (#363).
Analyst estimates
- The latest price target's date and the days since it (#355).
- The number of analysts behind the revenue and the EPS estimates as separate rows, and
min_analysts=on the forward P/E and PEG to leave out thinly covered years. Estimate years follow the same calendar labelling as the statements, so for fiscal years ending January to May they move one year back and the forward PEG compares the right years. Removed: the averaged "Number of Analysts" row (#381). - The market-wide earnings calendar returns every release of a range instead of the latest 4,000, with each company's name, exchange and market cap in USD (from its primary listing), and
min_market_cap,country,exchange,primary_onlyandsort_by(#381).
Commodities
get_commodity_term_structure: the front and far contracts, spread, slope, annualised roll yield and backwardation or contango per date, on the curve as listed each day or on fixed contracts (#382).get_commodity_forward_curvetakesgeneric=Truefor the front month and the next eleven as listed each day, andgrowth/lag; its end date is no...
FinanceToolkit v2.3.2
A large release with new analytics throughout the package: factor analysis (predictive power) in every module, return decomposition, earnings surprises, ETF contributions, alternative betas, Morningstar's risk-adjusted return, drawdown tables, candlestick patterns and indicator summaries. New data comes from analyst targets, 13F ownership, FINRA short volume, historical index constituents, the NY Fed, the BIS and Damodaran. The MCP setup wizard now writes to the right places for Claude Desktop on Linux, Devin Desktop and VS Code.
Package
New analytics
- Factor analysis (predictive power):
get_factor_analysisin ratios, technicals, models, risk and performance tests any metric, a list of them or a collection (e.g.ratios="valuation") as a cross-sectional signal: information coefficient (mean, IR, t-stat), quantile returns, top-minus-bottom spread, turnover and rank autocorrelation, without look-ahead.performance.get_factor_analysisalso takes your own signal as a DataFrame (#335). - Returns explained:
models.get_return_decompositionsplits a price return into EPS growth × P/E change, plus dividends (#320).performance.get_beta_implied_returngives the beta-implied return, the actual return and the residual per period (#344).risk.get_move_significanceshows how unusual a move is: volatility multiple, percentile and "largest since" date (#324). - Earnings:
get_earnings_surprisegives EPS and revenue surprises, SUE, and the share's reaction against the benchmark per announcement (#321). - ETFs:
etf.get_contributiongives each holding's or sector's contribution to the return (also in index points), andetf.get_return_excludingthe return without chosen holdings or sectors (#322).etf.get_active_share(#339). - Performance: alternative betas via
get_beta(method=...): Blume, Vasicek, Dimson, Scholes-Williams, upside and downside (#325).get_kelly_criterionandget_return_statistics(profit factor, payoff ratio, expectancy, streaks, recovery factor) (#336). Morningstar Risk-Adjusted Return and Morningstar Risk, checked against Morningstar's April 2023 methodology (#339). - Risk:
get_drawdown_seriesandget_drawdown_table(#323), with the gain needed to recover:get_drawdown_series(recovery=True)and a Recovery Needed column (#350).get_move_significancealso gives the VaR Multiple, a move divided by its trailing VaR (#350). - Portfolio:
covariance=on every optimiser: sample, Ledoit-Wolf, OAS, semicovariance or exponential. Matches scikit-learn and PyPortfolioOpt (#326). - Models: H-model, three-stage DDM (
transition_periods=), justified P/E and P/B, andget_intrinsic_valuation(historical=True)(#341).get_quality_score, a Quality Minus Junk composite (#343). - Growth:
compound=Truewithgrowth=True, lag=ngives the n-period CAGR of statements, estimates and every ratio (#329). - Technicals:
get_candlestick_patterns, 15 patterns identical to TA-Lib, andget_signals(crossovers, RSI zones, band breaks, new highs/lows) (#338).get_indicator_summarygives an indicator's latest value, its percentile and rank in its own history, its extremes with dates, threshold crossings and the average returns after them (#348).
New data
- Analyst price targets, target history and monthly recommendations (#328). Institutional ownership from 13F filings (#333). FINRA daily short sale volume (#337).
- Index constituents on a past date, plus the dated changes (#330).
- NY Fed ACM Treasury term premium and Global Supply Chain Pressure Index (#331). BIS credit-to-GDP gap, debt service ratios and credit to GDP by sector (#332). Damodaran industry averages (#334).
Fixes
- A ticker without data no longer adds calendar days to the other tickers (#318).
- Yahoo Finance prices are taken unrepaired when yfinance's
repairextra (scikit-learn) is missing, instead of failing (#327). - A daily bar fetched before its session closed is asked for again after the close (#345).
- Technical indicators are calculated on a year of prices before the start date, so smoothed indicators (RSI, ATR, ADX, MACD, moving averages) no longer depend on it (#349).
- The daily P/E, P/B, earnings yield and dividend yield carry the latest reported figures to the latest close instead of dropping periods before the start date (#349).
get_factor_analysisleaves out a metric that fewer than three tickers have (e.g. the preferred dividend coverage ratio inratios.get_factor_analysis(metrics="all")), with a warning, instead of failing the whole analysis.
MCP server
limitselects rows per ticker in multi-ticker answers (#342).- A requested ticker that is also the benchmark is returned by methods without a benchmark, such as quotes (#346).
benchmark_ticker"None", "null" or "" means no benchmark (#318).- All new methods are available on their tools; there are still 25 router tools.
- A note says when the last daily row comes from an unclosed session (#345).
- Setup wizard (
financetoolkit-mcp-setup):- Claude Desktop's Linux path is
~/.config/Claude. - The Windsurf client is replaced by "Devin Desktop (Windsurf)" (
devin): it writes~/.config/devin/mcp_config.json, or the old Windsurf file where it still exists. - VS Code entries carry
"type": "stdio". - A new
copilotclient writes the global~/.copilot/mcp-config.json.
- Claude Desktop's Linux path is
server.jsonis published to the MCP Registry on every release (#319), andfinancetoolkit.mcpbis built and attached to every release by the release workflow (#351).
Removed
- The
windsurfchoice offinancetoolkit-mcp-setup --client; usedevin.
The version is 2.3.2 in pyproject.toml, server.json, the bundle manifest, mcp_server/mcpb/pyproject.toml (including the financetoolkit[mcp]== pin) and uv.lock.
FinanceToolkit v2.3.1
Some minor new functionalities that resolve edge cases and potential security flaws. Also introduced point-in-time mode as suggested by #298.
Package
New
- Point-in-time mode (
Toolkit(..., point_in_time=True), #301, closes #298).- Statements, ratios and models use each period's figures only from the day they were filed (FMP's accepted and filing dates, otherwise the reporting deadline).
end_dateworks as an "as of" date.- The statistics statement now holds the Filing Date and Fiscal Year.
- Country equity risk premium in the Cost of Equity (#307).
- CAPM, WACC, EVA, residual income and PVGO use the Damodaran premium of each company's country instead of the benchmark's historical excess return.
- An explicit
market_risk_premiumstill wins. - AAPL's 2024 WACC goes from 0.1947 to 0.0823.
- Portfolio performance on the MCP server. The new tool
portfolio_performancetakes a portfolio as transactions, holdings, weights or a preset. It returns the overview, performance, positions, transactions and the money-weighted return.
Improved
- Toolkit core (#300, #305):
- Quarterly data looks back far enough for the trailing year and its growth in the first periods.
- Supplied statements are never downloaded again and detect their own date order.
- Statement periods after
end_dateare left out. - Exchange rates older than a month aren't used.
- Inputs are validated (tickers, benchmark, lag, dates).
- Data layer (#304):
- The price cache refetches when a dividend or split rescales the adjusted history.
- Prices with a failed dividend or adjusted-close request aren't cached.
- API keys are removed from logged errors.
- Discovery queries are URL-encoded ("AT&T", "Aerospace & Defense").
- Certificate errors fail at once instead of retrying for minutes.
- On the Free plan, dividends before the latest five come from Yahoo Finance.
- Portfolio (#311):
- Transactions in several currencies are converted to one reporting currency at each day's rate; pence and pounds are unified.
- Purchases come before sales within a day.
- Decimal commas are read per column.
- The stored data is not rounded.
- Utilities and cache (#311):
- Growth is NaN around a gap.
standardizecovers the shown window.- Cache entries are evicted daily, and the coverage of a cached range never extends past today.
- Heavy calculations are bounded (#306): option, bond, Monte Carlo and binomial models cap their sizes.
- Edge-case fixes:
- Speed (#310): the rolling CVaR, CDaR and Omega, the grouped Ulcer Index and period resampling are vectorised.
- Clearer messages (#308): a missing key outside the statements says which data is missing, and inflation expectations name the countries they cover.
- Fixes in this PR:
- Discovery:
- The screener sends the upper market cap bound.
- UK home listings are recognised (SHEL.L instead of the NYSE ADR).
- Performance: the whole-period Omega, Gain-to-Pain and Rachev ratios are no longer empty.
- Company data:
- Annual ESG scores take the year's latest disclosure.
- Missing revenue segments are NaN, and names keep FMP's spelling ("iPhone").
- A changed
actual_datesapplies to a cached earnings calendar. - Statements and analyst estimates are stored unrounded, so the rounding of a first call no longer sticks.
- The statistics statement follows
start_dateandend_date.
- Docstrings: the WLS and GLS examples use the estimated weights and the AR(1) Omega.
- Discovery:
- Docs and packaging (#302): docstring defaults match the code, and the package metadata has project links.
Breaking
- The
reverse_datesandsmooth_widowparameters are removed (#305, #302). - ROC, PPO, TRIX and the KST are decimals instead of percentages (#309).
- Economics country names follow the Global Macro Database: Macedonia, Macau, Cape Verde, Sao Tome and Principe. The official names are accepted too (#309).
- The rolling Omega is
inffor a window with gains and no losses, likeget_omega_ratio(#306, #310).
MCP server
- Tools:
- New tool
portfolio_performance. Its inputs are validated before anything is fetched (one way to describe the portfolio, at most 1,000 transactions and 50 tickers), and transactions never reach the server as a file path. - 25 router tools.
- New tool
- OAuth hardening (#303):
- Single-use authorization codes that expire after 5 minutes.
- Validated redirect URIs.
- PKCE S256 is required.
- Field length caps.
- An invalid token gets a 401.
- Request handling (#303):
- Tools run in a bounded thread pool with a lock per cached Toolkit.
- A call waiting for a busy Toolkit gives up its slot.
- Default dates are computed per call.
- Ticker, search and series counts are capped.
- Credentials are redacted from the access log.
- The secret file is created private.
- Inputs (#295): fractional, JSON and estimated inputs are accepted.
Other
- Tests:
- The flaky CI tests are fixed. 16 portfolio and point-in-time tests built Toolkits from supplied prices but without Treasury data, so valuing them retrieved Treasury yields from Yahoo Finance in parallel threads, which locked yfinance's SQLite cache ("database is locked"). Every such Toolkit now gets the supplied Treasury yields; with yfinance blocked, no test reaches it any more. Another test re-read the shared Portfolio's dataset and sent later tests online; it now uses its own Portfolio, and each test process gets its own yfinance cache.
- 20 technical indicator tests that only recorded NaN now run on 60 sample bars and assert values.
- New offline tests for the company data parsers.
- Weekly source checks: a source that returns nothing is reported with what it logged, and a refusal over a request limit is skipped. The BLS allows 25 keyless requests a day per IP address, which GitHub's runners share (#315).
- Dependencies:
- Dead code: the dead "pip install yfinance" branches are removed (yfinance is a required dependency).
FinanceToolkit v2.3.0
New release with some new modules, lots of verification checks and overall improvements. See the complete list below.
Library
New
toolkit.portfolio: optimal weights (minimum variance, maximum Sharpe, mean-variance, risk parity, HRP, inverse volatility, minimum CVaR, Black-Litterman), efficient frontier, risk contributions, stress tests, look-through exposure and Brinson-Fachler attribution.Portfoliotakes weights, holdings or transactions and ships 13 presets (#281).toolkit.etf: holdings, weightings, profile with premium/discount to NAV (stale_nav=Truefor a NAV that is not current), tracking, concentration, overlap, look-through valuation and liquidity (#288).- Models: peer comparison, Merton distance to default, synthetic credit rating, reverse DCF, Monte Carlo valuation and more (#282).
- Options: put/call ratio, max pain, gamma exposure, implied move, model-free implied volatility and the volatility risk premium (#283).
- Fixed income: zero-coupon curves, Nelson-Siegel-Svensson, yield curve PCA, Vasicek/CIR/Hull-White, callable bond OAS, implied default probabilities and Treasury auctions (#284).
- Econometrics on any series (
Econometrics(data=...)), Markov regime switching, structural breaks, time-varying beta, DCC-GARCH and pairs trading (#285). - Performance and technicals: style analysis, regional Fama-French factors, market breadth, seasonality, relative strength and index constituents (#287).
Improved
- Modules load statements and prices only when a calculation needs them (#290).
- Every calculating method documents its formula with the defaults it uses, and every docstring example is regenerated live (#286).
- Correctness, verified against the sources (#286): decimals everywhere, the traded close for market values, no look-ahead (figures apply from the fiscal period end), each ticker keeps its own history, quarterly ratios over a trailing year, continuous rates for options, the
archbackcast for GARCH, and many method-level fixes. show_columnsselects numeric column labels such as years, thanks to @jackiectl2 (#292).
Breaking
Toolkit.get_etf_holdings,get_etf_information,get_etf_sector_weightingsandget_etf_country_weightingsmoved totoolkit.etf(get_holdingsand so on).- ICE BofA option-adjusted spreads and the discovery quote changes are decimals.
- A Toolkit whose only ticker is the benchmark raises a clear error; pass
benchmark_ticker=None.
MCP server
- New
portfolioandetftools: 24 router tools. - Econometrics takes
series=to analyse any indicator. - Descriptions follow the new methods, units and defaults; the bundle manifest is synced.
Other
- A weekly GitHub Actions run checks every keyless data source and opens an issue per broken source (#289).
FinanceToolkit v2.2.3
Mostly work for the MCP server to provide more reliable answers.
MCP server
- Answers stay readable: anything over 60,000 characters is cut to the most recent rows, with a note on how to narrow it.
limitworks as head/tail (5first,-5last) on every tool. Large outputs (simulations, binomial tree, ETF holdings) have compact defaults. - Better discovery:
search_metricsreturns the exact indicator and its tool and understands analyst wording ("how expensive is the stock"). The server sends usage instructions on connect. Every parameter has a real description, from the docstrings. - Notes explain the answer: tickers without data, ignored arguments, yearly-only data, sample yield curves, assumed bond inputs, where a series ends, fiscal-year mapping and currency conversion.
- Inputs:
show_columnsmatches any casing and countries accept short forms (USA, UK). Weights, yield curves, option strategy legs and lists can be passed. Transcripts come back in full per company and fiscal period. Option chains say which expiry they show. - Hosted server: at most 4 Economics and 64 Toolkit instances are kept, and one Global Macro Database copy is shared per process (memory stays flat across date ranges).
- Tools:
search_instrumentsis removed (Finance Database covers it),search_categoriesis renamedsearch_metric_categories, and EGARCH is no longer offered (~45s per fit).
Package
- Stale FMP data fixed: Commitment of Traders, sector and industry history and 8-K filings now request a date range.
- Stock screener: market caps are in USD,
primary_onlykeeps the home listing, and it returns stocks only unlessis_etf=True. - Symbols: ISIN/CUSIP/CIK search uses its own parameters, and currency and crypto pairs are accepted in either notation (EURUSD or EURUSD=X).
- Economics: quarterly and monthly rates use sources with that frequency, calendar event matching ignores spaces and hyphens, and Current Policies climate scenarios work.
- Fixes and additions:
- Daily GARCH forecasts are dated by business day.
- Gainers and losers are sorted by change and ETF weights largest first.
- Gross margin is empty where cost of goods sold is zero.
- The Sharpe ratio takes
annualize. - Option chains take
strike_price_range.
FinanceToolkit v2.2.2
This release adds new FMP endpoints and recent economic and market data from official sources that need no API key. It also adds scenario and credit inputs, caches external data by default, speeds up many calculations, and fixes a range of calculations and data issues.
New data
- FinancialModelingPrep:
- Toolkit: executives and compensation, company notes, employee count, shares float, M&A, stock splits, insider statistics, stock grades, ETF holdings, information and weightings, and earnings call transcripts. Stock news now covers crypto and forex.
- Discovery: market-wide earnings calendar, 8-K filings and latest insider trades.
- Economics: economic calendar and market risk premium (optional key).
- No API key needed:
- Monthly inflation, CPI and unemployment; quarterly GDP growth.
- Daily policy, overnight and long-term rates.
- Government yield curves for nine economies, plus every EU member's 10-year yield.
- FRED no longer required for the mortgage rate, industrial production, the recession indicator, real yields and breakevens. FRED is still used when a key is set.
- Credit:
- The Treasury's HQM corporate curve and spreads, and Moody's Aaa/Baa yields.
- The excess bond premium and corporate bond yields by country.
- Rating transition matrices and default rates from ESMA's CEREP database.
- ECB borrowing costs and stress index.
- Insurance and long horizons:
- EIOPA risk-free curves, incl. shocks and the symmetric adjustment.
- Eurostat life tables and Shiller's US stock market data.
- Long-run asset returns 1870–2020 (opt-in, non-commercial licence).
- The Bank of England's UK series from 1086.
- Scenario inputs:
- Real and breakeven inflation curves for the US, UK and Germany, and survey inflation expectations.
- Implied volatility indices (VIX, VSTOXX, MOVE).
- Fed and ESRB stress-test scenarios and DNB pension scenario sets.
- NGFS climate scenarios and the EU carbon price.
- Listed proxies for private markets.
- Global Macro Database: uses the latest quarterly release. IMF projections are excluded by default.
Caching
- On by default: data from external sources is cached in the Toolkit, Economics, Fixed Income, Discovery and Portfolio modules. Each dataset is refreshed on its own schedule, and calculated metrics are never cached.
- Empty answers: an answer that a source has no data is cached too; a failed request is not.
- FMP first: with an FMP key, FMP data takes priority. Yahoo Finance is used when it has the longer history, for example on the Free plan.
Performance
| Before | After | |
|---|---|---|
| EGARCH / GARCH | 186 s / 7.9 s | 13 s / 1.2 s |
| Monte Carlo option prices | 66 s | 2.5 s |
| All option greeks | 5.4 s | 0.15 s |
| All technical indicators | 3.3 s | 0.7 s |
from financetoolkit import Toolkit |
2.5 s | 0.9 s |
| Reading prices from the cache | 3.8 s | 1.1 s |
Excel files are read with calamine, modules load on first use, and repeated module access is about twice as fast.
Breaking changes
get_real_yield_curveandget_breakeven_inflation_expectationstakecountries=and return (Country, Maturity) columns labelled5Y.- The market risk premium and percentage releases in the economic calendar are now decimals.
- GMDB series end at the last observation;
gmdb_forecasts=Trueon a method includes the projections. - Corrected calculations change some results:
- Drawdowns now count a loss on the first day.
- RSI and CMO start one day later, matching Wilder's definition and TA-Lib.
- Growth from zero is missing instead of infinite.
- Missing values no longer skew beta, VaR/CVaR or the portfolio average.
- External data is cached by default. Opt out with
use_cached_data=FalseorFINANCE_TOOLKIT_CACHE_ENABLED=0. - Requests carrying an API key are no longer retried without certificate verification. Behind a corporate proxy, set
REQUESTS_CA_BUNDLE; the README Q&A explains how.
Fixes
- Data:
- FMP timeouts and server errors are retried per ticker instead of failing the whole request.
- Cached prices survive a provider switch.
- Holidays no longer invent returns or volume.
- CIK and CUSIP keep their leading zeros.
- Yahoo listing dates use the exchange's timezone.
- Calculations:
- WACC is correct for companies without debt, and the Graham Number for loss-making companies.
- Barrier options are priced correctly once the barrier has been crossed.
- Black-Scholes at expiry, bond coupon counts and yields, and binomial tree inputs are handled correctly.
- Consistency:
rounding=0androunding=Nonework everywhere.- One error handler serves all modules, so strict mode now covers Performance and Technicals.
- Other:
- EIOPA shocked curves and the life tables are fixed.
- The enterprise value breakdown and implied volatility work again.
- The economic calendar's order is now deterministic.
- Moved BIS, Fed and NBER endpoints are updated.
- Docstrings: all 553 examples were run and their output tables refreshed.
MCP server
- New methods are added to the existing tools, and an empty result explains why.
- Tool descriptions are no longer cut off.
- Daily rolling performance metrics work.
- A hosted server caches every source except FMP.
- Optional usage statistics at
/statswithFT_MCP_ANALYTICS=1. - The server reports the Finance Toolkit's version to clients.
Dependencies
All new requirements use wide version ranges, so the Finance Toolkit fits alongside other packages without forcing upgrades.
| Change | Package | Why |
|---|---|---|
| Added | python-calamine>=0.3.0 |
Reads Excel workbooks up to 10× faster than openpyxl. Used for the large official workbooks, such as the Bank of England, EIOPA and ESRB files. Not installed on Python 3.15 until wheels are published there; openpyxl reads the files instead. |
| Added | xlrd>=2.0 |
Reads the older .xls workbooks some official sources still publish, when calamine is not available or cannot read a file. |
| Declared | numpy>=1.26, scipy>=1.10 |
Already used throughout, but until now they were only installed because other packages depended on them. They are now listed explicitly. |
| Removed | scikit-learn |
Only used for the linear regressions of the factor models (e.g. Fama-French). These now use SciPy's least squares, with the same results, which removes a large dependency. |
Unchanged: pandas>=3.0, requests, yfinance, openpyxl and pyyaml, and the optional econometrics and mcp extras.
FinanceToolkit v2.2.1
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
FinanceToolkit v2.2.0
This is the largest release to date. The headline addition is a brand new Econometrics module (toolkit.econometrics) — 48 methods covering regression, panel data, causal inference, unit root/cointegration testing, Granger causality, diagnostics, and forecasting, all funneled through statsmodels/linearmodels instead of hand-built math. Alongside it, Risk, Performance, Models and Economics roughly double in depth with systemic-risk, market-timing, bankruptcy-scoring and crisis-dating metrics, and Technicals, Ratios, Fixed Income and Options each pick up a batch of previously-missing metrics.
The second large addition is a rebuilt caching layer. Three separate, incompatible caching mechanisms have been replaced by one incremental, range-aware SQLite cache that knows what it already holds per ticker and per date range — widening a period now retrieves only the missing years, and adding a ticker retrieves only that ticker. It also removes a genuine correctness bug in the old one, which silently overwrote the tickers and dates you asked for with whatever an earlier cached run had used. See Caching below.
Just as importantly, this release includes an extensive formula-correctness pass across nearly every module, carried out over two separate audit rounds — the second explicitly re-checking the first round's own fixes rather than trusting them. Every accepted finding was verified against live FMP/FRED/OECD/ECB/NY Fed data, published figures from BLS/BEA/the US Treasury/Ken French/the source papers, a reference library (TA-Lib/arch/statsmodels/linearmodels/py_vollib) where one exists, or analytic ground truth via Monte Carlo and finite differences. Combined, this fixed over 200 defects. Several of these are not edge-case bugs — Sharpe Ratio, Sortino Ratio, M2 Ratio, RSI, ADX, ATR, EWMA Volatility, the Hurst Exponent, Macaulay's Duration, bond DV01, Portfolio Volatility, US "headline" unemployment and every price-to-fundamental ratio on the LSE/JSE/TASE were all silently returning the wrong number in prior releases. See Bug Fixes below for the full list with what changed and why.
Minimum requirements changed: pandas>=3.0 (was >=2.2) and Python 3.11+ (was 3.10+, since pandas 3.0 itself drops 3.10 support).
Every method below hangs off the matching Toolkit sub-module (e.g. toolkit.econometrics.get_ols()) or its standalone class (Discovery(...), Economics(...), FixedIncome(...)). Full runnable examples with real output live in the documentation — each section below links to its module's page.
New Features
Econometrics (new module)
toolkit.econometrics is a new, optional module for testing statistical properties of price/return series and estimating causal effects — installed via the financetoolkit[econometrics] extra (statsmodels/linearmodels). Every result is a plain dict (matching the rest of the codebase's convention of returning pd.Series/pd.DataFrame rather than opaque result objects), with a matching *_summary_table/*_summary companion wherever a .summary() would otherwise be expected. Full examples: docs/econometrics.
- Regression:
get_ols,get_wls,get_gls(nonrobust/HC0-3/cluster/HAC-Newey-West standard errors),get_logistic_regression,get_probit_regression,get_quantile_regression. - Panel data:
get_fixed_effects,get_random_effects,get_hausman_test(which specification to prefer). - Causal inference:
get_difference_in_differences,get_iv_2sls,get_propensity_score_matching,get_regression_discontinuity,get_synthetic_control(Abadie/Diamond/Hainmueller 2010, with placebo-based inference). - Time series forecasting:
get_arima_forecast,get_var_forecast(plusget_impulse_response_functionandget_variance_decomposition),get_vecm_forecast. - Cross-sectional asset pricing:
get_fama_macbeth_regression(Fama-MacBeth two-pass regression). - Event studies:
get_event_study(MacKinlay 1997 market-model cumulative abnormal return analysis). - Unit root & cointegration:
get_augmented_dickey_fuller,get_kpss_test,get_phillips_perron_test,get_zivot_andrews_test;get_engle_granger_cointegration,get_johansen_cointegration. - Granger causality:
get_granger_causality. - Diagnostics:
get_arch_lm_test,get_jarque_bera_test,get_ljung_box_test,get_cusum_test,get_variance_ratio_test. - Specification & hypothesis tests:
get_breusch_pagan_test,get_white_test,get_durbin_watson_test,get_vif,get_ramsey_reset_test,get_chow_test,get_f_test,get_likelihood_ratio_test,get_wald_test,get_hausman_wu_test,get_two_sample_t_test. - Forecast evaluation:
get_rmse,get_mae,get_diebold_mariano_test,get_out_of_sample_validation.
Risk
Call these via toolkit.risk.<method>(). Full examples: docs/risk.
get_covar(CoVaR / Delta-CoVaR) — how muchreturns' own tail risk worsens whenconditioning_returns(e.g. the financial system, or a specific counterparty) is itself in distress, via quantile regression. Reference: Adrian, T. & Brunnermeier, M.K. (2016), American Economic Review.get_marginal_value_at_risk/get_component_value_at_risk— Euler risk decomposition of portfolio VaR: Marginal VaR is each asset's Beta to the portfolio times portfolio VaR; Component VaR is that scaled by the asset's weight, and the components sum exactly to total portfolio VaR (Garman 1997, Litterman 1996).get_amihud_illiquidity(Amihud 2002) andget_roll_spread(Roll 1984) — market-microstructure liquidity measures derived purely from price/volume, without needing order-book data.get_volatility(method=...)— four realized-volatility estimators ("parkinson","garman_klass","rogers_satchell","yang_zhang") built from daily OHLC(V) prices rather than just closes, each using more of the day's price action (and, for Yang-Zhang, overnight gaps) to estimate volatility more efficiently than a close-to-close standard deviation. The defaultmethod="close_to_close"is the existing behaviour.get_har_rv_forecast(Corsi 2009 HAR-RV) — forecasts future realized variance from a simple linear combination of daily, weekly and monthly lagged realized variance, a cheap and surprisingly effective alternative to a full GARCH fit.get_hill_estimator— a nonparametric estimate of the tail index of the return distribution, i.e. how fat the tails actually are.get_gjr_garch/get_egarch(plus_forecast/_parametersvariants, andget_garch_parametersfor the plain model) — asymmetric GARCH models where negative and positive shocks affect future volatility differently (the leverage effect), unlike plain GARCH. References: Glosten, Jagannathan & Runkle (1993); Nelson (1991).get_var_backtest— builds a rolling, out-of-sample VaR path and backtests it with the Kupiec (1995) proportion-of-failures test and the Christoffersen (1998) independence test, to check whether a VaR model's breach rate and breach clustering match what the confidence level implies.get_acerbi_szekely_test— the counterpart for Expected Shortfall, backtesting a CVaR model against realized returns via the Acerbi-Szekely (2014) Z2 statistic, since the breach-counting tests above only validate VaR.get_tail_dependence_coefficient,get_copula_parameters/get_copula_simulation/get_best_fitting_copula— Gaussian, Student-t, Clayton, Gumbel and Frank copulas with MLE fitting, simulation, and AIC-based family comparison, for modelling joint tail behaviour between two return series beyond what linear correlation captures. Default to running across every unique ticker pair on the Toolkit instance when no explicit pair is given.
Performance
Call these via toolkit.performance.<method>(). Full examples: docs/performance.
get_sharpe_ratio(method=...)— three corrections to the plain Sharpe Ratio:"adjusted"for skewness/kurtosis,"probabilistic"for estimation uncertainty given the sample size, and"deflated"for multiple-testing bias when many strategies were tried before picking the best one. The defaultmethod="standard"is the existing behaviour.get_appraisal_ratio— Jensen's Alpha divided by the idiosyncratic (residual) standard deviation left over from the CAPM regression that produced it: how much stock-picking skill an active manager is generating per unit of the risk that skill itself introduces.get_fama_decomposition(Fama 1972) — splits total excess return into Selectivity and Diversification, decomposing Jensen's Alpha further than the Alpha figure alone can.get_henriksson_merton_modelandget_treynor_mazuy_model— market-timing regressions that test whether a manager's returns show evidence of successfully timing the market, not just picking good securities.get_rachev_ratio(R-Ratio) andget_starr_ratio— tail-focused alternatives to Sharpe/Sortino that measure reward per unit of tail risk (CVaR-based) rather than per unit of standard deviation or downside deviation.get_carhart_four_factor_model(Carhart 1997) — adds a momentum factor to the existing Fama-French regression machinery.
Models
Call these via toolkit.models.<method>(). Full examples: docs/models.
get_fulmer_h_score,get_grover_score,get_ohlson_o_score,get_springate_score,get_zmijewski_score— five more bankruptcy/distress prediction models alongside the existing Alt...
FinanceToolkit v2.1.4
This is one of the larger feature releases to date. It roughly doubles the depth of three modules — Models, Performance, and Risk — and rounds out Technicals, Ratios, and Discovery with metrics that were previously missing.
Two capabilities now cut across nearly the whole toolkit:
rollingandtrailingwindows. Many metrics used to return one value per reporting period. They can now be computed over a sliding window (rolling=<n>) or a trailing sum/average (trailing=<n>), turning a snapshot into a proper time series.standardize(Z-Score). Mostget_*methods across Economics, Ratios, Technicals, Risk, Performance, Models, Options, and Fixed Income now acceptstandardize=True. It converts raw values into standard deviations from their own historical mean, so metrics on completely different scales become directly comparable — useful for ranking, scoring, or spotting an unusual reading at a glance.
On the fix side: swaption prices are discounted correctly now, a case-mismatch bug that was silently zeroing out diluted EPS is gone, and the new Forward P/E / Forward PEG ratios cache analyst estimates properly instead of double-fetching.
Volatility, Excess Return, and Excess Volatility are gone from toolkit.get_historical_data(). Use these instead:
toolkit.risk.get_volatility(period=...)toolkit.risk.get_excess_volatility(period=...)toolkit.risk.get_variance(period=...)toolkit.performance.get_excess_return(risk_free_rate=..., cumulative=...)
Every method below hangs off the matching Toolkit sub-module (e.g. toolkit.models.get_graham_number()) or its standalone class (Discovery(...), Economics(...), FixedIncome(...)). Full runnable examples with real output live in the documentation — each section below links to its module's page.
New Features
Standardization (Z-Score)
standardize: bool = False is now available on nearly every controller — Economics, Ratios, Technicals, Risk, Performance, Models, Options, and Fixed Income, roughly 270 methods in total.
Z-Score = (value - mean) / standard deviation
Mean and standard deviation come from each series' own history: per ticker for entity-indexed data like ratios, per column for date-indexed data like economic indicators. With growth=True, it standardizes the growth values instead of the raw ones. Use it to compare metrics that live on incompatible scales, build a quick cross-sectional ranking straight from toolkit output, or spot when a current reading is an unusual number of standard deviations from its own norm.
Financial Models
Call these via toolkit.models.<method>(). Full examples: docs/models.
get_economic_value_added (EVA, economic profit) — whether a company earns more than its true cost of capital, something Net Income alone can't tell you.
NOPAT = EBIT × (1 − Effective Tax Rate)Invested Capital = Total Equity + Total Debt(each smoothed as a 2-period average, or atrailing-period average, to soften a single noisy balance-sheet snapshot)EVA = NOPAT − (WACC × Invested Capital)- Read it as: positive EVA creates value for capital providers; negative EVA destroys it, even in periods where net income is positive.
get_beneish_m_score (earnings manipulation score) — Messod Beneish's model for flagging likely earnings manipulation, a companion to the Altman Z-Score and Piotroski F-Score already in the toolkit.
M-Score = -4.84 + 0.92·DSRI + 0.528·GMI + 0.404·AQI + 0.892·SGI + 0.115·DEPI − 0.172·SGAI + 4.679·TATA − 0.327·LVGI- Each of the eight inputs is its own function in
beneish_model.py:get_days_sales_in_receivables_index(DSRI — receivables outgrowing sales),get_gross_margin_index(GMI — margin deterioration),get_asset_quality_index(AQI — a rising share of non-core assets),get_sales_growth_index(SGI — fast growth raises manipulation pressure),get_depreciation_index(DEPI — a slowing depreciation rate flatters earnings),get_selling_general_and_administrative_expenses_index(SGAI),get_leverage_index(LVGI), andget_total_accruals_to_total_assets(TATA — the accrual share of earnings, a red flag across most accounting-fraud models). - Read it as: above roughly -1.78, manipulation is more likely; below it, less likely. It's probabilistic, not a verdict — pair it with real fundamental analysis. The first period is always NaN since every input needs a prior period to compare against.
- Reference: Beneish, M. D. "The Detection of Earnings Manipulation." Financial Analysts Journal, Vol. 55, No. 5, 1999, pp. 24-36.
get_sustainable_growth_rate (SGR) — the fastest a company can grow revenue on internal funds alone, without new equity or added leverage.
SGR = Return on Equity × (1 − Dividend Payout Ratio)- Read it as: growing faster than SGR without outside financing means improving margins, cutting the payout, or taking on more debt — so actual growth versus SGR is a quick tell for how a company is funding its expansion.
get_internal_growth_rate (IGR) — a stricter version of SGR: growth funded from retained earnings only, with no debt added at all.
IGR = (ROA × Retention Ratio) / (1 − (ROA × Retention Ratio))- Read it as: IGR is always below SGR for a levered company. The gap between them is roughly how much of that company's "sustainable" growth actually depends on debt.
get_graham_number (Graham fair value) — Benjamin Graham's conservative ceiling on what a defensive investor should pay.
Graham Number = √(22.5 × EPS × Book Value per Share)- Read it as: most useful for stable, profitable companies with positive book value. Negative earnings or book value produce NaN (the square root of a negative number is undefined) — comparing the current price against this number is a quick margin-of-safety check.
Ratios
Call these via toolkit.ratios.<method>(). Full examples: docs/ratios.
Efficiency
get_research_and_development_ratio,get_selling_and_marketing_ratio,get_general_and_administrative_ratio— R&D, S&M, and G&A each as a share of Revenue. Most companies only report these lumped into SG&A; splitting them out lets you compare R&D intensity or customer-acquisition spend independent of admin overhead.get_stock_based_compensation_ratio— SBC ÷ Revenue. A high or rising number is a quality-of-earnings flag, since SBC gets added back in cash flow from operations (flattering FCF) while still diluting existing shareholders.get_deferred_revenue_ratio— Deferred Revenue ÷ Revenue. A leading indicator for subscription/SaaS businesses: a growing balance here signals revenue that hasn't hit the income statement yet.
Profitability
get_cash_tax_rate— taxes actually paid in cash ÷ Income Before Tax, versus the accrual-based Effective Tax Rate.get_tax_rate_divergence— Cash Tax Rate minus Effective Tax Rate. Persistently positive means more cash tax paid than expensed (e.g. deferred liabilities unwinding); persistently negative can flag aggressive tax deferral.
Solvency
get_debt_to_capital_ratio—Total Debt / (Total Debt + Total Equity). Stays between 0 and 1, unlike Debt-to-Equity, which makes capital structure easier to compare across companies.get_preferred_dividend_coverage_ratio— Net Income ÷ |Preferred Dividends|: how many times earnings cover the obligation to preferred shareholders.get_interest_paid_to_expense_ratio— Interest Paid ÷ Interest Expense. Well below 1 suggests interest is being accrued rather than paid (e.g. payment-in-kind debt); well above 1 suggests catch-up payments or a cash/accrual timing gap.
Valuation
get_ev_to_free_cash_flow_ratio— Enterprise Value ÷ Free Cash Flow, a multiple that (unlike EV/EBITDA) accounts for capex and working capital.get_buyback_yield—-(Repurchases + Issuances) / Market Cap. Positive means the company is a net repurchaser; negative means it's a net issuer, diluting shareholders.get_shareholder_yield— Dividend Yield + Buyback Yield: total cash returned to shareholders through both channels.get_sbc_adjusted_free_cash_flow— Free Cash Flow minus Stock-Based Compensation, treating SBC as the real cost it is instead of the non-cash add-back FCF normally lets it be.get_forward_price_earnings_ratio(Forward P/E) —Stock Price / Estimated EPS Average, using the analyst consensus estimate for a future period instead of trailing EPS. Needs a Premium FMP subscription; estimates are fetched once perToolkitinstance and cached automatically, so there's no need to callget_analyst_estimates()first.get_forward_price_earnings_growth_ratio(Forward PEG) —Forward P/E / (Estimated EPS Growth Rate × 100), where growth bridges the analyst estimate to the company's most recent actual EPS (get_estimated_eps_growth_rate, its own reusable function) rather than comparing two historical periods the waygrowth=Truedoes everywhere else. Read it as: near or below 1 is the classic (simplistic) heuristic for fairly priced growth; well above 1 means the market is paying a premium for it.
Technical Indicators
Call these via toolkit.technicals.<method>(). Full examples: docs/technicals.
Overlap
get_weighted_moving_average(WMA) — weights recent prices more, so it tracks price more closely than a Simple Moving Average while staying smoother than an Exponential one.get_hull_moving_average(HMA) — combines a half-window WMA, a full-window WMA, and a √window WMA specifically to cut moving-average lag without g...
FinanceToolkit v2.1.3
Minor release with mostly bug fixes and some minor enhancements.
Bug Fixes
-
Corrected trailing rolling axis for valuation ratios: The
show_dailyandtrailingcombination inget_enterprise_value,get_market_cap, and related valuation ratios was applying the rolling window on the wrong axis (tickers instead of dates), producing incorrect results when both parameters were active simultaneously. -
Resolved trailing parameter having no effect in
get_market_cap: The if/else branches for trailing were identical, meaning the parameter was silently ignored regardless of what was passed. -
Fixed cumulative return calculations for edge case data: Replaced
(1 + returns).cumprod()with(1 + returns.fillna(0)).cumprod()acrosshistorical_model.pyandrisk_model.py, and added.replace([np.inf, -np.inf], np.nan)before cumulative product computation to prevent inf propagation when input data contains gaps or anomalies. -
Resolved pandas FutureWarning in performance controller: Added
future_stack=Truetostack()calls inperformance_controller.pyto silence the upcoming default behaviour change in pandas. -
Fixed type annotation in
helpers.py: Correctedpd.PeriodIndextopd.DatetimeIndexin a function signature.
New Features
-
trailingparameter for Models: Added atrailingparameter to the Models sub-module (e.g.get_weighted_average_cost_of_capital). When supplied, financial statement inputs are rolled over the trailing period before calculation, enabling TTM-style model outputs consistent with the same parameter in Ratios and Performance. -
trailingparameter for Ratios: Valuation ratios (P/E, P/B, free cash flow yield, price-to-earnings, etc.) now accept an explicittrailingparameter. Previously, quarterly data defaulted unconditionally totrailing=4; the parameter now allows overriding or disabling that behaviour.
Test Coverage
Added missing snapshot tests for: collect_custom_ratios, get_cash_conversion_efficiency, get_effective_tax_rate, get_debt_service_coverage_ratio, get_weighted_dividend_yield, get_reinvestment_rate, get_ev_to_ebit, collect_all_metrics (performance + risk), and get_compound_growth_rate. Updated snapshots for valuation methods affected by the trailing and show_daily axis fixes. Added httpx2 as a dev dependency to resolve StarletteDeprecationWarning in MCP OAuth tests.
MCP Server
Updated MCPB manifest and build script to reflect the latest configuration. Removed conflicting description remarks from the registry and tools model.
Full comparison: v2.1.2...v2.1.3