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.
@@ -0,0 +1,4 @@
1
+ include pyproject.toml
2
+ include src/callreports/README.md
3
+ recursive-include src/callreports *.py
4
+ prune tests
@@ -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
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+