ecbfx 0.2.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.
ecbfx-0.2.0/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .venv/
7
+ .coverage
8
+ htmlcov/
9
+ .mypy_cache/
10
+ .pytest_cache/
11
+ *.pyc
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 — 2026-05-01
4
+ - Added `fetch_rates_for_pairs()` — sparse transaction date support
5
+ - Added `fetch_latest()` — most recent available rate
6
+ - Added `validate_currency()` — public input validation
7
+ - Added `--pairs`, `--latest`, `--quiet`, `--decimal`, `--no-gap-fill` CLI flags
8
+ - LOCF gap-filling — always uses prior trading day, never look-ahead
9
+ - HTTP session reuse, exponential backoff with jitter
10
+ - Fixed: `re.fullmatch` for currency validation (trailing newline bypass)
ecbfx-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 edvinassvedas-dev
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.
ecbfx-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,226 @@
1
+ Metadata-Version: 2.4
2
+ Name: ecbfx
3
+ Version: 0.2.0
4
+ Summary: ECB foreign exchange rate lookup — CLI and Python API with gap-fill, strict mode, and scripting support
5
+ Project-URL: Homepage, https://github.com/edvinassvedas-dev/ecbfx
6
+ Project-URL: Repository, https://github.com/edvinassvedas-dev/ecbfx
7
+ Project-URL: Issues, https://github.com/edvinassvedas-dev/ecbfx/issues
8
+ Author: Edvinas Švedas
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: cli,currency,ecb,exchange-rate,finance,forex,fx
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Financial and Insurance Industry
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Office/Business :: Financial
24
+ Classifier: Topic :: Utilities
25
+ Requires-Python: >=3.9
26
+ Requires-Dist: requests>=2.28
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest-cov; extra == 'dev'
29
+ Requires-Dist: pytest>=7; extra == 'dev'
30
+ Requires-Dist: responses; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # ecbfx
34
+
35
+ [![PyPI version](https://img.shields.io/pypi/v/ecbfx.svg)](https://pypi.org/project/ecbfx/)
36
+ [![Python](https://img.shields.io/pypi/pyversions/ecbfx.svg)](https://pypi.org/project/ecbfx/)
37
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/edvinassvedas-dev/ecbfx/blob/main/LICENSE)
38
+
39
+ A minimal command-line tool and Python library for fetching EUR foreign exchange
40
+ rates directly from the [ECB SDMX API](https://data-api.ecb.europa.eu).
41
+
42
+ ---
43
+
44
+ ## Install
45
+
46
+ ```bash
47
+ pip install ecbfx
48
+ ```
49
+
50
+ **For development** (includes test dependencies):
51
+
52
+ ```bash
53
+ git clone https://github.com/edvinassvedas-dev/ecbfx.git
54
+ cd ecbfx
55
+ pip install -e ".[dev]"
56
+ pytest
57
+ ```
58
+
59
+ ## CLI usage
60
+
61
+ ```bash
62
+ # Today's rate for USD (indirect: USD per 1 EUR — ECB native)
63
+ # Note: ECB publishes rates ~16:00 CET on trading days.
64
+ # If today's rate isn't available yet, use --latest instead.
65
+ ecbfx USD
66
+
67
+ # Specific date
68
+ ecbfx USD 2025-01-15
69
+
70
+ # Multiple currencies, specific date
71
+ ecbfx USD GBP CHF 2025-01-15
72
+
73
+ # Date range
74
+ ecbfx USD GBP --from 2025-01-01 --to 2025-03-31
75
+
76
+ # Direct convention: EUR per 1 foreign unit (inverted)
77
+ ecbfx USD 2025-01-15 --direct
78
+
79
+ # Most recent available rate (ignores today being a weekend/holiday)
80
+ ecbfx USD --latest
81
+
82
+ # Single value only — ideal for shell scripting
83
+ ecbfx USD --latest --quiet
84
+ RATE=$(ecbfx USD --quiet)
85
+
86
+ # Control decimal precision (default: 4)
87
+ ecbfx USD 2025-01-15 --decimal 6
88
+
89
+ # Strict mode — error instead of gap-filling on weekends/holidays
90
+ ecbfx USD 2025-01-15 --no-gap-fill
91
+
92
+ # CSV output (pipe-friendly)
93
+ ecbfx USD --from 2025-01-01 --to 2025-01-31 --csv
94
+ ecbfx USD --from 2025-01-01 --to 2025-01-31 --csv > rates.csv
95
+
96
+ # Read pairs from a file — one HTTP call per currency
97
+ ecbfx --pairs transactions.csv --direct --csv
98
+
99
+ # Read pairs from stdin
100
+ cat transactions.csv | ecbfx --pairs - --direct --csv
101
+
102
+ # Combine with date filter
103
+ ecbfx --pairs transactions.csv --from 2025-01-01 --to 2025-03-31 --csv
104
+ ```
105
+
106
+ ### Convention
107
+
108
+ | Flag | Formula | Example |
109
+ |---|---|---|
110
+ | *(default)* | foreign units per 1 EUR | `1 EUR = 1.0830 USD` |
111
+ | `--direct` | EUR per 1 foreign unit | `1 USD = 0.9234 EUR` |
112
+
113
+ ECB publishes indirect natively. `--direct` inverts the rate.
114
+ The `convention` column in CSV output (`USD/EUR` or `EUR/USD`) makes the
115
+ direction explicit for downstream pipelines.
116
+
117
+ ### Flags reference
118
+
119
+ | Flag | Default | Description |
120
+ |---|---|---|
121
+ | `--direct` | off | EUR per 1 foreign unit instead of ECB native |
122
+ | `--latest` | off | Most recent available rate, regardless of date |
123
+ | `--quiet` / `-q` | off | Print rate value(s) only — ideal for scripting |
124
+ | `--decimal N` | 4 | Decimal places in output rate |
125
+ | `--no-gap-fill` | off | Raise an error on weekends/holidays instead of substituting the nearest rate |
126
+ | `--csv` | off | CSV output instead of formatted table |
127
+ | `--pairs FILE\|-` | — | Read `date,currency` pairs from a file or stdin |
128
+
129
+ ### Weekend and holiday gap-filling
130
+
131
+ ECB only publishes rates on trading days. By default, `ecbfx` automatically
132
+ uses the most recent prior trading day's rate (Last Observation Carried Forward)
133
+ when a requested date falls on a weekend or public holiday — including the first
134
+ date in a range that starts on a holiday such as January 1st.
135
+
136
+ Use `--no-gap-fill` to disable this and receive an explicit error instead —
137
+ useful in audit workflows where a substituted rate is not acceptable.
138
+
139
+ ---
140
+
141
+
142
+ ### Exit codes
143
+
144
+ | Code | Meaning |
145
+ |---|---|
146
+ | `0` | Success |
147
+ | `1` | Runtime error (ECB API failure, network issue, no data returned) |
148
+ | `2` | Usage error (invalid arguments, bad date format, missing required flag) |
149
+
150
+ Useful for scripting:
151
+ ```bash
152
+ ecbfx USD --quiet || echo "fetch failed, exit $?"
153
+ ```
154
+
155
+ ## Python API
156
+
157
+ ```python
158
+ from datetime import date
159
+ from ecbfx import fetch_rates, fetch_rates_for_pairs, fetch_latest
160
+
161
+ # Indirect (default) — foreign units per 1 EUR, contiguous range
162
+ rows = fetch_rates(["USD", "GBP"], date(2025, 1, 1), date(2025, 1, 31))
163
+
164
+ # Direct — EUR per 1 foreign unit
165
+ rows = fetch_rates(["USD"], date(2025, 1, 15), date(2025, 1, 15), direct=True)
166
+
167
+ # Most recent available rate
168
+ rows = fetch_latest(["USD", "CHF"])
169
+
170
+ # Sparse transaction dates — one HTTP call per currency regardless of pair count
171
+ pairs = [
172
+ (date(2025, 1, 15), "USD"),
173
+ (date(2025, 1, 20), "GBP"),
174
+ (date(2025, 2, 3), "USD"),
175
+ (date(2025, 2, 3), "CHF"),
176
+ ]
177
+ rows = fetch_rates_for_pairs(pairs, direct=True)
178
+
179
+ # Custom decimal precision
180
+ rows = fetch_rates(["USD"], date(2025, 1, 15), date(2025, 1, 15), decimals=6)
181
+
182
+ # Strict mode — raises ECBError on weekends/holidays
183
+ rows = fetch_rates(["USD"], date(2025, 1, 13), date(2025, 1, 13), gap_fill=False)
184
+ rows = fetch_rates_for_pairs([(date(2025, 1, 13), "USD")], gap_fill=False)
185
+
186
+ for r in rows:
187
+ print(r["date"], r["currency"], r["convention"], r["rate"])
188
+ ```
189
+
190
+ Each row is a dict: `{date, currency, rate, convention}`.
191
+
192
+ ### Input validation
193
+
194
+ ```python
195
+ from ecbfx import validate_currency, ECBError
196
+
197
+ # Normalises and validates a currency code — raises ECBError if invalid
198
+ print(validate_currency("usd")) # → "USD"
199
+ print(validate_currency(" GBP ")) # → "GBP"
200
+
201
+ try:
202
+ validate_currency("US$")
203
+ except ECBError as e:
204
+ print(e) # Invalid currency code 'US$'. Expected 2–4 ASCII letters.
205
+ ```
206
+
207
+ Use `ECBError` in `try/except` blocks when calling any `ecbfx` function
208
+ to handle API failures, network errors, or invalid inputs cleanly.
209
+
210
+ ### fetch_rates vs fetch_rates_for_pairs
211
+
212
+ | | `fetch_rates` | `fetch_rates_for_pairs` |
213
+ |---|---|---|
214
+ | Input | currency list + date range | list of `(date, currency)` tuples |
215
+ | Returns | every calendar day in range | exactly the requested dates |
216
+ | Best for | daily pipelines, backfill | transaction enrichment, broker CSVs |
217
+ | HTTP calls | one per currency | one per currency (full span, regardless of gaps) |
218
+
219
+ > **Note:** `fetch_rates_for_pairs` always fetches the full date span from the earliest to the latest date per currency in a single HTTP call. For very sparse data (e.g. two transactions 10 years apart), this fetches the entire intervening range. A warning is logged when the span exceeds one year.
220
+
221
+
222
+ ---
223
+
224
+ ## License
225
+
226
+ MIT
ecbfx-0.2.0/README.md ADDED
@@ -0,0 +1,194 @@
1
+ # ecbfx
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/ecbfx.svg)](https://pypi.org/project/ecbfx/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/ecbfx.svg)](https://pypi.org/project/ecbfx/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/edvinassvedas-dev/ecbfx/blob/main/LICENSE)
6
+
7
+ A minimal command-line tool and Python library for fetching EUR foreign exchange
8
+ rates directly from the [ECB SDMX API](https://data-api.ecb.europa.eu).
9
+
10
+ ---
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pip install ecbfx
16
+ ```
17
+
18
+ **For development** (includes test dependencies):
19
+
20
+ ```bash
21
+ git clone https://github.com/edvinassvedas-dev/ecbfx.git
22
+ cd ecbfx
23
+ pip install -e ".[dev]"
24
+ pytest
25
+ ```
26
+
27
+ ## CLI usage
28
+
29
+ ```bash
30
+ # Today's rate for USD (indirect: USD per 1 EUR — ECB native)
31
+ # Note: ECB publishes rates ~16:00 CET on trading days.
32
+ # If today's rate isn't available yet, use --latest instead.
33
+ ecbfx USD
34
+
35
+ # Specific date
36
+ ecbfx USD 2025-01-15
37
+
38
+ # Multiple currencies, specific date
39
+ ecbfx USD GBP CHF 2025-01-15
40
+
41
+ # Date range
42
+ ecbfx USD GBP --from 2025-01-01 --to 2025-03-31
43
+
44
+ # Direct convention: EUR per 1 foreign unit (inverted)
45
+ ecbfx USD 2025-01-15 --direct
46
+
47
+ # Most recent available rate (ignores today being a weekend/holiday)
48
+ ecbfx USD --latest
49
+
50
+ # Single value only — ideal for shell scripting
51
+ ecbfx USD --latest --quiet
52
+ RATE=$(ecbfx USD --quiet)
53
+
54
+ # Control decimal precision (default: 4)
55
+ ecbfx USD 2025-01-15 --decimal 6
56
+
57
+ # Strict mode — error instead of gap-filling on weekends/holidays
58
+ ecbfx USD 2025-01-15 --no-gap-fill
59
+
60
+ # CSV output (pipe-friendly)
61
+ ecbfx USD --from 2025-01-01 --to 2025-01-31 --csv
62
+ ecbfx USD --from 2025-01-01 --to 2025-01-31 --csv > rates.csv
63
+
64
+ # Read pairs from a file — one HTTP call per currency
65
+ ecbfx --pairs transactions.csv --direct --csv
66
+
67
+ # Read pairs from stdin
68
+ cat transactions.csv | ecbfx --pairs - --direct --csv
69
+
70
+ # Combine with date filter
71
+ ecbfx --pairs transactions.csv --from 2025-01-01 --to 2025-03-31 --csv
72
+ ```
73
+
74
+ ### Convention
75
+
76
+ | Flag | Formula | Example |
77
+ |---|---|---|
78
+ | *(default)* | foreign units per 1 EUR | `1 EUR = 1.0830 USD` |
79
+ | `--direct` | EUR per 1 foreign unit | `1 USD = 0.9234 EUR` |
80
+
81
+ ECB publishes indirect natively. `--direct` inverts the rate.
82
+ The `convention` column in CSV output (`USD/EUR` or `EUR/USD`) makes the
83
+ direction explicit for downstream pipelines.
84
+
85
+ ### Flags reference
86
+
87
+ | Flag | Default | Description |
88
+ |---|---|---|
89
+ | `--direct` | off | EUR per 1 foreign unit instead of ECB native |
90
+ | `--latest` | off | Most recent available rate, regardless of date |
91
+ | `--quiet` / `-q` | off | Print rate value(s) only — ideal for scripting |
92
+ | `--decimal N` | 4 | Decimal places in output rate |
93
+ | `--no-gap-fill` | off | Raise an error on weekends/holidays instead of substituting the nearest rate |
94
+ | `--csv` | off | CSV output instead of formatted table |
95
+ | `--pairs FILE\|-` | — | Read `date,currency` pairs from a file or stdin |
96
+
97
+ ### Weekend and holiday gap-filling
98
+
99
+ ECB only publishes rates on trading days. By default, `ecbfx` automatically
100
+ uses the most recent prior trading day's rate (Last Observation Carried Forward)
101
+ when a requested date falls on a weekend or public holiday — including the first
102
+ date in a range that starts on a holiday such as January 1st.
103
+
104
+ Use `--no-gap-fill` to disable this and receive an explicit error instead —
105
+ useful in audit workflows where a substituted rate is not acceptable.
106
+
107
+ ---
108
+
109
+
110
+ ### Exit codes
111
+
112
+ | Code | Meaning |
113
+ |---|---|
114
+ | `0` | Success |
115
+ | `1` | Runtime error (ECB API failure, network issue, no data returned) |
116
+ | `2` | Usage error (invalid arguments, bad date format, missing required flag) |
117
+
118
+ Useful for scripting:
119
+ ```bash
120
+ ecbfx USD --quiet || echo "fetch failed, exit $?"
121
+ ```
122
+
123
+ ## Python API
124
+
125
+ ```python
126
+ from datetime import date
127
+ from ecbfx import fetch_rates, fetch_rates_for_pairs, fetch_latest
128
+
129
+ # Indirect (default) — foreign units per 1 EUR, contiguous range
130
+ rows = fetch_rates(["USD", "GBP"], date(2025, 1, 1), date(2025, 1, 31))
131
+
132
+ # Direct — EUR per 1 foreign unit
133
+ rows = fetch_rates(["USD"], date(2025, 1, 15), date(2025, 1, 15), direct=True)
134
+
135
+ # Most recent available rate
136
+ rows = fetch_latest(["USD", "CHF"])
137
+
138
+ # Sparse transaction dates — one HTTP call per currency regardless of pair count
139
+ pairs = [
140
+ (date(2025, 1, 15), "USD"),
141
+ (date(2025, 1, 20), "GBP"),
142
+ (date(2025, 2, 3), "USD"),
143
+ (date(2025, 2, 3), "CHF"),
144
+ ]
145
+ rows = fetch_rates_for_pairs(pairs, direct=True)
146
+
147
+ # Custom decimal precision
148
+ rows = fetch_rates(["USD"], date(2025, 1, 15), date(2025, 1, 15), decimals=6)
149
+
150
+ # Strict mode — raises ECBError on weekends/holidays
151
+ rows = fetch_rates(["USD"], date(2025, 1, 13), date(2025, 1, 13), gap_fill=False)
152
+ rows = fetch_rates_for_pairs([(date(2025, 1, 13), "USD")], gap_fill=False)
153
+
154
+ for r in rows:
155
+ print(r["date"], r["currency"], r["convention"], r["rate"])
156
+ ```
157
+
158
+ Each row is a dict: `{date, currency, rate, convention}`.
159
+
160
+ ### Input validation
161
+
162
+ ```python
163
+ from ecbfx import validate_currency, ECBError
164
+
165
+ # Normalises and validates a currency code — raises ECBError if invalid
166
+ print(validate_currency("usd")) # → "USD"
167
+ print(validate_currency(" GBP ")) # → "GBP"
168
+
169
+ try:
170
+ validate_currency("US$")
171
+ except ECBError as e:
172
+ print(e) # Invalid currency code 'US$'. Expected 2–4 ASCII letters.
173
+ ```
174
+
175
+ Use `ECBError` in `try/except` blocks when calling any `ecbfx` function
176
+ to handle API failures, network errors, or invalid inputs cleanly.
177
+
178
+ ### fetch_rates vs fetch_rates_for_pairs
179
+
180
+ | | `fetch_rates` | `fetch_rates_for_pairs` |
181
+ |---|---|---|
182
+ | Input | currency list + date range | list of `(date, currency)` tuples |
183
+ | Returns | every calendar day in range | exactly the requested dates |
184
+ | Best for | daily pipelines, backfill | transaction enrichment, broker CSVs |
185
+ | HTTP calls | one per currency | one per currency (full span, regardless of gaps) |
186
+
187
+ > **Note:** `fetch_rates_for_pairs` always fetches the full date span from the earliest to the latest date per currency in a single HTTP call. For very sparse data (e.g. two transactions 10 years apart), this fetches the entire intervening range. A warning is logged when the span exceeds one year.
188
+
189
+
190
+ ---
191
+
192
+ ## License
193
+
194
+ MIT
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "ecbfx"
7
+ version = "0.2.0"
8
+ description = "ECB foreign exchange rate lookup — CLI and Python API with gap-fill, strict mode, and scripting support"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.9"
12
+ keywords = ["ecb", "fx", "forex", "exchange-rate", "currency", "finance", "cli"]
13
+
14
+ authors = [
15
+ { name = "Edvinas Švedas"},
16
+ ]
17
+
18
+ classifiers = [
19
+ "Development Status :: 3 - Alpha",
20
+ "Environment :: Console",
21
+ "Intended Audience :: Developers",
22
+ "Intended Audience :: Financial and Insurance Industry",
23
+ "License :: OSI Approved :: MIT License",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3.9",
26
+ "Programming Language :: Python :: 3.10",
27
+ "Programming Language :: Python :: 3.11",
28
+ "Programming Language :: Python :: 3.12",
29
+ "Programming Language :: Python :: 3.13",
30
+ "Topic :: Office/Business :: Financial",
31
+ "Topic :: Utilities",
32
+ ]
33
+
34
+ dependencies = [
35
+ "requests>=2.28",
36
+ ]
37
+
38
+ [project.optional-dependencies]
39
+ dev = [
40
+ "pytest>=7",
41
+ "pytest-cov",
42
+ "responses", # HTTP mocking for tests
43
+ ]
44
+
45
+ [project.scripts]
46
+ ecbfx = "ecbfx.cli:main"
47
+
48
+ [project.urls]
49
+ Homepage = "https://github.com/edvinassvedas-dev/ecbfx"
50
+ Repository = "https://github.com/edvinassvedas-dev/ecbfx"
51
+ Issues = "https://github.com/edvinassvedas-dev/ecbfx/issues"
52
+
53
+ [tool.hatch.build.targets.wheel]
54
+ packages = ["src/ecbfx"]
55
+
56
+ [tool.pytest.ini_options]
57
+ testpaths = ["tests"]
58
+ addopts = "-v --tb=short"
@@ -0,0 +1,31 @@
1
+ """
2
+ ecbfx — ECB foreign exchange rate lookup tool.
3
+ """
4
+
5
+ from .api import (
6
+ ECBError,
7
+ validate_currency,
8
+ fetch_rates,
9
+ fetch_rates_for_pairs,
10
+ fetch_latest,
11
+ fetch_ecb_raw,
12
+ parse_ecb_csv,
13
+ resolve_rate,
14
+ )
15
+
16
+ try:
17
+ from importlib.metadata import version as _v
18
+ __version__ = _v("ecbfx")
19
+ except Exception:
20
+ __version__ = "unknown"
21
+
22
+ __all__ = [
23
+ "ECBError",
24
+ "validate_currency",
25
+ "fetch_rates",
26
+ "fetch_rates_for_pairs",
27
+ "fetch_latest",
28
+ "fetch_ecb_raw",
29
+ "parse_ecb_csv",
30
+ "resolve_rate",
31
+ ]