├── .gitignore ├── MANIFEST.in ├── README ├── README.rst ├── bittrex ├── bittrex.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.ini 3 | build/* 4 | env/* 5 | *.swp 6 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include bittrex 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | pythonbittrex 2 | ============= 3 | 4 | A python wrapper for the bittrex.com api. Under developement. 5 | 6 | 7 | Overview 8 | ======== 9 | 10 | A simple wrapper for the `bittrex`_ API. Which can be used 11 | to build trading bots or as a simple terminal ticker 12 | using the built in command line runner. 13 | 14 | 15 | Quickstart 16 | ========== 17 | 18 | To get started quickey with the module, install it and run 19 | using the simple command line runner:: 20 | 21 | python setup.py install 22 | watch 'bittrex getticker btc-doge| python -m json.tool' 23 | 24 | 25 | API KEYS 26 | ======== 27 | 28 | To enable the usage of private API methods an ini style file 29 | is required with the folling format:: 30 | 31 | [bittrex] 32 | key=bittrex_api_key 33 | secret=bittrex_api_secret 34 | 35 | By default pythonbittrex witll look for a `.bittrex.ini` file 36 | in the current users home directory. An alternative location 37 | for this file can be specified by setting an environment 38 | variable `BITTREX_KEY_FILE` which points to the full path 39 | of the file. 40 | 41 | NOTE: Your bittex key file contains sensitive information so 42 | restrictive permissions should be set for the file:: 43 | 44 | chmod 600 ~/.bittrex.ini 45 | 46 | pythonbittrex will print a warning if this file doesn't exists. 47 | 48 | 49 | Usage 50 | ===== 51 | 52 | To use the API client in your own scripts:: 53 | 54 | from bittrex import BittrexAPI 55 | api = BittrexAPI(apt_key=key, api_secret=secret) 56 | data = api.getopenorders() 57 | 58 | 59 | By default all return values are the Python `dict` type. To get 60 | raw JSON response pass `raw=True` to the `BittrexAPI` constructor. 61 | 62 | 63 | .. _bittrex: https://bittrex.com 64 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | README -------------------------------------------------------------------------------- /bittrex: -------------------------------------------------------------------------------- 1 | bittrex.py -------------------------------------------------------------------------------- /bittrex.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | bittrex.com api wrapper. 4 | """ 5 | import ConfigParser 6 | import hashlib 7 | import hmac 8 | import json 9 | import os 10 | import sys 11 | import time 12 | import urllib 13 | import urllib2 14 | import urlparse 15 | import textwrap 16 | 17 | 18 | _KEY_FILE = os.environ.get( 19 | 'BITTREX_KEY_FILE', 20 | os.path.join(os.path.expanduser('~'), '.bittrex.ini')) 21 | _config = ConfigParser.SafeConfigParser() 22 | if not _config.read(_KEY_FILE): 23 | sys.stderr.write('Key file not read, private methods will not work.\n') 24 | API_KEY = None 25 | API_SECRET = None 26 | else: 27 | API_KEY = _config.get('bittrex', 'key') 28 | API_SECRET = _config.get('bittrex', 'secret') 29 | 30 | 31 | GET_MARKETS_URI = "https://bittrex.com/api/v1.1/public/getmarkets" 32 | GET_CURRENCIES_URI = "https://bittrex.com/api/v1.1/public/getcurrencies" 33 | GET_TICKER = "https://bittrex.com/api/v1.1/public/getticker" 34 | GET_MARKET_SUMMARIES = "https://bittrex.com/api/v1.1/public/getmarketsummaries" 35 | GET_MARKET_SUMMARY = "https://bittrex.com/api/v1.1/public/getmarketsummary" 36 | GET_ORDERBOOK = "https://bittrex.com/api/v1.1/public/getorderbook" 37 | GET_MARKET_HISTORY = "https://bittrex.com/api/v1.1/public/getmarkethistory" 38 | BUY_LIMIT = "https://bittrex.com/api/v1.1/market/buylimit" 39 | BUY_MARKET = "https://bittrex.com/api/v1.1/market/buymarket" 40 | SELL_LIMIT = "https://bittrex.com/api/v1.1/market/selllimit" 41 | SELL_MARKET = "https://bittrex.com/api/v1.1/market/sellmarket" 42 | CANCEL = "https://bittrex.com/api/v1.1/market/cancel" 43 | GET_OPEN_ORDERS = "https://bittrex.com/api/v1.1/market/getopenorders" 44 | GET_BALANCE = "https://bittrex.com/api/v1.1/account/getbalance" 45 | GET_BALANCES = "https://bittrex.com/api/v1.1/account/getbalances" 46 | GET_ORDER = "https://bittrex.com/api/v1.1/account/getorder" 47 | GET_ORDER_SUMMARY = "https://bittrex.com/api/v1.1/account/getorderhistory" 48 | WITHDRAW = "https://bittrex.com/api/v1.1/account/withdraw" 49 | 50 | 51 | def get(url, headers=None): 52 | """ 53 | Perform a HTTP get request returning the request body. 54 | """ 55 | headers = headers if headers else {} 56 | request = urllib2.Request(url, headers=headers) 57 | handle = urllib2.urlopen(request) 58 | return handle.read() 59 | 60 | 61 | def format_uri(uri, parameters): 62 | """ 63 | Format a uri with the given query parameters `dict`. 64 | """ 65 | parts = urlparse.urlsplit(uri) 66 | query_string = urllib.urlencode(parameters) 67 | return urlparse.urlunsplit(( 68 | parts.scheme, 69 | parts.netloc, 70 | parts.path, 71 | query_string, 72 | parts.fragment)) 73 | 74 | 75 | class BittrexAPIException(Exception): 76 | """ 77 | Exception when bittrex api returns a False success status. 78 | """ 79 | 80 | 81 | class NoAPIKeys(Exception): 82 | """ 83 | Exception raised when a private method called without credentials. 84 | """ 85 | 86 | 87 | class BittrexAPI(object): 88 | """ 89 | bittrex class which wraps the bittrex API. 90 | """ 91 | 92 | def __init__(self, api_key=None, api_secret=None, raw=False): 93 | self._api_key = api_key 94 | self._api_secret = api_secret 95 | self._raw = raw 96 | 97 | def _query(self, uri, params=None, public=True): 98 | if public is False and not all((self._api_key, self._api_secret)): 99 | raise NoAPIKeys 100 | params = params if params else {} 101 | headers = {} 102 | if public is False: 103 | params.update(self._auth_params) 104 | uri = format_uri(uri, params) 105 | if public is False: 106 | headers = self.api_headers(uri) 107 | if self._raw is True: 108 | return get(uri, headers) 109 | response = json.loads(get(uri, headers)) 110 | if not response['success']: 111 | raise BittrexAPIException(response) 112 | return response 113 | 114 | @property 115 | def _auth_params(self): 116 | return dict( 117 | apikey=self._api_key, 118 | nonce=int(time.time())) 119 | 120 | def api_sign(self, uri): 121 | sign = hmac.new(self._api_secret, uri, hashlib.sha512) 122 | return sign.hexdigest() 123 | 124 | def api_headers(self, uri): 125 | return {'apisign': self.api_sign(uri)} 126 | 127 | def getmarkets(self): 128 | return self._query(GET_MARKETS_URI, dict(), public=True) 129 | 130 | def getcurrencies(self): 131 | return self._query(GET_CURRENCIES_URI, dict(), public=True) 132 | 133 | def getticker(self, market): 134 | return self._query(GET_TICKER, dict(market=market), public=True) 135 | 136 | def getmarketsummaries(self): 137 | return self._query(GET_MARKET_SUMMARIES, dict(), public=True) 138 | 139 | def getmarketsummary(self, market): 140 | params = dict(market=market) 141 | return self._query(GET_MARKET_SUMMARY, params, public=True) 142 | 143 | def getorderbook(self, market, type_='both', depth='20'): 144 | params = dict(market=market, type=type_, depth=depth) 145 | return self._query(GET_ORDERBOOK, params, public=True) 146 | 147 | def getmarkethistory(self, market, count='20'): 148 | params = dict(market=market, count=count) 149 | return self._query(GET_MARKET_HISTORY, params, public=True) 150 | 151 | def buylimit(self, market, quantity, rate): 152 | params = dict(market=market, quantity=quantity, rate=rate) 153 | return self._query(BUY_LIMIT, params, public=False) 154 | 155 | def buymarket(self, market, quantity): 156 | params = dict(market=market, quantity=quantity) 157 | return self._query(BUY_MARKET, params, public=False) 158 | 159 | def selllimit(self, market, quantity, rate): 160 | params = dict(market=market, quantity=quantity, rate=rate) 161 | return self._query(SELL_LIMIT, params, public=False) 162 | 163 | def sellmarket(self, market, quantity): 164 | params = dict(market=market, quantity=quantity) 165 | return self._query(SELL_MARKET, params, public=False) 166 | 167 | def cancel(self, uuid): 168 | return self._query(CANCEL, dict(uuid=uuid), public=False) 169 | 170 | def getopenorders(self, market=None): 171 | params = dict(market=market) if market else dict() 172 | return self._query(GET_OPEN_ORDERS, params, public=False) 173 | 174 | def getbalance(self, currency): 175 | params = dict(currency=currency) 176 | return self._query(GET_BALANCE, params, public=False) 177 | 178 | def getorder(self, uuid): 179 | params = dict(uuid=uuid) 180 | return self._query(GET_ORDER, params, public=False) 181 | 182 | def getbalances(self): 183 | return self._query(GET_BALANCES, public=False) 184 | 185 | def getorderhistory(self, market=None, count=None): 186 | params = dict() 187 | if market is not None: 188 | params['market'] = market 189 | if count is not None: 190 | params['count'] = count 191 | return self._query(GET_ORDER_SUMMARY, params, public=False) 192 | 193 | def withdraw(self, currency, quantity, address, paymentid=None): 194 | params = dict(currency=currency, quantity=quantity, address=address) 195 | if paymentid is not None: 196 | params['paymentid'] = paymentid 197 | return self._query(WITHDRAW, params, public=False) 198 | 199 | 200 | def runner(*args): 201 | """ 202 | Simple runner for the bittrex api methods. 203 | """ 204 | bittrex = BittrexAPI(API_KEY, API_SECRET, raw=True) 205 | if len(sys.argv) > 1: 206 | return getattr(bittrex, sys.argv[1])(*sys.argv[2:]) 207 | return getattr(bittrex, sys.argv[1])() 208 | 209 | 210 | def usage(): 211 | return """Usage: 212 | python bittrex.py getticker [market] 213 | getmarkets 214 | getcurrencies 215 | getticker [market] 216 | getmarketsummaries 217 | getmarketsummary [market] 218 | getorderbook [market] [type] [depth] 219 | getmarkethistory [market] [count] 220 | buylimit [market] [quantity] [price] 221 | buymarket [market] [quantity] 222 | selllimit [market] [quantity] [price] 223 | sellmarket [market] [quantity] 224 | cancel [uuid] 225 | getopenorders [market] 226 | getbalance [currency] 227 | getbalances 228 | getorder [uuid] 229 | withdraw [currency] [quantity] [address] [paymentid] 230 | """ 231 | 232 | 233 | if __name__ == "__main__": 234 | import sys 235 | if len(sys.argv) == 1: 236 | sys.stdout.write(textwrap.dedent(usage())) 237 | sys.exit(1) 238 | try: 239 | response = runner(sys.argv[1:]) 240 | except Exception as exc: 241 | sys.stdout.write(str(exc) + '\n') 242 | sys.exit(1) 243 | sys.stdout.write(str(response) + '\n') 244 | sys.exit(0) 245 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup 4 | 5 | setup(name='bittrex', 6 | version='0.0.1', 7 | description='Python bittrex api wrapper', 8 | author='Justin Fay', 9 | author_email='mail@justinfay.me', 10 | url='http://justinfay.me', 11 | py_modules=['bittrex'], 12 | scripts=['bittrex'], 13 | ) 14 | --------------------------------------------------------------------------------