Skip to content

Add fiscal data #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions notebooks/_config.yml
Original file line number Diff line number Diff line change
@@ -2,8 +2,8 @@
# Learn more at https://jupyterbook.org/customize/config.html

title: Quantflow library
author: Quantmind Team
copyright: "2024"
author: <a href="https://quantmind.com">quantmind</a>
copyright: "2014-2025"
logo: assets/quantflow-light.svg

# Force re-execution of notebooks on each build.
31 changes: 31 additions & 0 deletions notebooks/data/fiscal_data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
jupytext:
text_representation:
extension: .md
format_name: myst
format_version: 0.13
jupytext_version: 1.16.6
kernelspec:
display_name: Python 3 (ipykernel)
language: python
name: python3
---

# Fiscal Data

```{code-cell} ipython3
from quantflow.data.fiscal_data import FiscalData
```

```{code-cell} ipython3
fd = FiscalData()
```

```{code-cell} ipython3
data = await fd.securities()
data
```

```{code-cell} ipython3

```
62 changes: 62 additions & 0 deletions quantflow/data/fiscal_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from dataclasses import dataclass
from datetime import date, timedelta

import pandas as pd
from fluid.utils.http_client import AioHttpClient

from quantflow.utils.dates import as_date

URL = (
"https://www.federalreserve.gov/datadownload/Output.aspx?"
"rel=H15&series=bf17364827e38702b42a58cf8eaa3f78&lastobs=&"
)

maturities = [
"month_1",
"month_3",
"month_6",
"year_1",
"year_2",
"year_3",
"year_5",
"year_7",
"year_10",
"year_20",
"year_30",
]


@dataclass
class FiscalData(AioHttpClient):
"""Fiscal Data API client.

THis class is used to fetch data from the
[fiscal data api](https://fiscaldata.treasury.gov/api-documentation/)
"""

url: str = "https://api.fiscaldata.treasury.gov/services/api/fiscal_service"

async def securities(self, record_date: date | None = None) -> pd.DataFrame:
"""Get treasury constant maturities rates"""
rd = as_date(record_date)
pm = rd.replace(day=1) - timedelta(days=1)
params = {"filter": f"record_date:eq:{pm.isoformat()}"}
data = await self.get_all("/v1/debt/mspd/mspd_table_3_market", params)
return pd.DataFrame(data)

async def get_all(self, path: str, params: dict[str, str]) -> list:
"""Get all data from the API"""
next_url: str | None = f"{self.url}{path}"
full_data = []
while next_url:
payload = await self.get(next_url, params=params)
full_data.extend(payload["data"])
params = {}
if links := payload.get("links"):
if next_path := links.get("next"):
next_url = f"{self.url}{next_path}"
else:
next_url = None
else:
next_url = None
return full_data
9 changes: 9 additions & 0 deletions quantflow/utils/dates.py
Original file line number Diff line number Diff line change
@@ -22,3 +22,12 @@ def isoformat(date: str | date) -> str:

def start_of_day(dt: date | None = None) -> datetime:
return as_utc(dt).replace(hour=0, minute=0, second=0, microsecond=0)


def as_date(dt: date | None = None) -> date:
if dt is None:
return date.today()
elif isinstance(dt, datetime):
return dt.date()
else:
return dt
12 changes: 12 additions & 0 deletions quantflow_tests/test_data.py
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
from aiohttp.client_exceptions import ClientError

from quantflow.data.fed import FederalReserve
from quantflow.data.fiscal_data import FiscalData
from quantflow.data.fmp import FMP

pytestmark = pytest.mark.skipif(not FMP().key, reason="No FMP API key found")
@@ -50,3 +51,14 @@ async def test_fed_rates() -> None:
assert df.shape[1] == 2
except (ConnectionError, ClientError) as e:
pytest.skip(f"Skipping test_fed due to network issue: {e}")


async def __test_fiscal_data() -> None:
try:
async with FiscalData() as fd:
df = await fd.securities()
assert df is not None
assert df.shape[0] > 0
assert df.shape[1] == 2
except (ConnectionError, ClientError) as e:
pytest.skip(f"Skipping test_fed due to network issue: {e}")