diff --git a/alphavantage_api_client/__init__.py b/alphavantage_api_client/__init__.py index b0f81ac..d4bc8b3 100644 --- a/alphavantage_api_client/__init__.py +++ b/alphavantage_api_client/__init__.py @@ -1,5 +1,5 @@ from alphavantage_api_client.client import AlphavantageClient from alphavantage_api_client.models import GlobalQuote, Quote, AccountingReport, CompanyOverview, RealGDP, \ CsvNotSupported, TickerSearch, MarketStatus, MarketMovers, NewsAndSentiment, EarningsCalendar\ - , EarningsCalendarItem,IpoCalendarItem, IpoCalendar, CurrencyQuote + , EarningsCalendarItem,IpoCalendarItem, IpoCalendar, CurrencyQuote, Commodity from alphavantage_api_client.ticker import Ticker diff --git a/alphavantage_api_client/client.py b/alphavantage_api_client/client.py index 7976718..0096875 100644 --- a/alphavantage_api_client/client.py +++ b/alphavantage_api_client/client.py @@ -6,8 +6,8 @@ from .response_validation_rules import ValidationRuleChecks import json from alphavantage_api_client.models import GlobalQuote, Quote, AccountingReport, CompanyOverview, RealGDP, \ - CsvNotSupported, TickerSearch, MarketStatus, NewsAndSentiment, MarketMovers, EarningsCalendar\ - , IpoCalendarItem, IpoCalendar, CurrencyQuote + CsvNotSupported, TickerSearch, MarketStatus, NewsAndSentiment, MarketMovers, EarningsCalendar \ + , IpoCalendarItem, IpoCalendar, CurrencyQuote, Commodity import copy import logging import hashlib @@ -470,10 +470,9 @@ def get_crypto_intraday(self, event: dict) -> CurrencyQuote: } json_request = self.__create_api_request_from__(defaults, event) json_response = self.get_data_from_alpha_vantage(json_request, self.__retry__) - #print(json.dumps(json_response)) + # print(json.dumps(json_response)) return CurrencyQuote.parse_obj(json_response) - def get_crypto_daily(self, event: dict) -> CurrencyQuote: """ This API returns the daily historical time series for a digital currency (e.g., BTC) @@ -930,4 +929,251 @@ def get_forex_monthly(self, event: dict) -> CurrencyQuote: json_request = self.__create_api_request_from__(defaults, event) json_response = self.get_data_from_alpha_vantage(json_request, self.__retry__) - return CurrencyQuote.parse_obj(json_response) \ No newline at end of file + return CurrencyQuote.parse_obj(json_response) + + def get_crude_oil_wti_prices(self, event) -> Commodity: + """ + This API returns the West Texas Intermediate (WTI) crude oil prices in daily, weekly, and monthly horizons. + + Source: U.S. Energy Information Administration, Crude Oil Prices: West Texas Intermediate (WTI) - Cushing, + Oklahoma, retrieved from FRED, Federal Reserve Bank of St. Louis. This data feed uses the FRED® API but is not + endorsed or certified by the Federal Reserve Bank of St. Louis. By using this data feed, you agree to be + bound by the FRED® API Terms of Use. + Args: + event: + + Returns: + + """ + defaults = { + "function": "WTI", + "interval": "monthly", + "datatype": "json" + } + json_request = self.__create_api_request_from__(defaults, event) + json_response = self.get_data_from_alpha_vantage(json_request, self.__retry__) + + return Commodity.parse_obj(json_response) + + def get_crude_oil_brent_prices(self, event: dict) -> Commodity: + """ + This API returns the Brent (Europe) crude oil prices in daily, weekly, and monthly horizons. + Source: U.S. Energy Information Administration, Crude Oil Prices: Brent - Europe, retrieved from FRED, + Federal Reserve Bank of St. Louis. This data feed uses the FRED® API but is not endorsed or certified by the + Federal Reserve Bank of St. Louis. By using this data feed, you agree to be bound by the FRED® API Terms of Use. + Args: + event: dict + + Returns: Commodity + + """ + defaults = { + "function": "BRENT", + "interval": "monthly", + "datatype": "json" + } + json_request = self.__create_api_request_from__(defaults, event) + json_response = self.get_data_from_alpha_vantage(json_request, self.__retry__) + + return Commodity.parse_obj(json_response) + + def get_natural_gas_prices(self, event: dict) -> Commodity: + """ + This API returns the Henry Hub natural gas spot prices in daily, weekly, and monthly horizons. + Source: U.S. Energy Information Administration, Henry Hub Natural Gas Spot Price, retrieved from FRED, + Federal Reserve Bank of St. Louis. This data feed uses the FRED® API but is not endorsed or certified by the + Federal Reserve Bank of St. Louis. By using this data feed, you agree to be bound by the FRED® API Terms of Use. + Args: + event: dict + + Returns: Commodity + + """ + defaults = { + "function": "NATURAL_GAS", + "interval": "monthly", + "datatype": "json" + } + json_request = self.__create_api_request_from__(defaults, event) + json_response = self.get_data_from_alpha_vantage(json_request, self.__retry__) + + return Commodity.parse_obj(json_response) + + def get_copper_prices(self, event: dict) -> Commodity: + """ + This API returns the global price of copper in monthly, quarterly, and annual horizons. + Source: International Monetary Fund (IMF Terms of Use), Global price of Copper, retrieved from FRED, Federal + Reserve Bank of St. Louis. This data feed uses the FRED® API but is not endorsed or certified by the Federal + Reserve Bank of St. Louis. By using this data feed, you agree to be bound by the FRED® API Terms of Use. + Args: + event: + + Returns: + + """ + defaults = { + "function": "COPPER", + "interval": "monthly", + "datatype": "json" + } + json_request = self.__create_api_request_from__(defaults, event) + json_response = self.get_data_from_alpha_vantage(json_request, self.__retry__) + + return Commodity.parse_obj(json_response) + + def get_aluminum_prices(self, event: dict) -> Commodity: + """ + This API returns the global price of aluminum in monthly, quarterly, and annual horizons. + Source: International Monetary Fund (IMF Terms of Use), Global price of Aluminum, retrieved from FRED, Federal + Reserve Bank of St. Louis. This data feed uses the FRED® API but is not endorsed or certified by the Federal + Reserve Bank of St. Louis. By using this data feed, you agree to be bound by the FRED® API Terms of Use. + Args: + event: dict + + Returns: Commodity + + """ + defaults = { + "function": "ALUMINUM", + "interval": "monthly", + "datatype": "json" + } + json_request = self.__create_api_request_from__(defaults, event) + json_response = self.get_data_from_alpha_vantage(json_request, self.__retry__) + + return Commodity.parse_obj(json_response) + + def get_wheat_prices(self, event: dict) -> Commodity: + """ + This API returns the global price of wheat in monthly, quarterly, and annual horizons. + Source: International Monetary Fund (IMF Terms of Use), Global price of Wheat, retrieved from FRED, Federal + Reserve Bank of St. Louis. This data feed uses the FRED® API but is not endorsed or certified by the Federal + Reserve Bank of St. Louis. By using this data feed, you agree to be bound by the FRED® API Terms of Use. + Args: + event: dict + + Returns: Commodity + + """ + defaults = { + "function": "WHEAT", + "interval": "monthly", + "datatype": "json" + } + json_request = self.__create_api_request_from__(defaults, event) + json_response = self.get_data_from_alpha_vantage(json_request, self.__retry__) + + return Commodity.parse_obj(json_response) + + def get_corn_prices(self, event: dict) -> Commodity: + """ + This API returns the global price of corn in monthly, quarterly, and annual horizons. + Source: International Monetary Fund (IMF Terms of Use), Global price of Corn, retrieved from FRED, Federal + Reserve Bank of St. Louis. This data feed uses the FRED® API but is not endorsed or certified by the Federal + Reserve Bank of St. Louis. By using this data feed, you agree to be bound by the FRED® API Terms of Use. + Args: + event: dict + + Returns: Commodity + + """ + defaults = { + "function": "CORN", + "interval": "monthly", + "datatype": "json" + } + json_request = self.__create_api_request_from__(defaults, event) + json_response = self.get_data_from_alpha_vantage(json_request, self.__retry__) + + return Commodity.parse_obj(json_response) + + def get_cotton_prices(self, event: dict) -> Commodity: + """ + This API returns the global price of cotton in monthly, quarterly, and annual horizons. + Source: International Monetary Fund (IMF Terms of Use), Global price of Cotton, retrieved from FRED, Federal + Reserve Bank of St. Louis. This data feed uses the FRED® API but is not endorsed or certified by the Federal + Reserve Bank of St. Louis. By using this data feed, you agree to be bound by the FRED® API Terms of Use. + Args: + event: dict + + Returns: Commodity + + """ + defaults = { + "function": "COTTON", + "interval": "monthly", + "datatype": "json" + } + json_request = self.__create_api_request_from__(defaults, event) + json_response = self.get_data_from_alpha_vantage(json_request, self.__retry__) + + return Commodity.parse_obj(json_response) + + def get_sugar_prices(self, event: dict) -> Commodity: + """ + This API returns the global price of sugar in monthly, quarterly, and annual horizons. + Source: International Monetary Fund (IMF Terms of Use), Global price of Sugar, No. 11, World, retrieved from + FRED, Federal Reserve Bank of St. Louis. This data feed uses the FRED® API but is not endorsed or certified by + the Federal Reserve Bank of St. Louis. By using this data feed, you agree to be bound + by the FRED® API Terms of Use. + Args: + event: dict + + Returns: Commodity + + """ + defaults = { + "function": "SUGAR", + "interval": "monthly", + "datatype": "json" + } + json_request = self.__create_api_request_from__(defaults, event) + json_response = self.get_data_from_alpha_vantage(json_request, self.__retry__) + + return Commodity.parse_obj(json_response) + + def get_coffee_prices(self, event: dict) -> Commodity: + """ + This API returns the global price of coffee in monthly, quarterly, and annual horizons. + Source: International Monetary Fund (IMF Terms of Use), Global price of Coffee, Other Mild Arabica, + retrieved from FRED, Federal Reserve Bank of St. Louis. This data feed uses the FRED® API but is not + endorsed or certified by the Federal Reserve Bank of St. Louis. By using this data feed, you agree to be + bound by the FRED® API Terms of Use. + Args: + event: dict + + Returns: Commodity + + """ + defaults = { + "function": "COFFEE", + "interval": "monthly", + "datatype": "json" + } + json_request = self.__create_api_request_from__(defaults, event) + json_response = self.get_data_from_alpha_vantage(json_request, self.__retry__) + + return Commodity.parse_obj(json_response) + + def get_all_commodity_prices(self, event: dict) -> Commodity: + """ + This API returns the global price index of all commodities in monthly, quarterly, and annual temporal dimensions. + Source: International Monetary Fund (IMF Terms of Use), Global Price Index of All Commodities, retrieved + from FRED, Federal Reserve Bank of St. Louis. This data feed uses the FRED® API but is not endorsed or + certified by the Federal Reserve Bank of St. Louis. By using this data feed, you agree to be bound by + the FRED® API Terms of Use. + Args: + event: dict + + Returns: Commodity + + """ + defaults = { + "function": "ALL_COMMODITIES", + "interval": "monthly", + "datatype": "json" + } + json_request = self.__create_api_request_from__(defaults, event) + json_response = self.get_data_from_alpha_vantage(json_request, self.__retry__) + + return Commodity.parse_obj(json_response) \ No newline at end of file diff --git a/alphavantage_api_client/models.py b/alphavantage_api_client/models.py index 7592624..bb2071b 100644 --- a/alphavantage_api_client/models.py +++ b/alphavantage_api_client/models.py @@ -81,6 +81,12 @@ def normalize_fields(cls, values): or k.startswith("Realtime Currency Exchange Rate") else k: v for k, v in values.items() } +class Commodity(BaseResponse): + name: str + interval: str + unit: str + data: list[dict] + class Quote(BaseQuote): """ data is this clients abstraction of the response from alpha vantage. Time Series, Technical Indicator diff --git a/tests/test_multi_client_integration.py b/tests/test_multi_client_integration.py index 2661364..50c454f 100644 --- a/tests/test_multi_client_integration.py +++ b/tests/test_multi_client_integration.py @@ -179,12 +179,13 @@ def test_can_quote_daily(): } client = AlphavantageClient().should_retry_once() quote = client.get_daily_quote(event) - #print(quote.json()) + # print(quote.json()) assert not quote.limit_reached, f"limit_reached should not be true {quote.error_message}" assert quote.success, f"success is false {quote.error_message}" assert len(quote.data), f"Did not return data for this symbol {quote.symbol}" logging.warning(f" Successfully quoted symbol {event['symbol']} in JSON") + @pytest.mark.integration def test_can_quote_daily_adjusted(): event = { @@ -198,6 +199,7 @@ def test_can_quote_daily_adjusted(): assert len(quote.data), f"Did not return data for this symbol {quote.symbol}" logging.warning(f" Successfully quoted symbol {event['symbol']} in JSON") + @pytest.mark.integration def test_can_quote_weekly(): event = { @@ -211,6 +213,7 @@ def test_can_quote_weekly(): assert len(quote.data), f"Did not return data for this symbol {quote.symbol}" logging.warning(f" Successfully quoted symbol {event['symbol']} in JSON") + @pytest.mark.integration def test_can_quote_weekly_adjusted(): event = { @@ -224,6 +227,7 @@ def test_can_quote_weekly_adjusted(): assert len(quote.data), f"Did not return data for this symbol {quote.symbol}" logging.warning(f" Successfully quoted symbol {event['symbol']} in JSON") + @pytest.mark.integration def test_can_quote_monthly(): event = { @@ -237,6 +241,7 @@ def test_can_quote_monthly(): assert len(quote.data), f"Did not return data for this symbol {quote.symbol}" logging.warning(f" Successfully quoted symbol {event['symbol']} in JSON") + @pytest.mark.integration def test_can_quote_monthly_adjusted(): event = { @@ -251,6 +256,22 @@ def test_can_quote_monthly_adjusted(): logging.warning(f" Successfully quoted symbol {event['symbol']} in JSON") +@pytest.mark.integration_paid +def test_can_quote_crypto(): + event = { + "function": "CRYPTO_INTRADAY", + "symbol": "ETH", + "market": "USD", + "interval": "5min" + } + client = AlphavantageClient().should_retry_once() + quote = client.get_crypto_intraday(event) + assert not quote.limit_reached, f"limit_reached should not be true {quote.error_message}" + assert quote.success, f"success is false {quote.error_message}" + assert len(quote.data), "Data{} property is empty but should have information" + logging.warning(f" Successfully quoted cryptocurrency symbol {event['symbol']} in JSON") + + @pytest.mark.integration_paid def test_can_quote_crypto_csv(): event = { @@ -371,7 +392,6 @@ def test_can_not_query_csv_company_overview(): @pytest.mark.integration def test_can_not_query_income_statement(): - event = { "symbol": "tsla22354q2354" } @@ -538,11 +558,160 @@ def test_get_data_from_alpha_vantage(): @pytest.mark.integration_paid def test_get_fx_currency_data(): event = { - "function" : "CURRENCY_EXCHANGE_RATE", - "from_currency" : "JPY", - "to_currency" : "USD" + "function": "CURRENCY_EXCHANGE_RATE", + "from_currency": "JPY", + "to_currency": "USD" } client = AlphavantageClient().should_retry_once() results = client.get_data_from_alpha_vantage(event) # print(results) assert results["success"], f"FX Exchange call failed{results}" + + +@pytest.mark.integration +def test_get_crude_oil_wti(): + client = AlphavantageClient() + event = { + "interval": "daily" + } + crude_wti = client.get_crude_oil_wti_prices(event) + name = crude_wti.name + assert crude_wti.success, f"success was found to be False: {crude_wti.error_message}" + assert not crude_wti.limit_reached, f"limit_reached is true {crude_wti.error_message}" + assert len(crude_wti.data), f"data is empty, we should have {name} prices" + assert name == "Crude Oil Prices WTI", f"You are not testing {name}" + + +@pytest.mark.integration +def test_get_crude_oil_brent(): + client = AlphavantageClient() + event = { + "interval": "daily" + } + crude_brent = client.get_crude_oil_brent_prices(event) + name = crude_brent.name + assert crude_brent.success, f"success was found to be False: {crude_brent.error_message}" + assert not crude_brent.limit_reached, f"limit_reached is true {crude_brent.error_message}" + assert len(crude_brent.data), f"data is empty, we should have {name} prices" + assert name == "Crude Oil Prices Brent", f"You are not testing {name}" + + +@pytest.mark.integration +def test_get_natural_gas(): + client = AlphavantageClient() + event = { + "interval": "daily" + } + natural_gas = client.get_natural_gas_prices(event) + name = natural_gas.name + assert natural_gas.success, f"success was found to be False: {natural_gas.error_message}" + assert not natural_gas.limit_reached, f"limit_reached is true {natural_gas.error_message}" + assert len(natural_gas.data), f"data is empty, we should have {name} prices" + assert name == "Henry Hub Natural Gas Spot Price", f"You are not testing {name}" + + +@pytest.mark.integration +def test_get_copper(): + client = AlphavantageClient() + event = { + "interval": "daily" + } + copper = client.get_copper_prices(event) + name = copper.name + assert copper.success, f"success was found to be False: {copper.error_message}" + assert not copper.limit_reached, f"limit_reached is true {copper.error_message}" + assert len(copper.data), f"data is empty, we should have {name} prices" + assert name == "Global Price of Copper", f"You are not testing {name}" + + +def test_get_aluminum(): + client = AlphavantageClient() + event = { + "interval": "daily" + } + aluminum = client.get_aluminum_prices(event) + name = aluminum.name + assert aluminum.success, f"success was found to be False: {aluminum.error_message}" + assert not aluminum.limit_reached, f"limit_reached is true {aluminum.error_message}" + assert len(aluminum.data), f"data is empty, we should have {name} prices" + assert name == "Global Price of Aluminum", f"You are not testing {name}" + + +def test_get_wheat(): + client = AlphavantageClient() + event = { + "interval": "daily" + } + wheat = client.get_wheat_prices(event) + name = wheat.name + assert wheat.success, f"success was found to be False: {wheat.error_message}" + assert not wheat.limit_reached, f"limit_reached is true {wheat.error_message}" + assert len(wheat.data), f"data is empty, we should have {name} prices" + assert name == "Global Price of Wheat", f"You are not testing {name}" + + +def test_get_corn(): + client = AlphavantageClient() + event = { + "interval": "daily" + } + corn = client.get_corn_prices(event) + name = corn.name + assert corn.success, f"success was found to be False: {corn.error_message}" + assert not corn.limit_reached, f"limit_reached is true {corn.error_message}" + assert len(corn.data), f"data is empty, we should have {name} prices" + assert name == "Global Price of Corn", f"You are not testing {name}" + + +def test_get_cotton(): + client = AlphavantageClient() + event = { + "interval": "daily" + } + cotton = client.get_cotton_prices(event) + name = cotton.name + assert cotton.success, f"success was found to be False: {cotton.error_message}" + assert not cotton.limit_reached, f"limit_reached is true {cotton.error_message}" + assert len(cotton.data), f"data is empty, we should have {name} prices" + assert name == "Global Price of Cotton", f"You are not testing {name}" + + +def test_get_sugar(): + client = AlphavantageClient() + event = { + "interval": "daily" + } + sugar = client.get_sugar_prices(event) + name = sugar.name + assert sugar.success, f"success was found to be False: {sugar.error_message}" + assert not sugar.limit_reached, f"limit_reached is true {sugar.error_message}" + assert len(sugar.data), f"data is empty, we should have {name} prices" + assert name == "Global Price of Sugar", f"You are not testing {name}" + + +@pytest.mark.integration +def test_get_coffee_commodity(): + client = AlphavantageClient() + event = { + "interval": "daily" + } + coffee = client.get_coffee_prices(event) + name = coffee.name + assert coffee.success, f"success was found to be False: {coffee.error_message}" + assert not coffee.limit_reached, f"limit_reached is true {coffee.error_message}" + assert len(coffee.data), f"data is empty, we should have {name} prices" + assert name == "Global Price of Coffee", f"You are not testing {name}" + + +@pytest.mark.integration +def test_get_all_commodities(): + client = AlphavantageClient() + event = { + "interval": "daily" + } + all_commodities = client.get_all_commodity_prices(event) + name = all_commodities.name + assert all_commodities.success, f"success was found to be False: {all_commodities.error_message}" + assert not all_commodities.limit_reached, f"limit_reached is true {all_commodities.error_message}" + assert len(all_commodities.data), f"data is empty, we should have {name} prices" + assert name == "Global Price Index of All Commodities", f"You are not testing {name}"