mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-13 12:51:49 +00:00
add mortgage indices (#6501)
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
"""Mortgage Indices Standard Model."""
|
||||
|
||||
from datetime import (
|
||||
date as dateType,
|
||||
)
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from openbb_core.provider.abstract.data import Data
|
||||
from openbb_core.provider.abstract.query_params import QueryParams
|
||||
from openbb_core.provider.utils.descriptions import (
|
||||
DATA_DESCRIPTIONS,
|
||||
QUERY_DESCRIPTIONS,
|
||||
)
|
||||
|
||||
|
||||
class MortgageIndicesQueryParams(QueryParams):
|
||||
"""Mortgage Indices Query."""
|
||||
|
||||
start_date: Optional[dateType] = Field(
|
||||
default=None,
|
||||
description=QUERY_DESCRIPTIONS.get("start_date", ""),
|
||||
)
|
||||
end_date: Optional[dateType] = Field(
|
||||
default=None,
|
||||
description=QUERY_DESCRIPTIONS.get("end_date", ""),
|
||||
)
|
||||
|
||||
|
||||
class MortgageIndicesData(Data):
|
||||
"""Mortgage Indices Data."""
|
||||
|
||||
date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", ""))
|
||||
symbol: Optional[str] = Field(
|
||||
default=None,
|
||||
description=DATA_DESCRIPTIONS.get("symbol", ""),
|
||||
)
|
||||
name: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Name of the index.",
|
||||
)
|
||||
rate: float = Field(
|
||||
description="Mortgage rate.",
|
||||
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
||||
)
|
||||
@@ -694,3 +694,29 @@ def test_fixedincome_bond_indices(params, headers):
|
||||
result = requests.get(url, headers=headers, timeout=10)
|
||||
assert isinstance(result, requests.Response)
|
||||
assert result.status_code == 200
|
||||
|
||||
|
||||
@parametrize(
|
||||
"params",
|
||||
[
|
||||
{
|
||||
"provider": "fred",
|
||||
"index": "usda_30y,fha_30y",
|
||||
"start_date": "2023-05-31",
|
||||
"end_date": "2024-06-01",
|
||||
"transform": None,
|
||||
"frequency": None,
|
||||
"aggregation_method": "avg",
|
||||
},
|
||||
],
|
||||
)
|
||||
@pytest.mark.integration
|
||||
def test_fixedincome_mortgage_indices(params, headers):
|
||||
"""Test the mortgage indices endpoint."""
|
||||
params = {p: v for p, v in params.items() if v}
|
||||
|
||||
query_str = get_querystring(params, [])
|
||||
url = f"http://0.0.0.0:8000/api/v1/fixedincome/mortgage_indices?{query_str}"
|
||||
result = requests.get(url, headers=headers, timeout=10)
|
||||
assert isinstance(result, requests.Response)
|
||||
assert result.status_code == 200
|
||||
|
||||
@@ -647,3 +647,28 @@ def test_fixedincome_bond_indices(params, obb):
|
||||
assert result
|
||||
assert isinstance(result, OBBject)
|
||||
assert len(result.results) > 0
|
||||
|
||||
|
||||
@parametrize(
|
||||
"params",
|
||||
[
|
||||
{
|
||||
"provider": "fred",
|
||||
"index": "usda_30y,fha_30y",
|
||||
"start_date": "2023-05-31",
|
||||
"end_date": "2024-06-01",
|
||||
"transform": None,
|
||||
"frequency": None,
|
||||
"aggregation_method": "avg",
|
||||
},
|
||||
],
|
||||
)
|
||||
@pytest.mark.integration
|
||||
def test_fixedincome_mortgage_indices(params, obb):
|
||||
"""Test the mortgage indices endpoint."""
|
||||
params = {p: v for p, v in params.items() if v}
|
||||
|
||||
result = obb.fixedincome.mortgage_indices(**params)
|
||||
assert result
|
||||
assert isinstance(result, OBBject)
|
||||
assert len(result.results) > 0
|
||||
|
||||
@@ -81,3 +81,29 @@ async def bond_indices(
|
||||
) -> OBBject: # type: ignore
|
||||
"""Bond Indices."""
|
||||
return await OBBject.from_query(Query(**locals()))
|
||||
|
||||
|
||||
@router.command(
|
||||
model="MortgageIndices",
|
||||
examples=[
|
||||
APIEx(
|
||||
description="The default state for FRED are the primary mortgage indices from Optimal Blue.",
|
||||
parameters={"provider": "fred"},
|
||||
),
|
||||
APIEx(
|
||||
description="Multiple indices can be requested.",
|
||||
parameters={
|
||||
"index": "jumbo_30y,conforming_30y,conforming_15y",
|
||||
"provider": "fred",
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
async def mortgage_indices(
|
||||
cc: CommandContext,
|
||||
provider_choices: ProviderChoices,
|
||||
standard_params: StandardParams,
|
||||
extra_params: ExtraParams,
|
||||
) -> OBBject: # type: ignore
|
||||
"""Mortgage Indices."""
|
||||
return await OBBject.from_query(Query(**locals()))
|
||||
|
||||
185
openbb_platform/openbb/assets/reference.json
vendored
185
openbb_platform/openbb/assets/reference.json
vendored
@@ -32235,6 +32235,191 @@
|
||||
},
|
||||
"model": "BondIndices"
|
||||
},
|
||||
"/fixedincome/mortgage_indices": {
|
||||
"deprecated": {
|
||||
"flag": null,
|
||||
"message": null
|
||||
},
|
||||
"description": "Mortgage Indices.",
|
||||
"examples": "\nExamples\n--------\n\n```python\nfrom openbb import obb\n# The default state for FRED are the primary mortgage indices from Optimal Blue.\nobb.fixedincome.mortgage_indices(provider='fred')\n# Multiple indices can be requested.\nobb.fixedincome.mortgage_indices(index=jumbo_30y,conforming_30y,conforming_15y, provider='fred')\n```\n\n",
|
||||
"parameters": {
|
||||
"standard": [
|
||||
{
|
||||
"name": "start_date",
|
||||
"type": "Union[date, str]",
|
||||
"description": "Start date of the data, in YYYY-MM-DD format.",
|
||||
"default": null,
|
||||
"optional": true,
|
||||
"choices": null
|
||||
},
|
||||
{
|
||||
"name": "end_date",
|
||||
"type": "Union[date, str]",
|
||||
"description": "End date of the data, in YYYY-MM-DD format.",
|
||||
"default": null,
|
||||
"optional": true,
|
||||
"choices": null
|
||||
},
|
||||
{
|
||||
"name": "provider",
|
||||
"type": "Literal['fred']",
|
||||
"description": "The provider to use, by default None. If None, the priority list configured in the settings is used. Default priority: f, r, e, d.",
|
||||
"default": null,
|
||||
"optional": true
|
||||
}
|
||||
],
|
||||
"fred": [
|
||||
{
|
||||
"name": "index",
|
||||
"type": "Union[Union[Literal['primary', 'ltv_lte_80', 'ltv_gt_80', 'conforming_30y', 'conforming_30y_na', 'jumbo_30y', 'fha_30y', 'va_30y', 'usda_30y', 'conforming_15y', 'ltv_lte80_fico_ge740', 'ltv_lte80_fico_a720b739', 'ltv_lte80_fico_a700b719', 'ltv_lte80_fico_a680b699', 'ltv_lte80_fico_lt680', 'ltv_gt80_fico_ge740', 'ltv_gt80_fico_a720b739', 'ltv_gt80_fico_a700b719', 'ltv_gt80_fico_a680b699', 'ltv_gt80_fico_lt680'], str], List[Union[Literal['primary', 'ltv_lte_80', 'ltv_gt_80', 'conforming_30y', 'conforming_30y_na', 'jumbo_30y', 'fha_30y', 'va_30y', 'usda_30y', 'conforming_15y', 'ltv_lte80_fico_ge740', 'ltv_lte80_fico_a720b739', 'ltv_lte80_fico_a700b719', 'ltv_lte80_fico_a680b699', 'ltv_lte80_fico_lt680', 'ltv_gt80_fico_ge740', 'ltv_gt80_fico_a720b739', 'ltv_gt80_fico_a700b719', 'ltv_gt80_fico_a680b699', 'ltv_gt80_fico_lt680'], str]]]",
|
||||
"description": "The specific index, or index group, to query. Default is the 'primary' group. Multiple items allowed for provider(s): fred.",
|
||||
"default": "primary",
|
||||
"optional": true,
|
||||
"choices": [
|
||||
"primary",
|
||||
"ltv_lte_80",
|
||||
"ltv_gt_80",
|
||||
"conforming_30y",
|
||||
"conforming_30y_na",
|
||||
"jumbo_30y",
|
||||
"fha_30y",
|
||||
"va_30y",
|
||||
"usda_30y",
|
||||
"conforming_15y",
|
||||
"ltv_lte80_fico_ge740",
|
||||
"ltv_lte80_fico_a720b739",
|
||||
"ltv_lte80_fico_a700b719",
|
||||
"ltv_lte80_fico_a680b699",
|
||||
"ltv_lte80_fico_lt680",
|
||||
"ltv_gt80_fico_ge740",
|
||||
"ltv_gt80_fico_a720b739",
|
||||
"ltv_gt80_fico_a700b719",
|
||||
"ltv_gt80_fico_a680b699",
|
||||
"ltv_gt80_fico_lt680"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "frequency",
|
||||
"type": "Literal['a', 'q', 'm', 'w', 'd', 'wef', 'weth', 'wew', 'wetu', 'wem', 'wesu', 'wesa', 'bwew', 'bwem']",
|
||||
"description": "Frequency aggregation to convert daily data to lower frequency. None = No change a = Annual q = Quarterly m = Monthly w = Weekly d = Daily wef = Weekly, Ending Friday weth = Weekly, Ending Thursday wew = Weekly, Ending Wednesday wetu = Weekly, Ending Tuesday wem = Weekly, Ending Monday wesu = Weekly, Ending Sunday wesa = Weekly, Ending Saturday bwew = Biweekly, Ending Wednesday bwem = Biweekly, Ending Monday",
|
||||
"default": null,
|
||||
"optional": true,
|
||||
"choices": [
|
||||
"a",
|
||||
"q",
|
||||
"m",
|
||||
"w",
|
||||
"d",
|
||||
"wef",
|
||||
"weth",
|
||||
"wew",
|
||||
"wetu",
|
||||
"wem",
|
||||
"wesu",
|
||||
"wesa",
|
||||
"bwew",
|
||||
"bwem"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "aggregation_method",
|
||||
"type": "Literal['avg', 'sum', 'eop']",
|
||||
"description": "A key that indicates the aggregation method used for frequency aggregation. This parameter has no affect if the frequency parameter is not set, default is 'avg'. avg = Average sum = Sum eop = End of Period",
|
||||
"default": "avg",
|
||||
"optional": true,
|
||||
"choices": [
|
||||
"avg",
|
||||
"sum",
|
||||
"eop"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "transform",
|
||||
"type": "Literal['chg', 'ch1', 'pch', 'pc1', 'pca', 'cch', 'cca', 'log']",
|
||||
"description": "Transformation type None = No transformation chg = Change ch1 = Change from Year Ago pch = Percent Change pc1 = Percent Change from Year Ago pca = Compounded Annual Rate of Change cch = Continuously Compounded Rate of Change cca = Continuously Compounded Annual Rate of Change log = Natural Log",
|
||||
"default": null,
|
||||
"optional": true,
|
||||
"choices": [
|
||||
"chg",
|
||||
"ch1",
|
||||
"pch",
|
||||
"pc1",
|
||||
"pca",
|
||||
"cch",
|
||||
"cca",
|
||||
"log"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"returns": {
|
||||
"OBBject": [
|
||||
{
|
||||
"name": "results",
|
||||
"type": "List[MortgageIndices]",
|
||||
"description": "Serializable results."
|
||||
},
|
||||
{
|
||||
"name": "provider",
|
||||
"type": "Optional[Literal['fred']]",
|
||||
"description": "Provider name."
|
||||
},
|
||||
{
|
||||
"name": "warnings",
|
||||
"type": "Optional[List[Warning_]]",
|
||||
"description": "List of warnings."
|
||||
},
|
||||
{
|
||||
"name": "chart",
|
||||
"type": "Optional[Chart]",
|
||||
"description": "Chart object."
|
||||
},
|
||||
{
|
||||
"name": "extra",
|
||||
"type": "Dict[str, Any]",
|
||||
"description": "Extra info."
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"standard": [
|
||||
{
|
||||
"name": "date",
|
||||
"type": "date",
|
||||
"description": "The date of the data.",
|
||||
"default": "",
|
||||
"optional": false,
|
||||
"choices": null
|
||||
},
|
||||
{
|
||||
"name": "symbol",
|
||||
"type": "str",
|
||||
"description": "Symbol representing the entity requested in the data.",
|
||||
"default": null,
|
||||
"optional": true,
|
||||
"choices": null
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"type": "str",
|
||||
"description": "Name of the index.",
|
||||
"default": null,
|
||||
"optional": true,
|
||||
"choices": null
|
||||
},
|
||||
{
|
||||
"name": "rate",
|
||||
"type": "float",
|
||||
"description": "Mortgage rate.",
|
||||
"default": "",
|
||||
"optional": false,
|
||||
"choices": null
|
||||
}
|
||||
],
|
||||
"fred": []
|
||||
},
|
||||
"model": "MortgageIndices"
|
||||
},
|
||||
"/index/price/historical": {
|
||||
"deprecated": {
|
||||
"flag": null,
|
||||
|
||||
@@ -16,6 +16,7 @@ class ROUTER_fixedincome(Container):
|
||||
bond_indices
|
||||
/corporate
|
||||
/government
|
||||
mortgage_indices
|
||||
/rate
|
||||
sofr
|
||||
/spreads
|
||||
@@ -184,6 +185,132 @@ class ROUTER_fixedincome(Container):
|
||||
command_runner=self._command_runner
|
||||
)
|
||||
|
||||
@exception_handler
|
||||
@validate
|
||||
def mortgage_indices(
|
||||
self,
|
||||
start_date: Annotated[
|
||||
Union[datetime.date, None, str],
|
||||
OpenBBField(description="Start date of the data, in YYYY-MM-DD format."),
|
||||
] = None,
|
||||
end_date: Annotated[
|
||||
Union[datetime.date, None, str],
|
||||
OpenBBField(description="End date of the data, in YYYY-MM-DD format."),
|
||||
] = None,
|
||||
provider: Annotated[
|
||||
Optional[Literal["fred"]],
|
||||
OpenBBField(
|
||||
description="The provider to use, by default None. If None, the priority list configured in the settings is used. Default priority: fred."
|
||||
),
|
||||
] = None,
|
||||
**kwargs
|
||||
) -> OBBject:
|
||||
"""Mortgage Indices.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
start_date : Union[datetime.date, None, str]
|
||||
Start date of the data, in YYYY-MM-DD format.
|
||||
end_date : Union[datetime.date, None, str]
|
||||
End date of the data, in YYYY-MM-DD format.
|
||||
provider : Optional[Literal['fred']]
|
||||
The provider to use, by default None. If None, the priority list configured in the settings is used. Default priority: fred.
|
||||
index : Union[Literal['primary', 'ltv_lte_80', 'ltv_gt_80', 'conforming_30y', 'conforming_30y_na', 'jumbo_30y', 'fha_30y', 'va_30y', 'usda_30y', 'conforming_15y', 'ltv_lte80_fico_ge740', 'ltv_lte80_fico_a720b739', 'ltv_lte80_fico_a700b719', 'ltv_lte80_fico_a680b699', 'ltv_lte80_fico_lt680', 'ltv_gt80_fico_ge740', 'ltv_gt80_fico_a720b739', 'ltv_gt80_fico_a700b719', 'ltv_gt80_fico_a680b699', 'ltv_gt80_fico_lt680'], str]
|
||||
The specific index, or index group, to query. Default is the 'primary' group. Multiple comma separated items allowed. (provider: fred)
|
||||
frequency : Optional[Literal['a', 'q', 'm', 'w', 'd', 'wef', 'weth', 'wew', 'wetu', 'wem', 'wesu', 'wesa', 'bwew', 'bwem']]
|
||||
|
||||
Frequency aggregation to convert daily data to lower frequency.
|
||||
None = No change
|
||||
a = Annual
|
||||
q = Quarterly
|
||||
m = Monthly
|
||||
w = Weekly
|
||||
d = Daily
|
||||
wef = Weekly, Ending Friday
|
||||
weth = Weekly, Ending Thursday
|
||||
wew = Weekly, Ending Wednesday
|
||||
wetu = Weekly, Ending Tuesday
|
||||
wem = Weekly, Ending Monday
|
||||
wesu = Weekly, Ending Sunday
|
||||
wesa = Weekly, Ending Saturday
|
||||
bwew = Biweekly, Ending Wednesday
|
||||
bwem = Biweekly, Ending Monday
|
||||
(provider: fred)
|
||||
aggregation_method : Literal['avg', 'sum', 'eop']
|
||||
|
||||
A key that indicates the aggregation method used for frequency aggregation.
|
||||
This parameter has no affect if the frequency parameter is not set, default is 'avg'.
|
||||
avg = Average
|
||||
sum = Sum
|
||||
eop = End of Period
|
||||
(provider: fred)
|
||||
transform : Optional[Literal['chg', 'ch1', 'pch', 'pc1', 'pca', 'cch', 'cca', 'log']]
|
||||
|
||||
Transformation type
|
||||
None = No transformation
|
||||
chg = Change
|
||||
ch1 = Change from Year Ago
|
||||
pch = Percent Change
|
||||
pc1 = Percent Change from Year Ago
|
||||
pca = Compounded Annual Rate of Change
|
||||
cch = Continuously Compounded Rate of Change
|
||||
cca = Continuously Compounded Annual Rate of Change
|
||||
log = Natural Log
|
||||
(provider: fred)
|
||||
|
||||
Returns
|
||||
-------
|
||||
OBBject
|
||||
results : List[MortgageIndices]
|
||||
Serializable results.
|
||||
provider : Optional[Literal['fred']]
|
||||
Provider name.
|
||||
warnings : Optional[List[Warning_]]
|
||||
List of warnings.
|
||||
chart : Optional[Chart]
|
||||
Chart object.
|
||||
extra : Dict[str, Any]
|
||||
Extra info.
|
||||
|
||||
MortgageIndices
|
||||
---------------
|
||||
date : date
|
||||
The date of the data.
|
||||
symbol : Optional[str]
|
||||
Symbol representing the entity requested in the data.
|
||||
name : Optional[str]
|
||||
Name of the index.
|
||||
rate : float
|
||||
Mortgage rate.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from openbb import obb
|
||||
>>> # The default state for FRED are the primary mortgage indices from Optimal Blue.
|
||||
>>> obb.fixedincome.mortgage_indices(provider='fred')
|
||||
>>> # Multiple indices can be requested.
|
||||
>>> obb.fixedincome.mortgage_indices(index='jumbo_30y,conforming_30y,conforming_15y', provider='fred')
|
||||
""" # noqa: E501
|
||||
|
||||
return self._run(
|
||||
"/fixedincome/mortgage_indices",
|
||||
**filter_inputs(
|
||||
provider_choices={
|
||||
"provider": self._get_provider(
|
||||
provider,
|
||||
"fixedincome.mortgage_indices",
|
||||
("fred",),
|
||||
)
|
||||
},
|
||||
standard_params={
|
||||
"start_date": start_date,
|
||||
"end_date": end_date,
|
||||
},
|
||||
extra_params=kwargs,
|
||||
info={"index": {"fred": {"multiple_items_allowed": True}}},
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
def rate(self):
|
||||
# pylint: disable=import-outside-toplevel
|
||||
|
||||
@@ -18,6 +18,7 @@ from openbb_fred.models.hqm import FREDHighQualityMarketCorporateBondFetcher
|
||||
from openbb_fred.models.ice_bofa import FREDICEBofAFetcher
|
||||
from openbb_fred.models.iorb_rates import FREDIORBFetcher
|
||||
from openbb_fred.models.moody import FREDMoodyCorporateBondIndexFetcher
|
||||
from openbb_fred.models.mortgage_indices import FredMortgageIndicesFetcher
|
||||
from openbb_fred.models.regional import FredRegionalDataFetcher
|
||||
from openbb_fred.models.retail_prices import FredRetailPricesFetcher
|
||||
from openbb_fred.models.search import (
|
||||
@@ -57,6 +58,7 @@ Research division of the Federal Reserve Bank of St. Louis that has more than
|
||||
"EuropeanCentralBankInterestRates": FREDEuropeanCentralBankInterestRatesFetcher,
|
||||
"ICEBofA": FREDICEBofAFetcher,
|
||||
"MoodyCorporateBondIndex": FREDMoodyCorporateBondIndexFetcher,
|
||||
"MortgageIndices": FredMortgageIndicesFetcher,
|
||||
"CommercialPaper": FREDCommercialPaperFetcher,
|
||||
"FredSearch": FredSearchFetcher,
|
||||
"FredSeries": FredSeriesFetcher,
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
"""FRED Mortgage Indices Model."""
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
from typing import Any, Dict, List, Literal, Optional, Union
|
||||
from warnings import warn
|
||||
|
||||
from openbb_core.app.model.abstract.error import OpenBBError
|
||||
from openbb_core.provider.abstract.annotated_result import AnnotatedResult
|
||||
from openbb_core.provider.abstract.fetcher import Fetcher
|
||||
from openbb_core.provider.standard_models.mortgage_indices import (
|
||||
MortgageIndicesData,
|
||||
MortgageIndicesQueryParams,
|
||||
)
|
||||
from openbb_core.provider.utils.errors import EmptyDataError
|
||||
from openbb_fred.models.series import FredSeriesFetcher
|
||||
from pandas import Categorical, DataFrame
|
||||
from pydantic import Field, field_validator
|
||||
|
||||
MORTGAGE_ID_TO_TITLE = {
|
||||
"OBMMIC30YF": "30-Year Fixed Rate Conforming",
|
||||
"OBMMIC30YFNA": "30-Year Fixed Rate Conforming Non-Adjusted",
|
||||
"OBMMIJUMBO30YF": "30-Year Fixed Rate Jumbo",
|
||||
"OBMMIFHA30YF": "30-Year Fixed Rate FHA",
|
||||
"OBMMIVA30YF": "30-Year Fixed Rate Veterans Affairs",
|
||||
"OBMMIUSDA30YF": "30-Year Fixed Rate USDA",
|
||||
"OBMMIC15YF": "15-Year Fixed Rate Conforming",
|
||||
"OBMMIC30YFLVLE80FGE740": "30-Year Fixed Rate Conforming LTV <= 80 FICO >= 740",
|
||||
"OBMMIC30YFLVLE80FB720A739": "30-Year Fixed Rate Conforming LTV <= 80 FICO 720-739",
|
||||
"OBMMIC30YFLVLE80FB700A719": "30-Year Fixed Rate Conforming LTV <= 80 FICO 700-719",
|
||||
"OBMMIC30YFLVLE80FB680A699": "30-Year Fixed Rate Conforming LTV <= 80 FICO 680-699",
|
||||
"OBMMIC30YFLVLE80FLT680": "30-Year Fixed Rate Conforming LTV <= 80 FICO < 680",
|
||||
"OBMMIC30YFLVGT80FGE740": "30-Year Fixed Rate Conforming LTV > 80 FICO >= 740",
|
||||
"OBMMIC30YFLVGT80FB720A739": "30-Year Fixed Rate Conforming LTV > 80 FICO 720-739",
|
||||
"OBMMIC30YFLVGT80FB700A719": "30-Year Fixed Rate Conforming LTV > 80 FICO 700-719",
|
||||
"OBMMIC30YFLVGT80FB680A699": "30-Year Fixed Rate Conforming LTV > 80 FICO 680-699",
|
||||
"OBMMIC30YFLVGT80FLT680": "30-Year Fixed Rate Conforming LTV > 80 FICO < 680",
|
||||
}
|
||||
|
||||
MORTGAGE_GROUPS = {
|
||||
"primary": [
|
||||
"OBMMIC30YF",
|
||||
"OBMMIC30YFNA",
|
||||
"OBMMIJUMBO30YF",
|
||||
"OBMMIFHA30YF",
|
||||
"OBMMIVA30YF",
|
||||
"OBMMIUSDA30YF",
|
||||
"OBMMIC15YF",
|
||||
],
|
||||
"ltv_lte_80": [
|
||||
"OBMMIC30YFLVLE80FGE740",
|
||||
"OBMMIC30YFLVLE80FB720A739",
|
||||
"OBMMIC30YFLVLE80FB700A719",
|
||||
"OBMMIC30YFLVLE80FB680A699",
|
||||
"OBMMIC30YFLVLE80FLT680",
|
||||
],
|
||||
"ltv_gt_80": [
|
||||
"OBMMIC30YFLVGT80FGE740",
|
||||
"OBMMIC30YFLVGT80FB720A739",
|
||||
"OBMMIC30YFLVGT80FB700A719",
|
||||
"OBMMIC30YFLVGT80FB680A699",
|
||||
"OBMMIC30YFLVGT80FLT680",
|
||||
],
|
||||
}
|
||||
|
||||
MORTGAGE_CHOICES_TO_ID = {
|
||||
"primary": ",".join(MORTGAGE_GROUPS["primary"]),
|
||||
"ltv_lte_80": ",".join(MORTGAGE_GROUPS["ltv_lte_80"]),
|
||||
"ltv_gt_80": ",".join(MORTGAGE_GROUPS["ltv_gt_80"]),
|
||||
"conforming_30y": "OBMMIC30YF",
|
||||
"conforming_30y_na": "OBMMIC30YFNA",
|
||||
"jumbo_30y": "OBMMIJUMBO30YF",
|
||||
"fha_30y": "OBMMIFHA30YF",
|
||||
"va_30y": "OBMMIVA30YF",
|
||||
"usda_30y": "OBMMIUSDA30YF",
|
||||
"conforming_15y": "OBMMIC15YF",
|
||||
"ltv_lte80_fico_ge740": "OBMMIC30YFLVLE80FGE740",
|
||||
"ltv_lte80_fico_a720b739": "OBMMIC30YFLVLE80FB720A739",
|
||||
"ltv_lte80_fico_a700b719": "OBMMIC30YFLVLE80FB700A719",
|
||||
"ltv_lte80_fico_a680b699": "OBMMIC30YFLVLE80FB680A699",
|
||||
"ltv_lte80_fico_lt680": "OBMMIC30YFLVLE80FLT680",
|
||||
"ltv_gt80_fico_ge740": "OBMMIC30YFLVGT80FGE740",
|
||||
"ltv_gt80_fico_a720b739": "OBMMIC30YFLVGT80FB720A739",
|
||||
"ltv_gt80_fico_a700b719": "OBMMIC30YFLVGT80FB700A719",
|
||||
"ltv_gt80_fico_a680b699": "OBMMIC30YFLVGT80FB680A699",
|
||||
"ltv_gt80_fico_lt680": "OBMMIC30YFLVGT80FLT680",
|
||||
}
|
||||
|
||||
MortgageChoices = Literal[
|
||||
"primary",
|
||||
"ltv_lte_80",
|
||||
"ltv_gt_80",
|
||||
"conforming_30y",
|
||||
"conforming_30y_na",
|
||||
"jumbo_30y",
|
||||
"fha_30y",
|
||||
"va_30y",
|
||||
"usda_30y",
|
||||
"conforming_15y",
|
||||
"ltv_lte80_fico_ge740",
|
||||
"ltv_lte80_fico_a720b739",
|
||||
"ltv_lte80_fico_a700b719",
|
||||
"ltv_lte80_fico_a680b699",
|
||||
"ltv_lte80_fico_lt680",
|
||||
"ltv_gt80_fico_ge740",
|
||||
"ltv_gt80_fico_a720b739",
|
||||
"ltv_gt80_fico_a700b719",
|
||||
"ltv_gt80_fico_a680b699",
|
||||
"ltv_gt80_fico_lt680",
|
||||
]
|
||||
|
||||
|
||||
class FredMortgageIndicesQueryParams(MortgageIndicesQueryParams):
|
||||
"""FRED Mortgage Indices Query."""
|
||||
|
||||
__json_schema_extra__ = {"index": {"multiple_items_allowed": True}}
|
||||
|
||||
index: Union[MortgageChoices, str] = Field(
|
||||
default="primary",
|
||||
description="The specific index, or index group, to query. Default is the 'primary' group.",
|
||||
choices=list(MORTGAGE_CHOICES_TO_ID.keys()),
|
||||
)
|
||||
frequency: Union[
|
||||
None,
|
||||
Literal[
|
||||
"a",
|
||||
"q",
|
||||
"m",
|
||||
"w",
|
||||
"d",
|
||||
"wef",
|
||||
"weth",
|
||||
"wew",
|
||||
"wetu",
|
||||
"wem",
|
||||
"wesu",
|
||||
"wesa",
|
||||
"bwew",
|
||||
"bwem",
|
||||
],
|
||||
] = Field(
|
||||
default=None,
|
||||
description="""
|
||||
Frequency aggregation to convert daily data to lower frequency.
|
||||
None = No change
|
||||
a = Annual
|
||||
q = Quarterly
|
||||
m = Monthly
|
||||
w = Weekly
|
||||
d = Daily
|
||||
wef = Weekly, Ending Friday
|
||||
weth = Weekly, Ending Thursday
|
||||
wew = Weekly, Ending Wednesday
|
||||
wetu = Weekly, Ending Tuesday
|
||||
wem = Weekly, Ending Monday
|
||||
wesu = Weekly, Ending Sunday
|
||||
wesa = Weekly, Ending Saturday
|
||||
bwew = Biweekly, Ending Wednesday
|
||||
bwem = Biweekly, Ending Monday
|
||||
""",
|
||||
json_schema_extra={
|
||||
"choices": [
|
||||
"a",
|
||||
"q",
|
||||
"m",
|
||||
"w",
|
||||
"d",
|
||||
"wef",
|
||||
"weth",
|
||||
"wew",
|
||||
"wetu",
|
||||
"wem",
|
||||
"wesu",
|
||||
"wesa",
|
||||
"bwew",
|
||||
"bwem",
|
||||
]
|
||||
},
|
||||
)
|
||||
aggregation_method: Literal["avg", "sum", "eop"] = Field(
|
||||
default="avg",
|
||||
description="""
|
||||
A key that indicates the aggregation method used for frequency aggregation.
|
||||
This parameter has no affect if the frequency parameter is not set, default is 'avg'.
|
||||
avg = Average
|
||||
sum = Sum
|
||||
eop = End of Period
|
||||
""",
|
||||
json_schema_extra={"choices": ["avg", "sum", "eop"]},
|
||||
)
|
||||
transform: Union[
|
||||
None, Literal["chg", "ch1", "pch", "pc1", "pca", "cch", "cca", "log"]
|
||||
] = Field(
|
||||
default=None,
|
||||
description="""
|
||||
Transformation type
|
||||
None = No transformation
|
||||
chg = Change
|
||||
ch1 = Change from Year Ago
|
||||
pch = Percent Change
|
||||
pc1 = Percent Change from Year Ago
|
||||
pca = Compounded Annual Rate of Change
|
||||
cch = Continuously Compounded Rate of Change
|
||||
cca = Continuously Compounded Annual Rate of Change
|
||||
log = Natural Log
|
||||
""",
|
||||
json_schema_extra={
|
||||
"choices": ["chg", "ch1", "pch", "pc1", "pca", "cch", "cca", "log"]
|
||||
},
|
||||
)
|
||||
|
||||
@field_validator("index", mode="before", check_fields=False)
|
||||
@classmethod
|
||||
def validate_index(cls, v):
|
||||
"""Validate index."""
|
||||
indices = v.split(",")
|
||||
new_indices: List = []
|
||||
for index in indices:
|
||||
if index in MORTGAGE_CHOICES_TO_ID:
|
||||
new_indices.append(index)
|
||||
else:
|
||||
warn(f"Invalid index '{index}' will be ignored.")
|
||||
if not new_indices:
|
||||
raise OpenBBError(
|
||||
f"No valid indices found. Must be any of: {list(MORTGAGE_CHOICES_TO_ID.keys())}"
|
||||
)
|
||||
return ",".join(new_indices)
|
||||
|
||||
|
||||
class FredMortgageIndicesData(MortgageIndicesData):
|
||||
"""FRED Mortgage Indices Data."""
|
||||
|
||||
|
||||
class FredMortgageIndicesFetcher(
|
||||
Fetcher[
|
||||
FredMortgageIndicesQueryParams,
|
||||
List[FredMortgageIndicesData],
|
||||
]
|
||||
):
|
||||
"""FRED Mortgage Indices Fetcher."""
|
||||
|
||||
@staticmethod
|
||||
def transform_query(params: Dict[str, Any]) -> FredMortgageIndicesQueryParams:
|
||||
"""Transform query."""
|
||||
return FredMortgageIndicesQueryParams(**params)
|
||||
|
||||
@staticmethod
|
||||
async def aextract_data(
|
||||
query: FredMortgageIndicesQueryParams,
|
||||
credentials: Optional[Dict[str, str]],
|
||||
**kwargs: Any,
|
||||
) -> Dict:
|
||||
"""Extract data."""
|
||||
indices = query.index.split(",")
|
||||
ids = [MORTGAGE_CHOICES_TO_ID[index] for index in indices]
|
||||
try:
|
||||
response = await FredSeriesFetcher.fetch_data(
|
||||
dict(
|
||||
symbol=",".join(ids),
|
||||
start_date=query.start_date,
|
||||
end_date=query.end_date,
|
||||
transform=query.transform,
|
||||
frequency=query.frequency,
|
||||
aggregation_method=query.aggregation_method,
|
||||
),
|
||||
credentials,
|
||||
)
|
||||
except Exception as e:
|
||||
raise e from e
|
||||
|
||||
return {
|
||||
"metadata": response.metadata,
|
||||
"data": [d.model_dump() for d in response.result],
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def transform_data(
|
||||
query: FredMortgageIndicesQueryParams,
|
||||
data: Dict,
|
||||
**kwargs: Any,
|
||||
) -> AnnotatedResult[List[FredMortgageIndicesData]]:
|
||||
"""Transform data."""
|
||||
if not data.get("data"):
|
||||
raise EmptyDataError("The request was returned empty.")
|
||||
df = DataFrame.from_records(data["data"])
|
||||
metadata = data.get("metadata", {})
|
||||
# Flatten the data.
|
||||
df = (
|
||||
df.melt(id_vars="date", var_name="symbol", value_name="value")
|
||||
.query("value.notnull()")
|
||||
.rename(columns={"value": "rate"})
|
||||
)
|
||||
df["name"] = df.symbol.map(MORTGAGE_ID_TO_TITLE)
|
||||
# Normalize the percent values.
|
||||
df["rate"] = df["rate"] / 100
|
||||
df = df.fillna("N/A").replace("N/A", None)
|
||||
df["name"] = Categorical(
|
||||
df["name"],
|
||||
categories=[
|
||||
d
|
||||
for d in list(MORTGAGE_ID_TO_TITLE.values())
|
||||
if d in df["name"].unique()
|
||||
],
|
||||
ordered=True,
|
||||
)
|
||||
df.sort_values(["date", "name"], inplace=True)
|
||||
records = df.to_dict(orient="records")
|
||||
|
||||
return AnnotatedResult(
|
||||
result=[FredMortgageIndicesData.model_validate(r) for r in records],
|
||||
metadata=metadata,
|
||||
)
|
||||
@@ -0,0 +1,105 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- application/json
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
method: GET
|
||||
uri: https://api.stlouisfed.org/fred/series/observations?aggregation_method=avg&api_key=MOCK_API_KEY&file_type=json&limit=100000&observation_end=2024-06-04&observation_start=2024-06-01&series_id=OBMMIJUMBO30YF
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAEA6pWKkpNzCnJzE2NLy5JLCpRslIyMjAy0TUw0zU0UdJByKbmpaDL5ScVpxaVJZZk
|
||||
5udhaDYwVNJRQlaAqt8AZHZpXmZJsZKVUk5mHkhxaUlBaUl8SWVBqpKVoY5SWmZOKpSnlFWcD1ZS
|
||||
lJJaFJ9UqWSFYnRKYkmqko5ScX5RSXw+SImSlVJicbKSjlJyfmleiZKVkY5SflpacWqJkpWBjlJO
|
||||
Zm5miZKVoQEIoDiyWMkqmpIAATsEEYAGxko6SmWJOaWpSlZK5nqGppZKtTrUNB8UiEjmA2ZmrFQb
|
||||
WwsAU848LNIBAAA=
|
||||
headers:
|
||||
Cache-Control:
|
||||
- max-age=0, no-cache
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Length:
|
||||
- '239'
|
||||
Content-Type:
|
||||
- application/json; charset=UTF-8
|
||||
Date:
|
||||
- Fri, 14 Jun 2024 05:34:53 GMT
|
||||
Expires:
|
||||
- Fri, 14 Jun 2024 05:34:53 GMT
|
||||
Last-Modified:
|
||||
- Thu, 13 Jun 2024 12:01:06 GMT
|
||||
Pragma:
|
||||
- no-cache
|
||||
Server:
|
||||
- Apache
|
||||
Strict-Transport-Security:
|
||||
- max-age=86400
|
||||
Vary:
|
||||
- Accept-Encoding
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- application/json
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
method: GET
|
||||
uri: https://api.stlouisfed.org/fred/series?api_key=MOCK_API_KEY&file_type=json&series_id=OBMMIJUMBO30YF
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAAAAAAAEA6pWKkpNzCnJzE2NLy5JLCpRslIyMjAy0TUw0zU0VtJByKbmpaDLFacWZaYWFytZ
|
||||
RVcrZYJk/Z18fT29Qn2d/I0NIt2QdZNsdklmSU6qkpWSsYFuZGpikYJbZkVqikJQYkmqgldpblK+
|
||||
gm9+UUl6YnqqgmdeSmqFko5SflJxalFZYklmfh6SVwzNdQ0MdQ1AXkFWgOYbIyUdpbSi1MLS1Lzk
|
||||
SiUrJZfEzJxKZLH44ox8cOC4KOkoleZllhQrWSkFpBYlp+aVwETgalSVdJSKUxOL8/MSc+ITU7JK
|
||||
i0tyQeqslPzySxSCoTI5lQqOYLnUFOzq4eb5BTsq6SjlJBaXxJcWpCSWpILCGhFLCgbmVgaGVgZm
|
||||
ugamSjpKBfkFpTmJRZkllUpWZqY6Snn5Jakg1/oXlGTmJuYoOOWUpiJCzzexKDu1BBSImcmpxQoa
|
||||
GSUlBcVWMfox+uXl5UZ6+RBNSTmlqXrJ+bkx+vlJubmZMfqaMaVGhkZGChr+oCiHcDQVMosVkhNz
|
||||
kktzQG5USCvKz1VITC4pTcxRyMlPzk5NUShKLEktVijPLMlQSM7PKy7NTS0qVkhMLsovLlbIL0st
|
||||
UsjPS9UtycgsSlHIT1NIzMlRyIVFc0lRYl5xYjIoeosV8sDRXJ6ZkqqnAHaBQmZeck5pSmqxQm5p
|
||||
TklmQU4qQmdBUWZyZl66QmZeCtiPKallqTn5BakpColF+aV5KQolGSDVxSUK0KBTKCjKTylNLilW
|
||||
SMxLUSguSE3OTMtMVkjKLyrKL08tAovm5CfmKeSATFJILCkpykwqLUkt1ovJi8lzTUzOANmVWoEW
|
||||
HonFYJsSy1KLQOkW6sHEgoKi/IKiTFDKBgUPOKiKwWQqyGlF+aXpGWCNKBEIdaJCak5memZSZk5m
|
||||
SSXYXTDPpualZ+alKuTnKSQqpGeWpeYppCRW6oHiPVUhJbUkMTOnWCExKb+0RCE3tSQjPyU/Jz8d
|
||||
YkJKalpmXiYknBOLUhUSyxIzcxKTclIVMlKLUolPIYDpKdXG1gIAFK52ql8EAAA=
|
||||
headers:
|
||||
Cache-Control:
|
||||
- max-age=0, no-cache
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Length:
|
||||
- '788'
|
||||
Content-Type:
|
||||
- application/json; charset=UTF-8
|
||||
Date:
|
||||
- Fri, 14 Jun 2024 05:34:53 GMT
|
||||
Expires:
|
||||
- Fri, 14 Jun 2024 05:34:53 GMT
|
||||
Last-Modified:
|
||||
- Thu, 13 Jun 2024 12:01:06 GMT
|
||||
Pragma:
|
||||
- no-cache
|
||||
Server:
|
||||
- Apache
|
||||
Strict-Transport-Security:
|
||||
- max-age=86400
|
||||
Vary:
|
||||
- Accept-Encoding
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -21,6 +21,7 @@ from openbb_fred.models.hqm import FREDHighQualityMarketCorporateBondFetcher
|
||||
from openbb_fred.models.ice_bofa import FREDICEBofAFetcher
|
||||
from openbb_fred.models.iorb_rates import FREDIORBFetcher
|
||||
from openbb_fred.models.moody import FREDMoodyCorporateBondIndexFetcher
|
||||
from openbb_fred.models.mortgage_indices import FredMortgageIndicesFetcher
|
||||
from openbb_fred.models.regional import FredRegionalDataFetcher
|
||||
from openbb_fred.models.retail_prices import FredRetailPricesFetcher
|
||||
from openbb_fred.models.search import (
|
||||
@@ -357,3 +358,17 @@ def test_fred_bond_indices_fetcher(credentials=test_credentials):
|
||||
fetcher = FredBondIndicesFetcher()
|
||||
result = fetcher.test(params, credentials)
|
||||
assert result is None
|
||||
|
||||
|
||||
@pytest.mark.record_http
|
||||
def test_fred_bond_mortgage_fetcher(credentials=test_credentials):
|
||||
"""Test FredMortgageIndicesFetcher."""
|
||||
params = {
|
||||
"index": "jumbo_30y",
|
||||
"start_date": datetime.date(2024, 6, 1),
|
||||
"end_date": datetime.date(2024, 6, 4),
|
||||
}
|
||||
|
||||
fetcher = FredMortgageIndicesFetcher()
|
||||
result = fetcher.test(params, credentials)
|
||||
assert result is None
|
||||
|
||||
Reference in New Issue
Block a user