From 6413845e57c77a2ccf80c6e8657abed032dbf298 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 07:26:01 -0600 Subject: [PATCH 01/59] Initial test suite add using VCRpy to cache requests --- requirements.txt | 3 ++- tests/__init__.py | 2 ++ tests/cassettes/test_getProducts | 38 +++++++++++++++++++++++++++++++ tests/test_GDAXPublicClient.py | 39 ++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 tests/__init__.py create mode 100644 tests/cassettes/test_getProducts create mode 100644 tests/test_GDAXPublicClient.py diff --git a/requirements.txt b/requirements.txt index 8b49833a..3cdb369c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ requests>=2.5 -websocket-client>=0.37.0 \ No newline at end of file +websocket-client>=0.37.0 +vcrpy>=1.10.4 \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..136d8eb3 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,2 @@ +__all__ = ['publicclient'] +from GDAX import PublicClient \ No newline at end of file diff --git a/tests/cassettes/test_getProducts b/tests/cassettes/test_getProducts new file mode 100644 index 00000000..63538b27 --- /dev/null +++ b/tests/cassettes/test_getProducts @@ -0,0 +1,38 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products + response: + body: + string: !!binary | + H4sIAAAAAAAAA7STTwuCMBiHv8t71lxXj7ORBw9R8xQhpjsMcpV/IIu+eyxpbbCBUl7f/bbfw/Oy + /QN4CSFgGvlrvAEPjnnDsqKrayaKfjgBD67duTXGWrjiImv4nUEIaIGWapzfPuMlQgipV7goalYx + 0X4vlLy5nPI+E3nFhs5AFjw9DY+k2/F4WngePFmg8AiN/XS3suARGtvwtPBIvEmAhMaBrDAAB1Mj + AbXwz4AIuRhli2JMaOSQmNh3PLPEhEamRAlol+gAnF+iZDQkYqdE/C+JEz/KW+HhBQAA//8DAMTR + GkZoBAAA + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31d7a8ddc8973852-ATL] + connection: [keep-alive] + content-encoding: [gzip] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 13:12:08 GMT'] + etag: [W/"468-plj3qTuGFbXRJk5e/mRc7g"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=d8d22025090fa296a284d0db5738537151483794728; expires=Sun, + 07-Jan-18 13:12:08 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py new file mode 100644 index 00000000..0a9b7d75 --- /dev/null +++ b/tests/test_GDAXPublicClient.py @@ -0,0 +1,39 @@ +import unittest +from GDAX import PublicClient + +import vcr + +my_vcr = vcr.VCR( + serializer='yaml', + cassette_library_dir='tests/cassettes', + record_mode='once', + match_on=['uri', 'method'], +) + +class TestGDAXPublicClient(unittest.TestCase): + def setUp(self): + self.GDAX = PublicClient() + + @my_vcr.use_cassette() + def test_getProducts(self): + #Results from direct run on Jan 7, 2017 + correct = [ + {"id": "BTC-GBP", "base_currency": "BTC", "quote_currency": "GBP", "base_min_size": "0.01", + "base_max_size": "10000", "quote_increment": "0.01", "display_name": "BTC/GBP"}, + {"id": "BTC-EUR", "base_currency": "BTC", "quote_currency": "EUR", "base_min_size": "0.01", + "base_max_size": "10000", "quote_increment": "0.01", "display_name": "BTC/EUR"}, + {"id": "ETH-USD", "base_currency": "ETH", "quote_currency": "USD", "base_min_size": "0.01", + "base_max_size": "1000000", "quote_increment": "0.01", "display_name": "ETH/USD"}, + {"id": "ETH-BTC", "base_currency": "ETH", "quote_currency": "BTC", "base_min_size": "0.01", + "base_max_size": "1000000", "quote_increment": "0.00001", "display_name": "ETH/BTC"}, + {"id": "LTC-USD", "base_currency": "LTC", "quote_currency": "USD", "base_min_size": "0.01", + "base_max_size": "1000000", "quote_increment": "0.01", "display_name": "LTC/USD"}, + {"id": "LTC-BTC", "base_currency": "LTC", "quote_currency": "BTC", "base_min_size": "0.01", + "base_max_size": "1000000", "quote_increment": "0.00001", "display_name": "LTC/BTC"}, + {"id": "BTC-USD", "base_currency": "BTC", "quote_currency": "USD", "base_min_size": "0.01", + "base_max_size": "10000", "quote_increment": "0.01", "display_name": "BTC/USD"} + ] + self.assertEqual(self.GDAX.getProducts(), correct) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From 2123d000a5d26a5501d83765032a175928947143 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 07:42:54 -0600 Subject: [PATCH 02/59] Updated setup.py for test suite --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index 45bd856f..fe166e2f 100644 --- a/setup.py +++ b/setup.py @@ -5,6 +5,7 @@ install_requires = [ 'requests>=2.5', 'websocket-client>=0.37.0', + 'vcrpy>=1.10.4' ] setup( @@ -16,6 +17,7 @@ url = 'https://github.com/danpaquin/coinbase-gdax-python', packages = find_packages(), install_requires = install_requires, + test_suite="tests", description = 'A Python client for the GDAX API', download_url = 'https://github.com/danpaquin/coinbase-gdax-python/archive/master.zip', keywords = ['coinbase', 'gdax', 'bitcoin', 'ethereum', 'client', 'api', 'exchange', 'crypto', 'currency'], From 4155fe05f774abea6b5ab4a23703a9d8fc2d92c2 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 08:25:44 -0600 Subject: [PATCH 03/59] Move Public api cassettes to public sub-folder --- tests/cassettes/{ => public}/test_getProducts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename tests/cassettes/{ => public}/test_getProducts (85%) diff --git a/tests/cassettes/test_getProducts b/tests/cassettes/public/test_getProducts similarity index 85% rename from tests/cassettes/test_getProducts rename to tests/cassettes/public/test_getProducts index 63538b27..4f499f19 100644 --- a/tests/cassettes/test_getProducts +++ b/tests/cassettes/public/test_getProducts @@ -23,15 +23,15 @@ interactions: access-control-expose-headers: ['cb-before, cb-after'] access-control-max-age: ['7200'] cache-control: ['public, max-age=1'] - cf-ray: [31d7a8ddc8973852-ATL] + cf-ray: [31d813936ee4389a-ATL] connection: [keep-alive] content-encoding: [gzip] content-type: [application/json; charset=utf-8] - date: ['Sat, 07 Jan 2017 13:12:08 GMT'] + date: ['Sat, 07 Jan 2017 14:24:59 GMT'] etag: [W/"468-plj3qTuGFbXRJk5e/mRc7g"] server: [cloudflare-nginx] - set-cookie: ['__cfduid=d8d22025090fa296a284d0db5738537151483794728; expires=Sun, - 07-Jan-18 13:12:08 GMT; path=/; domain=.gdax.com; HttpOnly'] + set-cookie: ['__cfduid=d16fd765d4d4747aa1441dba01c6deb721483799099; expires=Sun, + 07-Jan-18 14:24:59 GMT; path=/; domain=.gdax.com; HttpOnly'] strict-transport-security: [max-age=15552000; includeSubDomains; preload] x-content-type-options: [nosniff] status: {code: 200, message: OK} From 333d28f01e2e4fff2f5626b6645a7aba82863797 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 08:26:16 -0600 Subject: [PATCH 04/59] Move Public api cassettes to public sub-folder --- tests/test_GDAXPublicClient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index 0a9b7d75..6a0d10e3 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -5,7 +5,7 @@ my_vcr = vcr.VCR( serializer='yaml', - cassette_library_dir='tests/cassettes', + cassette_library_dir='tests/cassettes/public', record_mode='once', match_on=['uri', 'method'], ) From 21ecf6ccea6a6cc2ac7a6ae3f3e377dbe0c492e4 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 11:43:27 -0600 Subject: [PATCH 05/59] Add test for GetProductOrderBook Level 1 --- tests/test_GDAXPublicClient.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index 6a0d10e3..e4d0dcb3 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -10,13 +10,24 @@ match_on=['uri', 'method'], ) +#NOTE THAT THESE TESTS ARE FOCUSED ON BTC-USD +TEST_PRODUCT_ID = 'BTC-USD' + class TestGDAXPublicClient(unittest.TestCase): def setUp(self): - self.GDAX = PublicClient() + #Only testing for BTC-USD + self.GDAX = PublicClient(product_id=TEST_PRODUCT_ID) + + def test_PublicClientInitCorrectProductID(self): + self.assertEquals(self.GDAX.productId,TEST_PRODUCT_ID) + + def test_PublicClientInitWrongProductID(self): + EurProductIdClient = PublicClient(product_id="BTC-EUR") + self.assertNotEquals(self.GDAX,EurProductIdClient) @my_vcr.use_cassette() def test_getProducts(self): - #Results from direct run on Jan 7, 2017 + #Results from direct browser run on Jan 7, 2017 correct = [ {"id": "BTC-GBP", "base_currency": "BTC", "quote_currency": "GBP", "base_min_size": "0.01", "base_max_size": "10000", "quote_increment": "0.01", "display_name": "BTC/GBP"}, @@ -35,5 +46,16 @@ def test_getProducts(self): ] self.assertEqual(self.GDAX.getProducts(), correct) + @my_vcr.use_cassette() + def test_getProductOrderBook_level_1(self): + #test for first level depth + test_depth = 1 + + #Results from direct browser run on Jan 7, 2017 + correct_sequence = 1974627815 + results = self.GDAX.getProductOrderBook(level=test_depth,\ + product=TEST_PRODUCT_ID) + self.assertEqual(results['sequence'], correct_sequence) + if __name__ == '__main__': unittest.main() \ No newline at end of file From 8c1a803f06f3e7b371c4cf55caa5d7ba3a3a8986 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 11:45:37 -0600 Subject: [PATCH 06/59] Add test for GetProductOrderBook Levels 2 and 3 --- tests/test_GDAXPublicClient.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index e4d0dcb3..7684cdba 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -51,10 +51,29 @@ def test_getProductOrderBook_level_1(self): #test for first level depth test_depth = 1 - #Results from direct browser run on Jan 7, 2017 + #Results from run on Jan 7, 2017 correct_sequence = 1974627815 - results = self.GDAX.getProductOrderBook(level=test_depth,\ - product=TEST_PRODUCT_ID) + results = self.GDAX.getProductOrderBook(level=test_depth, product=TEST_PRODUCT_ID) + self.assertEqual(results['sequence'], correct_sequence) + + @my_vcr.use_cassette() + def test_getProductOrderBook_level_2(self): + #test for second level depth + test_depth = 2 + + #Results from direct browser run on Jan 7, 2017 + correct_sequence = 1974769472 + results = self.GDAX.getProductOrderBook(level=test_depth, product=TEST_PRODUCT_ID) + self.assertEqual(results['sequence'], correct_sequence) + + @my_vcr.use_cassette() + def test_getProductOrderBook_level_3(self): + #test for second level depth + test_depth = 3 + + #Results from direct browser run on Jan 7, 2017 + correct_sequence = 1974771030 + results = self.GDAX.getProductOrderBook(level=test_depth, product=TEST_PRODUCT_ID) self.assertEqual(results['sequence'], correct_sequence) if __name__ == '__main__': From 1d17a352414238952816185b95f8a1d2db3cc607 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 11:52:23 -0600 Subject: [PATCH 07/59] Add test for GetProductOrderBook, invalid product and levels - TODO:Consider putting error catching in library rather than relying on GDAX for error catching --- tests/test_GDAXPublicClient.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index 7684cdba..b44c3d3f 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -68,7 +68,7 @@ def test_getProductOrderBook_level_2(self): @my_vcr.use_cassette() def test_getProductOrderBook_level_3(self): - #test for second level depth + #test for third level depth test_depth = 3 #Results from direct browser run on Jan 7, 2017 @@ -76,5 +76,23 @@ def test_getProductOrderBook_level_3(self): results = self.GDAX.getProductOrderBook(level=test_depth, product=TEST_PRODUCT_ID) self.assertEqual(results['sequence'], correct_sequence) + # TODO: it may be better functionality for library to throw exception for invalid level + @my_vcr.use_cassette() + def test_getProductOrderBook_level_bad(self): + #test for non-existent level depth + test_depth = 4 + + results = self.GDAX.getProductOrderBook(level=test_depth, product=TEST_PRODUCT_ID) + self.assertEqual(results['message'], "Invalid level") + + # TODO: it may be better functionality for library to throw exception for invalid product + @my_vcr.use_cassette() + def test_getProductOrderBook_product_bad(self): + #test for non-existent level depth + test_depth = 1 + + results = self.GDAX.getProductOrderBook(level=test_depth, product="BTC-USR") + self.assertEqual(results['message'], "NotFound") + if __name__ == '__main__': unittest.main() \ No newline at end of file From 41100fdbd544a1e38910d9035596a012019d4ff7 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 12:13:31 -0600 Subject: [PATCH 08/59] Add test for getProductTicker --- tests/test_GDAXPublicClient.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index b44c3d3f..8fa3a81c 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -52,7 +52,7 @@ def test_getProductOrderBook_level_1(self): test_depth = 1 #Results from run on Jan 7, 2017 - correct_sequence = 1974627815 + correct_sequence = 1974671651 results = self.GDAX.getProductOrderBook(level=test_depth, product=TEST_PRODUCT_ID) self.assertEqual(results['sequence'], correct_sequence) @@ -62,7 +62,7 @@ def test_getProductOrderBook_level_2(self): test_depth = 2 #Results from direct browser run on Jan 7, 2017 - correct_sequence = 1974769472 + correct_sequence = 1974823003 results = self.GDAX.getProductOrderBook(level=test_depth, product=TEST_PRODUCT_ID) self.assertEqual(results['sequence'], correct_sequence) @@ -72,7 +72,7 @@ def test_getProductOrderBook_level_3(self): test_depth = 3 #Results from direct browser run on Jan 7, 2017 - correct_sequence = 1974771030 + correct_sequence = 1974823009 results = self.GDAX.getProductOrderBook(level=test_depth, product=TEST_PRODUCT_ID) self.assertEqual(results['sequence'], correct_sequence) @@ -94,5 +94,18 @@ def test_getProductOrderBook_product_bad(self): results = self.GDAX.getProductOrderBook(level=test_depth, product="BTC-USR") self.assertEqual(results['message'], "NotFound") + @my_vcr.use_cassette() + def test_getProductTicker(self): + correct = {u"ask":u"905.99", + u"bid":u"905.98", + u"price": u"905.99000000", + u"size":u"0.70384000", + u"time":u"2017-01-07T18:06:01.618000Z", + u"trade_id":12502526, + u"volume":u"12904.32111369" + } + results = self.GDAX.getProductTicker(product=TEST_PRODUCT_ID) + self.assertEqual(results, correct) + if __name__ == '__main__': unittest.main() \ No newline at end of file From e44de4c12fa4a45dd6677c13d79e8200f777a055 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 12:21:38 -0600 Subject: [PATCH 09/59] Add bad product ticker test and cassette files --- .../public/test_getProductOrderBook_level_1 | 35 + .../public/test_getProductOrderBook_level_2 | 42 + .../public/test_getProductOrderBook_level_3 | 2231 +++++++++++++++++ .../public/test_getProductOrderBook_level_bad | 32 + .../test_getProductOrderBook_product_bad | 32 + tests/cassettes/public/test_getProductTicker | 36 + .../public/test_getProductTicker_product_bad | 32 + 7 files changed, 2440 insertions(+) create mode 100644 tests/cassettes/public/test_getProductOrderBook_level_1 create mode 100644 tests/cassettes/public/test_getProductOrderBook_level_2 create mode 100644 tests/cassettes/public/test_getProductOrderBook_level_3 create mode 100644 tests/cassettes/public/test_getProductOrderBook_level_bad create mode 100644 tests/cassettes/public/test_getProductOrderBook_product_bad create mode 100644 tests/cassettes/public/test_getProductTicker create mode 100644 tests/cassettes/public/test_getProductTicker_product_bad diff --git a/tests/cassettes/public/test_getProductOrderBook_level_1 b/tests/cassettes/public/test_getProductOrderBook_level_1 new file mode 100644 index 00000000..e3e159e3 --- /dev/null +++ b/tests/cassettes/public/test_getProductOrderBook_level_1 @@ -0,0 +1,35 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USD/book?level=1 + response: + body: + string: !!binary | + H4sIAAAAAAAAA0zKOwqAMBAFwLu8egnZ/Dabq8gWflKIIEiwEu9u69TzYPTr7ufa0VglFeGSmbDs + 20CbJqj3rgQQklNRTSD2ZoR5HL8QQfAuFwlaawZFs/cDAAD//wMAVZWoJVsAAAA= + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31d918e8af1b2018-DFW] + connection: [keep-alive] + content-encoding: [gzip] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 17:23:23 GMT'] + etag: [W/"5b-ke17Fj1CJ2tynYFUphipdA"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=d3c81f6511ceecde19d0be94f4bfa56ab1483809803; expires=Sun, + 07-Jan-18 17:23:23 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/cassettes/public/test_getProductOrderBook_level_2 b/tests/cassettes/public/test_getProductOrderBook_level_2 new file mode 100644 index 00000000..0bbafe58 --- /dev/null +++ b/tests/cassettes/public/test_getProductOrderBook_level_2 @@ -0,0 +1,42 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USD/book?level=2 + response: + body: + string: !!binary | + H4sIAAAAAAAAA2yVSYodMQyG7+J1ISzLGtxXad4iw1uEQCA8sgq5e6gqt0td0vbzL1nW5L/l9fz9 + 5/nr27O84dBujWqlrXz98f1V3t7fy6gMrWylQu2M1movGz628wDnSStbWwxPhpfuDjoonazaYHNc + NGpF1iUX64lu+uwrmA4SZTyiO9aEZbYtXsGY6CLqI2G7KYIg97LRojEWShy2JFUtSVXjhCV3NEr8 + Je/F5A7XB4tVi/5q1B2g2ZUAgnGmag8cF9u9EdSO/AnT3SHBaPeLCcyizjRhmW2U6SzmKBsvZtFU + MTJJdLMn0cSlgfs9+ZQ0G0GfDsnp+kfRG/q46XweqjgtoRvix1a+vH5eoz8HjajVTm6kQftREKq0 + z+yF9+4QIMXeyfzBnm0DZGRHQ24ZTBI2Iht27xI+OwcRqGk37W2VR45g8UqRQA0rRJKeFcB7diVZ + cZKsQvGjslgii5Mna12oY4zRljXa6iy+eDazZc52PpY9CokWGB9zwh6exuTRbtvqIqcZtn3Xz6bW + I9DP8SpkyCKjGZs51hPbIyUKSp2bxzZnzDEZ0dw0sl02gJU9C6sixGzxabZ2qlTTui8zWicjqine + knwlFn8cW5+VZ8f/itDFqu6PXwc6N8NR0Me//wAAAP//AwDyfRSdFwgAAA== + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31d944578bbd1189-DFW] + connection: [keep-alive] + content-encoding: [gzip] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 17:53:02 GMT'] + etag: [W/"817-Q2a9BiuuFef0GvQYeN2a/Q"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=d8e7c407028e5754905359bec944ba5b81483811582; expires=Sun, + 07-Jan-18 17:53:02 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/cassettes/public/test_getProductOrderBook_level_3 b/tests/cassettes/public/test_getProductOrderBook_level_3 new file mode 100644 index 00000000..dd63bf71 --- /dev/null +++ b/tests/cassettes/public/test_getProductOrderBook_level_3 @@ -0,0 +1,2231 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USD/book?level=3 + response: + body: + string: !!binary | + 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 + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31d9445909ef58af-DFW] + connection: [keep-alive] + content-encoding: [gzip] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 17:53:03 GMT'] + etag: [W/"43db4-pNqbi25JMrJWSCH0AxyROQ"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=dde46748d39e126513a161992adf292bf1483811582; expires=Sun, + 07-Jan-18 17:53:02 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/cassettes/public/test_getProductOrderBook_level_bad b/tests/cassettes/public/test_getProductOrderBook_level_bad new file mode 100644 index 00000000..a6991937 --- /dev/null +++ b/tests/cassettes/public/test_getProductOrderBook_level_bad @@ -0,0 +1,32 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USD/book?level=4 + response: + body: {string: !!python/unicode '{"message":"Invalid level"}'} + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31d9445c9da11189-DFW] + connection: [keep-alive] + content-length: ['27'] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 17:53:03 GMT'] + etag: [W/"1b-5Id2QAwDvDb1YPwODBNaBQ"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=d3f1590fca149e53a3df056ab7db4879f1483811583; expires=Sun, + 07-Jan-18 17:53:03 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 400, message: Bad Request} +version: 1 diff --git a/tests/cassettes/public/test_getProductOrderBook_product_bad b/tests/cassettes/public/test_getProductOrderBook_product_bad new file mode 100644 index 00000000..bae422cc --- /dev/null +++ b/tests/cassettes/public/test_getProductOrderBook_product_bad @@ -0,0 +1,32 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USR/book?level=1 + response: + body: {string: !!python/unicode '{"message":"NotFound"}'} + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31d9445daa055801-DFW] + connection: [keep-alive] + content-length: ['22'] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 17:53:03 GMT'] + etag: [W/"16-zqcshJDirwfGUF9rSKQKCg"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=d873890b85dd80065915c27236b3713fc1483811583; expires=Sun, + 07-Jan-18 17:53:03 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 404, message: Not Found} +version: 1 diff --git a/tests/cassettes/public/test_getProductTicker b/tests/cassettes/public/test_getProductTicker new file mode 100644 index 00000000..048d9a3b --- /dev/null +++ b/tests/cassettes/public/test_getProductTicker @@ -0,0 +1,36 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USD/ticker + response: + body: + string: !!binary | + H4sIAAAAAAAAAzyLOw7CMBQE77K1Y+17jh3b56CiQYGksACB8qEAcXfkRGLL2ZkPlqkfxlMZkEU9 + 1WsweE7lMiIj0duUuA0Gc3lXStvRxXZn51ruYoRBP1//HQxej9t6r41oYmudiogL9VnKxpXSNZSG + 3UFiZsgUGySSPOL7AwAA//8DAJUR9MedAAAA + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31d957de09975879-DFW] + connection: [keep-alive] + content-encoding: [gzip] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 18:06:22 GMT'] + etag: [W/"9d-5N/s0SlEGi8aYbQPF6udRA"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=d5ca9b8a9888b2cd5f92ebd2696fb65c61483812382; expires=Sun, + 07-Jan-18 18:06:22 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/cassettes/public/test_getProductTicker_product_bad b/tests/cassettes/public/test_getProductTicker_product_bad new file mode 100644 index 00000000..5fb8c092 --- /dev/null +++ b/tests/cassettes/public/test_getProductTicker_product_bad @@ -0,0 +1,32 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USR/ticker + response: + body: {string: !!python/unicode '{"message":"NotFound"}'} + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31d968dc3ac457fb-DFW] + connection: [keep-alive] + content-length: ['22'] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 18:17:58 GMT'] + etag: [W/"16-zqcshJDirwfGUF9rSKQKCg"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=d2c4e93f8d1f583f0994985f7b85668281483813078; expires=Sun, + 07-Jan-18 18:17:58 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 404, message: Not Found} +version: 1 From 2b2610d97e033eb02925457d5f9a818125f58d81 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 12:27:44 -0600 Subject: [PATCH 10/59] Add in tests for no/bad product ID --- .../public/test_getProductTicker_product_none | 36 +++++++++++++++++++ tests/test_GDAXPublicClient.py | 23 +++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 tests/cassettes/public/test_getProductTicker_product_none diff --git a/tests/cassettes/public/test_getProductTicker_product_none b/tests/cassettes/public/test_getProductTicker_product_none new file mode 100644 index 00000000..f291ca42 --- /dev/null +++ b/tests/cassettes/public/test_getProductTicker_product_none @@ -0,0 +1,36 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USD/ticker + response: + body: + string: !!binary | + H4sIAAAAAAAAAzSMOw7CMBQE77K1Y+178f8cVDQokBQWIFA+FCDujhyJLUc788E6D+N0qiOKqGcv + PQ2ec71MKEjZW1Hug8FS343Suhi8JhdgcG4mUnYwGJbrX4LB63Hb7u0vmnOwjE4dpXXWunOlxI7S + MR4kFdUizqboSR7x/QEAAP//AwCnGx8omQAAAA== + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31d96f23ca861fbe-DFW] + connection: [keep-alive] + content-encoding: [gzip] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 18:22:15 GMT'] + etag: [W/"99-cL8voCB1YGbN1WnZbBkieA"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=dc0029467887bd2091e364098e9a04f791483813335; expires=Sun, + 07-Jan-18 18:22:15 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index 8fa3a81c..f9bb5b7d 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -13,6 +13,9 @@ #NOTE THAT THESE TESTS ARE FOCUSED ON BTC-USD TEST_PRODUCT_ID = 'BTC-USD' +#NON-EXISTENT PRODUCT ID AS OF Jan 7, 2017 +BAD_TEST_PRODUCT_ID = 'BTC-USR' + class TestGDAXPublicClient(unittest.TestCase): def setUp(self): #Only testing for BTC-USD @@ -91,7 +94,7 @@ def test_getProductOrderBook_product_bad(self): #test for non-existent level depth test_depth = 1 - results = self.GDAX.getProductOrderBook(level=test_depth, product="BTC-USR") + results = self.GDAX.getProductOrderBook(level=test_depth, product=BAD_TEST_PRODUCT_ID) self.assertEqual(results['message'], "NotFound") @my_vcr.use_cassette() @@ -107,5 +110,23 @@ def test_getProductTicker(self): results = self.GDAX.getProductTicker(product=TEST_PRODUCT_ID) self.assertEqual(results, correct) + @my_vcr.use_cassette('test_getProductTicker') + def test_getProductTicker_product_none(self): + correct = {u"ask": u"905.99", + u"bid": u"905.98", + u"price": u"905.99000000", + u"size": u"0.70384000", + u"time": u"2017-01-07T18:06:01.618000Z", + u"trade_id": 12502526, + u"volume": u"12904.32111369" + } + results = self.GDAX.getProductTicker() + self.assertEqual(results, correct) + + @my_vcr.use_cassette() + def test_getProductTicker_product_bad(self): + results = self.GDAX.getProductTicker(product=BAD_TEST_PRODUCT_ID) + self.assertEqual(results['message'], "NotFound") + if __name__ == '__main__': unittest.main() \ No newline at end of file From dc65d978211936a26b821f621fe5b604997b86ba Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 12:29:00 -0600 Subject: [PATCH 11/59] Add *.egg files to .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f022c845..ffcfd32e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ example.py build/ dist/ -*.egg-info/ \ No newline at end of file +*.egg-info/ +*.egg \ No newline at end of file From 744f2d93a2161995604266d328f956d0ea08ec1f Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 12:38:59 -0600 Subject: [PATCH 12/59] Small code clean ups --- tests/test_GDAXPublicClient.py | 46 ++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index f9bb5b7d..bea28368 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -3,6 +3,7 @@ import vcr +# SET UP VCR TO SAVE YAML CASSETTETTES TO PUBLIC FOLDER my_vcr = vcr.VCR( serializer='yaml', cassette_library_dir='tests/cassettes/public', @@ -10,27 +11,28 @@ match_on=['uri', 'method'], ) -#NOTE THAT THESE TESTS ARE FOCUSED ON BTC-USD +# NOTE THAT THESE TESTS ARE FOCUSED ON BTC-USD TEST_PRODUCT_ID = 'BTC-USD' -#NON-EXISTENT PRODUCT ID AS OF Jan 7, 2017 +# NON-EXISTENT PRODUCT ID AS OF Jan 7, 2017 BAD_TEST_PRODUCT_ID = 'BTC-USR' + class TestGDAXPublicClient(unittest.TestCase): def setUp(self): - #Only testing for BTC-USD + # Only testing for BTC-USD self.GDAX = PublicClient(product_id=TEST_PRODUCT_ID) def test_PublicClientInitCorrectProductID(self): - self.assertEquals(self.GDAX.productId,TEST_PRODUCT_ID) + self.assertEquals(self.GDAX.productId, TEST_PRODUCT_ID) def test_PublicClientInitWrongProductID(self): - EurProductIdClient = PublicClient(product_id="BTC-EUR") - self.assertNotEquals(self.GDAX,EurProductIdClient) + eurProductIdClient = PublicClient(product_id="BTC-EUR") + self.assertNotEquals(self.GDAX, eurProductIdClient) @my_vcr.use_cassette() def test_getProducts(self): - #Results from direct browser run on Jan 7, 2017 + # Results from direct browser run on Jan 7, 2017 correct = [ {"id": "BTC-GBP", "base_currency": "BTC", "quote_currency": "GBP", "base_min_size": "0.01", "base_max_size": "10000", "quote_increment": "0.01", "display_name": "BTC/GBP"}, @@ -51,30 +53,30 @@ def test_getProducts(self): @my_vcr.use_cassette() def test_getProductOrderBook_level_1(self): - #test for first level depth + # Test for first level depth test_depth = 1 - #Results from run on Jan 7, 2017 + # Results from run on Jan 7, 2017 correct_sequence = 1974671651 results = self.GDAX.getProductOrderBook(level=test_depth, product=TEST_PRODUCT_ID) self.assertEqual(results['sequence'], correct_sequence) @my_vcr.use_cassette() def test_getProductOrderBook_level_2(self): - #test for second level depth + # Test for second level depth test_depth = 2 - #Results from direct browser run on Jan 7, 2017 + # Results from direct browser run on Jan 7, 2017 correct_sequence = 1974823003 results = self.GDAX.getProductOrderBook(level=test_depth, product=TEST_PRODUCT_ID) self.assertEqual(results['sequence'], correct_sequence) @my_vcr.use_cassette() def test_getProductOrderBook_level_3(self): - #test for third level depth + # Test for third level depth test_depth = 3 - #Results from direct browser run on Jan 7, 2017 + # Results from direct browser run on Jan 7, 2017 correct_sequence = 1974823009 results = self.GDAX.getProductOrderBook(level=test_depth, product=TEST_PRODUCT_ID) self.assertEqual(results['sequence'], correct_sequence) @@ -82,7 +84,7 @@ def test_getProductOrderBook_level_3(self): # TODO: it may be better functionality for library to throw exception for invalid level @my_vcr.use_cassette() def test_getProductOrderBook_level_bad(self): - #test for non-existent level depth + # test for non-existent level depth test_depth = 4 results = self.GDAX.getProductOrderBook(level=test_depth, product=TEST_PRODUCT_ID) @@ -91,7 +93,7 @@ def test_getProductOrderBook_level_bad(self): # TODO: it may be better functionality for library to throw exception for invalid product @my_vcr.use_cassette() def test_getProductOrderBook_product_bad(self): - #test for non-existent level depth + # test for non-existent level depth test_depth = 1 results = self.GDAX.getProductOrderBook(level=test_depth, product=BAD_TEST_PRODUCT_ID) @@ -99,13 +101,13 @@ def test_getProductOrderBook_product_bad(self): @my_vcr.use_cassette() def test_getProductTicker(self): - correct = {u"ask":u"905.99", - u"bid":u"905.98", + correct = {u"ask": u"905.99", + u"bid": u"905.98", u"price": u"905.99000000", - u"size":u"0.70384000", - u"time":u"2017-01-07T18:06:01.618000Z", - u"trade_id":12502526, - u"volume":u"12904.32111369" + u"size": u"0.70384000", + u"time": u"2017-01-07T18:06:01.618000Z", + u"trade_id": 12502526, + u"volume": u"12904.32111369" } results = self.GDAX.getProductTicker(product=TEST_PRODUCT_ID) self.assertEqual(results, correct) @@ -129,4 +131,4 @@ def test_getProductTicker_product_bad(self): self.assertEqual(results['message'], "NotFound") if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() From ab1975efe6d12d24407bd43730fc68f9d62a9608 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 12:48:11 -0600 Subject: [PATCH 13/59] Add getProductTrades test for top result --- tests/test_GDAXPublicClient.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index bea28368..cb9ad1d1 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -130,5 +130,12 @@ def test_getProductTicker_product_bad(self): results = self.GDAX.getProductTicker(product=BAD_TEST_PRODUCT_ID) self.assertEqual(results['message'], "NotFound") + @my_vcr.use_cassette() + def test_getProductTrades(self): + correct_top_one = {u'trade_id': 12503748, u'size': u'0.00816557', u'side': u'sell', u'price': u'900.81000000', u'time': u'2017-01-07T18:46:40.988Z'} + results = self.GDAX.getProductTrades(product=TEST_PRODUCT_ID) + self.assertEqual(results[0], correct_top_one) + + if __name__ == '__main__': unittest.main() From fe33a1fc2eac638203aa4604b243b4bd45edc405 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 12:52:09 -0600 Subject: [PATCH 14/59] Add getProductTrades test for number of entries; add test for bad product --- tests/test_GDAXPublicClient.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index cb9ad1d1..60d372de 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -136,6 +136,13 @@ def test_getProductTrades(self): results = self.GDAX.getProductTrades(product=TEST_PRODUCT_ID) self.assertEqual(results[0], correct_top_one) + # TODO: Add additional tests for pagination + self.assertEqual(len(results), 100) + + @my_vcr.use_cassette() + def test_getProductTrades_product_bad(self): + results = self.GDAX.getProductTrades(product=BAD_TEST_PRODUCT_ID) + self.assertEqual(results['message'], "NotFound") if __name__ == '__main__': unittest.main() From 58f045cde2afa5e03c5c27779f31df0a342e9870 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 12:53:39 -0600 Subject: [PATCH 15/59] Add getProductTrades test cassette files --- tests/cassettes/public/test_getProductTrades | 52 +++++++++++++++++++ .../public/test_getProductTrades_product_bad | 32 ++++++++++++ tests/test_GDAXPublicClient.py | 2 + 3 files changed, 86 insertions(+) create mode 100644 tests/cassettes/public/test_getProductTrades create mode 100644 tests/cassettes/public/test_getProductTrades_product_bad diff --git a/tests/cassettes/public/test_getProductTrades b/tests/cassettes/public/test_getProductTrades new file mode 100644 index 00000000..27db1eb7 --- /dev/null +++ b/tests/cassettes/public/test_getProductTrades @@ -0,0 +1,52 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USD/trades + response: + body: + string: !!binary | + H4sIAAAAAAAAA7zay24sNRAG4HfpdY5V97L7OViBEAKSRaSDhM5lAYh3R9MgoJ3StD2xnWVLoy/l + y2+33d/9sX15/eVl2zcC9A+AH8C/wbyL7QKp5Pzt9rR9+fTj88sPr8/bjqTALvlp+/XT68+3nxWA + lBGOv+1p+/z6++0pJICMpurHs+fbs88vHz9ufz49BPoZ/Mc7g///J9pAxmRKIWj/gbmU5KUGKZGy + 0wn86etvj3p6LhCaCnyHJ5UX9SAP9Ljy+K136tT3elR58rb/cn99kCDm8HpCcEGyIqVjfAImLxyC + cAViApGci8oQkMsZdA8GDHXPwDtglTE2HawyxqBlSrwHtDOoHIGUx4HaALqVcWCVMlSGxPYdsIoZ + 0tmDhqYsTHdAbAjuoSBcrUy3n2ZXZBsCUjkvvUXeVkgwsELKZzDnoElvzwoOAr0CZXIfUr2bwcnT + gvQMms2uUFpA1XEgn0GdXiGdQZmdpYQVOH2Uwhmk2Ss+lgZwZIVYJQ3NHjToq0FbDepqUFaDvBqk + 1SCuBmExCGU1uDppYHXSwOqkgSpp0Ca/roE0gCNf16BKmukvM1AlDegAUI9jofgcEU5Jk1OJNlFA + YE5lDAgVmAfsae6AVspqMK8GfTVoq0FdDcpqkFeDVIE4G8QGEEeCVdJ4mQzmshrMq0FfDVZJ49GK + L4RZzceAVdL47FGaq6TRN6AkJT7ObceAVdLI9D6skiY8885/H2qOAbEBLJnNQTpAsCQgIQjXICoh + U7EhoJdrUFkLqXaBmIzCJvV8DQr4cYHYDMrRh2gh6NdgUTmmZwdIOYFpCFrDoKFSbldEPSAnYwrB + KmkoBxeWnRfAh6fxrHBpKTAroEhHgahJc9yFfA2ydgaNHIMUPQSpZZAydoG8qyWkeBpeBg0lVj6i + bQzYEDTdN7L3QLsMGkyIBtYHMibNGIINQcNImPtA5OQczntrCBrS7ia9jVKIK2wKGj5OMZpB2rUk + KqGnDQX23h7SzpJcOQTlKtge2GDQsVbEXkvOCGUf5dF1fQoD68PF7QlrPa0zxiZ7VcSATPaqhIm+ + 9HrAI0nZwp2FVgkTfRFBxOJs3LqzuOtVCXM6Nvh3+2tKxt7hYUIKNxZaBUz0aSCzFO1rUKSEHq6C + yldgtFW7BMFSlrhCaqjwARCTh4ug4rWHDqVrmcddKRnEcwImFIi7SNLwYN2kPA5+/xcAAAD//wMA + ULThZhMsAAA= + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cb-after: ['12503649'] + cb-before: ['12503748'] + cf-ray: [31d992ef4a9958d9-DFW] + connection: [keep-alive] + content-encoding: [gzip] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 18:46:41 GMT'] + etag: [W/"2c13-ki5YCTBH11gvzD3v2t/YaQ"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=db71425c6354d5e09a4800243ef5138b21483814801; expires=Sun, + 07-Jan-18 18:46:41 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/cassettes/public/test_getProductTrades_product_bad b/tests/cassettes/public/test_getProductTrades_product_bad new file mode 100644 index 00000000..742c06d2 --- /dev/null +++ b/tests/cassettes/public/test_getProductTrades_product_bad @@ -0,0 +1,32 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USR/trades + response: + body: {string: !!python/unicode '{"message":"NotFound"}'} + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31d99a48dd375909-DFW] + connection: [keep-alive] + content-length: ['22'] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 18:51:43 GMT'] + etag: [W/"16-zqcshJDirwfGUF9rSKQKCg"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=dd1c1156937fb459ba264c41c5c32c9a01483815102; expires=Sun, + 07-Jan-18 18:51:42 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 404, message: Not Found} +version: 1 diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index 60d372de..46c98f57 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -144,5 +144,7 @@ def test_getProductTrades_product_bad(self): results = self.GDAX.getProductTrades(product=BAD_TEST_PRODUCT_ID) self.assertEqual(results['message'], "NotFound") + + if __name__ == '__main__': unittest.main() From 0334b451a7417bec57cc8df5d48051ce196f8538 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 13:09:59 -0600 Subject: [PATCH 16/59] Add getHistoricRates candle test for default pull and cassette file --- .../public/test_getProductHistoricRates | 51 +++++++++++++++++++ tests/test_GDAXPublicClient.py | 8 +++ 2 files changed, 59 insertions(+) create mode 100644 tests/cassettes/public/test_getProductHistoricRates diff --git a/tests/cassettes/public/test_getProductHistoricRates b/tests/cassettes/public/test_getProductHistoricRates new file mode 100644 index 00000000..21b461f0 --- /dev/null +++ b/tests/cassettes/public/test_getProductHistoricRates @@ -0,0 +1,51 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USD/candles?start=&end=&granularity= + response: + body: + string: !!binary | + H4sIAAAAAAAAA4RWC47kOgi8UIT4G86y2vtf4wnjxO50a1+kDI2Hiu3i++cPaUiQpeOViJB8JSqk + nkLkIgQiSUOlnE/8vR4sYlvbb8FgHEw8vKE5Njb0/7CcRLYBIwrAIDRNIl8aMRiZiRL2ZnpgGa/I + hGwQx6khXQQ5KDFiYD/Hvu6FNbCihNaLFymQJxPhupwfEOztpjWITYVbw7wUQkhNWZvQPHYzvaET + 8/GXgIRVyHnba5S9F2O36aEQgbNb2R2QSYb12XQ6X7NxWtcyRXUzaY8dZ5MVKoOat9yCLhrgSRQj + rSmUAzjjhKf53PgWRbw40TblGRYEvUX6qdiFoEHJuZ8joij6eGgn1uleJFBVVtZYTj5oJF7bLuzY + Yi4iRNbZnuc4MnoHJnqD4kMblwJ7eA6JbyguqLYYJxT10joyWqK/oZqLqHgy9RFzMQAzjjjWWPkz + z6cVlacml0MMCkzRA8NNadA2PrUBEZZqvIuCDu8Inm6Yxp9agjgNNLR3PdEx8yaAc4rMl+ZASGRh + 9E45dV3QODFbMAx3dlXJL6wF/gYtoYBhMQ7AzKBzp/unAEeMseNzg3TyMiDGAjw/GdTUTlNsU5M2 + o5fGUCVth6+KNgDbZNgU5I3jIk4Mjx14lY0KlNeV5yKDRiLuexyBxPxvughkEB1uJV+b0TQZ/tIY + FLN8yO+cVsKGnphHjLgQfCgfh0NtAFtbngr7RcDimG6rSB1VSnJyopV3D+jUCEHMMX5wIjk50UbE + h6gsRqGInVcSvs1V2s5vrUJVQ9jiq8ZVCbkiO8FTO0MeDSsyhgXFrlPH/UZREwHSoBEvjQiGIoXp + lx/Ei5sYlcYF0lb6CxiXBiBJZTS/wl68mAkH9SsiZ0xG1lHrC1UNkIzUcrzTsrieRqO8vd7IK4Bp + dc63F6zZaTJyC6q7CQ9Sjyb1yGZRbU6zPZDjrYGTignjFy0yQwartxdGo8W4FwUq1Wx3q+OwMokx + yN2yl5BqBEqQVHbE7ciDGe5SMisq0S1qhcqhGW5k7yFIGLuYF0DWbLLAV1UJRVJaMWfHQUlXE6hO + YzByi7lIBknETP5NEM6mY905vEJlCr0XhUEchV3jAPEGEfaLfmkVzIpObkb2NtxDbC7T7jaEc3a1 + ml7zq+hzz67eg0iWzz41ZnBkeVq+HNv17OowRxrvMcFh3GsClMR+D71Hh+MeYr2nkh+CgNLHCeAF + 4N+i+KuBjXbKsfu6WvwWCEZxFGj2xQV7m2QLuQHEUOG6Y4ltMeB+fntr2CFhY9efmrvmv/uyFodC + PHuaER8eUv6InRhbzEUHHeLHBj2f2nP+fGnE4BrJMfR2zN//AAAA//8DAAgZ/JwIDQAA + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31d9afdb0dba58df-DFW] + connection: [keep-alive] + content-encoding: [gzip] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 19:06:26 GMT'] + etag: [W/"d08-kMLpPKe0c1nMgB24jBJTDA"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=dd355c0d4a12a11ff26ee3813e63e555a1483815986; expires=Sun, + 07-Jan-18 19:06:26 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index 46c98f57..ab1c4a7d 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -144,7 +144,15 @@ def test_getProductTrades_product_bad(self): results = self.GDAX.getProductTrades(product=BAD_TEST_PRODUCT_ID) self.assertEqual(results['message'], "NotFound") + @my_vcr.use_cassette() + def test_getProductHistoricRates(self): + # Test for default one minute BTC-USD candle poll - without start/end pull last hour + correct_top_one = [1483815960, 900.92, 904.94, 904.94, 904.33, 10.113950419999998] + results = self.GDAX.getProductHistoricRates(product=TEST_PRODUCT_ID) + self.assertEqual(results[0], correct_top_one) + # TODO: Add additional tests for pagination + self.assertEqual(len(results), 61) if __name__ == '__main__': unittest.main() From 385b7a0f7eb86945e5f26f89a951982b54c46961 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 13:13:15 -0600 Subject: [PATCH 17/59] Add getHistoricRates candle test for 5 minute pull and cassette file --- .../public/test_getProductHistoricRates_5mins | 39 +++++++++++++++++++ tests/test_GDAXPublicClient.py | 11 ++++++ 2 files changed, 50 insertions(+) create mode 100644 tests/cassettes/public/test_getProductHistoricRates_5mins diff --git a/tests/cassettes/public/test_getProductHistoricRates_5mins b/tests/cassettes/public/test_getProductHistoricRates_5mins new file mode 100644 index 00000000..4eb10a5f --- /dev/null +++ b/tests/cassettes/public/test_getProductHistoricRates_5mins @@ -0,0 +1,39 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USD/candles?start=&end=&granularity=300 + response: + body: + string: !!binary | + H4sIAAAAAAAAA1ySa44EIQiEL2QIb4qzbPb+19iI4/Ts+KNLDJ9Fgz8/4jBIKvNqdmIZifl6XxEQ + t5dA83e9mOhhmFonreNTvJcqWXE0W/Beqg+bzAsdFH4hdJPKjSyoqjVgg7I9qB10NSs9MvjypNas + yjyW8mD8qpZfCEY47wUiFOaSaOm9UG/WayxBehpi8URC8GUgE2RCv6t1H7Qo7DL/I3Gq0FJ36+P7 + sDJsUuVkjyRBzhW6xIirzT31sG/UMOgUN8xnoLlUaP8VTPy74j2sBST5tnPifkSosAAyBhvUvqZq + OmjQboyRTHtk8K5VTdHeHIkZTbw5vS8Jvlr2Ns9k50QUtN9QsckYfoDJJ5v1QDWSeg+TKsxDBLe5 + v38AAAD//wMAqh7pOvMCAAA= + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31d9b7dd3bf2587f-DFW] + connection: [keep-alive] + content-encoding: [gzip] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 19:11:55 GMT'] + etag: [W/"2f3-9JzIxYJe+TT0mnM6Vrdang"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=df0cb9e11ff8270149c832f507d2504f01483816314; expires=Sun, + 07-Jan-18 19:11:54 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index ab1c4a7d..db6db2bd 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -154,5 +154,16 @@ def test_getProductHistoricRates(self): # TODO: Add additional tests for pagination self.assertEqual(len(results), 61) + @my_vcr.use_cassette() + def test_getProductHistoricRates_5mins(self): + test_5min_granularity = 5 * 60 + # Test for default five minute BTC-USD candle poll - without start/end pull last hour + correct_top_one = [1483816200, 904.01, 904.5, 904.49, 904.49, 18.09471826] + results = self.GDAX.getProductHistoricRates(product=TEST_PRODUCT_ID,granularity=test_5min_granularity) + self.assertEqual(results[0], correct_top_one) + + # TODO: Add additional tests for pagination + self.assertEqual(len(results), 13) + if __name__ == '__main__': unittest.main() From 820929d1eea6dbb0c2006293e7c4675c02783a4b Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 13:38:31 -0600 Subject: [PATCH 18/59] Add getHistoricRates candle test start and end; add todos for changing library potentially --- requirements.txt | 3 +- ...est_getProductHistoricRates_start_end_good | 49 +++++++++++++++++++ tests/test_GDAXPublicClient.py | 27 ++++++++-- 3 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 tests/cassettes/public/test_getProductHistoricRates_start_end_good diff --git a/requirements.txt b/requirements.txt index 3cdb369c..0b6c3a70 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ requests>=2.5 websocket-client>=0.37.0 -vcrpy>=1.10.4 \ No newline at end of file +vcrpy>=1.10.4 +pytz>=2016.10 \ No newline at end of file diff --git a/tests/cassettes/public/test_getProductHistoricRates_start_end_good b/tests/cassettes/public/test_getProductHistoricRates_start_end_good new file mode 100644 index 00000000..64135c3c --- /dev/null +++ b/tests/cassettes/public/test_getProductHistoricRates_start_end_good @@ -0,0 +1,49 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USD/candles?start=2017-01-07T12%3A28%3A00-06%3A00&end=2017-01-07T13%3A26%3A00-06%3A00&granularity= + response: + body: + string: !!binary | + H4sIAAAAAAAAA4RWCY7jMAz7UCDoPt4ymP9/Y+EjtZoWswFalQFZ2bSk5OeHNCUpCPEqVKgv3wjI + Gvx73WTURaYvZIYgSqKwF98rJ7/x8qYTEIVU4/JfCwlhbX+cvsn5PTCokXu9Lm9a/FuLoJ0ec88C + KZ0ndiMBFhX3wJ0qj9bz71QEGMiqXrgubtrtxybXWxhSs2xn47YtEfoiELoYyFXaxgy/CQ4iBqlk + ksiPjekuhH22Im+Br4BgFEa6j+BIZXsi/pb2hRAMsxeGbCOW467vIEBcVFjbznhbgdS461vrMkjU + 7jRvI/SdtwMxcKFXcstAe/+aXZMvDVBqmoseCc59I1R0MtONBNQ9kiN2LbRyR141iNsl69sruxQS + LW/hm9bKd15ebH0EQpDU9NR9VtG0d6vY98BgnEwc9zE3ber/tFxEbaExDeK7Auek6IgYjMxEaXea + Ni3jlVWrmhg4O0K6CCqoMPPTXHMfWgMbltD+4EUKYygRoT9HiDmudJM9ZsEAvBDWOA4hNWVdfVMt + m+ktnZq3bwISViE/xWmag+/DsZvaABE4uw1ek0wzbK1N5+FrLZ2ObZmiupmsE2trk10qQcu3OoEu + CvAiyihbFkoTzjrhSZ+J7zCMFyc6VJ5lQbBSlHdgY/YmFZ+53SuKdgehda3TfZNAVVlZ82OaGvFO + u7VxwryJkDXW9rraktFXYc7u4zW+D4pLgT29QvJTiluqK0SXol46loxW6E+p1jYqP0fsvJmAla2O + NXf/9CfUQXI5ZFBitaGkycvSpEPuKCDTSo3P7NfwVcHzGCb5HRWIU6ChPR8bGrNvErhmqHogB0Ii + S6Nny6nrlmbXnMAQ7uyq8vHEV0v8LtpBAdMymmB2UM90/xTgzIhTn0ek05eAjC14/RxvI2qdiotq + smj0QAxjpJ3yVdElwEUJm4F86XgYJ4YtA++xMQrlseV5k0GzEM8+WiEx/20XgYxXvSMg38loUsIf + iEGxxhnys6eVcEm75hVivIx5KLfFoS4B22J2wH4RsDiW2x5SbUpJTU/mS8tL1NF4EppjfvFEanqi + S5FvYXQxCmWevpL0Q1dZPL/RKFVNYcuPGTdGyJW1Grx0dcgL4aiMsKQ8c6rtb7ypZibIEkU+EBGE + IqXpxznIeFPNjNHGQ6QLrH/AvDQBSUZHb+nv7z8AAAD//wMA2QwHJkIMAAA= + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31d9d9d2ce5257fb-DFW] + connection: [keep-alive] + content-encoding: [gzip] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 19:35:05 GMT'] + etag: [W/"c42-k6WQiIJnk+jeCnv3DV3f+Q"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=d99742c4af1227064bb6b9988398b65cc1483817705; expires=Sun, + 07-Jan-18 19:35:05 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index db6db2bd..76513eae 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -1,6 +1,9 @@ import unittest from GDAX import PublicClient +import pytz +from datetime import datetime + import vcr # SET UP VCR TO SAVE YAML CASSETTETTES TO PUBLIC FOLDER @@ -149,20 +152,36 @@ def test_getProductHistoricRates(self): # Test for default one minute BTC-USD candle poll - without start/end pull last hour correct_top_one = [1483815960, 900.92, 904.94, 904.94, 904.33, 10.113950419999998] results = self.GDAX.getProductHistoricRates(product=TEST_PRODUCT_ID) - self.assertEqual(results[0], correct_top_one) - # TODO: Add additional tests for pagination + self.assertEqual(results[0], correct_top_one) self.assertEqual(len(results), 61) + # TODO: Consider making API give Unix seconds to format to ISO 8601 for users + @my_vcr.use_cassette() + def test_getProductHistoricRates_start_end_good(self): + tz = pytz.timezone('America/Chicago') + + start_time = datetime.fromtimestamp(1483813680, tz).isoformat() + end_time = datetime.fromtimestamp(1483817160, tz).isoformat() + + # Test for default one minute BTC-USD candle poll - without start/end pull last hour + correct_top_one = [1483817100, 904.9, 904.9, 904.9, 904.9, 0.02472] + results = self.GDAX.getProductHistoricRates(product=TEST_PRODUCT_ID,start=start_time,end=end_time) + + self.assertEqual(results[0], correct_top_one) + self.assertEqual(len(results), 58) + + # TODO: Add tests for valid start, end, granularity to avoid bad requests to upstream + @my_vcr.use_cassette() def test_getProductHistoricRates_5mins(self): test_5min_granularity = 5 * 60 + # Test for default five minute BTC-USD candle poll - without start/end pull last hour correct_top_one = [1483816200, 904.01, 904.5, 904.49, 904.49, 18.09471826] results = self.GDAX.getProductHistoricRates(product=TEST_PRODUCT_ID,granularity=test_5min_granularity) - self.assertEqual(results[0], correct_top_one) - # TODO: Add additional tests for pagination + self.assertEqual(results[0], correct_top_one) self.assertEqual(len(results), 13) if __name__ == '__main__': From dded89007bd47d9b5d69d87decd60a11dae9a018 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 13:56:54 -0600 Subject: [PATCH 19/59] Move test dependencies into tests_require --- setup.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index fe166e2f..064553ed 100644 --- a/setup.py +++ b/setup.py @@ -4,8 +4,12 @@ install_requires = [ 'requests>=2.5', - 'websocket-client>=0.37.0', - 'vcrpy>=1.10.4' + 'websocket-client>=0.37.0' +] + +test_requires = [ + 'vcrpy>=1.10.4', + 'pytz>=2016.10' ] setup( @@ -18,6 +22,7 @@ packages = find_packages(), install_requires = install_requires, test_suite="tests", + tests_require=test_requires, description = 'A Python client for the GDAX API', download_url = 'https://github.com/danpaquin/coinbase-gdax-python/archive/master.zip', keywords = ['coinbase', 'gdax', 'bitcoin', 'ethereum', 'client', 'api', 'exchange', 'crypto', 'currency'], From fe9f01cd495f67a233a264392cc2114617413646 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 14:08:21 -0600 Subject: [PATCH 20/59] Added test for 24HrStats and cassette --- .../cassettes/public/test_getProduct24HrStats | 36 +++++++++++++++++++ tests/test_GDAXPublicClient.py | 16 +++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 tests/cassettes/public/test_getProduct24HrStats diff --git a/tests/cassettes/public/test_getProduct24HrStats b/tests/cassettes/public/test_getProduct24HrStats new file mode 100644 index 00000000..e879bb49 --- /dev/null +++ b/tests/cassettes/public/test_getProduct24HrStats @@ -0,0 +1,36 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USD/stats + response: + body: + string: !!binary | + H4sIAAAAAAAAA2SNywqDMBRE/2XW5XJvJiaanymCQQXbCH0hpf9e4sKNszwcznxR1nxHQttRAnUf + LpjmcUJCZybmD7qUT1XVicYDvsvyumUkmDNSfIjqlbHq/eO5p8PZv1KHfkOCc8rGRH19J/H7AwAA + //8DAEnWq9+TAAAA + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31da010b9df31fbe-DFW] + connection: [keep-alive] + content-encoding: [gzip] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 20:01:52 GMT'] + etag: [W/"93-0uYc8hmCoR6DmqoMT7yPrQ"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=d2526e7d2213c36492babaf95cfda91f81483819311; expires=Sun, + 07-Jan-18 20:01:51 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index 76513eae..17515fda 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -30,8 +30,8 @@ def test_PublicClientInitCorrectProductID(self): self.assertEquals(self.GDAX.productId, TEST_PRODUCT_ID) def test_PublicClientInitWrongProductID(self): - eurProductIdClient = PublicClient(product_id="BTC-EUR") - self.assertNotEquals(self.GDAX, eurProductIdClient) + eur_product_client = PublicClient(product_id="BTC-EUR") + self.assertNotEquals(self.GDAX, eur_product_client) @my_vcr.use_cassette() def test_getProducts(self): @@ -184,5 +184,17 @@ def test_getProductHistoricRates_5mins(self): self.assertEqual(results[0], correct_top_one) self.assertEqual(len(results), 13) + @my_vcr.use_cassette() + def test_getProduct24HrStats(self): + correct = { u"high": u"911.14000000", + u"last": u"896.07000000", + u"low": u"802.07000000", + u"open": u"893.63000000", + u"volume": u"12133.46704037", + u"volume_30day": u"220351.04630033" + } + results = self.GDAX.getProduct24HrStats(product=TEST_PRODUCT_ID) + self.assertEqual(results, correct) + if __name__ == '__main__': unittest.main() From 1092bd0104f2ec3a999f15b75db2e85344c117d6 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 14:10:49 -0600 Subject: [PATCH 21/59] Added test for 24HrStats for bad product and cassette --- .../test_getProduct24HrStats_product_bad | 32 +++++++++++++++++++ tests/test_GDAXPublicClient.py | 5 +++ 2 files changed, 37 insertions(+) create mode 100644 tests/cassettes/public/test_getProduct24HrStats_product_bad diff --git a/tests/cassettes/public/test_getProduct24HrStats_product_bad b/tests/cassettes/public/test_getProduct24HrStats_product_bad new file mode 100644 index 00000000..009897b6 --- /dev/null +++ b/tests/cassettes/public/test_getProduct24HrStats_product_bad @@ -0,0 +1,32 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/products/BTC-USR/stats + response: + body: {string: !!python/unicode '{"message":"NotFound"}'} + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=1'] + cf-ray: [31da0d34588d57d1-DFW] + connection: [keep-alive] + content-length: ['22'] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 20:10:10 GMT'] + etag: [W/"16-zqcshJDirwfGUF9rSKQKCg"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=d1b8103a423e4843e5e80894cc5a7688c1483819809; expires=Sun, + 07-Jan-18 20:10:09 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 404, message: Not Found} +version: 1 diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index 17515fda..181eab41 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -196,5 +196,10 @@ def test_getProduct24HrStats(self): results = self.GDAX.getProduct24HrStats(product=TEST_PRODUCT_ID) self.assertEqual(results, correct) + @my_vcr.use_cassette() + def test_getProduct24HrStats_product_bad(self): + results = self.GDAX.getProduct24HrStats(product=BAD_TEST_PRODUCT_ID) + self.assertEqual(results['message'], "NotFound") + if __name__ == '__main__': unittest.main() From 18bef4c90e2d2be7e8169a2d71d646600ec2fcab Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 14:15:23 -0600 Subject: [PATCH 22/59] Added test for getCurrencies and cassette file --- tests/cassettes/public/test_getCurrencies | 36 +++++++++++++++++++++++ tests/test_GDAXPublicClient.py | 13 ++++++++ 2 files changed, 49 insertions(+) create mode 100644 tests/cassettes/public/test_getCurrencies diff --git a/tests/cassettes/public/test_getCurrencies b/tests/cassettes/public/test_getCurrencies new file mode 100644 index 00000000..7d3b8695 --- /dev/null +++ b/tests/cassettes/public/test_getCurrencies @@ -0,0 +1,36 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/currencies + response: + body: + string: !!binary | + H4sIAAAAAAAAA4quVspMUbJScgpxVtJRykvMTQVxMkuS8zPzlHSUcjPz4oszq0CCBnoGEGCoVKsD + 1eUa4oHQ5VqSkVqEVY+hgQGSntAgJD2lRfkYWgwh9iC0+CA7ziezJJU417k7BSD5qSizJLM4QyEg + vzQvhbCVocEuCL2heZklqSkKwSWJJanFCi75OTmJRbiNiAUAAAD//wMAtSygtVQBAAA= + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cache-control: ['public, max-age=30'] + cf-ray: [31da114918671189-DFW] + connection: [keep-alive] + content-encoding: [gzip] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 20:12:57 GMT'] + etag: [W/"154-jJh/PIiHO7SFGDokCIKeMQ"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=d02d5402cc0b524b2ffbbfe1028d9f3de1483819977; expires=Sun, + 07-Jan-18 20:12:57 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index 181eab41..e58c524a 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -201,5 +201,18 @@ def test_getProduct24HrStats_product_bad(self): results = self.GDAX.getProduct24HrStats(product=BAD_TEST_PRODUCT_ID) self.assertEqual(results['message'], "NotFound") + @my_vcr.use_cassette() + def test_getCurrencies(self): + correct = [ + {u'min_size': u'0.00000001', u'id': u'BTC', u'name': u'Bitcoin'}, + {u'min_size': u'0.00000100', u'id': u'ETH', u'name': u'Ether'}, + {u'min_size': u'0.01000000', u'id': u'EUR', u'name': u'Euro'}, + {u'min_size': u'0.00000001', u'id': u'LTC', u'name': u'Litecoin'}, + {u'min_size': u'0.01000000', u'id': u'GBP', u'name': u'British Pound'}, + {u'min_size': u'0.01000000', u'id': u'USD', u'name': u'United States Dollar'} + ] + results = self.GDAX.getCurrencies() + self.assertEqual(results, correct) + if __name__ == '__main__': unittest.main() From 904eea4caf33ac11064109bbf00a44feea9ebc64 Mon Sep 17 00:00:00 2001 From: mits9 Date: Sat, 7 Jan 2017 14:18:21 -0600 Subject: [PATCH 23/59] Added test for getTime and cassette --- tests/cassettes/public/test_getTime | 34 +++++++++++++++++++++++++++++ tests/test_GDAXPublicClient.py | 7 ++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/cassettes/public/test_getTime diff --git a/tests/cassettes/public/test_getTime b/tests/cassettes/public/test_getTime new file mode 100644 index 00000000..018e4aa8 --- /dev/null +++ b/tests/cassettes/public/test_getTime @@ -0,0 +1,34 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Connection: [keep-alive] + User-Agent: [python-requests/2.5.1 CPython/2.7.12 Darwin/15.6.0] + method: GET + uri: https://api.gdax.com/time + response: + body: + string: !!binary | + H4sIAAAAAAAAA6pWyizOV7JSMjIwNNc1MNQ1MA8xMrAyNLMyNtSzNDeLUtJRSi3IT85QsjI0sTC2 + MDIwtARL1AIAAAD//wMAl5binTkAAAA= + headers: + access-control-allow-headers: ['Content-Type, Accept, cb-session'] + access-control-allow-methods: ['GET,POST,DELETE,PUT'] + access-control-allow-origin: ['*'] + access-control-expose-headers: ['cb-before, cb-after'] + access-control-max-age: ['7200'] + cf-ray: [31da16870aa658d9-DFW] + connection: [keep-alive] + content-encoding: [gzip] + content-type: [application/json; charset=utf-8] + date: ['Sat, 07 Jan 2017 20:16:32 GMT'] + etag: [W/"39-MC2UtyVZSHHVi6qRM3DPlA"] + server: [cloudflare-nginx] + set-cookie: ['__cfduid=d23d9e3df6422b5d7b6d38199e209e76a1483820191; expires=Sun, + 07-Jan-18 20:16:31 GMT; path=/; domain=.gdax.com; HttpOnly'] + strict-transport-security: [max-age=15552000; includeSubDomains; preload] + x-content-type-options: [nosniff] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/test_GDAXPublicClient.py b/tests/test_GDAXPublicClient.py index e58c524a..7a483acf 100644 --- a/tests/test_GDAXPublicClient.py +++ b/tests/test_GDAXPublicClient.py @@ -214,5 +214,12 @@ def test_getCurrencies(self): results = self.GDAX.getCurrencies() self.assertEqual(results, correct) + @my_vcr.use_cassette() + def test_getTime(self): + correct = {u'epoch': 1483820191.976, u'iso': u'2017-01-07T20:16:31.976Z'} + results = self.GDAX.getTime() + self.assertEqual(results, correct) + + if __name__ == '__main__': unittest.main() From 9d8328d0f17ac904959fd5a87be09106e4d192a0 Mon Sep 17 00:00:00 2001 From: Philip J Freeman Date: Wed, 18 Jan 2017 18:50:12 -0800 Subject: [PATCH 24/59] daemonize websocket thread (kill when main exits) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit from https://docs.python.org/2/library/threading.html: ``` A thread can be flagged as a “daemon thread”. The significance of this flag is that the entire Python program exits when only daemon threads are left. The initial value is inherited from the creating thread. The flag can be set through the daemon property. ``` --- GDAX/WebsocketClient.py | 1 + 1 file changed, 1 insertion(+) diff --git a/GDAX/WebsocketClient.py b/GDAX/WebsocketClient.py index 187b1e29..4f7225f0 100644 --- a/GDAX/WebsocketClient.py +++ b/GDAX/WebsocketClient.py @@ -21,6 +21,7 @@ def go(): self.listen() self.thread = Thread(target=go) + self.thread.daemon = True self.thread.start() def connect(self): From b8094cd8d34f80a05c58d9447b66cee63bbb59ea Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Thu, 22 Jun 2017 18:58:22 -0700 Subject: [PATCH 25/59] add dev branch --- gdax/order_book.py | 4 ++-- gdax/websocket_client.py | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index 11fc6fb6..a95b074f 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -159,14 +159,14 @@ def change(self, order): bids = self.get_bids(price) if bids is None or not any(o['id'] == order['order_id'] for o in bids): return - index = map(itemgetter('id'), bids).index(order['order_id']) + index = [b['id'] for b in bids].index(order['order_id']) bids[index]['size'] = new_size self.set_bids(price, bids) else: asks = self.get_asks(price) if asks is None or not any(o['id'] == order['order_id'] for o in asks): return - index = map(itemgetter('id'), asks).index(order['order_id']) + index = [a['id'] for a in asks].index(order['order_id']) asks[index]['size'] = new_size self.set_asks(price, asks) diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index 28633e32..e2c8a2b0 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -5,10 +5,11 @@ # Template object to receive messages from the GDAX Websocket Feed from __future__ import print_function +import time import json from threading import Thread -from websocket import create_connection, WebSocketConnectionClosedException +from websocket import create_connection, WebSocketConnectionClosedException, WebSocketBadStatusException class WebsocketClient(object): @@ -22,7 +23,8 @@ def __init__(self, url="wss://ws-feed.gdax.com", products=None, message_type="su def start(self): def _go(): - self._connect() + while not self._connect(): + time.sleep(1) self._listen() self.on_open() @@ -38,7 +40,10 @@ def _connect(self): if self.url[-1] == "/": self.url = self.url[:-1] - self.ws = create_connection(self.url) + try: + self.ws = create_connection(self.url) + except WebSocketBadStatusException: + return False self.stop = False sub_params = {'type': 'subscribe', 'product_ids': self.products} @@ -46,6 +51,7 @@ def _connect(self): if self.type == "heartbeat": sub_params = {"type": "heartbeat", "on": True} self.ws.send(json.dumps(sub_params)) + return True def _listen(self): while not self.stop: @@ -62,6 +68,7 @@ def close(self): self.ws.send(json.dumps({"type": "heartbeat", "on": False})) self.on_close() self.stop = True + self.ws = None try: if self.ws: self.ws.close() From d028cc66339bd8c5c063ab13cdc3661c6ba5e9d9 Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Sun, 25 Jun 2017 10:01:21 -0700 Subject: [PATCH 26/59] Remove logto --- gdax/order_book.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index a6024e36..34d03deb 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -19,15 +19,9 @@ def __init__(self, product_id='BTC-USD'): self._bids = RBTree() self._client = PublicClient(product_id=product_id) self._sequence = -1 - self._log_to = log_to - if self._log_to: - assert hasattr(self._log_to, 'write') self._current_ticker = None def on_message(self, message): - if self._log_to: - pickle.dump(message, self._log_to) - sequence = message['sequence'] if self._sequence == -1: self._asks = RBTree() From 4e8ea12461d013a784680d3bafd82fe5bd5a663d Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Mon, 26 Jun 2017 05:58:00 -0700 Subject: [PATCH 27/59] store product id --- gdax/order_book.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index 34d03deb..702585c1 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -17,7 +17,8 @@ def __init__(self, product_id='BTC-USD'): super(OrderBook, self).__init__(products=product_id) self._asks = RBTree() self._bids = RBTree() - self._client = PublicClient(product_id=product_id) + self._client = PublicClient() + self._product_id = product_id self._sequence = -1 self._current_ticker = None @@ -26,7 +27,7 @@ def on_message(self, message): if self._sequence == -1: self._asks = RBTree() self._bids = RBTree() - res = self._client.get_product_order_book(level=3) + res = self._client.get_product_order_book(self._product_id, level=3) for bid in res['bids']: self.add({ 'id': bid[2], From 2c23994d246bc7afcaeac646a5389175aa047752 Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Mon, 26 Jun 2017 23:14:29 -0700 Subject: [PATCH 28/59] Remove old websocket --- GDAX/WebsocketClient.py | 74 ----------------------------------------- 1 file changed, 74 deletions(-) delete mode 100644 GDAX/WebsocketClient.py diff --git a/GDAX/WebsocketClient.py b/GDAX/WebsocketClient.py deleted file mode 100644 index 4f7225f0..00000000 --- a/GDAX/WebsocketClient.py +++ /dev/null @@ -1,74 +0,0 @@ -# -# GDAX/WebsocketClient.py -# Daniel Paquin -# -# Template object to receive messages from the GDAX Websocket Feed - -import json, time -from threading import Thread -from websocket import create_connection - -class WebsocketClient(object): - def __init__(self, ws_url="wss://ws-feed-public.sandbox.gdax.com", product_id="BTC-USD"): - if ws_url[-1] == "/": - self.url = ws_url[:-1] - else: - self.url = ws_url - self.product_id = product_id - - def go(): - self.connect() - self.listen() - - self.thread = Thread(target=go) - self.thread.daemon = True - self.thread.start() - - def connect(self): - self.open() - self.stop = False - self.ws = create_connection(self.url) - if type(self.product_id) is list: - #product_ids - plural for multiple products - subParams = json.dumps({"type": "subscribe", "product_ids": self.product_id}) - else: - subParams = json.dumps({"type": "subscribe", "product_id": self.product_id}) - self.ws.send(subParams) - - def open(self): - print("-- Subscribed! --") - - def listen(self): - while not self.stop: - try: - msg = json.loads(self.ws.recv()) - except Exception as e: - self.on_error(e) - else: - self.message(msg) - - def on_error(self, e): - self.close() - - def message(self, msg): - print(msg) - - def close(self): - self.stop = True - if self.ws and self.ws.connected: - self.ws.close() - self.ws = None - self.closed() - - def closed(self): - print("Socket Closed") - -if __name__ == "__main__": - newWS = WebsocketClient() # Runs in a separate thread - try: - while True: - time.sleep(0.1) - except KeyboardInterrupt: - newWS.stop = True - newWS.thread.join() - newWS.close() From ead4869d64cef5b0a616a26870f9b9d69c777c6a Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Mon, 26 Jun 2017 23:15:18 -0700 Subject: [PATCH 29/59] Clean up imports --- gdax/order_book.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index 702585c1..a8e9e766 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -4,10 +4,8 @@ # # Live order book updated from the gdax Websocket Feed -from operator import itemgetter from bintrees import RBTree from decimal import Decimal -import pickle from gdax.public_client import PublicClient from gdax.websocket_client import WebsocketClient From d32055b04ca4788d517d33c9899c677daad90ea6 Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Mon, 26 Jun 2017 23:17:17 -0700 Subject: [PATCH 30/59] Handle badstatus when connecting --- gdax/websocket_client.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index 1fc42209..0352dacc 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -5,11 +5,10 @@ # Template object to receive messages from the gdax Websocket Feed from __future__ import print_function -import time import json from threading import Thread -from websocket import create_connection, WebSocketConnectionClosedException, WebSocketBadStatusException +from websocket import create_connection, WebSocketConnectionClosedException class WebsocketClient(object): @@ -23,8 +22,7 @@ def __init__(self, url="wss://ws-feed.gdax.com", products=None, message_type="su def start(self): def _go(): - while not self._connect(): - time.sleep(1) + self._connect() self._listen() self.on_open() @@ -40,10 +38,7 @@ def _connect(self): if self.url[-1] == "/": self.url = self.url[:-1] - try: - self.ws = create_connection(self.url) - except WebSocketBadStatusException: - return False + self.ws = create_connection(self.url) self.stop = False sub_params = {'type': 'subscribe', 'product_ids': self.products} @@ -51,7 +46,6 @@ def _connect(self): if self.type == "heartbeat": sub_params = {"type": "heartbeat", "on": True} self.ws.send(json.dumps(sub_params)) - return True def _listen(self): while not self.stop: @@ -68,7 +62,6 @@ def close(self): self.ws.send(json.dumps({"type": "heartbeat", "on": False})) self.on_close() self.stop = True - self.ws = None try: if self.ws: self.ws.close() From d0d2a60af781326bb43f34d35628d9ef84e9889e Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Mon, 26 Jun 2017 23:23:55 -0700 Subject: [PATCH 31/59] Merging websocket connection handling --- gdax/websocket_client.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index 0352dacc..1fc42209 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -5,10 +5,11 @@ # Template object to receive messages from the gdax Websocket Feed from __future__ import print_function +import time import json from threading import Thread -from websocket import create_connection, WebSocketConnectionClosedException +from websocket import create_connection, WebSocketConnectionClosedException, WebSocketBadStatusException class WebsocketClient(object): @@ -22,7 +23,8 @@ def __init__(self, url="wss://ws-feed.gdax.com", products=None, message_type="su def start(self): def _go(): - self._connect() + while not self._connect(): + time.sleep(1) self._listen() self.on_open() @@ -38,7 +40,10 @@ def _connect(self): if self.url[-1] == "/": self.url = self.url[:-1] - self.ws = create_connection(self.url) + try: + self.ws = create_connection(self.url) + except WebSocketBadStatusException: + return False self.stop = False sub_params = {'type': 'subscribe', 'product_ids': self.products} @@ -46,6 +51,7 @@ def _connect(self): if self.type == "heartbeat": sub_params = {"type": "heartbeat", "on": True} self.ws.send(json.dumps(sub_params)) + return True def _listen(self): while not self.stop: @@ -62,6 +68,7 @@ def close(self): self.ws.send(json.dumps({"type": "heartbeat", "on": False})) self.on_close() self.stop = True + self.ws = None try: if self.ws: self.ws.close() From 745a32a04496ae1ac829064c2bc529099faacb49 Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Wed, 28 Jun 2017 09:19:08 -0700 Subject: [PATCH 32/59] Check for key errors --- gdax/order_book.py | 52 +++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index a8e9e766..0454b2fa 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -6,6 +6,7 @@ from bintrees import RBTree from decimal import Decimal +import time from gdax.public_client import PublicClient from gdax.websocket_client import WebsocketClient @@ -48,6 +49,7 @@ def on_message(self, message): elif sequence > self._sequence + 1: print('Error: messages missing ({} - {}). Re-initializing websocket.'.format(sequence, self._sequence)) self.close() + time.sleep(1) self.start() return @@ -100,11 +102,17 @@ def add(self, order): self.set_asks(order['price'], asks) def remove(self, order): - price = Decimal(order['price']) - if order['side'] == 'buy': + try: + price = Decimal(order['price']) + order_id = order['order_id'] + side = order['side'] + except KeyError: + return + + if side == 'buy': bids = self.get_bids(price) if bids is not None: - bids = [o for o in bids if o['id'] != order['order_id']] + bids = [o for o in bids if o['id'] != order_id] if len(bids) > 0: self.set_bids(price, bids) else: @@ -112,21 +120,26 @@ def remove(self, order): else: asks = self.get_asks(price) if asks is not None: - asks = [o for o in asks if o['id'] != order['order_id']] + asks = [o for o in asks if o['id'] != order_id] if len(asks) > 0: self.set_asks(price, asks) else: self.remove_asks(price) def match(self, order): - size = Decimal(order['size']) - price = Decimal(order['price']) + try: + size = Decimal(order['size']) + price = Decimal(order['price']) + side = order['side'] + maker_order_id = order['maker_order_id'] + except KeyError: + return - if order['side'] == 'buy': + if side == 'buy': bids = self.get_bids(price) if not bids: return - assert bids[0]['id'] == order['maker_order_id'] + assert bids[0]['id'] == maker_order_id if bids[0]['size'] == size: self.set_bids(price, bids[1:]) else: @@ -136,7 +149,7 @@ def match(self, order): asks = self.get_asks(price) if not asks: return - assert asks[0]['id'] == order['maker_order_id'] + assert asks[0]['id'] == maker_order_id if asks[0]['size'] == size: self.set_asks(price, asks[1:]) else: @@ -144,28 +157,33 @@ def match(self, order): self.set_asks(price, asks) def change(self, order): - new_size = Decimal(order['new_size']) - price = Decimal(order['price']) + try: + new_size = Decimal(order['new_size']) + price = Decimal(order['price']) + order_id = order['order_id'] + side = order['side'] + except KeyError: + return - if order['side'] == 'buy': + if side == 'buy': bids = self.get_bids(price) - if bids is None or not any(o['id'] == order['order_id'] for o in bids): + if bids is None or not any(o['id'] == order_id for o in bids): return - index = [b['id'] for b in bids].index(order['order_id']) + index = [b['id'] for b in bids].index(order_id) bids[index]['size'] = new_size self.set_bids(price, bids) else: asks = self.get_asks(price) - if asks is None or not any(o['id'] == order['order_id'] for o in asks): + if asks is None or not any(o['id'] == order_id for o in asks): return - index = [a['id'] for a in asks].index(order['order_id']) + index = [a['id'] for a in asks].index(order_id) asks[index]['size'] = new_size self.set_asks(price, asks) tree = self._asks if order['side'] == 'sell' else self._bids node = tree.get(price) - if node is None or not any(o['id'] == order['order_id'] for o in node): + if node is None or not any(o['id'] == order_id for o in node): return def get_current_ticker(self): From fb4980e9a287d5df05c0b66ee77be20554f1239b Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Tue, 11 Jul 2017 15:32:14 +0000 Subject: [PATCH 33/59] Catch type error --- gdax/order_book.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index 0454b2fa..3765735b 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -200,7 +200,7 @@ def get_current_book(self): # There can be a race condition here, where a price point is removed # between these two ops this_ask = self._asks[ask] - except KeyError: + except KeyError, TypeError: continue for order in this_ask: result['asks'].append([order['price'], order['size'], order['id']]) @@ -209,7 +209,7 @@ def get_current_book(self): # There can be a race condition here, where a price point is removed # between these two ops this_bid = self._bids[bid] - except KeyError: + except KeyError, TypeError: continue for order in this_bid: From cd4a8363102b58bd98d15e756f84058f68b963cc Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Thu, 13 Jul 2017 04:55:18 +0000 Subject: [PATCH 34/59] Fix error in except --- gdax/order_book.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index 3765735b..c5a94b3f 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -200,7 +200,7 @@ def get_current_book(self): # There can be a race condition here, where a price point is removed # between these two ops this_ask = self._asks[ask] - except KeyError, TypeError: + except (KeyError, TypeError): continue for order in this_ask: result['asks'].append([order['price'], order['size'], order['id']]) @@ -209,7 +209,7 @@ def get_current_book(self): # There can be a race condition here, where a price point is removed # between these two ops this_bid = self._bids[bid] - except KeyError, TypeError: + except (KeyError, TypeError): continue for order in this_bid: From 5701e1a28ce7c9094827b1783a4ad46e4550d008 Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Tue, 12 Sep 2017 21:03:10 -0700 Subject: [PATCH 35/59] Cleaning up some logic for stop start, added restart --- gdax/order_book.py | 8 +++++--- gdax/websocket_client.py | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index c5a94b3f..75fc04c8 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -48,9 +48,7 @@ def on_message(self, message): return elif sequence > self._sequence + 1: print('Error: messages missing ({} - {}). Re-initializing websocket.'.format(sequence, self._sequence)) - self.close() - time.sleep(1) - self.start() + self._restart() return msg_type = message['type'] @@ -75,8 +73,12 @@ def on_message(self, message): # print('bid: %f @ %f - ask: %f @ %f' % (bid_depth, bid, ask_depth, ask)) def on_error(self, e): + self._restart() + + def _restart(self): self._sequence = -1 self.close() + time.sleep(1) self.start() def add(self, order): diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index 1fc42209..feb6e552 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -27,7 +27,6 @@ def _go(): time.sleep(1) self._listen() - self.on_open() self.thread = Thread(target=_go) self.thread.start() @@ -45,12 +44,14 @@ def _connect(self): except WebSocketBadStatusException: return False - self.stop = False sub_params = {'type': 'subscribe', 'product_ids': self.products} - self.ws.send(json.dumps(sub_params)) + if self.type == "heartbeat": sub_params = {"type": "heartbeat", "on": True} - self.ws.send(json.dumps(sub_params)) + + self.stop = False + self.ws.send(json.dumps(sub_params)) + self.on_open() return True def _listen(self): @@ -66,15 +67,17 @@ def close(self): if not self.stop: if self.type == "heartbeat": self.ws.send(json.dumps({"type": "heartbeat", "on": False})) - self.on_close() - self.stop = True - self.ws = None + try: if self.ws: self.ws.close() - except WebSocketConnectionClosedException as e: + except WebSocketConnectionClosedException: pass + self.stop = True + self.on_close() + self.ws = None + def on_open(self): print("-- Subscribed! --\n") From 1e052869e761a0b6e24c16966c81b9d4c3b1445e Mon Sep 17 00:00:00 2001 From: uzbit Date: Thu, 19 Oct 2017 10:33:53 -0700 Subject: [PATCH 36/59] join thread --- gdax/websocket_client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index feb6e552..e100692b 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -76,6 +76,7 @@ def close(self): self.stop = True self.on_close() + self.thread.join() self.ws = None def on_open(self): From 37bc819df68dd2504358eacfa20a259ad01dd478 Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Fri, 20 Oct 2017 16:37:02 +0000 Subject: [PATCH 37/59] Fix restart --- gdax/order_book.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index 75fc04c8..c30eb884 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -48,7 +48,7 @@ def on_message(self, message): return elif sequence > self._sequence + 1: print('Error: messages missing ({} - {}). Re-initializing websocket.'.format(sequence, self._sequence)) - self._restart() + self.restart() return msg_type = message['type'] @@ -73,9 +73,9 @@ def on_message(self, message): # print('bid: %f @ %f - ask: %f @ %f' % (bid_depth, bid, ask_depth, ask)) def on_error(self, e): - self._restart() + self.restart() - def _restart(self): + def restart(self): self._sequence = -1 self.close() time.sleep(1) From bbf863d81bff97ebf5fcaccedb820b9e55e1bde1 Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Tue, 24 Oct 2017 01:42:47 +0000 Subject: [PATCH 38/59] remove join --- gdax/websocket_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index e100692b..2e51a352 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -76,7 +76,7 @@ def close(self): self.stop = True self.on_close() - self.thread.join() + #self.thread.join() self.ws = None def on_open(self): From 6539e34662607b4178f5dde9f0a5920e991a8660 Mon Sep 17 00:00:00 2001 From: uzbit Date: Wed, 25 Oct 2017 19:47:01 -0700 Subject: [PATCH 39/59] Debugging, keepalive --- gdax/websocket_client.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index 2e51a352..d4b460e9 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -25,6 +25,7 @@ def start(self): def _go(): while not self._connect(): time.sleep(1) + print("Trying to connect...") self._listen() self.thread = Thread(target=_go) @@ -42,6 +43,7 @@ def _connect(self): try: self.ws = create_connection(self.url) except WebSocketBadStatusException: + print("WebSocketBadStatusException...") return False sub_params = {'type': 'subscribe', 'product_ids': self.products} @@ -57,6 +59,9 @@ def _connect(self): def _listen(self): while not self.stop: try: + if int(time.time() % 30) == 0: + # Set a 30 second ping to keep connection alive + self.ws.ping("keepalive") msg = json.loads(self.ws.recv()) except Exception as e: self.on_error(e) From 7a7adf5b564a6b99f3d5755e5277c43290408027 Mon Sep 17 00:00:00 2001 From: uzbit Date: Sat, 28 Oct 2017 16:53:24 -0700 Subject: [PATCH 40/59] Adding more debugging --- gdax/order_book.py | 15 +++++---------- gdax/websocket_client.py | 7 ++++--- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index c30eb884..2bf5180b 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -45,6 +45,7 @@ def on_message(self, message): if sequence <= self._sequence: # ignore older messages (e.g. before order book initialization from getProductOrderBook) + print("Error: sequence too old.") return elif sequence > self._sequence + 1: print('Error: messages missing ({} - {}). Re-initializing websocket.'.format(sequence, self._sequence)) @@ -64,18 +65,11 @@ def on_message(self, message): self._sequence = sequence - # bid = self.get_bid() - # bids = self.get_bids(bid) - # bid_depth = sum([b['size'] for b in bids]) - # ask = self.get_ask() - # asks = self.get_asks(ask) - # ask_depth = sum([a['size'] for a in asks]) - # print('bid: %f @ %f - ask: %f @ %f' % (bid_depth, bid, ask_depth, ask)) - def on_error(self, e): self.restart() def restart(self): + print("Error: order_book restarting") self._sequence = -1 self.close() time.sleep(1) @@ -197,6 +191,7 @@ def get_current_book(self): 'asks': [], 'bids': [], } + for ask in self._asks: try: # There can be a race condition here, where a price point is removed @@ -206,6 +201,7 @@ def get_current_book(self): continue for order in this_ask: result['asks'].append([order['price'], order['size'], order['id']]) + for bid in self._bids: try: # There can be a race condition here, where a price point is removed @@ -213,9 +209,9 @@ def get_current_book(self): this_bid = self._bids[bid] except (KeyError, TypeError): continue - for order in this_bid: result['bids'].append([order['price'], order['size'], order['id']]) + return result def get_ask(self): @@ -244,7 +240,6 @@ def set_bids(self, price, bids): if __name__ == '__main__': - import time order_book = OrderBook() order_book.start() diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index d4b460e9..ea66f2af 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -25,7 +25,7 @@ def start(self): def _go(): while not self._connect(): time.sleep(1) - print("Trying to connect...") + print("Error: Trying to connect...") self._listen() self.thread = Thread(target=_go) @@ -43,7 +43,7 @@ def _connect(self): try: self.ws = create_connection(self.url) except WebSocketBadStatusException: - print("WebSocketBadStatusException...") + print("Error: WebSocketBadStatusException") return False sub_params = {'type': 'subscribe', 'product_ids': self.products} @@ -94,7 +94,8 @@ def on_message(self, msg): print(msg) def on_error(self, e): - return + print("Error: " + str(e)) + if __name__ == "__main__": import gdax From a73f5f9a5b6bedbbb3fefa25e89eb0a8850e624e Mon Sep 17 00:00:00 2001 From: uzbit Date: Tue, 31 Oct 2017 23:49:09 -0700 Subject: [PATCH 41/59] Move the thread join --- gdax/websocket_client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index ea66f2af..932eee5f 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -30,6 +30,8 @@ def _go(): self.thread = Thread(target=_go) self.thread.start() + self.thread.join() + def _connect(self): if self.products is None: @@ -81,7 +83,6 @@ def close(self): self.stop = True self.on_close() - #self.thread.join() self.ws = None def on_open(self): From ad13ba45216a6f1ead6621d328ffa0bbc29c3748 Mon Sep 17 00:00:00 2001 From: uzbit Date: Tue, 31 Oct 2017 23:54:27 -0700 Subject: [PATCH 42/59] Move the thread join --- gdax/websocket_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index 932eee5f..edad6e1c 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -30,8 +30,6 @@ def _go(): self.thread = Thread(target=_go) self.thread.start() - self.thread.join() - def _connect(self): if self.products is None: @@ -83,6 +81,8 @@ def close(self): self.stop = True self.on_close() + if self.thread: + self.thread.join() self.ws = None def on_open(self): From 2a9c70fff97351a4fa7ea25acd44e658a05827df Mon Sep 17 00:00:00 2001 From: uzbit Date: Wed, 1 Nov 2017 00:03:57 -0700 Subject: [PATCH 43/59] Move go out of method --- gdax/websocket_client.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index edad6e1c..802b4175 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -22,15 +22,15 @@ def __init__(self, url="wss://ws-feed.gdax.com", products=None, message_type="su self.thread = None def start(self): - def _go(): - while not self._connect(): - time.sleep(1) - print("Error: Trying to connect...") - self._listen() - - self.thread = Thread(target=_go) + self.thread = Thread(target=self._go) self.thread.start() + def _go(self): + while not self._connect(): + time.sleep(1) + print("Error: Trying to connect...") + self._listen() + def _connect(self): if self.products is None: self.products = ["BTC-USD"] From d4e4018d86a3bd3fef954960f72cfa4b92efa952 Mon Sep 17 00:00:00 2001 From: uzbit Date: Wed, 1 Nov 2017 00:11:12 -0700 Subject: [PATCH 44/59] Debugging --- gdax/order_book.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gdax/order_book.py b/gdax/order_book.py index 2bf5180b..d760a71e 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -66,6 +66,7 @@ def on_message(self, message): self._sequence = sequence def on_error(self, e): + print("Error: " + str(e)) self.restart() def restart(self): From 9dc9ab63d7922c280176db11aa2dd57b8194a20e Mon Sep 17 00:00:00 2001 From: uzbit Date: Thu, 2 Nov 2017 20:45:55 -0700 Subject: [PATCH 45/59] Move join --- gdax/websocket_client.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index 802b4175..a5686fc3 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -24,6 +24,7 @@ def __init__(self, url="wss://ws-feed.gdax.com", products=None, message_type="su def start(self): self.thread = Thread(target=self._go) self.thread.start() + self.thread.join() def _go(self): while not self._connect(): @@ -81,8 +82,6 @@ def close(self): self.stop = True self.on_close() - if self.thread: - self.thread.join() self.ws = None def on_open(self): From 9afcc8ff054d283884c73f9d8acf0d9f22a102a8 Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Tue, 7 Nov 2017 04:45:51 +0000 Subject: [PATCH 46/59] Restart if sequence is too old --- gdax/order_book.py | 5 +++-- gdax/websocket_client.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index d760a71e..c9fc285b 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -46,11 +46,12 @@ def on_message(self, message): if sequence <= self._sequence: # ignore older messages (e.g. before order book initialization from getProductOrderBook) print("Error: sequence too old.") - return + self.restart() + return elif sequence > self._sequence + 1: print('Error: messages missing ({} - {}). Re-initializing websocket.'.format(sequence, self._sequence)) self.restart() - return + return msg_type = message['type'] if msg_type == 'open': diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index a5686fc3..10cc7ec1 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -24,7 +24,7 @@ def __init__(self, url="wss://ws-feed.gdax.com", products=None, message_type="su def start(self): self.thread = Thread(target=self._go) self.thread.start() - self.thread.join() + #self.thread.join() def _go(self): while not self._connect(): From 895047fe5784a68aea1ae143c42530f731ece120 Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Tue, 7 Nov 2017 04:53:55 +0000 Subject: [PATCH 47/59] No restart, just set sequence --- gdax/order_book.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index c9fc285b..83f36c39 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -46,8 +46,8 @@ def on_message(self, message): if sequence <= self._sequence: # ignore older messages (e.g. before order book initialization from getProductOrderBook) print("Error: sequence too old.") - self.restart() - return + self._sequence = sequence + return elif sequence > self._sequence + 1: print('Error: messages missing ({} - {}). Re-initializing websocket.'.format(sequence, self._sequence)) self.restart() From 6383f1e48d015f7fd6086ce5902a1c0ab65ad4cc Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Mon, 27 Nov 2017 00:49:11 +0000 Subject: [PATCH 48/59] Use FastRBTree --- gdax/order_book.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index 83f36c39..448104cc 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -4,7 +4,7 @@ # # Live order book updated from the gdax Websocket Feed -from bintrees import RBTree +from bintrees import FastRBTree from decimal import Decimal import time @@ -14,8 +14,8 @@ class OrderBook(WebsocketClient): def __init__(self, product_id='BTC-USD'): super(OrderBook, self).__init__(products=product_id) - self._asks = RBTree() - self._bids = RBTree() + self._asks = FastRBTree() + self._bids = FastRBTree() self._client = PublicClient() self._product_id = product_id self._sequence = -1 @@ -24,8 +24,8 @@ def __init__(self, product_id='BTC-USD'): def on_message(self, message): sequence = message['sequence'] if self._sequence == -1: - self._asks = RBTree() - self._bids = RBTree() + self._asks = FastRBTree() + self._bids = FastRBTree() res = self._client.get_product_order_book(self._product_id, level=3) for bid in res['bids']: self.add({ From 81e6be309dd9fefd057c596b10519558ae6ab448 Mon Sep 17 00:00:00 2001 From: uzbit Date: Wed, 6 Dec 2017 09:09:09 -0800 Subject: [PATCH 49/59] Error handling --- gdax/websocket_client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index 10cc7ec1..8a238d6e 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -28,7 +28,7 @@ def start(self): def _go(self): while not self._connect(): - time.sleep(1) + time.sleep(10) print("Error: Trying to connect...") self._listen() @@ -43,8 +43,8 @@ def _connect(self): try: self.ws = create_connection(self.url) - except WebSocketBadStatusException: - print("Error: WebSocketBadStatusException") + except Exception as e: + print("Error connecting: %s" % str(e)) return False sub_params = {'type': 'subscribe', 'product_ids': self.products} From e2adc145b41253165ca52effbff4a57b70687355 Mon Sep 17 00:00:00 2001 From: uzbit Date: Sun, 10 Dec 2017 12:59:31 -0800 Subject: [PATCH 50/59] Reset ticker --- gdax/order_book.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index 448104cc..5cb77f7a 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -4,7 +4,7 @@ # # Live order book updated from the gdax Websocket Feed -from bintrees import FastRBTree +from bintrees import FastRBTree from decimal import Decimal import time @@ -47,11 +47,11 @@ def on_message(self, message): # ignore older messages (e.g. before order book initialization from getProductOrderBook) print("Error: sequence too old.") self._sequence = sequence - return + return elif sequence > self._sequence + 1: print('Error: messages missing ({} - {}). Re-initializing websocket.'.format(sequence, self._sequence)) self.restart() - return + return msg_type = message['type'] if msg_type == 'open': @@ -73,6 +73,7 @@ def on_error(self, e): def restart(self): print("Error: order_book restarting") self._sequence = -1 + self._current_ticker = None self.close() time.sleep(1) self.start() From 7cf1a5b9b42b425635d0a49d0996f860c6184b24 Mon Sep 17 00:00:00 2001 From: uzbit Date: Tue, 12 Dec 2017 09:41:16 -0800 Subject: [PATCH 51/59] debugging --- gdax/order_book.py | 2 ++ gdax/websocket_client.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/gdax/order_book.py b/gdax/order_book.py index 5cb77f7a..a56cb24e 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -6,6 +6,7 @@ from bintrees import FastRBTree from decimal import Decimal +import traceback import time from gdax.public_client import PublicClient @@ -67,6 +68,7 @@ def on_message(self, message): self._sequence = sequence def on_error(self, e): + traceback.print_exc() print("Error: " + str(e)) self.restart() diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index 8a238d6e..8b39a109 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -5,6 +5,7 @@ # Template object to receive messages from the gdax Websocket Feed from __future__ import print_function +import traceback import time import json @@ -94,6 +95,7 @@ def on_message(self, msg): print(msg) def on_error(self, e): + traceback.print_exc() print("Error: " + str(e)) From 16656ed860ec03e11341c6ed023c5cd10c82eb09 Mon Sep 17 00:00:00 2001 From: uzbit Date: Tue, 12 Dec 2017 21:58:32 -0800 Subject: [PATCH 52/59] Catch exception --- gdax/order_book.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index a56cb24e..dbc86541 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -27,7 +27,12 @@ def on_message(self, message): if self._sequence == -1: self._asks = FastRBTree() self._bids = FastRBTree() - res = self._client.get_product_order_book(self._product_id, level=3) + try: + res = self._client.get_product_order_book(self._product_id, level=3) + except Exception as e: + self.on_error(e) + return + for bid in res['bids']: self.add({ 'id': bid[2], @@ -77,7 +82,7 @@ def restart(self): self._sequence = -1 self._current_ticker = None self.close() - time.sleep(1) + time.sleep(5) self.start() def add(self, order): From 0fa75c66a8dd9d80816ec17a4549b385d9ed9fb3 Mon Sep 17 00:00:00 2001 From: uzbit Date: Thu, 14 Dec 2017 12:08:10 -0800 Subject: [PATCH 53/59] Adding kill --- gdax/websocket_client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index 8b39a109..ebf3491f 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -21,6 +21,7 @@ def __init__(self, url="wss://ws-feed.gdax.com", products=None, message_type="su self.stop = False self.ws = None self.thread = None + self.killme = False def start(self): self.thread = Thread(target=self._go) @@ -67,6 +68,7 @@ def _listen(self): msg = json.loads(self.ws.recv()) except Exception as e: self.on_error(e) + self.killme = True else: self.on_message(msg) From 4347f9d4affd3f89a9921596614cd8d363c88462 Mon Sep 17 00:00:00 2001 From: uzbit Date: Fri, 15 Dec 2017 13:25:41 -0800 Subject: [PATCH 54/59] Trying to clean up errors --- gdax/order_book.py | 28 ++++++++++++++++------------ gdax/websocket_client.py | 2 +- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index dbc86541..0857ecde 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -55,8 +55,8 @@ def on_message(self, message): self._sequence = sequence return elif sequence > self._sequence + 1: - print('Error: messages missing ({} - {}). Re-initializing websocket.'.format(sequence, self._sequence)) - self.restart() + error = 'Error: messages missing ({} - {}). Re-initializing websocket.'.format(sequence, self._sequence) + self.on_error(error) return msg_type = message['type'] @@ -65,7 +65,10 @@ def on_message(self, message): elif msg_type == 'done' and 'price' in message: self.remove(message) elif msg_type == 'match': - self.match(message) + try: + self.match(message) + except Exception as e: + self.on_error(e) self._current_ticker = message elif msg_type == 'change': self.change(message) @@ -75,15 +78,16 @@ def on_message(self, message): def on_error(self, e): traceback.print_exc() print("Error: " + str(e)) - self.restart() - - def restart(self): - print("Error: order_book restarting") - self._sequence = -1 - self._current_ticker = None - self.close() - time.sleep(5) - self.start() + self.killme = True + + # def restart(self): + # print("Error: order_book restarting") + # self.killme = True + # self._sequence = -1 + # self._current_ticker = None + # self.close() + # time.sleep(5) + # self.start() def add(self, order): order = { diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index ebf3491f..f5cc254c 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -68,7 +68,6 @@ def _listen(self): msg = json.loads(self.ws.recv()) except Exception as e: self.on_error(e) - self.killme = True else: self.on_message(msg) @@ -97,6 +96,7 @@ def on_message(self, msg): print(msg) def on_error(self, e): + self.killme = True traceback.print_exc() print("Error: " + str(e)) From ce5e99823cae5cebd4eea8e187652e378af3a6b1 Mon Sep 17 00:00:00 2001 From: uzbit Date: Mon, 25 Dec 2017 17:46:09 -0800 Subject: [PATCH 55/59] Stop also --- gdax/order_book.py | 15 --------------- gdax/websocket_client.py | 1 + 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index 0857ecde..5fb8e5d9 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -6,7 +6,6 @@ from bintrees import FastRBTree from decimal import Decimal -import traceback import time from gdax.public_client import PublicClient @@ -75,20 +74,6 @@ def on_message(self, message): self._sequence = sequence - def on_error(self, e): - traceback.print_exc() - print("Error: " + str(e)) - self.killme = True - - # def restart(self): - # print("Error: order_book restarting") - # self.killme = True - # self._sequence = -1 - # self._current_ticker = None - # self.close() - # time.sleep(5) - # self.start() - def add(self, order): order = { 'id': order.get('order_id') or order['id'], diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index f5cc254c..abc84ae8 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -96,6 +96,7 @@ def on_message(self, msg): print(msg) def on_error(self, e): + self.stop = True self.killme = True traceback.print_exc() print("Error: " + str(e)) From 0593fa0916df34799c3da13c8f42f19abb310e09 Mon Sep 17 00:00:00 2001 From: Ted McCormack Date: Sat, 30 Dec 2017 22:53:52 +0000 Subject: [PATCH 56/59] Pulling upstream changes manually --- gdax/authenticated_client.py | 124 ++++++++++----------- gdax/gdax_auth.py | 34 ++++++ gdax/order_book.py | 202 ++++++++++++++++++++++------------- gdax/public_client.py | 50 ++++----- gdax/websocket_client.py | 146 +++++++++++++++---------- tests/test_public_client.py | 29 +++-- 6 files changed, 356 insertions(+), 229 deletions(-) create mode 100644 gdax/gdax_auth.py diff --git a/gdax/authenticated_client.py b/gdax/authenticated_client.py index ec9df0c4..bd1fe948 100644 --- a/gdax/authenticated_client.py +++ b/gdax/authenticated_client.py @@ -12,15 +12,17 @@ import json from requests.auth import AuthBase from gdax.public_client import PublicClient +from gdax.gdax_auth import GdaxAuth class AuthenticatedClient(PublicClient): - def __init__(self, key, b64secret, passphrase, api_url="https://api.gdax.com"): + def __init__(self, key, b64secret, passphrase, api_url="https://api.gdax.com", timeout=30): super(AuthenticatedClient, self).__init__(api_url) self.auth = GdaxAuth(key, b64secret, passphrase) + self.timeout = timeout def get_account(self, account_id): - r = requests.get(self.url + '/accounts/' + account_id, auth=self.auth) + r = requests.get(self.url + '/accounts/' + account_id, auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() @@ -29,7 +31,7 @@ def get_accounts(self): def get_account_history(self, account_id): result = [] - r = requests.get(self.url + '/accounts/{}/ledger'.format(account_id), auth=self.auth) + r = requests.get(self.url + '/accounts/{}/ledger'.format(account_id), auth=self.auth, timeout=self.timeout) # r.raise_for_status() result.append(r.json()) if "cb-after" in r.headers: @@ -37,7 +39,7 @@ def get_account_history(self, account_id): return result def history_pagination(self, account_id, result, after): - r = requests.get(self.url + '/accounts/{}/ledger?after={}'.format(account_id, str(after)), auth=self.auth) + r = requests.get(self.url + '/accounts/{}/ledger?after={}'.format(account_id, str(after)), auth=self.auth, timeout=self.timeout) # r.raise_for_status() if r.json(): result.append(r.json()) @@ -47,7 +49,7 @@ def history_pagination(self, account_id, result, after): def get_account_holds(self, account_id): result = [] - r = requests.get(self.url + '/accounts/{}/holds'.format(account_id), auth=self.auth) + r = requests.get(self.url + '/accounts/{}/holds'.format(account_id), auth=self.auth, timeout=self.timeout) # r.raise_for_status() result.append(r.json()) if "cb-after" in r.headers: @@ -55,7 +57,7 @@ def get_account_holds(self, account_id): return result def holds_pagination(self, account_id, result, after): - r = requests.get(self.url + '/accounts/{}/holds?after={}'.format(account_id, str(after)), auth=self.auth) + r = requests.get(self.url + '/accounts/{}/holds?after={}'.format(account_id, str(after)), auth=self.auth, timeout=self.timeout) # r.raise_for_status() if r.json(): result.append(r.json()) @@ -69,51 +71,67 @@ def buy(self, **kwargs): kwargs["product_id"] = self.product_id r = requests.post(self.url + '/orders', data=json.dumps(kwargs), - auth=self.auth) + auth=self.auth, + timeout=self.timeout) return r.json() def sell(self, **kwargs): kwargs["side"] = "sell" r = requests.post(self.url + '/orders', data=json.dumps(kwargs), - auth=self.auth) + auth=self.auth, + timeout=self.timeout) return r.json() def cancel_order(self, order_id): - r = requests.delete(self.url + '/orders/' + order_id, auth=self.auth) + r = requests.delete(self.url + '/orders/' + order_id, auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() - def cancel_all(self, data=None, product=''): - if type(data) is dict: - if "product" in data: - product = data["product"] - r = requests.delete(self.url + '/orders/', - data=json.dumps({'product_id': product or self.product_id}), auth=self.auth) + def cancel_all(self, product_id=''): + url = self.url + '/orders/' + if product_id: + url += "?product_id={}&".format(str(product_id)) + r = requests.delete(url, auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() def get_order(self, order_id): - r = requests.get(self.url + '/orders/' + order_id, auth=self.auth) + r = requests.get(self.url + '/orders/' + order_id, auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() - def get_orders(self): + def get_orders(self, product_id='', status=[]): result = [] - r = requests.get(self.url + '/orders/', auth=self.auth) + url = self.url + '/orders/' + params = {} + if product_id: + params["product_id"] = product_id + if status: + params["status"] = status + r = requests.get(url, auth=self.auth, params=params, timeout=self.timeout) # r.raise_for_status() result.append(r.json()) if 'cb-after' in r.headers: - self.paginate_orders(result, r.headers['cb-after']) + self.paginate_orders(product_id, status, result, r.headers['cb-after']) return result - def paginate_orders(self, result, after): - r = requests.get(self.url + '/orders?after={}'.format(str(after)), auth=self.auth) + def paginate_orders(self, product_id, status, result, after): + url = self.url + '/orders' + + params = { + "after": str(after), + } + if product_id: + params["product_id"] = product_id + if status: + params["status"] = status + r = requests.get(url, auth=self.auth, params=params, timeout=self.timeout) # r.raise_for_status() if r.json(): result.append(r.json()) if 'cb-after' in r.headers: - self.paginate_orders(result, r.headers['cb-after']) + self.paginate_orders(product_id, status, result, r.headers['cb-after']) return result def get_fills(self, order_id='', product_id='', before='', after='', limit=''): @@ -122,14 +140,14 @@ def get_fills(self, order_id='', product_id='', before='', after='', limit=''): if order_id: url += "order_id={}&".format(str(order_id)) if product_id: - url += "product_id={}&".format(product_id or self.product_id) + url += "product_id={}&".format(product_id) if before: url += "before={}&".format(str(before)) if after: url += "after={}&".format(str(after)) if limit: url += "limit={}&".format(str(limit)) - r = requests.get(url, auth=self.auth) + r = requests.get(url, auth=self.auth, timeout=self.timeout) # r.raise_for_status() result.append(r.json()) if 'cb-after' in r.headers and limit is not len(r.json()): @@ -141,8 +159,8 @@ def paginate_fills(self, result, after, order_id='', product_id=''): if order_id: url += "order_id={}&".format(str(order_id)) if product_id: - url += "product_id={}&".format(product_id or self.product_id) - r = requests.get(url, auth=self.auth) + url += "product_id={}&".format(product_id) + r = requests.get(url, auth=self.auth, timeout=self.timeout) # r.raise_for_status() if r.json(): result.append(r.json()) @@ -158,7 +176,7 @@ def get_fundings(self, result='', status='', after=''): url += "status={}&".format(str(status)) if after: url += 'after={}&'.format(str(after)) - r = requests.get(url, auth=self.auth) + r = requests.get(url, auth=self.auth, timeout=self.timeout) # r.raise_for_status() result.append(r.json()) if 'cb-after' in r.headers: @@ -170,7 +188,7 @@ def repay_funding(self, amount='', currency=''): "amount": amount, "currency": currency # example: USD } - r = requests.post(self.url + "/funding/repay", data=json.dumps(payload), auth=self.auth) + r = requests.post(self.url + "/funding/repay", data=json.dumps(payload), auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() @@ -181,12 +199,12 @@ def margin_transfer(self, margin_profile_id="", transfer_type="", currency="", a "currency": currency, # example: USD "amount": amount } - r = requests.post(self.url + "/profiles/margin-transfer", data=json.dumps(payload), auth=self.auth) + r = requests.post(self.url + "/profiles/margin-transfer", data=json.dumps(payload), auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() def get_position(self): - r = requests.get(self.url + "/position", auth=self.auth) + r = requests.get(self.url + "/position", auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() @@ -194,7 +212,7 @@ def close_position(self, repay_only=""): payload = { "repay_only": repay_only or False } - r = requests.post(self.url + "/position/close", data=json.dumps(payload), auth=self.auth) + r = requests.post(self.url + "/position/close", data=json.dumps(payload), auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() @@ -204,7 +222,7 @@ def deposit(self, amount="", currency="", payment_method_id=""): "currency": currency, "payment_method_id": payment_method_id } - r = requests.post(self.url + "/deposits/payment-method", data=json.dumps(payload), auth=self.auth) + r = requests.post(self.url + "/deposits/payment-method", data=json.dumps(payload), auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() @@ -214,7 +232,7 @@ def coinbase_deposit(self, amount="", currency="", coinbase_account_id=""): "currency": currency, "coinbase_account_id": coinbase_account_id } - r = requests.post(self.url + "/deposits/coinbase-account", data=json.dumps(payload), auth=self.auth) + r = requests.post(self.url + "/deposits/coinbase-account", data=json.dumps(payload), auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() @@ -224,7 +242,7 @@ def withdraw(self, amount="", currency="", payment_method_id=""): "currency": currency, "payment_method_id": payment_method_id } - r = requests.post(self.url + "/withdrawals/payment-method", data=json.dumps(payload), auth=self.auth) + r = requests.post(self.url + "/withdrawals/payment-method", data=json.dumps(payload), auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() @@ -234,7 +252,7 @@ def coinbase_withdraw(self, amount="", currency="", coinbase_account_id=""): "currency": currency, "coinbase_account_id": coinbase_account_id } - r = requests.post(self.url + "/withdrawals/coinbase", data=json.dumps(payload), auth=self.auth) + r = requests.post(self.url + "/withdrawals/coinbase", data=json.dumps(payload), auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() @@ -244,17 +262,17 @@ def crypto_withdraw(self, amount="", currency="", crypto_address=""): "currency": currency, "crypto_address": crypto_address } - r = requests.post(self.url + "/withdrawals/crypto", data=json.dumps(payload), auth=self.auth) + r = requests.post(self.url + "/withdrawals/crypto", data=json.dumps(payload), auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() def get_payment_methods(self): - r = requests.get(self.url + "/payment-methods", auth=self.auth) + r = requests.get(self.url + "/payment-methods", auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() def get_coinbase_accounts(self): - r = requests.get(self.url + "/coinbase-accounts", auth=self.auth) + r = requests.get(self.url + "/coinbase-accounts", auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() @@ -269,40 +287,16 @@ def create_report(self, report_type="", start_date="", end_date="", product_id=" "format": report_format, "email": email } - r = requests.post(self.url + "/reports", data=json.dumps(payload), auth=self.auth) + r = requests.post(self.url + "/reports", data=json.dumps(payload), auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() def get_report(self, report_id=""): - r = requests.get(self.url + "/reports/" + report_id, auth=self.auth) + r = requests.get(self.url + "/reports/" + report_id, auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() def get_trailing_volume(self): - r = requests.get(self.url + "/users/self/trailing-volume", auth=self.auth) + r = requests.get(self.url + "/users/self/trailing-volume", auth=self.auth, timeout=self.timeout) # r.raise_for_status() return r.json() - - -class GdaxAuth(AuthBase): - # Provided by gdax: https://docs.gdax.com/#signing-a-message - def __init__(self, api_key, secret_key, passphrase): - self.api_key = api_key - self.secret_key = secret_key - self.passphrase = passphrase - - def __call__(self, request): - timestamp = str(time.time()) - message = timestamp + request.method + request.path_url + (request.body or '') - message = message.encode('ascii') - hmac_key = base64.b64decode(self.secret_key) - signature = hmac.new(hmac_key, message, hashlib.sha256) - signature_b64 = base64.b64encode(signature.digest()) - request.headers.update({ - 'Content-Type': 'Application/JSON', - 'CB-ACCESS-SIGN': signature_b64, - 'CB-ACCESS-TIMESTAMP': timestamp, - 'CB-ACCESS-KEY': self.api_key, - 'CB-ACCESS-PASSPHRASE': self.passphrase - }) - return request diff --git a/gdax/gdax_auth.py b/gdax/gdax_auth.py new file mode 100644 index 00000000..14757c3c --- /dev/null +++ b/gdax/gdax_auth.py @@ -0,0 +1,34 @@ +import hmac +import hashlib +import time +import base64 +from requests.auth import AuthBase + + +class GdaxAuth(AuthBase): + # Provided by gdax: https://docs.gdax.com/#signing-a-message + def __init__(self, api_key, secret_key, passphrase): + self.api_key = api_key + self.secret_key = secret_key + self.passphrase = passphrase + + def __call__(self, request): + timestamp = str(time.time()) + message = timestamp + request.method + request.path_url + (request.body or '') + request.headers.update(get_auth_headers(timestamp, message, self.api_key, self.secret_key, + self.passphrase)) + return request + + +def get_auth_headers(timestamp, message, api_key, secret_key, passphrase): + message = message.encode('ascii') + hmac_key = base64.b64decode(secret_key) + signature = hmac.new(hmac_key, message, hashlib.sha256) + signature_b64 = base64.b64encode(signature.digest()).decode('utf-8') + return { + 'Content-Type': 'Application/JSON', + 'CB-ACCESS-SIGN': signature_b64, + 'CB-ACCESS-TIMESTAMP': timestamp, + 'CB-ACCESS-KEY': api_key, + 'CB-ACCESS-PASSPHRASE': passphrase + } diff --git a/gdax/order_book.py b/gdax/order_book.py index 5fb8e5d9..6af28f0c 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -1,61 +1,74 @@ # -# gdax/OrderBook.py +# gdax/order_book.py # David Caseria # # Live order book updated from the gdax Websocket Feed -from bintrees import FastRBTree +from bintrees import RBTree from decimal import Decimal -import time +import pickle from gdax.public_client import PublicClient from gdax.websocket_client import WebsocketClient + class OrderBook(WebsocketClient): - def __init__(self, product_id='BTC-USD'): + def __init__(self, product_id='BTC-USD', log_to=None): super(OrderBook, self).__init__(products=product_id) - self._asks = FastRBTree() - self._bids = FastRBTree() + self._asks = RBTree() + self._bids = RBTree() self._client = PublicClient() - self._product_id = product_id self._sequence = -1 + self._log_to = log_to + if self._log_to: + assert hasattr(self._log_to, 'write') self._current_ticker = None + @property + def product_id(self): + ''' Currently OrderBook only supports a single product even though it is stored as a list of products. ''' + return self.products[0] + + def on_open(self): + self._sequence = -1 + print("-- Subscribed to OrderBook! --\n") + + def on_close(self): + print("\n-- OrderBook Socket Closed! --") + + def reset_book(self): + self._asks = RBTree() + self._bids = RBTree() + res = self._client.get_product_order_book(product_id=self.product_id, level=3) + for bid in res['bids']: + self.add({ + 'id': bid[2], + 'side': 'buy', + 'price': Decimal(bid[0]), + 'size': Decimal(bid[1]) + }) + for ask in res['asks']: + self.add({ + 'id': ask[2], + 'side': 'sell', + 'price': Decimal(ask[0]), + 'size': Decimal(ask[1]) + }) + self._sequence = res['sequence'] + def on_message(self, message): + if self._log_to: + pickle.dump(message, self._log_to) + sequence = message['sequence'] if self._sequence == -1: - self._asks = FastRBTree() - self._bids = FastRBTree() - try: - res = self._client.get_product_order_book(self._product_id, level=3) - except Exception as e: - self.on_error(e) - return - - for bid in res['bids']: - self.add({ - 'id': bid[2], - 'side': 'buy', - 'price': Decimal(bid[0]), - 'size': Decimal(bid[1]) - }) - for ask in res['asks']: - self.add({ - 'id': ask[2], - 'side': 'sell', - 'price': Decimal(ask[0]), - 'size': Decimal(ask[1]) - }) - self._sequence = res['sequence'] - + self.reset_book() + return if sequence <= self._sequence: # ignore older messages (e.g. before order book initialization from getProductOrderBook) - print("Error: sequence too old.") - self._sequence = sequence return elif sequence > self._sequence + 1: - error = 'Error: messages missing ({} - {}). Re-initializing websocket.'.format(sequence, self._sequence) - self.on_error(error) + self.on_sequence_gap(self._sequence, sequence) return msg_type = message['type'] @@ -64,16 +77,19 @@ def on_message(self, message): elif msg_type == 'done' and 'price' in message: self.remove(message) elif msg_type == 'match': - try: - self.match(message) - except Exception as e: - self.on_error(e) + self.match(message) self._current_ticker = message elif msg_type == 'change': self.change(message) self._sequence = sequence + def on_sequence_gap(self, gap_start, gap_end): + self.reset_book() + print('Error: messages missing ({} - {}). Re-initializing book at sequence.'.format( + gap_start, gap_end, self._sequence)) + + def add(self, order): order = { 'id': order.get('order_id') or order['id'], @@ -97,17 +113,11 @@ def add(self, order): self.set_asks(order['price'], asks) def remove(self, order): - try: - price = Decimal(order['price']) - order_id = order['order_id'] - side = order['side'] - except KeyError: - return - - if side == 'buy': + price = Decimal(order['price']) + if order['side'] == 'buy': bids = self.get_bids(price) if bids is not None: - bids = [o for o in bids if o['id'] != order_id] + bids = [o for o in bids if o['id'] != order['order_id']] if len(bids) > 0: self.set_bids(price, bids) else: @@ -115,26 +125,21 @@ def remove(self, order): else: asks = self.get_asks(price) if asks is not None: - asks = [o for o in asks if o['id'] != order_id] + asks = [o for o in asks if o['id'] != order['order_id']] if len(asks) > 0: self.set_asks(price, asks) else: self.remove_asks(price) def match(self, order): - try: - size = Decimal(order['size']) - price = Decimal(order['price']) - side = order['side'] - maker_order_id = order['maker_order_id'] - except KeyError: - return + size = Decimal(order['size']) + price = Decimal(order['price']) - if side == 'buy': + if order['side'] == 'buy': bids = self.get_bids(price) if not bids: return - assert bids[0]['id'] == maker_order_id + assert bids[0]['id'] == order['maker_order_id'] if bids[0]['size'] == size: self.set_bids(price, bids[1:]) else: @@ -144,7 +149,7 @@ def match(self, order): asks = self.get_asks(price) if not asks: return - assert asks[0]['id'] == maker_order_id + assert asks[0]['id'] == order['maker_order_id'] if asks[0]['size'] == size: self.set_asks(price, asks[1:]) else: @@ -154,31 +159,33 @@ def match(self, order): def change(self, order): try: new_size = Decimal(order['new_size']) + except KeyError: + return + + try: price = Decimal(order['price']) - order_id = order['order_id'] - side = order['side'] except KeyError: return - if side == 'buy': + if order['side'] == 'buy': bids = self.get_bids(price) - if bids is None or not any(o['id'] == order_id for o in bids): + if bids is None or not any(o['id'] == order['order_id'] for o in bids): return - index = [b['id'] for b in bids].index(order_id) + index = [b['id'] for b in bids].index(order['order_id']) bids[index]['size'] = new_size self.set_bids(price, bids) else: asks = self.get_asks(price) - if asks is None or not any(o['id'] == order_id for o in asks): + if asks is None or not any(o['id'] == order['order_id'] for o in asks): return - index = [a['id'] for a in asks].index(order_id) + index = [a['id'] for a in asks].index(order['order_id']) asks[index]['size'] = new_size self.set_asks(price, asks) tree = self._asks if order['side'] == 'sell' else self._bids node = tree.get(price) - if node is None or not any(o['id'] == order_id for o in node): + if node is None or not any(o['id'] == order['order_id'] for o in node): return def get_current_ticker(self): @@ -190,27 +197,25 @@ def get_current_book(self): 'asks': [], 'bids': [], } - for ask in self._asks: try: # There can be a race condition here, where a price point is removed # between these two ops this_ask = self._asks[ask] - except (KeyError, TypeError): + except KeyError: continue for order in this_ask: result['asks'].append([order['price'], order['size'], order['id']]) - for bid in self._bids: try: # There can be a race condition here, where a price point is removed # between these two ops this_bid = self._bids[bid] - except (KeyError, TypeError): + except KeyError: continue + for order in this_bid: result['bids'].append([order['price'], order['size'], order['id']]) - return result def get_ask(self): @@ -239,8 +244,55 @@ def set_bids(self, price, bids): if __name__ == '__main__': + import sys + import time + import datetime as dt + + + class OrderBookConsole(OrderBook): + ''' Logs real-time changes to the bid-ask spread to the console ''' - order_book = OrderBook() + def __init__(self, product_id=None): + super(OrderBookConsole, self).__init__(product_id=product_id) + + # latest values of bid-ask spread + self._bid = None + self._ask = None + self._bid_depth = None + self._ask_depth = None + + def on_message(self, message): + super(OrderBookConsole, self).on_message(message) + + # Calculate newest bid-ask spread + bid = self.get_bid() + bids = self.get_bids(bid) + bid_depth = sum([b['size'] for b in bids]) + ask = self.get_ask() + asks = self.get_asks(ask) + ask_depth = sum([a['size'] for a in asks]) + + if self._bid == bid and self._ask == ask and self._bid_depth == bid_depth and self._ask_depth == ask_depth: + # If there are no changes to the bid-ask spread since the last update, no need to print + pass + else: + # If there are differences, update the cache + self._bid = bid + self._ask = ask + self._bid_depth = bid_depth + self._ask_depth = ask_depth + print('{} {} bid: {:.3f} @ {:.2f}\task: {:.3f} @ {:.2f}'.format( + dt.datetime.now(), self.product_id, bid_depth, bid, ask_depth, ask)) + + order_book = OrderBookConsole() order_book.start() - time.sleep(10) - order_book.close() + try: + while True: + time.sleep(10) + except KeyboardInterrupt: + order_book.close() + + if order_book.error: + sys.exit(1) + else: + sys.exit(0) diff --git a/gdax/public_client.py b/gdax/public_client.py index ecbc7880..79fef798 100644 --- a/gdax/public_client.py +++ b/gdax/public_client.py @@ -18,7 +18,7 @@ class PublicClient(object): """ - def __init__(self, api_url='https://api.gdax.com'): + def __init__(self, api_url='https://api.gdax.com', timeout=30): """Create GDAX API public client. Args: @@ -26,6 +26,14 @@ def __init__(self, api_url='https://api.gdax.com'): """ self.url = api_url.rstrip('/') + self.timeout = timeout + + def _get(self, path, params=None): + """Perform get request""" + + r = requests.get(self.url + path, params=params, timeout=self.timeout) + # r.raise_for_status() + return r.json() def get_products(self): """Get a list of available currency pairs for trading. @@ -45,9 +53,7 @@ def get_products(self): ] """ - r = requests.get(self.url + '/products') - # r.raise_for_status() - return r.json() + return self._get('/products') def get_product_order_book(self, product_id, level=1): """Get a list of open orders for a product. @@ -84,11 +90,10 @@ def get_product_order_book(self, product_id, level=1): } """ - params = {'level': level} - r = requests.get(self.url + '/products/{}/book' - .format(product_id), params=params) - # r.raise_for_status() - return r.json() + + # Supported levels are 1, 2 or 3 + level = level if level in range(1, 4) else 1 + return self._get('/products/{}/book'.format(str(product_id)), params={'level': level}) def get_product_ticker(self, product_id): """Snapshot about the last trade (tick), best bid/ask and 24h volume. @@ -112,10 +117,7 @@ def get_product_ticker(self, product_id): } """ - r = requests.get(self.url + '/products/{}/ticker' - .format(product_id)) - # r.raise_for_status() - return r.json() + return self._get('/products/{}/ticker'.format(str(product_id))) def get_product_trades(self, product_id): """List the latest trades for a product. @@ -140,9 +142,7 @@ def get_product_trades(self, product_id): }] """ - r = requests.get(self.url + '/products/{}/trades'.format(product_id)) - # r.raise_for_status() - return r.json() + return self._get('/products/{}/trades'.format(str(product_id))) def get_product_historic_rates(self, product_id, start=None, end=None, granularity=None): @@ -188,10 +188,8 @@ def get_product_historic_rates(self, product_id, start=None, end=None, params['end'] = end if granularity is not None: params['granularity'] = granularity - r = requests.get(self.url + '/products/{}/candles' - .format(product_id), params=params) - # r.raise_for_status() - return r.json() + + return self._get('/products/{}/candles'.format(str(product_id)), params=params) def get_product_24hr_stats(self, product_id): """Get 24 hr stats for the product. @@ -210,9 +208,7 @@ def get_product_24hr_stats(self, product_id): } """ - r = requests.get(self.url + '/products/{}/stats'.format(product_id)) - # r.raise_for_status() - return r.json() + return self._get('/products/{}/stats'.format(str(product_id))) def get_currencies(self): """List known currencies. @@ -230,9 +226,7 @@ def get_currencies(self): }] """ - r = requests.get(self.url + '/currencies') - # r.raise_for_status() - return r.json() + return self._get('/currencies') def get_time(self): """Get the API server time. @@ -246,6 +240,4 @@ def get_time(self): } """ - r = requests.get(self.url + '/time') - # r.raise_for_status() - return r.json() + return self._get('/time') diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index abc84ae8..196ae8e4 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -1,38 +1,51 @@ -# # gdax/WebsocketClient.py -# Daniel Paquin +# original author: Daniel Paquin +# mongo "support" added by Drew Rice +# # # Template object to receive messages from the gdax Websocket Feed from __future__ import print_function -import traceback -import time import json - +import base64 +import hmac +import hashlib +import time from threading import Thread -from websocket import create_connection, WebSocketConnectionClosedException, WebSocketBadStatusException +from websocket import create_connection, WebSocketConnectionClosedException +from pymongo import MongoClient +from gdax.gdax_auth import get_auth_headers class WebsocketClient(object): - def __init__(self, url="wss://ws-feed.gdax.com", products=None, message_type="subscribe"): + def __init__(self, url="wss://ws-feed.gdax.com", products=None, message_type="subscribe", mongo_collection=None, + should_print=True, auth=False, api_key="", api_secret="", api_passphrase="", channels=None): self.url = url self.products = products + self.channels = channels self.type = message_type + self.killme = False self.stop = False + self.error = None self.ws = None self.thread = None - self.killme = False + self.auth = auth + self.api_key = api_key + self.api_secret = api_secret + self.api_passphrase = api_passphrase + self.should_print = should_print + self.mongo_collection = mongo_collection def start(self): - self.thread = Thread(target=self._go) - self.thread.start() - #self.thread.join() + def _go(): + self._connect() + self._listen() + self._disconnect() - def _go(self): - while not self._connect(): - time.sleep(10) - print("Error: Trying to connect...") - self._listen() + self.stop = False + self.on_open() + self.thread = Thread(target=_go) + self.thread.start() def _connect(self): if self.products is None: @@ -43,21 +56,31 @@ def _connect(self): if self.url[-1] == "/": self.url = self.url[:-1] - try: - self.ws = create_connection(self.url) - except Exception as e: - print("Error connecting: %s" % str(e)) - return False - - sub_params = {'type': 'subscribe', 'product_ids': self.products} + if self.channels is None: + sub_params = {'type': 'subscribe', 'product_ids': self.products} + else: + sub_params = {'type': 'subscribe', 'product_ids': self.products, 'channels': self.channels} + + if self.auth: + timestamp = str(time.time()) + message = timestamp + 'GET' + '/users/self/verify' + message = message.encode('ascii') + hmac_key = base64.b64decode(self.api_secret) + signature = hmac.new(hmac_key, message, hashlib.sha256) + signature_b64 = signature.digest().encode('base64').rstrip('\n') + sub_params['signature'] = signature_b64 + sub_params['key'] = self.api_key + sub_params['passphrase'] = self.api_passphrase + sub_params['timestamp'] = timestamp + + self.ws = create_connection(self.url) + self.ws.send(json.dumps(sub_params)) if self.type == "heartbeat": sub_params = {"type": "heartbeat", "on": True} - - self.stop = False + else: + sub_params = {"type": "heartbeat", "on": False} self.ws.send(json.dumps(sub_params)) - self.on_open() - return True def _listen(self): while not self.stop: @@ -65,47 +88,57 @@ def _listen(self): if int(time.time() % 30) == 0: # Set a 30 second ping to keep connection alive self.ws.ping("keepalive") - msg = json.loads(self.ws.recv()) + data = self.ws.recv() + msg = json.loads(data) + except ValueError as e: + self.on_error(e) except Exception as e: self.on_error(e) else: self.on_message(msg) - def close(self): - if not self.stop: - if self.type == "heartbeat": - self.ws.send(json.dumps({"type": "heartbeat", "on": False})) + def _disconnect(self): + if self.type == "heartbeat": + self.ws.send(json.dumps({"type": "heartbeat", "on": False})) + try: + if self.ws: + self.ws.close() + except WebSocketConnectionClosedException as e: + pass - try: - if self.ws: - self.ws.close() - except WebSocketConnectionClosedException: - pass + self.on_close() - self.stop = True - self.on_close() - self.ws = None + def close(self): + self.stop = True + self.thread.join() def on_open(self): - print("-- Subscribed! --\n") + if self.should_print: + print("-- Subscribed! --\n") def on_close(self): - print("\n-- Socket Closed --") + if self.should_print: + print("\n-- Socket Closed --") def on_message(self, msg): - print(msg) + if self.should_print: + print(msg) + if self.mongo_collection: # dump JSON to given mongo collection + self.mongo_collection.insert_one(msg) - def on_error(self, e): + def on_error(self, e, data=None): + self.error = e self.stop = True self.killme = True - traceback.print_exc() - print("Error: " + str(e)) + print('{} - data: {}'.format(e, data)) if __name__ == "__main__": + import sys import gdax import time + class MyWebsocketClient(gdax.WebsocketClient): def on_open(self): self.url = "wss://ws-feed.gdax.com/" @@ -114,19 +147,24 @@ def on_open(self): print("Let's count the messages!") def on_message(self, msg): - if 'price' in msg and 'type' in msg: - print("Message type:", msg["type"], "\t@ %.3f" % float(msg["price"])) + print(json.dumps(msg, indent=4, sort_keys=True)) self.message_count += 1 def on_close(self): print("-- Goodbye! --") + wsClient = MyWebsocketClient() wsClient.start() print(wsClient.url, wsClient.products) - # Do some logic with the data - while wsClient.message_count < 500: - print("\nMessageCount =", "%i \n" % wsClient.message_count) - time.sleep(1) - - wsClient.close() + try: + while True: + print("\nMessageCount =", "%i \n" % wsClient.message_count) + time.sleep(1) + except KeyboardInterrupt: + wsClient.close() + + if wsClient.error: + sys.exit(1) + else: + sys.exit(0) diff --git a/tests/test_public_client.py b/tests/test_public_client.py index 5da77c27..d33a1844 100644 --- a/tests/test_public_client.py +++ b/tests/test_public_client.py @@ -1,5 +1,6 @@ import pytest import gdax +import time @pytest.fixture(scope='module') @@ -9,18 +10,31 @@ def client(): @pytest.mark.usefixtures('client') class TestPublicClient(object): + + @staticmethod + def teardown_method(): + time.sleep(.25) # Avoid rate limit + def test_get_products(self, client): r = client.get_products() assert type(r) is list - def test_get_product_order_book(self, client): - r = client.get_product_order_book('BTC-USD') - assert type(r) is dict - r = client.get_product_order_book('BTC-USD', level=2) + @pytest.mark.parametrize('level', [1, 2, 3, None]) + def test_get_product_order_book(self, client, level): + r = client.get_product_order_book('BTC-USD', level=level) assert type(r) is dict assert 'asks' in r assert 'bids' in r + if level in (1, None) and (len(r['asks']) > 1 or len(r['bids']) > 1): + pytest.fail('Fail: Level 1 should only return the best ask and bid') + + if level is 2 and (len(r['asks']) > 50 or len(r['bids']) > 50): + pytest.fail('Fail: Level 2 should only return the top 50 asks and bids') + + if level is 2 and (len(r['asks']) < 50 or len(r['bids']) < 50): + pytest.fail('Fail: Level 3 should return the full order book') + def test_get_product_ticker(self, client): r = client.get_product_ticker('BTC-USD') assert type(r) is dict @@ -32,8 +46,11 @@ def test_get_product_trades(self, client): assert type(r) is list assert 'trade_id' in r[0] - def test_get_historic_rates(self, client): - r = client.get_product_historic_rates('BTC-USD') + @pytest.mark.parametrize('start', ('2017-11-01', None)) + @pytest.mark.parametrize('end', ('2017-11-30', None)) + @pytest.mark.parametrize('granularity', (3600, None)) + def test_get_historic_rates(self, client, start, end, granularity): + r = client.get_product_historic_rates('BTC-USD', start=start, end=end, granularity=granularity) assert type(r) is list def test_get_product_24hr_stats(self, client): From dcdd3e9b9760ed08d49146e9d1dbd53cb10e6614 Mon Sep 17 00:00:00 2001 From: uzbit Date: Thu, 11 Jan 2018 22:43:04 -0800 Subject: [PATCH 57/59] Adding typeerror catch --- gdax/order_book.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gdax/order_book.py b/gdax/order_book.py index 6af28f0c..5b701662 100644 --- a/gdax/order_book.py +++ b/gdax/order_book.py @@ -202,7 +202,7 @@ def get_current_book(self): # There can be a race condition here, where a price point is removed # between these two ops this_ask = self._asks[ask] - except KeyError: + except (KeyError, TypeError): continue for order in this_ask: result['asks'].append([order['price'], order['size'], order['id']]) @@ -211,7 +211,7 @@ def get_current_book(self): # There can be a race condition here, where a price point is removed # between these two ops this_bid = self._bids[bid] - except KeyError: + except (KeyError, TypeError): continue for order in this_bid: From daf57070f233812c87cd9c391f2420c7034db646 Mon Sep 17 00:00:00 2001 From: uzbit Date: Sat, 13 Jan 2018 08:52:30 -0800 Subject: [PATCH 58/59] Just reconnect, no error --- gdax/websocket_client.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index 196ae8e4..985377a3 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -85,9 +85,9 @@ def _connect(self): def _listen(self): while not self.stop: try: - if int(time.time() % 30) == 0: - # Set a 30 second ping to keep connection alive - self.ws.ping("keepalive") + # if int(time.time() % 30) == 0: + # # Set a 30 second ping to keep connection alive + # self.ws.ping("keepalive") data = self.ws.recv() msg = json.loads(data) except ValueError as e: @@ -127,10 +127,16 @@ def on_message(self, msg): self.mongo_collection.insert_one(msg) def on_error(self, e, data=None): - self.error = e - self.stop = True - self.killme = True print('{} - data: {}'.format(e, data)) + if 'Errno 104' in str(e): + self.close() + time.sleep(5) + self.start() + else: + self.error = e + self.stop = True + self.killme = True + if __name__ == "__main__": From 32b54fe02263fe0edb583dc88a269dc5294b5589 Mon Sep 17 00:00:00 2001 From: uzbit Date: Tue, 16 Jan 2018 17:24:53 -0800 Subject: [PATCH 59/59] Can't join thread at this point --- gdax/websocket_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdax/websocket_client.py b/gdax/websocket_client.py index 985377a3..7fbc54a2 100644 --- a/gdax/websocket_client.py +++ b/gdax/websocket_client.py @@ -110,7 +110,7 @@ def _disconnect(self): def close(self): self.stop = True - self.thread.join() + #self.thread.join() def on_open(self): if self.should_print: