FinAnalyzer โ€” Indian Bank Statement Extraction Model

A 4B-parameter model that turns the text of any Indian bank or credit-card statement into a structured, machine-verifiable transaction ledger โ€” running fully offline on a phone or a small server.

FinAnalyzer is the extraction core of an offline-first personal-finance app. It reads a statement page and emits strict JSON: account header + a list of transactions (ISO date, verbatim narration, debit/credit, amount, running balance). Because the most sensitive document most people own is their bank statement, the design goal is that nothing ever leaves the device โ€” the model is small enough to run on-device, and a separate deterministic checker verifies the arithmetic so the language model never has to be trusted with a sum.

  • Base model: Qwen/Qwen3-4B-Instruct-2507 (Apache-2.0)
  • Method: LoRA (rank 16, all attention + MLP projections), assistant-only loss, thinking disabled
  • Training data: 10,000 synthetic Indian statements (no real customer data โ€” see Training data)
  • Formats: Q8_0 (4.0 GB, recommended) and Q4_K_M (2.4 GB, phone-class) GGUF

What it extracts

Per statement page, a single JSON object:

{
  "bank": "STATE BANK OF INDIA",
  "doc_type": "savings",
  "account_number": "69666708937",
  "holder": "AMIT KULKARNI",
  "ifsc": "SBIN0009059",
  "period": {"from": "2026-10-01", "to": "2026-10-31"},
  "opening_balance": 202552.41,
  "closing_balance": 211678.35,
  "transactions": [
    {"date": "2026-10-03", "narration": "BY DEBIT CARD-OTHPOS531388VIMTA LABS--",
     "ref": null, "type": "debit", "amount": 420.53, "balance": 202131.88}
  ]
}

Credit-card statements emit card_number_masked, total_due, min_due, credit_limit instead of the balance fields. Dates are normalised to ISO-8601; amounts are numbers (no โ‚น, no lakh commas); multi-line narrations are rejoined; absent fields are null.

The trust layer (why the model never does arithmetic)

Language models miscount. FinAnalyzer's contract is that the model only transcribes โ€” every number it emits must already appear in the source text. A separate deterministic checker then verifies the ledger:

  • opening + ฮฃcredits โˆ’ ฮฃdebits == closing (ยฑ0.01)
  • every row's prev_balance ยฑ amount == balance
  • dates within the statement period, monotonic

If any invariant fails, the extraction is flagged for review. This check is pure code, ships with the model, and is what lets a downstream app trust the โ‚น figures absolutely.

Results

Transaction-level exact match = every field of a transaction (date, narration, type, amount, balance) correct. Value match = the ledger-critical subset (date, type, amount, balance) correct โ€” narration text ignored.

In-distribution (supported banks, unseen customers)

Split Exact Date Amount Balance Type
Validation (bf16) 99.9% 100% 100% 100% 99.8%
Validation (Q4_K_M) 99.6% 100% 100% 100% 99.8%

Supported banks: HDFC, SBI, ICICI, Axis (savings) + HDFC, ICICI (credit card).

Out-of-distribution generalisation (banks never seen in training)

Split Exact Value Amount Balance
Held-out banks โ€” Kotak, Canara (bf16) 94.5% 99.3% 100% 100%
Held-out banks (Q8_0) 94.9% ~99% 100% 100%

The numbers that carry money (amount, balance, date) generalise near-perfectly even to unseen layouts; verbatim narration is the only field that softens.

Independent external benchmark (AgamiAI/Indian-Bank-Statements)

A completely independent generator โ€” fictional banks, business current accounts, narration styles never in our training (By Clg:โ€ฆ). ~6,500 transactions across 40 multi-page statements.

Variant Transaction-count parity Value match
Digital Type 1 3232 / 3232 93.9%
Digital Type 2 3234 / 3226 95.0%

Perfect count parity (no dropped or hallucinated rows) and ~94โ€“95% value accuracy against a generator we never touched.

Quantization guidance

