From ed40722ed81f5032064fb32cb0e0ca54203cbb87 Mon Sep 17 00:00:00 2001 From: Robert Tidball Date: Wed, 15 Apr 2026 17:10:22 +1000 Subject: [PATCH 1/2] feat: add calendar, data_catalogue, cot, commodities methods; migrate to v1 API paths - Add get_calendar, get_data_catalogue, get_cot, get_commodities to Client and AsyncClient - Add indicators param to get_fx_price for technical indicators (SMA, RSI, MACD, etc.) - Migrate all endpoints from legacy /api/{path} to /api/v1/{path} - Extract shared _request and _auth_headers helpers to reduce duplication - Add client-side API key validation for commodities endpoints - Update tests with coverage for all new methods and auth guards - Update README with full usage examples and API reference - Bump version to 1.1.0 --- README.md | 87 +++++++++++++++++++--- fxmacrodata/async_client.py | 142 +++++++++++++++++++++++++++-------- fxmacrodata/client.py | 144 +++++++++++++++++++++++++++--------- pyproject.toml | 2 +- tests/test_client.py | 109 ++++++++++++++++++++++++++- 5 files changed, 405 insertions(+), 79 deletions(-) diff --git a/README.md b/README.md index 9534ad9..ad2dfef 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ![License](https://img.shields.io/github/license/fxmacrodata/fxmacrodata?style=flat-square) ![Build](https://img.shields.io/github/actions/workflow/status/fxmacrodata/fxmacrodata/ci-cd.yml?style=flat-square&logo=github) -The **FXMacroData Python SDK** provides a simple and efficient interface for fetching **macroeconomic indicators** and **forex price history** from [FXMacroData](https://fxmacrodata.com/?utm_source=github&utm_medium=readme&utm_campaign=python_sdk). +The **FXMacroData Python SDK** provides a simple and efficient interface for fetching **macroeconomic indicators**, **forex prices**, **release calendars**, **COT positioning**, and **commodity prices** from [FXMacroData](https://fxmacrodata.com/?utm_source=github&utm_medium=readme&utm_campaign=python_sdk). It includes both synchronous and asynchronous clients, supports free USD endpoints, and offers a free Forex Price API for exchange rate data. @@ -14,12 +14,12 @@ It includes both synchronous and asynchronous clients, supports free USD endpoin ## 🌟 Features - Fetch: - - **Policy Rates** - - **Inflation & CPI** - - **GDP** - - **Unemployment** - - **Balance of Trade** - - **Government Bond Yields** + - **Macroeconomic indicators** — policy rates, inflation, GDP, unemployment, bond yields, and 100+ more + - **FX spot rates** with optional technical indicators (SMA, RSI, MACD, Bollinger Bands) + - **Release calendars** — upcoming economic data release dates + - **Data catalogue** — discover available indicators per currency + - **COT data** — CFTC Commitment of Traders positioning + - **Commodity prices** — gold, silver, platinum - Free access to **USD** macro data. - Free **Forex Price API** (`get_fx_price`). - API key required only for **non-USD** indicators. @@ -66,6 +66,26 @@ print(data) # Free Forex Price Endpoint fx = client.get_fx_price("usd", "gbp", start_date="2025-01-01") print(fx) + +# Forex with technical indicators +fx = client.get_fx_price("eur", "usd", indicators="sma_20,rsi_14,macd") +print(fx) + +# Release calendar +calendar = client.get_calendar("usd") +print(calendar) + +# Data catalogue — discover available indicators +catalogue = client.get_data_catalogue("usd") +print(catalogue) + +# COT positioning data +cot = client.get_cot("eur", start_date="2025-01-01") +print(cot) + +# Commodity prices +gold = client.get_commodities("gold", start_date="2026-01-01") +print(gold) ``` --- @@ -79,13 +99,29 @@ from fxmacrodata import AsyncClient async def main(): async with AsyncClient(api_key="YOUR_API_KEY") as client: # Fetch macroeconomic indicators - data = await client.get_indicator("eur", "cpi") + data = await client.get_indicator("eur", "inflation") print(data) # Free Forex Price Endpoint fx = await client.get_fx_price("usd", "jpy") print(fx) + # Release calendar + calendar = await client.get_calendar("usd") + print(calendar) + + # Data catalogue + catalogue = await client.get_data_catalogue("usd") + print(catalogue) + + # COT positioning + cot = await client.get_cot("jpy") + print(cot) + + # Commodity prices + gold = await client.get_commodities("gold") + print(gold) + asyncio.run(main()) ``` @@ -96,12 +132,39 @@ asyncio.run(main()) ### `get_indicator(currency, indicator, start_date=None, end_date=None)` Fetches macroeconomic indicator time series data. -- `currency`: `"usd"`, `"aud"`, `"eur"`, `"gbp"`, `"cad"`, `"nok"`, `"nzd"`, `"jpy"`, etc. -- `indicator`: `"policy_rate"`, `"cpi"`, `"inflation"`, `"gdp"`, `"unemployment"`, `"trade_balance"`, `"current_account"`, etc. +- `currency`: `"usd"`, `"aud"`, `"eur"`, `"gbp"`, `"cad"`, `"nok"`, `"nzd"`, `"jpy"`, `"brl"`, `"cny"`, `"dkk"`, `"pln"`, `"sek"`, `"sgd"`, etc. +- `indicator`: `"policy_rate"`, `"inflation"`, `"gdp"`, `"unemployment"`, `"trade_balance"`, `"current_account_balance"`, `"gov_bond_10y"`, etc. - **API key required for non-USD.** -### `get_fx_price(base, quote, start_date=None, end_date=None)` -Fetches historical FX prices between two currencies. **No API key needed for USD-based queries.** +### `get_fx_price(base, quote, start_date=None, end_date=None, indicators=None)` +Fetches daily FX spot rates (ECB reference rates) between two currencies. + +- `indicators`: Optional comma-separated technical indicators — `"sma_20"`, `"sma_50"`, `"sma_200"`, `"rsi_14"`, `"macd"`, `"ema_12"`, `"ema_26"`, `"bollinger_bands"`, or `"all"`. +- **No API key needed.** + +### `get_calendar(currency, indicator=None)` +Fetches upcoming economic data release dates for a currency. + +- `indicator`: Optional filter to a specific indicator slug. +- Returns `announcement_datetime` (Unix timestamp) and `release` (indicator slug). + +### `get_data_catalogue(currency, include_capabilities=False, include_coverage=False, indicator=None)` +Discovers available macroeconomic indicators for a given currency. + +- Returns a dict keyed by indicator slug with `name`, `unit`, `frequency`, and `has_official_forecast`. +- **API key required for non-USD.** + +### `get_cot(currency, start_date=None, end_date=None)` +Fetches CFTC Commitment of Traders (COT) positioning data. + +- Supported currencies: `AUD`, `CAD`, `CHF`, `EUR`, `GBP`, `JPY`, `NZD`, `USD`. +- **API key required for non-USD.** + +### `get_commodities(indicator, start_date=None, end_date=None)` +Fetches commodity price time series. + +- `indicator`: `"gold"`, `"silver"`, or `"platinum"`. +- **API key required.** --- diff --git a/fxmacrodata/async_client.py b/fxmacrodata/async_client.py index 82fe006..681def1 100644 --- a/fxmacrodata/async_client.py +++ b/fxmacrodata/async_client.py @@ -2,6 +2,7 @@ from typing import Optional from .exceptions import FXMacroDataError + class AsyncClient: BASE_URL = "https://fxmacrodata.com/api" @@ -18,6 +19,29 @@ async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: await self.session.close() self.session = None + async def _request(self, url: str, params: dict, headers: dict) -> dict: + if not self.session: + self.session = aiohttp.ClientSession() + async with self.session.get(url, headers=headers, params=params) as resp: + if resp.status != 200: + text = await resp.text() + raise FXMacroDataError(f"{resp.status} - {text}") + return await resp.json() + + def _auth_headers(self, currency: str, *, required: bool = True) -> dict: + headers: dict[str, str] = {} + if currency != "usd": + if required and not self.api_key: + raise FXMacroDataError( + f"API key required for {currency.upper()} endpoints." + ) + if self.api_key: + headers["X-API-Key"] = self.api_key + return headers + + # ------------------------------------------------------------------ + # Macroeconomic indicator time-series + # ------------------------------------------------------------------ async def get_indicator( self, currency: str, @@ -26,55 +50,111 @@ async def get_indicator( end_date: Optional[str] = None, ) -> dict: currency = currency.lower() - url = f"{self.BASE_URL}/{currency}/{indicator}" - - headers = {} - if currency != "usd": - if not self.api_key: - raise FXMacroDataError(f"API key required for {currency.upper()} endpoints.") - headers["X-API-Key"] = self.api_key - - params = {} + url = f"{self.BASE_URL}/v1/announcements/{currency}/{indicator}" + headers = self._auth_headers(currency) + params: dict[str, str] = {} if start_date: params["start_date"] = start_date if end_date: params["end_date"] = end_date + return await self._request(url, params, headers) - if not self.session: - self.session = aiohttp.ClientSession() - - async with self.session.get(url, headers=headers, params=params) as resp: - if resp.status != 200: - text = await resp.text() - raise FXMacroDataError(f"{resp.status} - {text}") - return await resp.json() - - # -------------------------------------------- - # NEW: Free Forex Endpoint (Async) - # -------------------------------------------- + # ------------------------------------------------------------------ + # FX spot rates (free) + # ------------------------------------------------------------------ async def get_fx_price( self, base: str, quote: str, start_date: Optional[str] = None, end_date: Optional[str] = None, + indicators: Optional[str] = None, ) -> dict: base = base.lower() quote = quote.lower() + url = f"{self.BASE_URL}/v1/forex/{base}/{quote}" + params: dict[str, str] = {} + if start_date: + params["start_date"] = start_date + if end_date: + params["end_date"] = end_date + if indicators: + params["indicators"] = indicators + return await self._request(url, params, {}) + + # ------------------------------------------------------------------ + # Release calendar + # ------------------------------------------------------------------ + async def get_calendar( + self, + currency: str, + indicator: Optional[str] = None, + ) -> dict: + currency = currency.lower() + url = f"{self.BASE_URL}/v1/calendar/{currency}" + headers = self._auth_headers(currency, required=False) + params: dict[str, str] = {} + if indicator: + params["indicator"] = indicator + return await self._request(url, params, headers) - url = f"{self.BASE_URL}/forex/{base}/{quote}" + # ------------------------------------------------------------------ + # Data catalogue — available indicators for a currency + # ------------------------------------------------------------------ + async def get_data_catalogue( + self, + currency: str, + include_capabilities: bool = False, + include_coverage: bool = False, + indicator: Optional[str] = None, + ) -> dict: + currency = currency.lower() + url = f"{self.BASE_URL}/v1/data_catalogue/{currency}" + headers = self._auth_headers(currency) + params: dict[str, str] = {} + if include_capabilities: + params["include_capabilities"] = "true" + if include_coverage: + params["include_coverage"] = "true" + if indicator: + params["indicator"] = indicator + return await self._request(url, params, headers) - params = {} + # ------------------------------------------------------------------ + # CFTC Commitment of Traders (COT) + # ------------------------------------------------------------------ + async def get_cot( + self, + currency: str, + start_date: Optional[str] = None, + end_date: Optional[str] = None, + ) -> dict: + currency = currency.lower() + url = f"{self.BASE_URL}/v1/cot/{currency}" + headers = self._auth_headers(currency) + params: dict[str, str] = {} if start_date: params["start_date"] = start_date if end_date: params["end_date"] = end_date + return await self._request(url, params, headers) - if not self.session: - self.session = aiohttp.ClientSession() - - async with self.session.get(url, params=params) as resp: - if resp.status != 200: - text = await resp.text() - raise FXMacroDataError(f"{resp.status} - {text}") - return await resp.json() + # ------------------------------------------------------------------ + # Commodities (gold, silver, platinum) + # ------------------------------------------------------------------ + async def get_commodities( + self, + indicator: str, + start_date: Optional[str] = None, + end_date: Optional[str] = None, + ) -> dict: + if not self.api_key: + raise FXMacroDataError("API key required for commodities endpoints.") + url = f"{self.BASE_URL}/v1/commodities/{indicator.lower()}" + headers: dict[str, str] = {"X-API-Key": self.api_key} + params: dict[str, str] = {} + if start_date: + params["start_date"] = start_date + if end_date: + params["end_date"] = end_date + return await self._request(url, params, headers) diff --git a/fxmacrodata/client.py b/fxmacrodata/client.py index 336551a..e582ac4 100644 --- a/fxmacrodata/client.py +++ b/fxmacrodata/client.py @@ -2,12 +2,36 @@ from typing import Optional from .exceptions import FXMacroDataError + class Client: BASE_URL = "https://fxmacrodata.com/api" def __init__(self, api_key: Optional[str] = None): self.api_key = api_key + def _request(self, url: str, params: dict, headers: dict) -> dict: + try: + response = requests.get(url, headers=headers, params=params) + except Exception as e: + raise FXMacroDataError(f"Request failed: {e}") + if response.status_code != 200: + raise FXMacroDataError(f"{response.status_code} - {response.text}") + return response.json() + + def _auth_headers(self, currency: str, *, required: bool = True) -> dict: + headers: dict[str, str] = {} + if currency != "usd": + if required and not self.api_key: + raise FXMacroDataError( + f"API key required for {currency.upper()} endpoints." + ) + if self.api_key: + headers["X-API-Key"] = self.api_key + return headers + + # ------------------------------------------------------------------ + # Macroeconomic indicator time-series + # ------------------------------------------------------------------ def get_indicator( self, currency: str, @@ -16,57 +40,111 @@ def get_indicator( end_date: Optional[str] = None, ) -> dict: currency = currency.lower() - url = f"{self.BASE_URL}/{currency}/{indicator}" - - headers = {} - if currency != "usd": - if not self.api_key: - raise FXMacroDataError(f"API key required for {currency.upper()} endpoints.") - headers["X-API-Key"] = self.api_key - - params = {} + url = f"{self.BASE_URL}/v1/announcements/{currency}/{indicator}" + headers = self._auth_headers(currency) + params: dict[str, str] = {} if start_date: params["start_date"] = start_date if end_date: params["end_date"] = end_date + return self._request(url, params, headers) - try: - response = requests.get(url, headers=headers, params=params) - except Exception as e: - raise FXMacroDataError(f"Request failed: {e}") - - if response.status_code != 200: - raise FXMacroDataError(f"{response.status_code} - {response.text}") - - return response.json() - - # -------------------------------------------- - # NEW: Free Forex Endpoint - # -------------------------------------------- + # ------------------------------------------------------------------ + # FX spot rates (free) + # ------------------------------------------------------------------ def get_fx_price( self, base: str, quote: str, start_date: Optional[str] = None, end_date: Optional[str] = None, + indicators: Optional[str] = None, ) -> dict: base = base.lower() quote = quote.lower() - - url = f"{self.BASE_URL}/forex/{base}/{quote}" - - params = {} + url = f"{self.BASE_URL}/v1/forex/{base}/{quote}" + params: dict[str, str] = {} if start_date: params["start_date"] = start_date if end_date: params["end_date"] = end_date + if indicators: + params["indicators"] = indicators + return self._request(url, params, {}) - try: - response = requests.get(url, params=params) - except Exception as e: - raise FXMacroDataError(f"Request failed: {e}") + # ------------------------------------------------------------------ + # Release calendar + # ------------------------------------------------------------------ + def get_calendar( + self, + currency: str, + indicator: Optional[str] = None, + ) -> dict: + currency = currency.lower() + url = f"{self.BASE_URL}/v1/calendar/{currency}" + headers = self._auth_headers(currency, required=False) + params: dict[str, str] = {} + if indicator: + params["indicator"] = indicator + return self._request(url, params, headers) - if response.status_code != 200: - raise FXMacroDataError(f"{response.status_code} - {response.text}") + # ------------------------------------------------------------------ + # Data catalogue — available indicators for a currency + # ------------------------------------------------------------------ + def get_data_catalogue( + self, + currency: str, + include_capabilities: bool = False, + include_coverage: bool = False, + indicator: Optional[str] = None, + ) -> dict: + currency = currency.lower() + url = f"{self.BASE_URL}/v1/data_catalogue/{currency}" + headers = self._auth_headers(currency) + params: dict[str, str] = {} + if include_capabilities: + params["include_capabilities"] = "true" + if include_coverage: + params["include_coverage"] = "true" + if indicator: + params["indicator"] = indicator + return self._request(url, params, headers) - return response.json() + # ------------------------------------------------------------------ + # CFTC Commitment of Traders (COT) + # ------------------------------------------------------------------ + def get_cot( + self, + currency: str, + start_date: Optional[str] = None, + end_date: Optional[str] = None, + ) -> dict: + currency = currency.lower() + url = f"{self.BASE_URL}/v1/cot/{currency}" + headers = self._auth_headers(currency) + params: dict[str, str] = {} + if start_date: + params["start_date"] = start_date + if end_date: + params["end_date"] = end_date + return self._request(url, params, headers) + + # ------------------------------------------------------------------ + # Commodities (gold, silver, platinum) + # ------------------------------------------------------------------ + def get_commodities( + self, + indicator: str, + start_date: Optional[str] = None, + end_date: Optional[str] = None, + ) -> dict: + if not self.api_key: + raise FXMacroDataError("API key required for commodities endpoints.") + url = f"{self.BASE_URL}/v1/commodities/{indicator.lower()}" + headers: dict[str, str] = {"X-API-Key": self.api_key} + params: dict[str, str] = {} + if start_date: + params["start_date"] = start_date + if end_date: + params["end_date"] = end_date + return self._request(url, params, headers) diff --git a/pyproject.toml b/pyproject.toml index ba158c8..ab748fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "fxmacrodata" -version = "1.0.0" +version = "1.1.0" description = "Python client for the FXMacroData API, providing forex macroeconomic data for traders, quants, and analysts." readme = "README.md" requires-python = ">=3.9" diff --git a/tests/test_client.py b/tests/test_client.py index ed65615..77434da 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -19,6 +19,10 @@ def client_without_key(): return Client(api_key=None) +# ------------------------------------------------------------------ +# get_indicator +# ------------------------------------------------------------------ + def test_usd_endpoint_no_key(client_without_key): """USD indicator endpoint should work without API key.""" result = client_without_key.get_indicator("usd", "gdp") @@ -44,10 +48,111 @@ def test_non_usd_endpoint_with_key(client_with_key): assert isinstance(result["data"], list) +# ------------------------------------------------------------------ +# get_fx_price +# ------------------------------------------------------------------ + def test_free_fx_price_endpoint(): """Free forex price endpoint should return data without API key.""" - client = Client() # no key needed for forex endpoints + client = Client() result = client.get_fx_price("usd", "gbp", start_date="2025-01-01") assert result["base"] == "USD" assert result["quote"] == "GBP" - assert isinstance(result["data"], dict) + assert isinstance(result["data"], list) + + +def test_fx_price_with_indicators(): + """Forex endpoint should accept technical indicator parameter.""" + client = Client() + result = client.get_fx_price( + "eur", "usd", start_date="2026-01-01", indicators="sma_20,rsi_14" + ) + assert result["base"] == "EUR" + assert result["quote"] == "USD" + assert "indicators" in result + + +# ------------------------------------------------------------------ +# get_calendar +# ------------------------------------------------------------------ + +def test_calendar_usd_no_key(client_without_key): + """Calendar should return release dates for USD without API key.""" + result = client_without_key.get_calendar("usd") + assert result["currency"] == "USD" + assert isinstance(result["data"], list) + + +def test_calendar_with_indicator_filter(client_without_key): + """Calendar should accept an indicator filter.""" + result = client_without_key.get_calendar("usd", indicator="inflation") + assert result["currency"] == "USD" + assert isinstance(result["data"], list) + + +# ------------------------------------------------------------------ +# get_data_catalogue +# ------------------------------------------------------------------ + +def test_data_catalogue_usd_no_key(client_without_key): + """USD data catalogue should work without API key.""" + result = client_without_key.get_data_catalogue("usd") + assert isinstance(result, dict) + assert "inflation" in result + + +def test_data_catalogue_non_usd_no_key(client_without_key): + """Non-USD data catalogue should raise FXMacroDataError without API key.""" + with pytest.raises(FXMacroDataError) as exc: + client_without_key.get_data_catalogue("aud") + assert "API key required" in str(exc.value) + + +def test_data_catalogue_non_usd_with_key(client_with_key): + """Non-USD data catalogue should succeed with API key.""" + result = client_with_key.get_data_catalogue("aud") + assert isinstance(result, dict) + assert "policy_rate" in result + + +# ------------------------------------------------------------------ +# get_cot +# ------------------------------------------------------------------ + +def test_cot_usd_no_key(client_without_key): + """USD COT data should work without API key.""" + result = client_without_key.get_cot("usd") + assert result["currency"] == "USD" + assert isinstance(result["data"], list) + + +def test_cot_non_usd_no_key(client_without_key): + """Non-USD COT should raise FXMacroDataError without API key.""" + with pytest.raises(FXMacroDataError) as exc: + client_without_key.get_cot("eur") + assert "API key required" in str(exc.value) + + +def test_cot_non_usd_with_key(client_with_key): + """Non-USD COT should succeed with API key.""" + result = client_with_key.get_cot("eur", start_date="2025-01-01") + assert result["currency"] == "EUR" + assert isinstance(result["data"], list) + + +# ------------------------------------------------------------------ +# get_commodities +# ------------------------------------------------------------------ + +def test_commodities_gold(client_with_key): + """Commodities gold endpoint should return price data.""" + result = client_with_key.get_commodities("gold", start_date="2026-01-01") + assert result["indicator"] == "gold" + assert isinstance(result["data"], list) + + +def test_commodities_no_key(client_without_key): + """Commodities should raise FXMacroDataError without API key.""" + with pytest.raises(FXMacroDataError) as exc: + client_without_key.get_commodities("gold") + assert "API key required" in str(exc.value) From bd2a765a0e0b897c56d7579be2333cc656b408fe Mon Sep 17 00:00:00 2001 From: Robert Tidball Date: Wed, 15 Apr 2026 17:19:09 +1000 Subject: [PATCH 2/2] docs: update supported currencies to 18, add get_data_catalogue hint --- README.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ad2dfef..f46e527 100644 --- a/README.md +++ b/README.md @@ -170,25 +170,31 @@ Fetches commodity price time series. ## 💹 Supported Currencies & Indicators +**18 currencies supported:** USD, EUR, GBP, JPY, AUD, CAD, CHF, NZD, HKD, SGD, NOK, PLN, SEK, DKK, BRL, CNY, KRW, MXN. + +The table below shows a sample of indicator coverage across four major currencies. Use `get_data_catalogue()` to discover the full list for any currency. + | Category | Metric | USD | EUR | AUD | GBP | |---------|--------|-----|-----|-----|-----| | **Economy** | GDP Growth | ✓ | ✓ | ✓ | ✓ | | | Inflation Rate | ✓ | ✓ | ✓ | ✓ | | | Trade Balance | ✓ | ✓ | ✓ | ✓ | | | Current Account Balance | ✓ | ✓ | ✓ | ✓ | +| | Retail Sales | ✓ | ✓ | ✓ | ✓ | +| | Industrial Production | ✓ | — | — | — | | **Labor Market** | Unemployment Rate | ✓ | ✓ | ✓ | ✓ | -| | Employment Level | ✓ | — | ✓ | ✓ | -| | Full-Time Employment | ✓ | — | ✓ | — | -| | Part-Time Employment | ✓ | — | ✓ | — | -| | Participation Rate | ✓ | — | ✓ | ✓ | +| | Employment Level | ✓ | ✓ | ✓ | ✓ | +| | Full-Time Employment | ✓ | ✓ | ✓ | — | +| | Part-Time Employment | ✓ | ✓ | ✓ | ✓ | +| | Participation Rate | ✓ | ✓ | ✓ | ✓ | | | Non-Farm Payrolls | ✓ | — | — | — | | **Monetary Policy** | Policy Rate | ✓ | ✓ | ✓ | ✓ | -| | Interbank Rate | ✓ | ✓ | ✓ | ✓ | -| **Government Bond Yields** | 2-Year Govt Bond | ✓ | ✓ | ✓ | — | -| | 3-Year Govt Bond | ✓ | ✓ | ✓ | — | +| | Risk-Free Rate | ✓ | ✓ | ✓ | ✓ | +| | Central Bank Assets | ✓ | — | ✓ | — | +| **Government Bond Yields** | 2-Year Govt Bond | ✓ | ✓ | ✓ | ✓ | | | 5-Year Govt Bond | ✓ | ✓ | ✓ | ✓ | | | 10-Year Govt Bond | ✓ | ✓ | ✓ | ✓ | -| | Inflation-Linked Bond | ✓ | — | ✓ | ✓ | +| | Inflation-Linked Bond | ✓ | ✓ | ✓ | ✓ | ---