OfflineCurrencyConverter 2026.11.9__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.
Files changed (19) hide show
  1. offlinecurrencyconverter-2026.11.9/OfflineCurrencyConverter.egg-info/PKG-INFO +262 -0
  2. offlinecurrencyconverter-2026.11.9/OfflineCurrencyConverter.egg-info/SOURCES.txt +17 -0
  3. offlinecurrencyconverter-2026.11.9/OfflineCurrencyConverter.egg-info/dependency_links.txt +1 -0
  4. offlinecurrencyconverter-2026.11.9/OfflineCurrencyConverter.egg-info/entry_points.txt +2 -0
  5. offlinecurrencyconverter-2026.11.9/OfflineCurrencyConverter.egg-info/requires.txt +3 -0
  6. offlinecurrencyconverter-2026.11.9/OfflineCurrencyConverter.egg-info/top_level.txt +1 -0
  7. offlinecurrencyconverter-2026.11.9/PKG-INFO +262 -0
  8. offlinecurrencyconverter-2026.11.9/README.md +252 -0
  9. offlinecurrencyconverter-2026.11.9/offline_currency_converter/__init__.py +32 -0
  10. offlinecurrencyconverter-2026.11.9/offline_currency_converter/__main__.py +3 -0
  11. offlinecurrencyconverter-2026.11.9/offline_currency_converter/_version.py +1 -0
  12. offlinecurrencyconverter-2026.11.9/offline_currency_converter/assets.json +429 -0
  13. offlinecurrencyconverter-2026.11.9/offline_currency_converter/assets.py +139 -0
  14. offlinecurrencyconverter-2026.11.9/offline_currency_converter/cli.py +76 -0
  15. offlinecurrencyconverter-2026.11.9/offline_currency_converter/conversion.py +264 -0
  16. offlinecurrencyconverter-2026.11.9/offline_currency_converter/core.py +852 -0
  17. offlinecurrencyconverter-2026.11.9/offline_currency_converter/data/eurofxref_and_crypto_rates.2026.11.09.csv +10172 -0
  18. offlinecurrencyconverter-2026.11.9/pyproject.toml +21 -0
  19. offlinecurrencyconverter-2026.11.9/setup.cfg +4 -0
