From d3ce9e6f3974a11cf1f56648bc1c495010493e1d Mon Sep 17 00:00:00 2001 From: BloodyHell Date: Tue, 22 Oct 2024 20:02:27 +0200 Subject: [PATCH 01/12] Add columns as parameter to get_dataframe Allow the user to specify which columns to retrieve --- tiingo/api.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tiingo/api.py b/tiingo/api.py index 11f647c..5ca3862 100644 --- a/tiingo/api.py +++ b/tiingo/api.py @@ -262,6 +262,7 @@ class TiingoClient(RestClient): startDate=None, endDate=None, metric_name=None, + columns=None, frequency="daily", fmt="json", ): @@ -278,6 +279,7 @@ class TiingoClient(RestClient): tickers (string/list): One or more unique identifiers for a stock ticker. startDate (string): Start of ticker range in YYYY-MM-DD format. endDate (string): End of ticker range in YYYY-MM-DD format. + columns (string): Comma seperated parameter specifying which columns to retrieve (to include volume) metric_name (string): Optional parameter specifying metric to be returned for each ticker. In the event of a single ticker, this is optional and if not specified all of the available data will be returned. In the event of a list of tickers, @@ -315,6 +317,8 @@ class TiingoClient(RestClient): params["startDate"] = startDate if endDate: params["endDate"] = endDate + if columns: + params["columns"] = columns if pandas_is_installed: if type(tickers) is str: From 80b40c5be55819f096ee46b72e196ef5bb7b23c5 Mon Sep 17 00:00:00 2001 From: BloodyHell Date: Tue, 22 Oct 2024 20:06:41 +0200 Subject: [PATCH 02/12] Update HISTORY.rst --- HISTORY.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/HISTORY.rst b/HISTORY.rst index 3f010f5..a8fc461 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -5,6 +5,7 @@ History -------------------------------- * Dev: New config for readthedocs +* Feature: Add 'columns' parameter to 'get_dataframe' func (#1057) 0.15.6 (2024-05-25) -------------------------------- From 6b53725dcac65a26c5c1ca2b9c15a2d395ee896b Mon Sep 17 00:00:00 2001 From: Koen werklaptop Date: Wed, 23 Oct 2024 21:27:02 +0200 Subject: [PATCH 03/12] Add columns param to get_ticker_price, including tests --- .../ticker_price_with_multiple_columns.yaml | 27 +++++++++++++++ .../ticker_price_with_volume_column.yaml | 27 +++++++++++++++ tests/test_tiingo.py | 34 +++++++++++++++++-- tests/test_tiingo_pandas.py | 18 ++++++++++ tiingo/api.py | 7 ++-- 5 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/ticker_price_with_multiple_columns.yaml create mode 100644 tests/fixtures/ticker_price_with_volume_column.yaml diff --git a/tests/fixtures/ticker_price_with_multiple_columns.yaml b/tests/fixtures/ticker_price_with_multiple_columns.yaml new file mode 100644 index 0000000..352cc78 --- /dev/null +++ b/tests/fixtures/ticker_price_with_multiple_columns.yaml @@ -0,0 +1,27 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Token 0000000000000000000000000000000000000000] + Connection: [keep-alive] + Content-Type: [application/json] + User-Agent: [tiingo-python-client 0.5.0] + method: GET + uri: https://api.tiingo.com/tiingo/daily/GOOGL/prices?format=json&resampleFreq=daily&columns=open,high,low,close,volume + response: + body: {string: '[{"close":165.14,"date":"2024-10-22T00:00:00+00:00","high":165.77,"low":162.98,"open":162.98,"volume":16568121}]'} + headers: + Allow: ['GET, HEAD, OPTIONS'] + Content-Length: ['1982'] + Content-Type: [application/json] + Date: ['Wed, 23 Oct 2024 02:42:06 GMT'] + Server: [nginx/1.10.1] + Vary: ['Accept, Cookie'] + X-Frame-Options: [SAMEORIGIN] + status: {code: 200, message: OK} +version: 1 + + + diff --git a/tests/fixtures/ticker_price_with_volume_column.yaml b/tests/fixtures/ticker_price_with_volume_column.yaml new file mode 100644 index 0000000..fe8f1a7 --- /dev/null +++ b/tests/fixtures/ticker_price_with_volume_column.yaml @@ -0,0 +1,27 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Token 0000000000000000000000000000000000000000] + Connection: [keep-alive] + Content-Type: [application/json] + User-Agent: [tiingo-python-client 0.5.0] + method: GET + uri: https://api.tiingo.com/tiingo/daily/GOOGL/prices?format=json&resampleFreq=daily&columns=volume + response: + body: {string: '[{"date":"2024-10-22T00:00:00+00:00","volume":16568121}]'} + headers: + Allow: ['GET, HEAD, OPTIONS'] + Content-Length: ['1001'] + Content-Type: [application/json] + Date: ['Wed, 23 Oct 2024 02:42:06 GMT'] + Server: [nginx/1.10.1] + Vary: ['Accept, Cookie'] + X-Frame-Options: [SAMEORIGIN] + status: {code: 200, message: OK} +version: 1 + + + diff --git a/tests/test_tiingo.py b/tests/test_tiingo.py index 5500141..2cf4c65 100644 --- a/tests/test_tiingo.py +++ b/tests/test_tiingo.py @@ -55,7 +55,7 @@ class TestTickerPrices(TestCase): def test_ticker_metadata_as_object(self): metadata = self._client.get_ticker_metadata("GOOGL", fmt="object") assert metadata.ticker == "GOOGL" # Access property via ATTRIBUTE - assert metadata.name # (contrast with key access above + assert metadata.name # (contrast with key access above @vcr.use_cassette('tests/fixtures/ticker_price.yaml') def test_ticker_price(self): @@ -68,7 +68,7 @@ class TestTickerPrices(TestCase): def test_ticker_price(self): """Test that weekly frequency works""" prices = self._client.get_ticker_price("GOOGL", startDate='2018-01-05', - endDate='2018-01-19', frequency='weekly') + endDate='2018-01-19', frequency='weekly') assert len(prices) == 3 assert prices[0].get('adjClose') @@ -98,6 +98,34 @@ class TestTickerPrices(TestCase): rows = list(reader) assert len(rows) > 2 # more than 1 day of data + @vcr.use_cassette('tests/fixtures/ticker_price_with_volume_column.yaml') + def test_ticker_price_with_volume_column(self): + """Confirm that CSV endpoint works""" + prices = self._client.get_ticker_price("GOOGL", + columns="volume", + fmt='json') + assert len(prices) == 1 + assert prices[0].get('date') + assert not prices[0].get('high') + assert not prices[0].get('low') + assert not prices[0].get('open') + assert not prices[0].get('close') + assert prices[0].get('volume') + + @vcr.use_cassette('tests/fixtures/ticker_price_with_multiple_columns.yaml') + def test_ticker_price_with_multiple_columns(self): + """Confirm that CSV endpoint works""" + prices = self._client.get_ticker_price("GOOGL", + columns="open,high,low,close,volume", + fmt='json') + assert len(prices) == 1 + assert prices[0].get('date') + assert prices[0].get('high') + assert prices[0].get('low') + assert prices[0].get('open') + assert prices[0].get('close') + assert prices[0].get('volume') + @vcr.use_cassette('tests/fixtures/intraday_price.yaml') def test_intraday_ticker_price(self): """Test the EOD Prices Endpoint with data param""" @@ -149,6 +177,7 @@ class TestTickerPrices(TestCase): endDate="2018-01-02", frequency="1.5mins") + # tiingo/news class TestNews(TestCase): @@ -227,6 +256,7 @@ class TestNews(TestCase): with self.assertRaises(RestClientError): assert self._client.get_bulk_news(file_id="1", fmt="object") + # FUNDAMENTALS ENDPOINTS class TestFundamentals(TestCase): diff --git a/tests/test_tiingo_pandas.py b/tests/test_tiingo_pandas.py index add9731..da7b9d4 100644 --- a/tests/test_tiingo_pandas.py +++ b/tests/test_tiingo_pandas.py @@ -114,6 +114,24 @@ class TestTiingoWithPython(TestCase): frequency="30Min") self.assertGreater(len(prices), 1) + @vcr.use_cassette('tests/fixtures/ticker_price_with_volume_column.yaml') + def test_get_dataframe_with_volume_column(self): + """Confirm that CSV endpoint works""" + prices = self._client.get_dataframe("GOOGL", + columns="volume", + fmt='json') + assert len(prices) == 1 + assert len(prices.columns) == 2 + + @vcr.use_cassette('tests/fixtures/ticker_price_with_multiple_columns.yaml') + def test_get_dataframe_with_multiple_columns(self): + """Confirm that CSV endpoint works""" + prices = self._client.get_dataframe("GOOGL", + columns="open,high,low,close,volume", + fmt='json') + assert len(prices) == 1 + assert len(prices.columns) == 6 + def test_metric_name_column_error(self): with self.assertRaises(APIColumnNameError): self._client.get_dataframe(['GOOGL', 'AAPL'], startDate='2018-01-05', diff --git a/tiingo/api.py b/tiingo/api.py index 5ca3862..8c157fa 100644 --- a/tiingo/api.py +++ b/tiingo/api.py @@ -219,7 +219,7 @@ class TiingoClient(RestClient): return prices def get_ticker_price( - self, ticker, startDate=None, endDate=None, fmt="json", frequency="daily" + self, ticker, startDate=None, endDate=None, columns=None, fmt="json", frequency="daily" ): """By default, return latest EOD Composite Price for a stock ticker. On average, each feed contains 3 data sources. @@ -231,6 +231,7 @@ class TiingoClient(RestClient): ticker (string): Unique identifier for stock ticker startDate (string): Start of ticker range in YYYY-MM-DD format endDate (string): End of ticker range in YYYY-MM-DD format + columns (string): Comma separated parameter specifying which columns to retrieve (to include volume) fmt (string): 'csv' or 'json' frequency (string): Resample frequency """ @@ -244,6 +245,8 @@ class TiingoClient(RestClient): params["startDate"] = startDate if endDate: params["endDate"] = endDate + if columns: + params["columns"] = columns # TODO: evaluate whether to stream CSV to cache on disk, or # load as array in memory, or just pass plain text @@ -279,7 +282,7 @@ class TiingoClient(RestClient): tickers (string/list): One or more unique identifiers for a stock ticker. startDate (string): Start of ticker range in YYYY-MM-DD format. endDate (string): End of ticker range in YYYY-MM-DD format. - columns (string): Comma seperated parameter specifying which columns to retrieve (to include volume) + columns (string): Comma separated parameter specifying which columns to retrieve (to include volume) metric_name (string): Optional parameter specifying metric to be returned for each ticker. In the event of a single ticker, this is optional and if not specified all of the available data will be returned. In the event of a list of tickers, From 013ee37c90ca620362ab2f92a9308f0e5cfda1bb Mon Sep 17 00:00:00 2001 From: Koen werklaptop Date: Wed, 23 Oct 2024 21:38:05 +0200 Subject: [PATCH 04/12] Update docstring --- tiingo/api.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tiingo/api.py b/tiingo/api.py index 8c157fa..0dcc51a 100644 --- a/tiingo/api.py +++ b/tiingo/api.py @@ -231,7 +231,8 @@ class TiingoClient(RestClient): ticker (string): Unique identifier for stock ticker startDate (string): Start of ticker range in YYYY-MM-DD format endDate (string): End of ticker range in YYYY-MM-DD format - columns (string): Comma separated parameter specifying which columns to retrieve (to include volume) + columns (string): Optional comma separated parameter specifying which columns to retrieve. + By default, 'date', 'open', 'close', 'high' and 'low' are retrieved. 'volume' is an extra option. fmt (string): 'csv' or 'json' frequency (string): Resample frequency """ @@ -282,7 +283,8 @@ class TiingoClient(RestClient): tickers (string/list): One or more unique identifiers for a stock ticker. startDate (string): Start of ticker range in YYYY-MM-DD format. endDate (string): End of ticker range in YYYY-MM-DD format. - columns (string): Comma separated parameter specifying which columns to retrieve (to include volume) + columns (string): Optional comma separated parameter specifying which columns to retrieve. + By default, 'date', 'open', 'close', 'high' and 'low' are retrieved. 'volume' is an extra option. metric_name (string): Optional parameter specifying metric to be returned for each ticker. In the event of a single ticker, this is optional and if not specified all of the available data will be returned. In the event of a list of tickers, From 9277e3363bc94046d69957065ca4072575f37e4f Mon Sep 17 00:00:00 2001 From: Koen werklaptop Date: Mon, 28 Oct 2024 20:54:33 +0100 Subject: [PATCH 05/12] Expand HISTORY.rst entry for 'columns' param --- HISTORY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index a8fc461..d05389e 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -5,7 +5,7 @@ History -------------------------------- * Dev: New config for readthedocs -* Feature: Add 'columns' parameter to 'get_dataframe' func (#1057) +* Feature: Add 'columns' parameter to 'get_dataframe' and 'get_ticker_price' func (#1057) 0.15.6 (2024-05-25) -------------------------------- From 355c2812ffe8db9ee8a608be140fd96c90c2bb84 Mon Sep 17 00:00:00 2001 From: Koen werklaptop Date: Mon, 28 Oct 2024 20:54:53 +0100 Subject: [PATCH 06/12] Update test descriptions and simplify some assertions --- tests/test_tiingo.py | 4 ++-- tests/test_tiingo_pandas.py | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/test_tiingo.py b/tests/test_tiingo.py index 2cf4c65..6a6f5cc 100644 --- a/tests/test_tiingo.py +++ b/tests/test_tiingo.py @@ -100,7 +100,7 @@ class TestTickerPrices(TestCase): @vcr.use_cassette('tests/fixtures/ticker_price_with_volume_column.yaml') def test_ticker_price_with_volume_column(self): - """Confirm that CSV endpoint works""" + """Confirm that requesting a single column works""" prices = self._client.get_ticker_price("GOOGL", columns="volume", fmt='json') @@ -114,7 +114,7 @@ class TestTickerPrices(TestCase): @vcr.use_cassette('tests/fixtures/ticker_price_with_multiple_columns.yaml') def test_ticker_price_with_multiple_columns(self): - """Confirm that CSV endpoint works""" + """Confirm that requesting specific columns works""" prices = self._client.get_ticker_price("GOOGL", columns="open,high,low,close,volume", fmt='json') diff --git a/tests/test_tiingo_pandas.py b/tests/test_tiingo_pandas.py index da7b9d4..8c24ce7 100644 --- a/tests/test_tiingo_pandas.py +++ b/tests/test_tiingo_pandas.py @@ -116,21 +116,23 @@ class TestTiingoWithPython(TestCase): @vcr.use_cassette('tests/fixtures/ticker_price_with_volume_column.yaml') def test_get_dataframe_with_volume_column(self): - """Confirm that CSV endpoint works""" + """Confirm that requesting a single column works""" + requested_column = "volume" prices = self._client.get_dataframe("GOOGL", - columns="volume", + columns=requested_column, fmt='json') assert len(prices) == 1 - assert len(prices.columns) == 2 + assert len(prices.columns) == len(requested_column) + 1 @vcr.use_cassette('tests/fixtures/ticker_price_with_multiple_columns.yaml') def test_get_dataframe_with_multiple_columns(self): - """Confirm that CSV endpoint works""" + """Confirm that requesting specific columns works""" + requested_columns = "open,high,low,close,volume" prices = self._client.get_dataframe("GOOGL", - columns="open,high,low,close,volume", + columns=requested_columns, fmt='json') assert len(prices) == 1 - assert len(prices.columns) == 6 + assert len(prices.columns) == len(requested_columns.split(",")) + 1 def test_metric_name_column_error(self): with self.assertRaises(APIColumnNameError): From b9c12567c7ac543fe8e9dd607f80c33498e1f9ca Mon Sep 17 00:00:00 2001 From: Koen werklaptop Date: Mon, 28 Oct 2024 21:05:14 +0100 Subject: [PATCH 07/12] Add readme information --- README.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.rst b/README.rst index 66c5cba..ff1ed21 100644 --- a/README.rst +++ b/README.rst @@ -90,6 +90,9 @@ for each function for details.). # Get latest prices, based on 3+ sources as JSON, sampled weekly ticker_price = client.get_ticker_price("GOOGL", frequency="weekly") + # Get 1 min prices, including the "open", "close" and "volume" columns + ticker_price = client.get_ticker_price("GOOGL", frequency="1min", columns="open,close,volume") + # Get historical GOOGL prices from August 2017 as JSON, sampled daily historical_prices = client.get_ticker_price("GOOGL", fmt='json', @@ -154,11 +157,21 @@ To receive results in ``pandas`` format, use the ``get_dataframe()`` method: endDate='2018-05-31') + #Get a pd.DataFrame for a list of symbols for "close" and "volume" columns: + ticker_history = client.get_dataframe(['GOOGL', 'AAPL'], + frequency='weekly', + columns="close,volume" + startDate='2017-01-01', + endDate='2018-05-31') + + You can specify any of the end of day frequencies (daily, weekly, monthly, and annually) or any intraday frequency for both the ``get_ticker_price`` and ``get_dataframe`` methods. Weekly frequencies resample to the end of day on Friday, monthly frequencies resample to the last day of the month, and annually frequencies resample to the end of day on 12-31 of each year. The intraday frequencies are specified using an integer followed by "Min" or "Hour", for example "30Min" or "1Hour". +It's also possible to specify which columns you're interested in, for example: "open", "close", "low", "high" and "volume" (see https://www.tiingo.com/documentation/iex for future columns). + Cryptocurrency ----------------- From 90c35381abcff7b91d37b2c65903396031c53948 Mon Sep 17 00:00:00 2001 From: Cameron Yick Date: Sat, 2 Nov 2024 16:58:55 -0400 Subject: [PATCH 08/12] docs: use end of day rather than IEX link for supported columns --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index ff1ed21..bb15735 100644 --- a/README.rst +++ b/README.rst @@ -170,7 +170,8 @@ You can specify any of the end of day frequencies (daily, weekly, monthly, and a methods. Weekly frequencies resample to the end of day on Friday, monthly frequencies resample to the last day of the month, and annually frequencies resample to the end of day on 12-31 of each year. The intraday frequencies are specified using an integer followed by "Min" or "Hour", for example "30Min" or "1Hour". -It's also possible to specify which columns you're interested in, for example: "open", "close", "low", "high" and "volume" (see https://www.tiingo.com/documentation/iex for future columns). +It's also possible to specify which columns you're interested in, for example: "open", "close", "low", "high" and "volume" (see `End of Day response docs `_ for future columns). + Cryptocurrency ----------------- From f3b9e34f26b1cad45a8cb953687b3814d0df466b Mon Sep 17 00:00:00 2001 From: Cameron Yick Date: Sat, 2 Nov 2024 17:01:26 -0400 Subject: [PATCH 09/12] fix: apply black formatting --- tiingo/api.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tiingo/api.py b/tiingo/api.py index 0dcc51a..dca1a22 100644 --- a/tiingo/api.py +++ b/tiingo/api.py @@ -219,7 +219,13 @@ class TiingoClient(RestClient): return prices def get_ticker_price( - self, ticker, startDate=None, endDate=None, columns=None, fmt="json", frequency="daily" + self, + ticker, + startDate=None, + endDate=None, + columns=None, + fmt="json", + frequency="daily", ): """By default, return latest EOD Composite Price for a stock ticker. On average, each feed contains 3 data sources. From 027bd0d1c6f50ed07fa697a53d2646787a65bbf2 Mon Sep 17 00:00:00 2001 From: Koen werklaptop Date: Sun, 3 Nov 2024 19:32:33 +0100 Subject: [PATCH 10/12] Fix link to docs --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index ff1ed21..df64a84 100644 --- a/README.rst +++ b/README.rst @@ -170,7 +170,7 @@ You can specify any of the end of day frequencies (daily, weekly, monthly, and a methods. Weekly frequencies resample to the end of day on Friday, monthly frequencies resample to the last day of the month, and annually frequencies resample to the end of day on 12-31 of each year. The intraday frequencies are specified using an integer followed by "Min" or "Hour", for example "30Min" or "1Hour". -It's also possible to specify which columns you're interested in, for example: "open", "close", "low", "high" and "volume" (see https://www.tiingo.com/documentation/iex for future columns). +It's also possible to specify which columns you're interested in, for example: "open", "close", "low", "high" and "volume" (see https://www.tiingo.com/documentation/end-of-day for future columns). Cryptocurrency ----------------- From 27e634d807e3eca1e842f929e801c544377542e1 Mon Sep 17 00:00:00 2001 From: Koen werklaptop Date: Sun, 3 Nov 2024 20:01:54 +0100 Subject: [PATCH 11/12] Fix tests --- tests/test_tiingo.py | 4 +++- tests/test_tiingo_pandas.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_tiingo.py b/tests/test_tiingo.py index 6a6f5cc..4d56b5f 100644 --- a/tests/test_tiingo.py +++ b/tests/test_tiingo.py @@ -115,10 +115,12 @@ class TestTickerPrices(TestCase): @vcr.use_cassette('tests/fixtures/ticker_price_with_multiple_columns.yaml') def test_ticker_price_with_multiple_columns(self): """Confirm that requesting specific columns works""" + requested_columns = "open,high,low,close,volume" prices = self._client.get_ticker_price("GOOGL", - columns="open,high,low,close,volume", + columns=requested_columns, fmt='json') assert len(prices) == 1 + assert len(prices[0]) == len(requested_columns.split(',')) + 1 assert prices[0].get('date') assert prices[0].get('high') assert prices[0].get('low') diff --git a/tests/test_tiingo_pandas.py b/tests/test_tiingo_pandas.py index 8c24ce7..7e69d73 100644 --- a/tests/test_tiingo_pandas.py +++ b/tests/test_tiingo_pandas.py @@ -132,7 +132,7 @@ class TestTiingoWithPython(TestCase): columns=requested_columns, fmt='json') assert len(prices) == 1 - assert len(prices.columns) == len(requested_columns.split(",")) + 1 + assert len(prices.columns) == len(requested_columns.split(',')) def test_metric_name_column_error(self): with self.assertRaises(APIColumnNameError): From 803b2a9c5e27fdb5205fc2835fb29ed904e4b783 Mon Sep 17 00:00:00 2001 From: Koen werklaptop Date: Mon, 11 Nov 2024 20:23:29 +0100 Subject: [PATCH 12/12] Fix test_get_dataframe_with_volume_column, don't take the length of the string --- tests/test_tiingo_pandas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_tiingo_pandas.py b/tests/test_tiingo_pandas.py index 7e69d73..d2d2eae 100644 --- a/tests/test_tiingo_pandas.py +++ b/tests/test_tiingo_pandas.py @@ -122,7 +122,7 @@ class TestTiingoWithPython(TestCase): columns=requested_column, fmt='json') assert len(prices) == 1 - assert len(prices.columns) == len(requested_column) + 1 + assert len(prices.columns) == 1 @vcr.use_cassette('tests/fixtures/ticker_price_with_multiple_columns.yaml') def test_get_dataframe_with_multiple_columns(self):