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