A tabular foundation model for corporate credit risk, built from scratch and measured in public.
Given a table of labelled rows, FinTFM predicts new rows in a single forward pass, with your
data supplied as context rather than trained on. By default there are no gradient steps at fit
time and no per-dataset tuning; dataset-specific fine-tuning is opt-in (finetune_steps). The
model is pretrained once, on synthetic data only, and never sees a real table during
pretraining.
That last property is the point. A model that never saw your benchmark in pretraining cannot have memorised it, and in a regulated domain the ability to demonstrate that is worth more than a few points of accuracy.
from huggingface_hub import hf_hub_download
from fintfm.inference import FinancialTFMClassifier
ckpt = hf_hub_download("kabartay/fintfm-binary", "v4-cellattn-labels.pt")
clf = FinancialTFMClassifier(ckpt, device="cpu")
clf.fit(X_train, y_train) # stores the table as context; no training happens
pd_estimates = clf.predict_proba(X_test)[:, 1]The checkpoint is on Hugging Face at kabartay/fintfm-binary — 885K parameters, binary, up to 136 features, Apache-2.0. It is the checkpoint every published binary number below was measured on, so those results are reproducible against this file rather than a variant of it.
FinTFM is a research codebase with a public claims ledger, not a product. It exists to find out whether in-context tabular learning can be made competitive with gradient-boosted trees on financial tables, and to say honestly where it currently is not.
| External benchmark | TabArena, 27 binary datasets against 94 other methods: rank 94 of 95, integration confirmed correct by a maintainer and the entry declined on competitiveness (§122) |
| On real credit panels | calibration consistently among the best measured; discrimination consistently loses to gradient boosting — on V4FinBench's full panel, untuned CatBoost scores 0.40 mean AP against FinTFM's 0.20 (§163). (§167, 2026-10-16: on V4FinBench's published features, which keep operational_status; LightGBM at 1,000 trees scores 0.3908, so the field is about 0.39-0.40 mean AP and the lead is boosting rounds, not one library.) |
| Fine-tuning | opt-in per-dataset fine-tuning (finetune_steps, 0.7.0) lifts V4FinBench AP on 5 of 5 folds, 0.1986 → 0.2062 — the first lever to move it, closing 3.8% of the gap to CatBoost (§163) |
| Licence | Apache-2.0, code and weights — chosen separately, not inherited (see Licensing) |
| Checkpoint | kabartay/fintfm-binary — 885K, binary, ≤136 features |
| Tests | 413 collected, plus ruff, the openspec validator, a dependency-licence check and a documentation-link check — all in CI |
| Measurement log | 164 numbered entries (§164 is pre-registered, with no result yet), each declaring how its numbers were produced |
| Problem types | binary declared; multiclass and regression implemented but not declared (§121) |
Read docs/paper/CLAIMS.md before anything else. Every claim this
project could make is tagged SURVIVES / SINGLE DRAW / SUPERSEDED / RETRACTED / OPEN against its
evidence. That file, not this one, is the source of truth — and it records what stopped being
true as prominently as what holds.
Throughout this repository, §N refers to entry N in
docs/results/FINDINGS.md, a measurement log in which every number states how it was produced (MEASURED, SMOKE-TEST, SIMULATED or ESTIMATED) and negative results are kept deliberately, because they are what stops the same wrong conclusion being reached twice.
TabArena, 27 binary datasets (26 for the §98 row), one fold each. The rank column is as measured at the time, against an earlier cached snapshot of the board:
| change | mean ROC-AUC | Elo | rank (earlier snapshot) |
|---|---|---|---|
| label encoding (§98), 26 of 27 datasets | 0.7642 | 662 | 93 / 95 |
| + out-of-fold target statistics (§101) | 0.7823 | 765 | 93 / 95 |
| + multiclass-capable prior (§105) | 0.7817 | 813 | 93 / 95 |
| + 5.0M parameters, matched task count (§114) | 0.7774 | 751 | 93 / 95 |
| + tree-structured prior (§115), two seeds | 0.7926 / 0.7913 | 842 / 826 | 93 / 95 |
The §101 model, unchanged at 0.7823 mean ROC-AUC, ranks 94 of 95 against the snapshot the TabArena review used (§122, Elo 765), and still 94 of 95 at Elo 763 after a refreshed baseline cache (§150). The move from 93 to 94 is the board changing, not the model.
One change has moved the TabArena number, and it was preprocessing rather than architecture. The tree prior adds a further +0.0094 here and replicates across two seeds — but costs −0.0221 average precision on credit data (§116), negative on 5 of 5 folds with three surviving multiple-comparison correction at p < 0.001, so it ships off by default. (§166, 2026-10-16: that comparison is confounded. The prior draws the tree source first, so the tree arm also cut the financial share from 0.70 to 0.49. Against a control at the same financial share the tree arm is +0.0119 mean AP, 4 of 5 folds positive, one seed per arm. The evidence that the tree prior hurts credit is gone; it still ships off, now pending a re-test at a matched share and two seeds, task DR2.4.)
The full table, including an unpublished exploratory variant (a linear probe on the model's representation, §132) measured at 910 Elo, is published at kabartay.github.io/fintfm/leaderboard — this project's own comparison, not tabarena.ai, and its Elo is comparable only within the population it was computed over. Everything else tried — parameter scale at matched task volume, training volume, a widened structural-causal prior — has been null or negative. None of them moved the published model's rank; only the unpublished linear-probe variant moved one position (§132).
Three things, stated at their true weight:
Incoherent PD term structures are impossible by construction. The field's standard per-horizon construction produces non-monotone cumulative default curves for 39% of firms on real data — a firm whose 3-year default probability is below its 2-year. A hazard head makes that unrepresentable, at zero measured accuracy cost.
A severe capacity defect was found by measurement, not review. The original architecture had capped discrimination regardless of true task difficulty, caught against an exactly-known Bayes-optimal AUC. Its cause was isolated to the architecture rather than the prior after eliminating seven other candidates one at a time, then closed by a two-way cell-attention change worth +0.042 average precision on real data. That comparison also changed the prior mix (100% against 70% financial tasks; confirmed 2026-10-16, audit task AU2.8), so the effect of the architecture alone is unmeasured.
The record corrects itself in public. This README has been wrong about its own results at
least twice (§107, §112). Both are retracted in place, with the reasoning kept, and
docs/paper/CLAIMS.md tags retracted claims as prominently as the ones that hold. In a domain
where a model must arrive with its own validation evidence, that record is part of what is
offered.
- Priors (
src/fintfm/prior/) — synthetic task generators, sampled and mixed per batch (mixture.py).financial.py— a structural generative story for company balance sheets, P&L and default labels: accounting identities, sector and macro effects, a wide derived-ratio family, missingness, a sharpness (signal-to-noise) knob, and a default-rate envelope that can reach real low-default-portfolio rates (~0.2%).scm.py— a generic random-graph structural-causal-model prior (the TabPFN/TabICL-style idea): random layered functions with several nonlinearities, for general nonlinear and multiclass structure the financial prior does not cover.tree.py— ensembles of oblivious decision trees, generating the axis-aligned, piecewise-constant structure real tabular data is full of and the other priors do not. Added on distinctiveness grounds afterfintfm-priorscoremeasured it as the only member of the mixture with a positive tree-versus-linear gap (§112). Off by default (p_tree=0.0).trivial.py,crossed.py— diagnostic-only priors used to isolate specific hypotheses (whether the architecture can learn at all; whether a prior's features or its label mechanism carries a measured effect). Not part of the default training mixture.
- Model (
src/fintfm/modeling/model.py) — a from-scratch Transformer. Cells are embedded individually and carry a random per-task column identity (so the model can tell columns apart without a positional embedding, which would break invariance to column order). Columns attend to each other within a row; optionally (ModelConfig.n_cell_blocks, 0 by default but used by the published checkpoint) cells also attend across rows within one feature before that, giving each column a distribution-derived identity rather than only a random tag. Rows are then pooled and attend to context rows to perform in-context learning. An optional hazard head (modeling/hazard.py) produces a cumulative PD term structure that is monotone by construction. Independent implementation of ideas described in the public TabPFN / TabICL literature — no code or weights from any existing project (see Licensing below). - Training (
src/fintfm/modeling/train.py,fintfm-train) — infinite synthetic data, one gradient step per fresh batch, cosine LR schedule, checkpointing, a held-out quality metric that reports per-task AUC (not the pooled-across-tasks number, which is inflated whenever task base rates differ) and Brier skill against a base-rate-only predictor. - Inference (
src/fintfm/inference/) — an sklearn-compatibleFinancialTFMClassifier:fit()stores the table as context,predict_proba()runs the frozen network. Several context-construction strategies (uniform, balanced, hybrid, retrieval, prototype) and a base-rate correction, plus ensembling over column-identity draws to recover the distributional column-order invariance a random identity trades for expressiveness.inference/categorical.pyencodes categorical columns as out-of-fold smoothed target statistics, because the model reads every cell as an ordered scalar and label encoding's arbitrary order is measurably worse than no order at all (§100). The out-of-fold step is not a refinement: the naive form puts a row's own label into its own encoding, which makes the context self-predictive and the feature absent at query time — it harms the model rather than flattering the score, so it survives careless validation.inference/regressor.pyadds aFinancialTFMRegressoron the same frozen network and no new architecture: a continuous target cut into K quantile bins is an integer index over K outcomes, so the existing classification head regresses as-is, and the output is natively distributional — quantiles and prediction intervals come free, and the predicted density can be bimodal, which is what loss given default actually is and what a Gaussian head cannot represent. - Evaluation (
src/fintfm/evaluation/,src/fintfm/experiments/) — real corporate-default panels (V4FinBench via its published protocol,fintfm-v4protocol; Polish and Taiwan bankruptcy,fintfm-bench), an out-of-time harness, a synthetic capability suite (fintfm-capability, including a class-count sweep for multiclass, §99) with probes of a known ceiling — some with a closed-form Bayes-optimal AUC — specifically built to catch a model that looks fine on average while capped in a way an aggregate score cannot see. Average precision is reported alongside ROC-AUC everywhere, and read first at low base rates (ROC-AUC's chance floor is 0.5 regardless of prevalence; AP's floor is the prevalence itself, so it stays legible at the base rates this project's target segment actually has).fintfm-priorscorescores a prior rather than a model, on the three criteria the literature converged on — performance, diversity, distinctiveness — using fitted baselines only, so it cannot confuse "the prior lacks this structure" with "our model cannot learn it". Its first version could, and §112 is the record of what that cost.
pip install fintfmThe published checkpoint is Apache-2.0 and ungated, so nothing else is needed to predict:
from huggingface_hub import hf_hub_download
ckpt = hf_hub_download(
"kabartay/fintfm-binary",
"v4-cellattn-labels.pt",
revision="f116bfd43a2b15c65ed3551ea8c38e3364629ddc", # pin it; weights behind a number should not move
)To work on the model rather than use it, clone the repository instead:
uv sync --extra bench --extra hf # naming one extra uninstalls the others
uv run pytest # 413 tests collectedThe estimators are scikit-learn compatible. By default fit() stores the table as context
and takes no gradient steps, and predict_proba() runs the frozen network once. Dataset-specific
fine-tuning is opt-in (finetune_steps); a fine-tuned model has seen your training rows and is
not the zero-shot model (§163).
from fintfm.inference import FinancialTFMClassifier, FinancialTFMRegressor
clf = FinancialTFMClassifier(ckpt, device="mps") # ckpt from hf_hub_download, above
clf.fit(X_train, y_train) # stores context; no training happens
proba = clf.predict_proba(X_test)[:, 1]
# Regression needs a checkpoint trained for it (--max-classes >= n_bins). None is published:
# the only public checkpoint is binary, so this line needs one you trained yourself.
reg = FinancialTFMRegressor("runs/v4-regression.pt", n_bins=10)
reg.fit(X_train, y_cont)
point = reg.predict(X_test) # distribution mean
lo, hi = reg.predict_interval(X_test, 0.8) # 80% interval, free from the same headCategorical columns need encoding before they reach the model — it reads every cell as an
ordered scalar, and label encoding is measurably worse than no order at all (§100). Use
fintfm.inference.categorical.CategoricalTargetEncoder, which is out-of-fold on the context
rows for reasons that are not optional; see How it works.
uv run fintfm-train --steps 300 --d-model 32 --d-cell 16 --n-layers 2 --max-features 16 \
--max-classes 2 --device cpu --out runs/v0-smoke.pt # pipeline check, a couple of minutes
uv run fintfm-bench --model runs/v0-smoke.pt --credit # real corporate-default panelsThat smoke config exists to check the pipeline runs, not to produce a usable checkpoint — see
docs/infra/COMPUTE.md for measured step costs at real scale. A real pretraining
run (thousands of steps, --d-model 128+, --max-features matching your data) takes
hours-to-a-day and needs a GPU: --device mps on Apple Silicon, --device cuda, or see
docs/infra/HF_JOBS.md for the Hugging Face Jobs recipe this project actually
uses for training runs. Check uptime before running anything heavy locally — see
CLAUDE.md for why.
Both fintfm-bench --credit (Polish/Taiwan need 64/95 features) and fintfm-v4protocol
(V4FinBench needs 136) will print SKIPPED for fintfm: model takes 16 features, data has N —
correct, expected behaviour for the smoke checkpoint above, not a bug. Baselines still run and
score normally; only the FinTFM arm needs a checkpoint pretrained with a matching
--max-features to be evaluated.
The numbers experiments use — split years, context sizes, strategies, seeds, scoring
thresholds, the prior's default-rate and sharpness envelope — live in
src/fintfm/configs/default.yaml, not scattered through
the code. Every entry point takes --config with a file that is deep-merged over that
default, so an override carries only what it changes:
uv run fintfm-ctxsweep --model runs/m.pt --config configs/context-sweep-3seed.yaml
uv run fintfm-v4oot --model runs/m.pt --config configs/retrieval-best.yamlResolution order is packaged default → --config (or $FINTFM_CONFIG) → explicit CLI flag.
Runs print the layers they used and record them as config_sources in their output JSON.
Unknown keys are refused by name rather than ignored, and an overlapping train/test split is
rejected before the run starts. configs/README.md explains what
belongs in configuration and what deliberately stays in code; several files under
configs/ are deliberately diagnostic, not production (their own headers say
so), used to isolate a single variable while chasing a specific finding.
FinTFM is the synthetic-only model this repository has shipped from the start, and is what every result, release and benchmark number here refers to unless a page says otherwise. Its provenance argument — the model cannot have memorised a benchmark, because it never saw real data — depends on that and is the project's differentiator (decisions D2, D14).
FinTFM-R (R for real data) is a second family, opened by decision D17 on 2026-10-06. It mixes real tabular data into pretraining: SEC EDGAR's public-domain Financial Statement Data Sets with 8-K Item 1.03 bankruptcy disclosures as labels (§151-§152), plus loaders for Freddie Mac's loan-level sample (§154) and LendingClub (§155), and a generic real-panel sampler that SBA loans are fed through (§157). It exists to test the one lever D16 showed this project had never pulled, after seven architecture and prior changes returned seven nulls or losses.
So far it has not helped. At a 30% real share, 6,000 steps from scratch, two seeds each, neither real source improved V4FinBench mean average precision over five folds: EDGAR is a stable null (−0.0019 and −0.0029 mean AP at seeds 0 and 1), and SBA a null whose seeds disagree (−0.0355 and +0.0024 mean AP) (§160-§161). No FinTFM-R checkpoint is published.
The two are not interchangeable, and the difference is the part this project sells: the provenance claim holds for FinTFM and does not hold for FinTFM-R. Any statement about one names which one. FinTFM is not renamed by this — it keeps its name, package, DOI and TabArena entry; "FinTFM (synthetic-only)" is prose for when the contrast needs stating, not a new identifier.
Everything here — the prior, the architecture, the training loop — is original code written
for this project. No weights, datasets, or source from Neuralk (Seldon), Fundamental
(NEXUS), Google TabFM, TabPFN, TabICL, TabDPT, or any other tabular-foundation-model product
are used or may be added. Those are cited in project discussion purely as public
research/product context. Every third-party dataset or dependency's licence is checked before
use, and a weight licence is checked separately from its code licence, every time — never
assumed from a prior check. Direct dependencies are all permissive — numpy, pandas and
scikit-learn BSD-3, scipy BSD-3, torch BSD-3, PyYAML MIT, and the optional benchmark extras
lightgbm MIT, xgboost Apache-2.0, catboost Apache-2.0, pyarrow Apache-2.0 — and
scripts/check_licences.py enforces that in CI rather than leaving it to this paragraph.
FinTFM-R's real-data sources, checked and dated. SEC EDGAR's Financial Statement Data
Sets and 8-K full-text search (efts.sec.gov) — a US federal government work, public domain,
no stated copyright restriction, commercially usable, verified 2026-10-06 by direct access
(curl returns 200 with a descriptive User-Agent; without one, SEC's Akamai front end
returns 403 — a bot filter, not a licensing gate). Freddie Mac's Single-Family Loan-Level
Dataset is registered and in use under its free tier, read in full rather than assumed
(docs/results/FINDINGS.md §153): royalty-free for internal use (training, evaluating,
including against public benchmarks) and for academic/research use, which explicitly covers
publishing research results and derived products provided distribution is itself
noncommercial — which fintfm is (Apache-2.0, free checkpoints, no paid product). A separate
fee-based agreement ($27,562.50) exists for a licensee who resells or commercially
redistributes the Data or Derived Products; that applies only if fintfm's status changes, not
to the research release this project does today. One obligation binds regardless of tier:
no correlating the data to individuals — this is consumer loan-level data, unlike EDGAR's
corporate filings.
One honest exception, which CI found and this paragraph previously did not mention. On
Linux, torch pulls in around fifteen NVIDIA CUDA runtime packages that are NVIDIA
Proprietary, not permissive. They are a transitive runtime dependency rather than a choice
made here, this project does not redistribute them — uv build produces a pure-Python
wheel and pip fetches them from PyPI under NVIDIA's own terms — and they are absent on macOS,
which is why a check run only on a developer's Mac reported everything permissive while CI did
not. Anyone shipping this in a product must read NVIDIA's EULA themselves; it is not
something this repository can assert on their behalf.
Stated so the project is falsifiable rather than open-ended. Two deficits are measured
separately. On TabArena, FinTFM trails tuned logistic regression by ~0.035 mean ROC-AUC over 27
datasets (§101). On V4FinBench's full panel, untuned CatBoost (library defaults, 1,000
iterations) leads it by about 0.19 mean average precision over five folds (0.4005 against 0.2062
fine-tuned, §163). (§167, 2026-10-16: both figures use V4FinBench's published features, which
keep operational_status. On the same features LightGBM at 1,000 trees and learning rate 0.05
scores 0.3908, so a booster given enough rounds reaches about 0.39-0.40 mean AP; the 100-tree
LightGBM (0.2666) and XGBoost (0.2229) defaults are under-trained and are not the field. Without
operational_status, CatBoost's defaults score 0.3662.)
One lever has moved V4FinBench: opt-in fine-tuning on each fold's training rows, +0.0076 mean AP, positive on 5 of 5 folds (0.1986 to 0.2062, §163). That closes 3.8% of the gap to CatBoost, and a fine-tuned model is no longer the zero-shot model. Real-data pretraining (FinTFM-R) was null at GPU scale (§160-§161). These are the remaining candidates:
| still open | why it is a candidate | status |
|---|---|---|
| factorized attention | the encoder is memory-bound at every turn, and three peers independently chose the cheaper form | proposed (44.x), prior art recorded |
| a joint objective | p(x, y | D) rather than p(y | x, D) makes every column a training signal, not just the target |
proposed (48.15), scoped as a measurement before a rewrite |
| prior design | the only lever a peer's own ablations identify as decisive | instrument built and partly falsified — see below |
Not abandoned — tested and ruled out, which is the more useful half of the record and the reason the list above is short:
| candidate | what the measurement said |
|---|---|
| parameter scale | −0.0049 at 5.7× the parameters on matched task volume (§114). A peer's published curve returns +0.0049 R² for 16.7×, against a 0.035 deficit. |
| training volume | null at 5× (§93); +0.0028 at 2× (§114). |
| depth over width | on TabArena it could not be scored: the harness's per-dataset time limit stopped it after 8 of 27 datasets (§114). On V4FinBench, 16 layers against 4 at width 128 is null: +0.0010 mean AP over five folds at a matched learning rate (§124, §126). |
column_id_dim |
peaks at the value chosen by accident; all four non-peak arms below it on all five folds (§104). |
| a widened structural-causal prior | p = 1.000 against its own matched control. |
| a tree-structured prior | +0.0094 on general tabular data, replicated across two seeds (§115) — and −0.0221 average precision on credit (§116). Ships off by default. (§166, 2026-10-16: §116's arms differed in financial share, 0.49 against 0.70; at a matched share the tree arm is +0.0119 mean AP, 4 of 5 folds, one seed, so the credit loss is not the tree prior's.) |
The prior-scoring instrument is half-confirmed and half-falsified. It predicted the general-tabular gain above before it was measured (§112 → §115). A second arm, added to ask the credit question, then predicted the wrong direction on its first real test — raising the structural-causal prior's share cost −0.0339 AP, 0 of 5 folds, all five significant (§118). So it predicts breadth, not fit, and no claim rests on its credit arm.
Every number in docs/results/FINDINGS.md names the command that produced it.
The entry points:
| command | what it measures |
|---|---|
fintfm-v4protocol |
V4FinBench under its published five-fold protocol — the benchmark this project's claims rest on |
fintfm-bench --credit |
Polish and Taiwan bankruptcy panels |
fintfm-v4oot |
out-of-time split, which the published protocol is not |
fintfm-capability |
synthetic probes with a known ceiling, some with a closed-form Bayes-optimal AUC |
fintfm-priorscore |
scores a prior, not a model, on performance / diversity / distinctiveness |
fintfm-ctxsweep |
context strategy against context size, out of time. Strategy mattered at smoke-test scale (§5); on V4FinBench's full panel, context size is not what limits average precision (§162) |
Two conventions worth knowing before reading any of it. Every number declares how it was produced — SMOKE-TEST, MEASURED, SIMULATED or ESTIMATED — because in an ML repository a wrong number does not crash, it looks like a result. And negative and superseded results are kept, because they are what stops the same wrong conclusion being reached twice.
docs/ holds the project's reasoning, indexed in docs/README.md.
docs/paper/CLAIMS.md— start here. Every claim this project could make, tagged by status, newest evidence wins.docs/results/FINDINGS.md— the full measurement log, numbered sequentially (164 entries at the time of writing), each declaring how its numbers were produced.docs/design/DECISIONS.md— why the project is built the way it is, and what would reverse each choice.docs/roadmap/STRATEGY.md— the plan of record.docs/design/ARCHITECTURE.md— how the model works, in more depth than this file.docs/results/TABARENA.md— the external-evaluation recipe, the coverage fraction any score must carry, and four silent failure modes including a results cache that returns stale numbers after a preprocessing change.docs/paper/RELATED_WORK.md— the competing models, read from primary sources, with what each costs this project's positioning. Its digest is the shortest useful summary of why the remaining lever is the prior.docs/results/POSTMORTEM.md— wrong diagnoses, each caught by measurement rather than review, kept on the record deliberately.openspec/changes/— the live roadmap as structured proposals with verifiable tasks, not prose.uv run python openspec/tools/validate.pychecks every proposal is well-formed and every finding declares its provenance.
Short version: the mechanism claims ("no training on your data", "no feature engineering") are already owned by better-funded competitors, so the thesis is not the mechanism. It is a credit model that arrives with its own validation evidence — calibrated, auditably free of benchmark contamination, and eventually backed by a pre-registered forward track record that cannot be bought — plus a public, self-correcting record of what has and has not been shown to be true.
src/fintfm/
prior/ the synthetic data-generating processes: a structural model of company
financials, a random-graph SCM, a tree prior, and the mixture that samples
them; plus FinTFM-R's real-panel samplers (real_*.py)
modeling/ the network and the pretraining loop
inference/ scikit-learn compatible estimators, plus the categorical target encoder
evaluation/ benchmark harnesses and baselines
experiments/ one module per measured question; each writes a numbered finding
configs/ packaged defaults, read at runtime by path
docs/
results/ FINDINGS.md, the numbered measurement log this project is organised around,
plus TABARENA.md and POSTMORTEM.md
paper/ CLAIMS.md (what holds, what was retracted), RELATED_WORK.md, LIMITATIONS.md
design/ ARCHITECTURE.md, DECISIONS.md, GLOSSARY.md
roadmap/ ROADMAP.md (what to do next), STRATEGY.md, NEXT.md, TABARENA_BAR.md
competition/ LANDSCAPE.md, SUBMISSION.md
infra/ COMPUTE.md, HF_JOBS.md
research/ REFERENCES.md, RESEARCH_NOTES.md
openspec/changes/ one proposal per change, with its task list; the queue lives here
scripts/ the repository's own gates (licence, documentation links, Zenodo metadata)
plus run tooling: real-data panel builders, Kaggle
kernels, score comparison and pre-registered verdicts
Read docs/results/FINDINGS.md first. Every number quoted anywhere in this repository
points at a numbered entry there stating how it was produced, and entries that were later
retracted stay in place rather than being edited away.
No code, weights, or training data from any tabular foundation model was used here; see Licensing / provenance. What follows is credit for what was used.
-
V4FinBench (Kostrzewa et al., arXiv:2605.10896, CC BY 4.0) for the corporate-default panel and its evaluation protocol, which this project reimplements from the published specification rather than vendoring.
-
TabArena for the external protocol, and its maintainers for reviewing the submission and confirming the integration and numbers are correct while declining the entry on competitiveness (§122). That review is the only independent check this project has had.
-
The published work of TabPFN, TabICL, TabDPT, LimiX, Nori, MITRA, TabSTAR, TabSwift, OrionMSP, iLTM, ConTextTab and EXAONE-Tabular — for ideas, which this project took freely and then measured:
idea from what happened three criteria for scoring a prior: performance, diversity, distinctiveness MITRA built into fintfm-priorscore; §112 then measured this prior's distinctiveness as weaka tree-structured prior TabICLv2, reimplemented from its appendix +0.0094 on general tabular data, −0.0221 AP on credit; ships off (§116). (§166, 2026-10-16: the credit loss is confounded with a lower financial share; matched, the tree arm is +0.0119 mean AP, one seed.) published scaling curve Nori capped a scale hypothesis GPU budget was being spent on, before more was spent (§114) rejecting unlearnable synthetic tasks Nori measured: would reject 35% of this prior, and discards signal the model uses (§125) narrow-and-deep shape, 16 layers at width 128 Nori measured: null here, +0.0010 AP (§124, §126) Four of those five ideas were measured and not adopted. That is the point of taking them: a published result from a different prior and architecture is a hypothesis about this one, not a conclusion about it.
Reading a paper is not ingesting a codebase, and the distinction is enforced in CLAUDE.md.
A CITATION.cff is in the repository root, so GitHub's "Cite this repository" button
renders BibTeX and APA directly. The same entry, to copy:
@software{Organokov_FinTFM_2026,
author = {Organokov, Mukharbek},
title = {{FinTFM: a tabular foundation model for corporate credit risk,
pretrained only on synthetic data}},
year = {2026},
version = {0.7.1},
doi = {10.5281/zenodo.22949759},
url = {https://github.com/kabartay/fintfm},
publisher = {Zenodo},
}There is no paper; the numbered measurement log is this project's public record, so the software entry above is the citable artifact rather than a stand-in for one.
Cite the repository for the method, the measurement log or any finding; cite the checkpoint (kabartay/fintfm-binary) when the specific weights matter to what you are reporting. They are different artifacts and a reader can only check the one you name.
Every release is archived on Zenodo with its own DOI. The concept DOI 10.5281/zenodo.22949759 always resolves to the newest version; each release also has one of its own. That distinction is load-bearing here, because this repository retracts and supersedes results: §107 and §112 both withdrew earlier claims. Cite a version DOI to pin what you actually read, and the concept DOI to point at current state.
Issues and discussion are welcome. Two hard boundaries, both of which protect the only claim this project has that competitors do not:
No code, weights, or training data from any tabular-foundation-model product — TabPFN,
TabICL, TabDPT, LimiX, Nori, MITRA, or any other — may enter this repository. Their published
papers are legitimate to read and cite, and docs/paper/RELATED_WORK.md
does exactly that for ten of them. Ingesting any of it would destroy the provenance argument
above, and nothing would warn us.
Check a weights licence separately from its code licence, every time. Four of the ten peer projects read for this release ship permissive code with non-commercial weights, a split invisible from a repository's headline licence badge. One of them restricts commercial use of the model's output, not merely the weights.
