- name
- financials
- description
- Extract structured financials from Icelandic annual report PDFs with pdfplumber (Docling optional) — income statement, balance sheet, cash flow, ratios.
# Financials (Annual Report Extraction)
**Requires:** Tier 0 (core) — pdfplumber pipeline. Docling OCR path needs Tier 3 (pdf).
Extract structured financial data from Icelandic annual reports (ársreikningar) using AI-powered PDF parsing.
## Overview
This skill combines:
1. **skatturinn** skill - Downloads annual report PDFs
2. **Docling** - IBM's PDF extraction with 97.9% table accuracy
3. **Claude interpretation** - Standardizes extracted data into structured format
## Pipeline
```
Company kennitala
↓
skatturinn.py download (PDF)
↓
Docling (markdown + tables)
↓
Claude (structured JSON)
↓
Standardized financial data
```
## Output Schema
```typescript
interface CompanyFinancials {
// Identification
company_name: string;
kennitala: string;
fiscal_year: number;
report_type: "full" | "hnappurinn" | "consolidated";
// Income Statement (Rekstraryfirlit)
income: {
revenue: number; // Rekstrartekjur
operating_expenses: number; // Rekstrargjöld
ebitda: number; // Afkoma fyrir afskriftir
depreciation: number; // Afskriftir
ebit: number; // Afkoma fyrir fjármagnsliði
financial_income: number; // Fjármunatekjur
financial_expenses: number; // Fjármagnsgjöld
profit_before_tax: number; // Afkoma fyrir skatt
income_tax: number; // Tekjuskattur
net_profit: number; // Hagnaður/Tap ársins
};
// Balance Sheet (Efnahagsyfirlit)
balance: {
// Assets (Eignir)
fixed_assets: number; // Fastafjármunir
current_assets: number; // Veltufjármunir
total_assets: number; // Eignir samtals
// Equity & Liabilities
share_capital: number; // Hlutafé
retained_earnings: number; // Óráðstafað eigið fé
total_equity: number; // Eigið fé samtals
long_term_debt: number; // Langtímaskuldir
short_term_debt: number; // Skammtímaskuldir
total_liabilities: number; // Skuldir samtals
};
// Cash Flow (if available)
cashflow?: {
operating: number; // Handbært fé frá rekstri
investing: number; // Fjárfestingarhreyfingar
financing: number; // Fjármögnunarhreyfingar
net_change: number; // Breyting á handbæru fé
};
// Ownership (from skatturinn page or PDF)
ownership: Array<{
name: string;
kennitala?: string; // If company owner
birth_year_month?: string; // If individual (privacy)
percentage: number;
type: "direct" | "indirect";
}>;
// Corporate Structure
parent_company?: {
name: string;
kennitala: string;
ownership_pct: number;
};
subsidiaries: Array<{
name: string;
kennitala: string;
ownership_pct: number; // How much THIS company owns
book_value?: number; // Bókfært virði
equity_method: boolean; // Hlutdeildaraðferð (20-50% ownership)
}>;
associates: Array<{ // Hlutdeildarfélög (20-50% ownership)
name: string;
kennitala: string;
ownership_pct: number;
book_value?: number;
}>;
// Key Metrics (calculated)
metrics: {
equity_ratio: number; // Eiginfjárhlutfall
current_ratio?: number; // Veltufjárhlutfall
debt_to_equity?: number; // Skuldir/Eigið fé
profit_margin?: number; // Hagnaðarhlutfall
};
// Notable Events
events: Array<{
type: "acquisition" | "investment" | "dividend" | "restructuring" | "other";
description: string;
amount?: number;
}>;
// Data Quality
extraction: {
source_pdf: string;
extracted_at: string;
confidence: "high" | "medium" | "low";
notes: string[];
};
}
```
## Bank-Specific Schema
Banks have a different financial structure than commercial companies:
```typescript
interface BankFinancials {
bank_name: string;
kennitala: string;
fiscal_year: number;
report_type: "parent" | "consolidated" | "quarterly";
// Income Statement (different from commercial companies)
income: {
interest_income: number; // Vaxtatekjur
interest_expense: number; // Vaxtagjöld
net_interest_income: number; // Hreinar vaxtatekjur (KEY METRIC)
fee_income: number; // Þóknanatekjur
fee_expense: number; // Þóknanagjöld
net_fee_income: number; // Hreinar þóknanatekjur
total_operating_income: number; // Rekstrartekjur samtals
total_operating_expense: number;// Rekstrarkostnaður
bank_tax: number; // Sérstakur skattur á fjármálafyrirtæki
impairment: number; // Virðisrýrnun
net_profit: number; // Hagnaður
};
// Balance Sheet
balance: {
loans_to_customers: number; // Lán til viðskiptavina
deposits_from_customers: number;// Innlán frá viðskiptavinum
total_assets: number; // Eignir samtals
total_equity: number; // Eigið fé samtals
};
// Regulatory Capital (Basel III)
capital: {
cet1_ratio: number; // CET1 hlutfall
tier1_ratio: number; // Eiginfjárþáttur 1
total_car: number; // Eiginfjárhlutfall (CAR)
rwa: number; // Áhættugrunnar (Risk-Weighted Assets)
lcr: number; // Lausafjárþekjuhlutfall
nsfr: number; // Fjármögnunarhlutfall
};
// Key Metrics
metrics: {
roe: number; // Return on Equity
nim: number; // Net Interest Margin (KEY)
cost_income_ratio: number; // Kostnaðarhlutfall
npl_ratio: number; // Non-performing loans
dividend_total: number; // Total dividends paid
buybacks: number; // Share repurchases
payout_ratio: number; // Dividend + buybacks / profit
};
}
```
## Icelandic Financial Terms
| Icelandic | English | Schema Field |
|-----------|---------|--------------|
| Rekstrartekjur | Revenue | income.revenue |
| Rekstrargjöld | Operating expenses | income.operating_expenses |
| Afskriftir | Depreciation | income.depreciation |
| Fjármunatekjur | Financial income | income.financial_income |
| Fjármagnsgjöld | Financial expenses | income.financial_expenses |
| Tekjuskattur | Income tax | income.income_tax |
| Hagnaður ársins | Net profit | income.net_profit |
| Fastafjármunir | Fixed assets | balance.fixed_assets |
| Veltufjármunir | Current assets | balance.current_assets |
| Hlutafé | Share capital | balance.share_capital |
| Eigið fé | Equity | balance.total_equity |
| Langtímaskuldir | Long-term debt | balance.long_term_debt |
| Skammtímaskuldir | Short-term debt | balance.short_term_debt |
| Arðgreiðslur | Dividends | events (type: dividend) |
| Dótturfélög | Subsidiaries | subsidiaries[] |
| Hlutdeildarfélög | Associates (20-50%) | associates[] |
| Móðurfélag | Parent company | parent_company |
| Eignarhlutir í dótturfélögum | Shares in subsidiaries | subsidiaries[].book_value |
| Samstæða | Group/Consolidated | report_type: "consolidated" |
## CLI Usage
### Standard Companies
```bash
# Extract financials from a downloaded PDF
uv run python scripts/financials.py extract /path/to/report.pdf
# Full pipeline: download + extract for a company
uv run python scripts/financials.py company 5012043070 --year 2024
# Output as JSON
uv run python scripts/financials.py company 5012043070 --year 2024 --format json
```
### Banks (Arion, Íslandsbanki, Landsbankinn)
Banks have different financial statement structures. Use the `--bank` flag or dedicated `bank` command:
```bash
# Extract bank financials from PDF (local file)
uv run python scripts/financials.py extract /path/to/arion_2024.pdf --bank
# Full pipeline for banks
uv run python scripts/financials.py bank 5810080150 --year 2024 # Arion
uv run python scripts/financials.py bank 4910083880 --year 2024 # Íslandsbanki
uv run python scripts/financials.py bank 4710044100 --year 2024 # Landsbankinn
# Output JSON schema for banks
uv run python scripts/financials.py bank-schema
```
### Bank Kennitalas
| Bank | Kennitala |
|------|-----------|
| Arion banki hf. | 5810080150 |
| Íslandsbanki hf. | 4910083880 |
| Landsbankinn hf. | 4710044100 |
| Kvika banki hf. | 5407992500 |
## Docling Integration
### Setup
```bash
uv sync
# Docling downloads models on first run (~500MB)
```
### Basic Extraction
```python
from docling.document_converter import DocumentConverter
def extract_pdf(pdf_path: str) -> tuple[str, list[dict]]:
"""Extract markdown and tables from PDF."""
converter = DocumentConverter()
result = converter.convert(pdf_path)
markdown = result.document.export_to_markdown()
tables = []
for table in result.document.tables:
tables.append({
"dataframe": table.export_to_dataframe().to_dict(),
})
return markdown, tables
```
### Table Detection
Docling's TableFormer model handles:
- Multi-row headers
- Merged cells
- Nested tables
- Currency formatting (ISK with dots: 1.234.567)
## Claude Interpretation
After Docling extracts the content, use Claude to interpret and standardize:
```python
EXTRACTION_PROMPT = '''
Extract structured financial data from this Icelandic annual report.
DOCUMENT:
{markdown}
TABLES:
{tables_json}
Return a JSON object matching this schema:
{schema}
Guidelines:
1. All amounts in ISK (Icelandic króna), no thousands separators
2. Negative values for expenses/losses (use actual signs from document)
3. Calculate metrics if raw data available
4. Note any unusual items in events[]
5. Set confidence based on data completeness
6. If "Hnappurinn" format, mark report_type accordingly
Return ONLY valid JSON, no markdown.
'''
```
## Report Types
### Hnappurinn (Micro-company)
- 4-page simplified format
- No detailed notes
- No ownership section in PDF
- Fields: revenue, expenses, profit, basic balance sheet
### Full Ársreikningur
- Complete financial statements
- Auditor's report
- Detailed notes
- Ownership/shareholder tables
- Cash flow statement
### Consolidated (Samstæðureikningur)
- Group financials
- Subsidiary breakdown
- Intercompany eliminations
## Data Quality Notes
1. **Currency**: All figures in ISK. Watch for thousands (þús.kr.) vs millions (m.kr.) notation.
2. **Fiscal Year**: Most companies use calendar year, but some differ. Check "Reikningsár" field.
3. **Comparatives**: Reports show current year + prior year. Extract both for trend analysis.
4. **Rounding**: Hnappurinn reports often round to thousands. Full reports may have exact figures.
5. **Negative Signs**: Expenses sometimes shown as positive numbers with context, sometimes with parentheses or minus signs.
## Integration with skatturinn
```python
from scripts.skatturinn import download_annual_report, get_company_info
from scripts.financials import extract_financials
async def get_company_financials(kennitala: str, year: int):
"""Full pipeline: fetch PDF, extract, standardize."""
# Get company info (includes ownership from page)
info = await get_company_info(kennitala)
# Download the PDF
pdf_path = await download_annual_report(kennitala, year)
# Extract with Docling + Claude
financials = extract_financials(pdf_path, info)
return financials
```
## Evidence Integration
Store extracted financials in `/data/processed/financials/`:
```
/data/processed/financials/
/{kennitala}/
/2024.json # Full extracted data
/2023.json
/summary.csv # Multi-year comparison
```
Example query — company trends (DuckDB):
```sql
SELECT
fiscal_year,
income_revenue,
income_net_profit,
balance_total_equity,
metrics_equity_ratio
FROM read_json_auto('../data/processed/financials/5012043070/*.json')
ORDER BY fiscal_year
```
## Corporate Structure Mapping
### Data Sources
| Data | Source | Method |
|------|--------|--------|
| Parent company | skatturinn page | Beneficial owners with company kennitala |
| Subsidiaries | Annual report PDF | Notes section, "Eignarhlutir í dótturfélögum" |
| Associates | Annual report PDF | "Hlutdeildarfélög" section |
| Group structure | Recursive lookup | Follow kennitalas both directions |
### Deriving Parent Company
From skatturinn ownership scraping, if a beneficial owner has a company kennitala (starts with 4-7), that's a parent company:
```python
for owner in company.beneficial_owners:
if owner.is_company: # kennitala[0] in "4567"
financials.parent_company = {
"name": owner.name,
"kennitala": owner.kennitala,
"ownership_pct": owner.ownership_pct
}
```
### Extracting Subsidiaries from PDF
Look for these patterns in annual report notes:
```
Eignarhlutir í dótturfélögum:
| Nafn | Kennitala | Eignarhlutur | Bókfært virði |
|------|-----------|--------------|---------------|
| ABC ehf. | 1234567890 | 100% | 50.000.000 |
```
Also check balance sheet line "Eignarhlutir í dóttur- og hlutdeildarfélögum" - if > 0, there are subsidiaries.
### Ownership Classifications
| Ownership % | Classification | Accounting |
|-------------|----------------|------------|
| >50% | Dótturfélag (Subsidiary) | Full consolidation |
| 20-50% | Hlutdeildarfélag (Associate) | Equity method |
Ver no GitHub