CallReports 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- callreports-0.1.0/MANIFEST.in +4 -0
- callreports-0.1.0/PKG-INFO +193 -0
- callreports-0.1.0/README.md +356 -0
- callreports-0.1.0/pyproject.toml +28 -0
- callreports-0.1.0/setup.cfg +4 -0
- callreports-0.1.0/src/CallReports.egg-info/PKG-INFO +193 -0
- callreports-0.1.0/src/CallReports.egg-info/SOURCES.txt +15 -0
- callreports-0.1.0/src/CallReports.egg-info/dependency_links.txt +1 -0
- callreports-0.1.0/src/CallReports.egg-info/entry_points.txt +2 -0
- callreports-0.1.0/src/CallReports.egg-info/requires.txt +2 -0
- callreports-0.1.0/src/CallReports.egg-info/top_level.txt +1 -0
- callreports-0.1.0/src/callreports/README.md +179 -0
- callreports-0.1.0/src/callreports/__init__.py +17 -0
- callreports-0.1.0/src/callreports/__main__.py +5 -0
- callreports-0.1.0/src/callreports/cli.py +39 -0
- callreports-0.1.0/src/callreports/definitions.py +7254 -0
- callreports-0.1.0/src/callreports/preparation.py +275 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: CallReports
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Date-aware FFIEC Call Report definitions and bulk-file preparation
|
|
5
|
+
Author: Jonathan Waller
|
|
6
|
+
Project-URL: Repository, https://github.com/OCBliss/Basel3
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: numpy>=1.24
|
|
13
|
+
Requires-Dist: pandas>=2.0
|
|
14
|
+
|
|
15
|
+
# CallReports
|
|
16
|
+
|
|
17
|
+
CallReports provides date-aware FFIEC Call Report definitions and local bulk-file
|
|
18
|
+
preparation. It uses pandas and NumPy for structured data handling. Its schedule
|
|
19
|
+
methods expose the reporting fields and calculations; users can choose their own
|
|
20
|
+
ratios without running Basel3's research pipeline.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
Python 3.8 or later is required, with compatible pandas and NumPy releases.
|
|
25
|
+
Published releases install from PyPI:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
python3 -m pip install CallReports
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
To install the current source directly, run this from the repository root:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
python3 -m pip install .
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For development, use `python3 -m pip install -e .`. The distribution is named
|
|
38
|
+
`CallReports`; the Python import and installed command are named `callreports`.
|
|
39
|
+
The repository's `requirements.txt` installs this local library along with the
|
|
40
|
+
additional dependencies needed by Basel3. Bank data is supplied separately and is
|
|
41
|
+
not downloaded during installation.
|
|
42
|
+
|
|
43
|
+
## Use an existing structured CSV
|
|
44
|
+
|
|
45
|
+
The CSV must contain one row per bank and reporting quarter, with the original
|
|
46
|
+
FFIEC code headers. Select one bank row before constructing a report:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
import pandas as pd
|
|
50
|
+
from callreports import CallReport_051, safe_divide
|
|
51
|
+
|
|
52
|
+
table = pd.read_csv("Cleaned_Call_Report_20240331.csv", low_memory=False)
|
|
53
|
+
row = table.loc[table["IDRSSD"] == 101].iloc[0]
|
|
54
|
+
report = CallReport_051(row)
|
|
55
|
+
date = "P0"
|
|
56
|
+
|
|
57
|
+
assets = report.BalanceSheet.TotalAssets(date=date)
|
|
58
|
+
trading_share = safe_divide(
|
|
59
|
+
report.BalanceSheet.TradingAssets(date=date),
|
|
60
|
+
report.BalanceSheet.TotalAssets(date=date),
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This example requires `IDRSSD`, `RCON9999`, `RCON2170`, `RCONJA22`,
|
|
65
|
+
`RCON3123` and `RCON3545`. `TotalAssets` retains the existing adjusted definition:
|
|
66
|
+
`RCON2170 - RCONJA22 + RCON3123` from 2018Q1 onward, and `RCON2170 + RCON3123`
|
|
67
|
+
earlier. For the raw reported field, use `report.Value("RCON2170", date=date)`.
|
|
68
|
+
Other methods require their own reporting fields. `Certificate()` additionally
|
|
69
|
+
requires `FDIC Certificate Number`. Pass numeric observations; pandas' CSV reader
|
|
70
|
+
usually infers these from FFIEC numeric columns. A dictionary or pandas Series
|
|
71
|
+
with the same headers can also supply a bank row. Missing current-quarter headers
|
|
72
|
+
raise `KeyError`; installing the library does not supply bank data.
|
|
73
|
+
|
|
74
|
+
`CallReport_051` uses domestic reporting fields. `CallReport_031` exposes its
|
|
75
|
+
separate consolidated form definitions, including `RCFD` fields. Choose the form
|
|
76
|
+
appropriate to the supplied data; this library does not automatically infer or
|
|
77
|
+
substitute reporting forms. FFIEC 041 is not a separate implemented class.
|
|
78
|
+
|
|
79
|
+
Methods retain the existing definitions, historical field changes, missing-value
|
|
80
|
+
behavior and `name=True` labels. `safe_divide` preserves missing observations and
|
|
81
|
+
returns zero for a known numerator divided by zero, matching the existing API.
|
|
82
|
+
Ordinary Python `/` uses the operands' own division behavior instead.
|
|
83
|
+
|
|
84
|
+
## Historical dates
|
|
85
|
+
|
|
86
|
+
`date="P0"` means the bound row's quarter. `P1Q`, `P2Q`, `P3Q` and `P1Y`
|
|
87
|
+
refer to one, two, three and four calendar quarters earlier. Supply the historical
|
|
88
|
+
rows explicitly; the library never downloads them or searches a Basel3 checkout:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from callreports import CallReport_051, QuarterCache
|
|
92
|
+
|
|
93
|
+
history = QuarterCache(frames={20231231: previous_quarter_table})
|
|
94
|
+
report = CallReport_051(row, history=history)
|
|
95
|
+
previous_assets = report.BalanceSheet.TotalAssets(date="P1Q")
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Historical rows match by `IDRSSD`, independently of row order. A `frames` mapping
|
|
99
|
+
may contain several quarter DataFrames; a single DataFrame with several quarters
|
|
100
|
+
is also accepted. For files, use `QuarterCache(data_dir="prepared")`; it reads
|
|
101
|
+
`Cleaned_Call_Report_YYYYMMDD.csv`, including the quarter folders produced below.
|
|
102
|
+
Ambiguous files and duplicate historical bank rows raise an error.
|
|
103
|
+
|
|
104
|
+
Without a supplied map, lag dates follow calendar quarter ends. An explicit
|
|
105
|
+
`map_file` path or DataFrame preserves custom mappings with columns `P0`, `P1Q`,
|
|
106
|
+
`P2Q`, `P3Q`, `P1Y` and optional `Quarter`. Missing historical observations remain
|
|
107
|
+
missing rather than being taken from the current quarter. The API expects valid
|
|
108
|
+
calendar quarter-end dates in `RCON9999` when using calendar lags.
|
|
109
|
+
|
|
110
|
+
## Prepare a downloaded FFIEC ZIP
|
|
111
|
+
|
|
112
|
+
Use a local **Call Reports — Single Period**, tab-delimited ZIP containing one
|
|
113
|
+
quarter's POR and ENT files and the desired schedules:
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
callreports prepare "Call_Reports_20240331.zip" --output-dir prepared
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The equivalent command is `python3 -m callreports prepare ...`. No network request
|
|
120
|
+
is made. The reporting date is inferred from the FFIEC filenames; `--date
|
|
121
|
+
20240331` can specify it explicitly and must agree with those filenames.
|
|
122
|
+
|
|
123
|
+
The result is:
|
|
124
|
+
|
|
125
|
+
```text
|
|
126
|
+
prepared/20240331/
|
|
127
|
+
├── TXT/ # extracted originals
|
|
128
|
+
├── Schedules/ # individual schedule CSVs
|
|
129
|
+
└── Cleaned_Call_Report_20240331.csv # merged bank-quarter table
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The Python equivalent returns the merged CSV's path:
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from callreports import prepare_archive
|
|
136
|
+
|
|
137
|
+
csv_path = prepare_archive("Call_Reports_20240331.zip", "prepared")
|
|
138
|
+
table = pd.read_csv(csv_path, low_memory=False)
|
|
139
|
+
report = CallReport_051(table.iloc[0])
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Preparation attaches FDIC certificates using POR, drops TXT description rows and
|
|
143
|
+
merges all available schedules by `IDRSSD`. Ancillary files such as `Readme.txt`
|
|
144
|
+
are retained in TXT but are not treated as schedules. ENT defines the bank population and
|
|
145
|
+
order. A repeated column retains its first value, with ENT first and remaining
|
|
146
|
+
schedule filenames sorted. It sets `RCON9999` to the validated reporting date and
|
|
147
|
+
removes the legacy `RCON9224` text field. Other fields, text values and missing
|
|
148
|
+
observations are preserved. This is a structural merge, not statistical cleaning.
|
|
149
|
+
|
|
150
|
+
Mixed quarters, duplicate bank identifiers, invalid archives and inconsistent
|
|
151
|
+
reporting dates fail explicitly. Preparation stages a complete quarter before
|
|
152
|
+
publishing its directory and refuses to replace an existing quarter directory.
|
|
153
|
+
Source ZIPs remain unchanged. Archive limits are 512 MiB compressed and 2 GiB
|
|
154
|
+
uncompressed; unsafe member paths, links and duplicate names are rejected.
|
|
155
|
+
|
|
156
|
+
## Use extracted TXT or schedule CSVs
|
|
157
|
+
|
|
158
|
+
Each command operates on one reporting quarter:
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
callreports convert "downloaded-quarter" --output-dir schedules
|
|
162
|
+
callreports merge schedules --output Cleaned_Call_Report_20240331.csv
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The corresponding functions are `convert_txt_to_csv(input_dir, output_dir)` and
|
|
166
|
+
`merge_schedules(input_dir, output_file=None, reporting_date=None)`.
|
|
167
|
+
`merge_schedules` returns a DataFrame, whether or not an output file is requested.
|
|
168
|
+
Lower-level `merge_schedule_files(base_file, files_to_append, output_file=None)`
|
|
169
|
+
supports an explicit subset of schedules, skips absent optional paths and returns
|
|
170
|
+
the merged DataFrame without applying the reporting-date cleanup.
|
|
171
|
+
|
|
172
|
+
## Basel3 integration and limits
|
|
173
|
+
|
|
174
|
+
Basel3 imports these definitions as a dependency. Its existing conversion and
|
|
175
|
+
merge entry points delegate to this library, while its selected schedule list,
|
|
176
|
+
76 research ratios, cohort definitions and distribution comparisons remain in
|
|
177
|
+
Basel3. The compatibility module `src/call reports/Call_Report.py` re-exports the
|
|
178
|
+
public definitions for existing callers.
|
|
179
|
+
|
|
180
|
+
The standalone merge includes all available schedules; Basel3 intentionally
|
|
181
|
+
retains its historical schedule selection. Local ZIP preparation does not include
|
|
182
|
+
Basel3's YAML-driven FFIEC downloader. Coverage depends on the implemented 031/051
|
|
183
|
+
methods and available source headers; it does not establish completeness across
|
|
184
|
+
all reporting forms, historical revisions or regulatory interpretations.
|
|
185
|
+
|
|
186
|
+
Package and existing Call Report regressions run from the repository root after
|
|
187
|
+
installing the development requirements:
|
|
188
|
+
|
|
189
|
+
```sh
|
|
190
|
+
python3 -m unittest discover -s tests -p 'test_callreports_package.py' -v
|
|
191
|
+
python3 -m unittest discover -s tests -p 'test_call_report*.py' -v
|
|
192
|
+
python3 -m unittest discover -s tests -p 'test_dynamic_ratios.py' -v
|
|
193
|
+
```
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
## Basel III Regulatory Analytics Pipeline
|
|
2
|
+
|
|
3
|
+
**Author:** Jonathan Waller\
|
|
4
|
+
**GitHub:** github.com/OCBLISS\
|
|
5
|
+
**Contact:** johnny.waller.nb@gmail.com\
|
|
6
|
+
**Affiliation:** Independent Researcher\
|
|
7
|
+
|
|
8
|
+
## Basel III Under Strain: Empirical Evidence from Call Report Data
|
|
9
|
+
|
|
10
|
+
This project implements a data pipeline used to test key assumptions of the Basel III regulatory framework, specifically the belief that regulatory capital adequacy and liquidity classifications (e.g., HQLA, NML stability) correspond to reduced failure risk.
|
|
11
|
+
|
|
12
|
+
Using FFIEC Call Report data, the system constructs bank cohorts (Failures, Mergers, Survivors) and evaluates their distributional behavior across multiple horizons (T+1–T+4). Jensen-Shannon Distance (JSD) is used to measure whether regulatory-compliant institutions exhibit statistically distinct (and safer) profiles relative to failing banks.
|
|
13
|
+
|
|
14
|
+
Results suggest that:
|
|
15
|
+
- Regulatory classifications fail to preserve monotonic risk ordering
|
|
16
|
+
- Duration risk and classification arbitrage distort capital adequacy signals
|
|
17
|
+
- Depositor behavior deviates from standard NML assumptions (dynamic, heterogeneous)
|
|
18
|
+
|
|
19
|
+
The pipeline in this repository produces the empirical outputs supporting these findings.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
1. Do Basel III capital and liquidity classifications
|
|
24
|
+
retain statistical separation between future failures
|
|
25
|
+
and survivors?
|
|
26
|
+
|
|
27
|
+
2. Does predictive separation deteriorate as the
|
|
28
|
+
forecast horizon increases?
|
|
29
|
+
|
|
30
|
+
3. Do depositor behaviors conform to homogeneous,
|
|
31
|
+
memoryless assumptions commonly used in regulatory
|
|
32
|
+
liquidity frameworks?
|
|
33
|
+
|
|
34
|
+
4. Can duration-sensitive interest-rate modeling
|
|
35
|
+
identify capital impairment not reflected in
|
|
36
|
+
regulatory risk weights?
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
# How to Run It
|
|
41
|
+
|
|
42
|
+
### Independent CallReports library
|
|
43
|
+
|
|
44
|
+
The reusable CallReports definitions and local ZIP/TXT preparation can be used
|
|
45
|
+
without running the Basel3 research pipeline. From the repository root:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
python3 -m pip install .
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from callreports import CallReport_051
|
|
53
|
+
report = CallReport_051(bank_row)
|
|
54
|
+
assets = report.BalanceSheet.TotalAssets(date="P0")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Supply a bank row with the method's FFIEC headers. The library is built on pandas
|
|
58
|
+
and NumPy and retains the existing adjusted asset definitions and historical-date
|
|
59
|
+
behavior. The [CallReports guide](src/callreports/README.md) documents CSV inputs,
|
|
60
|
+
user-defined ratios, prior quarters and the `callreports prepare` command for local
|
|
61
|
+
FFIEC ZIPs. `requirements.txt` installs the local library as an editable dependency
|
|
62
|
+
along with Basel3's additional requirements. A PyPI release is separate.
|
|
63
|
+
|
|
64
|
+
### OS Compatibility Note
|
|
65
|
+
- The pipeline was developed on macOS. Most scripts are platform-independent, although some file path handling and directory conventions may require minor adjustments on Windows.
|
|
66
|
+
|
|
67
|
+
### Performance Note
|
|
68
|
+
- The pipeline prioritizes reproducibility, modularity, and transparent workflow execution over low-level optimization. Many stages are embarrassingly parallel and can benefit from additional CPU resources when processing large historical FFIEC datasets. This pipeline was developed and tested on a high-memory multi-core workstation. The pipeline is compatible with smaller systems, but execution times will increase as available CPU, memory, and storage bandwidth decrease. Large historical datasets may benefit from additional memory or processing cores.
|
|
69
|
+
|
|
70
|
+
### How It Works
|
|
71
|
+
1. Install the dependencies from the repository root:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
python3 -m pip install --upgrade pip
|
|
75
|
+
python3 -m pip install -r requirements.txt
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The pipeline's first task downloads FFIEC **Call Reports — Single Period**, in tab-delimited format. It reads the material-event date ranges in the active YAML, includes three earlier quarters for the dynamic ratios, validates the ZIPs, and extracts them under `data/TXT/`. The current settings require 47 quarters, from 2013Q2 through 2024Q4. No bulk data needs to be copied from another checkout. See the [preparation and rerun instructions](src/readme.md).
|
|
79
|
+
2. `Basel3_Global_Filepath.py`:
|
|
80
|
+
- Finds the `Basel3/` root directory dynamically from its own location, then;
|
|
81
|
+
- Resolves `src/`, `data/` and `Material Events/` within that checkout.
|
|
82
|
+
3. Run the manager from the repository root:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
python3 pipeline/Task_manager_Global_yaml_driven.py --dry-run
|
|
86
|
+
python3 pipeline/Task_manager_Global_yaml_driven.py
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The dry run shows the expanded arguments and artifact paths without running scripts or creating logs. Use `--config path/to/config.yaml` for another configuration, or `--list` to list its tasks.
|
|
90
|
+
|
|
91
|
+
4. `pipeline/pipeline_config_dynamic.yaml` defines:
|
|
92
|
+
- `parameters`: Shared horizon and bin selections.
|
|
93
|
+
- `scripts`: Paths relative to `src/`, argument lists, inputs, outputs and directories to create.
|
|
94
|
+
- `dependencies`: Prerequisites that must complete before a task runs.
|
|
95
|
+
- `execution`: An ordered list of sequential or concurrent phases. Each phase completes before the next begins.
|
|
96
|
+
|
|
97
|
+
The manager expands parameter placeholders in both arguments and artifact paths, validates the plan, and runs each declared task once. Per-task logs are saved under `pipeline_logs/<run-id>/` by default.
|
|
98
|
+
|
|
99
|
+
### Select Horizons and Bins
|
|
100
|
+
|
|
101
|
+
Edit this block at the top of the YAML configuration:
|
|
102
|
+
|
|
103
|
+
```yaml
|
|
104
|
+
parameters:
|
|
105
|
+
horizons: [1, 2, 3, 4]
|
|
106
|
+
bins1: [20, 30, 50, 100]
|
|
107
|
+
bins2: 5
|
|
108
|
+
bins3: 1
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The default runs all four horizons and all four central bin counts. For an optional subset, `horizons: [1, 3]` and `bins1: [20, 50]` select only T+1/T+3 and those two central bin counts. The same selection reaches peer-group creation, calendar concatenation, binning, PMFs, JSD comparisons and the final filter. Expected input/output paths expand to that selection as well; no separate horizon scripts or duplicated YAML branches are needed.
|
|
112
|
+
|
|
113
|
+
`bins1` supplies the counts across −1 to 1, `bins2` supplies the count across 1 to 2, and `bins3` is the single unbounded upper bin. Thus `20 + 5 + 1` produces 26 bins. `bins1` accepts positive counts, `bins2` is a positive count, and `bins3` currently accepts only `1`. Output filenames retain the central count, such as `_binned_20.csv`; changing `bins2` updates that specification's outputs.
|
|
114
|
+
|
|
115
|
+
### Current Workflow
|
|
116
|
+
|
|
117
|
+
The current configuration has **20 tasks**:
|
|
118
|
+
|
|
119
|
+
| Tasks | Processing |
|
|
120
|
+
| --- | --- |
|
|
121
|
+
| `0.0` | Download and verify all required FFIEC reporting quarters before conversion |
|
|
122
|
+
| `1.0`, `2.0`, `2.1` | Convert, merge and numerically clean Call Reports |
|
|
123
|
+
| `3.0`, `4.0` | Build current-quarter ratios and dynamic ratios using historical rows joined by `CERT` |
|
|
124
|
+
| `6.0.1`–`6.0.3` | Call `Material_events_download.py` concurrently for de novo, failures and mergers |
|
|
125
|
+
| `6.1`, `6.1.1` | Clean the downloads, then remove matching RAW download files |
|
|
126
|
+
| `6.2`–`6.4` | Call `Material_events_flag.py` in de novo → failures → mergers order |
|
|
127
|
+
| `7.0` | Call `peer groups/base/Material_events_peer_group_basel3.py` for selected horizons |
|
|
128
|
+
| `7.1` | Call `peer groups/horizons/Material_events_peer_group_basel3.py` to select calendar files and concatenate each group/horizon |
|
|
129
|
+
| `8.0` | Call `Material_events_JSD_binning_basel3.py` for selected horizons and bin specifications |
|
|
130
|
+
| `8.1.1` | Normalize selected histograms with `Material_events_JSD_probabilities_basel3.py` |
|
|
131
|
+
| `8.2` | Calculate the three comparisons with `Material_events_JSD_basel3.py` |
|
|
132
|
+
| `8.3.0.1` | Apply `JSD_intragroup_filter.py` to selected results; the configured threshold is `0.3` |
|
|
133
|
+
|
|
134
|
+
The eight material-event tasks use four direct scripts. Their YAML `args` lists supply event selections and paths. The six subsequent tasks use two peer-group scripts, one binning script, one PMF script, one calculation script and one filter script. Each selected group/horizon retains its own output files.
|
|
135
|
+
|
|
136
|
+
See the [pipeline configuration guide](pipeline/readme.md), [material-event CLI](src/material%20events/readme.md), [peer-group CLI](src/peer%20groups/base/readme.md), [binning CLI](src/jsd/binning/readme.md), and [JSD calculation CLI](src/jsd/calculation/readme.md).
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Conceptual Framework
|
|
141
|
+
|
|
142
|
+
The pipeline implements a class-conditional transformation approach:
|
|
143
|
+
|
|
144
|
+
- Failures, Mergers, and Survivors are treated as structurally distinct states, not simple partitions of a common dataset.
|
|
145
|
+
- Each cohort undergoes independent preprocessing to preserve class-specific characteristics.
|
|
146
|
+
- Distributional comparisons (JSD) are performed across cohorts using multiple horizons (T+1–T+4) to evaluate whether regulatory classifications retain predictive separation, mitigating endogeneity concerns such as window dressing, simultaneity, and reverse causality.
|
|
147
|
+
|
|
148
|
+
This design ensures that JSD metrics reflect underlying economic differences rather than artifacts of shared preprocessing.
|
|
149
|
+
|
|
150
|
+
### Forecast Horizons
|
|
151
|
+
|
|
152
|
+
Observations are indexed relative to the quarter in which a structural event
|
|
153
|
+
occurs.
|
|
154
|
+
|
|
155
|
+
If a bank experiences a material event (failure, merger, etc.) during **2023 Q1**:
|
|
156
|
+
|
|
157
|
+
| Observation Quarter | Horizon | Event Quarter |
|
|
158
|
+
|---------------------|---------|---------------|
|
|
159
|
+
| 2022 Q4 | T+1 | 2023 Q1 |
|
|
160
|
+
| 2022 Q3 | T+2 | 2023 Q1 |
|
|
161
|
+
| 2022 Q2 | T+3 | 2023 Q1 |
|
|
162
|
+
| 2022 Q1 | T+4 | 2023 Q1 |
|
|
163
|
+
|
|
164
|
+
The structural event remains fixed while the observation quarter moves further
|
|
165
|
+
back in time.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
### Jensen-Shannon Distance Matrices
|
|
170
|
+
|
|
171
|
+
Each matrix reports the Jensen-Shannon distance for a single regulatory
|
|
172
|
+
variable (for example **RCE-HK07-Saving+**).
|
|
173
|
+
* Item Number HK09
|
|
174
|
+
* * MATURITY AND REPRICING DATA FOR TIME DEPOITS OF 250000 OR LESS, TIME DEPOSITS OF 250000 OR LESS WITH A REMAINING MATURITY OR NEXT REPRICING DATE OF THREE MONTHS OR LESS
|
|
175
|
+
|
|
176
|
+
* Item Number 2385 (Savings)
|
|
177
|
+
* * TOTAL NONTRANSACTION ACCOUNTS (INCLUDING MMDA'S)
|
|
178
|
+
|
|
179
|
+
Rows represent the information used to compute the distance.
|
|
180
|
+
|
|
181
|
+
| Row | Meaning |
|
|
182
|
+
|------|---------|
|
|
183
|
+
| **q0** | Regulatory ratio measured at the observation quarter |
|
|
184
|
+
| **q0q1** | One-quarter change in the ratio (`q1 → q0`) |
|
|
185
|
+
| **q0q2** | Two-quarter change in the ratio (`q2 → q0`) |
|
|
186
|
+
| **q0q3** | Three-quarter change in the ratio (`q3 → q0`) |
|
|
187
|
+
|
|
188
|
+
Example:
|
|
189
|
+
|
|
190
|
+
| | BASEL III T+1 | BASEL III T+2 | BASEL III T+3 | BASEL III T+4 |
|
|
191
|
+
|----------------|--------------:|--------------:|--------------:|--------------:|
|
|
192
|
+
| **q0** | 0.42 | 0.326 | 0.311 | 0.287 |
|
|
193
|
+
| **q0q1** | 0.551 | 0.458 | 0.405 | 0.347 |
|
|
194
|
+
| **q0q2** | 0.578 | 0.470 | 0.404 | 0.349 |
|
|
195
|
+
| **q0q3** | 0.573 | 0.482 | 0.439 | 0.386 |
|
|
196
|
+
|
|
197
|
+
For a bank failing during **2023 Q1**, the temporal alignment is:
|
|
198
|
+
|
|
199
|
+
| q3 | q2 | q1 | q0 | Horizon | Event |
|
|
200
|
+
|----|----|----|----|---------|-------|
|
|
201
|
+
| 2022 Q1 | 2022 Q2 | 2022 Q3 | 2022 Q4 | T+1 | 2023 Q1 |
|
|
202
|
+
| 2021 Q4 | 2022 Q1 | 2022 Q2 | 2022 Q3 | T+2 | 2023 Q1 |
|
|
203
|
+
| 2021 Q3 | 2021 Q4 | 2022 Q1 | 2022 Q2 | T+3 | 2023 Q1 |
|
|
204
|
+
| 2021 Q2 | 2021 Q3 | 2021 Q4 | 2022 Q1 | T+4 | 2023 Q1 |
|
|
205
|
+
|
|
206
|
+
Thus, the value in **q0q1 × BASEL III T+1** measures the Jensen-Shannon
|
|
207
|
+
distance computed from the one-quarter change in the regulatory ratio
|
|
208
|
+
(**2022 Q3 → 2022 Q4**), evaluated one quarter before the structural event.
|
|
209
|
+
|
|
210
|
+
**Conclusion**
|
|
211
|
+
Across all forecast horizons, the JSD associated with quarter-over-quarter changes exceeds the distance obtained from contemporaneous observations alone. This suggests that temporal dynamics provide greater discriminatory power than static balance-sheet measurements.
|
|
212
|
+
|
|
213
|
+
### Why Multiple Horizons?
|
|
214
|
+
|
|
215
|
+
If Basel III classifications genuinely identify safer institutions, statistical
|
|
216
|
+
separation should persist as the forecast horizon increases.
|
|
217
|
+
|
|
218
|
+
Evaluating multiple horizons allows the framework to distinguish between:
|
|
219
|
+
|
|
220
|
+
- variables that separate only immediately before failure,
|
|
221
|
+
- variables that retain predictive information several quarters in advance,
|
|
222
|
+
- variables whose predictive content decays through time.
|
|
223
|
+
|
|
224
|
+
The evolution of JSD across T+1–T+4 therefore becomes an empirical
|
|
225
|
+
measure of the temporal stability of regulatory classifications.
|
|
226
|
+
|
|
227
|
+
### Dynamic JSD
|
|
228
|
+
|
|
229
|
+
The framework evaluates not only JSD at individual horizons but also
|
|
230
|
+
the evolution of distance between successive horizons.
|
|
231
|
+
|
|
232
|
+
Traditional analyses often evaluate regulatory variables independently at each reporting period. This framework instead evaluates the evolution of distributional separation across successive forecast horizons. The objective is to determine whether the trajectory of distance contains predictive information beyond that available from any single quarterly observation.
|
|
233
|
+
|
|
234
|
+
Rather than asking only,
|
|
235
|
+
|
|
236
|
+
"How different are failures from survivors at T+2?"
|
|
237
|
+
|
|
238
|
+
the framework also asks,
|
|
239
|
+
|
|
240
|
+
"How rapidly does that difference change between T+2 and T+1?"
|
|
241
|
+
|
|
242
|
+
These temporal transitions capture changes in depositor behavior and balance
|
|
243
|
+
sheet adjustment immediately preceding structural events.
|
|
244
|
+
|
|
245
|
+
For example:
|
|
246
|
+
- T+4 → T+3
|
|
247
|
+
- T+3 → T+2
|
|
248
|
+
- T+2 → T+1
|
|
249
|
+
|
|
250
|
+
### Motivation
|
|
251
|
+
|
|
252
|
+
Traditional liquidity assumptions often model depositor behavior as relatively
|
|
253
|
+
stable through time.
|
|
254
|
+
|
|
255
|
+
If the JSD changes abruptly between adjacent horizons, this suggests that
|
|
256
|
+
depositor responses may occur in rapid, heterogeneous repricing episodes
|
|
257
|
+
rather than as smooth, memoryless processes.
|
|
258
|
+
|
|
259
|
+
The multi-horizon framework was therefore designed to evaluate whether the
|
|
260
|
+
timing of distributional changes itself contains economically meaningful
|
|
261
|
+
|
|
262
|
+
## Pipeline
|
|
263
|
+
|
|
264
|
+
[Here](https://github.com/OCBliss/Basel3/tree/refactor/pipeline)
|
|
265
|
+
|
|
266
|
+
The configured Basel III pipeline runs 20 tasks from raw FFIEC Call Reports through peer-group construction, Jensen-Shannon distance analysis, and the final column filter. Duration-sensitive RWA modeling remains a separate module under active development, described below.
|
|
267
|
+
|
|
268
|
+
Rather than implementing the workflow as a single monolithic script, the pipeline decomposes the research process into independent analytical stages connected through explicit dependency graphs.
|
|
269
|
+
|
|
270
|
+
This design was chosen because:
|
|
271
|
+
|
|
272
|
+
research modules evolve independently;
|
|
273
|
+
statistical methodologies may be substituted without changing upstream preprocessing;
|
|
274
|
+
different regulatory epochs, schedules, horizons, and parameterizations can be executed independently;
|
|
275
|
+
computationally independent tasks can execute concurrently.
|
|
276
|
+
|
|
277
|
+
The YAML configuration therefore represents the research workflow itself rather than simply a list of Python scripts.
|
|
278
|
+
|
|
279
|
+
### Call Reports
|
|
280
|
+
|
|
281
|
+
[Here](https://github.com/OCBliss/Basel3/tree/refactor/src/call%20reports)
|
|
282
|
+
|
|
283
|
+
The empirical analysis in this repository is built upon FFIEC Call Reports, the
|
|
284
|
+
quarterly regulatory filings submitted by every insured U.S. commercial bank.
|
|
285
|
+
These reports provide standardized disclosures covering capital adequacy,
|
|
286
|
+
balance sheet composition, liquidity, credit quality, income, and regulatory
|
|
287
|
+
ratios.
|
|
288
|
+
|
|
289
|
+
The objective of this module is not simply to clean regulatory filings, but to
|
|
290
|
+
transform heterogeneous reporting schedules into a longitudinal analytical
|
|
291
|
+
dataset suitable for statistical inference. The pipeline standardizes reporting
|
|
292
|
+
formats across time, performs cross-schedule validation, engineers derived
|
|
293
|
+
variables, and reorganizes the data into structures optimized for temporal,
|
|
294
|
+
cross-sectional, and distributional analysis.
|
|
295
|
+
|
|
296
|
+
### JSD
|
|
297
|
+
|
|
298
|
+
[Binning, PMF and calculation guide](src/readme.md)
|
|
299
|
+
|
|
300
|
+
Basel III makes claims about classification: that regulatory categories such as capital adequacy and liquidity distinguish safer institutions from riskier ones. The objective is therefore to evaluate whether these classifications remain statistically separable over future horizons. Jensen-Shannon distance was selected because it compares entire probability distributions rather than only summary statistics, allowing the framework to detect changes in distributional shape, overlap, and multimodality that moment-based measures may miss.
|
|
301
|
+
|
|
302
|
+
### Material Events
|
|
303
|
+
|
|
304
|
+
[Here](https://github.com/OCBliss/Basel3/tree/refactor/Material%20Events)
|
|
305
|
+
|
|
306
|
+
Basel III is ultimately concerned with the ability of regulatory metrics to
|
|
307
|
+
distinguish institutions that subsequently experience materially different
|
|
308
|
+
outcomes.
|
|
309
|
+
|
|
310
|
+
The Material Events module constructs the dependent variables used throughout
|
|
311
|
+
the empirical framework by identifying future structural banking events—
|
|
312
|
+
including failures, mergers, de novo institutions, and surviving banks—from
|
|
313
|
+
FDIC BankFind data.
|
|
314
|
+
|
|
315
|
+
Rather than treating all institutions as a homogeneous population, these
|
|
316
|
+
outcomes are preserved as distinct analytical cohorts before preprocessing.
|
|
317
|
+
This enables subsequent peer-group construction and distributional analysis to
|
|
318
|
+
evaluate whether regulatory classifications retain predictive separation across
|
|
319
|
+
future forecast horizons (T+1 through T+4).
|
|
320
|
+
|
|
321
|
+
## RWA Module (Active Development)
|
|
322
|
+
|
|
323
|
+
The RWA module extends the empirical Basel III analysis by investigating
|
|
324
|
+
duration-sensitive capital impairment arising from interest-rate risk that is
|
|
325
|
+
not reflected in regulatory risk weights.
|
|
326
|
+
|
|
327
|
+
### PDF
|
|
328
|
+
|
|
329
|
+
Explanation of the RWA module mathematical foundation [Here](https://github.com/OCBliss/Basel3/tree/refactor/PDF)
|
|
330
|
+
|
|
331
|
+
### Current Components
|
|
332
|
+
|
|
333
|
+
- `FRED/`
|
|
334
|
+
- Constant Maturity Treasury yield data.
|
|
335
|
+
|
|
336
|
+
- `Practical/`
|
|
337
|
+
- Simplified non-parallel yield-shock framework.
|
|
338
|
+
- Preliminary expected-loss estimation.
|
|
339
|
+
- Exploratory risk-weight methodology.
|
|
340
|
+
|
|
341
|
+
### Research Under Development
|
|
342
|
+
|
|
343
|
+
Components include:
|
|
344
|
+
|
|
345
|
+
- `FRED MLE YIELD/`
|
|
346
|
+
- Recursive estimation of yield-process parameters
|
|
347
|
+
(`μᵧ(t)`, `νᵧ(t)`, `θᵧ(t)`)
|
|
348
|
+
|
|
349
|
+
- `FRED MLE VOL/`
|
|
350
|
+
- Recursive estimation of stochastic volatility
|
|
351
|
+
(`μᵥ(t)`, `ξᵥ(t)`, `θᵥ(t)`)
|
|
352
|
+
|
|
353
|
+
- `FRED MLE CORR/`
|
|
354
|
+
- Maximum-likelihood estimation of time-varying correlation matrices
|
|
355
|
+
(`ΣX(t)`, `Σν(t)`, `Σξ(t)`)
|
|
356
|
+
using recursive Cholesky decomposition.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "CallReports"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Date-aware FFIEC Call Report definitions and bulk-file preparation"
|
|
9
|
+
readme = "src/callreports/README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
authors = [{name = "Jonathan Waller"}]
|
|
12
|
+
dependencies = ["numpy>=1.24", "pandas>=2.0"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Topic :: Office/Business :: Financial",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Repository = "https://github.com/OCBliss/Basel3"
|
|
21
|
+
|
|
22
|
+
[project.scripts]
|
|
23
|
+
callreports = "callreports.cli:main"
|
|
24
|
+
|
|
25
|
+
[tool.setuptools]
|
|
26
|
+
package-dir = {"" = "src"}
|
|
27
|
+
packages = ["callreports"]
|
|
28
|
+
include-package-data = false
|