diff --git a/CHANGELOG.md b/CHANGELOG.md index 8833a26..99b9161 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,20 @@ All notable changes to this project will be documented in this file. +## [2.0.0] - 2026-02-19 + +### Added +- OHLC endpoint (`currency.ohlc()`) for Open, High, Low, Close price data (Tier 3+) + - Parameters: `currency` (required), `date` (required), `base` (optional), `interval` (optional) + - Supported intervals: `5m`, `15m`, `30m`, `1h`, `4h`, `12h`, `1d` + +### Changed +- **Breaking:** Updated to API v2 (`https://currencyapi.net/api/v2/`) +- Updated README with OHLC endpoint documentation and usage examples + +### Removed +- **Breaking:** API v1 support dropped; all requests now use v2 + ## [1.1.0] - 2026-02-13 ### Added diff --git a/README.md b/README.md index 3473a39..a24cdf2 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# CurrencyApi Python wrapper +# CurrencyApi Python wrapper -[![PyPI version](https://badge.fury.io/py/currencyapinet.svg)](https://pypi.org/project/currencyapinet/) [![Coverage Status](https://coveralls.io/repos/github/houseofapis/currencyapi-python/badge.svg?branch=main)](https://coveralls.io/github/houseofapis/currencyapi-python?branch=main) +[![PyPI version](https://badge.fury.io/py/currencyapinet.svg)](https://pypi.org/project/currencyapinet/) [![Coverage Status](https://coveralls.io/repos/github/houseofapis/currencyapi-python/badge.svg?branch=main)](https://coveralls.io/github/houseofapis/currencyapi-python?branch=main) -CurrencyApi.net provides live currency rates via a REST API. A live currency feed for over 152 currencies, including physical (USD, GBP, EUR + more) and cryptos (Bitcoin, Litecoin, Ethereum + more). A JSON and XML currency api updated every 60 seconds. +CurrencyApi.net provides live currency rates via a REST API. A live currency feed for over 152 currencies, including physical (USD, GBP, EUR + more) and cryptos (Bitcoin, Litecoin, Ethereum + more). A JSON and XML currency api updated every 60 seconds. Features: @@ -13,13 +13,14 @@ Features: - Popular cryptocurrencies included; Bitcoin, Litecoin etc. - Convert currencies on the fly with the convert endpoint. - Historical currency rates back to year 2000. +- OHLC (Open, High, Low, Close) data for technical analysis (Tier 3+). - Easy to follow documentation Signup for a free or paid account here. ## This package is a: -Python wrapper for CurrencyApi.net endpoints. +Python wrapper for CurrencyApi.net endpoints (API v2). ## Developer Guide @@ -39,4 +40,128 @@ Alternatively keep reading below. ## Installation -View our Python SDK guide \ No newline at end of file +View our Python SDK guide + +``` +pip install currencyapinet +``` + +## Usage + +```python +from currencyapinet import Currency + +currency = Currency('YOUR_API_KEY') +``` + +--- + +### Rates + +Returns live currency rates for all supported currencies. Base currency defaults to USD. + +```python +result = currency.rates().get() + +# With optional base currency +result = currency.rates().base('EUR').get() + +# XML output +result = currency.rates().output('XML').get() +``` + +--- + +### Convert + +Converts an amount from one currency to another. + +```python +result = currency.convert().from_currency('USD').to_currency('EUR').amount(100).get() +``` + +--- + +### History + +Returns historical currency rates for a specific date. + +```python +result = currency.history().date('2023-12-25').get() + +# With optional base currency +result = currency.history().base('GBP').date('2023-12-25').get() +``` + +--- + +### Timeframe + +Returns historical currency rates for a date range (max 365 days). + +```python +result = currency.timeframe().start_date('2023-12-01').end_date('2023-12-31').get() + +# With optional base currency +result = currency.timeframe().base('GBP').start_date('2023-12-01').end_date('2023-12-31').get() +``` + +--- + +### Currencies + +Returns a list of all supported currencies. + +```python +result = currency.currencies().get() +``` + +--- + +### OHLC + +Returns OHLC (Open, High, Low, Close) data for a currency pair on a specific date. Requires a Tier 3 subscription. + +**Parameters:** + +| Parameter | Required | Description | +|------------|----------|-------------| +| `currency` | Yes | Target currency code (e.g. `EUR`, `GBP`, `BTC`) | +| `date` | Yes | Date in `YYYY-MM-DD` format (must be in the past) | +| `base` | No | Base currency code (defaults to `USD`) | +| `interval` | No | Time interval: `5m`, `15m`, `30m`, `1h`, `4h`, `12h`, `1d` (defaults to `1d`) | + +```python +# Basic request (1-day interval) +result = currency.ohlc().currency('EUR').date('2023-12-25').get() + +# With custom interval +result = currency.ohlc().currency('GBP').date('2023-12-25').interval('1h').get() + +# With custom base currency and interval +result = currency.ohlc().currency('JPY').date('2023-12-25').base('EUR').interval('4h').get() + +# XML output +result = currency.ohlc().currency('EUR').date('2023-12-25').output('XML').get() +``` + +**Example response:** + +```json +{ + "valid": true, + "base": "USD", + "quote": "EUR", + "date": "2023-12-25", + "interval": "1d", + "ohlc": [ + { + "start": "2023-12-25T00:00:00Z", + "open": 0.92000000000000, + "high": 0.92500000000000, + "low": 0.91800000000000, + "close": 0.92200000000000 + } + ] +} +``` diff --git a/currencyapinet/currency.py b/currencyapinet/currency.py index 9595d7f..5293c2f 100644 --- a/currencyapinet/currency.py +++ b/currencyapinet/currency.py @@ -3,6 +3,7 @@ from currencyapinet.endpoints.history import History from currencyapinet.endpoints.timeframe import Timeframe from currencyapinet.endpoints.currencies import Currencies +from currencyapinet.endpoints.ohlc import Ohlc class Currency: def __init__(self, api_key: str): @@ -10,15 +11,18 @@ def __init__(self, api_key: str): def rates(self) -> Rates: return Rates(self._api_key) - + def convert(self) -> Convert: return Convert(self._api_key) - + def history(self) -> History: return History(self._api_key) - + def timeframe(self) -> Timeframe: return Timeframe(self._api_key) - + def currencies(self) -> Currencies: return Currencies(self._api_key) + + def ohlc(self) -> Ohlc: + return Ohlc(self._api_key) diff --git a/currencyapinet/endpoints/__init__.py b/currencyapinet/endpoints/__init__.py index 1bddcf1..66478f2 100644 --- a/currencyapinet/endpoints/__init__.py +++ b/currencyapinet/endpoints/__init__.py @@ -2,4 +2,5 @@ from currencyapinet.endpoints.convert import Convert from currencyapinet.endpoints.history import History from currencyapinet.endpoints.timeframe import Timeframe -from currencyapinet.endpoints.currencies import Currencies \ No newline at end of file +from currencyapinet.endpoints.currencies import Currencies +from currencyapinet.endpoints.ohlc import Ohlc \ No newline at end of file diff --git a/currencyapinet/endpoints/endpoint.py b/currencyapinet/endpoints/endpoint.py index 884a7ab..a0188c8 100644 --- a/currencyapinet/endpoints/endpoint.py +++ b/currencyapinet/endpoints/endpoint.py @@ -1,6 +1,6 @@ import requests -API_VERSION = 'v1' +API_VERSION = 'v2' BASE_URL = 'https://currencyapi.net/api/' + API_VERSION + '/' DEFAULT_BASE = 'USD' DEFAULT_OUTPUT = 'JSON' diff --git a/currencyapinet/endpoints/ohlc.py b/currencyapinet/endpoints/ohlc.py new file mode 100644 index 0000000..18c8184 --- /dev/null +++ b/currencyapinet/endpoints/ohlc.py @@ -0,0 +1,23 @@ +from currencyapinet.endpoints.endpoint import Endpoint + +OHLC_ENDPOINT = 'ohlc' + +class Ohlc(Endpoint): + def __init__(self, api_key: str): + super().__init__(api_key, OHLC_ENDPOINT) + + def currency(self, currency: str): + self.add_param('currency', currency.upper()) + return self + + def date(self, date: str): + self.add_param('date', date) + return self + + def base(self, currency: str): + self._base(currency) + return self + + def interval(self, interval: str): + self.add_param('interval', interval) + return self diff --git a/setup.py b/setup.py index 7a8f355..9ad3a4f 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name="currencyapinet", - version="1.1.0", + version="2.0.0", packages=find_packages(exclude="tests"), description="Python wrapper for CurrencyApi.net", long_description=README_MD, diff --git a/tests/endpoints/test_ohlc.py b/tests/endpoints/test_ohlc.py new file mode 100644 index 0000000..2b77ba7 --- /dev/null +++ b/tests/endpoints/test_ohlc.py @@ -0,0 +1,70 @@ +from unittest import TestCase +from unittest.mock import Mock, patch +from currencyapinet.endpoints.ohlc import Ohlc +import requests + +class Test(TestCase): + + def setUp(self): + self.class_under_test = Ohlc('fakekey') + + def test_ohlc_currency(self): + self.class_under_test.currency('eUr') + self.assertEqual('EUR', self.class_under_test.param.get('currency')) + self.assertIsInstance(self.class_under_test.currency('EUR'), Ohlc) + + def test_ohlc_date(self): + self.class_under_test.date('2023-12-25') + self.assertEqual('2023-12-25', self.class_under_test.param.get('date')) + self.assertIsInstance(self.class_under_test.date('2023-12-25'), Ohlc) + + def test_ohlc_base(self): + self.class_under_test.base('gBp') + self.assertEqual('GBP', self.class_under_test.param.get('base')) + self.assertIsInstance(self.class_under_test.base('USD'), Ohlc) + + def test_ohlc_interval(self): + self.class_under_test.interval('1h') + self.assertEqual('1h', self.class_under_test.param.get('interval')) + self.assertIsInstance(self.class_under_test.interval('1d'), Ohlc) + + def test_ohlc_endpoint_set(self): + self.assertEqual('ohlc', self.class_under_test.endpoint) + + def test_ohlc__build_url_params(self): + self.assertDictEqual( + {'key': 'fakekey', 'output': 'JSON'}, + self.class_under_test._build_url_params() + ) + self.class_under_test.currency('EUR') + self.class_under_test.date('2023-12-25') + self.assertDictEqual( + {'key': 'fakekey', 'output': 'JSON', 'currency': 'EUR', 'date': '2023-12-25'}, + self.class_under_test._build_url_params() + ) + self.class_under_test.base('gbP') + self.class_under_test.interval('1h') + self.assertDictEqual( + {'key': 'fakekey', 'output': 'JSON', 'currency': 'EUR', 'date': '2023-12-25', 'base': 'GBP', 'interval': '1h'}, + self.class_under_test._build_url_params() + ) + + @patch.object(requests, 'get') + def test_ohlc_get(self, mock_requests_get): + mock_data = [{"id": 1}, {"id": 2}] + response_mock = Mock(return_value=mock_data) + mock_requests_get.return_value.json = response_mock + self.assertEqual( + mock_data, + self.class_under_test.get() + ) + + @patch.object(requests, 'get') + def test_ohlc_get_xml(self, mock_requests_get): + self.class_under_test.output('xMl') + mock_data = [{"id": 1}, {"id": 2}] + mock_requests_get.return_value.text = mock_data + self.assertEqual( + mock_data, + self.class_under_test.get() + ) diff --git a/tests/test_currency.py b/tests/test_currency.py index 8edb9d6..e45d76b 100644 --- a/tests/test_currency.py +++ b/tests/test_currency.py @@ -4,6 +4,7 @@ from currencyapinet.endpoints.timeframe import Timeframe from currencyapinet.endpoints.convert import Convert from currencyapinet.endpoints.currencies import Currencies +from currencyapinet.endpoints.ohlc import Ohlc from unittest import TestCase class Test(TestCase): @@ -25,4 +26,8 @@ def test_convert_method(self): def test_currencies_method(self): class_under_test = Currency('fakeKey') - self.assertIsInstance(class_under_test.currencies(), Currencies) \ No newline at end of file + self.assertIsInstance(class_under_test.currencies(), Currencies) + + def test_ohlc_method(self): + class_under_test = Currency('fakeKey') + self.assertIsInstance(class_under_test.ohlc(), Ohlc) \ No newline at end of file