Extraction d'états financiers

Extrait les chiffres du compte de résultat, du bilan et du tableau des flux de trésorerie à partir de rapports PDF d'entreprise (rapports annuels, trimestriels, y compris les scans) dans une base de données relationnelle, avec des vérifications arithmétiques pour ne charger que des données fiables.

Qu'est-ce que Extraction d'états financiers ?

Cette compétence transforme les rapports PDF en faits financiers vérifiés : `financial_fact(document, période, libellé, périmètre, valeur, unité, statut d'audit)`. Développée et éprouvée sur deux années de dépôts à la Bourse de Colombo (2 707 rapports, 720 000 chiffres, 99,4 % complètement chargés). La méthode repose sur un analyseur automatique qui traite la majorité des rapports, des vérifications arithmétiques décident quels chiffres sont fiables, une étape de tri explique pourquoi chaque état restant a échoué, et seuls ces états sont lus à partir d'images de pages, par lots, par des sous-agents. Chaque modification n'atteint la base publiée qu'après un chargement par étapes et un audit. Elle prend en charge les rapports PDF standard, les scans, et fonctionne avec des sous-agents ou en mode direct. Idéale pour extraire, charger, récupérer ou auditer des chiffres financiers, construire ou étendre une base de données financière à partir de dépôts boursiers, et corriger des rapports partiels ou échoués.

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/AdhishaInd/agent-skills/tree/HEAD/skills/financial-statement-extraction

Installed? Explore more Recherche et analyse de données skills: affaan-m/uspto-database, affaan-m/scholar-evaluation, affaan-m/literature-review · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait Extraction d'états financiers ?

