mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-27 04:31:13 +00:00
[Feature] Add FRED Release Calendar to economy.calendar Endpoint (#7366)
* add fred release calendar to economy.calendar endpoint * fix deribit test fixture
This commit is contained in:
@@ -52,6 +52,14 @@ def headers():
|
||||
"end_date": "2023-11-03",
|
||||
}
|
||||
),
|
||||
(
|
||||
{
|
||||
"provider": "fred",
|
||||
"start_date": "2025-07-01",
|
||||
"end_date": "2025-07-02",
|
||||
"release_id": None,
|
||||
}
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.integration
|
||||
|
||||
@@ -46,6 +46,14 @@ def obb(pytestconfig): # pylint: disable=inconsistent-return-statements
|
||||
"end_date": "2023-11-03",
|
||||
}
|
||||
),
|
||||
(
|
||||
{
|
||||
"provider": "fred",
|
||||
"start_date": "2025-07-01",
|
||||
"end_date": "2025-07-02",
|
||||
"release_id": None,
|
||||
}
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.integration
|
||||
|
||||
@@ -14,6 +14,7 @@ from openbb_deribit.models.options_chains import (
|
||||
DeribitOptionsChainsData,
|
||||
DeribitOptionsChainsFetcher,
|
||||
)
|
||||
from openbb_deribit.utils.helpers import get_instruments
|
||||
|
||||
test_credentials = UserService().default_user_settings.credentials.model_dump(
|
||||
mode="json"
|
||||
@@ -62,6 +63,22 @@ MOCK_OPTIONS_DATA = DeribitOptionsChainsData.model_validate(
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _disable_alru_cache_loop_check(monkeypatch):
|
||||
"""Disable the alru_cache event loop safety check during tests.
|
||||
|
||||
async-lru >= 2.2.0 tracks the event loop and raises RuntimeError
|
||||
when the cache is reused across different event loops. During tests,
|
||||
multiple event loops are created within a single test (e.g. validators
|
||||
calling run_async inside transform_query, then fetcher.test calling
|
||||
run_async for extract_data), so we patch out the loop check entirely.
|
||||
"""
|
||||
from async_lru import _LRUCacheWrapper
|
||||
|
||||
monkeypatch.setattr(_LRUCacheWrapper, "_check_loop", lambda self, loop: None)
|
||||
get_instruments.cache_clear()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vcr_config():
|
||||
"""VCR configuration."""
|
||||
|
||||
@@ -11,6 +11,7 @@ from openbb_fred.models.dwpcr_rates import FREDDiscountWindowPrimaryCreditRateFe
|
||||
from openbb_fred.models.ecb_interest_rates import (
|
||||
FREDEuropeanCentralBankInterestRatesFetcher,
|
||||
)
|
||||
from openbb_fred.models.economic_calendar import FredEconomicCalendarFetcher
|
||||
from openbb_fred.models.euro_short_term_rate import FredEuroShortTermRateFetcher
|
||||
from openbb_fred.models.fed_projections import FREDPROJECTIONFetcher
|
||||
from openbb_fred.models.federal_funds_rate import FredFederalFundsRateFetcher
|
||||
@@ -75,6 +76,7 @@ Research division of the Federal Reserve Bank of St. Louis that has more than
|
||||
"PROJECTIONS": FREDPROJECTIONFetcher,
|
||||
"IORB": FREDIORBFetcher,
|
||||
"DiscountWindowPrimaryCreditRate": FREDDiscountWindowPrimaryCreditRateFetcher,
|
||||
"EconomicCalendar": FredEconomicCalendarFetcher,
|
||||
"EuropeanCentralBankInterestRates": FREDEuropeanCentralBankInterestRatesFetcher,
|
||||
"ManufacturingOutlookNY": FredManufacturingOutlookNYFetcher,
|
||||
"ManufacturingOutlookTexas": FredManufacturingOutlookTexasFetcher,
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
"""FRED Economic Calendar Model."""
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
from typing import Any
|
||||
|
||||
from openbb_core.app.model.abstract.error import OpenBBError
|
||||
from openbb_core.provider.abstract.fetcher import Fetcher
|
||||
from openbb_core.provider.standard_models.economic_calendar import (
|
||||
EconomicCalendarData,
|
||||
EconomicCalendarQueryParams,
|
||||
)
|
||||
from pydantic import Field
|
||||
|
||||
|
||||
class FredEconomicCalendarQueryParams(EconomicCalendarQueryParams):
|
||||
"""FRED Economic Calendar Query.
|
||||
|
||||
Source: https://fred.stlouisfed.org/releases/calendar
|
||||
"""
|
||||
|
||||
release_id: int | None = Field(default=None, description="Filter by release ID.")
|
||||
|
||||
|
||||
class FredEconomicCalendarData(EconomicCalendarData):
|
||||
"""FRED Economic Calendar Data."""
|
||||
|
||||
release_id: int | None = Field(
|
||||
default=None, description="Release ID associated with the economic event."
|
||||
)
|
||||
|
||||
|
||||
class FredEconomicCalendarFetcher(
|
||||
Fetcher[FredEconomicCalendarQueryParams, list[FredEconomicCalendarData]]
|
||||
):
|
||||
"""FRED Economic Calendar Fetcher."""
|
||||
|
||||
require_credentials = False
|
||||
|
||||
@staticmethod
|
||||
def transform_query(params: dict[str, Any]) -> FredEconomicCalendarQueryParams:
|
||||
"""Transform query parameters."""
|
||||
return FredEconomicCalendarQueryParams(**params)
|
||||
|
||||
@staticmethod
|
||||
async def aextract_data(
|
||||
query: FredEconomicCalendarQueryParams,
|
||||
credentials: dict[str, Any] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> list:
|
||||
"""Extract data from the source."""
|
||||
# pylint: disable=import-outside-toplevel
|
||||
import asyncio
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from io import StringIO
|
||||
from math import ceil
|
||||
|
||||
from openbb_core.provider.utils.helpers import amake_request
|
||||
from pandas import (
|
||||
DataFrame,
|
||||
concat,
|
||||
date_range as pd_date_range,
|
||||
read_html,
|
||||
)
|
||||
|
||||
def process_pager(pager: str, date: str = "") -> DataFrame:
|
||||
"""Process a page of events HTML into a DataFrame."""
|
||||
buf = StringIO(pager)
|
||||
df = read_html(buf, extract_links="all")[0]
|
||||
df.columns = ["date", "event"]
|
||||
df["date"] = df.date.apply(lambda x: x[0].replace("N/A", "") or None)
|
||||
|
||||
# Extract release_id from link; rows without a link are date-header rows.
|
||||
df["release_id"] = df.event.apply(
|
||||
lambda x: x[1].split("rid=")[-1] if x[1] else None
|
||||
)
|
||||
df["event"] = df.event.apply(lambda x: x[0])
|
||||
|
||||
is_header = df["release_id"].isna()
|
||||
|
||||
if is_header.any():
|
||||
# Header rows carry full date strings like
|
||||
# "Wednesday February 05, 2025 Updated".
|
||||
# Event rows carry times like "7:30 AM ET".
|
||||
# Clean header dates.
|
||||
df.loc[is_header, "date"] = (
|
||||
df.loc[is_header, "date"]
|
||||
.str.replace(r"\s*(Updated|Revised)\s*$", "", regex=True)
|
||||
.str.strip()
|
||||
)
|
||||
# Forward-fill header dates so each event row knows its date.
|
||||
df["_event_date"] = (
|
||||
df["date"]
|
||||
.where(is_header)
|
||||
.ffill()
|
||||
.astype("datetime64[ns]")
|
||||
.dt.strftime("%Y-%m-%d")
|
||||
)
|
||||
# Forward-fill times among event rows (handles N/A time rows).
|
||||
df["_time"] = df["date"].where(~is_header).ffill().fillna("")
|
||||
|
||||
# Build full datetime: "YYYY-MM-DD HH:MM AM ET"
|
||||
df.loc[~is_header, "date"] = (
|
||||
df.loc[~is_header, "_event_date"]
|
||||
+ " "
|
||||
+ df.loc[~is_header, "_time"]
|
||||
).str.strip()
|
||||
|
||||
# Drop header rows and temp columns.
|
||||
df = (
|
||||
df[~is_header]
|
||||
.drop(columns=["_event_date", "_time"])
|
||||
.reset_index(drop=True)
|
||||
)
|
||||
else:
|
||||
# Fallback: all rows are events with time values (no headers present).
|
||||
df["date"] = df["date"].ffill()
|
||||
if date:
|
||||
df["date"] = df["date"].apply(lambda x: f"{date} {x}")
|
||||
|
||||
df["date"] = (
|
||||
df["date"].astype("datetime64[ns]").dt.tz_localize("US/Central")
|
||||
)
|
||||
|
||||
return df
|
||||
|
||||
async def response_callback(response, _):
|
||||
"""Process the response text."""
|
||||
text = await response.text()
|
||||
return json.loads(text)
|
||||
|
||||
async def fetch_date_pages(
|
||||
start_date: str,
|
||||
end_date: str = "",
|
||||
rid: str = "",
|
||||
) -> DataFrame:
|
||||
"""Fetch all pages of the FRED releases calendar for a single date or date range."""
|
||||
end_date = end_date or start_date
|
||||
base_url = (
|
||||
"https://fred.stlouisfed.org/releases/calendar"
|
||||
f"?po=1&ptic=0&vs={start_date}&ve={end_date}&rid={rid}"
|
||||
)
|
||||
first_page = await amake_request(
|
||||
base_url, response_callback=response_callback
|
||||
)
|
||||
n_results = first_page.get("ptic", 0) # type: ignore
|
||||
|
||||
if n_results == 0:
|
||||
return DataFrame()
|
||||
|
||||
records = [process_pager(first_page["pager"], date=start_date)] # type: ignore
|
||||
|
||||
if len(records[0]) >= n_results:
|
||||
return records[0]
|
||||
|
||||
# Build URLs for remaining pages (50 results per page).
|
||||
total_pages = ceil(n_results / 50)
|
||||
remaining_urls = [
|
||||
f"{base_url}&pageID={page}" for page in range(2, total_pages + 1)
|
||||
]
|
||||
|
||||
async def _fetch_page(url: str):
|
||||
return await amake_request(url, response_callback=response_callback)
|
||||
|
||||
responses = await asyncio.gather(*[_fetch_page(u) for u in remaining_urls])
|
||||
|
||||
for resp in responses:
|
||||
records.append(process_pager(resp["pager"], date=start_date)) # type: ignore
|
||||
|
||||
result = concat(records, ignore_index=True)
|
||||
|
||||
return result
|
||||
|
||||
async def fetch_all_pages(
|
||||
start_date: str,
|
||||
end_date: str = "",
|
||||
rid: str = "",
|
||||
) -> DataFrame:
|
||||
"""Fetch all pages of the FRED releases calendar for each date in a range.
|
||||
|
||||
If a release ID is supplied, the entire date range is fetched in a single
|
||||
paginated call. Otherwise, each date is fetched individually to ensure
|
||||
complete results.
|
||||
"""
|
||||
end_date = end_date or start_date
|
||||
|
||||
if rid:
|
||||
# A specific release ID can be fetched across the full range at once.
|
||||
return await fetch_date_pages(start_date, end_date, rid)
|
||||
|
||||
# Without a release ID, fetch each date individually.
|
||||
dates = [
|
||||
d.strftime("%Y-%m-%d") for d in pd_date_range(start_date, end_date)
|
||||
]
|
||||
|
||||
results = await asyncio.gather(*[fetch_date_pages(d) for d in dates])
|
||||
|
||||
frames = [r for r in results if not r.empty]
|
||||
|
||||
if not frames:
|
||||
return DataFrame()
|
||||
|
||||
return concat(frames, ignore_index=True).sort_values(by="date")
|
||||
|
||||
start_date = (
|
||||
query.start_date.isoformat()
|
||||
if query.start_date
|
||||
else (datetime.today() - timedelta(days=1)).isoformat()
|
||||
)
|
||||
end_date = (
|
||||
query.end_date.isoformat()
|
||||
if query.end_date
|
||||
else (
|
||||
(query.start_date + timedelta(days=7)).isoformat()
|
||||
if query.start_date
|
||||
else (datetime.today() + timedelta(days=1)).isoformat()
|
||||
)
|
||||
)
|
||||
rid = str(query.release_id) if query.release_id else ""
|
||||
|
||||
try:
|
||||
all_results = await fetch_all_pages(start_date, end_date, rid)
|
||||
except Exception as e:
|
||||
raise OpenBBError(e) from e
|
||||
|
||||
if all_results.empty:
|
||||
raise OpenBBError(
|
||||
"The requested returned no data. Check the parameters and try again."
|
||||
)
|
||||
|
||||
return all_results.to_dict("records")
|
||||
|
||||
@staticmethod
|
||||
def transform_data(
|
||||
query: FredEconomicCalendarQueryParams,
|
||||
data: list,
|
||||
**kwargs: Any,
|
||||
) -> list[FredEconomicCalendarData]:
|
||||
"""Transform and validate the response."""
|
||||
return [FredEconomicCalendarData.model_validate(d) for d in data]
|
||||
@@ -0,0 +1,164 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- application/json
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
method: GET
|
||||
uri: https://fred.stlouisfed.org/releases/calendar?po=1&ptic=0&ve=2025-07-02&vs=2025-07-02
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
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
|
||||
eWi+0b7ytoLJvV/pNE7/+hslGg8l7TcAAA==
|
||||
headers:
|
||||
Alt-Svc:
|
||||
- h3=":443"; ma=93600
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Length:
|
||||
- '1507'
|
||||
Content-Type:
|
||||
- text/html; charset=UTF-8
|
||||
Date:
|
||||
- Fri, 20 Feb 2026 20:04:11 GMT
|
||||
Expires:
|
||||
- Fri, 20 Feb 2026 20:14:11 GMT
|
||||
Server:
|
||||
- Apache
|
||||
Set-Cookie:
|
||||
- _abck=6730888CA4F2D60FE2DF36FD689B92A2~-1~YAAQC3k1F/kZdVOcAQAACBmnfA8QKYd3F187f3Y6ygaiVVXuSBn+r/qUGGNomnpxh9CEGjFgqSSjAdgW8gbibOaazp2wWwL0EVU1NNnbnTu6+vNN9uEO0QQ123Cys3FDlANTOldMaYu9NyZr5LdSEqb7GIzx/5r3HKFWwyZOcjMe5MgOC+AJnxeMgywLQszQpCuKWQLMQHF0T4p7atCL3aw+F71N/qNBZ4j0yOgvWX/GFFMAUqd3WhRJV3lW4DpJQR4fGdJsQqiTgmXkHtbBi2GU0SMQYAE4v9N5EEccFuITZ3VVdikJ19252Q7B+xd8Ak6FvQxN5B5NinxuD5mssDX5Hvccta8myFkZlvyVbuu2ECFfPem47F72Ie5FPIje3zajyDWvH6TfcLiTP68UHXHxPugrpUi6629R/+XLzTImws89PXeYuUaYxfkRhx+oKN4WFuItARveP4gw71uWjDi5qRv3+Dxs~-1~-1~-1~-1~-1;
|
||||
Domain=.stlouisfed.org; Path=/; Expires=Sat, 20 Feb 2027 20:04:11 GMT; Max-Age=31536000;
|
||||
Secure
|
||||
- bm_sz=322DD331BDB174348E9B9E045652AA65~YAAQC3k1F/oZdVOcAQAACRmnfB52oqemJpaP4OaHY9774TKChrpsRHSy4NbSZomUqNd150qIyJJS/xQLIKFya0jwYULGSQ4IVx7q7BKY+wlgghHj8mr9oRZBv2nyPQnwyMT3NIUdQFzv8+ye7nLvA51ePBnwecX1t4xvVJd72ekhwCIu3AdOAgNUvkq/MSzWIwOZJapUCEsrzsdveYh7rxKYJdczHLdVcaydBsnC16sas/zGteG1wjq0A/THzUkopxgiR7MLlzPcl7RoPTVOqupVwgq+O7vNmBmhpV7YhTiMHn0w8PfHuy9wHx2hyNesOIVu1MTzfrJQnJyGoLDb8RsAPDWQSvwKUOB/s32+bZw=~4535351~3748406;
|
||||
Domain=.stlouisfed.org; Path=/; Expires=Sat, 21 Feb 2026 00:04:09 GMT; Max-Age=14398
|
||||
Strict-Transport-Security:
|
||||
- max-age=86400
|
||||
Vary:
|
||||
- Accept-Encoding
|
||||
X-Akamai-Transformed:
|
||||
- 0 - 0 -
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
X-Frame-Options:
|
||||
- SAMEORIGIN
|
||||
X-XSS-Protection:
|
||||
- 1; mode=block
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- application/json
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
method: GET
|
||||
uri: https://fred.stlouisfed.org/releases/calendar?po=1&ptic=0&ve=2025-07-01&vs=2025-07-01
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
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
|
||||
vvSmFFsRlFqN+r//AUF532vPQQAA
|
||||
headers:
|
||||
Alt-Svc:
|
||||
- h3=":443"; ma=93600
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Length:
|
||||
- '1731'
|
||||
Content-Type:
|
||||
- text/html; charset=UTF-8
|
||||
Date:
|
||||
- Fri, 20 Feb 2026 20:04:13 GMT
|
||||
Expires:
|
||||
- Fri, 20 Feb 2026 20:14:13 GMT
|
||||
Server:
|
||||
- Apache
|
||||
Set-Cookie:
|
||||
- _abck=C80A0F4E8F224714B5CC71B6D6C92BF2~-1~YAAQC3k1FxAddVOcAQAAhCCnfA9G9T6rDQXkzLeG5Ux6XaQBQgohJbicjRSAPSDkXOsKQqRZEHP87xN9Ty573pxSpJMnFkwrtwkrSsLaeiS7L0glW8h1li0lFdLL17cTV7FCmrZ0BWp9ZJbi/rp88fuUcDjgc0lATMWaIGi8tFUkLb8zVWJj4JA2nE8ZLCN9ngclaIOMaAn87fC4AWSyYUoWpB/e8M+Fs6aYB6kN78OnxwGjovQpsGsGdVvKBMEOX5tsQJKNWsK05YBt/lPoqcgWJFVQFEi+24avdyFTASUXkiLYtELFAEnZnc+n2QweUvimJf/nOKF6wndeZt00NWswIhb8ThX5CKY6ffdoJyHkswTyyUHYBr3yujYzh0JXLOYLEB5JzhDiTJVi9ed+BarVPku7wctx31zX4hkddcjYg9Gd/AkxAXOS/R0+uea+tSd2IwxA7J7wPdYb5m/Vmx3tijY=~-1~-1~-1~-1~-1;
|
||||
Domain=.stlouisfed.org; Path=/; Expires=Sat, 20 Feb 2027 20:04:13 GMT; Max-Age=31536000;
|
||||
Secure
|
||||
- bm_sz=2CEF56C32C2AE52C8000EDAD1E963D27~YAAQC3k1FxIddVOcAQAAhCCnfB6hYdgVhSx/71YWQpVcJnG5KqKjrmzHe1kL+6Zf7P8Vv+FOTZ2VUXOnTq8z0XsTTOLG/JlAqT8MRqwYgALCHKkL+PCbSyCU65dbEU/i68Vu4hSSW4Pz8sfVk7HBUsnamPrTIQSj4nHg3jp40opBvfqAxBNSmvxV8vHOPQILMPt6G93/sSC7lJxwKIvTVopkmn9hJuugdpXpigkGy2vO44acJiBJfqqQn3Wyxx0TKUOCTpU7LVE3CVW92LLAn0TIB9jXrKXL5jPfeukoLncbCMugreR3xKlwx2ojt70Lv8vHs13ZkJDUABrKesLsl8nr5l9hg90nVPEkVibSAg==~4535351~3748406;
|
||||
Domain=.stlouisfed.org; Path=/; Expires=Sat, 21 Feb 2026 00:04:09 GMT; Max-Age=14396
|
||||
Strict-Transport-Security:
|
||||
- max-age=86400
|
||||
Vary:
|
||||
- Accept-Encoding
|
||||
X-Akamai-Transformed:
|
||||
- 0 - 0 -
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
X-Frame-Options:
|
||||
- SAMEORIGIN
|
||||
X-XSS-Protection:
|
||||
- 1; mode=block
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -14,6 +14,7 @@ from openbb_fred.models.dwpcr_rates import FREDDiscountWindowPrimaryCreditRateFe
|
||||
from openbb_fred.models.ecb_interest_rates import (
|
||||
FREDEuropeanCentralBankInterestRatesFetcher,
|
||||
)
|
||||
from openbb_fred.models.economic_calendar import FredEconomicCalendarFetcher
|
||||
from openbb_fred.models.euro_short_term_rate import FredEuroShortTermRateFetcher
|
||||
from openbb_fred.models.fed_projections import FREDPROJECTIONFetcher
|
||||
from openbb_fred.models.federal_funds_rate import FredFederalFundsRateFetcher
|
||||
@@ -529,3 +530,16 @@ def test_fred_manufacturing_outlook_ny_fetcher(credentials=test_credentials):
|
||||
fetcher = FredManufacturingOutlookNYFetcher()
|
||||
result = fetcher.test(params, credentials)
|
||||
assert result is None
|
||||
|
||||
|
||||
@pytest.mark.record_http
|
||||
def test_fred_economic_calendar_fetcher(credentials=test_credentials):
|
||||
"""Test FRED Economic Calendar Fetcher."""
|
||||
params = {
|
||||
"start_date": datetime.date(2025, 7, 1),
|
||||
"end_date": datetime.date(2025, 7, 2),
|
||||
}
|
||||
|
||||
fetcher = FredEconomicCalendarFetcher()
|
||||
result = fetcher.test(params, credentials)
|
||||
assert result is None
|
||||
|
||||
Reference in New Issue
Block a user