Skip to content

Getting started

This tutorial generates a synthetic report for a small template, reads the package back with dpm-xbrl and checks the rules with dpm-xl. It takes about ten minutes.

Install

pip install dpm-synth

dpm-synth needs Python 3.11 or newer. It installs dpm-xl, which evaluates DPM-XL rules, and dpm-xbrl, which reads and writes EBA reports.

The template

A template describes one module: its tables, the cells of each table, and the rules a report must satisfy. It is a JSON document in the dpm-synth-template/1 format. This is minimal.json:

{
  "format": "dpm-synth-template/1",
  "release": "1.0",
  "module": "DEMO",
  "entry_point": "https://example.com/taxonomy/demo/1.0/mod/demo.json",
  "reference_date": "2026-12-31",
  "tables": [
    {
      "code": "T_01.00",
      "cells": [
        {"id": "T_01.00/r0010", "row": "0010", "column": "0010", "datapoint": "dp1001", "data_type": "m"},
        {"id": "T_01.00/r0020", "row": "0020", "column": "0010", "datapoint": "dp1002", "data_type": "m"},
        {"id": "T_01.00/r0030", "row": "0030", "column": "0010", "datapoint": "dp1003", "data_type": "m"}
      ]
    }
  ],
  "rules": [
    {"code": "v1", "expression": "with {tT_01.00, c0010, default: 0, interval: true}: {r0010} = {r0020} + {r0030}"},
    {"code": "v2", "expression": "with {tT_01.00, c0010}: {(r0020, r0030)} >= 0"}
  ]
}
  • Table T_01.00 has three monetary cells (data_type m) in rows 0010 to 0030 of column 0010. Each cell names the datapoint that carries its value in the package.
  • Rule v1 says row 0010 is the sum of rows 0020 and 0030; rule v2 says both components are zero or more.

For real modules you do not write templates by hand: any tool that reads a DPM release can export them; see build a template from a DPM release. To write your own, see write a template by hand.

Generate from the command line

$ dpm-synth generate minimal.json -o report.zip --seed 7
rules: 2, passed: 2, failed: 0, not evaluated: 0, not applicable: 0
rounds: 2 (failing rules per round: 1, 0)
wrote report.zip (3 facts)
$ echo $?
0

The first round found one failing rule (the random total did not match its components), repaired it, and the second round found none. Exit status 0 means every rule that could be evaluated passes; 1 would mean some still fail. The package is written either way.

Read the package back

report.zip is an EBA xBRL-CSV report package. Read it with dpm-xbrl:

from dpmxbrl import read_report

report = read_report("report.zip")
print(report.entity_id, report.reference_date, report.filing_indicators)
for fact in report.facts:
    print(fact.template, fact.datapoint, fact.value)
print([finding.code for finding in report.filing])
SYNTHETICDPMXL000002.CON 2026-12-31 {'T_01.00': True}
t_01.00 dp1001 1126000
t_01.00 dp1002 971000
t_01.00 dp1003 155000
['invalidFileName']

The entity is the fictional LEI SYNTHETICDPMXL000002 at the consolidated level. The only EBA filing rule the package breaks is the file name, which for a real submission encodes the entity, module and dates; name the output accordingly if that matters to you.

Generate from Python

The same from Python, with the result of the repair loop:

from dpmxbrl import write_xbrl_csv

from dpmsynth import generate, load_template

template = load_template("minimal.json")
result = generate(template, seed=7)
print(f"{result.passed} passed, failed: {result.failed}")
print(f"rounds: {result.rounds}, failing per round: {result.history}")
write_xbrl_csv(result.report, "report.zip")
2 passed, failed: []
rounds: 2, failing per round: [1, 0]

result.report is a dpmxbrl.Report; result.data holds the generated values and is a dpm-xl data provider, so you can evaluate any rule on it:

from dpmxl import evaluate_rule

rule = "with {tT_01.00, c0010}: {r0010} >= {r0020}"
print(evaluate_rule(rule, result.data).status)
passed

A bigger template

sample.json has a closed table with row and column totals, a ratio and an enumerated cell, an open table keyed by an enumerated property, an open table keyed by LEIs, and a time_shift rule that needs last year's report:

$ dpm-synth generate sample.json -o sample.zip --seed 7
rules: 9, passed: 8, failed: 0, not evaluated: 1, not applicable: 0
rounds: 5 (failing rules per round: 5, 3, 1, 1, 0)
wrote sample.zip (20 facts)

The time_shift rule is "not evaluated": a single report has no previous period. The evaluate-and-repair loop explains the rounds.

Next steps