· 8 years ago · Jan 24, 2018, 01:38 AM
1# Import Built-Ins
2import logging
3import requests
4import time
5# Import Third-Party
6
7# Import Homebrew
8
9
10log = logging.getLogger(__name__)
11
12
13class RESTAPI:
14
15 def __init__(self, uri, api_version='', key='', secret=''):
16 """
17 Base Class for REST API connections.
18 """
19 self.key = key
20 self.secret = secret
21 self.uri = uri
22 self.apiversion = api_version
23 self.req_methods = {'POST': requests.post, 'PUT': requests.put,
24 'GET': requests.get, 'DELETE': requests.delete,
25 'PATCH': requests.patch}
26 log.debug("Initialized RESTAPI for URI: %s; "
27 "Will request on API version: %s" %
28 (self.uri, self.apiversion))
29
30 def load_key(self, path):
31 """
32 Load key and secret from file.
33 """
34 with open(path, 'r') as f:
35 self.key = f.readline().strip()
36 self.secret = f.readline().strip()
37
38 def nonce(self):
39 return str(int(1000 * time.time()))
40
41 def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs):
42 """
43 Dummy Signature creation method. Override this in child.
44 URL is required to be returned, as some Signatures use the url for
45 sig generation, and api calls made must match the address exactly.
46 """
47 url = self.uri
48
49 return url, {'params': {'test_param': "authenticated_chimichanga"}}
50
51 def query(self, method_verb, endpoint, authenticate=False,
52 *args, **kwargs):
53 """
54 Queries exchange using given data. Defaults to unauthenticated query.
55 """
56 request_method = self.req_methods[method_verb]
57
58 if self.apiversion:
59 endpoint_path = '/' + self.apiversion + '/' + endpoint
60 else:
61 endpoint_path = '/' + endpoint
62
63 url = self.uri + endpoint_path
64 if authenticate: # sign off kwargs and url before sending request
65 url, request_kwargs = self.sign(url, endpoint, endpoint_path,
66 method_verb, *args, **kwargs)
67 else:
68 request_kwargs = kwargs
69 log.debug("Making request to: %s, kwargs: %s" % (url, request_kwargs))
70 r = request_method(url, timeout=5, **request_kwargs)
71 log.debug("Made %s request made to %s, with headers %s and body %s. "
72 "Status code %s" %
73 (r.request.method, r.request.url, r.request.headers,
74 r.request.body, r.status_code))
75 return r
76
77# Import Built-ins
78import logging
79import json
80import hashlib
81import hmac
82import base64
83import time
84import urllib
85import urllib.parse
86from requests.auth import AuthBase
87
88# Import Third-Party
89
90# Import Homebrew
91from bitex.api.api import RESTAPI
92
93
94log = logging.getLogger(__name__)
95
96
97class BitfinexREST(RESTAPI):
98 def __init__(self, key='', secret='', api_version='v1',
99 url='https://api.bitfinex.com'):
100 super(BitfinexREST, self).__init__(url, api_version=api_version,
101 key=key, secret=secret)
102
103 def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs):
104 try:
105 req = kwargs['params']
106 except KeyError:
107 req = {}
108 req['request'] = endpoint_path
109 req['nonce'] = self.nonce()
110
111 js = json.dumps(req)
112 data = base64.standard_b64encode(js.encode('utf8'))
113
114 h = hmac.new(self.secret.encode('utf8'), data, hashlib.sha384)
115 signature = h.hexdigest()
116 headers = {"X-BFX-APIKEY": self.key,
117 "X-BFX-SIGNATURE": signature,
118 "X-BFX-PAYLOAD": data}
119
120 return url, {'headers': headers}
121
122
123class BitstampREST(RESTAPI):
124 def __init__(self, user_id='', key='', secret='', api_version='',
125 url='https://www.bitstamp.net/api'):
126 self.id = user_id
127 super(BitstampREST, self).__init__(url, api_version=api_version,
128 key=key, secret=secret)
129
130 def load_key(self, path):
131 """
132 Load key and secret from file.
133 """
134 with open(path, 'r') as f:
135 self.id = f.readline().strip()
136 self.key = f.readline().strip()
137 self.secret = f.readline().strip()
138
139 def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs):
140 nonce = self.nonce()
141 message = nonce + self.id + self.key
142
143 signature = hmac.new(self.secret.encode(), message.encode(),
144 hashlib.sha256)
145 signature = signature.hexdigest().upper()
146
147 try:
148 req = kwargs['params']
149 except KeyError:
150 req = {}
151 req['key'] = self.key
152 req['nonce'] = nonce
153 req['signature'] = signature
154 return url, {'data': req}
155
156
157class BittrexREST(RESTAPI):
158 def __init__(self, key='', secret='', api_version='v1.1',
159 url='https://bittrex.com/api'):
160 super(BittrexREST, self).__init__(url, api_version=api_version, key=key,
161 secret=secret)
162
163 def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs):
164
165 try:
166 params = kwargs['params']
167 except KeyError:
168 params = {}
169
170 nonce = self.nonce()
171
172 req_string = endpoint_path + '?apikey=' + self.key + "&nonce=" + nonce + '&'
173 req_string += urllib.parse.urlencode(params)
174 headers = {"apisign": hmac.new(self.secret.encode('utf-8'),
175 (self.uri + req_string).encode('utf-8'),
176 hashlib.sha512).hexdigest()}
177
178 return self.uri + req_string, {'headers': headers, 'params': {}}
179
180
181class CoincheckREST(RESTAPI):
182 def __init__(self, key='', secret='', api_version='api',
183 url='https://coincheck.com'):
184 super(CoincheckREST, self).__init__(url, api_version=api_version,
185 key=key, secret=secret)
186
187 def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs):
188
189 nonce = self.nonce()
190 try:
191 params = kwargs['params']
192 except KeyError:
193 params = {}
194
195 params = json.dumps(params)
196 # sig = nonce + url + req
197 data = (nonce + endpoint_path + params).encode('utf-8')
198 h = hmac.new(self.secret.encode('utf8'), data, hashlib.sha256)
199 signature = h.hexdigest()
200 headers = {"ACCESS-KEY": self.key,
201 "ACCESS-NONCE": nonce,
202 "ACCESS-SIGNATURE": signature}
203
204 return url, {'headers': headers}
205
206
207class GdaxAuth(AuthBase):
208 def __init__(self, api_key, secret_key, passphrase):
209 self.api_key = api_key.encode('utf-8')
210 self.secret_key = secret_key.encode('utf-8')
211 self.passphrase = passphrase.encode('utf-8')
212
213 def __call__(self, request):
214 timestamp = str(time.time())
215 message = (timestamp + request.method + request.path_url +
216 (request.body or ''))
217 hmac_key = base64.b64decode(self.secret_key)
218 signature = hmac.new(hmac_key, message.encode('utf-8'), hashlib.sha256)
219 signature_b64 = base64.b64encode(signature.digest())
220
221 request.headers.update({
222 'CB-ACCESS-SIGN': signature_b64,
223 'CB-ACCESS-TIMESTAMP': timestamp,
224 'CB-ACCESS-KEY': self.api_key,
225 'CB-ACCESS-PASSPHRASE': self.passphrase,
226 'Content-Type': 'application/json'
227 })
228 return request
229
230
231class GDAXRest(RESTAPI):
232 def __init__(self, passphrase='', key='', secret='', api_version='',
233 url='https://api.gdax.com'):
234 self.passphrase = passphrase
235 super(GDAXRest, self).__init__(url, api_version=api_version, key=key,
236 secret=secret)
237
238 def load_key(self, path):
239 """
240 Load key and secret from file.
241 """
242 with open(path, 'r') as f:
243 self.passphrase = f.readline().strip()
244 self.key = f.readline().strip()
245 self.secret = f.readline().strip()
246
247 def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs):
248 auth = GdaxAuth(self.key, self.secret, self.passphrase)
249 try:
250 js = kwargs['params']
251 except KeyError:
252 js = {}
253
254 return url, {'json': js, 'auth': auth}
255
256
257class KrakenREST(RESTAPI):
258 def __init__(self, key='', secret='', api_version='0',
259 url='https://api.kraken.com'):
260 super(KrakenREST, self).__init__(url, api_version=api_version,
261 key=key, secret=secret)
262
263 def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs):
264 try:
265 req = kwargs['params']
266 except KeyError:
267 req = {}
268
269 req['nonce'] = self.nonce()
270 postdata = urllib.parse.urlencode(req)
271
272 # Unicode-objects must be encoded before hashing
273 encoded = (str(req['nonce']) + postdata).encode('utf-8')
274 message = (endpoint_path.encode('utf-8') +
275 hashlib.sha256(encoded).digest())
276
277 signature = hmac.new(base64.b64decode(self.secret),
278 message, hashlib.sha512)
279 sigdigest = base64.b64encode(signature.digest())
280
281 headers = {
282 'API-Key': self.key,
283 'API-Sign': sigdigest.decode('utf-8')
284 }
285
286 return url, {'data': req, 'headers': headers}
287
288
289class ItbitREST(RESTAPI):
290 def __init__(self, user_id = '', key='', secret='', api_version='v1',
291 url='https://api.itbit.com'):
292 self.userId = user_id
293 super(ItbitREST, self).__init__(url, api_version=api_version,
294 key=key, secret=secret)
295
296 def load_key(self, path):
297 """
298 Load user id, key and secret from file.
299 """
300 with open(path, 'r') as f:
301 self.userId = f.readline().strip()
302 self.clientKey = f.readline().strip()
303 self.secret = f.readline().strip()
304
305 def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs):
306 try:
307 params = kwargs['params']
308 except KeyError:
309 params = {}
310
311 verb = method_verb
312
313 if verb in ('PUT', 'POST'):
314 body = params
315 else:
316 body = {}
317
318 timestamp = self.nonce()
319 nonce = self.nonce()
320
321 message = json.dumps([verb, url, body, nonce, timestamp],
322 separators=(',', ':'))
323 sha256_hash = hashlib.sha256()
324 nonced_message = nonce + message
325 sha256_hash.update(nonced_message.encode('utf8'))
326 hash_digest = sha256_hash.digest()
327 hmac_digest = hmac.new(self.secret.encode('utf-8'),
328 url.encode('utf-8') + hash_digest,
329 hashlib.sha512).digest()
330 signature = base64.b64encode(hmac_digest)
331
332 auth_headers = {
333 'Authorization': self.key + ':' + signature.decode('utf8'),
334 'X-Auth-Timestamp': timestamp,
335 'X-Auth-Nonce': nonce,
336 'Content-Type': 'application/json'
337 }
338 return url, {'headers': auth_headers}
339
340
341class OKCoinREST(RESTAPI):
342 def __init__(self, key='', secret='', api_version='v1',
343 url='https://www.okcoin.com/api'):
344 super(OKCoinREST, self).__init__(url, api_version=api_version,
345 key=key,
346 secret=secret)
347
348 def sign(self,url, endpoint, endpoint_path, method_verb, *args, **kwargs):
349 nonce = self.nonce()
350
351 # sig = nonce + url + req
352 data = (nonce + url).encode()
353
354 h = hmac.new(self.secret.encode('utf8'), data, hashlib.sha256)
355 signature = h.hexdigest()
356 headers = {"ACCESS-KEY": self.key,
357 "ACCESS-NONCE": nonce,
358 "ACCESS-SIGNATURE": signature}
359
360 return url, {'headers': headers}
361
362
363class BTCERest(RESTAPI):
364 def __init__(self, key='', secret='', api_version='3',
365 url='https://btc-e.com/api'):
366 super(BTCERest, self).__init__(url, api_version=api_version, key=key,
367 secret=secret)
368
369 def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs):
370 nonce = self.nonce()
371 try:
372 params = kwargs['params']
373 except KeyError:
374 params = {}
375 post_params = params
376 post_params.update({'nonce': nonce, 'method': endpoint.split('/', 1)[1]})
377 post_params = urllib.parse.urlencode(post_params)
378
379 signature = hmac.new(self.secret.encode('utf-8'),
380 post_params.encode('utf-8'), hashlib.sha512)
381 headers = {'Key': self.key, 'Sign': signature.hexdigest(),
382 "Content-type": "application/x-www-form-urlencoded"}
383
384 # split by tapi str to gain clean url;
385 url = url.split('/tapi', 1)[0] + '/tapi'
386
387 return url, {'headers': headers, 'params': params}
388
389
390class CCEXRest(RESTAPI):
391 def __init__(self, key='', secret='', api_version='',
392 url='https://c-cex.com/t'):
393 super(CCEXRest, self).__init__(url, api_version=api_version, key=key,
394 secret=secret)
395
396 def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs):
397 nonce = self.nonce()
398 try:
399 params = kwargs['params']
400 except KeyError:
401 params = {}
402
403 params['apikey'] = self.key
404 params['nonce'] = nonce
405 post_params = params
406 post_params.update({'nonce': nonce, 'method': endpoint})
407 post_params = urllib.parse.urlencode(post_params)
408
409 url = uri + post_params
410
411 sig = hmac.new(url, self.secret, hashlib.sha512)
412 headers = {'apisign': sig}
413
414 return url, {'headers': headers}
415
416
417class CryptopiaREST(RESTAPI):
418 def __init__(self, key='', secret='', api_version='',
419 url='https://www.cryptopia.co.nz/api'):
420 super(CryptopiaREST, self).__init__(url, api_version=api_version, key=key,
421 secret=secret)
422
423 def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs):
424 nonce = self.nonce()
425 try:
426 params = kwargs['params']
427 except KeyError:
428 params = {}
429
430
431 post_data = json.dumps(params)
432 md5 = base64.b64encode(hashlib.md5().updated(post_data).digest())
433
434 sig = self.key + 'POST' + urllib.parse.quote_plus(uri).lower() + nonce + md5
435 hmac_sig = base64.b64encode(hmac.new(base64.b64decode(self.secret),
436 sig, hashlib.sha256).digest())
437 header_data = 'amx' + self.key + ':' + hmac_sig + ':' + nonce
438 headers = {'Authorization': header_data,
439 'Content-Type': 'application/json; charset=utf-8'}
440
441 return uri, {'headers': headers, 'data': post_data}
442
443
444class GeminiREST(RESTAPI):
445 def __init__(self, key='', secret='', api_version='v1',
446 url='https://api.gemini.com'):
447 super(GeminiREST, self).__init__(url, api_version=api_version, key=key,
448 secret=secret)
449
450 def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs):
451 nonce = self.nonce()
452 try:
453 params = kwargs['params']
454 except KeyError:
455 params = {}
456 payload = params
457 payload['nonce'] = nonce
458 payload['request'] = endpoint_path
459 payload = base64.b64encode(json.dumps(payload))
460 sig = hmac.new(self.secret, payload, hashlib.sha384).hexdigest()
461 headers = {'X-GEMINI-APIKEY': self.key,
462 'X-GEMINI-PAYLOAD': payload,
463 'X-GEMINI-SIGNATURE': sig}
464 return uri, {'headers': headers}
465
466
467class YunbiREST(RESTAPI):
468 def __init__(self, key='', secret='', api_version='v2',
469 url='https://yunbi.com/api'):
470 super(YunbiREST, self).__init__(url, api_version=api_version, key=key,
471 secret=secret)
472
473 def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs):
474 nonce = self.nonce()
475 try:
476 params = kwargs['params']
477 except KeyError:
478 params = {}
479 params['tonce'] = nonce
480 params['access_key'] = self.key
481 post_params = urllib.parse.urlencode(params)
482 msg = '%s|%s|%s' % (method_verb, endpoint_path, post_params)
483
484 sig = hmac.new(self.secret, msg, hashlib.sha256).hexdigest()
485 uri += post_params + '&signature=' + sig
486
487 return uri, {}
488
489
490class RockTradingREST(RESTAPI):
491 def __init__(self, key='', secret='', api_version='v1',
492 url='https://api.therocktrading.com'):
493 super(RockTradingREST, self).__init__(url, api_version=api_version,
494 key=key,
495 secret=secret)
496
497 def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs):
498 nonce = self.nonce()
499 try:
500 params = kwargs['params']
501 except KeyError:
502 params = {}
503 payload = params
504 payload['nonce'] = int(nonce)
505 payload['request'] = endpoint_path
506
507 msg = nonce + uri
508 sig = hmac.new(self.secret.encode(), msg.encode(), hashlib.sha384).hexdigest()
509 headers = {'X-TRT-APIKEY': self.key,
510 'X-TRT-Nonce': nonce,
511 'X-TRT-SIGNATURE': sig, 'Content-Type': 'application/json'}
512 return uri, {'headers': headers}
513
514
515class PoloniexREST(RESTAPI):
516 def __init__(self, key='', secret='', api_version='',
517 url='https://poloniex.com'):
518 super(PoloniexREST, self).__init__(url, api_version=api_version,
519 key=key, secret=secret)
520
521 def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs):
522 try:
523 params = kwargs['params']
524 except KeyError:
525 params = {}
526 params['nonce'] = self.nonce()
527 payload = params
528
529 msg = urllib.parse.urlencode(payload).encode('utf-8')
530 sig = hmac.new(self.secret.encode('utf-8'), msg, hashlib.sha512).hexdigest()
531 headers = {'Key': self.key, 'Sign': sig}
532 return uri, {'headers': headers, 'data': params}
533
534"""
535https:/kraken.com/help/api
536"""
537
538# Import Built-Ins
539import logging
540
541# Import Third-Party
542
543# Import Homebrew
544from bitex.api.rest import KrakenREST
545from bitex.utils import return_json
546from bitex.formatters.kraken import cancel, trade, order_book
547
548# Init Logging Facilities
549log = logging.getLogger(__name__)
550
551
552class Kraken(KrakenREST):
553 def __init__(self, key='', secret='', key_file=''):
554 super(Kraken, self).__init__(key, secret)
555 if key_file:
556 self.load_key(key_file)
557
558 def make_params(self, *pairs, **kwargs):
559 q = {'pair': ','.join(pairs)}
560 q.update(kwargs)
561 return q
562
563 def public_query(self, endpoint, **kwargs):
564 path = 'public/' + endpoint
565 return self.query('GET', path, **kwargs)
566
567 def private_query(self, endpoint, **kwargs):
568 path = 'private/' + endpoint
569 return self.query('POST', path, authenticate=True, **kwargs)
570
571 """
572 BitEx Standardized Methods
573 """
574
575 @return_json(None)
576 def ticker(self, *pairs):
577 q = self.make_params(*pairs)
578 return self.public_query('Ticker', params=q)
579
580 @return_json(order_book)
581 def order_book(self, pair, **kwargs):
582 q = self.make_params(pair, **kwargs)
583 return self.public_query('Depth', params=q)
584
585 @return_json(None)
586 def trades(self, pair, **kwargs):
587 q = self.make_params(pair, **kwargs)
588 return self.public_query('Trades', params=q)
589
590 def _add_order(self, pair, side, price, amount, **kwargs):
591 q = {'pair': pair, 'type': side, 'price': price,
592 'ordertype': 'limit', 'volume': amount,
593 'trading_agreement': 'agree'}
594 q.update(kwargs)
595 return self.private_query('AddOrder', params=q)
596
597 @return_json(trade)
598 def bid(self, pair, price, amount, **kwargs):
599 return self._add_order(pair, 'buy', price, amount, **kwargs)
600
601 @return_json(trade)
602 def ask(self, pair, price, amount, **kwargs):
603 return self._add_order(pair, 'sell', price, amount, **kwargs)
604
605 @return_json(cancel)
606 def cancel_order(self, order_id, **kwargs):
607 q = {'txid': order_id}
608 q.update(kwargs)
609 return self.private_query('CancelOrder', params=q)
610
611 @return_json(None)
612 def order_info(self, *txids, **kwargs):
613 if len(txids) > 1:
614 q = {'txid': txids}
615 elif txids:
616 txid, *_ = txids
617 q = {'txid': txid}
618 else:
619 q = {}
620 q.update(kwargs)
621 return self.private_query('QueryOrders', params=q)
622
623 @return_json(None)
624 def balance(self, **kwargs):
625 return self.private_query('Balance')
626
627 @return_json(None)
628 def withdraw(self, _type, source_wallet, amount, tar_addr, **kwargs):
629 raise NotImplementedError()
630
631 @return_json(None)
632 def deposit_address(self, **kwargs):
633 raise NotImplementedError()
634
635 """
636 Exchange Specific Methods
637 """
638
639 @return_json(None)
640 def time(self):
641 return self.public_query('Time')
642
643 @return_json(None)
644 def assets(self, **kwargs):
645 return self.public_query('Assets', params=kwargs)
646
647 @return_json(None)
648 def pairs(self, **kwargs):
649 return self.public_query('AssetPairs', params=kwargs)
650
651 @return_json(None)
652 def ohlc(self, pair, **kwargs):
653 q = self.make_params(pair, **kwargs)
654 return self.public_query('OHLC', params=q)
655
656 @return_json(None)
657 def spread(self, pair, **kwargs):
658 q = self.make_params(pair, **kwargs)
659 return self.public_query('Spread', params=q)
660
661 @return_json(None)
662 def orders(self, **kwargs):
663 q = kwargs
664 return self.private_query('OpenOrders', params=q)
665
666 @return_json(None)
667 def closed_orders(self, **kwargs):
668 q = kwargs
669 return self.private_query('ClosedOrders', params=q)
670
671 @return_json(None)
672 def trade_history(self, **kwargs):
673 q = kwargs
674 return self.private_query('TradesHistory', params=q)
675
676 @return_json(None)
677 def fees(self, pair=None):
678 q = {'fee-info': True}
679
680 if pair:
681 q['pair'] = pair
682 return self.private_query('TradeVolume', params=q)
683
684"""
685http://docs.bitfinex.com/
686"""
687
688# Import Built-Ins
689import logging
690
691# Import Third-Party
692
693# Import Homebrew
694from bitex.api.rest import BitfinexREST
695from bitex.utils import return_json
696from bitex.formatters.bitfinex import trade, cancel, order_status
697# Init Logging Facilities
698log = logging.getLogger(__name__)
699
700
701class Bitfinex(BitfinexREST):
702 def __init__(self, key='', secret='', key_file=''):
703 super(Bitfinex, self).__init__(key, secret)
704 if key_file:
705 self.load_key(key_file)
706
707 def public_query(self, endpoint, **kwargs):
708 return self.query('GET', endpoint, **kwargs)
709
710 def private_query(self, endpoint, **kwargs):
711 return self.query('POST', endpoint, authenticate=True, **kwargs)
712
713 """
714 BitEx Standardized Methods
715 """
716 @return_json(None)
717 def order_book(self, pair, **kwargs):
718 return self.public_query('book/%s' % pair, params=kwargs)
719
720 @return_json(None)
721 def ticker(self, pair, **kwargs):
722 return self.public_query('pubticker/%s' % pair, params=kwargs)
723
724 @return_json(None)
725 def trades(self, pair, **kwargs):
726 return self.public_query('trades/%s' % pair, params=kwargs)
727
728 def _place_order(self, pair, amount, price, side, replace, **kwargs):
729 q = {'symbol': pair, 'amount': amount, 'price': price, 'side': side,
730 'type': 'exchange limit'}
731 q.update(kwargs)
732 if replace:
733 return self.private_query('order/cancel/replace', params=q)
734 else:
735 return self.private_query('order/new', params=q)
736
737 @return_json(trade)
738 def bid(self, pair, price, amount, replace=False, **kwargs):
739 return self._place_order(pair, amount, price, 'buy', replace=replace,
740 **kwargs)
741
742 @return_json(trade)
743 def ask(self, pair, price, amount, replace=False, **kwargs):
744 return self._place_order(pair, str(amount), str(price), 'sell',
745 replace=replace, **kwargs)
746
747 @return_json(cancel)
748 def cancel_order(self, order_id, all=False, **kwargs):
749
750 q = {'order_id': int(order_id)}
751 q.update(kwargs)
752 if not all:
753 return self.private_query('order/cancel', params=q)
754 else:
755 endpoint = 'order/cancel/all'
756 return self.private_query(endpoint)
757
758 @return_json(order_status)
759 def order(self, order_id, **kwargs):
760 q = {'order_id': order_id}
761 q.update(kwargs)
762 return self.private_query('order/status', params=q)
763
764 @return_json(None)
765 def balance(self, **kwargs):
766 return self.private_query('balances', params=kwargs)
767
768 @return_json(None)
769 def withdraw(self, _type, source_wallet, amount, tar_addr, **kwargs):
770 q = {'withdraw_type': _type, 'walletselected': source_wallet,
771 'amount': amount, 'address': tar_addr}
772 q.update(kwargs)
773 return self.private_query('withdraw', params=q)
774
775 @return_json(None)
776 def deposit_address(self, **kwargs):
777 q = {'method': currency, 'wallet_name': target_wallet}
778 q.update(kwargs)
779 return self.private_query('deposit/new', params=kwargs)
780
781 """
782 Exchange Specific Methods
783 """
784
785 @return_json(None)
786 def statistics(self, pair):
787 return self.public_query('stats/%s' % pair)
788
789 @return_json(None)
790 def funding_book(self, currency, **kwargs):
791 return self.public_query('lendbook/%s' % currency, params=kwargs)
792
793 @return_json(None)
794 def lends(self, currency, **kwargs):
795 return self.public_query('lends/%s' % currency, params=kwargs)
796
797 @return_json(None)
798 def pairs(self, details=False):
799 if details:
800 return self.public_query('symbols_details')
801 else:
802 return self.public_query('symbols')
803
804 @return_json(None)
805 def fees(self):
806 return self.private_query('account_infos')
807
808 @return_json(None)
809 def orders(self):
810 return self.private_query('orders')
811
812 @return_json(None)
813 def balance_history(self, currency, **kwargs):
814 q = {'currency': currency}
815 q.update(kwargs)
816 return self.private_query('history/movements', params=q)
817
818 @return_json(None)
819 def trade_history(self, pair, since, **kwargs):
820 q = {'symbol': pair, 'timestamp': since}
821 q.update(kwargs)
822 return self.private_query('mytrades', params=q)
823
824"""
825https://docs.gdax.com/
826"""
827
828# Import Built-Ins
829import logging
830
831# Import Third-Party
832
833# Import Homebrew
834from bitex.api.rest import GDAXRest
835from bitex.utils import return_json
836
837# Init Logging Facilities
838log = logging.getLogger(__name__)
839
840
841class GDAX(GDAXRest):
842 def __init__(self, key='', secret='', key_file=''):
843 super(GDAX, self).__init__(key, secret)
844 if key_file:
845 self.load_key(key_file)
846
847 def public_query(self, endpoint, **kwargs):
848 return self.query('GET', endpoint, **kwargs)
849
850 def private_query(self, endpoint, method_verb='POST', **kwargs):
851 return self.query(method_verb, endpoint, authenticate=True, **kwargs)
852
853 """
854 BitEx Standardized Methods
855 """
856
857 @return_json(None)
858 def ticker(self, pair, **kwargs):
859 return self.public_query('products/%s/ticker' % pair, params=kwargs)
860
861 @return_json(None)
862 def order_book(self, pair, **kwargs):
863 return self.public_query('products/%s/book' % pair, params=kwargs)
864
865 @return_json(None)
866 def trades(self, pair, **kwargs):
867 return self.public_query('products/%s/trades' % pair, params=kwargs)
868
869 @return_json(None)
870 def bid(self, pair, price, size, **kwargs):
871 q = {'side': 'buy', 'type': 'market', 'product_id': pair,
872 'price': price, 'size': size}
873 q.update(kwargs)
874 return self.private_query('orders', params=q)
875
876 @return_json(None)
877 def ask(self, pair, price, amount, **kwargs):
878 q = {'side': 'sell', 'type': 'market', 'product_id': pair,
879 'price': price, 'size': size}
880 q.update(kwargs)
881 return self.private_query('orders', params=q)
882
883 @return_json(None)
884 def cancel_order(self, order_id, all=False, **kwargs):
885
886 if not all:
887 return self.private_query('orders/%s' % order_id,
888 method_verb='DELETE', params=kwargs)
889 else:
890 return self.private_query('orders', method_verb='DELETE',
891 params=kwargs)
892
893 @return_json(None)
894 def order(self, order_id, **kwargs):
895 return self.private_query('orders/%s' % order_id, method_verb='GET',
896 params=kwargs)
897
898 @return_json(None)
899 def balance(self, **kwargs):
900 return self.private_query('accounts', method_verb='GET', params=kwargs)
901
902 @return_json(None)
903 def withdraw(self, _type, source_wallet, amount, tar_addr, **kwargs):
904 raise NotImplementedError()
905
906 @return_json(None)
907 def deposit_address(self, **kwargs):
908 raise NotImplementedError()
909
910 """
911 Exchange Specific Methods
912 """
913
914 @return_json
915 def time(self):
916 return self.public_query('time')
917
918 @return_json(None)
919 def currencies(self):
920 return self.public_query('currencies')
921
922 @return_json(None)
923 def pairs(self):
924 return self.public_query('products')
925
926 @return_json(None)
927 def ohlc(self, pair, **kwargs):
928 return self.public_query('products/%s/candles' % pair, params=kwargs)
929
930 @return_json(None)
931 def stats(self, pair, **kwargs):
932 return self.public_query('products/%s/stats' % pair, params=kwargs)
933
934# Import Built-Ins
935import logging
936import json
937import requests
938# Import Third-Party
939
940# Import Homebrew
941
942# Init Logging Facilities
943log = logging.getLogger(__name__)
944
945
946def return_json(formatter=None):
947 def decorator(func):
948 def wrapper(*args, **kwargs):
949 try:
950 r = func(*args, **kwargs)
951 except Exception as e:
952 log.error("return_json(): Error during call to "
953 "%s(%s, %s) %s" % (func.__name__, args, kwargs, e))
954 raise
955
956 try:
957 r.raise_for_status()
958 except requests.HTTPError as e:
959 log.error("return_json: HTTPError for url %s: "
960 "%s" % (r.request.url, e))
961 return None, r
962
963 try:
964 data = r.json()
965 except json.JSONDecodeError:
966 log.error('return_json: Error while parsing json. '
967 'Request url was: %s, result is: '
968 '%s' % (r.request.url, r.text))
969 return None, r
970 except Exception as e:
971 log.error("return_json(): Unexpected error while parsing json "
972 "from %s: %s" % (r.request.url, e))
973 raise
974
975 # Apply formatter and return
976 if formatter is not None:
977 return formatter(data, *args, **kwargs), r
978 else:
979 return data, r
980 return wrapper
981return decorator
982
983# Supported HTTP verbs.
984VERBS = 'DELETE GET PATCH POST PUT'.split()
985REQ_METHODS = {verb: getattr(requests, verb.lower()) for verb in VERBS}
986
987request_method = self.req_methods[method_verb]
988# ...
989r = request_method(url, timeout=5, **request_kwargs)
990
991r = requests.request(method_verb, url, timeout=5, **request_kwargs)
992
993>>> import time
994>>> time.get_clock_info('time').monotonic
995False
996
997from abc import ABCMeta, abstractmethod
998
999class RESTAPI(metaclass=ABCMeta):
1000 # ...
1001 @abstractmethod
1002 def sign(self, ...):
1003 # ...