├── .gitignore ├── .travis.yml ├── LICENSE ├── README.rst ├── bigone ├── __init__.py ├── client.py └── exceptions.py ├── docs ├── Makefile ├── bigone.rst ├── changelog.rst ├── conf.py ├── index.rst └── overview.rst ├── requirements.txt ├── setup.cfg ├── setup.py ├── test-requirements.txt ├── tests ├── __init__.py └── test_api_request.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | lastfailed 2 | python_bigone.egg-info 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "2.7" 5 | - "3.4" 6 | - "3.5" 7 | - "3.6" 8 | 9 | install: 10 | - pip install -r test-requirements.txt 11 | - pip install -r requirements.txt 12 | - pip install tox-travis 13 | 14 | script: 15 | - tox 16 | 17 | after_success: 18 | - coveralls 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 sammchardy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | =============================== 2 | Welcome to python-bigone v0.1.0 3 | =============================== 4 | 5 | .. image:: https://img.shields.io/pypi/v/python-bigone.svg 6 | :target: https://pypi.python.org/pypi/python-bigone 7 | 8 | .. image:: https://img.shields.io/pypi/l/python-bigone.svg 9 | :target: https://pypi.python.org/pypi/python-bigone 10 | 11 | .. image:: https://img.shields.io/travis/sammchardy/python-bigone.svg 12 | :target: https://travis-ci.org/sammchardy/python-bigone 13 | 14 | .. image:: https://img.shields.io/coveralls/sammchardy/python-bigone.svg 15 | :target: https://coveralls.io/github/sammchardy/python-bigone 16 | 17 | .. image:: https://img.shields.io/pypi/wheel/python-bigone.svg 18 | :target: https://pypi.python.org/pypi/python-bigone 19 | 20 | .. image:: https://img.shields.io/pypi/pyversions/python-bigone.svg 21 | :target: https://pypi.python.org/pypi/python-bigone 22 | 23 | This is an unofficial Python wrapper for the `BigONE exchanges REST API v2 `_. I am in no way affiliated with BigONE, use at your own risk. 24 | 25 | PyPi 26 | https://pypi.python.org/pypi/python-bigone 27 | 28 | Source code 29 | https://github.com/sammchardy/python-bigone 30 | 31 | Documentation 32 | https://python-bigone.readthedocs.io/en/latest/ 33 | 34 | 35 | Features 36 | -------- 37 | 38 | - Implementation of all REST endpoints 39 | - Simple handling of authentication 40 | - Response exception handling 41 | 42 | Quick Start 43 | ----------- 44 | 45 | Register an account with `BigONE `_. 46 | 47 | `Generate an API Key `_ and store it. 48 | 49 | .. code:: bash 50 | 51 | pip install python-bigone 52 | 53 | 54 | .. code:: python 55 | 56 | from bigone.client import Client 57 | client = Client(api_key, api_secret) 58 | 59 | # get markets 60 | markets = client.get_markets() 61 | 62 | # get market order book 63 | depth = client.get_order_book('ETH-BTC') 64 | 65 | # get market trades 66 | trades = client.get_market_trades('ETH-BTC') 67 | 68 | # get your accounts 69 | currencies = client.get_accounts() 70 | 71 | # place a bid order 72 | transaction = client.create_order('KCS-BTC', Client.SIDE_BID, '0.01', '1000') 73 | 74 | # place an ask order 75 | transaction = client.create_order('KCS-BTC', Client.SIDE_ASK, '0.01', '1000') 76 | 77 | # get a list of your orders for a symbol 78 | orders = client.get_orders('ETH-BTC') 79 | 80 | # get a list of your trades for a symbol 81 | orders = client.get_trades('ETH-BTC') 82 | 83 | # get list of all withdrawals 84 | withdrawals = client.get_withdrawals() 85 | 86 | # get list of all deposits 87 | deposits = client.get_deposits() 88 | 89 | 90 | For more `check out the documentation `_. 91 | 92 | Donate 93 | ------ 94 | 95 | If this library helped you out feel free to donate. 96 | 97 | - ETH: 0xD7a7fDdCfA687073d7cC93E9E51829a727f9fE70 98 | - NEO: AVJB4ZgN7VgSUtArCt94y7ZYT6d5NDfpBo 99 | - LTC: LPC5vw9ajR1YndE1hYVeo3kJ9LdHjcRCUZ 100 | - BTC: 1Dknp6L6oRZrHDECRedihPzx2sSfmvEBys 101 | 102 | Other Exchanges 103 | --------------- 104 | 105 | If you use `Binance `_ check out my `python-binance `_ library. 106 | 107 | If you use `Kucoin `_ check out my `python-kucoin `_ library. 108 | 109 | If you use `Allcoin `_ check out my `python-allucoin `_ library. 110 | 111 | If you use `Quoinex `_ 112 | or `Qryptos `_ check out my `python-quoine `_ library. 113 | 114 | If you use `IDEX `_ check out my `python-idex `_ library. 115 | 116 | .. image:: https://analytics-pixel.appspot.com/UA-111417213-1/github/python-bigone?pixel 117 | -------------------------------------------------------------------------------- /bigone/__init__.py: -------------------------------------------------------------------------------- 1 | """An unofficial Python wrapper for the Big.One exchange 2 | 3 | .. moduleauthor:: Sam McHardy 4 | 5 | """ 6 | 7 | __version__ = '0.1.0' 8 | -------------------------------------------------------------------------------- /bigone/client.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | import jwt 4 | import requests 5 | import time 6 | 7 | from .exceptions import BigoneAPIException, BigoneRequestException 8 | 9 | 10 | class Client(object): 11 | 12 | API_URL = 'https://big.one/api/v2' 13 | 14 | SIDE_BID = 'BID' 15 | SIDE_ASK = 'ASK' 16 | 17 | def __init__(self, api_key, api_secret): 18 | """Big.One API Client constructor 19 | 20 | https://open.big.one/ 21 | 22 | :param api_key: Api Key 23 | :type api_key: str 24 | :param api_key: Api Secret 25 | :type api_key: str 26 | 27 | .. code:: python 28 | 29 | client = Client(api_key, api_secret) 30 | 31 | """ 32 | 33 | self.API_KEY = api_key 34 | self.API_SECRET = api_secret 35 | self.session = self._init_session() 36 | 37 | def _init_session(self): 38 | 39 | session = requests.session() 40 | headers = {'Accept': 'application/json', 41 | 'User-Agent': 'python-bigone'} 42 | session.headers.update(headers) 43 | return session 44 | 45 | def _create_uri(self, path): 46 | return '{}/{}'.format(self.API_URL, path) 47 | 48 | def _create_signature(self, ): 49 | 50 | headers = {'typ': 'JWT', 'alg': 'HS256'} 51 | payload = { 52 | 'type': 'OpenAPI', 53 | 'sub': self.API_KEY, 54 | 'nonce': int(time.time() * 1000000000) # convert to nanoseconds 55 | } 56 | sig = jwt.encode(payload, self.API_SECRET, algorithm='HS256', headers=headers) 57 | return sig.decode("utf-8") 58 | 59 | def _request(self, method, path, signed, **kwargs): 60 | 61 | data = kwargs.get('data', None) 62 | 63 | if signed: 64 | kwargs['headers'] = { 65 | 'Authorization': 'Bearer {}'.format(self._create_signature()) 66 | } 67 | 68 | uri = self._create_uri(path) 69 | 70 | if method == 'get' and data: 71 | kwargs['params'] = kwargs['data'] 72 | del(kwargs['data']) 73 | 74 | if method == 'post' and data: 75 | kwargs['json'] = kwargs['data'] 76 | del(kwargs['data']) 77 | 78 | response = getattr(self.session, method)(uri, **kwargs) 79 | return self._handle_response(response) 80 | 81 | def _handle_response(self, response): 82 | """Internal helper for handling API responses from the Quoine server. 83 | Raises the appropriate exceptions when necessary; otherwise, returns the 84 | response. 85 | """ 86 | 87 | if not str(response.status_code).startswith('2'): 88 | raise BigoneAPIException(response) 89 | try: 90 | json = response.json() 91 | 92 | if 'msg' in json or 'errors' in json: 93 | raise BigoneAPIException(response) 94 | 95 | # by default return full response 96 | res = json 97 | # if it's a normal response we have a data attribute, return that 98 | if 'data' in json: 99 | res = json['data'] 100 | return res 101 | except ValueError: 102 | raise BigoneRequestException('Invalid Response: {}'.format(response.text)) 103 | 104 | def _get(self, path, signed=False, **kwargs): 105 | return self._request('get', path, signed, **kwargs) 106 | 107 | def _post(self, path, signed=False, **kwargs): 108 | return self._request('post', path, signed, **kwargs) 109 | 110 | def _put(self, path, signed=False, **kwargs): 111 | return self._request('put', path, signed, **kwargs) 112 | 113 | def _delete(self, path, signed=False, **kwargs): 114 | return self._request('delete', path, signed, **kwargs) 115 | 116 | # Account endpoints 117 | 118 | def get_accounts(self): 119 | """List accounts of current user 120 | 121 | .. code:: python 122 | 123 | accounts = client.get_accounts() 124 | 125 | :return: list of dicts 126 | 127 | .. code:: python 128 | 129 | [ 130 | { 131 | "type": "account", 132 | "user_id": "5c1b7700-c903-41f8-9b4c-d78be9b2685d", 133 | "account_id": "7eb4201b-9ae8-450d-819a-1c7dfd272e3d", 134 | "account_type": "BTC", 135 | "account_name": "Bitcoin", 136 | "account_logo_url": "https://storage.googleapis.com/big-one/coins/BTC.svg", 137 | "public_key": "16zy7YGERPEygQitMFaqP4afPJBZeo2WK6", 138 | "system_account": "", 139 | "active_balance": "0.00000000", 140 | "frozen_balance": "0.00000000", 141 | "estimated_btc_price": "1.00000000", 142 | "verification": "EMAIL_VERIFIED", 143 | "has_asset_pin": false 144 | }, 145 | { 146 | "type": "account", 147 | "user_id": "5c1b7700-c903-41f8-9b4c-d78be9b2685d", 148 | "account_id": "3d7c2353-add3-4e03-8637-c78e4d2b17cb", 149 | "account_type": "ETH", 150 | "account_name": "Ether", 151 | "account_logo_url": "https://storage.googleapis.com/big-one/coins/ETH.svg", 152 | "public_key": "0xaefde180aae6e0916632dabfdd2f8733c8a03826", 153 | "system_account": "", 154 | "active_balance": "0.00000000", 155 | "frozen_balance": "0.00000000", 156 | "estimated_btc_price": "0.06500000", 157 | "verification": "EMAIL_VERIFIED", 158 | "has_asset_pin": false 159 | } 160 | ] 161 | 162 | :raises: BigoneRequestException, BigoneAPIException 163 | 164 | """ 165 | 166 | return self._get('viewer/accounts', True) 167 | 168 | def get_account(self, currency): 169 | """Get account for a currency 170 | 171 | :param currency: Name of currency 172 | :type currency: str 173 | 174 | .. code:: python 175 | 176 | account = client.get_account('BTC') 177 | 178 | :return: dict 179 | 180 | .. code:: python 181 | 182 | { 183 | "type": "account", 184 | "user_id": "5c1b7700-c903-41f8-9b4c-d78be9b2685d", 185 | "account_id": "7eb4201b-9ae8-450d-819a-1c7dfd272e3d", 186 | "account_type": "BTC", 187 | "account_name": "Bitcoin", 188 | "account_logo_url": "https://storage.googleapis.com/big-one/coins/BTC.svg", 189 | "public_key": "16zy7YGERPEygQitMFaqP4afPJBZeo2WK6", 190 | "system_account": "", 191 | "active_balance": "0.00000000", 192 | "frozen_balance": "0.00000000", 193 | "estimated_btc_price": "1.00000000", 194 | "verification": "EMAIL_VERIFIED", 195 | "has_asset_pin": false, 196 | "withdrawal": { 197 | "fee": "0.002", 198 | "fee_type": "BTC" 199 | }, 200 | "deposits": [], 201 | "withdrawals": [], 202 | "recipients": [] 203 | } 204 | 205 | :raises: BigoneRequestException, BigoneAPIException 206 | 207 | """ 208 | 209 | return self._get('accounts/{}'.format(currency), True) 210 | 211 | # Market endpoints 212 | 213 | def get_markets(self): 214 | """List markets 215 | 216 | https://open.big.one/docs/api_market.html#all-markets 217 | 218 | .. code:: python 219 | 220 | markets = client.get_markets() 221 | 222 | :return: list of dicts 223 | 224 | .. code:: python 225 | 226 | [ 227 | { 228 | "uuid": "d2185614-50c3-4588-b146-b8afe7534da6", 229 | "quoteScale": 8, 230 | "quoteAsset": { 231 | "uuid": "0df9c3c3-255a-46d7-ab82-dedae169fba9", 232 | "symbol": "BTC", 233 | "name": "Bitcoin" 234 | }, 235 | "name": "BTG/BTC", 236 | "baseScale": 4, 237 | "baseAsset": { 238 | "uuid": "5df3b155-80f5-4f5a-87f6-a92950f0d0ff", 239 | "symbol": "BTG", 240 | "name": "Bitcoin Gold" 241 | } 242 | } 243 | ] 244 | 245 | :raises: BigoneRequestException, BigoneAPIException 246 | 247 | """ 248 | 249 | return self._get('markets') 250 | 251 | def get_tickers(self): 252 | """List market tickers 253 | 254 | https://open.big.one/docs/api_tickers.html#tickers-of-all-market 255 | 256 | .. code:: python 257 | 258 | markets = client.get_tickers() 259 | 260 | :return: list of dicts 261 | 262 | .. code:: python 263 | 264 | [ 265 | { 266 | "volume": null, 267 | "open": "1.0000000000000000", 268 | "market_uuid": "ETH-EOS", 269 | "low": null, 270 | "high": null, 271 | "daily_change_perc": "0", 272 | "daily_change": "0E-16", 273 | "close": "1.0000000000000000", 274 | "bid": { 275 | "price": "1.0000000000000000", 276 | "amount": "106.0000000000000000" 277 | }, 278 | "ask": { 279 | "price": "45.0000000000000000", 280 | "amount": "4082.3283464000000000" 281 | } 282 | } 283 | ] 284 | 285 | :raises: BigoneRequestException, BigoneAPIException 286 | 287 | """ 288 | 289 | return self._get('tickers') 290 | 291 | def get_ticker(self, symbol): 292 | """Get symbol market details 293 | 294 | https://open.big.one/docs/api_tickers.html#ticker-of-one-market 295 | 296 | :param symbol: Name of symbol 297 | :type symbol: str 298 | 299 | .. code:: python 300 | 301 | # using market ID 302 | market = client.get_ticker('ETH-BTC') 303 | 304 | # using market UUID 305 | market = client.get_ticker('d2185614-50c3-4588-b146-b8afe7534da6') 306 | 307 | :return: dict 308 | 309 | .. code:: python 310 | 311 | { 312 | "volume": null, 313 | "open": "42.0000000000000000", 314 | "market_uuid": "BTC-EOS", 315 | "low": "42.0000000000000000", 316 | "high": null, 317 | "daily_change_perc": "0", 318 | "daily_change": "0E-16", 319 | "close": "42.0000000000000000", 320 | "bid": { 321 | "price": "42.0000000000000000", 322 | "amount": "3.3336371100000000" 323 | }, 324 | "ask": { 325 | "price": "45.0000000000000000", 326 | "amount": "4082.3283464000000000" 327 | } 328 | } 329 | 330 | :raises: BigoneRequestException, BigoneAPIException 331 | 332 | """ 333 | 334 | return self._get('markets/{}/ticker'.format(symbol)) 335 | 336 | def get_order_book(self, symbol): 337 | """Get symbol market details 338 | 339 | :param symbol: Name of symbol 340 | :type symbol: str 341 | 342 | .. code:: python 343 | 344 | # Using market ID 345 | book = client.get_order_book('ETH-BTC') 346 | 347 | # Using market UUID 348 | book = client.get_order_book('d2185614-50c3-4588-b146-b8afe7534da6') 349 | 350 | :return: dict 351 | 352 | .. code:: python 353 | 354 | { 355 | "market_uuid": "BTC-EOS", 356 | "bids": [ 357 | { 358 | "price": "42", 359 | "order_count": 4, 360 | "amount": "23.33363711" 361 | } 362 | ], 363 | "asks": [ 364 | { 365 | "price": "45", 366 | "order_count": 2, 367 | "amount": "4193.3283464" 368 | } 369 | ] 370 | } 371 | 372 | :raises: BigoneRequestException, BigoneAPIException 373 | 374 | """ 375 | 376 | return self._get('markets/{}/depth'.format(symbol)) 377 | 378 | def get_market_trades(self, symbol, after=None, before=None, first=None, last=None): 379 | """Get market trades - max 50 380 | 381 | https://open.big.one/docs/api_market_trade.html#trades-of-a-market 382 | 383 | :param symbol: Name of symbol 384 | :type symbol: str 385 | :param after: Return trades after this id 386 | :type after: int 387 | :param before: Return trades before this id 388 | :type before: int 389 | :param first: Slicing count 390 | :type first: int 391 | :param last: Slicing count 392 | :type last: int 393 | 394 | .. code:: python 395 | 396 | trades = client.get_market_trades('ETH-BTC') 397 | 398 | # using after trade ID 399 | trades = client.get_market_trades('ETH-BTC', after=1) 400 | 401 | # using first slice value 402 | trades = client.get_market_trades('ETH-BTC', first=20) 403 | 404 | :return: list of dicts 405 | 406 | .. code:: python 407 | 408 | { 409 | "edges": [ 410 | { 411 | "node": { 412 | "taker_side": "BID", 413 | "price": "46.1450000000000000", 414 | "market_uuid": "BTC-EOS", 415 | "id": 1, 416 | "amount": "0.2465480000000000" 417 | }, 418 | "cursor": "dGVzdGN1cmVzZQo=" 419 | } 420 | ], 421 | "page_info": { 422 | "end_cursor": "dGVzdGN1cmVzZQo=", 423 | "start_cursor": "dGVzdGN1cmVzZQo=", 424 | "has_next_page": true, 425 | "has_previous_page": false 426 | } 427 | } 428 | 429 | :raises: BigoneRequestException, BigoneAPIException 430 | 431 | """ 432 | data = {} 433 | if after: 434 | data['after'] = after 435 | if before: 436 | data['before'] = before 437 | if first: 438 | data['first'] = first 439 | if last: 440 | data['last'] = last 441 | 442 | return self._get('markets/{}/trades'.format(symbol), data=data) 443 | 444 | # Order Endpoints 445 | 446 | def create_order(self, symbol, side, price, amount): 447 | """Create a new order 448 | 449 | :param symbol: Name of symbol 450 | :type symbol: str 451 | :param side: side of order (BID or ASK) 452 | :type side: str 453 | :param price: Price as string 454 | :type price: str 455 | :param amount: Amount as string 456 | :type amount: str 457 | 458 | .. code:: python 459 | 460 | order = client.create_order('ETH-BTC', 'BID', '1.0', '1.0') 461 | 462 | :return: dict 463 | 464 | .. code:: python 465 | 466 | { 467 | "id": 10, 468 | "market_uuid": "BTC-EOS", 469 | "price": "10.00", 470 | "amount": "10.00", 471 | "filled_amount": "9.0", 472 | "avg_deal_price": "12.0", 473 | "side": "ASK", 474 | "state": "FILLED" 475 | } 476 | 477 | :raises: BigoneRequestException, BigoneAPIException 478 | 479 | """ 480 | 481 | data = { 482 | 'market_id': symbol, 483 | 'side': side, 484 | 'price': price, 485 | 'amount': amount 486 | } 487 | 488 | return self._post('viewer/orders', True, data=data) 489 | 490 | def get_orders(self, symbol, after=None, before=None, first=None, last=None, side=None, state=None): 491 | """Get a list of orders 492 | 493 | :param symbol: Name of symbol 494 | :type symbol: str 495 | :param after: Return trades after this id 496 | :type after: int 497 | :param before: Return trades before this id 498 | :type before: int 499 | :param first: Slicing count 500 | :type first: int 501 | :param last: Slicing count 502 | :type last: int 503 | :param side: Order Side ASK|BID 504 | :type side: str 505 | :param state: Order State CANCELED|FILLED|PENDING 506 | :type state: str 507 | 508 | .. code:: python 509 | 510 | orders = client.get_orders('ETH-BTC') 511 | 512 | :return: dict 513 | 514 | .. code:: python 515 | 516 | { 517 | "edges": [ 518 | { 519 | "node": { 520 | "id": 10, 521 | "market_uuid": "d2185614-50c3-4588-b146-b8afe7534da6", 522 | "price": "10.00", 523 | "amount": "10.00", 524 | "filled_amount": "9.0", 525 | "avg_deal_price": "12.0", 526 | "side": "ASK", 527 | "state": "FILLED" 528 | }, 529 | "cursor": "dGVzdGN1cmVzZQo=" 530 | } 531 | ], 532 | "page_info": { 533 | "end_cursor": "dGVzdGN1cmVzZQo=", 534 | "start_cursor": "dGVzdGN1cmVzZQo=", 535 | "has_next_page": true, 536 | "has_previous_page": false 537 | } 538 | } 539 | 540 | :raises: BigoneRequestException, BigoneAPIException 541 | 542 | """ 543 | 544 | data = { 545 | 'market_id': symbol 546 | } 547 | if after: 548 | data['after'] = after 549 | if before: 550 | data['before'] = before 551 | if first: 552 | data['first'] = first 553 | if last: 554 | data['last'] = last 555 | if side: 556 | data['side'] = side 557 | if state: 558 | data['state'] = state 559 | 560 | return self._get('viewer/orders', True, data=data) 561 | 562 | def get_order(self, order_id): 563 | """Get an order 564 | 565 | https://open.big.one/docs/api_orders.html#get-one-order 566 | 567 | :param order_id: Id of order 568 | :type order_id: str 569 | 570 | .. code:: python 571 | 572 | orders = client.get_order('10') 573 | 574 | :return: dict 575 | 576 | .. code:: python 577 | 578 | { 579 | "id": 10, 580 | "market_uuid": "d2185614-50c3-4588-b146-b8afe7534da6", 581 | "price": "10.00", 582 | "amount": "10.00", 583 | "filled_amount": "9.0", 584 | "avg_deal_price": "12.0", 585 | "side": "ASK", 586 | "state": "FILLED" 587 | } 588 | 589 | :raises: BigoneRequestException, BigoneAPIException 590 | 591 | """ 592 | 593 | return self._get('viewer/orders/{}'.format(order_id), True) 594 | 595 | def cancel_order(self, order_id): 596 | """Cancel an order 597 | 598 | https://open.big.one/docs/api_orders.html#cancle-order 599 | 600 | :param order_id: Id of order 601 | :type order_id: str 602 | 603 | .. code:: python 604 | 605 | res = client.cancel_order('10') 606 | 607 | :return: dict 608 | 609 | .. code:: python 610 | 611 | {} 612 | 613 | :raises: BigoneRequestException, BigoneAPIException 614 | 615 | """ 616 | 617 | return self._post('viewer/orders/{}/cancel'.format(order_id), True) 618 | 619 | def cancel_orders(self): 620 | """Cancel all orders 621 | 622 | https://open.big.one/docs/api_orders.html#cancle-all-orders 623 | 624 | 625 | .. code:: python 626 | 627 | res = client.cancel_orders() 628 | 629 | :return: dict 630 | 631 | .. code:: python 632 | 633 | {} 634 | 635 | :raises: BigoneRequestException, BigoneAPIException 636 | 637 | """ 638 | 639 | return self._post('viewer/orders/cancel_all', True) 640 | 641 | # Trade endpoints 642 | 643 | def get_trades(self, symbol=None, after=None, before=None, first=None, last=None): 644 | """Get a list of your trades 645 | 646 | :param symbol: Name of symbol 647 | :type symbol: str 648 | :type after: int 649 | :param before: Return trades before this id 650 | :type before: int 651 | :param first: Slicing count 652 | :type first: int 653 | :param last: Slicing count 654 | :type last: int 655 | 656 | .. code:: python 657 | 658 | trades = client.get_trades('ETH-BTC') 659 | 660 | :return: dict 661 | 662 | .. code:: python 663 | 664 | { 665 | "edges": [ 666 | { 667 | "node": { 668 | "viewer_side": "ASK" // ASK, BID, SELF_TRADING 669 | "taker_side": "BID", 670 | "price": "46.1450000000000000", 671 | "market_uuid": "BTC-EOS", 672 | "id": 1, 673 | "amount": "0.2465480000000000" 674 | }, 675 | "cursor": "dGVzdGN1cmVzZQo=" 676 | } 677 | ], 678 | "page_info": { 679 | "end_cursor": "dGVzdGN1cmVzZQo=", 680 | "start_cursor": "dGVzdGN1cmVzZQo=", 681 | "has_next_page": true, 682 | "has_previous_page": false 683 | } 684 | } 685 | 686 | :raises: BigoneRequestException, BigoneAPIException 687 | 688 | """ 689 | 690 | data = {} 691 | if symbol: 692 | data['market_id'] = symbol 693 | if after: 694 | data['after'] = after 695 | if before: 696 | data['before'] = before 697 | if first: 698 | data['first'] = first 699 | if last: 700 | data['last'] = last 701 | 702 | return self._get('viewer/trades', True, data=data) 703 | 704 | # Withdraw endpoints 705 | 706 | def withdrawals(self, first=None, after=None): 707 | """Get a list of withdrawals 708 | 709 | https://open.big.one/docs/api_withdrawal.html#get-withdrawals-of-user 710 | 711 | :param first: Slicing count 712 | :type first: str 713 | :param after: Return withdrawals after this value 714 | :type after: str 715 | 716 | .. code:: python 717 | 718 | withdrawal = client.withdrawals() 719 | 720 | :return: dict 721 | 722 | .. code:: python 723 | 724 | { 725 | "edges": [ 726 | { 727 | "node": { 728 | "id": 10, 729 | "customer_id": "ETH", 730 | "asset_uuid": "ETH", 731 | "amount": "5", 732 | "state": "CONFIRMED", 733 | "note": "2018-03-15T16:13:45.610463Z", 734 | "txid": "0x4643bb6b393ac20a6175c713175734a72517c63d6f73a3ca90a15356f2e967da0", 735 | "completed_at": "2018-03-15T16:13:45.610463Z", 736 | "inserted_at": "2018-03-15T16:13:45.610463Z", 737 | "is_internal": true, 738 | "target_address": "0x4643bb6b393ac20a6175c713175734a72517c63d6f7" 739 | }, 740 | "cursor": "dGVzdGN1cmVzZQo=" 741 | } 742 | ], 743 | "page_info": { 744 | "end_cursor": "dGVzdGN1cmVzZQo=", 745 | "start_cursor": "dGVzdGN1cmVzZQo=", 746 | "has_next_page": true, 747 | "has_previous_page": false 748 | } 749 | } 750 | 751 | :raises: BigoneRequestException, BigoneAPIException 752 | 753 | """ 754 | 755 | data = {} 756 | if after: 757 | data['after'] = after 758 | if first: 759 | data['first'] = first 760 | 761 | return self._get('viewer/withdrawals', True, data=data) 762 | 763 | # Deposit endpoints 764 | 765 | def get_deposits(self, first=None, after=None): 766 | """Get a list of deposits 767 | 768 | https://open.big.one/docs/api_deposit.html#deposit-of-user 769 | 770 | :param first: Slicing count 771 | :type first: str 772 | :param after: Return withdrawals after this value 773 | :type after: str 774 | 775 | .. code:: python 776 | 777 | # get all deposits 778 | deposits = client.get_deposits() 779 | 780 | # get BTC deposits 781 | deposits = client.get_deposits('BTC') 782 | 783 | :return: list of dicts 784 | 785 | .. code:: python 786 | 787 | [ 788 | { 789 | "type": "deposit", 790 | "deposit_id": "7b76603c-db87-4c4a-b36f-9981058d885e", 791 | "deposit_type": "ETH", 792 | "amount": "1.00000000", 793 | "confirmations": 2231, 794 | "state": "confirmed", 795 | "scanner_url": "https://etherscan.io/tx/0x181a1d616b5b07d5a404636c527ca5432878664d4b85a20976bbd72c07e58abe", 796 | "created_at": "2017-09-06T07:53:41.297047856Z" 797 | } 798 | ] 799 | 800 | :raises: BigoneRequestException, BigoneAPIException 801 | 802 | """ 803 | 804 | data = {} 805 | if after: 806 | data['after'] = after 807 | if first: 808 | data['first'] = first 809 | 810 | return self._get('viewer/deposits', True, data=data) 811 | -------------------------------------------------------------------------------- /bigone/exceptions.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | 4 | class BigoneAPIException(Exception): 5 | """Exception class to handle general API Exceptions 6 | 7 | `code` values 8 | 9 | `message` format 10 | 11 | """ 12 | def __init__(self, response): 13 | self.code = '' 14 | self.message = 'Unknown Error' 15 | try: 16 | json_res = response.json() 17 | except ValueError: 18 | print("Can't parse error response: {}".format(response.text)) 19 | self.message = response.text 20 | else: 21 | print("doing something with json_res: {}".format(json_res)) 22 | if 'msg' in json_res: 23 | self.message = json_res['msg'] 24 | self.code = json_res['code'] 25 | elif 'errors' in json_res: 26 | if type(json_res['errors']) == list: 27 | self.message = "\n ".join(["{}: {}".format(el['code'], el['message']) for el in json_res['errors']]) 28 | elif 'detail' in json_res['errors']: 29 | self.message = json_res['errors']['detail'] 30 | 31 | self.status_code = response.status_code 32 | self.response = response 33 | self.request = getattr(response, 'request', None) 34 | 35 | def __str__(self): 36 | return 'BigoneAPIException {}: {}'.format(self.code, self.message) 37 | 38 | 39 | class BigoneRequestException(Exception): 40 | def __init__(self, message): 41 | self.message = message 42 | 43 | def __str__(self): 44 | return 'BigoneRequestException: {}'.format(self.message) 45 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = python-bigone 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | 22 | rst: 23 | sphinx-apidoc -f -o ./ ../ 24 | -------------------------------------------------------------------------------- /docs/bigone.rst: -------------------------------------------------------------------------------- 1 | BigONE API 2 | ========== 3 | 4 | client module 5 | ---------------------- 6 | 7 | .. automodule:: bigone.client 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | :member-order: bysource 12 | 13 | exceptions module 14 | -------------------------- 15 | 16 | .. automodule:: bigone.exceptions 17 | :members: 18 | :undoc-members: 19 | :show-inheritance: 20 | :member-order: bysource 21 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | v0.1.0 - 2018-06-27 5 | ^^^^^^^^^^^^^^^^^^^ 6 | 7 | **Changed** 8 | 9 | - updated to v2 of Bigone API 10 | 11 | v0.0.5 - 2018-01-28 12 | ^^^^^^^^^^^^^^^^^^^ 13 | 14 | **Changed** 15 | 16 | - changed `cancel_all_orders` to `cancel_orders` requiring a list of order ids 17 | 18 | v0.0.4 - 2018-01-28 19 | ^^^^^^^^^^^^^^^^^^^ 20 | 21 | **Fixed** 22 | 23 | - broken request function 24 | 25 | v0.0.3 - 2018-01-28 26 | ^^^^^^^^^^^^^^^^^^^ 27 | 28 | **Fixed** 29 | 30 | - private POST call param format 31 | 32 | v0.0.2 - 2018-01-28 33 | ^^^^^^^^^^^^^^^^^^^ 34 | 35 | **Fixed** 36 | 37 | - get_orders endpoint 38 | 39 | v0.0.1 - 2017-01-20 40 | ^^^^^^^^^^^^^^^^^^^ 41 | 42 | Initial version 43 | 44 | **Added** 45 | 46 | - General, Market Data and Account endpoints 47 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # python-kucoin documentation build configuration file, created by 5 | # sphinx-quickstart on Thu Sep 21 20:24:54 2017. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | # 20 | import os 21 | import sys 22 | sys.path.insert(0, os.path.abspath('..')) 23 | 24 | 25 | # -- General configuration ------------------------------------------------ 26 | 27 | # If your documentation needs a minimal Sphinx version, state it here. 28 | # 29 | # needs_sphinx = '1.0' 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = ['sphinx.ext.autodoc', 35 | 'sphinx.ext.imgmath', 36 | 'sphinx.ext.viewcode', 37 | 'sphinx.ext.githubpages'] 38 | 39 | # Add any paths that contain templates here, relative to this directory. 40 | templates_path = ['_templates'] 41 | 42 | # The suffix(es) of source filenames. 43 | # You can specify multiple suffix as a list of string: 44 | # 45 | # source_suffix = ['.rst', '.md'] 46 | source_suffix = '.rst' 47 | 48 | # The master toctree document. 49 | master_doc = 'index' 50 | 51 | # General information about the project. 52 | project = 'python-bigone' 53 | copyright = '2018, Sam McHardy' 54 | author = 'Sam McHardy' 55 | 56 | # The version info for the project you're documenting, acts as replacement for 57 | # |version| and |release|, also used in various other places throughout the 58 | # built documents. 59 | # 60 | # The short X.Y version. 61 | version = '0.2.0' 62 | # The full version, including alpha/beta/rc tags. 63 | release = '0.2.0' 64 | 65 | # The language for content autogenerated by Sphinx. Refer to documentation 66 | # for a list of supported languages. 67 | # 68 | # This is also used if you do content translation via gettext catalogs. 69 | # Usually you set "language" from the command line for these cases. 70 | language = None 71 | 72 | # List of patterns, relative to source directory, that match files and 73 | # directories to ignore when looking for source files. 74 | # This patterns also effect to html_static_path and html_extra_path 75 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 76 | 77 | # The name of the Pygments (syntax highlighting) style to use. 78 | pygments_style = 'sphinx' 79 | 80 | # If true, `todo` and `todoList` produce output, else they produce nothing. 81 | todo_include_todos = False 82 | 83 | 84 | # -- Options for HTML output ---------------------------------------------- 85 | 86 | # The theme to use for HTML and HTML Help pages. See the documentation for 87 | # a list of builtin themes. 88 | # 89 | #html_theme = 'alabaster' 90 | html_theme = 'sphinx_rtd_theme' 91 | 92 | # Theme options are theme-specific and customize the look and feel of a theme 93 | # further. For a list of options available for each theme, see the 94 | # documentation. 95 | # 96 | # html_theme_options = {} 97 | 98 | # Add any paths that contain custom static files (such as style sheets) here, 99 | # relative to this directory. They are copied after the builtin static files, 100 | # so a file named "default.css" will overwrite the builtin "default.css". 101 | html_static_path = ['_static'] 102 | 103 | # Custom sidebar templates, must be a dictionary that maps document names 104 | # to template names. 105 | # 106 | # This is required for the alabaster theme 107 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 108 | html_sidebars = { 109 | '**': [ 110 | 'about.html', 111 | 'navigation.html', 112 | 'relations.html', # needs 'show_related': True theme option to display 113 | 'searchbox.html', 114 | 'donate.html', 115 | ] 116 | } 117 | 118 | 119 | # -- Options for HTMLHelp output ------------------------------------------ 120 | 121 | # Output file base name for HTML help builder. 122 | htmlhelp_basename = 'python-bigonedoc' 123 | 124 | 125 | # -- Options for LaTeX output --------------------------------------------- 126 | 127 | latex_elements = { 128 | # The paper size ('letterpaper' or 'a4paper'). 129 | # 130 | # 'papersize': 'letterpaper', 131 | 132 | # The font size ('10pt', '11pt' or '12pt'). 133 | # 134 | # 'pointsize': '10pt', 135 | 136 | # Additional stuff for the LaTeX preamble. 137 | # 138 | # 'preamble': '', 139 | 140 | # Latex figure (float) alignment 141 | # 142 | # 'figure_align': 'htbp', 143 | } 144 | 145 | # Grouping the document tree into LaTeX files. List of tuples 146 | # (source start file, target name, title, 147 | # author, documentclass [howto, manual, or own class]). 148 | latex_documents = [ 149 | (master_doc, 'python-bigone.tex', 'python-bigone Documentation', 150 | 'Sam McHardy', 'manual'), 151 | ] 152 | 153 | 154 | # -- Options for manual page output --------------------------------------- 155 | 156 | # One entry per manual page. List of tuples 157 | # (source start file, name, description, authors, manual section). 158 | man_pages = [ 159 | (master_doc, 'python-bigone', 'python-bigone Documentation', 160 | [author], 1) 161 | ] 162 | 163 | 164 | # -- Options for Texinfo output ------------------------------------------- 165 | 166 | # Grouping the document tree into Texinfo files. List of tuples 167 | # (source start file, target name, title, author, 168 | # dir menu entry, description, category) 169 | texinfo_documents = [ 170 | (master_doc, 'python-bigone', 'python-bigone Documentation', 171 | author, 'python-bigone', 'One line description of project.', 172 | 'Miscellaneous'), 173 | ] 174 | 175 | 176 | def skip(app, what, name, obj, skip, options): 177 | # Ensure that the __init__ method gets documented. 178 | if name == "__init__": 179 | return False 180 | return skip 181 | 182 | 183 | def setup(app): 184 | app.connect("autodoc-skip-member", skip) 185 | 186 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. python-bigone documentation master file 2 | 3 | .. include:: ../README.rst 4 | 5 | Contents 6 | ======== 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | 11 | overview 12 | 13 | bigone 14 | 15 | Index 16 | ================== 17 | 18 | * :ref:`genindex` 19 | -------------------------------------------------------------------------------- /docs/overview.rst: -------------------------------------------------------------------------------- 1 | Getting Started 2 | =============== 3 | 4 | Installation 5 | ------------ 6 | 7 | ``python-bigone`` is available on `PYPI `_. 8 | Install with ``pip``: 9 | 10 | .. code:: bash 11 | 12 | pip install python-bigone 13 | 14 | 15 | Register on BigONE 16 | ------------------ 17 | 18 | Firstly register an account with `BigONE `_. 19 | 20 | Generate an API Key 21 | ------------------- 22 | 23 | To use signed account methods you are required to `create an API Key `_ and store it. 24 | 25 | Initialise the client 26 | --------------------- 27 | 28 | Pass your API Key and Secret 29 | 30 | .. code:: python 31 | 32 | from bigone.client import Client 33 | client = Client(api_key, api_secret) 34 | 35 | API Rate Limit 36 | -------------- 37 | 38 | No information 39 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyJWT==1.6.4 2 | requests==2.19.1 3 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | 4 | [pep8] 5 | ignore = E501 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import codecs 4 | import os 5 | import re 6 | 7 | from setuptools import setup 8 | 9 | here = os.path.abspath(os.path.dirname(__file__)) 10 | 11 | 12 | def read(*parts): 13 | with codecs.open(os.path.join(here, *parts), 'r') as fp: 14 | return \ 15 | fp.read() 16 | 17 | 18 | def find_version(*file_paths): 19 | version_file = read(*file_paths) 20 | version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", 21 | version_file, re.M) 22 | if version_match: 23 | return version_match.group(1) 24 | raise RuntimeError("Unable to find version string.") 25 | 26 | 27 | setup( 28 | name='python-bigone', 29 | version=find_version("bigone", "__init__.py"), 30 | packages=['bigone'], 31 | description='BigONE REST API python implementation', 32 | url='https://github.com/sammchardy/python-bigone', 33 | author='Sam McHardy', 34 | license='MIT', 35 | author_email='', 36 | install_requires=['requests', 'pyJWT'], 37 | keywords='bigone exchange rest api bitcoin btc eos qtum bitcny', 38 | classifiers=[ 39 | 'Intended Audience :: Developers', 40 | 'License :: OSI Approved :: MIT License', 41 | 'Operating System :: OS Independent', 42 | 'Programming Language :: Python :: 2', 43 | 'Programming Language :: Python :: 2.7', 44 | 'Programming Language :: Python :: 3', 45 | 'Programming Language :: Python :: 3.4', 46 | 'Programming Language :: Python :: 3.5', 47 | 'Programming Language :: Python :: 3.6', 48 | 'Programming Language :: Python', 49 | 'Topic :: Software Development :: Libraries :: Python Modules', 50 | ], 51 | ) 52 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | coverage 2 | flake8 3 | pytest 4 | pytest-cov 5 | pytest-pep8 6 | python-coveralls 7 | requests-mock 8 | tox 9 | setuptools 10 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mchardysam/python-bigone/b1f8ef7ab3c25dc4acf1c785f0e374e3815f93c5/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_api_request.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from bigone.client import Client 4 | from bigone.exceptions import BigoneAPIException, BigoneRequestException 5 | import pytest 6 | import requests_mock 7 | 8 | 9 | client = Client('api_key', 'api_secret') 10 | 11 | 12 | def test_invalid_json(): 13 | """Test Invalid response Exception""" 14 | 15 | with pytest.raises(BigoneRequestException): 16 | with requests_mock.mock() as m: 17 | m.get('https://big.one/api/v2/markets', text='') 18 | client.get_markets() 19 | 20 | 21 | def test_api_exception(): 22 | """Test API response Exception""" 23 | 24 | with pytest.raises(BigoneAPIException): 25 | with requests_mock.mock() as m: 26 | json_obj = { 27 | 'errors': [{ 28 | 'code': 20102, 29 | 'message': 'Unsupported currency ABC' 30 | }] 31 | } 32 | m.get('https://big.one/api/v2/accounts/ABC', json=json_obj, status_code=422) 33 | client.get_account('ABC') 34 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27, py34, py35, py36 3 | 4 | [testenv] 5 | deps = 6 | -rtest-requirements.txt 7 | -rrequirements.txt 8 | commands = py.test -v tests/ --doctest-modules --cov bigone --cov-report term-missing 9 | passenv = 10 | TRAVIS 11 | TRAVIS_BRANCH 12 | TRAVIS_JOB_ID 13 | 14 | [testenv:flake8] 15 | commands = flake8 bigone/ 16 | deps = flake8 17 | 18 | [travis] 19 | python = 20 | 3.6: py36, flake8 21 | 22 | [flake8] 23 | exclude = 24 | .git, 25 | .tox, 26 | build, 27 | dist 28 | ignore = E501 29 | 30 | [pep8] 31 | ignore = E501 32 | --------------------------------------------------------------------------------