The ML-powered financial modeling toolkit for Python
Project description
finverse
Institutional-grade financial modeling for Python.
DCF · LBO · Credit · Options · Bonds · ML Forecasting · Portfolio · Macro · Audit
No Bloomberg. No API keys. Pure Python.
pip install finverse
from finverse import pull, DCF, sensitivity, regime_dcf
from finverse.ml import garch, cross_sectional
from finverse.credit import merton, altman
from finverse.risk import evt, kelly
from finverse.portfolio import hrp, black_litterman
from finverse.audit import earnings_quality, manipulation
from finverse.models.options import call, put, implied_vol
from finverse.models.bonds import price as bond_price
from finverse.macro import nelson_siegel
data = pull.ticker("AAPL")
# ML-powered DCF
model = DCF(data).use_ml_forecast()
model.run().summary()
sensitivity(model, rows="wacc", cols="terminal_growth")
# Regime-conditional DCF — adjusts every assumption to the macro regime
regime_dcf(data).summary()
# GARCH vol → Merton credit model
vol = garch.fit(data, model_type="GJR-GARCH")
merton.analyze(data, garch_vol=vol.current_vol).summary()
altman.analyze(data).summary()
# Manipulation fingerprint — 40+ accounting signals
manipulation.fingerprint(data).summary()
# Tail risk and Kelly sizing
evt.analyze(data).summary()
kelly.from_distribution(data).summary()
# Portfolio with analyst views
stocks = [pull.ticker(t) for t in ["AAPL", "MSFT", "GOOGL", "JPM", "XOM"]]
black_litterman.optimize(stocks, views=[
BLView(["AAPL"], [1.0], expected_ret=0.15, confidence=0.8),
]).summary()
# Options and bonds
call(spot=185, strike=190, sigma=0.28, maturity=0.25).summary()
bond_price(face=1000, coupon_rate=0.05, ytm=0.06, maturity=10).summary()
# Yield curve
nelson_siegel.us_curve().summary()
What's inside
51 modules across 11 layers. Everything runs fully offline except pull.* data functions.
Data — finverse.pull
| Function | Source | Key needed |
|---|---|---|
pull.ticker("AAPL") |
yfinance — financials, price history | None |
pull.edgar("AAPL", "10-K") |
SEC EDGAR — filings, XBRL facts | None |
pull.fred("GDP", "UNRATE") |
Federal Reserve macro data | Free at fred.stlouisfed.org |
pull.macro_snapshot() |
Rates, inflation, VIX, credit spreads | Free |
Valuation — finverse.models
| Model | How to use | What it does |
|---|---|---|
DCF |
DCF(data).use_ml_forecast().run() |
Discounted cash flow with ML-estimated assumptions |
LBO |
LBO(LBOAssumptions(...)).run() |
Full buyout: debt schedule, IRR, MoM |
ThreeStatement |
ThreeStatement(data).run() |
Linked IS / BS / CF |
comps |
comps(data, peers=["MSFT","GOOGL"]) |
Comparable company analysis, implied price range |
regime_dcf |
regime_dcf(data) |
DCF with per-regime assumptions, probability-weighted price |
synthetic_peers |
build_peers(data, {"software":0.6,"hardware":0.4}) |
Peer multiples for companies with no clean peer set |
gordon |
gordon(dividend=1.84, growth_rate=0.04, ke=0.085) |
Gordon Growth Model |
h_model |
h_model(dividend=1.84, high_growth=0.15, ...) |
H-Model DDM |
multistage |
multistage(dividend=1.84, stage1_growth=0.15, ...) |
Multistage DDM |
sotp |
sotp([Segment(...), Segment(...)]) |
Sum of the Parts |
apv |
apv.analyze(data) |
Adjusted Present Value (Modigliani-Miller) |
real_options |
real_options.expand(500, 200, 0.30, 3.0) |
Expand, abandon, defer (Black-Scholes) |
macro.nowcast |
macro.nowcast() |
GDP nowcast, recession probability, regime |
options |
call(185, 190, 0.28, 0.25) |
European call/put, all 5 Greeks, IV solver |
bonds |
bond_price(1000, 0.05, 0.06, 10) |
Price, YTM, duration, convexity, DV01 |
from finverse import pull, DCF, sensitivity, scenarios, regime_dcf
from finverse.models.lbo import LBO, LBOAssumptions
from finverse.models.ddm import gordon, h_model, multistage
from finverse.models.sotp import Segment, analyze as sotp
from finverse.models.synthetic_peers import build_peers
from finverse.models.options import call, put, implied_vol, vol_surface
from finverse.models.bonds import price as bond_price, ytm_from_price
data = pull.ticker("AAPL")
# --- DCF ---
model = DCF(data).use_ml_forecast()
model.run().summary()
sensitivity(model, rows="wacc", cols="terminal_growth")
scenarios(model,
bull={"wacc": 0.085, "revenue_growth": 0.12},
base={"wacc": 0.095, "revenue_growth": 0.08},
bear={"wacc": 0.115, "revenue_growth": 0.03},
)
# --- Regime-Conditional DCF ---
# Adjusts WACC, growth, margins per detected macro regime
# Returns probability-weighted implied price across all regimes
result = regime_dcf(data)
result.summary()
print(f"Current regime: {result.current_regime}")
print(f"Regime-weighted: ${result.weighted_price:.2f}")
print(f"Static DCF: ${result.static_price:.2f}")
print(f"Regime adjustment: {result.regime_discount_pct:+.1%}")
# --- LBO ---
result = LBO(LBOAssumptions(
entry_ebitda=150, entry_ev_ebitda=10.0,
equity_pct=0.40, hold_years=5, exit_ev_ebitda=12.0,
)).run()
print(f"IRR: {result.irr:.1%} MoM: {result.mom:.2f}x")
# --- Synthetic Peers ---
# For conglomerates or niche companies with no clean peer set
result = build_peers(data, segment_weights={
"hardware": 0.55,
"software": 0.25,
"consumer_staples": 0.20,
})
result.summary()
p25, median, p75 = result.implied_price_range
# --- Options ---
c = call(spot=185, strike=190, sigma=0.28, maturity=0.25)
c.summary()
# → price, delta, gamma, theta, vega, rho
p = put(spot=185, strike=190, sigma=0.28, maturity=0.25)
# Solve implied vol from market price
iv = implied_vol(market_price=9.03, spot=185, strike=190, maturity=0.25)
print(f"Implied vol: {iv.implied_vol:.2%}")
# Vol surface across strikes and maturities
surface = vol_surface(spot=185, sigma=0.28)
# --- Bonds ---
b = bond_price(face=1000, coupon_rate=0.05, ytm=0.06, maturity=10)
b.summary()
# → clean price, dirty price, YTM, Macaulay duration,
# modified duration, convexity, DV01, yield scenario table
# Solve YTM from market price
b2 = ytm_from_price(market_price=950, coupon_rate=0.05, maturity=10)
print(f"YTM: {b2.ytm:.4%}")
ML — finverse.ml
| Module | Algorithm | What it does |
|---|---|---|
ml.forecast |
XGBoost + bootstrap | Revenue/margin forecasts with 80% confidence intervals |
ml.cross_sectional |
GBM on universe | Train on 80+ companies simultaneously, forecast any ticker |
ml.garch |
MLE via scipy | GARCH(1,1), EGARCH, GJR-GARCH volatility — no arch package needed |
ml.factor |
Rolling OLS | Fama-French factor decomposition (market, value, momentum, quality) |
ml.regime |
Hidden Markov Model | Regime detection — expansion, slowdown, contraction, recovery |
ml.nlp |
Lexicon | Financial text sentiment (no external model) |
ml.cluster |
KMeans / DBSCAN | ML peer group detection from financial ratios |
ml.anomaly |
Isolation Forest + Beneish | Earnings anomaly detection |
ml.causal |
Granger causality | Which macro variables drive earnings |
from finverse.ml import forecast, garch, cross_sectional, factor, regime
# Revenue forecast with confidence intervals
fc = forecast.revenue(data, n_years=5)
fc.summary()
# → point estimates, 80% CI, implied CAGR, key drivers
# GARCH volatility — picks up leverage effect (bad news = more vol)
vol = garch.fit(data, model_type="GJR-GARCH")
vol.summary()
garch.compare(data) # compare all 3 models by AIC
# Cross-sectional — trained on universe, not just the company's own history
cs = cross_sectional.forecast(data, target="revenue_growth")
cs.summary()
# → forecast, 80% CI, percentile rank vs 80-company universe, feature importance
# Factor decomposition
factors = factor.decompose(data, window="3y")
factors.summary()
# Regime detection — feeds into regime_dcf automatically
r = regime.detect(data.price_history["Close"])
print(f"Current regime: {r.current_regime.value}")
adjusted_wacc = r.adjust_wacc(0.095)
Risk — finverse.risk
| Module | Method | What it does |
|---|---|---|
risk.monte_carlo |
Monte Carlo | 10k DCF scenarios, price distribution, P(upside) |
risk.var |
Historical + parametric | VaR(95/99%), CVaR, max drawdown, stress scenarios |
risk.evt |
Peaks-Over-Threshold (GPD) | Tail VaR at 99%, 99.9%, 99.99% — beyond normal distribution |
risk.kelly |
Continuous + binary + multi-asset | Optimal position sizing, wealth path simulation |
from finverse.risk import monte_carlo, var, evt, kelly
# Monte Carlo
mc = monte_carlo.simulate(model, n_simulations=10_000)
mc.summary()
mc.plot()
# Extreme Value Theory — models the tail that normal distribution misses
tail = evt.analyze(data)
tail.summary()
# → ξ (tail index), VaR(99%), VaR(99.9%), VaR(99.99%), return periods
# e.g. "a 15% daily loss is expected once every 47 years"
evt.compare_tails([pull.ticker(t) for t in ["AAPL","MSFT","GOOGL"]])
# Kelly criterion
k = kelly.from_distribution(data) # continuous: f* = μ/σ²
k.summary()
# → full Kelly, half Kelly, quarter Kelly, expected geometric growth
paths = k.simulate(n_periods=252) # wealth path simulation
k2 = kelly.from_binary(win_prob=0.55, win_return=0.10, loss_return=0.08)
k3 = kelly.multi_asset([pull.ticker(t) for t in ["AAPL","MSFT","GOOGL"]])
Portfolio — finverse.portfolio
| Module | Method | What it does |
|---|---|---|
portfolio.optimizer |
Mean-variance | Max Sharpe, min vol, risk parity, equal weight, efficient frontier |
portfolio.hrp |
Hierarchical clustering | Hierarchical Risk Parity — no matrix inversion, more stable |
portfolio.shrinkage |
Ledoit-Wolf | Shrinks covariance toward constant correlation target |
portfolio.black_litterman |
Bayesian updating | Blends CAPM equilibrium with analyst views |
portfolio.cvar_opt |
Linear programming | Minimises CVaR directly — tail-risk optimal weights |
from finverse.portfolio import optimizer, hrp, shrinkage
from finverse.portfolio.black_litterman import optimize as bl_optimize, BLView
from finverse.portfolio.cvar_opt import optimize as cvar_optimize
stocks = [pull.ticker(t) for t in ["AAPL", "MSFT", "GOOGL", "JPM", "XOM"]]
# Standard MVO
optimizer.optimize(stocks, method="max_sharpe").summary()
optimizer.optimize(stocks, method="min_vol").summary()
optimizer.optimize(stocks, method="risk_parity").summary()
# HRP — no matrix inversion, works with correlated assets
hrp.optimize(stocks).summary()
hrp.optimize(stocks).compare_to_equal_weight()
# Ledoit-Wolf shrinkage
cov = shrinkage.shrink(stocks, method="constant_correlation")
cov.summary()
# Black-Litterman — combine equilibrium with views
views = [
BLView(["AAPL"], [1.0], expected_ret=0.15, confidence=0.8),
# "AAPL returns 15%, high confidence"
BLView(["MSFT", "GOOGL"], [1.0, -1.0], expected_ret=0.03, confidence=0.6),
# "MSFT outperforms GOOGL by 3%"
]
bl = bl_optimize(stocks, views=views)
bl.summary()
# → posterior returns, how much views shifted each asset, optimal weights
# CVaR optimization — minimises tail loss via linear programming
cvar = cvar_optimize(stocks, confidence=0.95)
cvar.summary()
# → weights that minimise expected loss in worst 5% of days
Credit — finverse.credit
from finverse.credit import merton, altman
from finverse.ml import garch
# Merton structural credit model
vol = garch.fit(data)
r = merton.analyze(data, garch_vol=vol.current_vol)
r.summary()
# → asset value, asset vol, distance-to-default (σ)
# PD(1y), PD(5y), implied credit spread (bps), rating (AAA → D)
# Altman Z-Score
r = altman.analyze(data) # auto-selects variant
r = altman.analyze(data, model="Z-Score") # public manufacturers
r = altman.analyze(data, model="Z'-Score") # private companies
r = altman.analyze(data, model="Z''-Score") # non-manufacturers
r.summary()
# → score, zone (safe / grey / distress), component ratios
Audit — finverse.audit
| Module | Method | What it does |
|---|---|---|
audit() |
Rule-based | Model health check: bad assumptions, broken logic, 0–100 score |
audit.manipulation |
40-signal Random Forest | Accounting manipulation probability, Beneish M-Score, top drivers |
audit.earnings_quality |
10-factor composite | Accruals, OCF/NI, persistence, smoothness, FCF — A–F grade |
audit.benford |
Chi-square + MAD | Benford's Law test for number manipulation |
audit.loughran_mcdonald |
LM dictionary | Financial text: negative, uncertainty, litigious, modal scores |
from finverse import audit
from finverse.audit import manipulation, earnings_quality, benford, loughran_mcdonald
# Model health check
audit(model).summary()
# → score, errors, warnings, specific fix suggestions
# Manipulation fingerprint — 40+ signals, not just Beneish
result = manipulation.fingerprint(data)
result.summary()
# → probability 0–1, risk level, top 8 risk drivers with scores
# e.g. "AR growth exceeding revenue growth (0.18), SGA inflation (0.14)"
# Earnings quality
earnings_quality.score(data).summary()
# → 0–100 score, A–F grade, 10 signals scored individually
# Benford's Law
benford.test_financials(data).summary()
# → MAD, chi-square p-value, conformity, flagged digits
# LM sentiment on filing text
result = loughran_mcdonald.analyze(filing_text, source="AAPL 10-K 2024")
result.summary()
loughran_mcdonald.compare_filings({"2022": t1, "2023": t2, "2024": t3})
Macro — finverse.macro
from finverse.macro import var_model, nelson_siegel
from finverse.models.macro import nowcast
# Macro nowcast
result = nowcast()
result.summary()
# → GDP nowcast (%), recession probability (12M), yield curve signal,
# regime, 4-quarter inflation path, 4-quarter fed rate path
# VAR with impulse response functions
macro = pull.fred("UNRATE", "FEDFUNDS", "CPIAUCSL")
quarterly = macro.resample("QE").last().pct_change().dropna()
result = var_model.fit(quarterly, n_lags=2, forecast_horizon=8)
result.summary()
result.plot_irf("FEDFUNDS") # shock to fed funds
result.irf("FEDFUNDS", "UNRATE") # specific pair
var_model.select_lag_order(quarterly, max_lags=6)
# Nelson-Siegel yield curve
curve = nelson_siegel.us_curve()
curve.summary()
print(f"10Y yield: {curve.yield_at(10):.3%}")
print(f"5Y forward rate: {curve.forward_rate(5):.3%}")
print(f"Level (β₀): {curve.level:.3%}")
print(f"Slope (β₁): {curve.slope:.3%}")
curve.curve().plot()
# Svensson (two humps)
curve = nelson_siegel.fit(maturities, yields, model="Svensson")
Analysis — finverse.analysis
from finverse import sensitivity, scenarios
from finverse.screen import screener
from finverse import backtest
# 2-variable sensitivity heatmap
sensitivity(model, rows="wacc", cols="terminal_growth")
sensitivity(model, rows="ebitda_margin", cols="revenue_growth", n=7)
# Bull / base / bear scenarios
scenarios(model,
bull={"wacc": 0.085, "ebitda_margin": 0.36, "revenue_growth": 0.12},
base={"wacc": 0.095, "ebitda_margin": 0.32, "revenue_growth": 0.08},
bear={"wacc": 0.115, "ebitda_margin": 0.26, "revenue_growth": 0.03},
)
# ML stock screener
screener.undervalued(sector="tech").summary()
screener.by_criteria(["AAPL","MSFT","GOOGL"], min_revenue_growth=0.05, max_pe=40)
# Signal backtesting
prices = data.price_history["Close"]
signal = prices.pct_change(63).shift(1)
result = backtest.run(signal, prices, "3M Momentum")
result.summary()
result.plot()
backtest.dcf_signal(model, data).summary()
Export — finverse.export
from finverse.export import to_excel, to_report
to_excel(model, "aapl_dcf.xlsx")
# → banker-formatted Excel: blue = formulas, green = outputs, gray = headers
to_report(model, "aapl_summary.txt")
# → plain text one-pager with all assumptions and outputs
Full module list
finverse/
├── pull/ ticker, edgar, fred
├── models/ dcf, lbo, three_statement, comps, regime_dcf,
│ synthetic_peers, ddm, sotp, options, bonds,
│ real_options, apv, macro
├── ml/ forecast, cross_sectional, garch, factor,
│ regime, nlp, cluster, anomaly, causal
├── risk/ monte_carlo, var, evt, kelly
├── portfolio/ optimizer, hrp, shrinkage,
│ black_litterman, cvar_opt
├── credit/ merton, altman
├── valuation/ real_options, apv
├── macro/ var_model, nelson_siegel
├── audit/ model_audit, manipulation, earnings_quality,
│ benford, loughran_mcdonald
├── analysis/ sensitivity, scenarios
├── screen/ screener
├── backtest/ engine
└── export/ excel, report
Install
pip install finverse
pip install finverse[full] # seaborn, hmmlearn, reportlab
pip install finverse[dev] # pytest, black, ruff, mypy
Requirements: Python 3.9+, numpy, pandas, scikit-learn, scipy, xgboost, yfinance, rich, openpyxl, matplotlib
FRED API key
Only needed for pull.fred() and pull.macro_snapshot(). All other features work without any keys.
export FRED_API_KEY=your_key_here
Free at fred.stlouisfed.org — no credit card, 30 seconds.
Tests
pip install finverse[dev]
pytest tests/ -v
All tests use synthetic data — no network calls, no API keys required. Tested against Python 3.9–3.12.
Contributing
See CONTRIBUTING.md.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file finverse-0.8.0.tar.gz.
File metadata
- Download URL: finverse-0.8.0.tar.gz
- Upload date:
- Size: 184.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
10539aab40446de880491b3de0f3627da47fda896abefb21d159da23ea28ebef
|
|
| MD5 |
b516e474016edb6cc4bc12625796ba91
|
|
| BLAKE2b-256 |
ecac1a36ff0842c1f331d378bb35c157a16d6bcf7b286caf1e2332d0b47a095d
|
Provenance
The following attestation bundles were made for finverse-0.8.0.tar.gz:
Publisher:
publish.yml on Nityahapani/finverse
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
finverse-0.8.0.tar.gz -
Subject digest:
10539aab40446de880491b3de0f3627da47fda896abefb21d159da23ea28ebef - Sigstore transparency entry: 1171788776
- Sigstore integration time:
-
Permalink:
Nityahapani/finverse@a8edd811faba946c9e7bbbffffb63dbc8ed9a5ea -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/Nityahapani
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a8edd811faba946c9e7bbbffffb63dbc8ed9a5ea -
Trigger Event:
push
-
Statement type:
File details
Details for the file finverse-0.8.0-py3-none-any.whl.
File metadata
- Download URL: finverse-0.8.0-py3-none-any.whl
- Upload date:
- Size: 209.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
591cfac40550b9db47eb2e3284133f5e70a20db77d8f7a0d2b6df6e808c5eb2a
|
|
| MD5 |
e000dd7159674dc5e8b559a4c3f2cf46
|
|
| BLAKE2b-256 |
4cb7c3c3ab9161d066711d02770fc44d11c9806e2c9c7b0f52083db6df56d5f0
|
Provenance
The following attestation bundles were made for finverse-0.8.0-py3-none-any.whl:
Publisher:
publish.yml on Nityahapani/finverse
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
finverse-0.8.0-py3-none-any.whl -
Subject digest:
591cfac40550b9db47eb2e3284133f5e70a20db77d8f7a0d2b6df6e808c5eb2a - Sigstore transparency entry: 1171788805
- Sigstore integration time:
-
Permalink:
Nityahapani/finverse@a8edd811faba946c9e7bbbffffb63dbc8ed9a5ea -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/Nityahapani
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a8edd811faba946c9e7bbbffffb63dbc8ed9a5ea -
Trigger Event:
push
-
Statement type: