Skip to main content

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} — markdown produces a copy-paste table for PR comments; json is for downstream tooling.
  • --specificity {article,full} — default article collapses sub-paragraph detail into one row per article. Use full for 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)

Source distribution for watchfire 0.3.1
File Size Uploaded
watchfire-0.3.1.tar.gz 866.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for watchfire 0.3.1
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.3.1 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page