Transforme les rapports PDF en faits vérifiés : financial_fact(document, période, libellé, périmètre, valeur, unité, statut d'audit). Développée et éprouvée sur deux années de dépôts à la Bourse de Colombo (2 707 rapports, 720 000 chiffres, 99,4 % complètement chargés). La méthode est ce qui la rend rapide : un analyseur automatique traite la plupart des rapports, des vérifications arithmétiques décident ce qui est fiable, une étape de tri explique exactement pourquoi chaque état restant a échoué, et seuls ces états sont lus à partir d'images de pages, par lots, par des sous-agents. Chaque modification n'atteint la base publiée qu'après un chargement par étapes et un audit.

La règle unique sur laquelle tout repose

Un chiffre ne se charge que si sa colonne passe les vérifications arithmétiques (actif = passif + capitaux propres ; résultat avant impôt - impôt = résultat net ; part du groupe + intérêts minoritaires = résultat ; sections du tableau de flux = variation nette ; solde d'ouverture + variation = solde de clôture). Rien n'est ajusté pour faire passer une vérification. Les vérifications n'autorisent que les arrondis imprimés (quelques unités du dernier chiffre imprimé : 2 pour deux termes, 3 pour trois, 5 pour plus), donc un total décalé de 1 à 2 unités par rapport à ses parties passe, et davantage est une erreur de lecture ou d'impression. C'est pourquoi un analyseur, un moteur OCR, une transcription visuelle et un humain peuvent tous alimenter la même base : les vérifications, pas la source, décident. Lorsqu'une vérification échoue, soit la lecture est erronée (relire), soit l'impression est erronée (enregistrer pourquoi, laisser de côté).

Corollaires importants en pratique :

  • Les libellés restent exactement tels qu'imprimés et correspondent aux lignes standard uniquement via des alias exacts (label_alias), jamais par correspondance floue. Un libellé mal imprimé rend un état non chargeable plutôt que silencieusement erroné.
  • Un total imprimé sans libellé peut recevoir un libellé entre crochets ([Total actif]) car sa position le définit.
  • "UNCHECKED" (aucune vérification possible) n'est pas "FAIL". Cela signifie généralement une mise en page que le vérificateur ne connaît pas encore - corrigez le vérificateur une fois, et chaque rapport avec cette mise en page se chargera (voir références/checks.md).

Où se trouvent les choses

  • scripts/pipeline/ - le pipeline : parse_report.py (PDF -> tableaux JSON), segment.py, columns.py, tokens.py, checks.py, ocr_words.py (OCR en tant que couche de texte factice), load_facts.py (JSON -> BD, uniquement colonnes passantes), vision_tables.py (transcriptions d'images -> tableaux JSON), extract_all.py, normalize.py. Carte, formes de données et comment l'adapter à un autre marché : références/pipeline.md.
  • scripts/triage.py, scripts/make_batches.py, scripts/render.py, scripts/stage_publish.py - les outils pour les étapes ci-dessous.
  • assets/schema.sql (base de données), assets/dictionary.sql (252 lignes standard + 471 alias exacts de libellés), assets/worker_brief.md (brief pour les travailleurs de transcription), assets/examples/ (fichiers de transcription).
  • Le répertoire du projet : là où se trouvent la base de données, les PDF et les sorties. L'utilisateur le nomme dans CLAUDE.md / AGENTS.md (par ex. "Projet financier : ~/stocks/cse_financials") ; demandez s'il n'y est pas. Structure : db/<nom>.db, reports/<SYMBOLE>/<doc>.pdf, sortie de l'analyseur extracted_final/, source publiée extracted_final_v/, transcriptions reviewed/vision/, notes docs/. Si le projet a son propre pipeline/, traitez-le comme le code actuel (cette copie de la compétence peut être plus ancienne).

Exécutez toujours Python avec -I (isolé) et CSE_OCR=0 sauf si l'OCR est souhaité (le préfixe CSE_ est historique). Le PDF d'un rapport : select local_path from report_document where document_id=? (relatif au répertoire du projet), puis python -I scripts/render.py <pdf> <pages> <out_dir> --text pour extraire la couche de texte et rendre les pages.

Flux de travail

1. Périmètre et téléchargement

Choisissez la plage de périodes ; prenez les dates de fin de période dans les titres des rapports (les dates de dépôt boursières ne sont pas fiables). Remplissez les lignes report_document (une par rapport : société, catégorie ANNUAL/INTERIM, titre, fin de période, URL source), puis téléchargez en parallèle et de manière reprenable. La partie spécifique à la bourse (API, URL, particularités) appartient à une compétence associée - cse-financials pour Colombo. Conservez chaque bloc d'années comme sa propre exécution de ces étapes. Avant un nouveau marché ou de nouvelles années, lisez références/traps.md : il liste les pièges de données qui coûtent le plus de temps.

2. Analysez tout avec une version de l'analyseur

python -I scripts/pipeline/extract_all.py <db> <out_dir> --workers N avec CSE_OCR=0 (environ 9 min pour 2 700 rapports), puis un passage OCR séparé pour les scans (GPU si disponible ; voir les pièges). Après toute modification de l'analyseur, ré-analysez tous les rapports et comparez par rapport - des ré-analyses partielles rendent la base de données non reproductible.

3. Chargez et lisez le statut

load_facts.py (via stage_publish.py, étape 6) donne à chaque rapport le statut EXTRACTED (IS et BS chargés), PARTIAL ou FAILED. Attendez-vous à environ 80 % EXTRACTED rien qu'avec l'analyseur.

4. Trions ce qui reste - avant de regarder une page

python -I scripts/triage.py <db> <extracted_dir> triage.json --pdf-hints
python -I scripts/make_batches.py triage.json batches/ --target=12

Le tri donne pour chaque état manquant la raison (not_found, UNCHECKED, FAIL, no_columns, no_months, unit_unproven, pass_refused...) et où chercher. Lisez les comptages : un grand groupe avec une seule raison (par ex. 100 UNCHECKED) signifie souvent un manque dans le vérificateur ou une règle du chargeur - corriger le code vaut mieux que transcrire des centaines de pages. Vérifiez également si les rapports FAILED sont bien des états : les avis, listes d'actionnaires et erratas sont courants et nécessitent seulement une raison enregistrée.

5. Transcrivez à partir d'images, par lots

Chaque travailleur reçoit un fichier de lot et assets/worker_brief.md (remplissez les chemins). Il trouve la page d'état, la lit (d'abord la couche de texte, puis l'image pour la mise en page et tout ce qui est douteux), écrit reviewed/vision/<doc>.txt, exécute vision_tables.py sur ses documents jusqu'à ce que chaque colonne PASSE, et écrit bNN.result.json. Le travailleur a besoin d'un modèle qui peut lire les images. Format et règles : références/vision-format.md. Exécution des travailleurs en parallèle, limites d'utilisation, reprise et examen des résultats : références/orchestration.md. Sans sous-agents, travaillez sur les lots un après l'autre avec le même brief.

Pendant l'exécution des lots, lisez chaque résumé de résultat dès son arrivée :

  • "UNCHECKED parce que le vérificateur ne reconnaît pas X" récurrent -> étendez checks.py (références/checks.md), retestez les fichiers concernés avec vision_tables.py, ajoutez la règle au brief pour que les lots suivants la connaissent. Les tableaux déjà écrits passent alors sans retranscription.
  • Décisions discrétionnaires (statut d'audit, colonnes retraitées, périodes de longueur inhabituelle, libellés fournis) -> vérifiez qu'elles suivent le brief ; corrigez centralement (par ex. un sed sur les lignes COLS) plutôt que de relancer un travailleur.

6. Mettre en scène, auditer, publier

python -I scripts/stage_publish.py --project <dir>            # mise en scène + audit uniquement
python -I scripts/stage_publish.py --project <dir> --publish  # après avoir lu l'audit

La publication est refusée si l'audit montre des rapports quittant le statut EXTRACTED, des rapports devenant vides, des erreurs d'intégrité/FK, des valeurs aberrantes d'unités, ou des faits perdus/modifiés dans des rapports sans fichier de transcription (cela signifie qu'un changement de code a touché des données que personne n'a relues). Les gains en dehors des rapports vision sont normaux (un conflit résolu libère la colonne d'un autre rapport) mais regardez chacun d'eux. Publiez par étapes (les lots terminés d'abord) pour que le travail survive aux interruptions ; exécutez ensuite les tests du projet s'il en a.

7. Clôturez chaque état

L'objectif est : chaque état manquant est soit chargé, soit a une raison indiquée. Construisez la table de résultats par état (chargé / non chargé + raison à partir des lignes MISSING et des fichiers de résultats). Pour chaque état non chargé, vérifiez si les mêmes chiffres sont déjà chargés à partir d'un autre rapport (comparatifs de l'année suivante, l'intermédiaire correspondant) - généralement ils le sont, et cela transforme un "écart" en "couvert". Enregistrez les résultats dans la documentation du projet.

Économie de jetons et de temps

  • Trions d'abord ; n'envoyez jamais un travailleur pour "trouver ce qui ne va pas" sans la raison et les indices de page.
  • Couche de texte avant l'image : une couche de texte propre confirme les chiffres gratuitement ; l'image règle la mise en page. Rendez à 1500 px ; rognez (render.py --clip) au lieu de rendre des pages entières plus grandes. Les scans avec des couches de texte indésirables ("9,O4349L") nécessitent des images.
  • Regroupez par société : une mise en page, plusieurs trimestres. ~10 rapports ou ~12 unités de coût par lot, environ 6 travailleurs à la fois.
  • Les travailleurs écrivent le tableau complet même lorsqu'une vérification reste UNCHECKED, plus une ligne MISSING : si le vérificateur est étendu plus tard, le tableau se charge sans une autre lecture.
  • Corrigez les problèmes systématiques dans le code (vérificateur, chargeur) une fois, de manière centrale ; réexécutez vision_tables.py (quelques secondes) au lieu de relire les pages.
  • L'orchestrateur ne visualise pas les images de page sauf pour régler un litige ; il lit les résumés, exécute les vérifications et les audits.
  • Limitez les résumés des travailleurs à 3-6 lignes ; les détails appartiennent aux commentaires des fichiers.

Définition de "terminé"

Par rapport : EXTRACTED, ou PARTIAL/FAILED avec une raison pour chaque état manquant qui nomme la preuve imprimée (par ex. "total imprimé 3 115 995 977 vs equity + liabilities 3 115 995 968"), ou "couvert par le rapport N". Publié uniquement via stage_publish.py, avec une sauvegarde, un audit propre (ou chaque drapeau expliqué), des tests réussis, et la table de résultats plus une note datée dans la documentation du projet.

Skills associés