FinanceToolkit v2.1.4 #233
JerBouma
announced in
Announcements
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
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, andExcess Volatilityare gone fromtoolkit.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
Toolkitsub-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 = Falseis now available on nearly every controller — Economics, Ratios, Technicals, Risk, Performance, Models, Options, and Fixed Income, roughly 270 methods in total.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)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·LVGIbeneish_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).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)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))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)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 giving up smoothness.get_volume_weighted_average_price(VWAP) — typical price weighted by volume over a rolling window; the benchmark most execution algorithms are measured against.get_parabolic_sar— a trend-following stop-and-reverse level that flips sides when the trend reverses, with an acceleration factor (af_start/af_increment/af_max, defaults 0.02/0.02/0.2) that speeds convergence as a trend runs longer. Commonly used to trail stop-losses.get_pivot_points— Pivot Point plus Resistance/Support 1-3, derived from the prior period's high/low/close to flag likely next-period levels.Volatility
get_donchian_channels— highest high and lowest low over a rolling window, with the midpoint as the middle line; spots breakouts and shows how wide the recent range has been.get_volatility_cone— the historical percentile spread (min through max, plus current) of annualized realized volatility across several lookback windows ([10, 20, 30, 60, 90, 120]by default). Shows at a glance whether current volatility is cheap or expensive versus its own history — often used before comparing to options-implied volatility.Breadth (cross-sectional across tickers at a point in time, not per-ticker over time)
get_trin(Arms Index) —(Advancing Issues / Declining Issues) / (Advancing Volume / Declining Volume). Below 1 suggests volume conviction is outpacing the number of advancing names (bullish); above 1 suggests the reverse.get_new_highs_new_lows— tickers at a window-period high minus tickers at a window-period low: a read on how broad a market move really is beneath the index-level headline.Performance
Call these via
toolkit.performance.<method>(). Full examples: docs/performance.get_calmar_ratio—Return / |Maximum Drawdown|. Penalizes purely by the single worst peak-to-trough loss, not by volatility the way Sharpe/Sortino do.get_sterling_ratio—Return / (|Average Drawdown| + adjustment)(adjustment defaults to 10%), using the average of all drawdowns rather than just the worst one — softer and less driven by a single event than Calmar.get_burke_ratio—Excess Return / Burke Drawdown Measure, where the measure is√(Σ drawdown²)(newget_burke_drawdown_measure). Squaring before summing penalizes both frequency and depth of drawdowns, so two strategies sharing a max drawdown can still score very differently here.get_upside_capture_ratio/get_downside_capture_ratio— the asset's average return versus the benchmark's, split across periods where the benchmark was positive or negative. Upside capture above 1 and downside capture below 1 is the profile most investors want: gaining more in rallies than losing in selloffs.get_win_rate— the share of periods where the asset beat the benchmark. Simple, distribution-free complement to the capture ratios.get_kappa_ratio(defaultorder=3) — Sortino generalized to a higher-order lower partial moment; Sortino itself is theorder=2case. Higher orders punish larger losses more, making Kappa more tail-sensitive than Sortino.get_omega_ratio(defaultminimum_acceptable_return=0.0) — gains above a minimum acceptable return divided by losses below it, using the full return distribution rather than just mean and variance — so it captures skew and fat tails that Sharpe/Sortino miss.get_gain_to_pain_ratio—Σ all returns / Σ |losses|. A blunt, easy-to-explain measure of gain earned per unit of pain endured.get_returns/get_excess_returnnow take acumulativeparameter (pulled out ofget_historical_data, see Breaking Changes) —cumulative=Truecompounds returns into a running index instead of period-over-period values.get_rolling_alpha,get_rolling_sortino_ratio,get_rolling_m2_ratio,get_rolling_tracking_error,get_rolling_information_ratio, plus rolling support on Beta, CAPM, Jensen's Alpha, and Treynor Ratio. Passrolling=<window>to see risk-adjusted performance evolve through time instead of getting one backward-looking number.Risk
Call these via
toolkit.risk.<method>(). Full examples: docs/risk.get_conditional_drawdown_at_risk(CDaR) — extends VaR/CVaR from returns to drawdowns: DaR is the alpha-quantile of the drawdown distribution, CDaR is the average of drawdowns at least that severe. Where VaR/CVaR ask "how bad could one period be," CDaR asks "how bad could the cumulative peak-to-trough loss get."get_tail_ratio—|percentile(1-alpha)| / |percentile(alpha)|of returns. Above 1 means best-case gains outsize worst-case losses — a quick complement to skewness that doesn't assume a distribution shape.get_maximum_drawdown_duration— periods between the peak and trough of the single largest drawdown: how long the worst decline took to play out.get_maximum_drawdown_recovery_time— periods from that trough until cumulative return hits a new high (NaN if it hasn't yet). Paired with duration, this gives the full timeline of a strategy's worst stretch.get_downside_deviation(semi-deviation, defaultminimum_acceptable_return=0.0) — standard deviation of only the returns below a minimum acceptable return, so upside swings no longer count against the metric the way they do in plain standard deviation.get_mean_absolute_deviation(MAD) — mean absolute distance from the mean return. No squaring, so a handful of outliers move it less than they'd move variance.get_coefficient_of_variation(CV, relative standard deviation) —standard deviation / meanof returns, which lets you compare relative volatility across assets with very different average returns — something raw standard deviation can't do.get_ewma_volatility(RiskMetrics volatility, defaultlambda_=0.94) —EWMA Variance(t) = λ·EWMA Variance(t-1) + (1-λ)·Return(t-1)². Weights recent observations more, so it reacts to a changing volatility regime faster than a fixed rolling window, while staying much simpler than a full GARCH fit.get_autocorrelation(defaultlags=10) — correlation between a series and its own lagged values; usually the first thing checked before deciding how many AR/MA terms a series needs.get_hurst_exponent(defaultmax_lag=20) — estimated via rescaled range (R/S).H < 0.5mean-reverting,H = 0.5random walk,H > 0.5trending — a single number for whether a series tends to trend or revert.get_value_at_risk(distribution="evt")now fits a Generalized Pareto Distribution to the worst losses via Peak-over-Threshold (newget_var_evt, plus athreshold_percentileparameter, default0.95, for how much of the tail to fit on). Models the tail's actual shape instead of assuming the whole distribution (Gaussian, Cornish-Fisher, Student-t) represents extreme outcomes well.get_volatility/get_variance/get_excess_volatilityare now first-class Risk methods (pulled out ofget_historical_data, see Breaking Changes).get_rolling_var_historic,get_rolling_cvar_historic,get_rolling_variance,get_rolling_volatility,get_rolling_excess_volatility,get_rolling_tail_ratio,get_rolling_conditional_drawdown_at_risk,get_rolling_skewness,get_rolling_kurtosis,get_rolling_downside_deviation,get_rolling_omega_ratio— same sliding-window idea as Performance, across Risk.Economics
Call these via
Economics(...).get_*(), ortoolkit.economics.<method>()on aToolkitinstance. Full examples: docs/economics.rollingandtrailingparameters now cover essentially the whole module — GDP, CPI, inflation, trade, investment, consumption, confidence indices, government fiscal metrics, interest rates, unemployment, population, and more (45 methods).rolling=<n>smooths noisy monthly/quarterly prints with a simple moving average;trailing=<n>sums over a trailing window (e.g. a trailing 4-quarter sum to annualize a quarterly flow). Both apply before anygrowth/standardizestep.Discovery & News
Call these via
Discovery(...).get_*()/search_*(), ortoolkit.discovery.<method>()for the ticker-scoped ones. Full examples: docs/discovery.Toolkititself:get_stock_news(pages, limit)andget_press_releases(pages, limit)— ticker-scoped, filtered automatically to the Toolkit'sstart_date/end_date.Discovery, not ticker-scoped:get_stock_news,get_general_news(broad market news),get_press_releases,get_crypto_news,get_forex_news, plus free-text search viasearch_stock_news,search_press_releases,search_crypto_news,search_forex_news.get_ipo_calendar,get_ipo_disclosures(SEC pre-IPO filings),get_ipo_prospectuses(S-1/424B4 pricing),get_stock_splits_calendar,get_mergers_acquisitions_latest(recent deals with acquirer/target and a link to the SEC filing).get_sector_performance/get_industry_performanceandget_sector_pe/get_industry_pe— passdatefor a snapshot across everything, orsector/industryfor one name's history. Good for checking whether a move was sector-wide or idiosyncratic, or whether a sector looks rich or cheap against its own P/E history.Fixed Income
Call this via
FixedIncome().get_derivative_price(...). Full examples: docs/fixedincome.tenorandpayment_frequencyparameters onget_derivative_price— see Bug Fixes above; this is what makes swaption pricing account for the underlying swap's full payment schedule instead of treating it as a single-date payout.Bug Fixes
Swaption pricing was missing the annuity leg.
get_black_priceandget_bachelier_priceused to discount the option payoff with a single factor to the expiration date, as if exercising a swaption paid out once. It doesn't — it grants the right to enter a swap that pays out at every payment date over the swap's tenor. Both functions now multiply by an annuity factor (the sum of discount factors across those payment dates) via a new_get_annuity_factor()helper. Two new parameters,tenorandpayment_frequency, let you set the underlying swap's length and payment schedule separately from the option's own expiration. Previously tenor had zero effect on price; now a 1-year option into a 5-year swap correctly prices roughly 5x higher than one into a 1-year swap, since it covers five times as many payment dates.trailingrolled the wrong axis when combined withshow_daily.get_enterprise_value,get_market_cap, and related valuation ratios rolled the trailing window across tickers instead of across dates whenever both parameters were active together.trailingdid nothing inget_market_cap. Its if/else branches were identical, so the parameter was silently ignored.Cumulative return broke on gappy data.
(1 + returns).cumprod()became(1 + returns.fillna(0)).cumprod()inhistorical_model.pyandrisk_model.py, with.replace([np.inf, -np.inf], np.nan)added before the product to stop infinities from propagating through the series.Silenced a pandas
FutureWarning. Addedfuture_stack=Truetostack()calls in the performance controller ahead of pandas' default-behaviour change.Fixed a type annotation.
pd.PeriodIndexcorrected topd.DatetimeIndexin ahelpers.pysignature.Risk methods ignored the requested date range.
get_value_at_risk,get_conditional_value_at_risk,get_entropic_value_at_risk,get_conditional_drawdown_at_risk,get_tail_ratio,get_maximum_drawdown,get_maximum_drawdown_duration,get_maximum_drawdown_recovery_time,get_ulcer_index,get_garch,get_skewness,get_kurtosis, andget_variancecould silently return data outsidestart_date/end_date, especially withrollingorwithin_periodset. All now truncate correctly.A one-character typo zeroed out diluted EPS everywhere.
financetoolkit/normalization/income.csvmapped FMP's field asepsdiluted(lowercase), but thestableendpoint actually returnsepsDiluted(capital D). Normalization fills any expected-but-missing column with0before renaming, so this mismatch meantget_income_statement()always reported "EPS Diluted" as zero — the real column was silently dropped instead of renamed. It stayed invisible until the new Forward PEG ratio started reading that field directly, at which point it returned exactly0.0for every ticker, every period. Fixed by correcting the key toepsDiluted.Forward-looking ratios fetched analyst estimates twice instead of caching once.
get_forward_price_earnings_ratio()andget_forward_price_earnings_growth_ratio()are meant to fetch Premium-only analyst estimates once and reuse them. The cache lived on theRatiosinstance, buttoolkit.ratiosbuilds a brand-newRatiosinstance on every access — so calling both methods back to back (exactly what the docs show) fetched estimates twice, doubling Premium API usage for nothing. Fixed by sharing a mutable cache dict from theToolkitinstance into everyRatiosinstance it builds, so the fetch-once behaviour survives across calls for the life of theToolkitobject.Log messages were mis-pluralized.
"Obtaining %s data for %d tickers"now reads"...for %d ticker(s)", so single-ticker runs no longer say "for 1 tickers".Local dev setup was missing MCP dependencies. The
devdependency group inpyproject.tomldidn't pull in the project's ownmcpextra, so a plainuv syncleftfastmcp/pydanticand friends missing. Added"financetoolkit[mcp]"todevplus a[tool.uv.sources]workspace entry.Test Coverage
New coverage for the Risk module's new metrics (
test_cvar_model.py, expandedtest_risk_controller.py,test_risk_model.py,test_var_model.py), the new technical indicators (Donchian Channels, Hull Moving Average, New Highs/New Lows, Parabolic SAR, Pivot Points, TRIN, VWAP, Weighted Moving Average, Volatility Cone), and the internalhelpers.pysplit (test_dataframe_model.py,test_requests_model.py,test_statistics_model.py). Added missing snapshot tests forcollect_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),get_compound_growth_rate, andget_estimated_eps_growth_rate. Updated snapshots for valuation methods affected by thetrailing/show_dailyaxis fix. Addedhttpx2as a dev dependency to resolve aStarletteDeprecationWarningin MCP OAuth tests.MCP Server
config.yamland parameter descriptions inregistry_controller.pyupdated to cover all of the above: rolling/trailing on Economics, the newstandardize/cumulative/threshold_percentile/minimum_acceptable_returnparams, the new Risk and Performance metrics, and the new Models functions.valuationtool group now explicitly listsget_forward_price_earnings_ratioandget_forward_price_earnings_growth_ratiovia amethod_overrideentry. They're deliberately excluded from the bulkcollect_valuation_ratiosscan — including them there would trigger a Premium-only analyst-estimates fetch on every bulk request, even for free-tier users — so normal method discovery can't find them on its own.get_stock_quotesfunction fromdiscovery_model.py— dead code, sincefmp_model.get_quotealready covers per-ticker real-time quotes.Full comparison: v2.1.3...v2.1.4
This discussion was created from the release FinanceToolkit v2.1.4.
Beta Was this translation helpful? Give feedback.
All reactions