3.2 KiB
3.2 KiB
EIA Brent Price Source — Primary with FRED Fallback
Date: 2026-04-14
Status: Approved
Problem
FRED (DCOILBRENTEU) publishes Brent crude prices with a lag on top of the EIA's own publication delay. As of 2026-04-14, FRED's most recent data is 2026-04-02 — 12 days stale. EIA is the original data source; FRED mirrors it. Using EIA directly removes the mirroring lag.
Goal
Replace FRED as the primary source for fetchBrentPrices() with the EIA Open Data API, keeping FRED as a fallback so the system degrades gracefully if EIA is unavailable.
Architecture
All changes are confined to app/Services/OilPriceService.php and supporting config/env files.
fetchBrentPrices() — updated flow
1. $rows = fetchFromEia()
2. if ($rows === null):
Log::warning('OilPriceService: EIA fetch failed, falling back to FRED')
$rows = fetchFromFred()
3. if ($rows === null):
Log::error('OilPriceService: both EIA and FRED fetch failed')
return
4. BrentPrice::upsert($rows, ['date'], ['price_usd'])
fetchFromEia(): ?array (new private method)
- Endpoint:
GET https://api.eia.gov/v2/petroleum/pri/spt/data/ - Params:
api_key,frequency=daily,data[0]=value,facets[series][]=RBRTE,sort[0][column]=period,sort[0][direction]=desc,length=30 - Returns
nullon: HTTP error, exception, empty/missingresponse.dataarray - Maps each row:
['date' => $row['period'], 'price_usd' => (float) $row['value']] - Filters rows where
valueis'.'(EIA uses same placeholder as FRED)
fetchFromFred(): ?array (extracted private method)
- Existing FRED logic moved verbatim from the current
fetchBrentPrices()body - Returns
nullon: HTTP error, exception, empty observations - Maps each row:
['date' => $obs['date'], 'price_usd' => (float) $obs['value']]
Configuration
config/services.php
Add under existing entries:
'eia' => [
'api_key' => env('EIA_API_KEY'),
],
.env.example
Add:
EIA_API_KEY= # US EIA Open Data API key — register free at eia.gov/opendata
FRED key stays in .env.example (still used as fallback).
Logging
| Event | Level | Message |
|---|---|---|
| EIA fetch succeeded | — | (no log — normal path) |
| EIA fetch failed, FRED used | warning |
OilPriceService: EIA fetch failed, falling back to FRED |
| Both failed | error |
OilPriceService: both EIA and FRED fetch failed |
| No rows after filter | warning |
OilPriceService: no valid EIA observations returned (existing pattern) |
Tests
Existing OilPriceService fetch tests are updated (not replaced):
- EIA succeeds —
Http::fake()returns valid EIA response; assert upsert called with correct rows; assert FRED endpoint never called. - EIA fails, FRED succeeds — EIA returns 500; assert FRED endpoint called; assert upsert called with FRED rows; assert warning logged.
- Both fail — both return 500; assert upsert never called; assert error logged.
- EIA returns empty data —
response.datais[]; assert falls back to FRED.
Out of Scope
- No changes to
generatePrediction(),PredictOilPricescommand, or scheduler - No new service classes or interfaces
- No changes to
brent_pricesschema