calcfinc 0.1.0__tar.gz
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.
- calcfinc-0.1.0/.gitignore +17 -0
- calcfinc-0.1.0/CHANGELOG.md +44 -0
- calcfinc-0.1.0/CONTRIBUTING.md +76 -0
- calcfinc-0.1.0/LICENSE +21 -0
- calcfinc-0.1.0/PKG-INFO +167 -0
- calcfinc-0.1.0/README.md +130 -0
- calcfinc-0.1.0/SECURITY.md +37 -0
- calcfinc-0.1.0/docs/adapters.md +171 -0
- calcfinc-0.1.0/docs/fact-schema.md +93 -0
- calcfinc-0.1.0/docs/manual.md +817 -0
- calcfinc-0.1.0/docs/ratios.md +254 -0
- calcfinc-0.1.0/examples/01_usd_company.py +23 -0
- calcfinc-0.1.0/examples/02_inr_consolidated_vs_standalone.py +16 -0
- calcfinc-0.1.0/examples/03_bank_and_insurer.py +16 -0
- calcfinc-0.1.0/examples/04_monthly_sme_dscr.py +24 -0
- calcfinc-0.1.0/examples/05_cross_currency.py +14 -0
- calcfinc-0.1.0/examples/06_sec_companyfacts.py +31 -0
- calcfinc-0.1.0/examples/07_ind_as_xbrl.py +21 -0
- calcfinc-0.1.0/examples/data/bank_and_insurer.csv +22 -0
- calcfinc-0.1.0/examples/data/inr_company.csv +7 -0
- calcfinc-0.1.0/examples/data/monthly_sme.csv +11 -0
- calcfinc-0.1.0/examples/data/sec_companyfacts_synthetic.json +388 -0
- calcfinc-0.1.0/examples/data/synthetic_consolidated_30-Jun-2025.xbrl +24 -0
- calcfinc-0.1.0/examples/data/two_currencies.csv +7 -0
- calcfinc-0.1.0/examples/data/usd_company.csv +16 -0
- calcfinc-0.1.0/pyproject.toml +75 -0
- calcfinc-0.1.0/scripts/gen_ratio_docs.py +111 -0
- calcfinc-0.1.0/src/calcfinc/__init__.py +30 -0
- calcfinc-0.1.0/src/calcfinc/adapters/__init__.py +1 -0
- calcfinc-0.1.0/src/calcfinc/adapters/ind_as_xbrl/__init__.py +29 -0
- calcfinc-0.1.0/src/calcfinc/adapters/ind_as_xbrl/canonical.py +171 -0
- calcfinc-0.1.0/src/calcfinc/adapters/ind_as_xbrl/load.py +231 -0
- calcfinc-0.1.0/src/calcfinc/adapters/ind_as_xbrl/tags.py +144 -0
- calcfinc-0.1.0/src/calcfinc/adapters/ind_as_xbrl/vocab.py +129 -0
- calcfinc-0.1.0/src/calcfinc/adapters/ind_as_xbrl/xbrl.py +128 -0
- calcfinc-0.1.0/src/calcfinc/adapters/sec_companyfacts/__init__.py +24 -0
- calcfinc-0.1.0/src/calcfinc/adapters/sec_companyfacts/fetch.py +70 -0
- calcfinc-0.1.0/src/calcfinc/adapters/sec_companyfacts/load.py +81 -0
- calcfinc-0.1.0/src/calcfinc/adapters/sec_companyfacts/parse.py +393 -0
- calcfinc-0.1.0/src/calcfinc/adapters/sec_companyfacts/tags.py +93 -0
- calcfinc-0.1.0/src/calcfinc/engine/__init__.py +11 -0
- calcfinc-0.1.0/src/calcfinc/engine/check.py +95 -0
- calcfinc-0.1.0/src/calcfinc/engine/decompose.py +80 -0
- calcfinc-0.1.0/src/calcfinc/engine/engine.py +690 -0
- calcfinc-0.1.0/src/calcfinc/engine/evaluate.py +189 -0
- calcfinc-0.1.0/src/calcfinc/engine/growth.py +28 -0
- calcfinc-0.1.0/src/calcfinc/engine/records.py +157 -0
- calcfinc-0.1.0/src/calcfinc/engine/segments.py +179 -0
- calcfinc-0.1.0/src/calcfinc/entity.py +41 -0
- calcfinc-0.1.0/src/calcfinc/fact.py +177 -0
- calcfinc-0.1.0/src/calcfinc/formula.py +180 -0
- calcfinc-0.1.0/src/calcfinc/loaders/__init__.py +6 -0
- calcfinc-0.1.0/src/calcfinc/loaders/csv.py +86 -0
- calcfinc-0.1.0/src/calcfinc/loaders/dataframe.py +93 -0
- calcfinc-0.1.0/src/calcfinc/loaders/records.py +255 -0
- calcfinc-0.1.0/src/calcfinc/num.py +96 -0
- calcfinc-0.1.0/src/calcfinc/period.py +112 -0
- calcfinc-0.1.0/src/calcfinc/py.typed +0 -0
- calcfinc-0.1.0/src/calcfinc/registry/__init__.py +6 -0
- calcfinc-0.1.0/src/calcfinc/registry/metrics.py +159 -0
- calcfinc-0.1.0/src/calcfinc/registry/ratios.py +427 -0
- calcfinc-0.1.0/src/calcfinc/store/__init__.py +5 -0
- calcfinc-0.1.0/src/calcfinc/store/base.py +91 -0
- calcfinc-0.1.0/src/calcfinc/store/schema.sql +104 -0
- calcfinc-0.1.0/src/calcfinc/store/sqlite.py +418 -0
- calcfinc-0.1.0/tests/__init__.py +0 -0
- calcfinc-0.1.0/tests/_fixture.py +74 -0
- calcfinc-0.1.0/tests/test_docs.py +118 -0
- calcfinc-0.1.0/tests/test_engine.py +333 -0
- calcfinc-0.1.0/tests/test_exactness.py +157 -0
- calcfinc-0.1.0/tests/test_examples.py +46 -0
- calcfinc-0.1.0/tests/test_formula.py +309 -0
- calcfinc-0.1.0/tests/test_generalised.py +253 -0
- calcfinc-0.1.0/tests/test_guardrails.py +108 -0
- calcfinc-0.1.0/tests/test_ind_as.py +408 -0
- calcfinc-0.1.0/tests/test_india.py +114 -0
- calcfinc-0.1.0/tests/test_loaders.py +219 -0
- calcfinc-0.1.0/tests/test_records.py +106 -0
- calcfinc-0.1.0/tests/test_robustness.py +302 -0
- calcfinc-0.1.0/tests/test_sec.py +479 -0
- calcfinc-0.1.0/tests/test_segments.py +84 -0
- calcfinc-0.1.0/tests/test_store.py +115 -0
- calcfinc-0.1.0/tests/test_ttm.py +145 -0
- calcfinc-0.1.0/tests/test_valuation.py +197 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes are listed here. The project follows [semantic versioning](https://semver.org/);
|
|
4
|
+
while the version is 0.x, a minor release may change the public API, and any such change is listed
|
|
5
|
+
under "Changed". A change to a built-in ratio definition raises its `definition_version`.
|
|
6
|
+
|
|
7
|
+
## 0.1.0 - first release
|
|
8
|
+
|
|
9
|
+
### What it is
|
|
10
|
+
|
|
11
|
+
An exact-decimal engine that turns periodic statements into ratios, growth, CAGR, valuation,
|
|
12
|
+
DuPont, trailing twelve months and segment attribution, with the formula, the input facts and the
|
|
13
|
+
limitations on every result. No runtime dependencies; Python 3.11 to 3.13.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `FinancialEngine` with `get_metric`, `get_ratio`, `get_valuation`, `get_growth`, `get_cagr`,
|
|
18
|
+
`compare_periods`, `compare_companies`, `decompose_metric`, `calculate`, `check`, `check_periods`,
|
|
19
|
+
segment queries, and loaders for CSV (long and wide), records and pandas DataFrames.
|
|
20
|
+
- More than a hundred ratios and derived quantities as formula strings in an open registry
|
|
21
|
+
(`register_metric`, `register_ratio`); trailing-twelve-month forms through `ttm(x)`; average
|
|
22
|
+
balances through `prior(x)`; a restricted Decimal formula language.
|
|
23
|
+
- Definitions reconciled across ACCA, the FTC Quarterly Financial Report, MCA/ICAI, RBI and SEBI,
|
|
24
|
+
with the Indian forms as `india.*` and RBI-form bank ratios (`bank.*`).
|
|
25
|
+
- Sector handling: ratios that do not describe a bank or an insurer return `None` with the reason;
|
|
26
|
+
sector is declared or inferred from the facts.
|
|
27
|
+
- Source adapters: SEC `companyfacts` (exact periods, restatement versions, derived fourth
|
|
28
|
+
quarters, US bank lines, a polite download helper) and Indian exchange XBRL (Ind-AS), tested on
|
|
29
|
+
19 US and 20 Indian companies' real filings.
|
|
30
|
+
- Restatements kept as versions by `reported_at`; each input of a result carries it; a result warns
|
|
31
|
+
when one period's inputs come from filings far apart.
|
|
32
|
+
- A SQLite store that keeps every value as exact decimal text.
|
|
33
|
+
- A manual whose examples are run by the test suite, and a ratio reference generated from the code.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
Nothing: this is the first release.
|
|
38
|
+
|
|
39
|
+
### Known limitations
|
|
40
|
+
|
|
41
|
+
- Point-in-time views (`as_of`) for restated data are not implemented.
|
|
42
|
+
- IFRS filers, SEC insurers and 20-F/40-F filers are not mapped.
|
|
43
|
+
- EBIT is `profit before exceptional items + finance costs`, so it includes non-operating gains.
|
|
44
|
+
- LLM function-calling schemas and an MCP server are planned for a later release.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for helping. This project cares about exactness and honesty more than breadth, so a few
|
|
4
|
+
rules matter more than usual.
|
|
5
|
+
|
|
6
|
+
## Set up
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
git clone https://github.com/Am1n1602/calcfinc && cd calcfinc
|
|
10
|
+
python -m venv .venv
|
|
11
|
+
.venv/bin/python -m pip install -e ".[dev]" # Windows: .venv\Scripts\python
|
|
12
|
+
.venv/bin/python -m pytest && .venv/bin/ruff check . && .venv/bin/mypy
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
All three must pass. CI runs them on Python 3.11, 3.12 and 3.13 on Linux, Windows and macOS.
|
|
16
|
+
|
|
17
|
+
## The rules
|
|
18
|
+
|
|
19
|
+
1. **No floats.** Numbers are `Decimal`, `int` or `str`. Anything that lets a float through to a
|
|
20
|
+
result is a bug. Arithmetic goes through `calcfinc.num`.
|
|
21
|
+
2. **Missing is `None` with a reason.** Never zero, never an estimate. If a definition needs a
|
|
22
|
+
substitute, make it a labelled fallback so the result announces it.
|
|
23
|
+
3. **The core stays generic.** No country, exchange, regulator or vendor names in `src/calcfinc`
|
|
24
|
+
outside `adapters/` (a test enforces this). Country-specific inputs and ratios belong in an
|
|
25
|
+
adapter (see `adapters/ind_as_xbrl/vocab.py`).
|
|
26
|
+
4. **Standard library only** at runtime. The network may be used only in
|
|
27
|
+
`adapters/sec_companyfacts/fetch.py` (a test enforces this).
|
|
28
|
+
5. **No real data in the repository.** Test and example data is synthetic and written for the test.
|
|
29
|
+
Do not commit exchange, SEC or vendor data or anything derived from it.
|
|
30
|
+
6. **Definitions need evidence.** A new or changed ratio comes with the source of its definition.
|
|
31
|
+
If regulators or standards disagree, say so in the pull request and in the ratio's `label`, and
|
|
32
|
+
pick one with a reason. Changing an existing definition bumps its `version`.
|
|
33
|
+
|
|
34
|
+
## Tests
|
|
35
|
+
|
|
36
|
+
Write a test when there is behaviour to pin down, with expected values **worked out by hand**
|
|
37
|
+
(show the arithmetic in a comment), never copied from the code's output. Fix the code, not the
|
|
38
|
+
test, when they disagree. A bug fix comes with a test that fails without it.
|
|
39
|
+
|
|
40
|
+
The documentation is tested too: every ```` ```python ```` block in `README.md` and
|
|
41
|
+
`docs/manual.md` is run, and `expr # -> value` lines are checked. `docs/ratios.md` is generated;
|
|
42
|
+
after changing a ratio or metric run
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
python scripts/gen_ratio_docs.py
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
and commit the result (a test fails if it is stale).
|
|
49
|
+
|
|
50
|
+
## Adding a source adapter
|
|
51
|
+
|
|
52
|
+
An adapter reads one source's tags and conventions and produces `FinancialFact`s through the
|
|
53
|
+
loaders; it never changes how a ratio is computed. Test it on synthetic data in the repository and,
|
|
54
|
+
if you can, on real filings *outside* it, and record what was and was not verified in
|
|
55
|
+
`docs/adapters.md`.
|
|
56
|
+
|
|
57
|
+
## Style
|
|
58
|
+
|
|
59
|
+
`ruff` (line length 120) and `mypy --strict` are the style guide. Comments explain why, not what.
|
|
60
|
+
|
|
61
|
+
## Releasing (maintainers)
|
|
62
|
+
|
|
63
|
+
1. Set `__version__` in `src/calcfinc/__init__.py`, move the changelog entry from "Unreleased" to the
|
|
64
|
+
new version, regenerate `docs/ratios.md`, and make sure CI is green on `main`.
|
|
65
|
+
2. One-time setup: on PyPI (and on TestPyPI) add a **trusted publisher** for owner `Am1n1602`,
|
|
66
|
+
repository `calcfinc`, workflow `release.yml`, environment `pypi` (`testpypi` on TestPyPI), and create
|
|
67
|
+
those two environments in the repository settings. No API token is ever stored.
|
|
68
|
+
3. Dry run: Actions, "release", **Run workflow** publishes the build to TestPyPI. Install it with
|
|
69
|
+
`pip install --index-url https://test.pypi.org/simple/ --no-deps calcfinc` and try it.
|
|
70
|
+
4. Release: `git tag v0.1.0 && git push origin v0.1.0`. The workflow refuses to publish if the tag
|
|
71
|
+
and `__version__` disagree.
|
|
72
|
+
|
|
73
|
+
## Pull requests
|
|
74
|
+
|
|
75
|
+
Keep one concern per PR, update `CHANGELOG.md` for anything a user would notice, and describe
|
|
76
|
+
how you checked it. By contributing you agree your work is released under the MIT licence.
|
calcfinc-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Am1n1602 (Aman Gautam)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
calcfinc-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: calcfinc
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Auditable financial metrics from periodic statements of any entity: every result carries its formula, input facts and limitations.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Am1n1602/calcfinc
|
|
6
|
+
Project-URL: Documentation, https://github.com/Am1n1602/calcfinc/blob/main/docs/manual.md
|
|
7
|
+
Project-URL: Source, https://github.com/Am1n1602/calcfinc
|
|
8
|
+
Project-URL: Issues, https://github.com/Am1n1602/calcfinc/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/Am1n1602/calcfinc/blob/main/CHANGELOG.md
|
|
10
|
+
Author: Am1n1602 (Aman Gautam)
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: audit,decimal,finance,financial-ratios,financial-statements,provenance,sec,xbrl
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
25
|
+
Classifier: Topic :: Office/Business :: Financial :: Accounting
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.11
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: coverage>=7; extra == 'dev'
|
|
30
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
31
|
+
Requires-Dist: pandas>=2; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
34
|
+
Provides-Extra: pandas
|
|
35
|
+
Requires-Dist: pandas>=2; extra == 'pandas'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# calcfinc
|
|
39
|
+
|
|
40
|
+
[](https://github.com/Am1n1602/calcfinc/actions/workflows/ci.yml)
|
|
41
|
+
|
|
42
|
+
**Auditable financial metrics from the statements of any entity.** Give it periodic figures (a
|
|
43
|
+
CSV, a pandas frame, an SEC filing, an Indian exchange filing) and ask for ratios, growth,
|
|
44
|
+
CAGR, valuation, DuPont, trailing twelve months. Every answer carries the formula that produced
|
|
45
|
+
it, the exact figures it used, and a plain-language note whenever something was substituted or
|
|
46
|
+
could not be computed.
|
|
47
|
+
|
|
48
|
+
```python skip
|
|
49
|
+
roe = eng.get_ratio("Acme Inc", "roe")
|
|
50
|
+
roe.value # Decimal('25')
|
|
51
|
+
roe.formula # '100 * net_profit / total_equity'
|
|
52
|
+
roe.inputs # the facts used: metric, value, period, currency, source, date reported
|
|
53
|
+
roe.limitations # () - or why the value is None, or what was substituted
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- **Exact.** Every number is a `Decimal`; floats are refused at every entrance. A result is the
|
|
57
|
+
same on any machine and any Python version.
|
|
58
|
+
- **Honest.** A missing input gives `None` and the reason, never zero and never an estimate.
|
|
59
|
+
- **Open.** More than a hundred ratios, each one a readable formula string
|
|
60
|
+
([list](docs/ratios.md)). Add your own with `register_ratio`.
|
|
61
|
+
- **Any entity.** Any currency, any fiscal calendar (March, 52/53-week...), months, quarters
|
|
62
|
+
and years, consolidated and standalone, banks and insurers.
|
|
63
|
+
- **Zero runtime dependencies.** Python 3.11 or later; pandas is optional.
|
|
64
|
+
|
|
65
|
+
> **Status: 0.1, beta.** Tested on 19 US and 20 Indian companies' real filings (see
|
|
66
|
+
> [what has and has not been verified](docs/manual.md#15-what-has-and-has-not-been-verified)).
|
|
67
|
+
> The API may still change in 0.x; changes are listed in [CHANGELOG.md](CHANGELOG.md).
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install calcfinc
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Quick start
|
|
76
|
+
|
|
77
|
+
A CSV with one row per metric and one column per fiscal year:
|
|
78
|
+
|
|
79
|
+
```csv acme.csv
|
|
80
|
+
metric,FY2025,FY2026
|
|
81
|
+
revenue,1000,1200
|
|
82
|
+
net_profit,100,150
|
|
83
|
+
total_equity,500,600
|
|
84
|
+
total_assets,2000,2400
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from calcfinc import FinancialEngine
|
|
89
|
+
|
|
90
|
+
eng = FinancialEngine.from_csv("acme.csv", entity="Acme Inc", currency="USD")
|
|
91
|
+
|
|
92
|
+
roe = eng.get_ratio("Acme Inc", "roe")
|
|
93
|
+
roe.value # -> 25
|
|
94
|
+
roe.formula # -> 100 * net_profit / total_equity
|
|
95
|
+
|
|
96
|
+
# growth in percent, FY2025 to FY2026, and compound annual growth
|
|
97
|
+
eng.get_growth("Acme Inc", "revenue").value # -> 20
|
|
98
|
+
eng.get_cagr("Acme Inc", "revenue").value # -> 20
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
A figure that cannot be computed says why instead of guessing. This file has no cash flow
|
|
102
|
+
statement:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
fcf = eng.get_ratio("Acme Inc", "free_cash_flow")
|
|
106
|
+
fcf.value # -> None
|
|
107
|
+
print(fcf.limitations[0])
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## What you can ask
|
|
111
|
+
|
|
112
|
+
| | |
|
|
113
|
+
|---|---|
|
|
114
|
+
| `get_ratio`, `get_metric` | margins, returns, leverage, liquidity, efficiency, per-share, trailing twelve months |
|
|
115
|
+
| `get_valuation` | P/E, P/B, EV/EBITDA, yields, market cap (you supply prices) |
|
|
116
|
+
| `get_growth`, `get_cagr` | year on year, quarter on quarter, month on month, compound |
|
|
117
|
+
| `compare_periods`, `compare_companies` | two periods side by side; rank entities (never across currencies) |
|
|
118
|
+
| `decompose_metric` | DuPont (three and five factor), net-margin bridge |
|
|
119
|
+
| `check`, `check_periods` | accounting identities; do the quarters add up to the year |
|
|
120
|
+
| `get_segment_data`, `segment_growth` | segment revenue, contribution, growth |
|
|
121
|
+
| `calculate` | any formula over numbers you give it |
|
|
122
|
+
|
|
123
|
+
## Where data comes from
|
|
124
|
+
|
|
125
|
+
CSV (long or wide), dicts, pandas, or your own storage; a SQLite file if you want it to persist.
|
|
126
|
+
Two adapters read real filings:
|
|
127
|
+
|
|
128
|
+
- **SEC `companyfacts`** (US-GAAP): exact periods, restatements kept as versions, the fourth
|
|
129
|
+
quarter derived, bank lines for banks.
|
|
130
|
+
- **Indian exchange XBRL** (Ind-AS): April-March year, `india.*` ratios in the Schedule III and ICAI
|
|
131
|
+
forms, placeholder zeros not loaded.
|
|
132
|
+
|
|
133
|
+
Both tie every fact to its filing. See [docs/adapters.md](docs/adapters.md). No market or vendor
|
|
134
|
+
data is bundled, and there is no network access except one explicit SEC download helper.
|
|
135
|
+
|
|
136
|
+
## Things it deliberately will not do
|
|
137
|
+
|
|
138
|
+
- Convert currencies. Amounts in different currencies are listed, never ranked.
|
|
139
|
+
- Annualise a quarterly ratio. It says "not annualised" and offers the trailing-twelve-month form.
|
|
140
|
+
- Hide a substitution. A fallback definition, a derived quarter or a mixed restatement vintage is
|
|
141
|
+
always in `limitations`.
|
|
142
|
+
- Apply operating-company ratios to a bank or an insurer. Interest cover, debt and the
|
|
143
|
+
working-capital ratios return `None` with the reason for financial companies.
|
|
144
|
+
|
|
145
|
+
## Documentation
|
|
146
|
+
|
|
147
|
+
- **[Manual](docs/manual.md)**: concepts, loading data, periods, every question you can ask,
|
|
148
|
+
reading results, custom ratios, running it in production, troubleshooting. Its examples are run
|
|
149
|
+
by the test suite.
|
|
150
|
+
- [Ratio and metric reference](docs/ratios.md) (generated from the code)
|
|
151
|
+
- [Fact format and CSV layouts](docs/fact-schema.md)
|
|
152
|
+
- [Adapters and real-data results](docs/adapters.md)
|
|
153
|
+
- [Examples](examples): seven runnable scripts with synthetic data
|
|
154
|
+
|
|
155
|
+
## Development
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
python -m venv .venv
|
|
159
|
+
.venv/bin/python -m pip install -e ".[dev]" # Windows: .venv\Scripts\python
|
|
160
|
+
.venv/bin/python -m pytest && .venv/bin/ruff check . && .venv/bin/mypy
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). To report a vulnerability, see [SECURITY.md](SECURITY.md).
|
|
164
|
+
|
|
165
|
+
## Licence
|
|
166
|
+
|
|
167
|
+
MIT. Results are calculations, not investment advice.
|
calcfinc-0.1.0/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# calcfinc
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Am1n1602/calcfinc/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
**Auditable financial metrics from the statements of any entity.** Give it periodic figures (a
|
|
6
|
+
CSV, a pandas frame, an SEC filing, an Indian exchange filing) and ask for ratios, growth,
|
|
7
|
+
CAGR, valuation, DuPont, trailing twelve months. Every answer carries the formula that produced
|
|
8
|
+
it, the exact figures it used, and a plain-language note whenever something was substituted or
|
|
9
|
+
could not be computed.
|
|
10
|
+
|
|
11
|
+
```python skip
|
|
12
|
+
roe = eng.get_ratio("Acme Inc", "roe")
|
|
13
|
+
roe.value # Decimal('25')
|
|
14
|
+
roe.formula # '100 * net_profit / total_equity'
|
|
15
|
+
roe.inputs # the facts used: metric, value, period, currency, source, date reported
|
|
16
|
+
roe.limitations # () - or why the value is None, or what was substituted
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- **Exact.** Every number is a `Decimal`; floats are refused at every entrance. A result is the
|
|
20
|
+
same on any machine and any Python version.
|
|
21
|
+
- **Honest.** A missing input gives `None` and the reason, never zero and never an estimate.
|
|
22
|
+
- **Open.** More than a hundred ratios, each one a readable formula string
|
|
23
|
+
([list](docs/ratios.md)). Add your own with `register_ratio`.
|
|
24
|
+
- **Any entity.** Any currency, any fiscal calendar (March, 52/53-week...), months, quarters
|
|
25
|
+
and years, consolidated and standalone, banks and insurers.
|
|
26
|
+
- **Zero runtime dependencies.** Python 3.11 or later; pandas is optional.
|
|
27
|
+
|
|
28
|
+
> **Status: 0.1, beta.** Tested on 19 US and 20 Indian companies' real filings (see
|
|
29
|
+
> [what has and has not been verified](docs/manual.md#15-what-has-and-has-not-been-verified)).
|
|
30
|
+
> The API may still change in 0.x; changes are listed in [CHANGELOG.md](CHANGELOG.md).
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install calcfinc
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
A CSV with one row per metric and one column per fiscal year:
|
|
41
|
+
|
|
42
|
+
```csv acme.csv
|
|
43
|
+
metric,FY2025,FY2026
|
|
44
|
+
revenue,1000,1200
|
|
45
|
+
net_profit,100,150
|
|
46
|
+
total_equity,500,600
|
|
47
|
+
total_assets,2000,2400
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from calcfinc import FinancialEngine
|
|
52
|
+
|
|
53
|
+
eng = FinancialEngine.from_csv("acme.csv", entity="Acme Inc", currency="USD")
|
|
54
|
+
|
|
55
|
+
roe = eng.get_ratio("Acme Inc", "roe")
|
|
56
|
+
roe.value # -> 25
|
|
57
|
+
roe.formula # -> 100 * net_profit / total_equity
|
|
58
|
+
|
|
59
|
+
# growth in percent, FY2025 to FY2026, and compound annual growth
|
|
60
|
+
eng.get_growth("Acme Inc", "revenue").value # -> 20
|
|
61
|
+
eng.get_cagr("Acme Inc", "revenue").value # -> 20
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
A figure that cannot be computed says why instead of guessing. This file has no cash flow
|
|
65
|
+
statement:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
fcf = eng.get_ratio("Acme Inc", "free_cash_flow")
|
|
69
|
+
fcf.value # -> None
|
|
70
|
+
print(fcf.limitations[0])
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## What you can ask
|
|
74
|
+
|
|
75
|
+
| | |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `get_ratio`, `get_metric` | margins, returns, leverage, liquidity, efficiency, per-share, trailing twelve months |
|
|
78
|
+
| `get_valuation` | P/E, P/B, EV/EBITDA, yields, market cap (you supply prices) |
|
|
79
|
+
| `get_growth`, `get_cagr` | year on year, quarter on quarter, month on month, compound |
|
|
80
|
+
| `compare_periods`, `compare_companies` | two periods side by side; rank entities (never across currencies) |
|
|
81
|
+
| `decompose_metric` | DuPont (three and five factor), net-margin bridge |
|
|
82
|
+
| `check`, `check_periods` | accounting identities; do the quarters add up to the year |
|
|
83
|
+
| `get_segment_data`, `segment_growth` | segment revenue, contribution, growth |
|
|
84
|
+
| `calculate` | any formula over numbers you give it |
|
|
85
|
+
|
|
86
|
+
## Where data comes from
|
|
87
|
+
|
|
88
|
+
CSV (long or wide), dicts, pandas, or your own storage; a SQLite file if you want it to persist.
|
|
89
|
+
Two adapters read real filings:
|
|
90
|
+
|
|
91
|
+
- **SEC `companyfacts`** (US-GAAP): exact periods, restatements kept as versions, the fourth
|
|
92
|
+
quarter derived, bank lines for banks.
|
|
93
|
+
- **Indian exchange XBRL** (Ind-AS): April-March year, `india.*` ratios in the Schedule III and ICAI
|
|
94
|
+
forms, placeholder zeros not loaded.
|
|
95
|
+
|
|
96
|
+
Both tie every fact to its filing. See [docs/adapters.md](docs/adapters.md). No market or vendor
|
|
97
|
+
data is bundled, and there is no network access except one explicit SEC download helper.
|
|
98
|
+
|
|
99
|
+
## Things it deliberately will not do
|
|
100
|
+
|
|
101
|
+
- Convert currencies. Amounts in different currencies are listed, never ranked.
|
|
102
|
+
- Annualise a quarterly ratio. It says "not annualised" and offers the trailing-twelve-month form.
|
|
103
|
+
- Hide a substitution. A fallback definition, a derived quarter or a mixed restatement vintage is
|
|
104
|
+
always in `limitations`.
|
|
105
|
+
- Apply operating-company ratios to a bank or an insurer. Interest cover, debt and the
|
|
106
|
+
working-capital ratios return `None` with the reason for financial companies.
|
|
107
|
+
|
|
108
|
+
## Documentation
|
|
109
|
+
|
|
110
|
+
- **[Manual](docs/manual.md)**: concepts, loading data, periods, every question you can ask,
|
|
111
|
+
reading results, custom ratios, running it in production, troubleshooting. Its examples are run
|
|
112
|
+
by the test suite.
|
|
113
|
+
- [Ratio and metric reference](docs/ratios.md) (generated from the code)
|
|
114
|
+
- [Fact format and CSV layouts](docs/fact-schema.md)
|
|
115
|
+
- [Adapters and real-data results](docs/adapters.md)
|
|
116
|
+
- [Examples](examples): seven runnable scripts with synthetic data
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
python -m venv .venv
|
|
122
|
+
.venv/bin/python -m pip install -e ".[dev]" # Windows: .venv\Scripts\python
|
|
123
|
+
.venv/bin/python -m pytest && .venv/bin/ruff check . && .venv/bin/mypy
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). To report a vulnerability, see [SECURITY.md](SECURITY.md).
|
|
127
|
+
|
|
128
|
+
## Licence
|
|
129
|
+
|
|
130
|
+
MIT. Results are calculations, not investment advice.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Only the latest release receives fixes. While the version is 0.x, upgrade to the newest 0.x.
|
|
6
|
+
|
|
7
|
+
## Reporting a vulnerability
|
|
8
|
+
|
|
9
|
+
Please **do not open a public issue** for a security problem. Use GitHub's private reporting:
|
|
10
|
+
the repository's **Security** tab, then **Report a vulnerability**
|
|
11
|
+
(<https://github.com/Am1n1602/calcfinc/security/advisories/new>).
|
|
12
|
+
|
|
13
|
+
Include what you found, the smallest input that shows it, and the version. You can expect an
|
|
14
|
+
acknowledgement within a week. A confirmed problem is fixed in a new release and credited in the
|
|
15
|
+
changelog unless you prefer otherwise.
|
|
16
|
+
|
|
17
|
+
## What is in scope
|
|
18
|
+
|
|
19
|
+
calcfinc parses text it is given, so these are the surfaces that matter:
|
|
20
|
+
|
|
21
|
+
- **The formula evaluator.** Formulas are parsed with Python's `ast` and run by a small
|
|
22
|
+
interpreter that allows numbers, arithmetic, names and six functions. `eval` is never used. Input
|
|
23
|
+
that escapes this (attribute access, calls to other functions, a crash on deeply nested or
|
|
24
|
+
enormous input, unbounded memory or time) is a vulnerability.
|
|
25
|
+
- **File readers.** The CSV loader, the SEC `companyfacts` JSON reader and the XBRL reader, which
|
|
26
|
+
uses the standard library's XML parser (it does not fetch external entities). Anything that makes
|
|
27
|
+
them execute code, read other files or exhaust resources on a reasonably sized input is in scope.
|
|
28
|
+
- **Storage.** All SQL uses bound parameters; an injection is in scope.
|
|
29
|
+
- **The one network call.** `sec_companyfacts.fetch_companyfacts` is the only code that can touch
|
|
30
|
+
the network. Anything that makes other code do so is in scope.
|
|
31
|
+
|
|
32
|
+
## Out of scope
|
|
33
|
+
|
|
34
|
+
- Wrong financial results are bugs, not vulnerabilities: please open a normal issue with the data.
|
|
35
|
+
- Running calcfinc on untrusted XBRL with the stock parser is documented as a limit; see the manual's
|
|
36
|
+
security section for the hardened-parser route.
|
|
37
|
+
- Denial of service by loading an enormous file you chose to load.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Source adapters
|
|
2
|
+
|
|
3
|
+
An adapter reads one source's tags and conventions and produces calcfinc facts. The core never
|
|
4
|
+
imports an adapter, and an adapter never changes how a ratio is computed.
|
|
5
|
+
|
|
6
|
+
| Adapter | Source | Notes |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| `calcfinc.adapters.ind_as_xbrl` | Indian exchange XBRL filings (.xbrl file, raw fact rows, or canonical records) | April-March year, INR, `india.*` ratios |
|
|
9
|
+
| `calcfinc.adapters.sec_companyfacts` | The SEC's public `companyfacts` JSON | US-GAAP filers; IFRS is not mapped yet |
|
|
10
|
+
|
|
11
|
+
## Ind-AS XBRL
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from calcfinc.adapters import ind_as_xbrl
|
|
15
|
+
ind_as_xbrl.load_xbrl_file(repos, "results_consolidated.xbrl", entity="ACME")
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- Three entry points, one per stage: `load_xbrl_file` (a filing), `load_raw_facts` (rows from
|
|
19
|
+
`parse_xbrl_file`), `load_canonical` / `load_canonical_file` (records already mapped to names).
|
|
20
|
+
- The file's basis comes from its name (`consolidated` or `standalone`), or pass `basis=`.
|
|
21
|
+
- Only whole-company contexts are read (`OneD`, `OneI`, `PY_D`...). Segment and note breakdowns
|
|
22
|
+
are ignored. Filings that tag the older `in-bse-fin:` prefix are matched by local concept name.
|
|
23
|
+
- Some filers declare their reporting period as plain facts and leave the context without one;
|
|
24
|
+
those are recovered. Banks that omit a start date get it worked out from the financial year and
|
|
25
|
+
the quarter label, and only when that agrees with the declared end.
|
|
26
|
+
- Numbers are parsed from the filing's text straight to `Decimal`. Indian formatting, accounting
|
|
27
|
+
negatives and a `sign="-"` attribute are handled.
|
|
28
|
+
- Bank totals (equity, cash, liabilities) are built from their parts only when every part is
|
|
29
|
+
present; a reported total always wins.
|
|
30
|
+
- **A record whose own arithmetic fails still loads**, but each of its facts carries a review
|
|
31
|
+
reason (`source record failed arithmetic checks: ...`) and the report lists the period.
|
|
32
|
+
- **Zeros that mean "not applicable" are not loaded.** Real filings report an exact 0 where a
|
|
33
|
+
figure does not apply: a consolidated bank filing gives 0 for its whole NPA block (gross and net
|
|
34
|
+
NPA, both ratios, ROA, CET1, AT1); paid-up capital, face value and CET1 are never genuinely 0;
|
|
35
|
+
a coverage ratio of exactly 0 is a placeholder. Stored as numbers they would give a 0% NPA ratio
|
|
36
|
+
instead of "not reported". The block rule needs all four NPA figures to be zero together, so a
|
|
37
|
+
real zero (no exceptional items, no borrowings, no minority interest) is kept. Dropped names are
|
|
38
|
+
listed in `report.skipped`; pass `placeholder_zeros=False` to keep every zero.
|
|
39
|
+
- **Two contexts that contradict each other.** A cumulative context can carry the quarter's own
|
|
40
|
+
dates with different values, and a context can carry a stale prior-year balance. The current
|
|
41
|
+
period's context (`OneD`/`OneI`, then `TwoD`..., then `PY_`) wins, the winner's fact is flagged
|
|
42
|
+
with what the other context said, and `report.conflicts` lists each one. The store never
|
|
43
|
+
decides by load order.
|
|
44
|
+
- `paid_up_equity_capital / face_value_per_share` becomes a `shares_outstanding` fact marked as
|
|
45
|
+
derived, so valuation works.
|
|
46
|
+
- The XML is read with the standard library, which does not fetch external entities. For a file
|
|
47
|
+
you do not trust, parse it with a hardened parser and use `map_facts`.
|
|
48
|
+
- Pass `reported_at=` (the filing date) so a later revised filing is kept beside the original
|
|
49
|
+
instead of overwriting it.
|
|
50
|
+
- **Original and Revision filings** carry the same board-meeting date and no filing date, so the
|
|
51
|
+
file cannot say which is later. Without `reported_at` a later load overwrites an earlier one for
|
|
52
|
+
the same period. `load_xbrl_files(repos, paths, entity=...)` loads every "Revision" after its
|
|
53
|
+
"Original" whatever order you give, so the correction wins. (19 revisions exist in the
|
|
54
|
+
author's 2,960 files; most have no original beside them.)
|
|
55
|
+
|
|
56
|
+
## SEC companyfacts
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from calcfinc.adapters import sec_companyfacts as sec
|
|
60
|
+
data = sec.read_companyfacts("CIK0000320193.json") # a file you downloaded once
|
|
61
|
+
sec.load_companyfacts(repos, data, ticker="AAPL")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- **Periods are matched on exact start and end dates.** The `fy` and `fp` fields in the JSON
|
|
65
|
+
describe the filing, not the period a figure covers, so they are never used to place a number.
|
|
66
|
+
- **Tag choice is per period.** For each metric, the first concept in `tags.CANDIDATES` that the
|
|
67
|
+
filer reported for that exact period wins, and a lower-priority concept is flagged
|
|
68
|
+
`alternate_tag` in the fact's mapping reason. Filers change concepts over time, so the choice is
|
|
69
|
+
never made once for a whole history.
|
|
70
|
+
- **Restatements are kept.** A figure that changes in a later filing becomes a second version with
|
|
71
|
+
`reported_at` set to that filing's date; the engine uses the latest. A later filing that merely
|
|
72
|
+
repeats a comparative figure adds nothing.
|
|
73
|
+
- **The fourth quarter is derived.** The SEC reports no stand-alone Q4, so it is the year less the
|
|
74
|
+
nine-month figure (or less the three reported quarters). Cash-flow statements, which are
|
|
75
|
+
reported year-to-date only, become single quarters the same way. Derived facts are marked
|
|
76
|
+
`derived` with the arithmetic in the reason. This is what makes trailing-twelve-month ratios
|
|
77
|
+
work for SEC filers.
|
|
78
|
+
- **Only currency amounts are derived.** Per-share figures are not additive, so a Q4 EPS is never
|
|
79
|
+
invented.
|
|
80
|
+
- **Every fact points to its filing.** Each accession number becomes a source with the form and
|
|
81
|
+
the filing URL, so `result.inputs[...].source_id` leads back to the document.
|
|
82
|
+
- The fiscal year end is inferred from the full-year figures (a 52/53-week year ending on 1 Feb is
|
|
83
|
+
a January year end). Pass `fiscal_year_end_month=` to override.
|
|
84
|
+
- Facts carrying a dimension are ignored; amounts are already in base units; only 10-K, 10-Q, 20-F
|
|
85
|
+
and 40-F family filings are read (`forms=` to change). 8-Ks are ignored.
|
|
86
|
+
- **US GAAP has no exceptional-items line**, so `pbt_before_exceptional` is set equal to pre-tax
|
|
87
|
+
income, marked `derived`, so EBIT-based ratios work.
|
|
88
|
+
|
|
89
|
+
### Downloading, politely
|
|
90
|
+
|
|
91
|
+
`fetch_companyfacts` is the only function in calcfinc that can use the network, and nothing else
|
|
92
|
+
calls it. The SEC's [fair access rules](https://www.sec.gov/os/accessing-edgar-data) require:
|
|
93
|
+
|
|
94
|
+
- a User-Agent that identifies you with a real contact (there is deliberately no default);
|
|
95
|
+
- fewer than 10 requests per second (the helper keeps to 5 and never retries by itself);
|
|
96
|
+
- downloading once and keeping the file; the data changes only when a company files.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
raw = sec.fetch_companyfacts(320193, "Jane Doe jane@example.com")
|
|
100
|
+
open("CIK0000320193.json", "wb").write(raw)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## What is and is not verified
|
|
104
|
+
|
|
105
|
+
- Every test in the repository uses synthetic data written for the test. No SEC or exchange data
|
|
106
|
+
is stored in this repository, and no test uses the network.
|
|
107
|
+
- **Ind-AS, checked on real filings outside the repository** (the installed wheel, in a separate
|
|
108
|
+
environment, reading the author's earlier extraction output read-only): 2,938 raw `.xbrl`
|
|
109
|
+
filings were parsed and mapped, and compared with the earlier pipeline's output for the same
|
|
110
|
+
filing: 138,313 fields, all identical. Facts from the canonical files were compared with the
|
|
111
|
+
earlier database for eight companies: all 22,293 overlapping facts matched exactly. The
|
|
112
|
+
comparison is what found the placeholder zeros and the contradicting contexts above. It also
|
|
113
|
+
showed that the earlier database stored a 6-month cash-flow figure under the 3-month quarter
|
|
114
|
+
for one filing; this adapter does not.
|
|
115
|
+
- **SEC, checked live** (the installed wheel, in a separate environment, downloading with
|
|
116
|
+
`fetch_companyfacts`): Apple, Microsoft and JPMorgan companyfacts, 3.8-8.0 MB each, loaded in
|
|
117
|
+
about 0.2 s each.
|
|
118
|
+
- The fiscal year end was inferred correctly for all three (Apple's 52/53-week September year,
|
|
119
|
+
Microsoft's June, JPMorgan's December).
|
|
120
|
+
- Revenue and net income matched published figures to the dollar for all seven company-years
|
|
121
|
+
checked (reference values written from memory, so a mismatch would have been investigated, not
|
|
122
|
+
trusted).
|
|
123
|
+
- The four quarters, with Q4 derived, added up to the reported year in 65 of 68 tests; the three
|
|
124
|
+
that did not are restatement-vintage cases (below).
|
|
125
|
+
- That run found and fixed: a quarter whose start date differed by a day between a filing and its
|
|
126
|
+
recast (a duplicate period); a bank's quarterly revenue concept missing from the list; a safety
|
|
127
|
+
rule that wrongly blocked deriving Q4 for a bank that reports one quantity under two concepts;
|
|
128
|
+
and an "alternate concept" flag that fired on filers that only ever use the second-choice
|
|
129
|
+
concept.
|
|
130
|
+
- **A second, broader run** (19 US filers: Apple, Microsoft, JPMorgan, Wells Fargo, Citigroup,
|
|
131
|
+
Goldman Sachs, MetLife, Walmart, Costco, Amazon, NVIDIA, Alphabet, Exxon, Johnson & Johnson,
|
|
132
|
+
Caterpillar, Duke, Prologis, Coca-Cola, AT&T; and 20 Indian companies, 5 of them banks, with
|
|
133
|
+
about 600 consolidated filings) found and fixed: `latest` landing on an SEC cover-page date;
|
|
134
|
+
Amazon's trailing-twelve-month 10-Q figures counted as fiscal years; Costco's 52/53-week years
|
|
135
|
+
labelled a year late; MetLife and other non-banks receiving `bank.*` lines; Indian banks not
|
|
136
|
+
recognised as banks; `paid_up_equity_capital` stored as a flow; and one filing whose declared
|
|
137
|
+
reporting period had day and month swapped. After the fixes, revenue and net income matched
|
|
138
|
+
published figures for all nine companies checked (written from memory; one differs by a
|
|
139
|
+
minority-interest amount), every company loaded in about 1 s, and the quarters add up to the year
|
|
140
|
+
for FY2019 onwards except where a company restated (ITC and Hindustan Unilever after demergers;
|
|
141
|
+
HDFC Bank after its merger).
|
|
142
|
+
- Identity checks still fail on some real statements for real reasons: regulatory deferral balances
|
|
143
|
+
(NTPC), redeemable minority interests and other temporary equity, minorities inside continuing
|
|
144
|
+
profit (Citigroup, AT&T), and utilities' deferred-tax presentation (Duke). They report; they
|
|
145
|
+
never correct.
|
|
146
|
+
- The candidate-concept list uses standard `us-gaap` names and has now met nineteen real filers, but
|
|
147
|
+
not hundreds. Expect other filers to use concepts that are not in the list; those metrics are
|
|
148
|
+
simply absent (never guessed), and the list is meant to be extended.
|
|
149
|
+
- **US banks.** Seven `bank.*` lines are mapped (interest income and expense, provisions, employee
|
|
150
|
+
cost, deposits, gross loans, and net loans where tagged), and only for a filer that reports both
|
|
151
|
+
`NoninterestExpense` and `Deposits`: insurers and industrials file `Deposits` and `InterestExpense`
|
|
152
|
+
too, where they do not mean what `bank.*` means. Interest income less interest expense
|
|
153
|
+
matched reported net interest income for every year checked on one large US bank (2010-2023). Not
|
|
154
|
+
mapped, so their ratios return None with a reason: non-performing assets (the US "nonaccrual" is a
|
|
155
|
+
different definition from the RBI's), CASA and interest-earning assets (not tagged), and "loans net
|
|
156
|
+
of allowance" (that bank's value exceeded its own gross loans, so it cannot be trusted). Loan to
|
|
157
|
+
deposit and credit cost therefore use gross loans, with a note; net interest margin uses total assets,
|
|
158
|
+
with a note. Only one bank has been checked.
|
|
159
|
+
- **Restatement vintages.** The engine uses the latest-reported figure for each metric and period.
|
|
160
|
+
When a company recasts only some figures (Microsoft's ASC 606 recast changed 2016 annual revenue
|
|
161
|
+
and equity but not the 2016 quarters or total assets), a period can combine figures from
|
|
162
|
+
different filings, and the four quarters need not add up to the year. The accounting-identity
|
|
163
|
+
check (`engine.check()`) flags the balance-sheet cases; 6-8 periods per company were flagged in
|
|
164
|
+
the live run and every one examined was this effect. Three aids exist: every input in a result
|
|
165
|
+
carries `reported_at`; a result says so in `limitations` when inputs for one period were first
|
|
166
|
+
reported more than 120 days apart; and `engine.check_periods()` tests, per year, whether the
|
|
167
|
+
four quarters add up to the year. Point-in-time views (`as_of`) are the planned fix.
|
|
168
|
+
- Debt lines use only the first available concept, never a sum, so a filer that reports several
|
|
169
|
+
overlapping debt concepts may show a partial figure.
|
|
170
|
+
- Not covered yet: IFRS filers (`ifrs-full`), insurer concepts from the SEC, and the
|
|
171
|
+
period-end details of 4-4-5 retail calendars beyond what the 52/53-week handling covers.
|