Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| #! /usr/bin/env python | |
| """ | |
| The MIT License (MIT) | |
| Copyright (c) 2015 creon (creon.nu@gmail.com) | |
| Permission is hereby granted, free of charge, to any person obtaining a copy | |
| of this software and associated documentation files (the "Software"), to deal | |
| in the Software without restriction, including without limitation the rights | |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| copies of the Software, and to permit persons to whom the Software is | |
| furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in all | |
| copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
| IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
| DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
| OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE | |
| OR OTHER DEALINGS IN THE SOFTWARE. | |
| """ | |
| import sys | |
| import json | |
| import hmac | |
| import time | |
| import urllib | |
| import urllib2 | |
| import random | |
| import hashlib | |
| import httplib | |
| import threading | |
| import datetime | |
| class Exchange(object): | |
| def __init__(self, fee): | |
| self.fee = fee | |
| self._shift = 1 | |
| self._nonce = 0 | |
| def adjust(self, error): | |
| if 'exception caught:' not in error: | |
| self._shift = ((self._shift + 7) % 200) - 100 # -92 15 -78 29 -64 43 -50 57 ... | |
| def nonce(self, factor=1000.0): | |
| n = int((time.time() + self._shift) * float(factor)) | |
| if self._nonce >= n: | |
| n = self._nonce + 10 | |
| self._nonce = n | |
| return n | |
| class Bittrex(Exchange): | |
| def __init__(self): | |
| super(Bittrex, self).__init__(0.0025) | |
| self.placed = {} | |
| self.closed = [] | |
| def __repr__(self): | |
| return "bittrex" | |
| def adjust(self, error): | |
| pass | |
| def post(self, method, params, key, secret, throttle=5): | |
| data = 'https://bittrex.com/api/v1.1' + method + '?apikey=%s&nonce=%d&' % ( | |
| key, self.nonce()) + urllib.urlencode(params) | |
| sign = hmac.new(secret, data, hashlib.sha512).hexdigest() | |
| headers = {'apisign': sign} | |
| connection = httplib.HTTPSConnection('bittrex.com', timeout=10) | |
| connection.request('GET', data, headers=headers) | |
| response = json.loads(connection.getresponse().read()) | |
| if throttle > 0 and not response['success'] and 'THROTTLED' in response['message']: | |
| time.sleep(2) | |
| return self.post(method, params, key, secret, throttle - 1) | |
| return response | |
| def get(self, method, params): | |
| data = 'https://bittrex.com/api/v1.1' + method + '?' + urllib.urlencode(params) | |
| connection = httplib.HTTPSConnection('bittrex.com', timeout=10) | |
| connection.request('GET', data, headers={}) | |
| return json.loads(connection.getresponse().read()) | |
| def cancel_orders(self, unit, side, key, secret): | |
| response = self.post('/market/getopenorders', {'market': "%s-NBT" % unit.upper()}, key, secret) | |
| if not response['success']: | |
| response['error'] = response['message'] | |
| return response | |
| if not response['result']: | |
| response['result'] = [] | |
| response['removed'] = [] | |
| response['amount'] = 0.0 | |
| for order in response['result']: | |
| if side == 'all' or (side == 'bid' and 'BUY' in order['OrderType']) or ( | |
| side == 'ask' and 'SELL' in order['OrderType']): | |
| ret = self.post('/market/cancel', {'uuid': order['OrderUuid']}, key, secret) | |
| if not ret['success'] and ret['message'] != "ORDER_NOT_OPEN": | |
| if not 'error' in response: response = {'error': ""} | |
| response['error'] += "," + ret['message'] | |
| else: | |
| response['removed'].append(order['OrderUuid']) | |
| response['amount'] += order['Quantity'] | |
| if not 'error' in response and key in self.placed and unit in self.placed[key]: | |
| if side == 'all': | |
| self.placed[key][unit]['bid'] = False | |
| self.placed[key][unit]['ask'] = False | |
| else: | |
| self.placed[key][unit][side] = False | |
| return response | |
| def place_order(self, unit, side, key, secret, amount, price): | |
| ret = self.cancel_orders(unit, side, key, secret) | |
| if 'error' in ret: return ret | |
| amount += ret['amount'] | |
| if side == 'bid': | |
| amount *= (1.0 - self.fee) | |
| params = {'market': "%s-NBT" % unit.upper(), "rate": price, "quantity": amount} | |
| response = self.post('/market/buylimit' if side == 'bid' else '/market/selllimit', params, key, secret) | |
| if response['success']: | |
| response['id'] = response['result']['uuid'] | |
| if not key in self.placed: | |
| self.placed[key] = {} | |
| if not unit in self.placed[key]: | |
| self.placed[key][unit] = {'bid': False, 'ask': False} | |
| self.placed[key][unit][side] = response['id'] | |
| else: | |
| response['error'] = response['message'] | |
| response['residual'] = ret['amount'] | |
| return response | |
| def get_balance(self, unit, key, secret): | |
| response = self.post('/account/getbalance', {'currency': unit.upper()}, key, secret) | |
| if response['success']: | |
| try: | |
| response['balance'] = float(response['result']['Available']) | |
| except: | |
| response['balance'] = 0.0 | |
| else: | |
| response['error'] = response['message'] | |
| return response | |
| def get_price(self, unit): | |
| response = self.get('/public/getticker', {'market': '%s-NBT' % unit}) | |
| if response['success']: | |
| response.update({'bid': response['result']['Bid'], 'ask': response['result']['Ask']}) | |
| else: | |
| response['error'] = response['message'] | |
| return response | |
| def create_request(self, unit, key=None, secret=None): | |
| if not secret or not key: | |
| return None, None | |
| uuids = [] | |
| if key in self.placed and unit in self.placed[key]: | |
| if self.placed[key][unit]['bid']: | |
| uuids.append(self.placed[key][unit]['bid']) | |
| if self.placed[key][unit]['ask']: | |
| uuids.append(self.placed[key][unit]['ask']) | |
| requests = [] | |
| signatures = [] | |
| for uuid in uuids: | |
| data = 'https://bittrex.com/api/v1.1/account/getorder?apikey=%s&nonce=%d&uuid=%s' % ( | |
| key, self.nonce(), uuid) | |
| requests.append(data) | |
| signatures.append(hmac.new(secret, data, hashlib.sha512).hexdigest()) | |
| return {'requests': json.dumps(requests), 'signs': json.dumps(signatures)}, None | |
| def validate_request(self, key, unit, data, signs): | |
| orders = [] | |
| last_error = "" | |
| requests = json.loads(data['requests']) | |
| signs = json.loads(data['signs']) | |
| if len(requests) != len(signs): | |
| return { | |
| 'error': 'missmatch between requests and signatures (%d vs %d)' % (len(data['requests']), len(signs))} | |
| if len(requests) > 2: | |
| return {'error': 'too many requests received: %d' % len(requests)} | |
| connection = httplib.HTTPSConnection('bittrex.com', timeout=5) | |
| for data, sign in zip(requests, signs): | |
| uuid = data.split('=')[-1] | |
| if not uuid in self.closed: | |
| headers = {'apisign': sign} | |
| connection.request('GET', data, headers=headers) | |
| response = json.loads(connection.getresponse().read()) | |
| if response['success']: | |
| try: | |
| opened = int( | |
| datetime.datetime.strptime(response['result']['Opened'], '%Y-%m-%dT%H:%M:%S.%f').strftime( | |
| "%s")) | |
| except: | |
| opened = 0 | |
| try: | |
| closed = int( | |
| datetime.datetime.strptime(response['result']['Closed'], '%Y-%m-%dT%H:%M:%S.%f').strftime( | |
| "%s")) | |
| except: | |
| closed = sys.maxint | |
| if closed < time.time() - 60: | |
| self.closed.append(uuid) | |
| orders.append({ | |
| 'id': response['result']['OrderUuid'], | |
| 'price': response['result']['Limit'], | |
| 'type': 'ask' if 'SELL' in response['result']['Type'] else 'bid', | |
| 'amount': response['result']['QuantityRemaining'], | |
| # if not closed == sys.maxint else response['result']['Quantity'], | |
| 'opened': opened, | |
| 'closed': closed, | |
| }) | |
| else: | |
| last_error = response['message'] | |
| if not orders and last_error != "": | |
| return {'error': last_error} | |
| return orders | |
| class Cryptsy(Exchange): | |
| def __init__(self): | |
| super(Cryptsy, self).__init__(0.002) | |
| self.market_codes = {} | |
| self.currency_codes = {} | |
| self.key = None | |
| self.secret = None | |
| def __repr__(self): | |
| return "cryptsy" | |
| def adjust(self, error): | |
| pass | |
| def post(self, params, key, secret): | |
| self.key = key | |
| self.secret = secret | |
| request = {'nonce': self.nonce()} | |
| request.update(params) | |
| data = urllib.urlencode(request) | |
| sign = hmac.new(secret, data, hashlib.sha512).hexdigest() | |
| headers = {'Sign': sign, 'Key': key} | |
| return json.loads(urllib2.urlopen(urllib2.Request( | |
| url='https://api.cryptsy.com/api', data=data, | |
| headers=headers)).read()) | |
| def get(self, method, params, key, secret): | |
| self.key = key | |
| self.secret = secret | |
| request = {'nonce': self.nonce()} | |
| request.update(params) | |
| data = urllib.urlencode(request) | |
| sign = hmac.new(secret, data, hashlib.sha512).hexdigest() | |
| headers = {'Sign': sign, 'Key': key} | |
| return json.loads(urllib2.urlopen(urllib2.Request( | |
| url='https://api.cryptsy.com/api/{0}?{1}'.format(method, data), | |
| headers=headers)).read()) | |
| def get_market_codes(self, key, secret): | |
| if self.market_codes: | |
| return self.market_codes | |
| market_data = self.post({'method': 'getmarkets'}, key, secret) | |
| for market in market_data['return']: | |
| self.market_codes[market['label']] = market['marketid'] | |
| return self.market_codes | |
| def get_market_id(self, unit, key, secret): | |
| codes = self.get_market_codes(key, secret) | |
| return codes['NBT/{0}'.format(unit.upper())] | |
| def cancel_orders(self, unit, side, key, secret): | |
| response = self.post({'method': 'myorders', | |
| 'marketid': self.get_market_id(unit, key, secret)}, key, | |
| secret) | |
| if int(response['success']) == 0: | |
| return response | |
| for order in response['return']: | |
| if side == 'all' or (side == 'bid' and order['ordertype'] == 'Buy') or ( | |
| side == 'ask' and order['ordertype'] == 'Sell'): | |
| ret = self.post({'method': 'cancelorder', 'orderid': order['orderid']}, | |
| key, secret) | |
| if int(ret['success']) == 0: | |
| if isinstance(response, list): | |
| response = {'error': ""} | |
| response['error'] += "," + ret['error'] | |
| return response | |
| def get_fee(self, unit, side, amount, price, key, secret): | |
| params = {'method': 'calculatefees', | |
| 'ordertype': 'Buy' if side == 'bid' else 'Sell', | |
| 'quantity': amount, | |
| 'price': price, | |
| 'marketid': self.get_market_id(unit, key, secret)} | |
| response = self.post(params, key, secret) | |
| if int(response['success']) == 0: | |
| return self.fee | |
| return (float(response['return']['fee']) / float(price)) / 100 | |
| def place_order(self, unit, side, key, secret, amount, price): | |
| params = {'method': 'createorder', | |
| 'marketid': self.get_market_id(unit, key, secret), | |
| 'ordertype': 'Buy' if side == 'bid' else 'Sell', | |
| 'quantity': | |
| (str(float(amount) - float(self.get_fee(unit, side, amount, | |
| price, key, secret)))) if | |
| side == 'bid' else amount, | |
| 'price': price} | |
| response = self.post(params, key, secret) | |
| if int(response['success']) > 0: | |
| response['id'] = int(response['orderid']) | |
| return response | |
| def get_balance(self, unit, key, secret): | |
| response = self.post({'method': 'getinfo'}, key, secret) | |
| if int(response['success']) > 0: | |
| response['balance'] = float( | |
| response['return']['balances_available'][unit.upper()]) | |
| return response | |
| def get_price(self, unit): | |
| response = self.post({'method': 'depth', 'marketid': self.get_market_id( | |
| unit, | |
| self.key, | |
| self.secret)}, self.key, self.secret) | |
| if int(response['success']) > 0: | |
| response.update({'bid': None, 'ask': None}) | |
| if response['return']['buy']: | |
| response['bid'] = float(response['return']['buy'][0][0]) | |
| if response['return']['sell']: | |
| response['ask'] = float(response['return']['sell'][0][0]) | |
| return response | |
| def create_request(self, unit, key=None, secret=None): | |
| if not secret: | |
| return None, None | |
| request = {'method': 'myorders', | |
| 'marketid': self.get_market_id(unit, key, secret), | |
| 'nonce': self.nonce()} | |
| data = urllib.urlencode(request) | |
| sign = hmac.new(secret, data, hashlib.sha512).hexdigest() | |
| return request, sign | |
| def validate_request(self, key, unit, data, sign): | |
| headers = {'Sign': sign, 'Key': key} | |
| ret = urllib2.urlopen(urllib2.Request('https://api.cryptsy.com/api', | |
| urllib.urlencode(data), headers), timeout=5) | |
| response = json.loads(ret.read()) | |
| if int(response['success']) == 0: | |
| return response | |
| return [{'id': int(order['orderid']), | |
| 'price': float(order['price']), | |
| 'type': 'ask' if order['ordertype'] == 'Sell' else 'bid', | |
| 'amount': float(order['quantity'])} for order in response['return']] | |
| class SouthXChange(Exchange): | |
| def __init__(self): | |
| super(SouthXChange, self).__init__(0.002) | |
| def __repr__(self): | |
| return 'southxchange' | |
| def adjust(self, error): | |
| pass | |
| def post(self, method, key, secret, params=None): | |
| data = {'nonce': self.nonce(), 'key': key} | |
| if params is not None: | |
| data.update(params) | |
| data = json.dumps(data) | |
| sign = hmac.new(secret, data, hashlib.sha512).hexdigest() | |
| headers = {'Hash': sign, 'Content-Type': 'application/json'} | |
| url = 'https://www.southxchange.com/api/{0}/'.format(method) | |
| try: | |
| request = urllib2.Request(url=url, data=data, headers=headers) | |
| except urllib2.HTTPError as e: | |
| if e.code == 500: | |
| return {'error': 'API returned 500 error'} | |
| return {'error': 'API error'} | |
| return json.loads(urllib2.urlopen(request).read()) | |
| def get(self, method, unit=None): | |
| url = 'https://www.southxchange.com/api/{0}/'.format(method) | |
| if unit is not None: | |
| url += unit.upper() + "/NBT" | |
| try: | |
| request = urllib2.Request(url=url) | |
| except urllib2.HTTPError as e: | |
| if e.code == 500: | |
| return {'error': 'API returned 500 error'} | |
| return {'error': 'API error'} | |
| return json.loads(urllib2.urlopen(request).read()) | |
| def get_price(self, unit): | |
| response = self.get('price', unit) | |
| if 'error' not in response: | |
| response.update({'bid': None, 'ask': None}) | |
| response['bid'] = response['Bid'] | |
| response['ask'] = response['Ask'] | |
| return response | |
| def place_order(self, unit, side, key, secret, amount, price): | |
| method = 'placeOrder' | |
| params = {'listingCurrency': unit.upper(), | |
| 'referenceCurrency': 'NBT', | |
| 'type': 'sell' if side == 'bid' else 'buy', | |
| 'amount': (round(float(amount) * float(price), 4)) if side == 'bid' | |
| else (round(float(amount) / float(price), 4)), | |
| 'limitPrice': round(1 / float(price), 4)} | |
| response = self.post(method, key, secret, params) | |
| if 'error' in response: | |
| return response | |
| return {'id': response} | |
| def cancel_orders(self, unit, side, key, secret): | |
| response = self.post('listOrders', key, secret) | |
| if 'error' in response: | |
| return response | |
| for order in response: | |
| if side == 'all' or (side == 'bid' and order['Type'] == 'sell') or \ | |
| (side == 'ask' and order['Type'] == 'buy'): | |
| if order['ListingCurrency'] == unit.upper() and \ | |
| order['ReferenceCurrency'] == 'NBT': | |
| ret = self.post('cancelOrder', key, secret, {'orderCode': order['Code']}) | |
| if 'error' in ret: | |
| if isinstance(response, list): | |
| response = {'error': ""} | |
| response['error'] += "," + ret['error'] | |
| print response | |
| return response | |
| def get_balance(self, unit, key, secret): | |
| response = self.post('listBalances', key, secret) | |
| if 'error' in response: | |
| response = [] | |
| unit = unit.upper() | |
| for balance in response: | |
| if balance['Currency'] == unit: | |
| return {'balance': balance['Available']} | |
| return {'balance': 0} | |
| def create_request(self, unit, key=None, secret=None): | |
| if not secret and not key: | |
| return None, None | |
| request = {'nonce': self.nonce(), 'key': key} | |
| data = json.dumps(request) | |
| sign = hmac.new(secret, data, hashlib.sha512).hexdigest() | |
| return {'data': data}, sign | |
| def validate_request(self, key, unit, data, sign): | |
| url = 'https://www.southxchange.com/api/listOrders' | |
| headers = {'Hash': sign, 'Content-Type': 'application/json'} | |
| request = urllib2.Request(url=url, data=data['data'], headers=headers) | |
| response = json.loads(urllib2.urlopen(request).read()) | |
| return [{ | |
| 'id': order['Code'], | |
| 'price': round(1 / float(order['LimitPrice']), 8), | |
| 'type': 'bid' if order['Type'] == 'sell' else 'ask', | |
| 'amount': round(float(order['Amount']) * float(order['LimitPrice']), | |
| 8) if order['Type'] == 'sell' else round(float( | |
| order['Amount']) / float(order['LimitPrice']), 8) | |
| } for order in response] | |
| class Poloniex(Exchange): | |
| def __init__(self): | |
| super(Poloniex, self).__init__(0.002) | |
| def __repr__(self): | |
| return "poloniex" | |
| def adjust(self, error): | |
| if "Nonce must be greater than" in error: # (TODO: regex) | |
| if ':' in error: error = error.split(':')[1].strip() | |
| error = error.replace('.', '').split() | |
| self._shift += 100.0 + (int(error[5]) - int(error[8])) / 1000.0 | |
| else: | |
| self._shift = self._shift + 100.0 | |
| def post(self, method, params, key, secret): | |
| request = {'nonce': self.nonce(), 'command': method} | |
| request.update(params) | |
| data = urllib.urlencode(request) | |
| sign = hmac.new(secret, data, hashlib.sha512).hexdigest() | |
| headers = {'Sign': sign, 'Key': key} | |
| return json.loads(urllib2.urlopen(urllib2.Request('https://poloniex.com/tradingApi', data, headers)).read()) | |
| def cancel_orders(self, unit, side, key, secret): | |
| response = self.post('returnOpenOrders', {'currencyPair': "%s_NBT" % unit.upper()}, key, secret) | |
| if 'error' in response: return response | |
| for order in response: | |
| if side == 'all' or (side == 'bid' and order['type'] == 'buy') or ( | |
| side == 'ask' and order['type'] == 'sell'): | |
| ret = self.post('cancelOrder', | |
| {'currencyPair': "%s_NBT" % unit.upper(), 'orderNumber': order['orderNumber']}, key, | |
| secret) | |
| if 'error' in ret: | |
| if isinstance(response, list): response = {'error': ""} | |
| response['error'] += "," + ret['error'] | |
| return response | |
| def place_order(self, unit, side, key, secret, amount, price): | |
| params = {'currencyPair': "%s_NBT" % unit.upper(), "rate": price, "amount": amount} | |
| response = self.post('buy' if side == 'bid' else 'sell', params, key, secret) | |
| if not 'error' in response: | |
| response['id'] = int(response['orderNumber']) | |
| return response | |
| def get_balance(self, unit, key, secret): | |
| response = self.post('returnBalances', {}, key, secret) | |
| if not 'error' in response: | |
| response['balance'] = float(response[unit.upper()]) | |
| return response | |
| def get_price(self, unit): | |
| response = json.loads(urllib2.urlopen('https://poloniex.com/public?' + | |
| urllib.urlencode({'command': 'returnOrderBook', | |
| 'currencyPair': "%s_NBT" % unit.upper(), 'depth': 1}), | |
| timeout=5).read()) | |
| if not 'error' in response: | |
| response.update({'bid': None, 'ask': None}) | |
| if response['bid']: response['bid'] = float(response['bid'][0]) | |
| if response['ask']: response['ask'] = float(response['ask'][0]) | |
| return response | |
| def create_request(self, unit, key=None, secret=None): | |
| if not secret: return None, None | |
| request = {'command': 'returnOpenOrders', 'nonce': self.nonce(), 'currencyPair': "%s_NBT" % unit.upper()} | |
| data = urllib.urlencode(request) | |
| sign = hmac.new(secret, data, hashlib.sha512).hexdigest() | |
| return request, sign | |
| def validate_request(self, key, unit, data, sign): | |
| headers = {'Sign': sign, 'Key': key} | |
| ret = urllib2.urlopen(urllib2.Request('https://poloniex.com/tradingApi', urllib.urlencode(data), headers), | |
| timeout=5) | |
| response = json.loads(ret.read()) | |
| if 'error' in response: return response | |
| return [{ | |
| 'id': int(order['orderNumber']), | |
| 'price': float(order['rate']), | |
| 'type': 'ask' if order['type'] == 'sell' else 'bid', | |
| 'amount': float(order['amount']), | |
| } for order in response] | |
| class CCEDK(Exchange): | |
| def __init__(self): | |
| super(CCEDK, self).__init__(0.002) | |
| self.pair_id = {} | |
| self.currency_id = {} | |
| failed = False | |
| while not self.pair_id or not self.currency_id: | |
| try: | |
| response = None | |
| if not self.pair_id: | |
| url = 'https://www.ccedk.com/api/v1/stats/marketdepthfull' | |
| response = json.loads(urllib2.urlopen(urllib2.Request(url), timeout=15).read()) | |
| for unit in response['response']['entities']: | |
| if unit['pair_name'][:4] == 'nbt/': | |
| self.pair_id[unit['pair_name'][4:]] = unit['pair_id'] | |
| if not self.currency_id: | |
| url = 'https://www.ccedk.com/api/v1/currency/list' | |
| response = json.loads(urllib2.urlopen(urllib2.Request(url), timeout=15).read()) | |
| for unit in response['response']['entities']: | |
| self.currency_id[unit['iso'].lower()] = unit['currency_id'] | |
| except Exception as e: | |
| if response and not response['response']: | |
| self.adjust(",".join(response['errors'].values())) | |
| if failed: | |
| print >> sys.stderr, "could not retrieve ccedk ids, will adjust shift to", self._shift, \ | |
| "reason:", ",".join(response['errors'].values()) | |
| else: | |
| print >> sys.stderr, "could not retrieve ccedk ids, server is unreachable", e | |
| failed = True | |
| time.sleep(1) | |
| def __repr__(self): | |
| return "ccedk" | |
| def nonce(self, factor=1.0): | |
| n = int(time.time() + self._shift) | |
| if n == self._nonce: | |
| n = self._nonce + 1 | |
| self._nonce = n | |
| return n | |
| def adjust(self, error): | |
| if "incorrect range" in error: # (TODO: regex) | |
| if ':' in error: | |
| error = error.split(':')[1].strip() | |
| try: | |
| minimum = int(error.strip().split()[-3].replace('`', '')) | |
| maximum = int(error.strip().split()[-1].replace('`', '')) | |
| current = int(error.strip().split()[-7].split('`')[3]) | |
| except: | |
| self._shift += random.randrange(-10, 10) | |
| else: | |
| if current < maximum: | |
| new_shift = (minimum + 2 * maximum) / 3 - current | |
| if new_shift < 0: | |
| new_shift = 10 | |
| else: | |
| new_shift = (2 * minimum + maximum) / 3 - current | |
| if new_shift != 0: | |
| self._shift += new_shift | |
| else: | |
| self._shift += random.randrange(-10, 10) | |
| else: | |
| self._shift += random.randrange(-10, 10) | |
| def post(self, method, params, key, secret): | |
| request = {'nonce': self.nonce()} # TODO: check for unique nonce | |
| request.update(params) | |
| data = urllib.urlencode(request) | |
| sign = hmac.new(secret, data, hashlib.sha512).hexdigest() | |
| headers = {"Content-type": "application/x-www-form-urlencoded", "Key": key, "Sign": sign} | |
| url = 'https://www.ccedk.com/api/v1/' + method | |
| response = json.loads(urllib2.urlopen(urllib2.Request(url, data, headers), timeout=15).read()) | |
| if response['errors'] is True: | |
| response['error'] = ",".join(response['errors'].values()) | |
| return response | |
| def cancel_orders(self, unit, side, key, secret): | |
| response = self.post('order/list', {}, key, secret) | |
| if not response['response'] or not response['response']['entities']: | |
| return response | |
| for order in response['response']['entities']: | |
| if side == 'all' \ | |
| or (side == 'bid' and order['type'] == 'buy') \ | |
| or (side == 'ask' and order['type'] == 'sell'): | |
| if order['pair_id'] == self.pair_id[unit.lower()]: | |
| ret = self.post('order/cancel', {'order_id': order['order_id']}, key, secret) | |
| if ret['errors'] is True: | |
| if 'error' not in response: | |
| response['error'] = "" | |
| response['error'] += ",".join(ret['errors'].values()) | |
| return response | |
| def place_order(self, unit, side, key, secret, amount, price): | |
| params = {"type": 'buy' if side == 'bid' else 'sell', | |
| "price": price, | |
| "pair_id": int(self.pair_id[unit.lower()]), | |
| "amount": amount} | |
| response = self.post('order/new', params, key, secret) | |
| if response['errors'] is True: | |
| response['error'] = ",".join(response['errors'].values()) | |
| else: | |
| response['id'] = int(response['response']['entity']['order_id']) | |
| return response | |
| def get_balance(self, unit, key, secret): | |
| params = {"currency_id": self.currency_id[unit.lower()]} | |
| response = self.post('balance/info', params, key, secret) | |
| if response['errors'] is True: | |
| response['error'] = ",".join(response['errors'].values()) | |
| else: | |
| response['balance'] = float(response['response']['entity']['balance']) | |
| return response | |
| def get_price(self, unit): | |
| url = 'https://www.ccedk.com/api/v1/orderbook/info?' + urllib.urlencode({'pair_id': self.pair_id[unit.lower()]}) | |
| response = json.loads(urllib2.urlopen(urllib2.Request(url), timeout=5).read()) | |
| if response['errors'] is True: | |
| response['error'] = ",".join(response['errors'].values()) | |
| return response | |
| response.update({'bid': None, 'ask': None}) | |
| if response['response']['entities']['bids']: | |
| response['bid'] = float(response['response']['entities']['bids'][0]['price']) | |
| if response['response']['entities']['asks']: | |
| response['ask'] = float(response['response']['entities']['asks'][0]['price']) | |
| return response | |
| def create_request(self, unit, key=None, secret=None): | |
| if not secret: | |
| return None, None | |
| request = {'nonce': self.nonce()} | |
| data = urllib.urlencode(request) | |
| sign = hmac.new(secret, data, hashlib.sha512).hexdigest() | |
| return request, sign | |
| def validate_request(self, key, unit, data, sign): | |
| headers = {"Content-type": "application/x-www-form-urlencoded", "Key": key, "Sign": sign} | |
| url = 'https://www.ccedk.com/api/v1/order/list' | |
| response = json.loads(urllib2.urlopen(urllib2.Request(url, urllib.urlencode(data), headers), timeout=5).read()) | |
| if response['errors'] is True: | |
| response['error'] = ",".join(response['errors'].values()) | |
| return response | |
| if not response['response']['entities']: | |
| response['response']['entities'] = [] | |
| validation = [{ | |
| 'id': int(order['order_id']), | |
| 'price': float(order['price']), | |
| 'type': 'ask' if order['type'] == 'sell' else 'bid', | |
| 'amount': float(order['volume']), | |
| } for order in response['response']['entities'] if order['pair_id'] == self.pair_id[unit.lower()]] | |
| return validation | |
| class BitcoinCoId(Exchange): | |
| def __init__(self): | |
| super(BitcoinCoId, self).__init__(0.0) | |
| try: | |
| ping = time.time() | |
| response = json.loads(urllib2.urlopen(urllib2.Request('https://vip.bitcoin.co.id/api/summaries')).read()) | |
| self._shift = float(response['tickers']['btc_idr']['server_time']) - ping | |
| except: | |
| pass | |
| def __repr__(self): | |
| return "bitcoincoid" | |
| def adjust(self, error): | |
| if "Nonce must be greater than" in error: # (TODO: regex) | |
| if ':' in error: error = error.split(':')[1].strip() | |
| error = error.replace('.', '').split() | |
| self._shift += 100.0 + (int(error[5]) - int(error[8])) / 1000.0 | |
| else: | |
| self._shift = self._shift + 100.0 | |
| def nonce(self, factor=1000.0): | |
| n = int((time.time() + self._shift) * float(factor)) | |
| if n - self._nonce < 300: | |
| n = self._nonce + 300 | |
| self._nonce = n | |
| return n | |
| def post(self, method, params, key, secret): | |
| request = {'nonce': self.nonce(), 'method': method} | |
| request.update(params) | |
| data = urllib.urlencode(request) | |
| sign = hmac.new(secret, data, hashlib.sha512).hexdigest() | |
| headers = {'Sign': sign, 'Key': key} | |
| response = json.loads(urllib2.urlopen(urllib2.Request('https://vip.bitcoin.co.id/tapi', data, headers)).read()) | |
| return response | |
| def cancel_orders(self, unit, side, key, secret): | |
| response = self.post('openOrders', {'pair': 'nbt_' + unit.lower()}, key, secret) | |
| if response['success'] == 0 or not response['return']['orders']: return response | |
| for order in response['return']['orders']: | |
| if side == 'all' or (side == 'bid' and order['type'] == 'buy') or ( | |
| side == 'ask' and order['type'] == 'sell'): | |
| params = {'pair': 'nbt_' + unit.lower(), 'order_id': order['order_id'], 'type': order['type']} | |
| ret = self.post('cancelOrder', params, key, secret) | |
| if 'error' in ret: | |
| if not 'error' in response: response['error'] = "" | |
| response['error'] += "," + ret['error'] | |
| return response | |
| def place_order(self, unit, side, key, secret, amount, price): | |
| params = {'pair': 'nbt_' + unit.lower(), 'type': 'buy' if side == 'bid' else 'sell', 'price': price} | |
| if side == 'bid': | |
| params[unit.lower()] = amount * price | |
| else: | |
| params['nbt'] = amount | |
| params[unit] = amount * price | |
| response = self.post('trade', params, key, secret) | |
| if response['success'] == 1: | |
| response['id'] = int(response['return']['order_id']) | |
| return response | |
| def get_balance(self, unit, key, secret): | |
| response = self.post('getInfo', {}, key, secret) | |
| if response['success'] == 1: | |
| response['balance'] = float(response['return']['balance'][unit.lower()]) | |
| return response | |
| def get_price(self, unit): | |
| response = json.loads( | |
| urllib2.urlopen(urllib2.Request('https://vip.bitcoin.co.id/api/nbt_%s/depth' % unit.lower()), | |
| timeout=5).read()) | |
| if 'error' in response: | |
| return response | |
| response.update({'bid': None, 'ask': None}) | |
| if response['buy']: response['bid'] = float(response['buy'][0][0]) | |
| if response['sell']: response['ask'] = float(response['sell'][0][0]) | |
| return response | |
| def create_request(self, unit, key=None, secret=None): | |
| if not secret: return None, None | |
| request = {'nonce': self.nonce(), 'pair': 'nbt_' + unit.lower(), 'method': 'openOrders'} | |
| data = urllib.urlencode(request) | |
| sign = hmac.new(secret, data, hashlib.sha512).hexdigest() | |
| return request, sign | |
| def validate_request(self, key, unit, data, sign): | |
| headers = {"Key": key, "Sign": sign} | |
| response = json.loads( | |
| urllib2.urlopen(urllib2.Request('https://vip.bitcoin.co.id/tapi', urllib.urlencode(data), headers), | |
| timeout=5).read()) | |
| if response['success'] == 0: | |
| return response | |
| if not response['return']['orders']: | |
| response['return']['orders'] = [] | |
| return [{ | |
| 'id': int(order['order_id']), | |
| 'price': float(order['price']), | |
| 'type': 'ask' if order['type'] == 'sell' else 'bid', | |
| 'amount': float(order['remain_' + (unit.lower() if order['type'] == 'buy' else 'nbt')]) / ( | |
| float(order['price']) if order['type'] == 'buy' else 1.0), | |
| } for order in response['return']['orders']] | |
| class BTER(Exchange): | |
| def __init__(self): | |
| super(BTER, self).__init__(0.002) | |
| def __repr__(self): | |
| return "bter" | |
| def adjust(self, error): | |
| pass | |
| def https_request(self, method, params, headers=None, timeout=None): | |
| if not headers: headers = {} | |
| connection = httplib.HTTPSConnection('data.bter.com', timeout=timeout) | |
| connection.request('POST', '/api/1/private/' + method, params, headers) | |
| response = connection.getresponse().read() | |
| return json.loads(response) | |
| def post(self, method, params, key, secret): | |
| data = urllib.urlencode(params) | |
| sign = hmac.new(secret, data, hashlib.sha512).hexdigest() | |
| headers = {'Sign': sign, 'Key': key, "Content-type": "application/x-www-form-urlencoded"} | |
| return self.https_request(method, data, headers) | |
| def cancel_orders(self, unit, side, key, secret): | |
| response = self.post('orderlist', {}, key, secret) | |
| if not response['result']: | |
| response['error'] = response['msg'] | |
| return response | |
| if not response['orders']: response['orders'] = [] | |
| for order in response['orders']: | |
| if side == 'all' or (side == 'ask' and order['sell_type'] != unit) or ( | |
| side == 'bid' and order['buy_type'] != unit): | |
| if order['pair'] == 'nbt_' + unit.lower(): | |
| params = {'order_id': order['oid']} | |
| ret = self.post('cancelorder', params, key, secret) | |
| if not ret['result']: | |
| if not 'error' in response: response['error'] = "" | |
| response['error'] += "," + ret['msg'] | |
| return response | |
| def place_order(self, unit, side, key, secret, amount, price): | |
| params = {'pair': 'nbt_' + unit.lower(), 'type': 'buy' if side == 'bid' else 'sell', 'rate': price, | |
| 'amount': amount} | |
| response = self.post('placeorder', params, key, secret) | |
| if response['result']: | |
| response['id'] = int(response['order_id']) | |
| else: | |
| response['error'] = response['msg'] | |
| return response | |
| def get_balance(self, unit, key, secret): | |
| response = self.post('getfunds', {}, key, secret) | |
| if response['result']: | |
| if unit.upper() in response['available_funds']: | |
| response['balance'] = float(response['available_funds'][unit.upper()]) | |
| else: | |
| response['balance'] = 0.0 | |
| else: | |
| response['error'] = response['msg'] | |
| return response | |
| def get_price(self, unit): | |
| connection = httplib.HTTPSConnection('data.bter.com', timeout=5) | |
| connection.request('GET', '/api/1/depth/nbt_' + unit.lower()) | |
| response = json.loads(connection.getresponse().read()) | |
| if not 'result' in response or not response['result']: | |
| response['error'] = response['msg'] if 'msg' in response else 'invalid response: %s' % str(response) | |
| return response | |
| response.update({'bid': None, 'ask': None}) | |
| if response['bids']: response['bid'] = float(response['bids'][0][0]) | |
| if response['asks']: response['ask'] = float(response['asks'][-1][0]) | |
| return response | |
| def create_request(self, unit, key=None, secret=None): | |
| if not secret: return None, None | |
| request = {} # no nonce required | |
| data = urllib.urlencode(request) | |
| sign = hmac.new(secret, data, hashlib.sha512).hexdigest() | |
| return request, sign | |
| def validate_request(self, key, unit, data, sign): | |
| headers = {'Sign': sign, 'Key': key, "Content-type": "application/x-www-form-urlencoded"} | |
| response = self.https_request('orderlist', urllib.urlencode(data), headers, timeout=15) | |
| if not 'result' in response or not response['result']: | |
| response['error'] = response['msg'] if 'msg' in response else 'invalid response: %s' % str(response) | |
| return response | |
| if not response['orders']: | |
| response['orders'] = [] | |
| return [{ | |
| 'id': int(order['oid']), | |
| 'price': float(order['rate']), | |
| 'type': 'ask' if order['buy_type'].lower() == unit.lower() else 'bid', | |
| 'amount': float(order['amount']) / ( | |
| 1.0 if order['buy_type'].lower() == unit.lower() else float(order['rate'])), | |
| } for order in response['orders'] if order['pair'] == 'nbt_' + unit.lower()] | |
| class Peatio(Exchange): | |
| def __init__(self): | |
| super(Peatio, self).__init__(0.002) | |
| def __repr__(self): | |
| return "testing" | |
| def adjust(self, error): | |
| if "is invalid, current timestamp is" in error: | |
| try: | |
| tonce = int(error.split()[2]) | |
| times = int(error.split()[-1].replace('.', '')) | |
| self._shift = int(float(times - tonce) / 1000.0) | |
| except: | |
| print error | |
| pass | |
| else: | |
| print error | |
| def urlencode(self, params): # from https://github.com/JohnnyZhao/peatio-client-python/blob/master/lib/auth.py#L11 | |
| keys = sorted(params.keys()) | |
| query = '' | |
| for key in keys: | |
| value = params[key] | |
| if key != "orders": | |
| query = "%s&%s=%s" % (query, key, value) if len(query) else "%s=%s" % (key, value) | |
| else: | |
| d = {key: params[key]} | |
| for v in value: | |
| ks = v.keys() | |
| ks.sort() | |
| for k in ks: | |
| item = "orders[][%s]=%s" % (k, v[k]) | |
| query = "%s&%s" % (query, item) if len(query) else "%s" % item | |
| return query | |
| def query(self, qtype, method, params, key, secret): | |
| request = {'tonce': self.nonce(), 'access_key': key} | |
| request.update(params) | |
| data = self.urlencode(request) | |
| msg = "%s|/api/v2/%s|%s" % (qtype, method, data) | |
| data += "&signature=" + hmac.new(secret, msg, hashlib.sha256).hexdigest() | |
| connection = httplib.HTTPSConnection('178.62.140.24', timeout=5) | |
| connection.request(qtype, '/api/v2/' + method + '?' + data) | |
| return json.loads(connection.getresponse().read()) | |
| def post(self, method, params, key, secret): | |
| return self.query('POST', method, params, key, secret) | |
| def get(self, method, params, key, secret): | |
| return self.query('GET', method, params, key, secret) | |
| def cancel_orders(self, unit, side, key, secret): | |
| response = self.get('orders.json', {'market': "nbt%s" % unit.lower()}, key, secret) | |
| if 'error' in response: | |
| response['error'] = response['error']['message'] | |
| return response | |
| for order in response: | |
| if side == 'all' or (side == 'bid' and order['side'] == 'buy') or ( | |
| side == 'ask' and order['side'] == 'sell'): | |
| ret = self.post('order/delete.json', {'id': order['id']}, key, secret) | |
| if 'error' in ret: | |
| if isinstance(response, list): response = {'error': ""} | |
| response['error'] += "," + ret['error']['message'] | |
| return response | |
| def place_order(self, unit, side, key, secret, amount, price): | |
| params = {'market': "nbt%s" % unit.lower(), "side": 'buy' if side == 'bid' else 'sell', "volume": amount, | |
| "price": price} | |
| response = self.post('orders', params, key, secret) | |
| if 'error' in response: | |
| response['error'] = response['error']['message'] | |
| else: | |
| response['id'] = int(response['id']) | |
| return response | |
| def get_balance(self, unit, key, secret): | |
| response = self.get('members/me.json', {}, key, secret) | |
| if 'error' in response: | |
| response['error'] = response['error']['message'] | |
| else: | |
| response['balance'] = 0.0 | |
| for pair in response['accounts']: | |
| if pair['currency'] == unit.lower(): | |
| response['balance'] = float(pair['balance']) | |
| return response | |
| def get_price(self, unit): | |
| connection = httplib.HTTPSConnection('178.62.140.24', timeout=15) | |
| connection.request('GET', | |
| '/api/v2/depth.json?' + self.urlencode({'market': "nbt%s" % unit.lower(), 'limit': 1})) | |
| response = json.loads(connection.getresponse().read()) | |
| if 'error' in response: | |
| response['error'] = response['error']['message'] | |
| return response | |
| response.update({'bid': None, 'ask': None}) | |
| if response['bids']: response['bid'] = float(response['bids'][0][0]) | |
| if response['asks']: response['ask'] = float(response['asks'][-1][0]) | |
| return response | |
| def create_request(self, unit, key=None, secret=None): | |
| if not secret: return None, None | |
| request = {'tonce': self.nonce(), 'access_key': key, 'market': "nbt%s" % unit.lower()} | |
| data = self.urlencode(request) | |
| msg = "GET|/api/v2/orders.json|%s" % data | |
| request['signature'] = hmac.new(secret, msg, hashlib.sha256).hexdigest() | |
| return request, '' | |
| def validate_request(self, key, unit, data, sign): | |
| if not 'market' in data or data['market'] != "nbt%s" % unit.lower(): | |
| return {'error': 'invalid market'} | |
| connection = httplib.HTTPSConnection('178.62.140.24', timeout=15) | |
| connection.request('GET', '/api/v2/orders.json?' + self.urlencode(data)) | |
| response = json.loads(connection.getresponse().read()) | |
| if 'error' in response: | |
| response['error'] = response['error']['message'] | |
| return response | |
| return [{ | |
| 'id': int(order['id']), | |
| 'price': float(order['price']), | |
| 'type': 'ask' if order['side'] == 'sell' else 'bid', | |
| 'amount': float(order['remaining_volume']), | |
| } for order in response] |