Q8_0 is near-lossless and is the recommended build (held-out 94.9% โ‰ˆ bf16 94.5%). Q4_K_M is near-lossless on supported banks (99.6%) but degrades debit/credit classification on unsupported layouts (held-out exact drops to ~80% because type inference weakens on formats like single-column DR/CR flags). Use Q4_K_M for phone deployment against banks you've trained/validated; use Q8_0 when robustness to unknown layouts matters.

How to use

# serve (llama.cpp, OpenAI-compatible). Build with CUDA for GPU.
llama-server -m finanalyzer-q8_0.gguf --port 8091 -c 6144 --parallel 1 -ngl 99

The model expects one page of statement text per request, with this system prompt:

You are a bank-statement extraction engine. Input: the text of one page of an Indian bank or credit-card statement. Output: ONLY a JSON object, no other text. For the first page include: bank, doc_type (savings|current|credit_card), account_number (or card_number_masked), holder, period {from,to} (ISO dates), opening_balance and closing_balance (savings) or total_due, min_due, credit_limit (credit_card), and transactions. For other pages include only transactions. Each transaction: date (ISO), narration (verbatim, join wrapped lines with a space), ref (or null), type (debit|credit), amount (number), balance (number, null if absent). Never invent values not present in the text; use null when absent.

User: Statement page 1 of 1:

<pdftext of the statement page>

Extract the PDF text layer (e.g. pdfplumber) page by page, send each page, and concatenate the transactions. Then run the invariant check before trusting the ledger.

Training data

100% synthetic, generated by a purpose-built pipeline โ€” no real customer statement was used, so nothing memorisable is private:

  • Persona-driven transaction streams (salary, rent, EMIs, SIPs, UPI habits, ATM, reversals) with balance-consistent running totals.
  • Per-bank narration grammars reproducing the real narration shapes of each bank (UPI/NEFT/IMPS/POS/ACH/clearing conventions), filled with checksum-valid fake account numbers, IFSC codes, and public brand names (brand names are facts about which businesses exist, not personal data).
  • Distinct PDF layouts per bank (column schemes, date formats, fonts) rendered to real text-layer PDFs.
  • Free ground truth: because the generator knows every value it placed, each statement ships with exact JSON labels; train/test use disjoint layouts, templates, and value pools so evaluation measures true generalisation.

10,000 statements โ†’ ~19k page-level training examples. Held-out banks (Kotak, Canara) and the external AgamiAI set were never in training.

Limitations & scope

  • Digital (text-layer) PDFs only. Scanned/photographed statements need an OCR front-end (e.g. Surya) feeding this model; that path is not evaluated here.
  • Supported banks are the six above; other banks work via generalisation (see held-out results) but are not guaranteed. Adding a bank means adding its layout to the generator and continuing training.
  • Verbatim narration on unseen banks/dialects is the weakest field (~96%); the numeric ledger is far more robust. For a downstream categoriser this is immaterial (categorisation tolerates paraphrase).
  • Not financial advice. This model extracts and structures; it does not advise.
  • Trained and evaluated in English + Hindi/Hinglish narration contexts.
  • Speed is hardware-bound: on unified-memory devices, single-stream decode is memory-bandwidth-limited (~40 tok/s for Q8 on a GB10-class chip).

Positioning

Cloud statement-analysis tools face a paradox: you upload your most sensitive document to a server to have sensitive data processed. FinAnalyzer is built to run entirely on-device โ€” the privacy claim is architectural, not a promise. Amounts and balances are verified by deterministic code, not asserted by a language model.

Citation / provenance

Built on Qwen3-4B-Instruct-2507. Synthetic-data + LoRA pipeline by Ganmoor AI Labs. Evaluated against the independent AgamiAI/Indian-Bank-Statements benchmark. Apache-2.0.

Downloads last month
388
GGUF
Model size
2B params
Architecture
qwen3
Hardware compatibility
Log In to add your hardware

4-bit

8-bit

Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for ganmoor-ai-labs/finanalyzer-indian-bank-statements

Adapter
(5707)
this model

Space using ganmoor-ai-labs/finanalyzer-indian-bank-statements 1