-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
728 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
from __future__ import annotations | ||
|
||
import abc | ||
import datetime as dt | ||
from collections.abc import Awaitable | ||
from typing import Final, Iterable | ||
|
||
from oxr import responses | ||
from oxr._types import Currency, Period | ||
|
||
_BASE_URL: Final = "https://openexchangerates.org/api" | ||
|
||
|
||
class BaseClient(abc.ABC): | ||
def __init__( | ||
self, | ||
app_id: str, | ||
*, | ||
base: Currency = "USD", | ||
base_url: str = _BASE_URL, | ||
) -> None: | ||
self._app_id = app_id | ||
self._base = base | ||
self._base_url = base_url | ||
|
||
@abc.abstractmethod | ||
def latest( | ||
self, | ||
base: str | None = None, | ||
symbols: Iterable[Currency] | None = None, | ||
show_alternative: bool = False, | ||
) -> responses.Rates | Awaitable[responses.Rates]: ... | ||
|
||
@abc.abstractmethod | ||
def historical( | ||
self, | ||
date: dt.date, | ||
base: str | None = None, | ||
symbols: Iterable[Currency] | None = None, | ||
show_alternative: bool = False, | ||
) -> responses.Rates | Awaitable[responses.Rates]: ... | ||
|
||
@abc.abstractmethod | ||
def convert( | ||
self, | ||
amount: float, | ||
from_: str, | ||
to: str, | ||
) -> responses.Conversion | Awaitable[responses.Conversion]: ... | ||
|
||
@abc.abstractmethod | ||
def time_series( | ||
self, | ||
start: dt.date, | ||
end: dt.date, | ||
symbols: Iterable[Currency] | None = None, | ||
base: str | None = None, | ||
show_alternative: bool = False, | ||
) -> responses.TimeSeries | Awaitable[responses.TimeSeries]: ... | ||
|
||
@abc.abstractmethod | ||
def olhc( | ||
self, | ||
start_time: dt.datetime, | ||
period: Period, | ||
base: str | None = None, | ||
symbols: Iterable[Currency] | None = None, | ||
show_alternative: bool = False, | ||
) -> responses.OHLC | Awaitable[responses.OHLC]: ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
from __future__ import annotations | ||
|
||
from typing import Literal | ||
|
||
from typing_extensions import TypeAlias | ||
|
||
# Valid currency codes. | ||
# fmt: off | ||
Currency: TypeAlias = Literal[ | ||
"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", | ||
"BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BRL", "BSD", "BTC", "BTN", "BWP", | ||
"BYN", "BZD", "CAD", "CDF", "CHF", "CLF", "CLP", "CNH", "CNY", "COP", "CRC", "CUC", | ||
"CUP", "CVE", "CZK", "DJF", "DKK", "DOP", "DZD", "EGP", "ERN", "ETB", "EUR", "FJD", | ||
"FKP", "GBP", "GEL", "GGP", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", | ||
"HRK", "HTG", "HUF", "IDR", "ILS", "IMP", "INR", "IQD", "IRR", "ISK", "JEP", "JMD", | ||
"JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", "LAK", | ||
"LBP", "LKR", "LRD", "LSL", "LYD", "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", | ||
"MRU", "MUR", "MVR", "MWK", "MXN", "MYR", "MZN", "NAD", "NGN", "NIO", "NOK", "NPR", | ||
"NZD", "OMR", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", "QAR", "RON", "RSD", | ||
"RUB", "RWF", "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", | ||
"SSP", "STD", "STN", "SVC", "SYP", "SZL", "THB", "TJS", "TMT", "TND", "TOP", "TRY", | ||
"TTD", "TWD", "TZS", "UAH", "UGX", "USD", "UYU", "UZS", "VEF", "VES", "VND", "VUV", | ||
"WST", "XAF", "XAG", "XAU", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "YER", "ZAR", | ||
"ZMW", "ZWL" | ||
] | ||
# fmt: on | ||
|
||
|
||
# Valid API endpoints. | ||
Endpoint: TypeAlias = Literal[ | ||
"latest", | ||
"historical", | ||
"convert", | ||
"time-series", | ||
"ohlc", | ||
"usage", | ||
] | ||
|
||
|
||
# Valid periods for the OHLC endpoint. | ||
Period: TypeAlias = Literal["1m", "5m", "15m", "30m", "1h", "12h", "1d", "1w", "1mo"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
"""Asynchronous client for Open Exchange Rates API.""" | ||
|
||
from __future__ import annotations | ||
|
||
from oxr.asynchronous.client import Client | ||
|
||
__all__ = ["Client"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
from __future__ import annotations | ||
|
||
import datetime as dt | ||
from collections.abc import Iterable | ||
from typing import Any, cast | ||
|
||
import aiohttp | ||
|
||
from oxr import responses | ||
from oxr._base import BaseClient | ||
from oxr._types import Currency, Endpoint, Period | ||
|
||
|
||
class Client(BaseClient): | ||
"""A asynchronous client for the Open Exchange Rates API.""" | ||
|
||
async def _get(self, endpoint: Endpoint, params: dict[str, Any]) -> dict[str, Any]: | ||
url = f"{self._base_url}/{endpoint}.json" | ||
async with aiohttp.ClientSession() as session, session.get(url, params=params) as response: | ||
response.raise_for_status() | ||
return await response.json() | ||
|
||
async def latest( | ||
self, | ||
base: str | None = None, | ||
symbols: Iterable[Currency] | None = None, | ||
show_alternative: bool = False, | ||
) -> responses.Rates: | ||
"""Get the latest exchange rates. | ||
Args: | ||
base: The base currency. | ||
symbols: The target currencies. | ||
show_alternative: Whether to show alternative currencies. | ||
Such as black market and digital currency rates. | ||
""" | ||
params = { | ||
"app_id": self._app_id, | ||
"base": base or self._base, | ||
"show_alternative": show_alternative, | ||
} | ||
if symbols is not None: | ||
params["symbols"] = ",".join(symbols) | ||
return cast(responses.Rates, await self._get("latest", params)) | ||
|
||
async def historical( | ||
self, | ||
date: dt.date, | ||
base: str | None = None, | ||
symbols: Iterable[Currency] | None = None, | ||
show_alternative: bool = False, | ||
) -> responses.Rates: | ||
"""Get historical exchange rates. | ||
Args: | ||
date: The date of the rates. | ||
base: The base currency. | ||
symbols: The target currencies. | ||
show_alternative: Whether to show alternative currencies. | ||
Such as black market and digital currency rates. | ||
""" | ||
params = { | ||
"base": base or self._base, | ||
"date": date.isoformat(), | ||
"show_alternative": show_alternative, | ||
} | ||
if symbols is not None: | ||
params["symbols"] = ",".join(symbols) | ||
return cast(responses.Rates, await self._get("historical", params)) | ||
|
||
async def convert( | ||
self, | ||
amount: float, | ||
from_: str, | ||
to: str, | ||
) -> responses.Conversion: | ||
"""Convert an amount between two currencies. | ||
Args: | ||
amount: The amount to convert. | ||
from_: The source currency. | ||
to: The target currency. | ||
date: The date of the rates to use. | ||
""" | ||
params = {"from": from_, "to": to, "amount": amount} | ||
return cast(responses.Conversion, await self._get("convert", params)) | ||
|
||
async def time_series( | ||
self, | ||
start: dt.date, | ||
end: dt.date, | ||
symbols: Iterable[Currency] | None = None, | ||
base: str | None = None, | ||
show_alternative: bool = False, | ||
) -> responses.TimeSeries: | ||
"""Get historical exchange rates for a range of dates. | ||
Args: | ||
start: The start date of the range. | ||
end: The end date of the range. | ||
symbols: The target currencies. | ||
base: The base currency. | ||
show_alternative: Whether to show alternative currencies. | ||
Such as black market and digital currency rates. | ||
""" | ||
params = { | ||
"start": start.isoformat(), | ||
"end": end.isoformat(), | ||
"show_alternative": show_alternative, | ||
} | ||
params["base"] = base or self._base | ||
if symbols is not None: | ||
params["symbols"] = ",".join(symbols) | ||
return cast(responses.TimeSeries, await self._get("time-series", params)) | ||
|
||
async def olhc( | ||
self, | ||
start_time: dt.datetime, | ||
period: Period, | ||
base: str | None = None, | ||
symbols: Iterable[Currency] | None = None, | ||
show_alternative: bool = False, | ||
) -> responses.OHLC: | ||
"""Get the latest open, low, high, and close rates for a currency. | ||
Args: | ||
base: The base currency. | ||
symbols: The target currencies. | ||
show_alternative: Whether to show alternative currencies. | ||
Such as black market and digital currency rates. | ||
""" | ||
params = { | ||
"start_time": start_time.isoformat(), | ||
"period": period, | ||
"show_alternative": show_alternative, | ||
} | ||
params["base"] = base or self._base | ||
if symbols is not None: | ||
params["symbols"] = ",".join(symbols) | ||
return cast(responses.OHLC, await self._get("ohlc", params)) | ||
|
||
async def usage(self) -> dict[str, Any]: | ||
"""Get the usage statistics for the API key.""" | ||
return await self._get("usage", {}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.