@@ -0,0 +1,262 @@
1
+ Metadata-Version: 2.4
2
+ Name: OfflineCurrencyConverter
3
+ Version: 2026.11.9
4
+ Summary: ECB and cryptocurrency historical rates with EUR cross-conversion
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: pandas<3.0,>=2.2
8
+ Requires-Dist: requests<3.0,>=2.32
9
+ Requires-Dist: openpyxl<4.0,>=3.1
10
+
11
+ # OfflineCurrencyConverter
12
+
13
+ Historical fiat and cryptocurrency conversion for offline Python applications.
14
+ Requires Python 3.10 or newer.
15
+
16
+ OfflineCurrencyConverter follows the familiar
17
+ [CurrencyConverter API](https://github.com/alexprengere/currencyconverter),
18
+ including `CurrencyConverter`, `convert()`, `date=`, currency bounds, and
19
+ optional fallback settings. It extends this interface with cryptocurrency
20
+ coverage, configurable date windows, and exchange-rate quotes with snapshot
21
+ metadata. See [API compatibility](#api-compatibility) for supported behavior.
22
+
23
+ **Exchange-rate data is included in every package.** Automated daily builds
24
+ refresh the bundled historical dataset and produce a date-versioned wheel.
25
+ Applications receive updated rates by installing a newer package build;
26
+ conversion and rate lookup never require access to external data services.
27
+ This supports air-gapped deployments using an internal Python package index
28
+ or a local wheelhouse.
29
+
30
+ ## Currency data
31
+
32
+ Fiat reference rates come from the European Central Bank. Cryptocurrency and
33
+ stablecoin rates come from CoinMarketCap. All source rates are stored as units
34
+ per EUR, enabling conversion between supported currencies through EUR.
35
+
36
+ Each wheel contains one cumulative CSV snapshot. Its calendar starts on
37
+ 1999-01-04 and ends on the day before the snapshot's run date. Individual
38
+ currencies have different periods of coverage. Crypto uses completed daily
39
+ closing prices; unfinished current-day observations are excluded.
40
+
41
+ A scheduled GitHub Action generates updated data and a wheel daily at
42
+ 16:30 Europe/Amsterdam. Successful builds are committed to [`output/`](output/)
43
+ and made available as workflow artifacts. Versions follow the snapshot date:
44
+ `2026.11.9` identifies the November 9, 2026 snapshot. Publication to public PyPI
45
+ is a separate, manually triggered workflow.
46
+
47
+ Fiat observations may be absent on weekends and holidays. Missing or failed
48
+ source observations remain unavailable until a subsequent update obtains them.
49
+ The installed dataset stays fixed until the package or local snapshot is
50
+ replaced; the converter does not refresh it automatically.
51
+
52
+ ## Installation
53
+
54
+ Download a wheel from [`output/`](output/), then install it in a virtual environment:
55
+
56
+ ```sh
57
+ python3 -m venv .venv
58
+ source .venv/bin/activate
59
+ python -m pip install /path/to/offlinecurrencyconverter-2026.11.9-py3-none-any.whl
60
+ ```
61
+
62
+ For air-gapped systems, make the wheel and its dependencies available through
63
+ an internal PyPI mirror or a local wheelhouse. For example:
64
+
65
+ ```sh
66
+ python -m pip install --no-index --find-links=/path/to/wheelhouse OfflineCurrencyConverter
67
+ ```
68
+
69
+ The wheel bundles the rates, but its dependencies must also be installed.
70
+ To replace an installed build with an updated wheel of the same version, use
71
+ `python -m pip install --force-reinstall /path/to/package.whl`.
72
+
73
+ ## Python API
74
+
75
+ Create a converter once and reuse it:
76
+
77
+ ```python
78
+ from offline_currency_converter import CurrencyConverter
79
+
80
+ c = CurrencyConverter()
81
+ euros = c.convert(100, "USD") # EUR is the default destination.
82
+ yen = c.convert(100, "USD", "JPY") # Latest common observation.
83
+ ```
84
+
85
+ Choose a historical date with `date=`:
86
+
87
+ ```python
88
+ from datetime import date
89
+
90
+ amount = c.convert(100, "USD", "EUR", date=date(2026, 11, 2))
91
+ ```
92
+
93
+ Omitting `date`, or passing `None`, uses the latest common observation in the
94
+ loaded snapshot. An explicit date requires an exact observation unless a date
95
+ window or fallback is enabled. Dates also accept `datetime` objects,
96
+ `YYYY-MM-DD` strings, and `D.M.YYYY` strings.
97
+
98
+ ### Decimal amounts and rounding
99
+
100
+ Conversions return floats by default. Enable `decimal=True` for `Decimal`
101
+ results, and pass string or `Decimal` amounts to preserve input precision:
102
+
103
+ ```python
104
+ from decimal import Decimal, ROUND_HALF_UP
105
+ from offline_currency_converter import CurrencyConverter
106
+
107
+ c = CurrencyConverter(decimal=True)
108
+ eur = c.convert("100", "USD", date="2026-11-02")
109
+ eur = eur.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
110
+ ```
111
+
112
+ The converter does not apply monetary rounding. Round the final amount to the
113
+ destination currency's minor unit: EUR and USD use two decimal places, while
114
+ some currencies use zero or three. Formatting with `f"{eur:.2f}"` changes only
115
+ the displayed text; `quantize()` returns a rounded `Decimal` value.
116
+
117
+ ### Date windows
118
+
119
+ Pass `date_window` to select the nearest usable observation within an inclusive
120
+ window of calendar days before or after the requested date:
121
+
122
+ ```python
123
+ amount = c.convert("100", "USD", "EUR", date="2026-11-02", date_window=30)
124
+ ```
125
+
126
+ An equally distant pair of observations resolves to the earlier date. Both
127
+ currencies must have valid rates on the same observation date. Zero requires
128
+ an exact match. An explicit window overrides fallback settings, including
129
+ out-of-range fallback, and raises `RateNotFoundError` if no match exists.
130
+
131
+ Applications requiring a 30-day policy must explicitly pass their selected
132
+ `date` and `date_window=30`.
133
+
134
+ ### Fallbacks
135
+
136
+ Enable last-known fallback for missing dates within the currencies' date bounds:
137
+
138
+ ```python
139
+ c = CurrencyConverter(fallback_on_missing_rate=True, decimal=True)
140
+ ```
141
+
142
+ Enable boundary fallback for requested dates outside the available range:
143
+
144
+ ```python
145
+ c = CurrencyConverter(
146
+ fallback_on_missing_rate=True,
147
+ fallback_on_wrong_date=True,
148
+ decimal=True,
149
+ )
150
+ ```
151
+
152
+ Last-known fallback selects a common observation on or before the requested
153
+ date. Boundary fallback uses the first or last common available observation.
154
+ The supported method is `fallback_on_missing_rate_method="last_known"`;
155
+ linear interpolation is not implemented. All quotes identify actual observations.
156
+
157
+ ### Rate quotes
158
+
159
+ `get_rate()` returns an immutable `RateQuote` containing:
160
+
161
+ | Field | Meaning |
162
+ | --- | --- |
163
+ | `rate` | `Decimal` multiplier: destination units per one source unit |
164
+ | `rate_date` | Actual selected observation date, as `datetime.date` |
165
+ | `snapshot_version` | Snapshot filename date, such as `"2026.11.09"`; `None` for unrecognized custom filenames |
166
+
167
+ ```python
168
+ from decimal import Decimal
169
+
170
+ quote = c.get_rate("USD", "EUR", date="2026-11-02", date_window=30)
171
+ euro_amount = Decimal("100") * quote.rate
172
+ print(quote.rate_date, quote.snapshot_version)
173
+ ```
174
+
175
+ The multiplier is destination-units-per-EUR divided by source-units-per-EUR.
176
+ EUR has an implicit rate of 1. For identical supported currencies, the rate is
177
+ 1 and `rate_date` is the resolved requested date, without requiring a historical
178
+ observation. The snapshot version describes the dataset, not per-observation
179
+ source provenance.
180
+
181
+ ### Supported currencies and date bounds
182
+
183
+ ```python
184
+ print(sorted(c.currencies))
185
+ first_date, last_date = c.bounds["USD"]
186
+ print(first_date, last_date)
187
+ ```
188
+
189
+ `currencies` includes EUR, fiat, crypto, and historical currency symbols present
190
+ in the snapshot. A listed currency may have no rate for a particular date.
191
+ `bounds` includes only currencies with usable observations. EUR bounds cover
192
+ the snapshot calendar, although EUR identity conversions also work outside it.
193
+
194
+ ### Custom data
195
+
196
+ Load an explicit local CSV, using the same units-per-EUR format:
197
+
198
+ ```python
199
+ c = CurrencyConverter("/path/to/snapshot.csv", decimal=True)
200
+ # Also accepted: CurrencyConverter(currency_file="/path/to/snapshot.csv")
201
+ ```
202
+
203
+ By default, the newest filename-dated snapshot is selected across local
204
+ `output/` and the package's bundled data. `output_dir=` restricts selection to
205
+ a chosen directory. URL and ZIP inputs are not supported.
206
+
207
+ ### Exceptions
208
+
209
+ - `UnknownCurrencyError`: a requested symbol is not supported by the snapshot.
210
+ - `RateNotFoundError`: no snapshot or suitable observation is available.
211
+ - `ValueError`: invalid amounts, dates, date windows, or unsupported fallback options.
212
+
213
+ Blank, `ERROR`, zero, negative, and non-finite rates are never used for conversion.
214
+
215
+ ## Command line
216
+
217
+ ```sh
218
+ offline-currency-converter 100 USD --to EUR
219
+ offline-currency-converter 100 USD --to EUR --date 2026-11-02 --date-window 30
220
+ offline-currency-converter currencies
221
+ ```
222
+
223
+ The equivalent module command is `python -m offline_currency_converter` followed
224
+ by the same arguments. Use `--rates-path /path/to/snapshot.csv` for an explicit
225
+ CSV. Commands use the latest common observation when no date is supplied;
226
+ with `--date`, supply `--date-window` if an exact match is not required.
227
+ Amounts are printed without monetary rounding.
228
+
229
+ ## API compatibility
230
+
231
+ This package implements the common CurrencyConverter calling interface with
232
+ offline fiat and crypto data. It is not a complete drop-in implementation:
233
+ network sources, ZIP inputs, and linear interpolation are not supported. Rates
234
+ for cross-currency conversion always come from the same observation date.
235
+ `date_window` and `get_rate()` are additional capabilities.
236
+
237
+ The original `Converter`, module-level `convert()`, and module-level `get_rate()`
238
+ remain available. They use `transaction_date=`, default to yesterday in
239
+ Europe/Amsterdam, interpret explicit `None` as today, and default to a +/-10-day
240
+ window. They always use `Decimal` amounts and rates:
241
+
242
+ ```python
243
+ from offline_currency_converter import get_rate, convert
244
+
245
+ quote = get_rate("USD", "EUR", transaction_date="2026-11-02", date_window=30)
246
+ amount = convert("100", "USD", "EUR", transaction_date="2026-11-02", date_window=30)
247
+ ```
248
+
249
+ ## Development and data maintenance
250
+
251
+ See [Data generation and package maintenance](docs/operations.md) for source
252
+ configuration, daily scheduling, CSV/Excel formats, caching, wheel builds,
253
+ and optional PyPI publication.
254
+
255
+ ```sh
256
+ python -m pip install -r requirements.txt -r requirements-release.txt
257
+ python -m unittest discover -v
258
+ python build_wheel.py
259
+ ```
260
+
261
+ `build_wheel.py` packages the newest existing snapshot without downloading data.
262
+ `python rates.py` performs the network-enabled data update separately.
@@ -0,0 +1,17 @@
1
+ README.md
2
+ pyproject.toml
3
+ OfflineCurrencyConverter.egg-info/PKG-INFO
4
+ OfflineCurrencyConverter.egg-info/SOURCES.txt
5
+ OfflineCurrencyConverter.egg-info/dependency_links.txt
6
+ OfflineCurrencyConverter.egg-info/entry_points.txt
7
+ OfflineCurrencyConverter.egg-info/requires.txt
8
+ OfflineCurrencyConverter.egg-info/top_level.txt
9
+ offline_currency_converter/__init__.py
10
+ offline_currency_converter/__main__.py
11
+ offline_currency_converter/_version.py
12
+ offline_currency_converter/assets.json
13
+ offline_currency_converter/assets.py
14
+ offline_currency_converter/cli.py
15
+ offline_currency_converter/conversion.py
16
+ offline_currency_converter/core.py
17
+ offline_currency_converter/data/eurofxref_and_crypto_rates.2026.11.09.csv
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ offline-currency-converter = offline_currency_converter.cli:main
@@ -0,0 +1,3 @@
1
+ pandas<3.0,>=2.2
2
+ requests<3.0,>=2.32
3
+ openpyxl<4.0,>=3.1
@@ -0,0 +1 @@
1
+ offline_currency_converter
@@ -0,0 +1,262 @@
1
+ Metadata-Version: 2.4
2
+ Name: OfflineCurrencyConverter
3
+ Version: 2026.11.9
4
+ Summary: ECB and cryptocurrency historical rates with EUR cross-conversion
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: pandas<3.0,>=2.2
8
+ Requires-Dist: requests<3.0,>=2.32
9
+ Requires-Dist: openpyxl<4.0,>=3.1
10
+
11
+ # OfflineCurrencyConverter
12
+
13
+ Historical fiat and cryptocurrency conversion for offline Python applications.
14
+ Requires Python 3.10 or newer.
15
+
16
+ OfflineCurrencyConverter follows the familiar
17
+ [CurrencyConverter API](https://github.com/alexprengere/currencyconverter),
18
+ including `CurrencyConverter`, `convert()`, `date=`, currency bounds, and
19
+ optional fallback settings. It extends this interface with cryptocurrency
20
+ coverage, configurable date windows, and exchange-rate quotes with snapshot
21
+ metadata. See [API compatibility](#api-compatibility) for supported behavior.
22
+
23
+ **Exchange-rate data is included in every package.** Automated daily builds
24
+ refresh the bundled historical dataset and produce a date-versioned wheel.
25
+ Applications receive updated rates by installing a newer package build;
26
+ conversion and rate lookup never require access to external data services.
27
+ This supports air-gapped deployments using an internal Python package index
28
+ or a local wheelhouse.
29
+
30
+ ## Currency data
31
+
32
+ Fiat reference rates come from the European Central Bank. Cryptocurrency and
33
+ stablecoin rates come from CoinMarketCap. All source rates are stored as units
34
+ per EUR, enabling conversion between supported currencies through EUR.
35
+
36
+ Each wheel contains one cumulative CSV snapshot. Its calendar starts on
37
+ 1999-01-04 and ends on the day before the snapshot's run date. Individual
38
+ currencies have different periods of coverage. Crypto uses completed daily
39
+ closing prices; unfinished current-day observations are excluded.
40
+
41
+ A scheduled GitHub Action generates updated data and a wheel daily at
42
+ 16:30 Europe/Amsterdam. Successful builds are committed to [`output/`](output/)
43
+ and made available as workflow artifacts. Versions follow the snapshot date:
44
+ `2026.11.9` identifies the November 9, 2026 snapshot. Publication to public PyPI
45
+ is a separate, manually triggered workflow.
46
+
47
+ Fiat observations may be absent on weekends and holidays. Missing or failed
48
+ source observations remain unavailable until a subsequent update obtains them.
49
+ The installed dataset stays fixed until the package or local snapshot is
50
+ replaced; the converter does not refresh it automatically.
51
+
52
+ ## Installation
53
+
54
+ Download a wheel from [`output/`](output/), then install it in a virtual environment:
55
+
56
+ ```sh
57
+ python3 -m venv .venv
58
+ source .venv/bin/activate
59
+ python -m pip install /path/to/offlinecurrencyconverter-2026.11.9-py3-none-any.whl
60
+ ```
61
+
62
+ For air-gapped systems, make the wheel and its dependencies available through
63
+ an internal PyPI mirror or a local wheelhouse. For example:
64
+
65
+ ```sh
66
+ python -m pip install --no-index --find-links=/path/to/wheelhouse OfflineCurrencyConverter
67
+ ```
68
+
69
+ The wheel bundles the rates, but its dependencies must also be installed.
70
+ To replace an installed build with an updated wheel of the same version, use
71
+ `python -m pip install --force-reinstall /path/to/package.whl`.
72
+
73
+ ## Python API
74
+
75
+ Create a converter once and reuse it:
76
+
77
+ ```python
78
+ from offline_currency_converter import CurrencyConverter
79
+
80
+ c = CurrencyConverter()
81
+ euros = c.convert(100, "USD") # EUR is the default destination.
82
+ yen = c.convert(100, "USD", "JPY") # Latest common observation.
83
+ ```
84
+
85
+ Choose a historical date with `date=`:
86
+
87
+ ```python
88
+ from datetime import date
89
+
90
+ amount = c.convert(100, "USD", "EUR", date=date(2026, 11, 2))
91
+ ```
92
+
93
+ Omitting `date`, or passing `None`, uses the latest common observation in the
94
+ loaded snapshot. An explicit date requires an exact observation unless a date
95
+ window or fallback is enabled. Dates also accept `datetime` objects,
96
+ `YYYY-MM-DD` strings, and `D.M.YYYY` strings.
97
+
98
+ ### Decimal amounts and rounding
99
+
100
+ Conversions return floats by default. Enable `decimal=True` for `Decimal`
101
+ results, and pass string or `Decimal` amounts to preserve input precision:
102
+
103
+ ```python
104
+ from decimal import Decimal, ROUND_HALF_UP
105
+ from offline_currency_converter import CurrencyConverter
106
+
107
+ c = CurrencyConverter(decimal=True)
108
+ eur = c.convert("100", "USD", date="2026-11-02")
109
+ eur = eur.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
110
+ ```
111
+
112
+ The converter does not apply monetary rounding. Round the final amount to the
113
+ destination currency's minor unit: EUR and USD use two decimal places, while
114
+ some currencies use zero or three. Formatting with `f"{eur:.2f}"` changes only
115
+ the displayed text; `quantize()` returns a rounded `Decimal` value.
116
+
117
+ ### Date windows
118
+
119
+ Pass `date_window` to select the nearest usable observation within an inclusive
120
+ window of calendar days before or after the requested date:
121
+
122
+ ```python
123
+ amount = c.convert("100", "USD", "EUR", date="2026-11-02", date_window=30)
124
+ ```
125
+
126
+ An equally distant pair of observations resolves to the earlier date. Both
127
+ currencies must have valid rates on the same observation date. Zero requires
128
+ an exact match. An explicit window overrides fallback settings, including
129
+ out-of-range fallback, and raises `RateNotFoundError` if no match exists.
130
+
131
+ Applications requiring a 30-day policy must explicitly pass their selected
132
+ `date` and `date_window=30`.
133
+
134
+ ### Fallbacks
135
+
136
+ Enable last-known fallback for missing dates within the currencies' date bounds:
137
+
138
+ ```python
139
+ c = CurrencyConverter(fallback_on_missing_rate=True, decimal=True)
140
+ ```
141
+
142
+ Enable boundary fallback for requested dates outside the available range:
143
+
144
+ ```python
145
+ c = CurrencyConverter(
146
+ fallback_on_missing_rate=True,
147
+ fallback_on_wrong_date=True,
148
+ decimal=True,
149
+ )
150
+ ```
151
+
152
+ Last-known fallback selects a common observation on or before the requested
153
+ date. Boundary fallback uses the first or last common available observation.
154
+ The supported method is `fallback_on_missing_rate_method="last_known"`;
155
+ linear interpolation is not implemented. All quotes identify actual observations.
156
+
157
+ ### Rate quotes
158
+
159
+ `get_rate()` returns an immutable `RateQuote` containing:
160
+
161
+ | Field | Meaning |
162
+ | --- | --- |
163
+ | `rate` | `Decimal` multiplier: destination units per one source unit |
164
+ | `rate_date` | Actual selected observation date, as `datetime.date` |
165
+ | `snapshot_version` | Snapshot filename date, such as `"2026.11.09"`; `None` for unrecognized custom filenames |
166
+
167
+ ```python
168
+ from decimal import Decimal
169
+
170
+ quote = c.get_rate("USD", "EUR", date="2026-11-02", date_window=30)
171
+ euro_amount = Decimal("100") * quote.rate
172
+ print(quote.rate_date, quote.snapshot_version)
173
+ ```
174
+
175
+ The multiplier is destination-units-per-EUR divided by source-units-per-EUR.
176
+ EUR has an implicit rate of 1. For identical supported currencies, the rate is
177
+ 1 and `rate_date` is the resolved requested date, without requiring a historical
178
+ observation. The snapshot version describes the dataset, not per-observation
179
+ source provenance.
180
+
181
+ ### Supported currencies and date bounds
182
+
183
+ ```python
184
+ print(sorted(c.currencies))
185
+ first_date, last_date = c.bounds["USD"]
186
+ print(first_date, last_date)
187
+ ```
188
+
189
+ `currencies` includes EUR, fiat, crypto, and historical currency symbols present
190
+ in the snapshot. A listed currency may have no rate for a particular date.
191
+ `bounds` includes only currencies with usable observations. EUR bounds cover
192
+ the snapshot calendar, although EUR identity conversions also work outside it.
193
+
194
+ ### Custom data
195
+
196
+ Load an explicit local CSV, using the same units-per-EUR format:
197
+
198
+ ```python
199
+ c = CurrencyConverter("/path/to/snapshot.csv", decimal=True)
200
+ # Also accepted: CurrencyConverter(currency_file="/path/to/snapshot.csv")
201
+ ```
202
+
203
+ By default, the newest filename-dated snapshot is selected across local
204
+ `output/` and the package's bundled data. `output_dir=` restricts selection to
205
+ a chosen directory. URL and ZIP inputs are not supported.
206
+
207
+ ### Exceptions
208
+
209
+ - `UnknownCurrencyError`: a requested symbol is not supported by the snapshot.
210
+ - `RateNotFoundError`: no snapshot or suitable observation is available.
211
+ - `ValueError`: invalid amounts, dates, date windows, or unsupported fallback options.
212
+
213
+ Blank, `ERROR`, zero, negative, and non-finite rates are never used for conversion.
214
+
215
+ ## Command line
216
+
217
+ ```sh
218
+ offline-currency-converter 100 USD --to EUR
219
+ offline-currency-converter 100 USD --to EUR --date 2026-11-02 --date-window 30
220
+ offline-currency-converter currencies
221
+ ```
222
+
223
+ The equivalent module command is `python -m offline_currency_converter` followed
224
+ by the same arguments. Use `--rates-path /path/to/snapshot.csv` for an explicit
225
+ CSV. Commands use the latest common observation when no date is supplied;
226
+ with `--date`, supply `--date-window` if an exact match is not required.
227
+ Amounts are printed without monetary rounding.
228
+
229
+ ## API compatibility
230
+
231
+ This package implements the common CurrencyConverter calling interface with
232
+ offline fiat and crypto data. It is not a complete drop-in implementation:
233
+ network sources, ZIP inputs, and linear interpolation are not supported. Rates
234
+ for cross-currency conversion always come from the same observation date.
235
+ `date_window` and `get_rate()` are additional capabilities.
236
+
237
+ The original `Converter`, module-level `convert()`, and module-level `get_rate()`
238
+ remain available. They use `transaction_date=`, default to yesterday in
239
+ Europe/Amsterdam, interpret explicit `None` as today, and default to a +/-10-day
240
+ window. They always use `Decimal` amounts and rates:
241
+
242
+ ```python
243
+ from offline_currency_converter import get_rate, convert
244
+
245
+ quote = get_rate("USD", "EUR", transaction_date="2026-11-02", date_window=30)
246
+ amount = convert("100", "USD", "EUR", transaction_date="2026-11-02", date_window=30)
247
+ ```
248
+
249
+ ## Development and data maintenance
250
+
251
+ See [Data generation and package maintenance](docs/operations.md) for source
252
+ configuration, daily scheduling, CSV/Excel formats, caching, wheel builds,
253
+ and optional PyPI publication.
254
+
255
+ ```sh
256
+ python -m pip install -r requirements.txt -r requirements-release.txt
257
+ python -m unittest discover -v
258
+ python build_wheel.py
259
+ ```
260
+
261
+ `build_wheel.py` packages the newest existing snapshot without downloading data.
262
+ `python rates.py` performs the network-enabled data update separately.