interest-rate-decisions 1.0.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.
- interest_rate_decisions-1.0.0/.gitignore +17 -0
- interest_rate_decisions-1.0.0/LICENSE +21 -0
- interest_rate_decisions-1.0.0/PKG-INFO +51 -0
- interest_rate_decisions-1.0.0/README.md +31 -0
- interest_rate_decisions-1.0.0/pyproject.toml +32 -0
- interest_rate_decisions-1.0.0/src/interest_rate_decisions/__init__.py +94 -0
- interest_rate_decisions-1.0.0/src/interest_rate_decisions/__main__.py +39 -0
- interest_rate_decisions-1.0.0/tests/test_client.py +35 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 InterestRateDecision.com
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: interest-rate-decisions
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Central bank interest rates and decisions (Fed, ECB, BoE, BoJ, BoC, RBA, RBNZ, SNB, Norges Bank, Riksbank): today's rates, the next meetings and every decision since 2008.
|
|
5
|
+
Project-URL: Homepage, https://interestratedecision.com/central-bank-data/
|
|
6
|
+
Project-URL: Source, https://github.com/Interest-Rate-Decision/interest-rate-decisions/tree/main/python
|
|
7
|
+
Author: InterestRateDecision.com
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: central bank,ecb,economic calendar,fed,fomc,forex,interest rates,monetary policy
|
|
11
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
15
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Provides-Extra: pandas
|
|
18
|
+
Requires-Dist: pandas; extra == 'pandas'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# interest-rate-decisions
|
|
22
|
+
|
|
23
|
+
Central bank interest rates and decisions, from [InterestRateDecision.com](https://interestratedecision.com/): today's policy rates, the next meetings and every decision since 2008, for the Fed, ECB, Bank of England, Bank of Japan, Bank of Canada, RBA, RBNZ, SNB, Norges Bank and the Riksbank.
|
|
24
|
+
|
|
25
|
+
Standard library only. pandas is optional.
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
pip install interest-rate-decisions
|
|
29
|
+
interest-rate-decisions # the next decisions and every rate
|
|
30
|
+
interest-rate-decisions fed # one bank
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
import interest_rate_decisions as ird
|
|
35
|
+
|
|
36
|
+
ird.get_rate("fed") # {'bank': 'fed', 'rate': 4.0, 'rangeLow': 3.75, 'nextDecision': ...}
|
|
37
|
+
ird.get_next() # the next decision of any bank
|
|
38
|
+
ird.get_upcoming("ecb") # the ECB's meetings to come
|
|
39
|
+
ird.get_decisions("boe") # every Bank of England decision since 2008
|
|
40
|
+
|
|
41
|
+
df = ird.decisions_frame("fed") # pip install "interest-rate-decisions[pandas]"
|
|
42
|
+
df.rate.plot(drawstyle="steps-post")
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Banks: `fed`, `ecb`, `boe`, `boj`, `boc`, `rba`, `rbnz`, `snb`, `norges`, `riksbank`. Rates are per cent; the Fed's `rate` is the top of its target range. Times are UTC. The ECB's `rate` is None before March 2015, where the source gives only its refinancing rate's change.
|
|
46
|
+
|
|
47
|
+
The data updates within minutes of each decision. For the odds of the next move, charts and what each decision did to the currency, see [interestratedecision.com](https://interestratedecision.com/).
|
|
48
|
+
|
|
49
|
+
## Data terms
|
|
50
|
+
|
|
51
|
+
The code is MIT. The data is free to use in anything, including commercial work, **with a visible link to [InterestRateDecision.com](https://interestratedecision.com/)** ([terms](https://interestratedecision.com/central-bank-data/#licence)).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# interest-rate-decisions
|
|
2
|
+
|
|
3
|
+
Central bank interest rates and decisions, from [InterestRateDecision.com](https://interestratedecision.com/): today's policy rates, the next meetings and every decision since 2008, for the Fed, ECB, Bank of England, Bank of Japan, Bank of Canada, RBA, RBNZ, SNB, Norges Bank and the Riksbank.
|
|
4
|
+
|
|
5
|
+
Standard library only. pandas is optional.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pip install interest-rate-decisions
|
|
9
|
+
interest-rate-decisions # the next decisions and every rate
|
|
10
|
+
interest-rate-decisions fed # one bank
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import interest_rate_decisions as ird
|
|
15
|
+
|
|
16
|
+
ird.get_rate("fed") # {'bank': 'fed', 'rate': 4.0, 'rangeLow': 3.75, 'nextDecision': ...}
|
|
17
|
+
ird.get_next() # the next decision of any bank
|
|
18
|
+
ird.get_upcoming("ecb") # the ECB's meetings to come
|
|
19
|
+
ird.get_decisions("boe") # every Bank of England decision since 2008
|
|
20
|
+
|
|
21
|
+
df = ird.decisions_frame("fed") # pip install "interest-rate-decisions[pandas]"
|
|
22
|
+
df.rate.plot(drawstyle="steps-post")
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Banks: `fed`, `ecb`, `boe`, `boj`, `boc`, `rba`, `rbnz`, `snb`, `norges`, `riksbank`. Rates are per cent; the Fed's `rate` is the top of its target range. Times are UTC. The ECB's `rate` is None before March 2015, where the source gives only its refinancing rate's change.
|
|
26
|
+
|
|
27
|
+
The data updates within minutes of each decision. For the odds of the next move, charts and what each decision did to the currency, see [interestratedecision.com](https://interestratedecision.com/).
|
|
28
|
+
|
|
29
|
+
## Data terms
|
|
30
|
+
|
|
31
|
+
The code is MIT. The data is free to use in anything, including commercial work, **with a visible link to [InterestRateDecision.com](https://interestratedecision.com/)** ([terms](https://interestratedecision.com/central-bank-data/#licence)).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "interest-rate-decisions"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Central bank interest rates and decisions (Fed, ECB, BoE, BoJ, BoC, RBA, RBNZ, SNB, Norges Bank, Riksbank): today's rates, the next meetings and every decision since 2008."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "InterestRateDecision.com" }]
|
|
14
|
+
keywords = ["interest rates", "central bank", "fed", "fomc", "ecb", "forex", "monetary policy", "economic calendar"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Topic :: Office/Business :: Financial",
|
|
18
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
19
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
20
|
+
"Intended Audience :: Science/Research",
|
|
21
|
+
]
|
|
22
|
+
dependencies = []
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
pandas = ["pandas"]
|
|
26
|
+
|
|
27
|
+
[project.scripts]
|
|
28
|
+
interest-rate-decisions = "interest_rate_decisions.__main__:main"
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://interestratedecision.com/central-bank-data/"
|
|
32
|
+
Source = "https://github.com/Interest-Rate-Decision/interest-rate-decisions/tree/main/python"
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Central bank interest rates and decisions from InterestRateDecision.com.
|
|
2
|
+
|
|
3
|
+
Today's policy rates, the next meetings and every decision since 2008 for the
|
|
4
|
+
Fed, ECB, Bank of England, Bank of Japan, Bank of Canada, RBA, RBNZ, SNB,
|
|
5
|
+
Norges Bank and the Riksbank. Standard library only; pandas is optional.
|
|
6
|
+
|
|
7
|
+
Data terms: free to reuse with a visible link to https://interestratedecision.com
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import csv
|
|
13
|
+
import io
|
|
14
|
+
import json
|
|
15
|
+
import time
|
|
16
|
+
import urllib.request
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
|
+
from typing import Any, Callable, Optional
|
|
19
|
+
|
|
20
|
+
__version__ = "1.0.0"
|
|
21
|
+
__all__ = ["BASE", "BANKS", "get_rates", "get_rate", "get_upcoming", "get_next", "get_decisions", "parse_decisions", "decisions_frame"]
|
|
22
|
+
|
|
23
|
+
BASE = "https://interestratedecision.com"
|
|
24
|
+
BANKS = ("fed", "ecb", "boe", "boj", "boc", "rba", "rbnz", "snb", "norges", "riksbank")
|
|
25
|
+
|
|
26
|
+
Fetch = Callable[[str], str]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _fetch(url: str) -> str:
|
|
30
|
+
req = urllib.request.Request(url, headers={"User-Agent": f"interest-rate-decisions-python/{__version__}"})
|
|
31
|
+
with urllib.request.urlopen(req, timeout=20) as res:
|
|
32
|
+
return res.read().decode("utf-8")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _get(path: str, fetch: Optional[Fetch] = None, base: str = BASE) -> str:
|
|
36
|
+
return (fetch or _fetch)(f"{base}{path}")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _ts(iso: str) -> float:
|
|
40
|
+
return datetime.fromisoformat(iso.replace("Z", "+00:00")).timestamp()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_rates(fetch: Optional[Fetch] = None) -> list[dict[str, Any]]:
|
|
44
|
+
"""Every bank's rate today (per cent; the Fed's is the top of its range, ``rangeLow`` the bottom), its last change and next decision."""
|
|
45
|
+
return json.loads(_get("/data/rates.json", fetch))["rates"]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_rate(bank: str, fetch: Optional[Fetch] = None) -> dict[str, Any]:
|
|
49
|
+
"""One bank's rate today."""
|
|
50
|
+
for r in get_rates(fetch):
|
|
51
|
+
if r["bank"] == bank:
|
|
52
|
+
return r
|
|
53
|
+
raise ValueError(f'unknown bank "{bank}" (one of {", ".join(BANKS)})')
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_upcoming(bank: Optional[str] = None, fetch: Optional[Fetch] = None) -> list[dict[str, Any]]:
|
|
57
|
+
"""The decisions to come, soonest first; times (``t``) are ISO UTC."""
|
|
58
|
+
ds = json.loads(_get("/data/next.json", fetch))["decisions"]
|
|
59
|
+
ds = [d for d in ds if bank is None or d["bank"] == bank]
|
|
60
|
+
return sorted(ds, key=lambda d: _ts(d["t"]))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def get_next(bank: Optional[str] = None, fetch: Optional[Fetch] = None) -> Optional[dict[str, Any]]:
|
|
64
|
+
"""The next decision, of one bank or of any; None when none is listed."""
|
|
65
|
+
now = time.time()
|
|
66
|
+
return next((d for d in get_upcoming(bank, fetch) if _ts(d["t"]) > now - 3600), None)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def parse_decisions(text: str) -> list[dict[str, Any]]:
|
|
70
|
+
"""Rows of the decisions CSV, with ``rate`` and ``change_bp`` as numbers (None where blank)."""
|
|
71
|
+
out = []
|
|
72
|
+
for row in csv.DictReader(io.StringIO(text)):
|
|
73
|
+
out.append({
|
|
74
|
+
"bank": row["bank"],
|
|
75
|
+
"bank_name": row["bank_name"],
|
|
76
|
+
"time_utc": datetime.fromisoformat(row["time_utc"].replace("Z", "+00:00")).astimezone(timezone.utc),
|
|
77
|
+
"date": row["date"],
|
|
78
|
+
"rate": float(row["rate"]) if row["rate"] != "" else None,
|
|
79
|
+
"change_bp": int(row["change_bp"]) if row["change_bp"] != "" else None,
|
|
80
|
+
})
|
|
81
|
+
return out
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_decisions(bank: Optional[str] = None, fetch: Optional[Fetch] = None) -> list[dict[str, Any]]:
|
|
85
|
+
"""Every decision since 2008, oldest first. The ECB's ``rate`` is None before March 2015."""
|
|
86
|
+
rows = parse_decisions(_get("/data/decisions.csv", fetch))
|
|
87
|
+
return [r for r in rows if bank is None or r["bank"] == bank]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def decisions_frame(bank: Optional[str] = None, fetch: Optional[Fetch] = None):
|
|
91
|
+
"""The decisions as a pandas DataFrame indexed by time (needs pandas)."""
|
|
92
|
+
import pandas as pd # optional
|
|
93
|
+
|
|
94
|
+
return pd.DataFrame(get_decisions(bank, fetch)).set_index("time_utc")
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""python -m interest_rate_decisions [bank] [--json]: the next decisions and every rate."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
import time
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
from . import BANKS, get_rates, get_upcoming, _ts
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _until(t: str) -> str:
|
|
12
|
+
s = _ts(t) - time.time()
|
|
13
|
+
if s <= 0:
|
|
14
|
+
return "now"
|
|
15
|
+
d, h, m = int(s // 86400), int(s % 86400 // 3600), int(s % 3600 // 60)
|
|
16
|
+
return f"{d}d {h}h" if d else f"{h}h {m}m" if h else f"{m}m"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main() -> None:
|
|
20
|
+
args = sys.argv[1:]
|
|
21
|
+
bank = next((a for a in args if a in BANKS), None)
|
|
22
|
+
rates = [r for r in get_rates() if bank is None or r["bank"] == bank]
|
|
23
|
+
upcoming = [d for d in get_upcoming(bank) if _ts(d["t"]) > time.time() - 3600]
|
|
24
|
+
if "--json" in args:
|
|
25
|
+
print(json.dumps({"rates": rates, "next": upcoming[:10]}, indent=2))
|
|
26
|
+
return
|
|
27
|
+
print("\nNext central bank decisions (your time)")
|
|
28
|
+
for d in upcoming[: 4 if bank else 6]:
|
|
29
|
+
local = datetime.fromtimestamp(_ts(d["t"])).strftime("%a %d %b, %H:%M")
|
|
30
|
+
print(f" {d['short']:<12} {local:<20}{' est.' if d['tentative'] else ' '} in {_until(d['t'])}")
|
|
31
|
+
print("\nPolicy rates")
|
|
32
|
+
for r in rates:
|
|
33
|
+
v = f"{r['rangeLow']:.2f}-{r['rate']:.2f}%" if r.get("rangeLow") is not None else f"{r['rate']:.2f}%"
|
|
34
|
+
print(f" {r['bank']:<12} {v:<13} {r['name']}")
|
|
35
|
+
print(f"\nOdds, history and what each decision did to the currency: https://interestratedecision.com{'/' + bank + '/' if bank else ''}\n")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if __name__ == "__main__":
|
|
39
|
+
main()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import unittest
|
|
3
|
+
|
|
4
|
+
import interest_rate_decisions as ird
|
|
5
|
+
|
|
6
|
+
FILES = {
|
|
7
|
+
"/data/rates.json": json.dumps({"rates": [{"bank": "fed", "name": "Federal Reserve", "rate": 4, "rangeLow": 3.75}]}),
|
|
8
|
+
"/data/next.json": json.dumps({"decisions": [{"bank": "ecb", "t": "2999-02-01T12:00:00Z"}, {"bank": "fed", "t": "2999-01-01T19:00:00Z"}, {"bank": "boe", "t": "2000-01-01T12:00:00Z"}]}),
|
|
9
|
+
"/data/decisions.csv": "bank,bank_name,time_utc,date,rate,change_bp\necb,European Central Bank,2008-01-10T12:45:00.000Z,2008-01-10,,\nfed,Federal Reserve,2008-01-22T13:20:00.000Z,2008-01-22,3.5,-75\n",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def fetch(url: str) -> str:
|
|
14
|
+
return FILES[url.replace(ird.BASE, "")]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Client(unittest.TestCase):
|
|
18
|
+
def test_rate(self):
|
|
19
|
+
self.assertEqual(ird.get_rate("fed", fetch)["rangeLow"], 3.75)
|
|
20
|
+
with self.assertRaises(ValueError):
|
|
21
|
+
ird.get_rate("xyz", fetch)
|
|
22
|
+
|
|
23
|
+
def test_next(self):
|
|
24
|
+
self.assertEqual(ird.get_next(fetch=fetch)["bank"], "fed")
|
|
25
|
+
self.assertIsNone(ird.get_next("boe", fetch))
|
|
26
|
+
|
|
27
|
+
def test_decisions(self):
|
|
28
|
+
rows = ird.get_decisions(fetch=fetch)
|
|
29
|
+
self.assertIsNone(rows[0]["rate"])
|
|
30
|
+
self.assertEqual(rows[1]["change_bp"], -75)
|
|
31
|
+
self.assertEqual(rows[1]["time_utc"].year, 2008)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
unittest.main()
|