watchfire
[!IMPORTANT] This package is still in development and is not production ready.
Static analysis for UK financial regulatory citations in Python code.
watchfire lets you annotate Python functions with citations to UK
financial regulation — CRR, the PRA Rulebook, PRA Policy Statements,
PRA Supervisory Statements — and then check those citations against a
bundled, versioned snapshot of the rulebook. The intent is to make the
mapping from compliance code to the regulation it implements executable:
runs in CI, lives next to the code, and breaks the build when it drifts.
Why this exists
Regulatory engineering teams in UK banks need an auditable trail from
every formula in their RWA / capital code back to a specific article of
the CRR or rule in the PRA Rulebook. Today that trail is produced by
hand in Word documents that drift the moment anyone changes the code.
watchfire puts the mapping where the code is, checks it on every commit,
and gives reviewers, auditors, and the PRA something verifiable to look
at instead of a stale spreadsheet.
The first real user is
OpenAfterHours/rwa_calculator
— a UK CRR / Basel 3.1 credit-risk RWA library whose formulas need to
trace back to specific articles. If you're building something similar,
watchfire is for you.
Install
uv add watchfire
# or
pip install watchfire
Python 3.11+.
Quickstart
Decorate a function with the regulation it implements:
from watchfire import cites
@cites("CRR Art. 153(1)(a)")
def corporate_rw(pd: float, lgd: float, maturity: float) -> float:
"""Risk-weight under the IRB approach for corporates."""
...
Add a [tool.watchfire] table to your pyproject.toml:
[tool.watchfire]
rulebook_version = "2024-07-09"
instruments = ["CRR", "PRA_RULEBOOK", "PS", "SS"]
source_paths = ["src"]
Run the checker:
$ uv run watchfire check
watchfire: checked 47 citation(s); no issues found.
If a citation fails to parse or points at something the bundled index
doesn't know about, watchfire check exits non-zero and prints a line
per finding with file, line number, and reason — suitable for CI:
src/myproj/sa.py:31: sovereign_rw: unknown_article: citation 'CRR Art. 999' points to CRR Article 999, which is not in the bundled rulebook index
watchfire: 1 failing finding(s), 0 unresolved, out of 12 resolved citation(s).
@cites is a no-op at runtime — it attaches the parsed citations to
the function as a tuple[Citation, ...] on __watchfire__ and returns
the function unchanged. No wrapping, no overhead, nothing to debug.
Multiple @cites decorators stack when the same rule lives in more
than one instrument (for example a CRR article and its corresponding
PRA Policy Statement). The outermost decorator is the primary citation
and appears first in __watchfire__; watchfire check reports one
finding per decorator.
@cites("CRR Art. 163") # outer / primary
@cites("PS1/26, paragraph 163") # inner / secondary
def apply_pd_floor(...): ...
Traceability matrix
watchfire matrix is the reverse lookup: given a project full of
decorated functions, group every citation by its article and list the
functions that cite it. The output is intended as an audit deliverable —
attach it to a PR or commit it as a CI artifact.
$ uv run watchfire matrix
CRR Art. 4(1)(75) Definitions: corporate 1 site
src/myproj/irb.py:21 is_corporate
CRR Art. 113 SA risk weights 1 site
src/myproj/sa.py:6 calculate_sa_rwa
CRR Art. 153 IRB risk weights 1 site
src/myproj/irb.py:7 corporate_rw CRR Art. 153(1)(a)
SS1/23, paragraph 2.5 (not in index) 1 site
src/myproj/irb.py:17 model_validation
watchfire matrix: 4 entries, 4 citation sites across 4 functions.
Useful flags:
--format {text,markdown,json}—markdownproduces a copy-paste table for PR comments;jsonis for downstream tooling.--specificity {article,full}— defaultarticlecollapses sub-paragraph detail into one row per article. Usefullfor the audit-grade view that keeps(1)(a)separate from(1)(b).--instrument CRR --article 153— narrow to one article. Answers "which functions cite Art. 153?".
watchfire matrix exits 0 unconditionally; it's informational. Parse
failures and unresolved citations are counted in the footer — fix them
with watchfire check.
Citation grammar
The parser accepts canonical UK regulatory citation strings. The shape
that comes out the other side is a frozen Citation dataclass; see
watchfire.Citation for the field list.
| Input | Meaning |
|---|---|
CRR Art. 153 |
Whole article |
CRR Article 153 |
(alternate spelling) |
CRR Art. 153(1) |
Paragraph |
CRR Art. 153(1)(a) |
Point |
CRR Art. 153(1)(a)(ii) |
Sub-point |
CRR Art. 4(1)(75) |
Numeric point (CRR definitions) |
CRR Art. 92a |
Inserted article with letter suffix |
CRR Art. 153(1a) |
Inserted paragraph with letter suffix |
PRA Rulebook, Credit Risk, 3.2 |
Rulebook section |
PS9/24 |
PRA Policy Statement, whole document |
SS1/23, paragraph 2.5 |
Supervisory Statement with paragraph reference |
PS1/26, paragraph 123B |
Alphanumeric paragraph identifier |
PS1/26 Art. 111(1)(a) |
PS-attached rulebook instrument with CRR-style article |
Delegated Regulation 2018/171 Art. 3 |
UK on-shored EU Delegated Regulation |
The keyword for an article accepts Art, Art., Article, or article
in any case. Whitespace is normalised. Letter suffixes are accepted on
article numbers, on CRR paragraph numbers, and on dotted segments of
PS/SS paragraph identifiers; each segment must still start with digits.
Article suffixes are canonicalised to lowercase (92A parses as 92a)
to match the bundled index.
PRA Rulebook section paths are digits-only. Anything that doesn't parse
is a CitationParseError, which watchfire check reports with the
offending input — these are code-review events, not silent skips.
Configuration reference
[tool.watchfire]
# Snapshot of the rulebook to pin to. Decorators that omit `version=`
# inherit this pin. ISO-8601 date.
rulebook_version = "2024-07-09"
# Citation instruments allowed in this project. A citation whose
# instrument is not in this list is reported by `watchfire check`.
instruments = ["CRR", "PRA_RULEBOOK", "PS", "SS", "DELEGATED_REG"]
# Directories to walk when running `watchfire check` with no arguments.
source_paths = ["src"]
Public API
from watchfire import (
Citation, # frozen dataclass: instrument, article, paragraph, ...
parse_citation, # str -> Citation, raises CitationParseError
CitationParseError,
cites, # the @cites decorator
)
Everything else (watchfire.ast_walker, watchfire.index, watchfire.checks,
watchfire.cli) is internal and may change between releases.
Roadmap
watchfire v0.1 is intentionally a narrow vertical slice: get the
citation grammar right against real usage in rwa_calculator, ship the
decorator and CLI, then expand.
| Version | Adds |
|---|---|
| v0.1 | Citation grammar, @cites, watchfire check, bundled CRR index |
| v0.2 | watchfire matrix (traceability matrix), watchfire stale (rulebook diff) |
| v0.3+ | Automated scraping of legislation.gov.uk + the PRA Rulebook |
If you have feedback on the citation grammar specifically, please open an issue — the grammar is the foundation, and getting it wrong now is much cheaper to fix than getting it wrong later.
Licence
Apache 2.0. See LICENSE.
Release files for watchfire 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| watchfire-0.3.1.tar.gz | 866.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| watchfire-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.6 MB
Release files / watchfire-0.3.1.tar.gz
| Download URL | watchfire-0.3.1.tar.gz |
|---|---|
| Size | 866.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f535ba81ae8a690ddd65a68d6dc1207a2718cd15d1e94873804436bae7c4296b
|
|
BLAKE2b-256 checksum How to use checksums |
2b9b3e237c25d80da18e7e8df9d34fe42830ea91d418ba32df39fec3a88f9c08
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 16, 2026.
Transparency logRelease files / watchfire-0.3.1-py3-none-any.whl
| Download URL | watchfire-0.3.1-py3-none-any.whl |
|---|---|
| Size | 747.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a979554192d00d88d9d9dcce0218ee8cf45388f7b640b23287c24e06d0787e16
|
|
BLAKE2b-256 checksum How to use checksums |
9926222918e527457aefabf100aeb8c9d5c6ba7092856b4d92ca103452fb2f06
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 16, 2026.
Transparency log