recall_guard.portfolio.prices
recall_guard.portfolio.prices
FMP-backed EOD price fetcher for the cmmd-backtest universe.
Pulls end-of-day close prices for SWDA.L, XLK, IAU, and BIL from FMP's
historical-price-eod/light endpoint, aligns them with an inner-join
on date, and returns a single pandas.DataFrame with one column per
ticker. Covers Reqs 5.1, 5.7, 7.2, and 9.2.
Public surface:
PriceFetchErroris raised on HTTP failure or when the inner-join has fewer than 30 aligned trading days.fetch_universe_pricesis the only entry point.
This is the only place in the portfolio layer that performs HTTP
I/O; tests mock requests.get. The retry / API-key resolution
pattern is the same one used by recall_guard.dataset.fmp_corpora.fetch_articles,
duplicated locally because the sentrux portfolio ↔ dataset boundary
forbids the import.
PriceFetchError
Bases: RuntimeError
Raised when an FMP price fetch fails or returns insufficient data.
Carries either the offending ticker plus HTTP status code (transport failure) or the offending ticker plus aligned-day count (overlap failure). The orchestrator script presents this directly to stderr.
Source code in recall_guard/portfolio/prices.py
35 36 37 38 39 40 41 | |
fetch_universe_prices
fetch_universe_prices(tickers, start, end, api_key=None)
Return aligned (date × ticker) close-price matrix for the universe.
Fetches each ticker's EOD close series from FMP's
historical-price-eod/light endpoint, filters each series to the
[start, end] window, and inner-joins on date so any day where any
ticker is missing (e.g. LSE holiday vs NYSE) is dropped uniformly.
Args:
tickers: Ordered list of FMP symbols. Output column order
matches this list.
start: Inclusive lower bound for retained dates.
end: Inclusive upper bound for retained dates.
api_key: Explicit FMP key; falls back to the FMP_API_KEY
environment variable.
Returns:
A DataFrame with a monotonic DatetimeIndex, one column
per ticker (in input order), and no NaN cells.
Raises:
RuntimeError: FMP_API_KEY is not set and no api_key was
passed.
PriceFetchError: any individual ticker request fails, or the
inner-joined frame has fewer than 30 aligned trading days.
ValueError: tickers is empty or start > end.
Source code in recall_guard/portfolio/prices.py
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | |