From 1aa9df98481dbda474a6480101ca08d0c30bebfe Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Thu, 30 Jul 2026 23:01:32 +0100 Subject: [PATCH 1/2] fix(tests): make the examples runner encoding-safe on Windows 781cdb59 fixed binary resolution on win32 but left the suite dependent on PYTHONIOENCODING=utf-8 PYTHONUTF8=1 being set externally. Without those the runner has three separate cp1252 failures: - printing the check/cross marks dies with UnicodeEncodeError before any results are reported; - subprocess.run(text=True) decodes the engine's stdout with the locale encoding, mangling every non-ASCII character and breaking formal JSON comparison (formatting_showcase, showcase_deterministic); - open() reads the .sql files as cp1252, so a UTF-8 example whose bytes are undefined in cp1252 raises inside get_data_file_hint, the -- #! hint is silently dropped, and the example runs without its data file (solar_system_calculations_simple). Pin all three to UTF-8 so the suite passes on a bare cp1252 console with no environment setup. No change on Linux/macOS, where UTF-8 was already the locale encoding. Co-Authored-By: Claude Opus 5 (1M context) --- tests/integration/test_examples.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/integration/test_examples.py b/tests/integration/test_examples.py index 67062751..30090bd3 100755 --- a/tests/integration/test_examples.py +++ b/tests/integration/test_examples.py @@ -23,6 +23,13 @@ from pathlib import Path from typing import Dict, List, Optional, Tuple +# Windows consoles default to cp1252, which cannot encode the check/cross marks +# this runner prints - without this the run dies on a UnicodeEncodeError before +# reporting any results. Saves having to set PYTHONIOENCODING/PYTHONUTF8. +if hasattr(sys.stdout, 'reconfigure'): + sys.stdout.reconfigure(encoding='utf-8', errors='replace') + sys.stderr.reconfigure(encoding='utf-8', errors='replace') + # ANSI colors class Color: RED = '\033[0;31m' @@ -51,7 +58,7 @@ def get_data_file_hint(sql_file: Path) -> Optional[str]: Returns absolute path if found and file exists, None otherwise. """ try: - with open(sql_file, 'r') as f: + with open(sql_file, 'r', encoding='utf-8') as f: # Check first 10 lines for data hint for i, line in enumerate(f): if i >= 10: @@ -102,6 +109,11 @@ def run_sql_file(cli_path: str, sql_file: Path) -> Tuple[bool, str]: cmd, capture_output=True, text=True, + # Without this, text=True decodes the child's stdout with the + # locale encoding (cp1252 on Windows), mangling every non-ASCII + # character the engine emits and failing formal comparisons. + encoding='utf-8', + errors='replace', timeout=30 ) # Combine stdout and stderr @@ -192,7 +204,7 @@ def compare_json(expected, actual) -> Tuple[bool, Optional[str]]: def should_skip_file(sql_file: Path) -> Tuple[bool, Optional[str]]: """Check if file should be skipped based on -- [TEST:SKIP] directive""" try: - with open(sql_file, 'r') as f: + with open(sql_file, 'r', encoding='utf-8') as f: # Check first 10 lines for [TEST:SKIP] directive for i, line in enumerate(f): if i >= 10: @@ -259,7 +271,7 @@ def run_test(cli_path: str, sql_file: Path, expectations_dir: Path, result: Test # Load expected output try: - with open(expectation_file, 'r') as f: + with open(expectation_file, 'r', encoding='utf-8') as f: expected_json = json.load(f) except Exception as e: print(f"{Color.RED}✗ FAIL - Cannot load expectation: {e}{Color.NC}") @@ -315,7 +327,7 @@ def capture_expectation(cli_path: str, example_name: str, expectations_dir: Path expectations_dir.mkdir(parents=True, exist_ok=True) expectation_file = expectations_dir / f"{example_name}.json" - with open(expectation_file, 'w') as f: + with open(expectation_file, 'w', encoding='utf-8') as f: json.dump(json_data, f, indent=2, sort_keys=True) print(f"{Color.GREEN}✓ Expectation captured successfully!{Color.NC}") From 99a6a49afc9c9b0892618d56c707d899a96b5c23 Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Thu, 30 Jul 2026 23:01:42 +0100 Subject: [PATCH 2/2] feat(examples): Bank of England rate history, 1975-2025 Adds a fifty-year worked example over the BoE Bank Rate: 258 rate changes from the 1975 post-oil-shock easing to December 2025, tagged with economic eras, the events behind the big moves, and the PM/chancellor/governor of the day. The raw bankofengland.co.uk download uses a "18 Dec 25" date format that the engine treats as a plain string, so ORDER BY sorts lexically and YEAR() returns nonsense. scripts/enrich_boe_rates.py rewrites the dates as ISO so a real DATETIME column is inferred, and denormalises the economic context alongside (one data source per invocation, so a join is not an option). Both CSVs are committed: the raw download for provenance, the enriched file for the example. Re-running the script reproduces the enriched CSV byte for byte. The example exercises GROUP BY over derived date parts, CASE banding via CTE, LAG cross-checked against a precomputed column, and date-range filtering. Registered as a formal test with a captured expectation, taking the suite from 31 formal tests to 32. Co-Authored-By: Claude Opus 5 (1M context) --- data/boe_rate_history.csv | 260 +++++ data/boe_rate_history_enriched.csv | 259 +++++ examples/boe_rate_history.sql | 163 +++ examples/expectations/boe_rate_history.json | 1057 +++++++++++++++++++ scripts/enrich_boe_rates.py | 238 +++++ 5 files changed, 1977 insertions(+) create mode 100644 data/boe_rate_history.csv create mode 100644 data/boe_rate_history_enriched.csv create mode 100644 examples/boe_rate_history.sql create mode 100644 examples/expectations/boe_rate_history.json create mode 100644 scripts/enrich_boe_rates.py diff --git a/data/boe_rate_history.csv b/data/boe_rate_history.csv new file mode 100644 index 00000000..298a90b1 --- /dev/null +++ b/data/boe_rate_history.csv @@ -0,0 +1,260 @@ +"Date Changed","Rate" +"18 Dec 25","3.75" +"07 Aug 25","4.00" +"08 May 25","4.25" +"06 Feb 25","4.50" +"07 Nov 24","4.75" +"01 Aug 24","5.00" +"03 Aug 23","5.25" +"22 Jun 23","5.00" +"11 May 23","4.50" +"23 Mar 23","4.25" +"02 Feb 23","4.00" +"15 Dec 22","3.50" +"03 Nov 22","3.00" +"22 Sep 22","2.25" +"04 Aug 22","1.75" +"16 Jun 22","1.25" +"05 May 22","1.00" +"17 Mar 22","0.75" +"03 Feb 22","0.50" +"16 Dec 21","0.25" +"19 Mar 20","0.10" +"11 Mar 20","0.25" +"02 Aug 18","0.75" +"02 Nov 17","0.50" +"04 Aug 16","0.25" +"05 Mar 09","0.50" +"05 Feb 09","1.00" +"08 Jan 09","1.50" +"04 Dec 08","2.00" +"06 Nov 08","3.00" +"08 Oct 08","4.50" +"10 Apr 08","5.00" +"07 Feb 08","5.25" +"06 Dec 07","5.50" +"05 Jul 07","5.75" +"10 May 07","5.50" +"11 Jan 07","5.25" +"09 Nov 06","5.00" +"03 Aug 06","4.75" +"04 Aug 05","4.50" +"05 Aug 04","4.75" +"10 Jun 04","4.50" +"06 May 04","4.25" +"05 Feb 04","4.00" +"06 Nov 03","3.75" +"10 Jul 03","3.50" +"06 Feb 03","3.75" +"08 Nov 01","4.00" +"04 Oct 01","4.50" +"18 Sep 01","4.75" +"02 Aug 01","5.00" +"10 May 01","5.25" +"05 Apr 01","5.50" +"08 Feb 01","5.75" +"10 Feb 00","6.00" +"13 Jan 00","5.75" +"04 Nov 99","5.50" +"08 Sep 99","5.25" +"10 Jun 99","5.00" +"08 Apr 99","5.25" +"04 Feb 99","5.50" +"07 Jan 99","6.00" +"10 Dec 98","6.25" +"05 Nov 98","6.75" +"08 Oct 98","7.25" +"04 Jun 98","7.50" +"06 Nov 97","7.25" +"07 Aug 97","7.00" +"10 Jul 97","6.75" +"06 Jun 97","6.50" +"06 May 97","6.25" +"30 Oct 96","5.94" +"06 Jun 96","5.69" +"08 Mar 96","5.94" +"18 Jan 96","6.13" +"13 Dec 95","6.38" +"02 Feb 95","6.63" +"07 Dec 94","6.13" +"12 Sep 94","5.63" +"08 Feb 94","5.13" +"23 Nov 93","5.38" +"26 Jan 93","5.88" +"13 Nov 92","6.88" +"16 Oct 92","7.88" +"22 Sep 92","8.88" +"05 May 92","9.88" +"04 Sep 91","10.38" +"12 Jul 91","10.88" +"24 May 91","11.38" +"12 Apr 91","11.88" +"22 Mar 91","12.38" +"27 Feb 91","12.88" +"13 Feb 91","13.38" +"08 Oct 90","13.88" +"06 Oct 89","14.88" +"08 Sep 89","13.75" +"04 Sep 89","13.88" +"31 Aug 89","13.84" +"25 May 89","13.75" +"25 Nov 88","12.88" +"25 Aug 88","11.88" +"08 Aug 88","10.88" +"21 Jul 88","10.38" +"07 Jul 88","9.88" +"24 Jun 88","8.88" +"10 Jun 88","8.38" +"03 Jun 88","7.88" +"17 May 88","7.38" +"08 Apr 88","7.88" +"17 Mar 88","8.38" +"01 Feb 88","8.88" +"03 Dec 87","8.38" +"04 Nov 87","8.88" +"23 Oct 87","9.38" +"06 Aug 87","9.88" +"08 May 87","8.88" +"28 Apr 87","9.38" +"18 Mar 87","9.88" +"09 Mar 87","10.38" +"15 Oct 86","10.88" +"23 May 86","9.88" +"18 Apr 86","10.38" +"11 Apr 86","10.88" +"19 Mar 86","11.38" +"15 Jan 86","12.38" +"26 Jul 85","11.38" +"11 Jul 85","11.88" +"19 Apr 85","12.38" +"28 Mar 85","12.88" +"20 Mar 85","13.38" +"28 Jan 85","13.88" +"14 Jan 85","11.88" +"23 Nov 84","9.50" +"19 Nov 84","9.75" +"05 Nov 84","10.00" +"17 Aug 84","10.50" +"16 Aug 84","10.75" +"09 Aug 84","11.00" +"08 Aug 84","11.50" +"11 Jul 84","12.00" +"06 Jul 84","10.00" +"29 Jun 84","8.88" +"10 May 84","9.06" +"14 Mar 84","8.56" +"07 Mar 84","8.81" +"03 Oct 83","9.06" +"10 Aug 83","9.56" +"09 Aug 83","9.44" +"14 Jun 83","9.56" +"13 Jun 83","9.81" +"14 Apr 83","10.06" +"13 Apr 83","10.31" +"15 Mar 83","10.56" +"12 Jan 83","11.00" +"26 Nov 82","10.00" +"02 Nov 82","9.13" +"01 Nov 82","9.38" +"12 Oct 82","9.63" +"30 Sep 82","10.13" +"29 Sep 82","10.25" +"28 Sep 82","10.38" +"27 Sep 82","10.50" +"27 Aug 82","10.63" +"26 Aug 82","10.88" +"25 Aug 82","11.00" +"24 Aug 82","11.13" +"17 Aug 82","11.25" +"16 Aug 82","11.38" +"04 Aug 82","11.50" +"02 Aug 82","11.56" +"30 Jul 82","11.63" +"29 Jul 82","11.75" +"28 Jul 82","11.81" +"26 Jul 82","11.94" +"21 Jul 82","12.06" +"13 Jul 82","12.13" +"12 Jul 82","12.25" +"09 Jul 82","12.50" +"08 Jun 82","12.63" +"20 Apr 82","13.13" +"19 Apr 82","13.00" +"16 Apr 82","13.13" +"10 Mar 82","13.25" +"25 Feb 82","13.63" +"22 Feb 82","13.81" +"22 Jan 82","13.88" +"21 Jan 82","14.00" +"20 Jan 82","14.13" +"19 Jan 82","14.25" +"18 Jan 82","14.31" +"04 Dec 81","14.38" +"25 Nov 81","14.56" +"09 Nov 81","14.63" +"06 Nov 81","15.06" +"28 Oct 81","15.13" +"12 Oct 81","15.00" +"15 Sep 81","14.00" +"25 Aug 81","12.69" +"11 Mar 81","12.00" +"25 Nov 80","14.00" +"03 Jul 80","16.00" +"15 Nov 79","17.00" +"13 Jun 79","14.00" +"05 Apr 79","12.00" +"01 Mar 79","13.00" +"08 Feb 79","14.00" +"09 Nov 78","12.50" +"08 Jun 78","10.00" +"15 May 78","9.00" +"08 May 78","8.75" +"12 Apr 78","7.50" +"09 Jan 78","6.50" +"28 Nov 77","7.00" +"17 Oct 77","5.00" +"10 Oct 77","5.50" +"19 Sep 77","6.00" +"12 Sep 77","6.50" +"15 Aug 77","7.00" +"08 Aug 77","7.50" +"16 May 77","8.00" +"02 May 77","8.25" +"25 Apr 77","8.75" +"18 Apr 77","9.00" +"12 Apr 77","9.25" +"31 Mar 77","9.50" +"21 Mar 77","10.50" +"10 Mar 77","11.00" +"03 Feb 77","12.00" +"31 Jan 77","12.25" +"24 Jan 77","13.25" +"10 Jan 77","14.00" +"29 Dec 76","14.25" +"20 Dec 76","14.50" +"22 Nov 76","14.75" +"07 Oct 76","15.00" +"13 Sep 76","13.00" +"24 May 76","11.50" +"26 Apr 76","10.50" +"08 Mar 76","9.00" +"01 Mar 76","9.25" +"09 Feb 76","9.50" +"02 Feb 76","10.00" +"26 Jan 76","10.50" +"19 Jan 76","10.75" +"05 Jan 76","11.00" +"29 Dec 75","11.25" +"01 Dec 75","11.50" +"17 Nov 75","11.75" +"06 Oct 75","12.00" +"28 Jul 75","11.00" +"05 May 75","10.00" +"21 Apr 75","9.75" +"24 Mar 75","10.00" +"10 Mar 75","10.25" +"17 Feb 75","10.50" +"10 Feb 75","10.75" +"27 Jan 75","11.00" +"20 Jan 75","11.25" + diff --git a/data/boe_rate_history_enriched.csv b/data/boe_rate_history_enriched.csv new file mode 100644 index 00000000..1a971acf --- /dev/null +++ b/data/boe_rate_history_enriched.csv @@ -0,0 +1,259 @@ +date,rate,prev_rate,change_bps,direction,days_since_prev,era,event,event_category,pm,party,chancellor,governor,boe_independent +1975-01-20,11.25,,,First,,Stagflation & Sterling Crisis,Post oil-shock easing begins,Oil Shock,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1975-01-27,11.00,11.25,-25,Cut,7,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1975-02-10,10.75,11.00,-25,Cut,14,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1975-02-17,10.50,10.75,-25,Cut,7,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1975-03-10,10.25,10.50,-25,Cut,21,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1975-03-24,10.00,10.25,-25,Cut,14,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1975-04-21,9.75,10.00,-25,Cut,28,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1975-05-05,10.00,9.75,25,Hike,14,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1975-07-28,11.00,10.00,100,Hike,84,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1975-10-06,12.00,11.00,100,Hike,70,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1975-11-17,11.75,12.00,-25,Cut,42,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1975-12-01,11.50,11.75,-25,Cut,14,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1975-12-29,11.25,11.50,-25,Cut,28,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1976-01-05,11.00,11.25,-25,Cut,7,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1976-01-19,10.75,11.00,-25,Cut,14,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1976-01-26,10.50,10.75,-25,Cut,7,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1976-02-02,10.00,10.50,-50,Cut,7,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1976-02-09,9.50,10.00,-50,Cut,7,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1976-03-01,9.25,9.50,-25,Cut,21,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1976-03-08,9.00,9.25,-25,Cut,7,Stagflation & Sterling Crisis,,,Harold Wilson,Labour,Denis Healey,Gordon Richardson,false +1976-04-26,10.50,9.00,150,Hike,49,Stagflation & Sterling Crisis,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1976-05-24,11.50,10.50,100,Hike,28,Stagflation & Sterling Crisis,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1976-09-13,13.00,11.50,150,Hike,112,Stagflation & Sterling Crisis,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1976-10-07,15.00,13.00,200,Hike,24,IMF Bailout & Winter of Discontent,Sterling crisis: MLR to 15%,Currency Crisis,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1976-11-22,14.75,15.00,-25,Cut,46,IMF Bailout & Winter of Discontent,IMF loan negotiations under way,Currency Crisis,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1976-12-20,14.50,14.75,-25,Cut,28,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1976-12-29,14.25,14.50,-25,Cut,9,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-01-10,14.00,14.25,-25,Cut,12,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-01-24,13.25,14.00,-75,Cut,14,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-01-31,12.25,13.25,-100,Cut,7,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-02-03,12.00,12.25,-25,Cut,3,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-03-10,11.00,12.00,-100,Cut,35,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-03-21,10.50,11.00,-50,Cut,11,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-03-31,9.50,10.50,-100,Cut,10,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-04-12,9.25,9.50,-25,Cut,12,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-04-18,9.00,9.25,-25,Cut,6,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-04-25,8.75,9.00,-25,Cut,7,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-05-02,8.25,8.75,-50,Cut,7,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-05-16,8.00,8.25,-25,Cut,14,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-08-08,7.50,8.00,-50,Cut,84,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-08-15,7.00,7.50,-50,Cut,7,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-09-12,6.50,7.00,-50,Cut,28,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-09-19,6.00,6.50,-50,Cut,7,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-10-10,5.50,6.00,-50,Cut,21,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-10-17,5.00,5.50,-50,Cut,7,IMF Bailout & Winter of Discontent,"Sterling stabilised, rate at decade low",Recovery,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1977-11-28,7.00,5.00,200,Hike,42,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1978-01-09,6.50,7.00,-50,Cut,42,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1978-04-12,7.50,6.50,100,Hike,93,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1978-05-08,8.75,7.50,125,Hike,26,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1978-05-15,9.00,8.75,25,Hike,7,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1978-06-08,10.00,9.00,100,Hike,24,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1978-11-09,12.50,10.00,250,Hike,154,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1979-02-08,14.00,12.50,150,Hike,91,IMF Bailout & Winter of Discontent,Winter of Discontent,Industrial Unrest,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1979-03-01,13.00,14.00,-100,Cut,21,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1979-04-05,12.00,13.00,-100,Cut,35,IMF Bailout & Winter of Discontent,,,James Callaghan,Labour,Denis Healey,Gordon Richardson,false +1979-06-13,14.00,12.00,200,Hike,69,Monetarist Shock,Howe's first Budget: VAT nearly doubled,Fiscal Policy,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1979-11-15,17.00,14.00,300,Hike,155,Monetarist Shock,Record high 17% - Thatcher's monetarist squeeze,Monetary Squeeze,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1980-07-03,16.00,17.00,-100,Cut,231,Monetarist Shock,"Recession bites, squeeze eases",Recession,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1980-11-25,14.00,16.00,-200,Cut,145,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1981-03-11,12.00,14.00,-200,Cut,106,Monetarist Shock,The 364 economists' letter Budget,Fiscal Policy,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1981-08-25,12.69,12.00,69,Hike,167,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1981-09-15,14.00,12.69,131,Hike,21,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1981-10-12,15.00,14.00,100,Hike,27,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1981-10-28,15.13,15.00,13,Hike,16,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1981-11-06,15.06,15.13,-7,Cut,9,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1981-11-09,14.63,15.06,-43,Cut,3,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1981-11-25,14.56,14.63,-7,Cut,16,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1981-12-04,14.38,14.56,-18,Cut,9,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-01-18,14.31,14.38,-7,Cut,45,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-01-19,14.25,14.31,-6,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-01-20,14.13,14.25,-12,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-01-21,14.00,14.13,-13,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-01-22,13.88,14.00,-12,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-02-22,13.81,13.88,-7,Cut,31,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-02-25,13.63,13.81,-18,Cut,3,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-03-10,13.25,13.63,-38,Cut,13,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-04-16,13.13,13.25,-12,Cut,37,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-04-19,13.00,13.13,-13,Cut,3,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-04-20,13.13,13.00,13,Hike,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-06-08,12.63,13.13,-50,Cut,49,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-07-09,12.50,12.63,-13,Cut,31,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-07-12,12.25,12.50,-25,Cut,3,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-07-13,12.13,12.25,-12,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-07-21,12.06,12.13,-7,Cut,8,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-07-26,11.94,12.06,-12,Cut,5,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-07-28,11.81,11.94,-13,Cut,2,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-07-29,11.75,11.81,-6,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-07-30,11.63,11.75,-12,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-08-02,11.56,11.63,-7,Cut,3,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-08-04,11.50,11.56,-6,Cut,2,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-08-16,11.38,11.50,-12,Cut,12,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-08-17,11.25,11.38,-13,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-08-24,11.13,11.25,-12,Cut,7,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-08-25,11.00,11.13,-13,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-08-26,10.88,11.00,-12,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-08-27,10.63,10.88,-25,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-09-27,10.50,10.63,-13,Cut,31,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-09-28,10.38,10.50,-12,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-09-29,10.25,10.38,-13,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-09-30,10.13,10.25,-12,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-10-12,9.63,10.13,-50,Cut,12,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-11-01,9.38,9.63,-25,Cut,20,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-11-02,9.13,9.38,-25,Cut,1,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1982-11-26,10.00,9.13,87,Hike,24,Monetarist Shock,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1983-01-12,11.00,10.00,100,Hike,47,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1983-03-15,10.56,11.00,-44,Cut,62,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1983-04-13,10.31,10.56,-25,Cut,29,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1983-04-14,10.06,10.31,-25,Cut,1,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Geoffrey Howe,Gordon Richardson,false +1983-06-13,9.81,10.06,-25,Cut,60,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Gordon Richardson,false +1983-06-14,9.56,9.81,-25,Cut,1,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Gordon Richardson,false +1983-08-09,9.44,9.56,-12,Cut,56,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1983-08-10,9.56,9.44,12,Hike,1,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1983-10-03,9.06,9.56,-50,Cut,54,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1984-03-07,8.81,9.06,-25,Cut,156,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1984-03-14,8.56,8.81,-25,Cut,7,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1984-05-10,9.06,8.56,50,Hike,57,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1984-06-29,8.88,9.06,-18,Cut,50,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1984-07-06,10.00,8.88,112,Hike,7,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1984-07-11,12.00,10.00,200,Hike,5,Recovery & Deregulation,Miners' strike & sterling pressure,Industrial Unrest,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1984-08-08,11.50,12.00,-50,Cut,28,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1984-08-09,11.00,11.50,-50,Cut,1,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1984-08-16,10.75,11.00,-25,Cut,7,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1984-08-17,10.50,10.75,-25,Cut,1,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1984-11-05,10.00,10.50,-50,Cut,80,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1984-11-19,9.75,10.00,-25,Cut,14,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1984-11-23,9.50,9.75,-25,Cut,4,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1985-01-14,11.88,9.50,238,Hike,52,Recovery & Deregulation,Sterling near parity with the dollar,Currency Crisis,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1985-01-28,13.88,11.88,200,Hike,14,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1985-03-20,13.38,13.88,-50,Cut,51,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1985-03-28,12.88,13.38,-50,Cut,8,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1985-04-19,12.38,12.88,-50,Cut,22,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1985-07-11,11.88,12.38,-50,Cut,83,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1985-07-26,11.38,11.88,-50,Cut,15,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1986-01-15,12.38,11.38,100,Hike,173,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1986-03-19,11.38,12.38,-100,Cut,63,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1986-04-11,10.88,11.38,-50,Cut,23,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1986-04-18,10.38,10.88,-50,Cut,7,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1986-05-23,9.88,10.38,-50,Cut,35,Recovery & Deregulation,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1986-10-15,10.88,9.88,100,Hike,145,Recovery & Deregulation,Big Bang deregulates the City,Deregulation,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1987-03-09,10.38,10.88,-50,Cut,145,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1987-03-18,9.88,10.38,-50,Cut,9,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1987-04-28,9.38,9.88,-50,Cut,41,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1987-05-08,8.88,9.38,-50,Cut,10,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1987-08-06,9.88,8.88,100,Hike,90,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1987-10-23,9.38,9.88,-50,Cut,78,Big Bang & Lawson Boom,Post-Black Monday emergency easing,Market Crash,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1987-11-04,8.88,9.38,-50,Cut,12,Big Bang & Lawson Boom,Crash response continues,Market Crash,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1987-12-03,8.38,8.88,-50,Cut,29,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1988-02-01,8.88,8.38,50,Hike,60,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1988-03-17,8.38,8.88,-50,Cut,45,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1988-04-08,7.88,8.38,-50,Cut,22,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1988-05-17,7.38,7.88,-50,Cut,39,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1988-06-03,7.88,7.38,50,Hike,17,Big Bang & Lawson Boom,Lawson Boom: start of 12 hikes in 18 months,Credit Boom,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1988-06-10,8.38,7.88,50,Hike,7,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1988-06-24,8.88,8.38,50,Hike,14,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1988-07-07,9.88,8.88,100,Hike,13,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1988-07-21,10.38,9.88,50,Hike,14,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1988-08-08,10.88,10.38,50,Hike,18,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1988-08-25,11.88,10.88,100,Hike,17,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1988-11-25,12.88,11.88,100,Hike,92,Big Bang & Lawson Boom,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1989-05-25,13.75,12.88,87,Hike,181,Overheating & Shadowing the D-Mark,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1989-08-31,13.84,13.75,9,Hike,98,Overheating & Shadowing the D-Mark,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1989-09-04,13.88,13.84,4,Hike,4,Overheating & Shadowing the D-Mark,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1989-09-08,13.75,13.88,-13,Cut,4,Overheating & Shadowing the D-Mark,,,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1989-10-06,14.88,13.75,113,Hike,28,Overheating & Shadowing the D-Mark,Rate to 15% - Lawson resigns weeks later,Political Crisis,Margaret Thatcher,Conservative,Nigel Lawson,Robin Leigh-Pemberton,false +1990-10-08,13.88,14.88,-100,Cut,367,ERM Membership,UK joins the ERM at DM 2.95,ERM,Margaret Thatcher,Conservative,John Major,Robin Leigh-Pemberton,false +1991-02-13,13.38,13.88,-50,Cut,128,ERM Membership,,,John Major,Conservative,Norman Lamont,Robin Leigh-Pemberton,false +1991-02-27,12.88,13.38,-50,Cut,14,ERM Membership,,,John Major,Conservative,Norman Lamont,Robin Leigh-Pemberton,false +1991-03-22,12.38,12.88,-50,Cut,23,ERM Membership,,,John Major,Conservative,Norman Lamont,Robin Leigh-Pemberton,false +1991-04-12,11.88,12.38,-50,Cut,21,ERM Membership,,,John Major,Conservative,Norman Lamont,Robin Leigh-Pemberton,false +1991-05-24,11.38,11.88,-50,Cut,42,ERM Membership,,,John Major,Conservative,Norman Lamont,Robin Leigh-Pemberton,false +1991-07-12,10.88,11.38,-50,Cut,49,ERM Membership,,,John Major,Conservative,Norman Lamont,Robin Leigh-Pemberton,false +1991-09-04,10.38,10.88,-50,Cut,54,ERM Membership,,,John Major,Conservative,Norman Lamont,Robin Leigh-Pemberton,false +1992-05-05,9.88,10.38,-50,Cut,244,ERM Membership,,,John Major,Conservative,Norman Lamont,Robin Leigh-Pemberton,false +1992-09-22,8.88,9.88,-100,Cut,140,Post-ERM Inflation Targeting,Black Wednesday aftermath - ERM exit,Currency Crisis,John Major,Conservative,Norman Lamont,Robin Leigh-Pemberton,false +1992-10-16,7.88,8.88,-100,Cut,24,Post-ERM Inflation Targeting,Post-ERM easing under inflation targeting,Policy Regime Change,John Major,Conservative,Norman Lamont,Robin Leigh-Pemberton,false +1992-11-13,6.88,7.88,-100,Cut,28,Post-ERM Inflation Targeting,,,John Major,Conservative,Norman Lamont,Robin Leigh-Pemberton,false +1993-01-26,5.88,6.88,-100,Cut,74,Post-ERM Inflation Targeting,,,John Major,Conservative,Norman Lamont,Robin Leigh-Pemberton,false +1993-11-23,5.38,5.88,-50,Cut,301,Post-ERM Inflation Targeting,,,John Major,Conservative,Kenneth Clarke,Eddie George,false +1994-02-08,5.13,5.38,-25,Cut,77,Post-ERM Inflation Targeting,,,John Major,Conservative,Kenneth Clarke,Eddie George,false +1994-09-12,5.63,5.13,50,Hike,216,Post-ERM Inflation Targeting,,,John Major,Conservative,Kenneth Clarke,Eddie George,false +1994-12-07,6.13,5.63,50,Hike,86,Post-ERM Inflation Targeting,,,John Major,Conservative,Kenneth Clarke,Eddie George,false +1995-02-02,6.63,6.13,50,Hike,57,Post-ERM Inflation Targeting,,,John Major,Conservative,Kenneth Clarke,Eddie George,false +1995-12-13,6.38,6.63,-25,Cut,314,Post-ERM Inflation Targeting,,,John Major,Conservative,Kenneth Clarke,Eddie George,false +1996-01-18,6.13,6.38,-25,Cut,36,Post-ERM Inflation Targeting,,,John Major,Conservative,Kenneth Clarke,Eddie George,false +1996-03-08,5.94,6.13,-19,Cut,50,Post-ERM Inflation Targeting,,,John Major,Conservative,Kenneth Clarke,Eddie George,false +1996-06-06,5.69,5.94,-25,Cut,90,Post-ERM Inflation Targeting,,,John Major,Conservative,Kenneth Clarke,Eddie George,false +1996-10-30,5.94,5.69,25,Hike,146,Post-ERM Inflation Targeting,,,John Major,Conservative,Kenneth Clarke,Eddie George,false +1997-05-06,6.25,5.94,31,Hike,188,BoE Independence & Dotcom Bubble,Bank of England granted independence,Policy Regime Change,Tony Blair,Labour,Gordon Brown,Eddie George,true +1997-06-06,6.50,6.25,25,Hike,31,BoE Independence & Dotcom Bubble,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +1997-07-10,6.75,6.50,25,Hike,34,BoE Independence & Dotcom Bubble,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +1997-08-07,7.00,6.75,25,Hike,28,BoE Independence & Dotcom Bubble,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +1997-11-06,7.25,7.00,25,Hike,91,BoE Independence & Dotcom Bubble,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +1998-06-04,7.50,7.25,25,Hike,210,BoE Independence & Dotcom Bubble,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +1998-10-08,7.25,7.50,-25,Cut,126,BoE Independence & Dotcom Bubble,LTCM collapse & Russian default,Financial Crisis,Tony Blair,Labour,Gordon Brown,Eddie George,true +1998-11-05,6.75,7.25,-50,Cut,28,BoE Independence & Dotcom Bubble,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +1998-12-10,6.25,6.75,-50,Cut,35,BoE Independence & Dotcom Bubble,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +1999-01-07,6.00,6.25,-25,Cut,28,BoE Independence & Dotcom Bubble,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +1999-02-04,5.50,6.00,-50,Cut,28,BoE Independence & Dotcom Bubble,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +1999-04-08,5.25,5.50,-25,Cut,63,BoE Independence & Dotcom Bubble,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +1999-06-10,5.00,5.25,-25,Cut,63,BoE Independence & Dotcom Bubble,Dotcom bubble inflating,Asset Bubble,Tony Blair,Labour,Gordon Brown,Eddie George,true +1999-09-08,5.25,5.00,25,Hike,90,BoE Independence & Dotcom Bubble,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +1999-11-04,5.50,5.25,25,Hike,57,BoE Independence & Dotcom Bubble,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +2000-01-13,5.75,5.50,25,Hike,70,BoE Independence & Dotcom Bubble,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +2000-02-10,6.00,5.75,25,Hike,28,BoE Independence & Dotcom Bubble,Peak rate weeks before Nasdaq tops out,Asset Bubble,Tony Blair,Labour,Gordon Brown,Eddie George,true +2001-02-08,5.75,6.00,-25,Cut,364,Dotcom Bust,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +2001-04-05,5.50,5.75,-25,Cut,56,Dotcom Bust,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +2001-05-10,5.25,5.50,-25,Cut,35,Dotcom Bust,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +2001-08-02,5.00,5.25,-25,Cut,84,Dotcom Bust,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +2001-09-18,4.75,5.00,-25,Cut,47,Dotcom Bust,Emergency coordinated cut after 9/11,Geopolitical Shock,Tony Blair,Labour,Gordon Brown,Eddie George,true +2001-10-04,4.50,4.75,-25,Cut,16,Dotcom Bust,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +2001-11-08,4.00,4.50,-50,Cut,35,Dotcom Bust,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +2003-02-06,3.75,4.00,-25,Cut,455,Dotcom Bust,,,Tony Blair,Labour,Gordon Brown,Eddie George,true +2003-07-10,3.50,3.75,-25,Cut,154,Great Moderation & Credit Boom,48-year low - dotcom bust trough,Recession,Tony Blair,Labour,Gordon Brown,Mervyn King,true +2003-11-06,3.75,3.50,25,Hike,119,Great Moderation & Credit Boom,,,Tony Blair,Labour,Gordon Brown,Mervyn King,true +2004-02-05,4.00,3.75,25,Hike,91,Great Moderation & Credit Boom,,,Tony Blair,Labour,Gordon Brown,Mervyn King,true +2004-05-06,4.25,4.00,25,Hike,91,Great Moderation & Credit Boom,,,Tony Blair,Labour,Gordon Brown,Mervyn King,true +2004-06-10,4.50,4.25,25,Hike,35,Great Moderation & Credit Boom,,,Tony Blair,Labour,Gordon Brown,Mervyn King,true +2004-08-05,4.75,4.50,25,Hike,56,Great Moderation & Credit Boom,,,Tony Blair,Labour,Gordon Brown,Mervyn King,true +2005-08-04,4.50,4.75,-25,Cut,364,Great Moderation & Credit Boom,,,Tony Blair,Labour,Gordon Brown,Mervyn King,true +2006-08-03,4.75,4.50,25,Hike,364,Great Moderation & Credit Boom,,,Tony Blair,Labour,Gordon Brown,Mervyn King,true +2006-11-09,5.00,4.75,25,Hike,98,Great Moderation & Credit Boom,,,Tony Blair,Labour,Gordon Brown,Mervyn King,true +2007-01-11,5.25,5.00,25,Hike,63,Great Moderation & Credit Boom,,,Tony Blair,Labour,Gordon Brown,Mervyn King,true +2007-05-10,5.50,5.25,25,Hike,119,Great Moderation & Credit Boom,,,Tony Blair,Labour,Gordon Brown,Mervyn King,true +2007-07-05,5.75,5.50,25,Hike,56,Great Moderation & Credit Boom,,,Gordon Brown,Labour,Alistair Darling,Mervyn King,true +2007-12-06,5.50,5.75,-25,Cut,154,Global Financial Crisis,First cut of the credit crunch,Financial Crisis,Gordon Brown,Labour,Alistair Darling,Mervyn King,true +2008-02-07,5.25,5.50,-25,Cut,63,Global Financial Crisis,,,Gordon Brown,Labour,Alistair Darling,Mervyn King,true +2008-04-10,5.00,5.25,-25,Cut,63,Global Financial Crisis,,,Gordon Brown,Labour,Alistair Darling,Mervyn King,true +2008-10-08,4.50,5.00,-50,Cut,181,Global Financial Crisis,Coordinated global emergency cut,Financial Crisis,Gordon Brown,Labour,Alistair Darling,Mervyn King,true +2008-11-06,3.00,4.50,-150,Cut,29,Global Financial Crisis,150bp cut - largest since 1981,Financial Crisis,Gordon Brown,Labour,Alistair Darling,Mervyn King,true +2008-12-04,2.00,3.00,-100,Cut,28,Global Financial Crisis,,,Gordon Brown,Labour,Alistair Darling,Mervyn King,true +2009-01-08,1.50,2.00,-50,Cut,35,Global Financial Crisis,,,Gordon Brown,Labour,Alistair Darling,Mervyn King,true +2009-02-05,1.00,1.50,-50,Cut,28,Global Financial Crisis,,,Gordon Brown,Labour,Alistair Darling,Mervyn King,true +2009-03-05,0.50,1.00,-50,Cut,28,ZIRP & Quantitative Easing,Record low 0.5% and QE launched,Quantitative Easing,Gordon Brown,Labour,Alistair Darling,Mervyn King,true +2016-08-04,0.25,0.50,-25,Cut,2709,Brexit Era,Post-Brexit-referendum cut,Brexit,Theresa May,Conservative,Philip Hammond,Mark Carney,true +2017-11-02,0.50,0.25,25,Hike,455,Brexit Era,First hike in over ten years,Normalisation,Theresa May,Conservative,Philip Hammond,Mark Carney,true +2018-08-02,0.75,0.50,25,Hike,273,Brexit Era,,,Theresa May,Conservative,Philip Hammond,Mark Carney,true +2020-03-11,0.25,0.75,-50,Cut,587,COVID-19 Pandemic,COVID-19 emergency cut,Pandemic,Boris Johnson,Conservative,Rishi Sunak,Mark Carney,true +2020-03-19,0.10,0.25,-15,Cut,8,COVID-19 Pandemic,Record low 0.1% as lockdown begins,Pandemic,Boris Johnson,Conservative,Rishi Sunak,Andrew Bailey,true +2021-12-16,0.25,0.10,15,Hike,637,Inflation Shock,First hike of the inflation cycle,Inflation Shock,Boris Johnson,Conservative,Rishi Sunak,Andrew Bailey,true +2022-02-03,0.50,0.25,25,Hike,49,Inflation Shock,,,Boris Johnson,Conservative,Rishi Sunak,Andrew Bailey,true +2022-03-17,0.75,0.50,25,Hike,42,Inflation Shock,,,Boris Johnson,Conservative,Rishi Sunak,Andrew Bailey,true +2022-05-05,1.00,0.75,25,Hike,49,Inflation Shock,,,Boris Johnson,Conservative,Rishi Sunak,Andrew Bailey,true +2022-06-16,1.25,1.00,25,Hike,42,Inflation Shock,,,Boris Johnson,Conservative,Rishi Sunak,Andrew Bailey,true +2022-08-04,1.75,1.25,50,Hike,49,Inflation Shock,,,Boris Johnson,Conservative,Nadhim Zahawi,Andrew Bailey,true +2022-09-22,2.25,1.75,50,Hike,49,Inflation Shock,Day before the Truss mini-Budget,Fiscal Crisis,Liz Truss,Conservative,Kwasi Kwarteng,Andrew Bailey,true +2022-11-03,3.00,2.25,75,Hike,42,Inflation Shock,75bp - biggest hike in 33 years,Inflation Shock,Rishi Sunak,Conservative,Jeremy Hunt,Andrew Bailey,true +2022-12-15,3.50,3.00,50,Hike,42,Inflation Shock,,,Rishi Sunak,Conservative,Jeremy Hunt,Andrew Bailey,true +2023-02-02,4.00,3.50,50,Hike,49,Inflation Shock,,,Rishi Sunak,Conservative,Jeremy Hunt,Andrew Bailey,true +2023-03-23,4.25,4.00,25,Hike,49,Inflation Shock,,,Rishi Sunak,Conservative,Jeremy Hunt,Andrew Bailey,true +2023-05-11,4.50,4.25,25,Hike,49,Inflation Shock,,,Rishi Sunak,Conservative,Jeremy Hunt,Andrew Bailey,true +2023-06-22,5.00,4.50,50,Hike,42,Inflation Shock,,,Rishi Sunak,Conservative,Jeremy Hunt,Andrew Bailey,true +2023-08-03,5.25,5.00,25,Hike,42,Disinflation & Easing Cycle,Peak of the tightening cycle,Inflation Shock,Rishi Sunak,Conservative,Jeremy Hunt,Andrew Bailey,true +2024-08-01,5.00,5.25,-25,Cut,364,Disinflation & Easing Cycle,First cut of the easing cycle,Disinflation,Keir Starmer,Labour,Rachel Reeves,Andrew Bailey,true +2024-11-07,4.75,5.00,-25,Cut,98,Disinflation & Easing Cycle,,,Keir Starmer,Labour,Rachel Reeves,Andrew Bailey,true +2025-02-06,4.50,4.75,-25,Cut,91,Disinflation & Easing Cycle,,,Keir Starmer,Labour,Rachel Reeves,Andrew Bailey,true +2025-05-08,4.25,4.50,-25,Cut,91,Disinflation & Easing Cycle,,,Keir Starmer,Labour,Rachel Reeves,Andrew Bailey,true +2025-08-07,4.00,4.25,-25,Cut,91,Disinflation & Easing Cycle,,,Keir Starmer,Labour,Rachel Reeves,Andrew Bailey,true +2025-12-18,3.75,4.00,-25,Cut,133,Disinflation & Easing Cycle,,,Keir Starmer,Labour,Rachel Reeves,Andrew Bailey,true diff --git a/examples/boe_rate_history.sql b/examples/boe_rate_history.sql new file mode 100644 index 00000000..51854303 --- /dev/null +++ b/examples/boe_rate_history.sql @@ -0,0 +1,163 @@ +-- #! ../data/boe_rate_history_enriched.csv +-- +-- Bank of England Bank Rate, 1975-2025, tagged with economic eras and +-- the events behind the big moves. Source: bankofengland.co.uk, dates +-- converted to ISO so the engine treats them as real DATETIME values. + +-- The famous moves: every rate change that is itself a piece of history +SELECT + date, + rate, + change_bps, + event_category, + event +FROM boe_rate_history_enriched +WHERE event IS NOT NULL +ORDER BY date DESC; +GO + +-- Fifty years in seventeen eras +SELECT + era, + COUNT(*) AS moves, + MIN(rate) AS low, + MAX(rate) AS high, + ROUND(AVG(rate), 2) AS avg_rate +FROM boe_rate_history_enriched +GROUP BY era +ORDER BY MIN(date); +GO + +-- The crisis playbook: how hard did the Bank cut, and how fast? +SELECT + era, + COUNT(*) AS cuts, + SUM(change_bps) AS total_bps, + MIN(change_bps) AS biggest_single_cut +FROM boe_rate_history_enriched +WHERE direction = 'Cut' +GROUP BY era +ORDER BY total_bps; +GO + +-- Prime ministerial league table - who presided over the dearest money? +SELECT + pm, + party, + COUNT(*) AS moves, + SUM(CASE WHEN direction = 'Hike' THEN 1 ELSE 0 END) AS hikes, + SUM(CASE WHEN direction = 'Cut' THEN 1 ELSE 0 END) AS cuts, + ROUND(AVG(rate), 2) AS avg_rate, + MAX(rate) AS peak +FROM boe_rate_history_enriched +GROUP BY pm, party +ORDER BY avg_rate DESC; +GO + +-- Did independence calm things down? Compare the two regimes +SELECT + boe_independent, + COUNT(*) AS moves, + ROUND(AVG(rate), 2) AS avg_rate, + ROUND(AVG(ABS(change_bps)), 1) AS avg_move_size, + MAX(ABS(change_bps)) AS biggest_move +FROM boe_rate_history_enriched +WHERE change_bps IS NOT NULL +GROUP BY boe_independent; +GO + +-- The long silences: the biggest gaps between rate decisions +SELECT + date, + rate, + days_since_prev, + ROUND(days_since_prev / 365.25, 1) AS years_unchanged, + era +FROM boe_rate_history_enriched +ORDER BY days_since_prev DESC +LIMIT 10; +GO + +-- 1982 had 36 changes; 2015 had none. Busiest years on record +SELECT + YEAR(date) AS yr, + COUNT(*) AS moves, + MIN(rate) AS low, + MAX(rate) AS high, + ROUND(MAX(rate) - MIN(rate), 2) AS swing +FROM boe_rate_history_enriched +GROUP BY YEAR(date) +ORDER BY moves DESC +LIMIT 10; +GO + +-- Which month of the year does the Bank like to move in? +SELECT + MONTH(date) AS month_no, + MONTHNAME(date) AS month, + COUNT(*) AS moves +FROM boe_rate_history_enriched +GROUP BY MONTH(date), MONTHNAME(date) +ORDER BY month_no; +GO + +-- Every rate change of the Global Financial Crisis, peak to trough +SELECT + date, + DAYNAME(date) AS day, + rate, + change_bps, + chancellor, + event +FROM boe_rate_history_enriched +WHERE date >= '2007-07-01' AND date <= '2009-04-01' +ORDER BY date; +GO + +-- Cross-check the precomputed prev_rate against a window function +SELECT + date, + rate, + LAG(rate) OVER (ORDER BY date) AS lag_rate, + prev_rate, + change_bps +FROM boe_rate_history_enriched +WHERE era = 'Inflation Shock' +ORDER BY date; +GO + +-- Banding: how much of the last 50 years was spent at each rate level? +WITH banded AS ( + SELECT + date, + rate, + era, + CASE + WHEN rate < 1 THEN '0-1% (emergency)' + WHEN rate < 5 THEN '1-5% (modern normal)' + WHEN rate < 10 THEN '5-10% (old normal)' + ELSE '10%+ (crisis money)' + END AS band + FROM boe_rate_history_enriched +) +SELECT + band, + COUNT(*) AS moves, + MIN(YEAR(date)) AS first_year, + MAX(YEAR(date)) AS last_year +FROM banded +GROUP BY band +ORDER BY first_year; +GO + +-- Governors and the rates they inherited and left behind +SELECT + governor, + COUNT(*) AS moves, + MIN(date) AS first_move, + MAX(date) AS last_move, + ROUND(AVG(rate), 2) AS avg_rate +FROM boe_rate_history_enriched +GROUP BY governor +ORDER BY first_move; +GO diff --git a/examples/expectations/boe_rate_history.json b/examples/expectations/boe_rate_history.json new file mode 100644 index 00000000..16b2a60c --- /dev/null +++ b/examples/expectations/boe_rate_history.json @@ -0,0 +1,1057 @@ +[ + [ + { + "change_bps": -25, + "date": "2024-08-01", + "event": "First cut of the easing cycle", + "event_category": "Disinflation", + "rate": 5.0 + }, + { + "change_bps": 25, + "date": "2023-08-03", + "event": "Peak of the tightening cycle", + "event_category": "Inflation Shock", + "rate": 5.25 + }, + { + "change_bps": 75, + "date": "2022-11-03", + "event": "75bp - biggest hike in 33 years", + "event_category": "Inflation Shock", + "rate": 3.0 + }, + { + "change_bps": 50, + "date": "2022-09-22", + "event": "Day before the Truss mini-Budget", + "event_category": "Fiscal Crisis", + "rate": 2.25 + }, + { + "change_bps": 15, + "date": "2021-12-16", + "event": "First hike of the inflation cycle", + "event_category": "Inflation Shock", + "rate": 0.25 + }, + { + "change_bps": -15, + "date": "2020-03-19", + "event": "Record low 0.1% as lockdown begins", + "event_category": "Pandemic", + "rate": 0.1 + }, + { + "change_bps": -50, + "date": "2020-03-11", + "event": "COVID-19 emergency cut", + "event_category": "Pandemic", + "rate": 0.25 + }, + { + "change_bps": 25, + "date": "2017-11-02", + "event": "First hike in over ten years", + "event_category": "Normalisation", + "rate": 0.5 + }, + { + "change_bps": -25, + "date": "2016-08-04", + "event": "Post-Brexit-referendum cut", + "event_category": "Brexit", + "rate": 0.25 + }, + { + "change_bps": -50, + "date": "2009-03-05", + "event": "Record low 0.5% and QE launched", + "event_category": "Quantitative Easing", + "rate": 0.5 + }, + { + "change_bps": -150, + "date": "2008-11-06", + "event": "150bp cut - largest since 1981", + "event_category": "Financial Crisis", + "rate": 3.0 + }, + { + "change_bps": -50, + "date": "2008-10-08", + "event": "Coordinated global emergency cut", + "event_category": "Financial Crisis", + "rate": 4.5 + }, + { + "change_bps": -25, + "date": "2007-12-06", + "event": "First cut of the credit crunch", + "event_category": "Financial Crisis", + "rate": 5.5 + }, + { + "change_bps": -25, + "date": "2003-07-10", + "event": "48-year low - dotcom bust trough", + "event_category": "Recession", + "rate": 3.5 + }, + { + "change_bps": -25, + "date": "2001-09-18", + "event": "Emergency coordinated cut after 9/11", + "event_category": "Geopolitical Shock", + "rate": 4.75 + }, + { + "change_bps": 25, + "date": "2000-02-10", + "event": "Peak rate weeks before Nasdaq tops out", + "event_category": "Asset Bubble", + "rate": 6.0 + }, + { + "change_bps": -25, + "date": "1999-06-10", + "event": "Dotcom bubble inflating", + "event_category": "Asset Bubble", + "rate": 5.0 + }, + { + "change_bps": -25, + "date": "1998-10-08", + "event": "LTCM collapse & Russian default", + "event_category": "Financial Crisis", + "rate": 7.25 + }, + { + "change_bps": 31, + "date": "1997-05-06", + "event": "Bank of England granted independence", + "event_category": "Policy Regime Change", + "rate": 6.25 + }, + { + "change_bps": -100, + "date": "1992-10-16", + "event": "Post-ERM easing under inflation targeting", + "event_category": "Policy Regime Change", + "rate": 7.88 + }, + { + "change_bps": -100, + "date": "1992-09-22", + "event": "Black Wednesday aftermath - ERM exit", + "event_category": "Currency Crisis", + "rate": 8.88 + }, + { + "change_bps": -100, + "date": "1990-10-08", + "event": "UK joins the ERM at DM 2.95", + "event_category": "ERM", + "rate": 13.88 + }, + { + "change_bps": 113, + "date": "1989-10-06", + "event": "Rate to 15% - Lawson resigns weeks later", + "event_category": "Political Crisis", + "rate": 14.88 + }, + { + "change_bps": 50, + "date": "1988-06-03", + "event": "Lawson Boom: start of 12 hikes in 18 months", + "event_category": "Credit Boom", + "rate": 7.88 + }, + { + "change_bps": -50, + "date": "1987-11-04", + "event": "Crash response continues", + "event_category": "Market Crash", + "rate": 8.88 + }, + { + "change_bps": -50, + "date": "1987-10-23", + "event": "Post-Black Monday emergency easing", + "event_category": "Market Crash", + "rate": 9.38 + }, + { + "change_bps": 100, + "date": "1986-10-15", + "event": "Big Bang deregulates the City", + "event_category": "Deregulation", + "rate": 10.88 + }, + { + "change_bps": 238, + "date": "1985-01-14", + "event": "Sterling near parity with the dollar", + "event_category": "Currency Crisis", + "rate": 11.88 + }, + { + "change_bps": 200, + "date": "1984-07-11", + "event": "Miners' strike & sterling pressure", + "event_category": "Industrial Unrest", + "rate": 12.0 + }, + { + "change_bps": -200, + "date": "1981-03-11", + "event": "The 364 economists' letter Budget", + "event_category": "Fiscal Policy", + "rate": 12.0 + }, + { + "change_bps": -100, + "date": "1980-07-03", + "event": "Recession bites, squeeze eases", + "event_category": "Recession", + "rate": 16.0 + }, + { + "change_bps": 300, + "date": "1979-11-15", + "event": "Record high 17% - Thatcher's monetarist squeeze", + "event_category": "Monetary Squeeze", + "rate": 17.0 + }, + { + "change_bps": 200, + "date": "1979-06-13", + "event": "Howe's first Budget: VAT nearly doubled", + "event_category": "Fiscal Policy", + "rate": 14.0 + }, + { + "change_bps": 150, + "date": "1979-02-08", + "event": "Winter of Discontent", + "event_category": "Industrial Unrest", + "rate": 14.0 + }, + { + "change_bps": -50, + "date": "1977-10-17", + "event": "Sterling stabilised, rate at decade low", + "event_category": "Recovery", + "rate": 5.0 + }, + { + "change_bps": -25, + "date": "1976-11-22", + "event": "IMF loan negotiations under way", + "event_category": "Currency Crisis", + "rate": 14.75 + }, + { + "change_bps": 200, + "date": "1976-10-07", + "event": "Sterling crisis: MLR to 15%", + "event_category": "Currency Crisis", + "rate": 15.0 + }, + { + "change_bps": null, + "date": "1975-01-20", + "event": "Post oil-shock easing begins", + "event_category": "Oil Shock", + "rate": 11.25 + } + ], + [ + { + "avg_rate": 10.7, + "era": "Stagflation & Sterling Crisis", + "high": 13.0, + "low": 9.0, + "moves": 23 + }, + { + "avg_rate": 10.06, + "era": "IMF Bailout & Winter of Discontent", + "high": 15.0, + "low": 5.0, + "moves": 32 + }, + { + "avg_rate": 12.58, + "era": "Monetarist Shock", + "high": 17.0, + "low": 9.13, + "moves": 49 + }, + { + "avg_rate": 10.66, + "era": "Recovery & Deregulation", + "high": 13.88, + "low": 8.56, + "moves": 35 + }, + { + "avg_rate": 9.43, + "era": "Big Bang & Lawson Boom", + "high": 12.88, + "low": 7.38, + "moves": 20 + }, + { + "avg_rate": 14.02, + "era": "Overheating & Shadowing the D-Mark", + "high": 14.88, + "low": 13.75, + "moves": 5 + }, + { + "avg_rate": 11.88, + "era": "ERM Membership", + "high": 13.88, + "low": 9.88, + "moves": 9 + }, + { + "avg_rate": 6.32, + "era": "Post-ERM Inflation Targeting", + "high": 8.88, + "low": 5.13, + "moves": 14 + }, + { + "avg_rate": 6.22, + "era": "BoE Independence & Dotcom Bubble", + "high": 7.5, + "low": 5.0, + "moves": 17 + }, + { + "avg_rate": 4.81, + "era": "Dotcom Bust", + "high": 5.75, + "low": 3.75, + "moves": 8 + }, + { + "avg_rate": 4.63, + "era": "Great Moderation & Credit Boom", + "high": 5.75, + "low": 3.5, + "moves": 12 + }, + { + "avg_rate": 3.47, + "era": "Global Financial Crisis", + "high": 5.5, + "low": 1.0, + "moves": 8 + }, + { + "avg_rate": 0.5, + "era": "ZIRP & Quantitative Easing", + "high": 0.5, + "low": 0.5, + "moves": 1 + }, + { + "avg_rate": 0.5, + "era": "Brexit Era", + "high": 0.75, + "low": 0.25, + "moves": 3 + }, + { + "avg_rate": 0.18, + "era": "COVID-19 Pandemic", + "high": 0.25, + "low": 0.1, + "moves": 2 + }, + { + "avg_rate": 2.46, + "era": "Inflation Shock", + "high": 5.0, + "low": 0.25, + "moves": 13 + }, + { + "avg_rate": 4.5, + "era": "Disinflation & Easing Cycle", + "high": 5.25, + "low": 3.75, + "moves": 7 + } + ], + [ + { + "biggest_single_cut": -100, + "cuts": 24, + "era": "IMF Bailout & Winter of Discontent", + "total_bps": -1250 + }, + { + "biggest_single_cut": -200, + "cuts": 41, + "era": "Monetarist Shock", + "total_bps": -1113 + }, + { + "biggest_single_cut": -100, + "cuts": 26, + "era": "Recovery & Deregulation", + "total_bps": -1024 + }, + { + "biggest_single_cut": -100, + "cuts": 10, + "era": "Post-ERM Inflation Targeting", + "total_bps": -569 + }, + { + "biggest_single_cut": -100, + "cuts": 9, + "era": "ERM Membership", + "total_bps": -500 + }, + { + "biggest_single_cut": -50, + "cuts": 10, + "era": "Big Bang & Lawson Boom", + "total_bps": -500 + }, + { + "biggest_single_cut": -150, + "cuts": 8, + "era": "Global Financial Crisis", + "total_bps": -475 + }, + { + "biggest_single_cut": -50, + "cuts": 16, + "era": "Stagflation & Sterling Crisis", + "total_bps": -450 + }, + { + "biggest_single_cut": -50, + "cuts": 7, + "era": "BoE Independence & Dotcom Bubble", + "total_bps": -250 + }, + { + "biggest_single_cut": -50, + "cuts": 8, + "era": "Dotcom Bust", + "total_bps": -225 + }, + { + "biggest_single_cut": -25, + "cuts": 6, + "era": "Disinflation & Easing Cycle", + "total_bps": -150 + }, + { + "biggest_single_cut": -50, + "cuts": 2, + "era": "COVID-19 Pandemic", + "total_bps": -65 + }, + { + "biggest_single_cut": -25, + "cuts": 2, + "era": "Great Moderation & Credit Boom", + "total_bps": -50 + }, + { + "biggest_single_cut": -50, + "cuts": 1, + "era": "ZIRP & Quantitative Easing", + "total_bps": -50 + }, + { + "biggest_single_cut": -25, + "cuts": 1, + "era": "Brexit Era", + "total_bps": -25 + }, + { + "biggest_single_cut": -13, + "cuts": 1, + "era": "Overheating & Shadowing the D-Mark", + "total_bps": -13 + } + ], + [ + { + "avg_rate": 11.47, + "cuts": 79, + "hikes": 31, + "moves": 110, + "party": "Conservative", + "peak": 17.0, + "pm": "Margaret Thatcher" + }, + { + "avg_rate": 10.55, + "cuts": 16, + "hikes": 3, + "moves": 20, + "party": "Labour", + "peak": 12.0, + "pm": "Harold Wilson" + }, + { + "avg_rate": 10.2, + "cuts": 24, + "hikes": 11, + "moves": 35, + "party": "Labour", + "peak": 15.0, + "pm": "James Callaghan" + }, + { + "avg_rate": 8.25, + "cuts": 18, + "hikes": 4, + "moves": 22, + "party": "Conservative", + "peak": 13.38, + "pm": "John Major" + }, + { + "avg_rate": 5.39, + "cuts": 17, + "hikes": 19, + "moves": 36, + "party": "Labour", + "peak": 7.5, + "pm": "Tony Blair" + }, + { + "avg_rate": 4.38, + "cuts": 6, + "hikes": 0, + "moves": 6, + "party": "Labour", + "peak": 5.0, + "pm": "Keir Starmer" + }, + { + "avg_rate": 4.21, + "cuts": 0, + "hikes": 7, + "moves": 7, + "party": "Conservative", + "peak": 5.25, + "pm": "Rishi Sunak" + }, + { + "avg_rate": 3.4, + "cuts": 9, + "hikes": 1, + "moves": 10, + "party": "Labour", + "peak": 5.75, + "pm": "Gordon Brown" + }, + { + "avg_rate": 2.25, + "cuts": 0, + "hikes": 1, + "moves": 1, + "party": "Conservative", + "peak": 2.25, + "pm": "Liz Truss" + }, + { + "avg_rate": 0.73, + "cuts": 2, + "hikes": 6, + "moves": 8, + "party": "Conservative", + "peak": 1.75, + "pm": "Boris Johnson" + }, + { + "avg_rate": 0.5, + "cuts": 1, + "hikes": 2, + "moves": 3, + "party": "Conservative", + "peak": 0.75, + "pm": "Theresa May" + } + ], + [ + { + "avg_move_size": 55.4, + "avg_rate": 10.75, + "biggest_move": 300, + "boe_independent": false, + "moves": 186 + }, + { + "avg_move_size": 33.3, + "avg_rate": 4.13, + "biggest_move": 150, + "boe_independent": true, + "moves": 71 + } + ], + [ + { + "date": "2016-08-04", + "days_since_prev": 2709, + "era": "Brexit Era", + "rate": 0.25, + "years_unchanged": 7.4 + }, + { + "date": "2021-12-16", + "days_since_prev": 637, + "era": "Inflation Shock", + "rate": 0.25, + "years_unchanged": 1.7 + }, + { + "date": "2020-03-11", + "days_since_prev": 587, + "era": "COVID-19 Pandemic", + "rate": 0.25, + "years_unchanged": 1.6 + }, + { + "date": "2003-02-06", + "days_since_prev": 455, + "era": "Dotcom Bust", + "rate": 3.75, + "years_unchanged": 1.2 + }, + { + "date": "2017-11-02", + "days_since_prev": 455, + "era": "Brexit Era", + "rate": 0.5, + "years_unchanged": 1.2 + }, + { + "date": "1990-10-08", + "days_since_prev": 367, + "era": "ERM Membership", + "rate": 13.88, + "years_unchanged": 1.0 + }, + { + "date": "2001-02-08", + "days_since_prev": 364, + "era": "Dotcom Bust", + "rate": 5.75, + "years_unchanged": 1.0 + }, + { + "date": "2005-08-04", + "days_since_prev": 364, + "era": "Great Moderation & Credit Boom", + "rate": 4.5, + "years_unchanged": 1.0 + }, + { + "date": "2006-08-03", + "days_since_prev": 364, + "era": "Great Moderation & Credit Boom", + "rate": 4.75, + "years_unchanged": 1.0 + }, + { + "date": "2024-08-01", + "days_since_prev": 364, + "era": "Disinflation & Easing Cycle", + "rate": 5.0, + "years_unchanged": 1.0 + } + ], + [ + { + "high": 14.31, + "low": 9.13, + "moves": 36, + "swing": 5.18, + "yr": 1982.0 + }, + { + "high": 14.0, + "low": 5.0, + "moves": 19, + "swing": 9.0, + "yr": 1977.0 + }, + { + "high": 15.0, + "low": 9.0, + "moves": 14, + "swing": 6.0, + "yr": 1976.0 + }, + { + "high": 12.0, + "low": 9.75, + "moves": 13, + "swing": 2.25, + "yr": 1975.0 + }, + { + "high": 12.0, + "low": 8.56, + "moves": 13, + "swing": 3.44, + "yr": 1984.0 + }, + { + "high": 12.88, + "low": 7.38, + "moves": 12, + "swing": 5.5, + "yr": 1988.0 + }, + { + "high": 15.13, + "low": 12.0, + "moves": 9, + "swing": 3.13, + "yr": 1981.0 + }, + { + "high": 11.0, + "low": 9.06, + "moves": 9, + "swing": 1.94, + "yr": 1983.0 + }, + { + "high": 10.38, + "low": 8.38, + "moves": 8, + "swing": 2.0, + "yr": 1987.0 + }, + { + "high": 3.5, + "low": 0.5, + "moves": 8, + "swing": 3.0, + "yr": 2022.0 + } + ], + [ + { + "month": "January", + "month_no": 1.0, + "moves": 24 + }, + { + "month": "February", + "month_no": 2.0, + "moves": 23 + }, + { + "month": "March", + "month_no": 3.0, + "moves": 26 + }, + { + "month": "April", + "month_no": 4.0, + "moves": 21 + }, + { + "month": "May", + "month_no": 5.0, + "moves": 20 + }, + { + "month": "June", + "month_no": 6.0, + "moves": 16 + }, + { + "month": "July", + "month_no": 7.0, + "moves": 20 + }, + { + "month": "August", + "month_no": 8.0, + "moves": 32 + }, + { + "month": "September", + "month_no": 9.0, + "moves": 16 + }, + { + "month": "October", + "month_no": 10.0, + "moves": 17 + }, + { + "month": "November", + "month_no": 11.0, + "moves": 29 + }, + { + "month": "December", + "month_no": 12.0, + "moves": 14 + } + ], + [ + { + "chancellor": "Alistair Darling", + "change_bps": 25, + "date": "2007-07-05", + "day": "Thursday", + "event": null, + "rate": 5.75 + }, + { + "chancellor": "Alistair Darling", + "change_bps": -25, + "date": "2007-12-06", + "day": "Thursday", + "event": "First cut of the credit crunch", + "rate": 5.5 + }, + { + "chancellor": "Alistair Darling", + "change_bps": -25, + "date": "2008-02-07", + "day": "Thursday", + "event": null, + "rate": 5.25 + }, + { + "chancellor": "Alistair Darling", + "change_bps": -25, + "date": "2008-04-10", + "day": "Thursday", + "event": null, + "rate": 5.0 + }, + { + "chancellor": "Alistair Darling", + "change_bps": -50, + "date": "2008-10-08", + "day": "Wednesday", + "event": "Coordinated global emergency cut", + "rate": 4.5 + }, + { + "chancellor": "Alistair Darling", + "change_bps": -150, + "date": "2008-11-06", + "day": "Thursday", + "event": "150bp cut - largest since 1981", + "rate": 3.0 + }, + { + "chancellor": "Alistair Darling", + "change_bps": -100, + "date": "2008-12-04", + "day": "Thursday", + "event": null, + "rate": 2.0 + }, + { + "chancellor": "Alistair Darling", + "change_bps": -50, + "date": "2009-01-08", + "day": "Thursday", + "event": null, + "rate": 1.5 + }, + { + "chancellor": "Alistair Darling", + "change_bps": -50, + "date": "2009-02-05", + "day": "Thursday", + "event": null, + "rate": 1.0 + }, + { + "chancellor": "Alistair Darling", + "change_bps": -50, + "date": "2009-03-05", + "day": "Thursday", + "event": "Record low 0.5% and QE launched", + "rate": 0.5 + } + ], + [ + { + "change_bps": 15, + "date": "2021-12-16", + "lag_rate": 0.1, + "prev_rate": 0.1, + "rate": 0.25 + }, + { + "change_bps": 25, + "date": "2022-02-03", + "lag_rate": 0.25, + "prev_rate": 0.25, + "rate": 0.5 + }, + { + "change_bps": 25, + "date": "2022-03-17", + "lag_rate": 0.5, + "prev_rate": 0.5, + "rate": 0.75 + }, + { + "change_bps": 25, + "date": "2022-05-05", + "lag_rate": 0.75, + "prev_rate": 0.75, + "rate": 1.0 + }, + { + "change_bps": 25, + "date": "2022-06-16", + "lag_rate": 1.0, + "prev_rate": 1.0, + "rate": 1.25 + }, + { + "change_bps": 50, + "date": "2022-08-04", + "lag_rate": 1.25, + "prev_rate": 1.25, + "rate": 1.75 + }, + { + "change_bps": 50, + "date": "2022-09-22", + "lag_rate": 1.75, + "prev_rate": 1.75, + "rate": 2.25 + }, + { + "change_bps": 75, + "date": "2022-11-03", + "lag_rate": 2.25, + "prev_rate": 2.25, + "rate": 3.0 + }, + { + "change_bps": 50, + "date": "2022-12-15", + "lag_rate": 3.0, + "prev_rate": 3.0, + "rate": 3.5 + }, + { + "change_bps": 50, + "date": "2023-02-02", + "lag_rate": 3.5, + "prev_rate": 3.5, + "rate": 4.0 + }, + { + "change_bps": 25, + "date": "2023-03-23", + "lag_rate": 4.0, + "prev_rate": 4.0, + "rate": 4.25 + }, + { + "change_bps": 25, + "date": "2023-05-11", + "lag_rate": 4.25, + "prev_rate": 4.25, + "rate": 4.5 + }, + { + "change_bps": 50, + "date": "2023-06-22", + "lag_rate": 4.5, + "prev_rate": 4.5, + "rate": 5.0 + } + ], + [ + { + "band": "10%+ (crisis money)", + "first_year": 1975.0, + "last_year": 1991.0, + "moves": 121 + }, + { + "band": "5-10% (old normal)", + "first_year": 1975.0, + "last_year": 2024.0, + "moves": 97 + }, + { + "band": "1-5% (modern normal)", + "first_year": 2001.0, + "last_year": 2025.0, + "moves": 31 + }, + { + "band": "0-1% (emergency)", + "first_year": 2009.0, + "last_year": 2022.0, + "moves": 9 + } + ], + [ + { + "avg_rate": 11.32, + "first_move": "1975-01-20", + "governor": "Gordon Richardson", + "last_move": "1983-06-14", + "moves": 110 + }, + { + "avg_rate": 10.55, + "first_move": "1983-08-09", + "governor": "Robin Leigh-Pemberton", + "last_move": "1993-01-26", + "moves": 67 + }, + { + "avg_rate": 5.81, + "first_move": "1993-11-23", + "governor": "Eddie George", + "last_move": "2003-02-06", + "moves": 35 + }, + { + "avg_rate": 3.99, + "first_move": "2003-07-10", + "governor": "Mervyn King", + "last_move": "2009-03-05", + "moves": 21 + }, + { + "avg_rate": 0.44, + "first_move": "2016-08-04", + "governor": "Mark Carney", + "last_move": "2020-03-11", + "moves": 4 + }, + { + "avg_rate": 3.03, + "first_move": "2020-03-19", + "governor": "Andrew Bailey", + "last_move": "2025-12-18", + "moves": 21 + } + ] +] \ No newline at end of file diff --git a/scripts/enrich_boe_rates.py b/scripts/enrich_boe_rates.py new file mode 100644 index 00000000..1f7f8edb --- /dev/null +++ b/scripts/enrich_boe_rates.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 +""" +Enrich the Bank of England Bank Rate history with ISO dates and economic context. + +The raw download from bankofengland.co.uk uses a "18 Dec 25" date format that +sql-cli treats as a plain string - ORDER BY sorts lexically and YEAR() returns +nonsense. This script rewrites the dates as ISO (YYYY-MM-DD) so the engine +infers a real DATETIME column, and denormalises economic context alongside +(sql-cli takes one data source per invocation, so a join is not an option). + +Two-digit years are pivoted at 70: 75 -> 1975, 25 -> 2025. + +Usage: + python3 scripts/enrich_boe_rates.py + python3 scripts/enrich_boe_rates.py --src other.csv --out other_enriched.csv + +To add a new event, add an entry to EVENTS keyed by the ISO date of the rate +change it belongs to. The script warns if a key matches no rate change. +""" + +import argparse +import csv +from datetime import date, datetime +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +DEFAULT_SRC = REPO_ROOT / "data" / "boe_rate_history.csv" +DEFAULT_OUT = REPO_ROOT / "data" / "boe_rate_history_enriched.csv" + +MONTHS = {m: i + 1 for i, m in enumerate( + "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split())} + +# Economic periods. Boundaries are the event that opened them where there is a +# clean one (ERM entry, Big Bang, BoE independence, Lehman week, first COVID cut). +ERAS = [ + ("1975-01-01", "1976-09-30", "Stagflation & Sterling Crisis"), + ("1976-10-01", "1979-05-03", "IMF Bailout & Winter of Discontent"), + ("1979-05-04", "1982-12-31", "Monetarist Shock"), + ("1983-01-01", "1986-10-26", "Recovery & Deregulation"), + ("1986-10-27", "1988-12-31", "Big Bang & Lawson Boom"), + ("1989-01-01", "1990-10-07", "Overheating & Shadowing the D-Mark"), + ("1990-10-08", "1992-09-15", "ERM Membership"), + ("1992-09-16", "1997-05-05", "Post-ERM Inflation Targeting"), + ("1997-05-06", "2000-03-09", "BoE Independence & Dotcom Bubble"), + ("2000-03-10", "2003-03-31", "Dotcom Bust"), + ("2003-04-01", "2007-08-08", "Great Moderation & Credit Boom"), + ("2007-08-09", "2009-03-04", "Global Financial Crisis"), + ("2009-03-05", "2016-06-23", "ZIRP & Quantitative Easing"), + ("2016-06-24", "2020-03-10", "Brexit Era"), + ("2020-03-11", "2021-12-15", "COVID-19 Pandemic"), + ("2021-12-16", "2023-08-02", "Inflation Shock"), + ("2023-08-03", None, "Disinflation & Easing Cycle"), +] + +PMS = [ + ("1974-03-04", "1976-04-05", "Harold Wilson", "Labour"), + ("1976-04-06", "1979-05-03", "James Callaghan", "Labour"), + ("1979-05-04", "1990-11-27", "Margaret Thatcher", "Conservative"), + ("1990-11-28", "1997-05-01", "John Major", "Conservative"), + ("1997-05-02", "2007-06-26", "Tony Blair", "Labour"), + ("2007-06-27", "2010-05-10", "Gordon Brown", "Labour"), + ("2010-05-11", "2016-07-12", "David Cameron", "Conservative"), + ("2016-07-13", "2019-07-23", "Theresa May", "Conservative"), + ("2019-07-24", "2022-09-05", "Boris Johnson", "Conservative"), + ("2022-09-06", "2022-10-24", "Liz Truss", "Conservative"), + ("2022-10-25", "2024-07-04", "Rishi Sunak", "Conservative"), + ("2024-07-05", None, "Keir Starmer", "Labour"), +] + +CHANCELLORS = [ + ("1974-03-05", "1979-05-03", "Denis Healey"), + ("1979-05-04", "1983-06-10", "Geoffrey Howe"), + ("1983-06-11", "1989-10-25", "Nigel Lawson"), + ("1989-10-26", "1990-11-27", "John Major"), + ("1990-11-28", "1993-05-26", "Norman Lamont"), + ("1993-05-27", "1997-05-01", "Kenneth Clarke"), + ("1997-05-02", "2007-06-27", "Gordon Brown"), + ("2007-06-28", "2010-05-10", "Alistair Darling"), + ("2010-05-11", "2016-07-12", "George Osborne"), + ("2016-07-13", "2019-07-23", "Philip Hammond"), + ("2019-07-24", "2020-02-12", "Sajid Javid"), + ("2020-02-13", "2022-07-04", "Rishi Sunak"), + ("2022-07-05", "2022-09-05", "Nadhim Zahawi"), + ("2022-09-06", "2022-10-13", "Kwasi Kwarteng"), + ("2022-10-14", "2024-07-04", "Jeremy Hunt"), + ("2024-07-05", None, "Rachel Reeves"), +] + +GOVERNORS = [ + ("1973-07-01", "1983-06-30", "Gordon Richardson"), + ("1983-07-01", "1993-06-30", "Robin Leigh-Pemberton"), + ("1993-07-01", "2003-06-30", "Eddie George"), + ("2003-07-01", "2013-06-30", "Mervyn King"), + ("2013-07-01", "2020-03-15", "Mark Carney"), + ("2020-03-16", None, "Andrew Bailey"), +] + +# Rate changes that are themselves a piece of economic history, keyed by the +# ISO date of the change. Note the BoE series does not contain Black Wednesday's +# intraday 12% -> 15% hikes (both reversed the same day), so 1992-09-22 is +# tagged as the ERM-exit aftermath instead. +EVENTS = { + "1975-01-20": ("Post oil-shock easing begins", "Oil Shock"), + "1976-10-07": ("Sterling crisis: MLR to 15%", "Currency Crisis"), + "1976-11-22": ("IMF loan negotiations under way", "Currency Crisis"), + "1977-10-17": ("Sterling stabilised, rate at decade low", "Recovery"), + "1979-02-08": ("Winter of Discontent", "Industrial Unrest"), + "1979-06-13": ("Howe's first Budget: VAT nearly doubled", "Fiscal Policy"), + "1979-11-15": ("Record high 17% - Thatcher's monetarist squeeze", "Monetary Squeeze"), + "1980-07-03": ("Recession bites, squeeze eases", "Recession"), + "1981-03-11": ("The 364 economists' letter Budget", "Fiscal Policy"), + "1984-07-11": ("Miners' strike & sterling pressure", "Industrial Unrest"), + "1985-01-14": ("Sterling near parity with the dollar", "Currency Crisis"), + "1986-10-15": ("Big Bang deregulates the City", "Deregulation"), + "1987-10-23": ("Post-Black Monday emergency easing", "Market Crash"), + "1987-11-04": ("Crash response continues", "Market Crash"), + "1988-06-03": ("Lawson Boom: start of 12 hikes in 18 months", "Credit Boom"), + "1989-10-06": ("Rate to 15% - Lawson resigns weeks later", "Political Crisis"), + "1990-10-08": ("UK joins the ERM at DM 2.95", "ERM"), + "1992-09-22": ("Black Wednesday aftermath - ERM exit", "Currency Crisis"), + "1992-10-16": ("Post-ERM easing under inflation targeting", "Policy Regime Change"), + "1997-05-06": ("Bank of England granted independence", "Policy Regime Change"), + "1998-10-08": ("LTCM collapse & Russian default", "Financial Crisis"), + "1999-06-10": ("Dotcom bubble inflating", "Asset Bubble"), + "2000-02-10": ("Peak rate weeks before Nasdaq tops out", "Asset Bubble"), + "2001-09-18": ("Emergency coordinated cut after 9/11", "Geopolitical Shock"), + "2003-07-10": ("48-year low - dotcom bust trough", "Recession"), + "2007-12-06": ("First cut of the credit crunch", "Financial Crisis"), + "2008-10-08": ("Coordinated global emergency cut", "Financial Crisis"), + "2008-11-06": ("150bp cut - largest since 1981", "Financial Crisis"), + "2009-03-05": ("Record low 0.5% and QE launched", "Quantitative Easing"), + "2016-08-04": ("Post-Brexit-referendum cut", "Brexit"), + "2017-11-02": ("First hike in over ten years", "Normalisation"), + "2020-03-11": ("COVID-19 emergency cut", "Pandemic"), + "2020-03-19": ("Record low 0.1% as lockdown begins", "Pandemic"), + "2021-12-16": ("First hike of the inflation cycle", "Inflation Shock"), + "2022-09-22": ("Day before the Truss mini-Budget", "Fiscal Crisis"), + "2022-11-03": ("75bp - biggest hike in 33 years", "Inflation Shock"), + "2023-08-03": ("Peak of the tightening cycle", "Inflation Shock"), + "2024-08-01": ("First cut of the easing cycle", "Disinflation"), +} + +HEADER = [ + "date", "rate", "prev_rate", "change_bps", "direction", "days_since_prev", + "era", "event", "event_category", + "pm", "party", "chancellor", "governor", "boe_independent", +] + +INDEPENDENCE = date(1997, 5, 6) + + +def parse_boe_date(s): + """'18 Dec 25' -> date(2025, 12, 18). Two-digit years >= 70 are 19xx.""" + day, mon, year = s.strip().split() + yy = int(year) + return date(1900 + yy if yy >= 70 else 2000 + yy, MONTHS[mon], int(day)) + + +def lookup(d, table): + """Find the row of `table` whose [start, end] range covers `d`. + + Each row is (start_iso, end_iso_or_None, *values); returns the values + tuple, or None if no range matches. An end of None means "to date". + """ + for start, end, *values in table: + lo = datetime.strptime(start, "%Y-%m-%d").date() + hi = datetime.strptime(end, "%Y-%m-%d").date() if end else date(9999, 1, 1) + if lo <= d <= hi: + return values + return None + + +def one(d, table): + """lookup() for single-value tables, blank when nothing matches.""" + values = lookup(d, table) + return values[0] if values else "" + + +def build_row(d, rate, prev): + """Assemble one output row. `prev` is the (date, rate) before it, or None.""" + iso = d.isoformat() + if prev: + prev_date, prev_rate = prev + bps = round((rate - prev_rate) * 100) + days = (d - prev_date).days + direction = "Hike" if bps > 0 else "Cut" if bps < 0 else "Hold" + prev_rate_out = f"{prev_rate:.2f}" + else: + prev_rate_out, bps, days, direction = "", "", "", "First" + + pm = lookup(d, PMS) or ["", ""] + event, category = EVENTS.get(iso, ("", "")) + + return [ + iso, f"{rate:.2f}", prev_rate_out, bps, direction, days, + one(d, ERAS), event, category, + pm[0], pm[1], one(d, CHANCELLORS), one(d, GOVERNORS), + "true" if d >= INDEPENDENCE else "false", + ] + + +def enrich(src, out): + with open(src, newline="") as f: + rows = [r for r in csv.DictReader(f) if r.get("Date Changed")] + + series = sorted( + ((parse_boe_date(r["Date Changed"]), float(r["Rate"])) for r in rows), + key=lambda t: t[0], + ) + + with open(out, "w", newline="") as f: + writer = csv.writer(f) + writer.writerow(HEADER) + for i, (d, rate) in enumerate(series): + writer.writerow(build_row(d, rate, series[i - 1] if i else None)) + + print(f"wrote {out}: {len(series)} rows, {len(HEADER)} columns") + print(f"date range: {series[0][0]} to {series[-1][0]}") + + orphans = sorted(set(EVENTS) - {d.isoformat() for d, _ in series}) + if orphans: + print(f"WARNING - {len(orphans)} event dates match no rate change: {orphans}") + else: + print(f"events tagged: {len(EVENTS)}/{len(EVENTS)}") + + +def main(): + parser = argparse.ArgumentParser(description=__doc__.splitlines()[1]) + parser.add_argument("--src", type=Path, default=DEFAULT_SRC, + help=f"raw BoE CSV (default: {DEFAULT_SRC.relative_to(REPO_ROOT)})") + parser.add_argument("--out", type=Path, default=DEFAULT_OUT, + help=f"enriched output (default: {DEFAULT_OUT.relative_to(REPO_ROOT)})") + args = parser.parse_args() + enrich(args.src, args.out) + + +if __name__ == "__main__": + main()