github JerBouma/FinanceToolkit v2.3.3
FinanceToolkit v2.3.3

48 minutes ago

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 trailing set, 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_coefficient and get_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_securities lists every marketable US Treasury with price, yield and duration from FedInvest, without an API key. get_bond_universe lists 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 with bonds=, issuer=, etfs= and a maturity range. The rate calculators read a country's government bond curves on a day (#358).
  • get_implied_default_probability takes ratings= and uses the latest ICE BofA spread per rating (#358).
  • Removed: get_option_adjusted_spread, get_derivative_price and 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 (with objective="information_ratio") and get_growth_optimal_weights (Kelly, with fraction=), and covariance="denoised" (López de Prado) in every optimizer and in get_risk_contributions, (#362).
  • Per-asset weight bounds (one number or a dict per ticker), sector_bounds= and country_bounds= from the company profiles, and net_exposure and gross_exposure for 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_weights and get_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= with maximum_turnover= and transaction_cost= in the optimizers, and expected_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) and get_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_weights takes target_volatility= for the highest expected return at a volatility, and Black-Litterman takes risk_aversion="market" (implied by the benchmark) and reports the posterior volatility (#394).
  • get_discrete_allocation turns weights into whole shares for a portfolio value (integer programme or greedy), get_portfolio_risk_metrics gives the return, volatility, Sharpe, Sortino, maximum drawdown, VaR and CVaR of given weights next to the benchmark, and get_risk_contributions adds 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_returns returning 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, and get_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_ratio of altman_model.get_altman_z_score, now equity_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_series a deepest_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, and get_coskewness and get_cokurtosis with 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 a start_date on 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.NS is 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_only and sort_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_curve takes generic=True for the front month and the next eleven as listed each day, and growth/lag; its end date is now included (#382).

Technicals

  • One get_moving_average(method=...) for the simple, exponential, double exponential, triangular, weighted and Hull moving averages. Removed: get_exponential_moving_average, get_double_exponential_moving_average, get_triangular_moving_average, get_weighted_moving_average and get_hull_moving_average (#388).
  • signals_only=True on get_signals and get_candlestick_patterns keeps only the days with a signal or pattern (#352).

Data sources

  • Fiscal periods are labelled alike from FMP and Yahoo Finance: a period ending in the first five days of a month counts as ending in the month before, which keeps the quarters of 52/53-week years that were dropped before. Yahoo statistics carry the fiscal quarter, so its filing deadlines match. Removed: yfinance_model.get_reported_currency, now get_company_information (#389).
  • Results whose periods have a multiplier, such as 2M, keep their period (#383).
  • The cache stores data as compressed JSON instead of pickle, so reading a cache file never runs code (#366).

Security

  • Only the FMP and FRED keys are read from .env files, never over a key already set in the environment; API keys are removed from every log message and traceback; the setup wizard writes its files readable only by you (#368).

Economics

  • Removed: get_real_gross_domestic_product_usd, the same series as get_gross_domestic_product(inflation_adjusted=True, usd=True) (#388).

Code

  • Misspelt names corrected, without aliases: get_gordon_growth_model (was get_gorden_growth_model), show_ticker_separation (was show_ticker_seperation), oecd_model.get_trust_in_government and the stock_simulation_dictionary parameter; duplicated value splitting and API key checks merged; unused pylint comments removed (#384).
  • Duplicated helpers merged into one place and dead code removed, including the unused constants Toolkit.TICKER_LIMIT and historical_model.TREASURY_LIMIT, portfolio.helpers.Style and SQLiteBackend.read_series and read_coverage (#374).
  • Docstring fixes across the package (#352).

MCP server

  • risk_free_rate ('13w', '5y', '10y' or '30y'; default '10y') on the performance, risk, models and portfolio tools (#367).
  • The earnings calendar answers with primary listings ranked by market cap by default (#381).
  • Size limits per call on simulations, backtests (lookback at most 504 days), resamples, strike grids, downloads and repeated fits, a 180-second time limit and at most three running calls per user when hosted, and responses cut to size before they are built (#360).
  • MCP_HOST defaults to 127.0.0.1 with DNS rebinding protection on loopback; forwarded headers are trusted only from FORWARDED_ALLOW_IPS; tickers are validated before they reach a URL; a hosted server never retries without TLS verification (#360).
  • Sign-in tokens are encrypted and last 24 hours, renewed by 90-day refresh tokens; registered clients are bound to their redirect addresses; pending authorization codes are limited per client address (#360, #368).
  • The Docker image pins its base image by digest, installs dependencies at build time and runs as a non-root user (#365).

Release pipeline

  • Merging a release PR publishes it to PyPI, GitHub and the MCP Registry after an approval, GitHub Actions are pinned to commit SHAs, and Dependabot keeps those pins and the Docker base image current (#365, #376, #377).
  • Offline tests block the network, and the calculations that no test covered are tested against values worked out by hand (#370, #375).

Don't miss a new FinanceToolkit release

NewReleases is sending notifications on new releases.