fin-ratios 0.7.3 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,779 @@
1
+ # fin-ratios
2
+
3
+ **The most comprehensive open-source financial ratios library.**
4
+
5
+ 136+ ratios · 10 institutional-grade scoring models · TypeScript + Python.
6
+ Caching, REST API, MCP server, React hooks, Pandas/Polars. Zero runtime core dependencies.
7
+
8
+ [![npm](https://img.shields.io/npm/v/fin-ratios)](https://npmjs.com/package/fin-ratios)
9
+ [![PyPI](https://img.shields.io/pypi/v/financial-ratios)](https://pypi.org/project/financial-ratios/)
10
+ [![CI](https://github.com/piyushgupta344/fin-ratios/actions/workflows/ci.yml/badge.svg)](https://github.com/piyushgupta344/fin-ratios/actions/workflows/ci.yml)
11
+ [![Docs](https://img.shields.io/badge/docs-piyushgupta344.github.io-blue)](https://piyushgupta344.github.io/fin-ratios/)
12
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
13
+
14
+ ---
15
+
16
+ ## Why fin-ratios?
17
+
18
+ Existing libraries either fetch data or compute 10–20 basic ratios.
19
+ **fin-ratios** does both and goes much further:
20
+
21
+ | Feature | fin-ratios | Competitors |
22
+ |---------|-----------|-------------|
23
+ | Ratio count | **136+** | 10–30 |
24
+ | Composite scores (Piotroski, Beneish, Altman, Montier, Shiller) | ✅ | Rarely |
25
+ | Risk/portfolio ratios (Sharpe, CVaR, Omega) | ✅ | No |
26
+ | SaaS metrics (Rule of 40, NRR, Burn Multiple) | ✅ | No |
27
+ | REIT / Banking / Insurance ratios | ✅ | No |
28
+ | Intrinsic value models (DCF, Reverse DCF, Graham) | ✅ | No |
29
+ | **Economic Moat Score** (formula-based, open-source first) | ✅ | No |
30
+ | **Capital Allocation Quality Score** | ✅ | No |
31
+ | **Earnings Quality Score** (accruals, cash backing, stability) | ✅ | No |
32
+ | **Fair Value Range** (5-method bear/base/bull) | ✅ | No |
33
+ | **Quality Factor Score** (QMJ composite) | ✅ | No |
34
+ | **Portfolio Quality Aggregation** (weighted scoring) | ✅ | No |
35
+ | **Valuation Attractiveness Score** (earnings yield, FCF yield, EV/EBITDA) | ✅ | No |
36
+ | **Management Quality Score** (ROIC, margins, dilution, execution) | ✅ | No |
37
+ | **Dividend Safety Score** (FCF payout, debt, track record) | ✅ | No |
38
+ | **Investment Score** (grand synthesis of all models) | ✅ | No |
39
+ | Backtesting scoring strategies against historical data | ✅ | No |
40
+ | Batch compute all ratios from one object | ✅ | No |
41
+ | Historical ratio trends + linear regression | ✅ | No |
42
+ | Scenario DCF (bull/base/bear) | ✅ | No |
43
+ | Peer comparison with percentile ranking | ✅ | No |
44
+ | Sector benchmarks (11 GICS sectors + score distributions) | ✅ | No |
45
+ | Caching layer (memory/disk/Redis) | ✅ | No |
46
+ | Pandas + Polars DataFrame integration | ✅ | No |
47
+ | Jupyter notebook rich display | ✅ | No |
48
+ | FastAPI REST API (6 endpoints) | ✅ | No |
49
+ | MCP server for AI agents (Claude Desktop) | ✅ | No |
50
+ | React hooks (`useRatios`, `useHealthScore`) | ✅ | No |
51
+ | Formula on every function | ✅ | No |
52
+ | Academic citations for every ratio | ✅ | No |
53
+ | TypeScript + Python | ✅ | Usually one |
54
+ | Zero core dependencies | ✅ | Usually no |
55
+
56
+ ---
57
+
58
+ ## What's New in v1.0.0
59
+
60
+ **v1.0.0 — Documentation site, full scoring suite, release automation**
61
+ - MkDocs Material documentation site at [piyushgupta344.github.io/fin-ratios](https://piyushgupta344.github.io/fin-ratios/)
62
+ - Polygon.io fetcher (Python + TypeScript)
63
+ - CLI `score` subcommand — full scoring dashboard in terminal
64
+ - 575 tests passing (410 Python × 5 versions + 165 TypeScript × 3 Node versions)
65
+ - Automated release pipeline with PyPI trusted publishing and npm provenance
66
+
67
+ **v0.8 — Valuation, Management, Dividend Safety & Investment Scores**
68
+ - `valuationAttractivenessScore()` / `valuation_attractiveness_score()` — 5-signal valuation model: earnings yield spread, FCF yield, EV/EBITDA, P/B, DCF upside. Score 0–100, rated attractive/fair/expensive/overvalued
69
+ - `managementQualityScoreFromSeries()` / `management_quality_score_from_series()` — 4-signal management quality: ROIC excellence, margin stability, shareholder orientation, revenue execution
70
+ - `dividendSafetyScoreFromSeries()` / `dividend_safety_score_from_series()` — 4-signal dividend safety: FCF payout, earnings payout, balance sheet strength, dividend growth track
71
+ - `investmentScoreFromSeries()` / `investment_score_from_series()` — grand synthesis of all scoring models into a single 0–100 investment score with letter grade (A+/A/B+/B/C/D/F) and conviction (strong buy to strong sell)
72
+
73
+ **v0.7 — Quality Factor Score + Portfolio Quality**
74
+ - `qualityScore()` / `quality_score()` — composite 0–100 score combining Earnings Quality (35%), Moat Score (35%), Capital Allocation (30%). Maps to the QMJ factor (Asness et al. 2019)
75
+ - `portfolioQuality()` / `portfolio_quality()` — weighted quality analysis across a portfolio of holdings
76
+ - 89-test TypeScript test suite covering all scoring utilities and core ratios
77
+ - Sector benchmarks expanded with score distributions for all 11 GICS sectors
78
+
79
+ **v0.6 — Fair Value Range**
80
+ - `fairValueRange()` / `fair_value_range()` — up to five valuation methods (DCF, Graham Number, FCF Yield, EV/EBITDA, EPV) returning bear (p25), base (trimmed mean), bull (p75) per-share estimates
81
+
82
+ **v0.5 — Earnings Quality Score**
83
+ - `earningsQualityScore()` / `earnings_quality_score()` — 5-signal framework: accruals ratio, cash earnings quality, revenue recognition, gross margin stability, asset efficiency trend
84
+
85
+ **v0.4 — Capital Allocation Quality Score**
86
+ - `capitalAllocationScore()` / `capital_allocation_score()` — 4-signal framework: value creation (ROIC vs WACC), FCF quality, reinvestment yield, payout discipline
87
+
88
+ **v0.3 — Quantitative Economic Moat Score**
89
+ - `moatScore()` / `moat_score()` — first open-source formula-based economic moat score derived entirely from financial statements. Five signals with academic grounding.
90
+
91
+ ---
92
+
93
+ ## Installation
94
+
95
+ ```bash
96
+ # TypeScript / JavaScript
97
+ npm install fin-ratios
98
+
99
+ # Python (core only — zero dependencies)
100
+ pip install financial-ratios
101
+
102
+ # Python with data fetchers (yfinance, httpx, requests)
103
+ pip install "financial-ratios[fetchers]"
104
+
105
+ # Python with REST API server (FastAPI + uvicorn)
106
+ pip install "financial-ratios[api]"
107
+
108
+ # Python with MCP server for AI agents
109
+ pip install "financial-ratios[mcp]"
110
+
111
+ # Python with Pandas/Polars DataFrame integration
112
+ pip install "financial-ratios[pandas]"
113
+
114
+ # Python with everything
115
+ pip install "financial-ratios[all]"
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Quick Start
121
+
122
+ ### TypeScript — Core Ratios
123
+ ```typescript
124
+ import {
125
+ pe, evEbitda, peg, pb,
126
+ roic, nopat, investedCapital,
127
+ sharpeRatio, conditionalVaR,
128
+ piotroskiFScore, altmanZScore, beneishMScore,
129
+ ruleOf40, netRevenueRetention, burnMultiple,
130
+ dcf2Stage, reverseDcf, grahamNumber,
131
+ } from 'fin-ratios'
132
+
133
+ // Valuation
134
+ const peRatio = pe({ marketCap: 3_000_000_000_000, netIncome: 100_000_000_000 })
135
+ // => 30
136
+
137
+ const ev = evEbitda({ enterpriseValue: 3_060_000_000_000, ebitda: 130_000_000_000 })
138
+ // => 23.5
139
+
140
+ // Access formula on any function
141
+ console.log(pe.formula) // "Market Capitalization / Net Income"
142
+ console.log(evEbitda.formula) // "Enterprise Value / EBITDA"
143
+
144
+ // ROIC (value creation test)
145
+ const ic = investedCapital({ totalEquity: 74e9, totalDebt: 110e9, cash: 62e9 })
146
+ const nopatVal = nopat({ ebit: 120e9, taxRate: 0.15 })
147
+ const roicVal = roic({ nopat: nopatVal, investedCapital: ic })
148
+ // => 0.99 (99% ROIC)
149
+
150
+ // Piotroski F-Score (0-9)
151
+ const score = piotroskiFScore({
152
+ current: { netIncome: 8e6, totalAssets: 100e6, operatingCashFlow: 12e6,
153
+ longTermDebt: 20e6, currentAssets: 35e6, currentLiabilities: 15e6,
154
+ sharesOutstanding: 10e6, grossProfit: 45e6, revenue: 90e6 },
155
+ prior: { netIncome: 5e6, totalAssets: 95e6, longTermDebt: 25e6,
156
+ currentAssets: 28e6, currentLiabilities: 14e6, sharesOutstanding: 10.5e6,
157
+ grossProfit: 38e6, revenue: 80e6 },
158
+ })
159
+ console.log(`F-Score: ${score.score}/9 — ${score.interpretation}`)
160
+ ```
161
+
162
+ ### TypeScript — Scoring Utilities (v0.3–v0.7)
163
+
164
+ ```typescript
165
+ import { moatScore, capitalAllocationScore, earningsQualityScore,
166
+ fairValueRange, qualityScore, portfolioQuality } from 'fin-ratios'
167
+
168
+ // Economic Moat Score
169
+ const moat = moatScore(annualData)
170
+ // => { score: 68, width: 'wide', components: { roicPersistence: 72, ... } }
171
+
172
+ // Capital Allocation Score
173
+ const ca = capitalAllocationScore(annualData)
174
+ // => { score: 74, rating: 'good', components: { valueCreation: 81, ... } }
175
+
176
+ // Earnings Quality Score
177
+ const eq = earningsQualityScore(annualData)
178
+ // => { score: 66, rating: 'medium', components: { accrualsRatio: 58, ... } }
179
+
180
+ // Fair Value Range (bear / base / bull per share)
181
+ const fv = fairValueRange({
182
+ fcf: 110e9, shares: 15.4e9, growthRate: 0.10, terminalGrowth: 0.03,
183
+ wacc: 0.09, dcfYears: 10, eps: 6.5, bvps: 4.8, targetYield: 0.04,
184
+ ebitda: 130e9, totalDebt: 120e9, cash: 62e9, evEbitdaMultiple: 20,
185
+ ebit: 112e9, taxRate: 0.15, currentPrice: 185,
186
+ })
187
+ console.log(`Bear: $${fv.bear.toFixed(0)} Base: $${fv.base.toFixed(0)} Bull: $${fv.bull.toFixed(0)}`)
188
+ console.log(`Margin of safety: ${(fv.marginOfSafety! * 100).toFixed(1)}%`)
189
+
190
+ // Quality Factor Score (combines EQ + Moat + Capital Allocation)
191
+ const qs = qualityScore(annualData)
192
+ // => { score: 69, grade: 'strong', components: { earningsQuality: 0.66, ... } }
193
+
194
+ // Portfolio Quality
195
+ const portfolio = portfolioQuality([
196
+ { ticker: 'AAPL', weight: 0.40, annualData: appleData },
197
+ { ticker: 'MSFT', weight: 0.35, annualData: msftData },
198
+ { ticker: 'GOOGL', weight: 0.25, annualData: googlData },
199
+ ])
200
+ console.log(`Portfolio quality: ${portfolio.weightedScore.toFixed(0)}/100`)
201
+ console.log(`Top holding: ${portfolio.topHolding}`)
202
+ ```
203
+
204
+ ### TypeScript — Batch Compute
205
+ ```typescript
206
+ import { computeAll } from 'fin-ratios'
207
+ import { fetchYahoo } from 'fin-ratios/fetchers/yahoo'
208
+
209
+ const data = await fetchYahoo('AAPL')
210
+ const ratios = computeAll(data)
211
+
212
+ console.log(ratios.pe) // 28.3
213
+ console.log(ratios.roic) // 0.55
214
+ console.log(ratios.grossMargin) // 0.433
215
+ console.log(ratios.altmanZ?.zone) // 'safe'
216
+ console.log(ratios.piotroski?.score) // 7
217
+ ```
218
+
219
+ ### TypeScript — Scenario DCF
220
+ ```typescript
221
+ import { scenarioDcf } from 'fin-ratios'
222
+
223
+ const result = scenarioDcf({
224
+ baseFcf: 100e9,
225
+ sharesOutstanding: 15.7e9,
226
+ currentPrice: 185,
227
+ scenarios: {
228
+ bear: { growthRate: 0.05, wacc: 0.12, terminalGrowth: 0.02, years: 10 },
229
+ base: { growthRate: 0.10, wacc: 0.10, terminalGrowth: 0.03, years: 10 },
230
+ bull: { growthRate: 0.18, wacc: 0.09, terminalGrowth: 0.04, years: 10 },
231
+ },
232
+ })
233
+ console.log(result.base.intrinsicValuePerShare) // 198.4
234
+ console.log(result.base.upsidePct) // 0.074 (7.4% upside)
235
+ ```
236
+
237
+ ### TypeScript — React Hooks
238
+ ```typescript
239
+ import { useRatios, useHealthScore, useCompareRatios } from 'fin-ratios/hooks'
240
+ import { fetchYahoo } from 'fin-ratios/fetchers/yahoo'
241
+
242
+ function StockCard({ ticker }: { ticker: string }) {
243
+ const { data, loading, error } = useRatios(ticker, fetchYahoo)
244
+ if (loading) return <div>Loading...</div>
245
+ if (error) return <div>Error: {error}</div>
246
+ return (
247
+ <div>
248
+ <div>P/E: {data?.pe?.toFixed(1)}</div>
249
+ <div>ROIC: {(data?.roic ?? 0 * 100).toFixed(1)}%</div>
250
+ <div>Gross Margin: {(data?.grossMargin ?? 0 * 100).toFixed(1)}%</div>
251
+ </div>
252
+ )
253
+ }
254
+ ```
255
+
256
+ ---
257
+
258
+ ### Python — Core Ratios
259
+ ```python
260
+ from fin_ratios import (
261
+ pe, ev_ebitda, graham_number,
262
+ roic, nopat as compute_nopat, invested_capital,
263
+ sharpe_ratio, conditional_var,
264
+ piotroski_f_score, altman_z_score, beneish_m_score,
265
+ rule_of_40, net_revenue_retention, burn_multiple,
266
+ dcf_2_stage, reverse_dcf,
267
+ )
268
+
269
+ # Valuation
270
+ ratio = pe(market_cap=3e12, net_income=100e9)
271
+ # => 30.0
272
+
273
+ # Access formula and description
274
+ print(pe.formula) # "Market Capitalization / Net Income"
275
+ print(pe.description) # "How much investors pay per $1 of earnings."
276
+
277
+ # Altman Z-Score
278
+ z = altman_z_score(
279
+ working_capital=50e9, retained_earnings=200e9, ebit=90e9,
280
+ market_cap=3000e9, total_liabilities=210e9,
281
+ total_assets=411e9, revenue=212e9,
282
+ )
283
+ print(z["interpretation"]) # "Z-Score 4.82: Safe zone — Low bankruptcy risk"
284
+ ```
285
+
286
+ ### Python — Scoring Utilities (v0.3–v0.7)
287
+
288
+ ```python
289
+ from fin_ratios import (
290
+ moat_score_from_series, capital_allocation_score_from_series,
291
+ earnings_quality_score_from_series,
292
+ fair_value_range, quality_score_from_series,
293
+ portfolio_quality_from_series,
294
+ )
295
+
296
+ # All *_from_series functions accept a list of annual dicts (oldest → newest)
297
+ annual_data = [
298
+ {'revenue': 200e9, 'ebit': 40e9, 'total_assets': 300e9, 'total_equity': 80e9,
299
+ 'operating_cash_flow': 50e9, 'capex': 10e9, 'net_income': 30e9,
300
+ 'total_debt': 60e9, 'cash': 25e9, 'gross_profit': 90e9},
301
+ # ... more years (oldest first) ...
302
+ ]
303
+
304
+ # Economic Moat Score
305
+ moat = moat_score_from_series(annual_data)
306
+ print(f"Moat: {moat.score}/100 ({moat.width})") # "Moat: 68/100 (wide)"
307
+ print(moat.table())
308
+
309
+ # Capital Allocation Score
310
+ ca = capital_allocation_score_from_series(annual_data)
311
+ print(f"Capital Allocation: {ca.score}/100 ({ca.rating})") # "Capital Allocation: 74/100 (good)"
312
+
313
+ # Earnings Quality Score
314
+ eq = earnings_quality_score_from_series(annual_data)
315
+ print(f"Earnings Quality: {eq.score}/100 ({eq.rating})") # "Earnings Quality: 66/100 (medium)"
316
+
317
+ # Fair Value Range
318
+ fv = fair_value_range(
319
+ fcf=110e9, shares=15.4e9, growth_rate=0.10, terminal_growth=0.03,
320
+ wacc=0.09, dcf_years=10, eps=6.50, bvps=4.80, target_yield=0.04,
321
+ ebitda=130e9, total_debt=120e9, cash=62e9, ev_ebitda_multiple=20,
322
+ ebit=112e9, tax_rate=0.15, current_price=185.0,
323
+ )
324
+ print(f"Bear: ${fv.bear:.0f} Base: ${fv.base:.0f} Bull: ${fv.bull:.0f}")
325
+ print(f"Margin of safety: {fv.margin_of_safety:.1%}")
326
+ print(fv.table())
327
+
328
+ # Quality Factor Score (EQ 35% + Moat 35% + Capital Allocation 30%)
329
+ qs = quality_score_from_series(annual_data)
330
+ print(f"Quality: {qs.score}/100 ({qs.grade})") # "Quality: 69/100 (strong)"
331
+
332
+ # Portfolio Quality
333
+ holdings_data = {
334
+ 'AAPL': (0.40, apple_annual_data), # (weight, annual_data_list)
335
+ 'MSFT': (0.35, msft_annual_data),
336
+ 'GOOGL': (0.25, googl_annual_data),
337
+ }
338
+ portfolio = portfolio_quality_from_series(holdings_data)
339
+ print(f"Portfolio quality score: {portfolio.weighted_score:.0f}/100")
340
+ for h in portfolio.holdings:
341
+ print(f" {h.ticker}: {h.quality_score:.0f}/100 (weight: {h.weight:.0%})")
342
+ ```
343
+
344
+ ### Python — Convenience Wrappers (auto-fetch)
345
+
346
+ All scoring utilities have a single-ticker wrapper that fetches data automatically:
347
+
348
+ ```python
349
+ from fin_ratios import moat_score, capital_allocation_score, earnings_quality_score
350
+ from fin_ratios.utils import quality_score, portfolio_quality
351
+
352
+ # Fetches 10 years of data from Yahoo Finance automatically
353
+ moat = moat_score('AAPL')
354
+ ca = capital_allocation_score('AAPL')
355
+ eq = earnings_quality_score('AAPL')
356
+ qs = quality_score('AAPL')
357
+
358
+ # Jupyter — renders as color-coded HTML table
359
+ moat # _repr_html_() called automatically
360
+
361
+ # Portfolio (fetches each holding)
362
+ portfolio = portfolio_quality({'AAPL': 0.40, 'MSFT': 0.35, 'GOOGL': 0.25})
363
+ ```
364
+
365
+ ### Python — Batch Compute
366
+ ```python
367
+ from fin_ratios.utils import compute_all
368
+ from fin_ratios.fetchers.yahoo import fetch_yahoo
369
+
370
+ data = fetch_yahoo('AAPL')
371
+ ratios = compute_all(data)
372
+
373
+ print(f"P/E: {ratios['pe']:.1f}")
374
+ print(f"ROIC: {ratios['roic']:.1%}")
375
+ print(f"Gross Margin: {ratios['gross_margin']:.1%}")
376
+ print(f"Altman Zone: {ratios['altman_z']['zone']}")
377
+ ```
378
+
379
+ ### Python — Ratio History & Trends
380
+ ```python
381
+ from fin_ratios.utils import ratio_history
382
+
383
+ history = ratio_history('AAPL', metrics=['roic', 'gross_margin', 'pe'], years=5)
384
+
385
+ print(history.trend('roic')) # 'improving'
386
+ print(history.trend('gross_margin')) # 'stable'
387
+ print(f"ROIC CAGR: {history.cagr('roic'):.1%}")
388
+ print(history.table())
389
+ ```
390
+
391
+ ### Python — Scenario DCF
392
+ ```python
393
+ from fin_ratios.utils import scenario_dcf
394
+
395
+ result = scenario_dcf(
396
+ base_fcf=100e9,
397
+ shares_outstanding=15.7e9,
398
+ current_price=185.0,
399
+ scenarios={
400
+ 'bear': {'growth_rate': 0.05, 'wacc': 0.12, 'terminal_growth': 0.02, 'years': 10},
401
+ 'base': {'growth_rate': 0.10, 'wacc': 0.10, 'terminal_growth': 0.03, 'years': 10},
402
+ 'bull': {'growth_rate': 0.18, 'wacc': 0.09, 'terminal_growth': 0.04, 'years': 10},
403
+ },
404
+ )
405
+ print(result.table())
406
+ # Scenario IV/Share Upside
407
+ # bear $142.30 -23.1%
408
+ # base $198.40 +7.2%
409
+ # bull $287.60 +55.5%
410
+ ```
411
+
412
+ ### Python — Sector Benchmarks & Percentile Ranking
413
+ ```python
414
+ from fin_ratios.utils.benchmarks import sector_benchmarks, percentile_rank
415
+
416
+ b = sector_benchmarks("Technology")
417
+ print(b.roic_median) # 0.20 (20%)
418
+ print(b.moat_score_median) # 52
419
+ print(b.quality_score_median) # 55
420
+
421
+ # Percentile rank a value against peers
422
+ rank = percentile_rank("Technology", "roic", 0.55)
423
+ print(f"ROIC of 55% is in the {rank:.0f}th percentile for Tech") # => 97th
424
+
425
+ # Works with all new scoring metrics too
426
+ moat_rank = percentile_rank("Technology", "moat_score", 68)
427
+ print(f"Moat score of 68 is in the {moat_rank:.0f}th percentile") # => 83rd
428
+ ```
429
+
430
+ ### Python — Caching
431
+ ```python
432
+ from fin_ratios.cache import set_cache, invalidate, clear_cache
433
+
434
+ set_cache('memory') # in-process (default)
435
+ set_cache('disk', ttl_hours=24) # JSON files in ~/.fin_ratios_cache/
436
+ set_cache('redis', ttl_hours=1, url='redis://localhost:6379')
437
+
438
+ # Fetchers use the cache automatically
439
+ from fin_ratios.fetchers.yahoo import fetch_yahoo
440
+ data = fetch_yahoo('AAPL') # live fetch
441
+ data = fetch_yahoo('AAPL') # served from cache
442
+
443
+ invalidate('AAPL') # clear one ticker
444
+ clear_cache() # clear everything
445
+ ```
446
+
447
+ ### Python — REST API
448
+ ```bash
449
+ pip install "financial-ratios[api]"
450
+ fin-ratios api --port 8000
451
+ # OpenAPI docs at http://localhost:8000/docs
452
+ ```
453
+
454
+ ```
455
+ GET /ratios/{ticker} — all 40+ ratios
456
+ GET /ratios/{ticker}/{ratio} — single ratio
457
+ GET /health/{ticker} — health score (0-100)
458
+ GET /history/{ticker} — 5-year ratio trends
459
+ GET /peers/{ticker} — peer comparison
460
+ GET /screen?tickers=AAPL,MSFT&roic_gt=0.15&pe_lt=30 — screening
461
+ ```
462
+
463
+ ### Python — MCP Server for AI Agents
464
+ ```bash
465
+ pip install "financial-ratios[mcp]"
466
+ fin-ratios serve
467
+ ```
468
+
469
+ Add to your Claude Desktop `claude_desktop_config.json`:
470
+ ```json
471
+ {
472
+ "mcpServers": {
473
+ "fin-ratios": {
474
+ "command": "fin-ratios",
475
+ "args": ["serve"]
476
+ }
477
+ }
478
+ }
479
+ ```
480
+
481
+ Available tools for Claude:
482
+ - `analyze_ticker` — all ratios for a ticker
483
+ - `health_score` — financial health score (0–100)
484
+ - `ratio_history` — multi-year trends
485
+ - `compare_peers` — peer comparison
486
+ - `screen_stocks` — filter by ratio thresholds
487
+ - `compute_ratio` — compute a specific ratio from raw inputs
488
+
489
+ ---
490
+
491
+ ## Data Fetchers (Optional)
492
+
493
+ Fetchers are separate optional modules — zero network code in the core.
494
+
495
+ ### Yahoo Finance (free, no API key)
496
+ ```python
497
+ from fin_ratios.fetchers.yahoo import fetch_yahoo
498
+ data = fetch_yahoo('AAPL')
499
+ ```
500
+ ```typescript
501
+ import { fetchYahoo } from 'fin-ratios/fetchers/yahoo'
502
+ const data = await fetchYahoo('AAPL')
503
+ ```
504
+
505
+ ### SEC EDGAR (free, no API key, US companies only)
506
+ ```python
507
+ from fin_ratios.fetchers.edgar import fetch_edgar
508
+ filings = fetch_edgar('AAPL', num_years=3) # newest-first list
509
+ ```
510
+ ```typescript
511
+ import { fetchEdgar, fetchEdgarFlat } from 'fin-ratios/fetchers/edgar'
512
+ // fetchEdgarFlat returns oldest-first flat records, ready for scoring utilities
513
+ const annualData = await fetchEdgarFlat('AAPL')
514
+ ```
515
+
516
+ ### Financial Modeling Prep (free tier: 250 req/day)
517
+ ```python
518
+ from fin_ratios.fetchers.fmp import fetch_fmp
519
+ data = fetch_fmp('AAPL', api_key='your_key', periods=4)
520
+ ```
521
+
522
+ ### SimFin (free tier: 500 req/day)
523
+ ```python
524
+ from fin_ratios.fetchers.simfin import fetch_simfin, set_api_key
525
+ set_api_key('your_simfin_key') # or SIMFIN_API_KEY env var
526
+ data = fetch_simfin('AAPL')
527
+ ```
528
+
529
+ ---
530
+
531
+ ## S&P 500 Bulk Analysis
532
+
533
+ ```bash
534
+ pip install "financial-ratios[fetchers]" pandas
535
+ cd python
536
+ python scripts/sp500_analysis.py # All 503 companies (~30-45 min)
537
+ python scripts/sp500_analysis.py --sample 50 # Quick test with 50 companies
538
+ ```
539
+
540
+ | Output file | Contents |
541
+ |-------------|----------|
542
+ | `sp500_ratios.csv` | All 30+ ratios for every company |
543
+ | `sp500_top_piotroski.csv` | Top 20 companies by F-Score |
544
+ | `sp500_distressed.csv` | Companies in Altman distress zone |
545
+ | `sp500_manipulation_flags.csv` | Companies flagged by Beneish M-Score |
546
+
547
+ ---
548
+
549
+ ## Full Ratio Catalog
550
+
551
+ ### Valuation (18)
552
+ | Ratio | Formula | Key Reference |
553
+ |-------|---------|--------------|
554
+ | P/E (Trailing) | Market Cap / Net Income | Graham & Dodd (1934) |
555
+ | P/E (Forward) | Price / Forward EPS | — |
556
+ | PEG Ratio | P/E / EPS Growth % | Lynch (1989) |
557
+ | P/B | Market Cap / Total Equity | Graham & Dodd (1934) |
558
+ | P/S | Market Cap / Revenue | — |
559
+ | P/FCF | Market Cap / FCF | — |
560
+ | EV/EBITDA | EV / EBITDA | Koller et al. (2020) |
561
+ | EV/EBIT | EV / EBIT | — |
562
+ | EV/Revenue | EV / Revenue | — |
563
+ | EV/FCF | EV / FCF | — |
564
+ | EV/Invested Capital | EV / IC | — |
565
+ | Tobin's Q | (MC + Debt) / Assets | Tobin (1969) |
566
+ | Graham Number | √(22.5 × EPS × BVPS) | Graham (1973) |
567
+ | Graham Intrinsic Value | EPS × (8.5 + 2g) × 4.4 / Y | Graham (1974) |
568
+ | 2-Stage DCF | Σ FCF/(1+r)^t + TV | Williams (1938) |
569
+ | Gordon Growth DDM | D1 / (r - g) | Gordon (1959) |
570
+ | Earnings Power Value | NOPAT / WACC | Greenwald et al. (2001) |
571
+ | Reverse DCF | Solve for g | Mauboussin (2006) |
572
+
573
+ ### Profitability (15)
574
+ Gross Margin, Operating Margin, EBITDA Margin, Net Margin, NOPAT Margin,
575
+ ROE, ROA, ROIC, ROCE, ROTE, DuPont 3-Factor, Invested Capital,
576
+ Revenue/Employee, Profit/Employee, NOPAT
577
+
578
+ ### Liquidity (9)
579
+ Current Ratio, Quick Ratio, Cash Ratio, OCF Ratio, DSO, DIO, DPO,
580
+ Cash Conversion Cycle, Defensive Interval Ratio
581
+
582
+ ### Solvency (9)
583
+ D/E, Net D/E, Net Debt/EBITDA, Debt/Assets, Debt/Capital,
584
+ Interest Coverage, EBITDA Coverage, DSCR, Equity Multiplier
585
+
586
+ ### Efficiency (8)
587
+ Asset Turnover, Fixed Asset Turnover, Inventory Turnover,
588
+ Receivables Turnover, Payables Turnover, Working Capital Turnover,
589
+ Capital Turnover, Operating Leverage
590
+
591
+ ### Cash Flow (11)
592
+ FCF, Levered FCF, Unlevered FCF (FCFF), Owner Earnings,
593
+ FCF Yield, FCF Margin, FCF Conversion, OCF/Sales,
594
+ Capex/Revenue, Capex/Depreciation, Cash Return on Assets
595
+
596
+ ### Growth (8)
597
+ Revenue YoY, Revenue CAGR, EPS Growth, EBITDA Growth,
598
+ FCF Growth, Book Value Growth, Dividend Growth, EPV
599
+
600
+ ### Risk / Portfolio (18)
601
+ Beta, Jensen's Alpha, Sharpe Ratio, Sortino Ratio, Treynor Ratio,
602
+ Calmar Ratio, Information Ratio, Omega Ratio, Maximum Drawdown,
603
+ Tracking Error, Historical VaR, Parametric VaR, CVaR/Expected Shortfall,
604
+ Ulcer Index, Upside Capture, Downside Capture, R-Squared
605
+
606
+ ### Composite Scores (7)
607
+ | Score | What it detects | Reference |
608
+ |-------|----------------|-----------|
609
+ | **Piotroski F-Score** (0–9) | Financial strength | Piotroski (2000) |
610
+ | **Altman Z-Score** | Bankruptcy probability | Altman (1968) |
611
+ | **Beneish M-Score** | Earnings manipulation | Beneish (1999) |
612
+ | **Ohlson O-Score** | Bankruptcy probability (logistic) | Ohlson (1980) |
613
+ | **Greenblatt Magic Formula** | Value + Quality ranking | Greenblatt (2005) |
614
+ | **Montier C-Score** (0–6) | Earnings quality signals | Montier (2008) |
615
+ | **Shiller CAPE** | Cyclically adjusted P/E | Shiller (2000) |
616
+
617
+ ### Scoring Models (v0.3–v0.7)
618
+ | Model | Signals | Output | Reference |
619
+ |-------|---------|--------|-----------|
620
+ | **Moat Score** (0–100) | ROIC persistence, pricing power, reinvestment quality, op. leverage, CAP | wide / narrow / none | Mauboussin & Johnson (1997) |
621
+ | **Capital Allocation Score** (0–100) | Value creation, FCF quality, reinvestment yield, payout discipline | excellent / good / fair / poor | Koller et al. (2020), Mauboussin (2012) |
622
+ | **Earnings Quality Score** (0–100) | Accruals ratio, cash earnings, revenue recognition, GM stability, asset efficiency | high / medium / low / poor | Sloan (1996), Richardson et al. (2005) |
623
+ | **Fair Value Range** | DCF, Graham Number, FCF Yield, EV/EBITDA, EPV | bear / base / bull per share | Graham & Dodd (1934), Koller et al. (2020) |
624
+ | **Quality Factor Score** (0–100) | EQ (35%) + Moat (35%) + Capital Allocation (30%) | exceptional → poor | Asness, Frazzini & Pedersen (2019) |
625
+ | **Portfolio Quality** | Weighted average across holdings | portfolio score + per-holding breakdown | — |
626
+
627
+ ### SaaS / Tech (11)
628
+ | Metric | Formula | Benchmark |
629
+ |--------|---------|-----------|
630
+ | Rule of 40 | Growth % + FCF Margin % | > 40 healthy |
631
+ | Net Revenue Retention | Net ARR / Beginning ARR | > 110% elite |
632
+ | Gross Revenue Retention | (ARR - Churn) / ARR | > 90% good |
633
+ | Magic Number | Net New ARR × 4 / S&M | > 0.75 efficient |
634
+ | LTV/CAC | Customer LTV / CAC | > 3x healthy |
635
+ | CAC Payback | CAC / Monthly Margin | < 12 mo excellent |
636
+ | Burn Multiple | Net Burn / New ARR | < 1 excellent |
637
+ | SaaS Quick Ratio | (New + Exp) / (Churn + Contraction) | > 4 excellent |
638
+ | ARR/FTE | ARR / Employees | > $200K elite |
639
+ | Customer LTV | (ARPU × GM) / Churn | — |
640
+ | CAC | S&M Spend / New Customers | — |
641
+
642
+ ### REIT (7)
643
+ FFO, AFFO, P/FFO, P/AFFO, NOI, Cap Rate, Occupancy Rate
644
+
645
+ ### Banking (8)
646
+ NIM, Efficiency Ratio, Loan/Deposit, NPL Ratio, Provision Coverage,
647
+ Tier 1 Capital, CET1 Ratio, Tangible BVPS
648
+
649
+ ### Insurance (5)
650
+ Loss Ratio, Expense Ratio, Combined Ratio, Underwriting Margin, Premiums/Surplus
651
+
652
+ ---
653
+
654
+ ## Design Principles
655
+
656
+ 1. **Pure functions** — `(inputs) → number | null`
657
+ 2. **Formula on every function** — `pe.formula`, `sharpeRatio.formula`
658
+ 3. **Null-safe** — returns `null` on division by zero, never `NaN`
659
+ 4. **Tree-shakeable** — import only what you need
660
+ 5. **Zero core deps** — only fetchers/api/mcp have external dependencies
661
+ 6. **Strict TypeScript** — `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`
662
+ 7. **Transparent caching** — opt-in cache layer; fetchers unchanged
663
+ 8. **Batch-first** — `compute_all()` / `computeAll()` for single-call analysis
664
+
665
+ ---
666
+
667
+ ## Examples
668
+
669
+ Run the included examples without any API keys:
670
+
671
+ ```bash
672
+ cd python
673
+ python examples/01_valuation_examples.py # DCF, Graham Number, EV multiples
674
+ python examples/03_composite_scores.py # Piotroski, Altman, Beneish, Magic Formula
675
+ python examples/04_risk_portfolio.py # Sharpe, Sortino, VaR, CVaR, Capture Ratios
676
+ python examples/05_saas_metrics.py # Rule of 40, NRR, Burn Multiple, LTV/CAC
677
+ ```
678
+
679
+ ---
680
+
681
+ ## Documentation
682
+
683
+ - [Quantitative Moat Score — methodology, signals, and usage](docs/MOAT_SCORE.md)
684
+ - [Academic Citations](docs/CITATIONS.md)
685
+ - [Changelog](CHANGELOG.md)
686
+
687
+ ---
688
+
689
+ ## Academic Citations
690
+
691
+ Every ratio is backed by either academic research or established industry practice.
692
+ See [docs/CITATIONS.md](docs/CITATIONS.md) for the full bibliography.
693
+
694
+ **Key papers:**
695
+ - Altman (1968) — Z-Score bankruptcy prediction, *Journal of Finance*
696
+ - Piotroski (2000) — F-Score value investing, *Journal of Accounting Research*
697
+ - Beneish (1999) — M-Score earnings manipulation, *Financial Analysts Journal*
698
+ - Sharpe (1966) — Risk-adjusted return, *Journal of Business*
699
+ - Gordon (1959) — Dividend discount model, *Review of Economics and Statistics*
700
+ - Rockafellar & Uryasev (2000) — CVaR optimization, *Journal of Risk*
701
+ - Tobin (1969) — Q ratio, *Journal of Money, Credit and Banking*
702
+ - Ohlson (1980) — O-Score bankruptcy probability, *Journal of Accounting Research*
703
+ - Keating & Shadwick (2002) — Omega ratio, *Journal of Performance Measurement*
704
+ - Montier (2008) — C-Score earnings quality, *Société Générale Cross Asset Research*
705
+ - Shiller (2000) — CAPE cyclically adjusted P/E, *Irrational Exuberance*
706
+ - Mauboussin & Johnson (1997) — Competitive Advantage Period (CAP)
707
+ - Sloan (1996) — Accruals anomaly, *Accounting Review*
708
+ - Richardson et al. (2005) — Accruals and future earnings, *Journal of Accounting & Economics*
709
+ - Novy-Marx (2013) — Gross profitability, *Journal of Financial Economics*
710
+ - Mauboussin (2012) — True measures of capital allocation success, *Harvard Business Review*
711
+ - Asness, Frazzini & Pedersen (2019) — Quality Minus Junk, *Review of Accounting Studies*
712
+
713
+ ---
714
+
715
+ ## Project Structure
716
+
717
+ ```
718
+ fin-ratios/
719
+ ├── typescript/
720
+ │ ├── src/
721
+ │ │ ├── ratios/ # 136+ ratio functions
722
+ │ │ ├── fetchers/ # Yahoo, FMP, EDGAR, Alpha Vantage
723
+ │ │ ├── hooks/ # React hooks (useRatios, useHealthScore, ...)
724
+ │ │ ├── types/ # IncomeStatement, BalanceSheet, MarketData, etc.
725
+ │ │ ├── utils/ # computeAll, scenarioDcf, cache, moatScore,
726
+ │ │ │ # capitalAllocationScore, earningsQualityScore,
727
+ │ │ │ # fairValueRange, qualityScore, portfolioQuality
728
+ │ │ └── __tests__/ # 89-test vitest suite
729
+ │ └── examples/
730
+ ├── python/
731
+ │ ├── fin_ratios/
732
+ │ │ ├── ratios/ # Python port of all ratios
733
+ │ │ ├── fetchers/ # Yahoo, FMP, EDGAR, Alpha Vantage, SimFin
734
+ │ │ ├── utils/ # compute_all, trends, scenario_dcf, peers,
735
+ │ │ │ # moat_score, capital_allocation, earnings_quality,
736
+ │ │ │ # fair_value, quality_score, portfolio, benchmarks
737
+ │ │ ├── cache.py # Memory / disk / Redis caching layer
738
+ │ │ ├── pandas_ext.py # Pandas + Polars DataFrame integration
739
+ │ │ ├── notebook.py # Jupyter rich display (RatioCard, ComparatorCard)
740
+ │ │ ├── api.py # FastAPI REST server
741
+ │ │ ├── mcp_server.py # MCP server for AI agents
742
+ │ │ └── cli.py # fin-ratios CLI
743
+ │ ├── tests/ # 306-test pytest suite
744
+ │ ├── examples/
745
+ │ └── scripts/
746
+ │ └── sp500_analysis.py
747
+ ├── docs/
748
+ │ ├── MOAT_SCORE.md # Moat score methodology deep-dive
749
+ │ └── CITATIONS.md # Full academic bibliography
750
+ └── CHANGELOG.md
751
+ ```
752
+
753
+ ---
754
+
755
+ ## Contributing
756
+
757
+ See [CONTRIBUTING.md](CONTRIBUTING.md). All contributions welcome.
758
+
759
+ When adding a new ratio, include:
760
+ 1. The formula as a string on the function (`.formula = "..."`)
761
+ 2. A description (`.description = "..."`)
762
+ 3. An academic or industry citation in `docs/CITATIONS.md`
763
+ 4. At least one test in the test suite
764
+
765
+ ---
766
+
767
+ ## Security
768
+
769
+ See [SECURITY.md](SECURITY.md) to report vulnerabilities.
770
+
771
+ ---
772
+
773
+ ## License
774
+
775
+ MIT — use freely in commercial projects.
776
+
777
+ ---
778
+
779
+ **Links:** [Docs](https://piyushgupta344.github.io/fin-ratios/) · [Changelog](CHANGELOG.md) · [Contributing](CONTRIBUTING.md) · [Security](SECURITY.md) · [npm](https://npmjs.com/package/fin-ratios) · [PyPI](https://pypi.org/project/financial-ratios/)