@@ -50,7 +50,7 @@ def _write_cached_structure(cache_path: Path, struct: dict[str, Any]) -> None:
5050
5151
5252def exchange_rates (
53- ref_areas : str | Sequence [str ],
53+ ref_areas : str | Sequence [str ] | None = None ,
5454 * ,
5555 base : str ,
5656 quote : str ,
@@ -72,11 +72,15 @@ def exchange_rates(
7272 debug : bool = False ,
7373) -> pl .DataFrame | tuple [pl .DataFrame , dict ]:
7474 """
75- Fetch ER series and return tidy Polars DataFrame (raw IMF dimensions + TIME_PERIOD + OBS_VALUE) .
75+ Fetch ER series and return a tidy Polars DataFrame.
7676
77- indicator is formed as '{base}_{quote}' (e.g., XDC_USD, USD_XDC, XDC_EUR, EUR_XDC, XDC_XDR, XDR_XDC).
77+ indicator is formed as '{base}_{quote}' (e.g. XDC_USD, USD_XDC, XDC_EUR,
78+ EUR_XDC, XDC_XDR, XDR_XDC).
7879
7980 If normalize=True, returns a standardized schema via transform.normalize_fx_rates().
81+
82+ If ref_areas is None, all valid IMF ER country codes from CL_ER_COUNTRY_PUB
83+ are requested.
8084 """
8185 tr_map = {"average" : "PA_RT" , "eop" : "EOP_RT" }
8286 freq_map = {"annual" : "A" , "monthly" : "M" , "quarterly" : "Q" }
@@ -93,7 +97,7 @@ def exchange_rates(
9397 quote = str (quote ).strip ().upper ()
9498 indicator = f"{ base } _{ quote } "
9599
96- # ---- structure (cached) ----
100+ # structure (cached)
97101 struct : dict [str , Any ] | None = None
98102 cache_hit = False
99103 if cache_structure :
@@ -120,14 +124,19 @@ def exchange_rates(
120124 f"Examples: { ', ' .join (examples ) if examples else 'see CL_ER_INDICATOR_PUB' } "
121125 )
122126
123- # Optional area labels for joins later
124- area_lu = None
125- if include_country_labels :
126- # CL_ER_COUNTRY_PUB is what you found as "COUNTRY/AREA-like"
127- area_lu = codelist_to_df (struct , "CL_ER_COUNTRY_PUB" )
127+ area_lu = codelist_to_df (struct , "CL_ER_COUNTRY_PUB" )
128128
129- # ---- normalize ref_areas ----
130- if isinstance (ref_areas , str ):
129+ # Optional area labels for joins later
130+ label_lu = area_lu if include_country_labels else None
131+
132+ # normalize ref_areas
133+ if ref_areas is None :
134+ refs = [
135+ c .strip ().upper ()
136+ for c in area_lu ["code" ].to_list ()
137+ if isinstance (c , str ) and _is_iso3_like (c .strip ().upper ())
138+ ]
139+ elif isinstance (ref_areas , str ):
131140 refs = [ref_areas ]
132141 else :
133142 refs = list (ref_areas )
@@ -149,10 +158,11 @@ def exchange_rates(
149158 "missing_ref_areas" : [],
150159 "structure_cache_hit" : cache_hit ,
151160 "rows_raw_total" : 0 ,
161+ "rows_out" : 0 ,
152162 }
153163 return (empty , meta ) if return_meta else empty
154164
155- # ---- batching + resilience ----
165+ # batching + resilience
156166 batches = _chunked (refs , batch_size )
157167 dfs : list [pl .DataFrame ] = []
158168 errors = 0
@@ -211,6 +221,7 @@ def _fetch_batch(batch: list[str], splits_left: int) -> tuple[pl.DataFrame, int]
211221 "errors" : errors ,
212222 "structure_cache_hit" : cache_hit ,
213223 "rows_raw_total" : 0 ,
224+ "rows_out" : 0 ,
214225 }
215226 return (empty , meta ) if return_meta else empty
216227
@@ -222,12 +233,12 @@ def _fetch_batch(batch: list[str], splits_left: int) -> tuple[pl.DataFrame, int]
222233 df_out = normalize_fx_rates (
223234 df_raw ,
224235 indicator = indicator ,
225- area_lu = area_lu ,
236+ area_lu = label_lu ,
226237 include_country_name = include_country_labels ,
227238 )
228239
229240 # meta
230- returned = []
241+ returned : list [ str ] = []
231242 for col in ["COUNTRY" , "REF_AREA" ]:
232243 if col in df_raw .columns :
233244 returned = sorted (df_raw .select (pl .col (col ).unique ()).to_series ().to_list ())
@@ -251,20 +262,23 @@ def _fetch_batch(batch: list[str], splits_left: int) -> tuple[pl.DataFrame, int]
251262 return (df_out , meta ) if return_meta else df_out
252263
253264
254- # ----------------------------
255265# Consistent wrappers (A/Q/M averages)
256- # ----------------------------
257266
258267
259- def monthly_usd_avg (ref_areas : str | Sequence [str ], ** kwargs ):
268+ def monthly_usd_avg (ref_areas : str | Sequence [str ] | None = None , ** kwargs ):
260269 return exchange_rates (
261- ref_areas , base = "XDC" , quote = "USD" , frequency = "monthly" , transformation = "average" , ** kwargs
270+ ref_areas = ref_areas ,
271+ base = "XDC" ,
272+ quote = "USD" ,
273+ frequency = "monthly" ,
274+ transformation = "average" ,
275+ ** kwargs ,
262276 )
263277
264278
265- def quarterly_usd_avg (ref_areas : str | Sequence [str ], ** kwargs ):
279+ def quarterly_usd_avg (ref_areas : str | Sequence [str ] | None = None , ** kwargs ):
266280 return exchange_rates (
267- ref_areas ,
281+ ref_areas = ref_areas ,
268282 base = "XDC" ,
269283 quote = "USD" ,
270284 frequency = "quarterly" ,
@@ -273,21 +287,31 @@ def quarterly_usd_avg(ref_areas: str | Sequence[str], **kwargs):
273287 )
274288
275289
276- def annual_usd_avg (ref_areas : str | Sequence [str ], ** kwargs ):
290+ def annual_usd_avg (ref_areas : str | Sequence [str ] | None = None , ** kwargs ):
277291 return exchange_rates (
278- ref_areas , base = "XDC" , quote = "USD" , frequency = "annual" , transformation = "average" , ** kwargs
292+ ref_areas = ref_areas ,
293+ base = "XDC" ,
294+ quote = "USD" ,
295+ frequency = "annual" ,
296+ transformation = "average" ,
297+ ** kwargs ,
279298 )
280299
281300
282- def monthly_eur_avg (ref_areas : str | Sequence [str ], ** kwargs ):
301+ def monthly_eur_avg (ref_areas : str | Sequence [str ] | None = None , ** kwargs ):
283302 return exchange_rates (
284- ref_areas , base = "XDC" , quote = "EUR" , frequency = "monthly" , transformation = "average" , ** kwargs
303+ ref_areas = ref_areas ,
304+ base = "XDC" ,
305+ quote = "EUR" ,
306+ frequency = "monthly" ,
307+ transformation = "average" ,
308+ ** kwargs ,
285309 )
286310
287311
288- def quarterly_eur_avg (ref_areas : str | Sequence [str ], ** kwargs ):
312+ def quarterly_eur_avg (ref_areas : str | Sequence [str ] | None = None , ** kwargs ):
289313 return exchange_rates (
290- ref_areas ,
314+ ref_areas = ref_areas ,
291315 base = "XDC" ,
292316 quote = "EUR" ,
293317 frequency = "quarterly" ,
@@ -296,21 +320,31 @@ def quarterly_eur_avg(ref_areas: str | Sequence[str], **kwargs):
296320 )
297321
298322
299- def annual_eur_avg (ref_areas : str | Sequence [str ], ** kwargs ):
323+ def annual_eur_avg (ref_areas : str | Sequence [str ] | None = None , ** kwargs ):
300324 return exchange_rates (
301- ref_areas , base = "XDC" , quote = "EUR" , frequency = "annual" , transformation = "average" , ** kwargs
325+ ref_areas = ref_areas ,
326+ base = "XDC" ,
327+ quote = "EUR" ,
328+ frequency = "annual" ,
329+ transformation = "average" ,
330+ ** kwargs ,
302331 )
303332
304333
305- def monthly_xdr_avg (ref_areas : str | Sequence [str ], ** kwargs ):
334+ def monthly_xdr_avg (ref_areas : str | Sequence [str ] | None = None , ** kwargs ):
306335 return exchange_rates (
307- ref_areas , base = "XDC" , quote = "XDR" , frequency = "monthly" , transformation = "average" , ** kwargs
336+ ref_areas = ref_areas ,
337+ base = "XDC" ,
338+ quote = "XDR" ,
339+ frequency = "monthly" ,
340+ transformation = "average" ,
341+ ** kwargs ,
308342 )
309343
310344
311- def quarterly_xdr_avg (ref_areas : str | Sequence [str ], ** kwargs ):
345+ def quarterly_xdr_avg (ref_areas : str | Sequence [str ] | None = None , ** kwargs ):
312346 return exchange_rates (
313- ref_areas ,
347+ ref_areas = ref_areas ,
314348 base = "XDC" ,
315349 quote = "XDR" ,
316350 frequency = "quarterly" ,
@@ -319,13 +353,16 @@ def quarterly_xdr_avg(ref_areas: str | Sequence[str], **kwargs):
319353 )
320354
321355
322- def annual_xdr_avg (ref_areas : str | Sequence [str ], ** kwargs ):
356+ def annual_xdr_avg (ref_areas : str | Sequence [str ] | None = None , ** kwargs ):
323357 return exchange_rates (
324- ref_areas , base = "XDC" , quote = "XDR" , frequency = "annual" , transformation = "average" , ** kwargs
358+ ref_areas = ref_areas ,
359+ base = "XDC" ,
360+ quote = "XDR" ,
361+ frequency = "annual" ,
362+ transformation = "average" ,
363+ ** kwargs ,
325364 )
326365
327366
328- # Back-compat name you want:
329- def monthly_usd_only (ref_areas : str | Sequence [str ], ** kwargs ):
330- # wrapper only; no special fetch logic
331- return monthly_usd_avg (ref_areas , ** kwargs )
367+ def monthly_usd_only (ref_areas : str | Sequence [str ] | None = None , ** kwargs ):
368+ return monthly_usd_avg (ref_areas = ref_areas , ** kwargs )
0 commit comments