├── .gitignore ├── .landscape.yml ├── .travis.yml ├── CONTRIBUTING.md ├── Changelog.md ├── LICENSE ├── README.md ├── ROADMAP.md ├── bitex ├── __init__.py ├── api │ ├── REST │ │ ├── __init__.py │ │ ├── api.py │ │ ├── bitfinex.py │ │ ├── bitstamp.py │ │ ├── bittrex.py │ │ ├── bter.py │ │ ├── ccex.py │ │ ├── coincheck.py │ │ ├── cryptopia.py │ │ ├── gdax.py │ │ ├── gemini.py │ │ ├── hitbtc.py │ │ ├── itbit.py │ │ ├── kraken.py │ │ ├── okcoin.py │ │ ├── poloniex.py │ │ ├── quadriga.py │ │ ├── quoine.py │ │ ├── response.py │ │ ├── rocktrading.py │ │ ├── vaultoro.py │ │ └── yunbi.py │ ├── WSS │ │ ├── __init__.py │ │ ├── base.py │ │ ├── bitfinex.py │ │ ├── bitstamp.py │ │ ├── exceptions.py │ │ ├── gdax.py │ │ ├── gemini.py │ │ ├── hitbtc.py │ │ ├── okcoin.py │ │ └── poloniex.py │ ├── __init__.py │ └── response.py ├── formatters │ ├── __init__.py │ ├── base.py │ ├── bitfinex.py │ ├── bitstamp.py │ ├── bittrex.py │ ├── bter.py │ ├── ccex.py │ ├── coincheck.py │ ├── cryptopia.py │ ├── gdax.py │ ├── gemini.py │ ├── hitbtc.py │ ├── itbit.py │ ├── kraken.py │ ├── okcoin.py │ ├── poloniex.py │ ├── quadriga.py │ ├── quoine.py │ ├── rocktrading.py │ ├── vaultoro.py │ └── yunbi.py ├── interfaces │ ├── __init__.py │ ├── bitfinex.py │ ├── bitstamp.py │ ├── bittrex.py │ ├── bter.py │ ├── ccex.py │ ├── coincheck.py │ ├── cryptopia.py │ ├── gdax.py │ ├── gemini.py │ ├── hitbtc.py │ ├── itbit.py │ ├── kraken.py │ ├── okcoin.py │ ├── poloniex.py │ ├── quadriga.py │ ├── quoine.py │ ├── rocktrading.py │ ├── vaultoro.py │ └── yunbi.py └── utils.py ├── dist ├── BitEx-1.1.0.tar.gz ├── BitEx-1.1.1.tar.gz ├── BitEx-1.1.2.tar.gz └── BitEx-1.1.3.tar.gz ├── docs ├── Makefile ├── _build │ ├── doctrees │ │ ├── environment.pickle │ │ └── index.doctree │ └── html │ │ ├── .buildinfo │ │ ├── _sources │ │ └── index.txt │ │ ├── _static │ │ ├── ajax-loader.gif │ │ ├── alabaster.css │ │ ├── basic.css │ │ ├── comment-bright.png │ │ ├── comment-close.png │ │ ├── comment.png │ │ ├── doctools.js │ │ ├── down-pressed.png │ │ ├── down.png │ │ ├── file.png │ │ ├── jquery-1.11.1.js │ │ ├── jquery.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── underscore-1.3.1.js │ │ ├── underscore.js │ │ ├── up-pressed.png │ │ ├── up.png │ │ └── websupport.js │ │ ├── genindex.html │ │ ├── index.html │ │ ├── objects.inv │ │ ├── search.html │ │ └── searchindex.js ├── conf.py ├── index.rst └── rtd-requirements.txt ├── pylint.rc ├── setup.cfg ├── setup.py └── tests ├── kraken.key ├── test.key ├── test_api.py └── test_formatters.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | .idea 4 | -------------------------------------------------------------------------------- /.landscape.yml: -------------------------------------------------------------------------------- 1 | doc-warnings: no # experimental, raises an exception 2 | test-warnings: no 3 | strictness: veryhigh 4 | max-line-length: 160 5 | # We don't use any of the auto-detected things, and 6 | # auto-detection slows down startup 7 | autodetect: false 8 | requirements: 9 | - requirements.txt 10 | 11 | python-targets: 12 | - 3 13 | ignore-paths: 14 | - docs/ 15 | - build/ 16 | - dist/ 17 | - travis/ 18 | - tests/ 19 | - .eggs 20 | 21 | ignore-patterns: 22 | # disabled code 23 | - ^src/greentest/xtest_.*py 24 | # standard library code 25 | - ^src/greentest/2.* 26 | - ^src/greentest/3.* 27 | # benchmarks that aren't used/changed much 28 | - ^src/greentest/bench_.*py 29 | 30 | pyroma: 31 | run: true 32 | 33 | mccabe: 34 | # We have way too many violations of the complexity measure. 35 | # We should enable this and fix them one at a time, but that's 36 | # more refactoring than I want to do initially. 37 | run: true 38 | 39 | 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.5" 4 | - "3.6" 5 | 6 | install: 7 | - pip install bitex coverage coveralls 8 | 9 | 10 | # command to run tests 11 | script: 12 | - coverage run --source=bitex setup.py test 13 | - coveralls 14 | 15 | 16 | 17 | branches: 18 | only: 19 | - master 20 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Filing Issues 2 | ------------- 3 | 4 | When filing an issue, please use this template: 5 | 6 | :: 7 | 8 | ### Overview Description 9 | 10 | # Steps to Reproduce 11 | 12 | 1. 13 | 2. 14 | 3. 15 | 16 | # Actual Results 17 | 18 | # Expected Results 19 | 20 | # Reproducibility 21 | 22 | # Additional Information: 23 | 24 | 25 | 26 | PR Merge Criteria 27 | ----------------- 28 | 29 | **Attention**: Please make sure that you merge your PR into `dev`, if it's a **feature**, and into `master` if you supply a **hotfix**! Also ensure you're developing from `dev`, not `master`, since the latter is not always kept up-to-date! 30 | 31 | For a PR to be merged, the following statements must hold true: 32 | 33 | - PEP8 has been enforced 34 | - All commits of the PR are atomic 35 | - The pull request was approved by a core maintainer 36 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## V 1.2.1 4 | ## Fixed 5 | - Fixed Bittrex `deposit_address()` per PR #72 6 | - Fixed Cryptopia's currencies per PR #71 7 | - Fixed Cryptopia's signature method #69 8 | - Fixed missing encoding in GDAX authorization 9 | 10 | ## Added 11 | - Implemented Kraken's`wWithdraw()` and `deposit_address()` per PR #70 12 | 13 | ## V 1.1.0 14 | ### Added 15 | Exchanges: 16 | - HitBTC API Client & Interface 17 | - Bter API Client & Interface 18 | - Vaultoro API Client & Interface 19 | 20 | APIs: 21 | - The Following Exchanges had their WSS API implemented: 22 | - HitBTC 23 | - GDAX 24 | - Bitfinex 25 | - Bitstamp 26 | - Gemini 27 | - Poloniex[Beta] 28 | - OkCoin 29 | 30 | ### Changed 31 | Project Structure 32 | - `bitex.api` now features 2 submodules, `REST` and `API`. This should 33 | not affect any imports in existing code. 34 | 35 | ### Deprecated 36 | ### Removed 37 | ### Fixed 38 | Various spelling errors 39 | 40 | ## V 1.0 41 | ### Added 42 | General: 43 | - Changelog (Yay!) 44 | - Pip install support 45 | - Semantic versioning now employed. 46 | - Added folder for Sphinx doc files, for hosting at ReadTheDocs 47 | - Added folder for Unittests; so far only tests for API classes exist 48 | 49 | Exchanges: 50 | - Poloniex API Client and interface 51 | - Interfaces for all API Client currently implemented 52 | - Standardized methods with identical method headers for all interfaces 53 | - Quoine API Client and interface 54 | - QuadrigaCX API Client and interface 55 | 56 | Formatters: 57 | - The sub-module `bitex.formatters` has been added, which provides formatter 58 | functions for exchange interfaces. 59 | 60 | ### Changed 61 | General: 62 | - Restructured project to store interfaces in `bitex.interfaces` sub-module 63 | 64 | API Clients 65 | - The base class `RESTAPI` has been renamed to `APIClient` to better reflect 66 | its purpose 67 | - The class `APIClient` is now an ABC 68 | - The attribute `request_methods` has been removed and its logic and purpose 69 | replaced by using `requests.request()` instead. 70 | 71 | Exchanges: 72 | - All calls to interface methods now return an `APIResponse()` object; this is a 73 | subclass of `requests.Response` and adds an attribute `formatted` - which 74 | contains formatted json data, if available. 75 | - Bitstamp's `tickers` method had its surplus `s` removed. 76 | 77 | ### Deprecated 78 | - btc-e API is no longer supported actively. 79 | 80 | ### Removed 81 | 82 | ### Fixed 83 | - Various spelling and code errors which resulted in the code crashing in unexpected situations. 84 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Nils Diefenbach 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 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/ROADMAP.md -------------------------------------------------------------------------------- /bitex/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | logging.getLogger(__name__).warning("The API clients available in this package are deprecated " 3 | "and will be no longer available in their current form " 4 | "starting with version 2.0!") 5 | from bitex.interfaces import Kraken, Bitfinex, Bitstamp, CCEX, Coincheck 6 | from bitex.interfaces import Cryptopia, Gemini, ItBit, OKCoin, RockTradingLtd 7 | from bitex.interfaces import Yunbi, Bittrex, Poloniex, Quoine, QuadrigaCX 8 | from bitex.interfaces import Vaultoro, HitBtc, Bter, GDAX 9 | 10 | -------------------------------------------------------------------------------- /bitex/api/REST/__init__.py: -------------------------------------------------------------------------------- 1 | from bitex.api.REST.bitfinex import BitfinexREST 2 | from bitex.api.REST.bitstamp import BitstampREST 3 | from bitex.api.REST.bittrex import BittrexREST 4 | from bitex.api.REST.bter import BterREST 5 | from bitex.api.REST.ccex import CCEXRest 6 | from bitex.api.REST.coincheck import CoincheckREST 7 | from bitex.api.REST.cryptopia import CryptopiaREST 8 | from bitex.api.REST.gdax import GDAXRest 9 | from bitex.api.REST.gemini import GeminiREST 10 | from bitex.api.REST.hitbtc import HitBTCREST 11 | from bitex.api.REST.itbit import ItbitREST 12 | from bitex.api.REST.kraken import KrakenREST 13 | from bitex.api.REST.okcoin import OKCoinREST 14 | from bitex.api.REST.poloniex import PoloniexREST 15 | from bitex.api.REST.quadriga import QuadrigaCXREST 16 | from bitex.api.REST.quoine import QuoineREST 17 | from bitex.api.REST.rocktrading import RockTradingREST 18 | from bitex.api.REST.vaultoro import VaultoroREST 19 | from bitex.api.REST.yunbi import YunbiREST 20 | 21 | -------------------------------------------------------------------------------- /bitex/api/REST/api.py: -------------------------------------------------------------------------------- 1 | """ 2 | ABC for Exchange APIs 3 | """ 4 | # Import Built-Ins 5 | import logging 6 | import time 7 | from abc import ABCMeta, abstractmethod 8 | from urllib.parse import urljoin 9 | from os.path import join 10 | 11 | # Import Third-Party 12 | import requests 13 | 14 | # Import Homebrew 15 | from bitex.api.REST.response import APIResponse 16 | 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | class APIClient(metaclass=ABCMeta): 21 | """ 22 | Base Class for API ojects. Provides basic methods to interact 23 | with exchange APIs, such as sending queries and signing messages to pass 24 | authentication. 25 | """ 26 | 27 | def __init__(self, uri, api_version=None, key=None, secret=None, timeout=5): 28 | """ 29 | Create API Client object. 30 | :param uri: string address for api (i.e. https://api.kraken.com/ 31 | :param api_version: version, as required to query an endpoint 32 | :param key: API access key 33 | :param secret: API secret 34 | """ 35 | self.key = key 36 | self.secret = secret 37 | self.uri = uri 38 | self.version = api_version if api_version else '' 39 | self.timeout = timeout 40 | log.debug("Initialized API Client for URI: %s; " 41 | "Will request on API version: %s" % 42 | (self.uri, self.version)) 43 | 44 | def load_key(self, path): 45 | """ 46 | Load key and secret from file. 47 | :param path: path to file with first two lines are key, secret respectively 48 | """ 49 | with open(path, 'r') as f: 50 | self.key = f.readline().strip() 51 | self.secret = f.readline().strip() 52 | 53 | def nonce(self): 54 | """ 55 | Creates a Nonce value for signature generation 56 | :return: 57 | """ 58 | return str(round(100000 * time.time()) * 2) 59 | 60 | @staticmethod 61 | def api_request(*args, **kwargs): 62 | """ 63 | Wrapper which converts a requests.Response into our custom APIResponse 64 | object 65 | :param args: 66 | :param kwargs: 67 | :return: 68 | """ 69 | r = requests.request(*args, **kwargs) 70 | return APIResponse(r) 71 | 72 | @abstractmethod 73 | def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs): 74 | """ 75 | Dummy Signature creation method. Override this in child. 76 | URL is required to be returned, as some Signatures use the url for 77 | sig generation, and api calls made must match the address exactly. 78 | param url: self.uri + self.version + endpoint (i.e https://api.kraken/0/Depth) 79 | param endpoint: api endpoint to call (i.e. 'Depth') 80 | param endpoint_path: self.version + endpoint (i.e. '0/Depth') 81 | param method_verb: valid request type (PUT, GET, POST etc) 82 | param return: 83 | """ 84 | url = self.uri 85 | 86 | return url, {'params': {'test_param': "authenticated_chimichanga"}} 87 | 88 | def query(self, method_verb, endpoint, authenticate=False, 89 | *args, **kwargs): 90 | """ 91 | Queries exchange using given data. Defaults to unauthenticated query. 92 | :param method_verb: valid request type (PUT, GET, POST etc) 93 | :param endpoint: endpoint path for the resource to query, sans the url & 94 | API version (i.e. '/btcusd/ticker/'). 95 | :param authenticate: Bool to determine whether or not a signature is 96 | required. 97 | :param args: Optional args for requests.request() 98 | :param kwargs: Optional Kwargs for self.sign() and requests.request() 99 | :return: request.response() obj 100 | """ 101 | if self.version: 102 | endpoint_path = join(self.version, endpoint) 103 | else: 104 | endpoint_path = endpoint 105 | 106 | url = urljoin(self.uri, endpoint_path) 107 | if authenticate: # sign off kwargs and url before sending request 108 | url, request_kwargs = self.sign(url, endpoint, endpoint_path, 109 | method_verb, *args, **kwargs) 110 | else: 111 | request_kwargs = kwargs 112 | log.debug("Making request to: %s, kwargs: %s", url, request_kwargs) 113 | r = self.api_request(method_verb, url, timeout=self.timeout, 114 | **request_kwargs) 115 | log.debug("Made %s request made to %s, with headers %s and body %s. " 116 | "Status code %s", r.request.method, 117 | r.request.url, r.request.headers, 118 | r.request.body, r.status_code) 119 | return r 120 | -------------------------------------------------------------------------------- /bitex/api/REST/bitfinex.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import json 8 | import hashlib 9 | import hmac 10 | import base64 11 | 12 | # Import Homebrew 13 | from bitex.api.REST.api import APIClient 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class BitfinexREST(APIClient): 20 | def __init__(self, key=None, secret=None, api_version='v1', 21 | url='https://api.bitfinex.com', timeout=5): 22 | super(BitfinexREST, self).__init__(url, api_version=api_version, 23 | key=key, secret=secret, 24 | timeout=timeout) 25 | 26 | def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs): 27 | try: 28 | req = kwargs['params'] 29 | except KeyError: 30 | req = {} 31 | if self.version == 'v1': 32 | req['request'] = endpoint_path 33 | req['nonce'] = self.nonce() 34 | 35 | js = json.dumps(req) 36 | data = base64.standard_b64encode(js.encode('utf8')) 37 | else: 38 | data = '/api/' + endpoint_path + self.nonce() + json.dumps(req) 39 | h = hmac.new(self.secret.encode('utf8'), data, hashlib.sha384) 40 | signature = h.hexdigest() 41 | headers = {"X-BFX-APIKEY": self.key, 42 | "X-BFX-SIGNATURE": signature, 43 | "X-BFX-PAYLOAD": data} 44 | if self.version == 'v2': 45 | headers['content-type'] = 'application/json' 46 | 47 | return url, {'headers': headers} 48 | 49 | -------------------------------------------------------------------------------- /bitex/api/REST/bitstamp.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import hashlib 8 | import hmac 9 | 10 | # Import Homebrew 11 | from bitex.api.REST.api import APIClient 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | class BitstampREST(APIClient): 18 | def __init__(self, user_id='', key=None, secret=None, api_version=None, 19 | url='https://www.bitstamp.net/api', timeout=5): 20 | self.id = user_id 21 | super(BitstampREST, self).__init__(url, api_version=api_version, 22 | key=key, secret=secret, 23 | timeout=timeout) 24 | 25 | def load_key(self, path): 26 | """Load key and secret from file.""" 27 | with open(path, 'r') as f: 28 | self.key = f.readline().strip() 29 | self.secret = f.readline().strip() 30 | self.id = f.readline().strip() 31 | 32 | def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs): 33 | nonce = self.nonce() 34 | message = nonce + self.id + self.key 35 | 36 | signature = hmac.new(self.secret.encode(), message.encode(), 37 | hashlib.sha256) 38 | signature = signature.hexdigest().upper() 39 | 40 | try: 41 | req = kwargs['params'] 42 | except KeyError: 43 | req = {} 44 | req['key'] = self.key 45 | req['nonce'] = nonce 46 | req['signature'] = signature 47 | return url, {'data': req} 48 | 49 | -------------------------------------------------------------------------------- /bitex/api/REST/bittrex.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import hashlib 8 | import hmac 9 | 10 | import urllib 11 | import urllib.parse 12 | 13 | # Import Homebrew 14 | from bitex.api.REST.api import APIClient 15 | 16 | 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | class BittrexREST(APIClient): 21 | def __init__(self, key=None, secret=None, api_version='v1.1', 22 | url='https://bittrex.com/api', timeout=5): 23 | super(BittrexREST, self).__init__(url, api_version=api_version, key=key, 24 | secret=secret, timeout=timeout) 25 | 26 | def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs): 27 | 28 | try: 29 | params = kwargs['params'] 30 | except KeyError: 31 | params = {} 32 | 33 | nonce = self.nonce() 34 | 35 | req_string = endpoint_path + '?apikey=' + self.key + "&nonce=" + nonce + '&' 36 | req_string += urllib.parse.urlencode(params) 37 | headers = {"apisign": hmac.new(self.secret.encode('utf-8'), 38 | (self.uri + req_string).encode('utf-8'), 39 | hashlib.sha512).hexdigest()} 40 | 41 | return self.uri + req_string, {'headers': headers, 'params': {}} 42 | 43 | -------------------------------------------------------------------------------- /bitex/api/REST/bter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import hashlib 8 | import hmac 9 | import urllib 10 | import urllib.parse 11 | 12 | # Import Homebrew 13 | from bitex.api.REST.api import APIClient 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class BterREST(APIClient): 20 | def __init__(self, key=None, secret=None, api_version=None, 21 | url='http://data.bter.com/api', timeout=5): 22 | api_version = '1' if not api_version else api_version 23 | super(BterREST, self).__init__(url, api_version=api_version, 24 | key=key, secret=secret, 25 | timeout=timeout) 26 | 27 | def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs): 28 | try: 29 | params = kwargs['params'] 30 | except KeyError: 31 | params = {} 32 | nonce = self.nonce() 33 | kwargs['nonce'] = nonce 34 | 35 | msg = urllib.parse.urlencode(params) 36 | 37 | signature = hmac.new(self.secret.encode(encoding='utf-8'), 38 | msg.encode(encoding='utf-8'), hashlib.sha512).hexdigest() 39 | headers = {'Key': signature, 'Sign': signature} 40 | return uri + msg, {'headers': headers} 41 | 42 | -------------------------------------------------------------------------------- /bitex/api/REST/ccex.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import hashlib 8 | import hmac 9 | import urllib 10 | import urllib.parse 11 | 12 | # Import Homebrew 13 | from bitex.api.REST.api import APIClient 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class CCEXRest(APIClient): 20 | def __init__(self, key=None, secret=None, api_version=None, 21 | url='https://c-cex.com/t', timeout=5): 22 | super(CCEXRest, self).__init__(url, api_version=api_version, key=key, 23 | secret=secret, timeout=timeout) 24 | 25 | def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs): 26 | nonce = self.nonce() 27 | try: 28 | params = kwargs['params'] 29 | except KeyError: 30 | params = {} 31 | 32 | params['apikey'] = self.key 33 | params['nonce'] = nonce 34 | post_params = params 35 | post_params.update({'nonce': nonce, 'method': endpoint}) 36 | post_params = urllib.parse.urlencode(post_params) 37 | 38 | url = uri + post_params 39 | 40 | sig = hmac.new(url, self.secret, hashlib.sha512) 41 | headers = {'apisign': sig} 42 | 43 | return url, {'headers': headers} 44 | 45 | -------------------------------------------------------------------------------- /bitex/api/REST/coincheck.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import json 8 | import hashlib 9 | import hmac 10 | 11 | # Import Homebrew 12 | from bitex.api.REST.api import APIClient 13 | 14 | 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | class CoincheckREST(APIClient): 19 | def __init__(self, key=None, secret=None, api_version='api', 20 | url='https://coincheck.com', timeout=5): 21 | super(CoincheckREST, self).__init__(url, api_version=api_version, 22 | key=key, secret=secret, 23 | timeout=timeout) 24 | 25 | def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs): 26 | 27 | nonce = self.nonce() 28 | try: 29 | params = kwargs['params'] 30 | except KeyError: 31 | params = {} 32 | 33 | params = json.dumps(params) 34 | # sig = nonce + url + req 35 | data = (nonce + endpoint_path + params).encode('utf-8') 36 | h = hmac.new(self.secret.encode('utf8'), data, hashlib.sha256) 37 | signature = h.hexdigest() 38 | headers = {"ACCESS-KEY": self.key, 39 | "ACCESS-NONCE": nonce, 40 | "ACCESS-SIGNATURE": signature} 41 | 42 | return url, {'headers': headers} 43 | 44 | -------------------------------------------------------------------------------- /bitex/api/REST/cryptopia.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import json 8 | import hashlib 9 | import hmac 10 | import base64 11 | import urllib 12 | import urllib.parse 13 | 14 | # Import Homebrew 15 | from bitex.api.REST.api import APIClient 16 | 17 | 18 | log = logging.getLogger(__name__) 19 | 20 | 21 | class CryptopiaREST(APIClient): 22 | def __init__(self, key=None, secret=None, api_version=None, 23 | url='https://www.cryptopia.co.nz/api', timeout=5): 24 | super(CryptopiaREST, self).__init__(url, api_version=api_version, key=key, 25 | secret=secret, timeout=timeout) 26 | 27 | def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs): 28 | nonce = self.nonce() 29 | try: 30 | params = kwargs['params'] 31 | except KeyError: 32 | params = {} 33 | 34 | post_data = json.dumps(params) 35 | 36 | # generate signature 37 | md5 = hashlib.md5() 38 | md5.update(post_data.encode('utf-8')) 39 | request_content_b64_string = base64.b64encode(md5.digest()).decode('utf-8') 40 | signature = (self.key + 'POST' + 41 | urllib.parse.quote_plus(uri).lower() + 42 | nonce + request_content_b64_string) 43 | 44 | hmac_sig = base64.b64encode(hmac.new(base64.b64decode(self.secret), 45 | signature.encode('utf-8'), 46 | hashlib.sha256).digest()) 47 | header_data = 'amx ' + self.key + ':' + hmac_sig.decode('utf-8') + ':' + nonce 48 | 49 | # Update req_kwargs keys 50 | headers = {'Authorization': header_data, 51 | 'Content-Type': 'application/json; charset=utf-8'} 52 | 53 | return uri, {'headers': headers, 'data': post_data} 54 | 55 | -------------------------------------------------------------------------------- /bitex/api/REST/gdax.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import hashlib 8 | import hmac 9 | import base64 10 | import time 11 | 12 | # Import Third-Party 13 | from requests.auth import AuthBase 14 | 15 | # Import Homebrew 16 | from bitex.api.REST.api import APIClient 17 | 18 | 19 | log = logging.getLogger(__name__) 20 | 21 | 22 | class GdaxAuth(AuthBase): 23 | def __init__(self, api_key, secret_key, passphrase): 24 | self.api_key = api_key.encode('utf-8') 25 | self.secret_key = secret_key.encode('utf-8') 26 | self.passphrase = passphrase.encode('utf-8') 27 | 28 | def __call__(self, request): 29 | timestamp = str(time.time()) 30 | message = (timestamp + request.method + request.path_url + 31 | (request.body.decode('utf-8') or '')) 32 | hmac_key = base64.b64decode(self.secret_key) 33 | signature = hmac.new(hmac_key, message.encode('utf-8'), hashlib.sha256) 34 | signature_b64 = base64.b64encode(signature.digest()) 35 | 36 | request.headers.update({ 37 | 'CB-ACCESS-SIGN': signature_b64, 38 | 'CB-ACCESS-TIMESTAMP': timestamp, 39 | 'CB-ACCESS-KEY': self.api_key, 40 | 'CB-ACCESS-PASSPHRASE': self.passphrase, 41 | 'Content-Type': 'application/json' 42 | }) 43 | return request 44 | 45 | 46 | class GDAXRest(APIClient): 47 | def __init__(self, passphrase='', key=None, secret=None, api_version=None, 48 | url='https://api.gdax.com', timeout=5): 49 | self.passphrase = passphrase 50 | super(GDAXRest, self).__init__(url, api_version=api_version, key=key, 51 | secret=secret, timeout=timeout) 52 | 53 | def load_key(self, path): 54 | """ 55 | Load key and secret from file. 56 | """ 57 | with open(path, 'r') as f: 58 | self.key = f.readline().strip() 59 | self.secret = f.readline().strip() 60 | self.passphrase = f.readline().strip() 61 | 62 | def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs): 63 | auth = GdaxAuth(self.key, self.secret, self.passphrase) 64 | try: 65 | js = kwargs['params'] 66 | except KeyError: 67 | js = {} 68 | 69 | return url, {'json': js, 'auth': auth} 70 | 71 | -------------------------------------------------------------------------------- /bitex/api/REST/gemini.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import json 8 | import hashlib 9 | import hmac 10 | import base64 11 | 12 | # Import Homebrew 13 | from bitex.api.REST.api import APIClient 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class GeminiREST(APIClient): 20 | def __init__(self, key=None, secret=None, api_version='v1', 21 | url='https://api.gemini.com', timeout=5): 22 | super(GeminiREST, self).__init__(url, api_version=api_version, key=key, 23 | secret=secret, timeout=timeout) 24 | 25 | def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs): 26 | nonce = self.nonce() 27 | try: 28 | params = kwargs['params'] 29 | except KeyError: 30 | params = {} 31 | payload = params 32 | payload['nonce'] = nonce 33 | payload['request'] = endpoint_path 34 | 35 | js = json.dumps(payload) 36 | data = base64.standard_b64encode(js.encode('utf8')) 37 | h = hmac.new(self.secret.encode('utf8'), data, hashlib.sha384) 38 | signature = h.hexdigest() 39 | headers = {'X-GEMINI-APIKEY': self.key, 40 | 'X-GEMINI-PAYLOAD': data, 41 | 'X-GEMINI-SIGNATURE': signature} 42 | return uri, {'headers': headers} 43 | 44 | -------------------------------------------------------------------------------- /bitex/api/REST/hitbtc.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import hashlib 8 | import hmac 9 | import urllib 10 | import urllib.parse 11 | 12 | # Import Homebrew 13 | from bitex.api.REST.api import APIClient 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class HitBTCREST(APIClient): 20 | def __init__(self, key=None, secret=None, api_version='1', 21 | url='http://api.hitbtc.com/api/', timeout=5): 22 | api_version = '' if not api_version else api_version 23 | super(HitBTCREST, self).__init__(url, api_version=api_version, 24 | key=key, secret=secret, 25 | timeout=timeout) 26 | 27 | def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs): 28 | try: 29 | params = kwargs['params'] 30 | except KeyError: 31 | params = {} 32 | nonce = self.nonce() 33 | params['nonce'] = nonce 34 | params['apikey'] = self.key 35 | msg = 'api' + endpoint_path + '?' + urllib.parse.urlencode(params) 36 | 37 | signature = hmac.new(self.secret.encode(encoding='utf-8'), 38 | msg.encode(encoding='utf-8'), hashlib.sha512).hexdigest() 39 | headers = {'Api-signature': signature} 40 | return self.uri + msg, {'headers': headers, 'data': params} 41 | 42 | -------------------------------------------------------------------------------- /bitex/api/REST/itbit.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import json 8 | import hashlib 9 | import hmac 10 | import base64 11 | 12 | # Import Homebrew 13 | from bitex.api.REST.api import APIClient 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class ItbitREST(APIClient): 20 | def __init__(self, user_id = '', key=None, secret=None, api_version='v1', 21 | url='https://api.itbit.com', timeout=5): 22 | self.userId = user_id 23 | super(ItbitREST, self).__init__(url, api_version=api_version, 24 | key=key, secret=secret, timeout=timeout) 25 | 26 | def load_key(self, path): 27 | """ 28 | Load user id, key and secret from file. 29 | """ 30 | with open(path, 'r') as f: 31 | self.key = f.readline().strip() 32 | self.secret = f.readline().strip() 33 | self.userId = f.readline().strip() 34 | 35 | def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs): 36 | try: 37 | params = kwargs['params'] 38 | except KeyError: 39 | params = {} 40 | 41 | verb = method_verb 42 | 43 | if verb in ('PUT', 'POST'): 44 | body = params 45 | else: 46 | body = {} 47 | 48 | timestamp = self.nonce() 49 | nonce = self.nonce() 50 | 51 | message = json.dumps([verb, url, body, nonce, timestamp], 52 | separators=(',', ':')) 53 | sha256_hash = hashlib.sha256() 54 | nonced_message = nonce + message 55 | sha256_hash.update(nonced_message.encode('utf8')) 56 | hash_digest = sha256_hash.digest() 57 | hmac_digest = hmac.new(self.secret.encode('utf-8'), 58 | url.encode('utf-8') + hash_digest, 59 | hashlib.sha512).digest() 60 | signature = base64.b64encode(hmac_digest) 61 | 62 | auth_headers = { 63 | 'Authorization': self.key + ':' + signature.decode('utf8'), 64 | 'X-Auth-Timestamp': timestamp, 65 | 'X-Auth-Nonce': nonce, 66 | 'Content-Type': 'application/json' 67 | } 68 | return url, {'headers': auth_headers} 69 | 70 | -------------------------------------------------------------------------------- /bitex/api/REST/kraken.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import hashlib 8 | import hmac 9 | import base64 10 | import urllib 11 | import urllib.parse 12 | 13 | # Import Homebrew 14 | from bitex.api.REST.api import APIClient 15 | 16 | 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | class KrakenREST(APIClient): 21 | def __init__(self, key=None, secret=None, api_version='0', 22 | url='https://api.kraken.com', timeout=5): 23 | super(KrakenREST, self).__init__(url, api_version=api_version, 24 | key=key, secret=secret, timeout=timeout) 25 | 26 | def sign(self, url, endpoint, endpoint_path, method_verb, *args, **kwargs): 27 | try: 28 | req = kwargs['params'] 29 | except KeyError: 30 | req = {} 31 | 32 | req['nonce'] = self.nonce() 33 | postdata = urllib.parse.urlencode(req) 34 | 35 | # Unicode-objects must be encoded before hashing 36 | encoded = (str(req['nonce']) + postdata).encode('utf-8') 37 | message = (endpoint_path.encode('utf-8') + 38 | hashlib.sha256(encoded).digest()) 39 | 40 | signature = hmac.new(base64.b64decode(self.secret), 41 | message, hashlib.sha512) 42 | sigdigest = base64.b64encode(signature.digest()) 43 | 44 | headers = { 45 | 'API-Key': self.key, 46 | 'API-Sign': sigdigest.decode('utf-8') 47 | } 48 | 49 | return url, {'data': req, 'headers': headers} 50 | 51 | -------------------------------------------------------------------------------- /bitex/api/REST/okcoin.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import hashlib 8 | import hmac 9 | 10 | # Import Homebrew 11 | from bitex.api.REST.api import APIClient 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | class OKCoinREST(APIClient): 18 | def __init__(self, key=None, secret=None, api_version='v1', 19 | url='https://www.okcoin.com/api', timeout=5): 20 | super(OKCoinREST, self).__init__(url, api_version=api_version, 21 | key=key, secret=secret, 22 | timeout=timeout) 23 | 24 | def sign(self,url, endpoint, endpoint_path, method_verb, *args, **kwargs): 25 | nonce = self.nonce() 26 | 27 | # sig = nonce + url + req 28 | data = (nonce + url).encode() 29 | 30 | h = hmac.new(self.secret.encode('utf8'), data, hashlib.sha256) 31 | signature = h.hexdigest() 32 | headers = {"ACCESS-KEY": self.key, 33 | "ACCESS-NONCE": nonce, 34 | "ACCESS-SIGNATURE": signature} 35 | 36 | return url, {'headers': headers} 37 | 38 | -------------------------------------------------------------------------------- /bitex/api/REST/poloniex.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import hashlib 8 | import hmac 9 | import urllib 10 | import urllib.parse 11 | 12 | # Import Homebrew 13 | from bitex.api.REST.api import APIClient 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class PoloniexREST(APIClient): 20 | def __init__(self, key=None, secret=None, api_version=None, 21 | url='https://poloniex.com', timeout=5): 22 | super(PoloniexREST, self).__init__(url, api_version=api_version, 23 | key=key, secret=secret, 24 | timeout=timeout) 25 | 26 | def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs): 27 | try: 28 | params = kwargs['params'] 29 | except KeyError: 30 | params = {} 31 | params['nonce'] = self.nonce() 32 | payload = params 33 | 34 | msg = urllib.parse.urlencode(payload).encode('utf-8') 35 | sig = hmac.new(self.secret.encode('utf-8'), msg, hashlib.sha512).hexdigest() 36 | headers = {'Key': self.key, 'Sign': sig} 37 | return uri, {'headers': headers, 'data': params} 38 | 39 | -------------------------------------------------------------------------------- /bitex/api/REST/quadriga.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import hashlib 8 | import hmac 9 | 10 | # Import Homebrew 11 | from bitex.api.REST.api import APIClient 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | class QuadrigaCXREST(APIClient): 18 | def __init__(self, key=None, secret=None, client_id='', api_version='v2', 19 | url='https://api.quoine.com/', timeout=5): 20 | self.client_id = client_id 21 | super(QuadrigaCXREST, self).__init__(url, api_version=api_version, 22 | key=key, secret=secret, 23 | timeout=timeout) 24 | 25 | def load_key(self, path): 26 | """ 27 | Load key and secret from file. 28 | """ 29 | with open(path, 'r') as f: 30 | self.key = f.readline().strip() 31 | self.secret = f.readline().strip() 32 | self.client_id = f.readline().strip() 33 | 34 | def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs): 35 | try: 36 | params = kwargs['params'] 37 | except KeyError: 38 | params = {} 39 | nonce = self.nonce() 40 | msg = nonce + self.client_id + self.key 41 | 42 | signature = hmac.new(self.secret.encode(encoding='utf-8'), 43 | msg.encode(encoding='utf-8'), hashlib.sha256) 44 | headers = {'key': self.key, 'signature': signature, 45 | 'nonce': nonce} 46 | return self.uri, {'headers': headers, 'data': params} 47 | 48 | -------------------------------------------------------------------------------- /bitex/api/REST/quoine.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import urllib 8 | import urllib.parse 9 | 10 | try: 11 | import jwt 12 | jwt_available = True 13 | except ImportError: 14 | jwt_available = False 15 | 16 | # Import Homebrew 17 | from bitex.api.REST.api import APIClient 18 | 19 | 20 | log = logging.getLogger(__name__) 21 | 22 | 23 | class QuoineREST(APIClient): 24 | """ 25 | The Quoine Api requires the API version to be designated in each requests's 26 | header as {'X-Quoine-API-Version': 2} 27 | """ 28 | def __init__(self, key=None, secret=None, api_version=None, 29 | url='https://api.quoine.com', timeout=5): 30 | if not jwt_available: 31 | raise SystemError("No JWT Installed! Quoine API Unavailable!") 32 | super(QuoineREST, self).__init__(url, api_version=api_version, 33 | key=key, secret=secret, timeout=timeout) 34 | 35 | def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs): 36 | try: 37 | params = kwargs['params'] 38 | except KeyError: 39 | params = {} 40 | 41 | if method_verb != 'POST': 42 | endpoint_path += urllib.parse.urlencode(params) 43 | msg = {'path': endpoint_path, 'nonce': self.nonce(), 'token_id': self.key} 44 | 45 | signature = jwt.encode(msg, self.secret, algorithm='HS256') 46 | headers = {'X-Quoine-API-Version': '2', 'X-Quoine-Auth': signature, 47 | 'Content-Type': 'application/json'} 48 | request = {'headers': headers} 49 | if method_verb == 'POST': 50 | request['json'] = params 51 | return self.uri + endpoint_path, request 52 | 53 | -------------------------------------------------------------------------------- /bitex/api/REST/response.py: -------------------------------------------------------------------------------- 1 | # Import Third-Party 2 | from requests import Response 3 | 4 | 5 | class APIResponse(Response): 6 | 7 | def __init__(self, req_response, formatted_json=None): 8 | for k, v in req_response.__dict__.items(): 9 | self.__dict__[k] = v 10 | self._formatted = formatted_json 11 | 12 | @property 13 | def formatted(self): 14 | return self._formatted 15 | 16 | @formatted.setter 17 | def formatted(self, value): 18 | self._formatted = value 19 | 20 | 21 | if __name__ == '__main__': 22 | from bitex import Kraken 23 | 24 | k = Kraken() 25 | resp = k.ticker('XXBTZEUR') 26 | print(resp.formatted) 27 | print(resp.json()) -------------------------------------------------------------------------------- /bitex/api/REST/rocktrading.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import hashlib 8 | import hmac 9 | 10 | # Import Homebrew 11 | from bitex.api.REST.api import APIClient 12 | 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | class RockTradingREST(APIClient): 18 | def __init__(self, key=None, secret=None, api_version='v1', 19 | url='https://api.therocktrading.com', timeout=5): 20 | super(RockTradingREST, self).__init__(url, api_version=api_version, 21 | key=key, secret=secret, 22 | timeout=timeout) 23 | 24 | def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs): 25 | nonce = self.nonce() 26 | try: 27 | params = kwargs['params'] 28 | except KeyError: 29 | params = {} 30 | payload = params 31 | payload['nonce'] = int(nonce) 32 | payload['request'] = endpoint_path 33 | 34 | msg = nonce + uri 35 | sig = hmac.new(self.secret.encode(), msg.encode(), hashlib.sha384).hexdigest() 36 | headers = {'X-TRT-APIKEY': self.key, 37 | 'X-TRT-Nonce': nonce, 38 | 'X-TRT-SIGNATURE': sig, 'Content-Type': 'application/json'} 39 | return uri, {'headers': headers} 40 | 41 | -------------------------------------------------------------------------------- /bitex/api/REST/vaultoro.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import hashlib 8 | import hmac 9 | import urllib 10 | import urllib.parse 11 | 12 | # Import Homebrew 13 | from bitex.api.REST.api import APIClient 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class VaultoroREST(APIClient): 20 | def __init__(self, key=None, secret=None, api_version=None, 21 | url='https://api.vaultoro.com', timeout=5): 22 | api_version = '' if not api_version else api_version 23 | super(VaultoroREST, self).__init__(url, api_version=api_version, 24 | key=key, secret=secret, 25 | timeout=timeout) 26 | 27 | def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs): 28 | try: 29 | params = kwargs['params'] 30 | except KeyError: 31 | params = {} 32 | nonce = self.nonce() 33 | kwargs['nonce'] = nonce 34 | kwargs['apikey'] = self.key 35 | msg = uri + urllib.parse.urlencode(params) 36 | 37 | signature = hmac.new(self.secret.encode(encoding='utf-8'), 38 | msg.encode(encoding='utf-8'), hashlib.sha256).hexdigest() 39 | headers = {'X-Signature': signature} 40 | return msg, {'headers': headers} 41 | 42 | -------------------------------------------------------------------------------- /bitex/api/REST/yunbi.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains all API Client sub-classes, which store exchange specific details 3 | and feature the respective exchanges authentication method (sign()). 4 | """ 5 | # Import Built-ins 6 | import logging 7 | import hashlib 8 | import hmac 9 | import urllib 10 | import urllib.parse 11 | 12 | # Import Homebrew 13 | from bitex.api.REST.api import APIClient 14 | 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class YunbiREST(APIClient): 20 | def __init__(self, key=None, secret=None, api_version='v2', 21 | url='https://yunbi.com/api', timeout=5): 22 | super(YunbiREST, self).__init__(url, api_version=api_version, key=key, 23 | secret=secret, timeout=timeout) 24 | 25 | def sign(self, uri, endpoint, endpoint_path, method_verb, *args, **kwargs): 26 | nonce = self.nonce() 27 | try: 28 | params = kwargs['params'] 29 | except KeyError: 30 | params = {} 31 | params['tonce'] = nonce 32 | params['access_key'] = self.key 33 | post_params = urllib.parse.urlencode(params) 34 | msg = '%s|%s|%s' % (method_verb, endpoint_path, post_params) 35 | 36 | sig = hmac.new(self.secret, msg, hashlib.sha256).hexdigest() 37 | uri += post_params + '&signature=' + sig 38 | 39 | return uri, {} 40 | 41 | -------------------------------------------------------------------------------- /bitex/api/WSS/__init__.py: -------------------------------------------------------------------------------- 1 | from bitex.api.WSS.bitfinex import BitfinexWSS 2 | from bitex.api.WSS.bitstamp import BitstampWSS 3 | from bitex.api.WSS.gdax import GDAXWSS 4 | from bitex.api.WSS.gemini import GeminiWSS 5 | from bitex.api.WSS.hitbtc import HitBTCWSS 6 | from bitex.api.WSS.okcoin import OKCoinWSS 7 | from bitex.api.WSS.poloniex import PoloniexWSS 8 | -------------------------------------------------------------------------------- /bitex/api/WSS/base.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | from queue import Queue, Empty 4 | from threading import Thread 5 | 6 | # Import Third-Party 7 | 8 | # Import Homebrew 9 | 10 | # Init Logging Facilities 11 | log = logging.getLogger(__name__) 12 | 13 | 14 | class WSSAPI: 15 | """ 16 | Base Class with no actual connection functionality. This is added in the 17 | subclass, as the various wss APIs are too diverse in order to distill a 18 | sensible pool of common attributes. 19 | """ 20 | def __init__(self, addr, name): 21 | """ 22 | Initialize Object. 23 | :param addr: 24 | :param name: 25 | """ 26 | log.debug("WSSAPI.__init__(): Initializing Websocket API") 27 | self.addr = addr 28 | self.name = name 29 | self.running = False 30 | 31 | # External Interface to interact with WSS. 32 | self.interface = Queue() 33 | 34 | # Internal Command Queue for restarts, stops and starts 35 | self._controller_q = Queue() 36 | 37 | # Queue storing all received data 38 | self.data_q = Queue() 39 | 40 | # Internal Controller thread, responsible for starts / restarts / stops 41 | self._controller_thread = None 42 | 43 | def start(self): 44 | """ 45 | Starts threads. Extend this in your child class. 46 | :return: 47 | """ 48 | log.info("WSSAPI.start(): Starting Basic Facilities") 49 | self.running = True 50 | if self._controller_thread is None or not self._controller_thread.is_alive(): 51 | self._controller_thread = Thread(target=self._controller, 52 | daemon=True, 53 | name='%s Controller Thread' % 54 | self.name) 55 | self._controller_thread.start() 56 | 57 | def stop(self): 58 | """ 59 | Stops Threads. Overwrite this in your child class as necessary. 60 | :return: 61 | """ 62 | log.debug("WSSAPI.stop(): Stopping..") 63 | self.running = False 64 | 65 | def restart(self): 66 | """ 67 | Restart Threads. 68 | :return: 69 | """ 70 | log.debug("WSSAPI.restart(): Restarting API Client..") 71 | self.stop() 72 | self.start() 73 | 74 | def _controller(self): 75 | """ 76 | This method runs in a dedicated thread, calling self.eval_command(). 77 | :return: 78 | """ 79 | while self.running: 80 | try: 81 | cmd = self._controller_q.get(timeout=1) 82 | except (TimeoutError, Empty): 83 | continue 84 | log.debug("WSSAPI._controller(): Received command: %s", cmd) 85 | Thread(target=self.eval_command, args=(cmd,)).start() 86 | 87 | def send(self, payload): 88 | """ 89 | Method to send instructions for subcribing, unsubscribing, etc to 90 | the exchange API. 91 | :return: 92 | """ 93 | raise NotImplementedError() 94 | 95 | def eval_command(self, cmd): 96 | """ 97 | Evaluates commands issued by internal threads. Extend this as necessary. 98 | :param cmd: 99 | :return: 100 | """ 101 | if cmd == 'restart': 102 | self.restart() 103 | 104 | elif cmd == 'stop': 105 | self.stop() 106 | 107 | else: 108 | raise ValueError("Unknown Command passed to controller! %s" % cmd) 109 | 110 | def get(self, **kwargs): 111 | return self.data_q.get(**kwargs) 112 | -------------------------------------------------------------------------------- /bitex/api/WSS/gdax.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | import json 4 | import threading 5 | import time 6 | 7 | # Import Third-Party 8 | from websocket import create_connection, WebSocketTimeoutException 9 | import requests 10 | # Import Homebrew 11 | from bitex.api.WSS.base import WSSAPI 12 | 13 | # Init Logging Facilities 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | class GDAXWSS(WSSAPI): 18 | def __init__(self): 19 | super(GDAXWSS, self).__init__('wss://ws-feed.gdax.com', 'GDAX') 20 | self.conn = None 21 | r = requests.get('https://api.gdax.com/products').json() 22 | self.pairs = [x['id'] for x in r] 23 | self._data_thread = None 24 | 25 | def start(self): 26 | super(GDAXWSS, self).start() 27 | 28 | self._data_thread = threading.Thread(target=self._process_data) 29 | self._data_thread.daemon = True 30 | self._data_thread.start() 31 | 32 | def stop(self): 33 | super(GDAXWSS, self).stop() 34 | 35 | self._data_thread.join() 36 | 37 | def _process_data(self): 38 | self.conn = create_connection(self.addr, timeout=4) 39 | payload = json.dumps({'type': 'subscribe', 'product_ids': self.pairs}) 40 | self.conn.send(payload) 41 | while self.running: 42 | try: 43 | data = json.loads(self.conn.recv()) 44 | except (WebSocketTimeoutException, ConnectionResetError): 45 | self._controller_q.put('restart') 46 | 47 | if 'product_id' in data: 48 | self.data_q.put(('order_book', data['product_id'], 49 | data, time.time())) 50 | self.conn = None -------------------------------------------------------------------------------- /bitex/api/WSS/gemini.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | import time 4 | from threading import Thread 5 | from queue import Queue 6 | 7 | # Import Third-Party 8 | import requests 9 | 10 | # Import Homebrew 11 | from bitex.api.WSS.base import WSSAPI 12 | 13 | # Init Logging Facilities 14 | log = logging.getLogger(__name__) 15 | 16 | from websocket import create_connection, WebSocketTimeoutException 17 | 18 | 19 | class GeminiWSS(WSSAPI): 20 | def __init__(self, endpoints=None): 21 | super(GeminiWSS, self).__init__('wss://api.gemini.com/v1/', 'Gemini') 22 | self.endpoints = (endpoints if endpoints else 23 | requests.get('https://api.gemini.com/v1/symbols').json()) 24 | self.endpoints = ['marketdata/' + x.upper() for x in self.endpoints] 25 | self.endpoint_threads = {} 26 | self.threads_running = {} 27 | self.restarter_thread = None 28 | self.restart_q = Queue() 29 | 30 | def _restart_thread(self): 31 | """ 32 | Restarts subscription threads if their connection drops. 33 | :return: 34 | """ 35 | while self.running['restart_thread']: 36 | try: 37 | endpoint = self.restart_q.get(timeout=.5) 38 | except TimeoutError: 39 | continue 40 | log.info("_restart_thread(): Restarting Thread for endpoint %s", 41 | endpoint) 42 | self.unsubscribe(endpoint) 43 | self.subscribe(endpoint) 44 | 45 | def _subscription_thread(self, endpoint): 46 | """ 47 | Thread Method, running the connection for each endpoint. 48 | :param endpoint: 49 | :return: 50 | """ 51 | try: 52 | conn = create_connection(self.addr + endpoint, timeout=5) 53 | except WebSocketTimeoutException: 54 | self.restart_q.put(endpoint) 55 | return 56 | 57 | while self.threads_running[endpoint]: 58 | try: 59 | msg = conn.recv() 60 | except WebSocketTimeoutException: 61 | self._controller_q.put(endpoint) 62 | 63 | log.debug("%s, %s", endpoint, msg) 64 | ep, pair = endpoint.split('/') 65 | log.debug("_subscription_thread(): Putting data on q..") 66 | try: 67 | self.data_q.put((ep, pair, msg, time.time()), timeout=1) 68 | except TimeoutError: 69 | continue 70 | finally: 71 | log.debug("_subscription_thread(): Data Processed, looping back..") 72 | conn.close() 73 | log.debug("_subscription_thread(): Thread Loop Ended.") 74 | 75 | def start(self): 76 | super(GeminiWSS, self).start() 77 | 78 | log.debug("GeminiWSS.start(): launching Endpoint Threads..") 79 | for endpoint in self.endpoints: 80 | self.subscribe(endpoint) 81 | 82 | def stop(self): 83 | super(GeminiWSS, self).stop() 84 | log.debug('stop(): Shutting down Endpoint threads..') 85 | for endpoint in self.endpoints: 86 | try: 87 | self.unsubscribe(endpoint) 88 | except KeyError: 89 | pass 90 | while not all(not self.endpoint_threads[x].is_alive() for x in self.endpoint_threads): 91 | time.sleep(1) 92 | log.debug('stop(): Waiting for threads to shutdown..') 93 | log.debug("stop(): Shutting down complete - running Grabage Collector..") 94 | self.garbage_collector() 95 | 96 | def restart(self): 97 | self.stop() 98 | self.start() 99 | 100 | def subscribe(self, endpoint): 101 | log.debug("GeminiWSS.subscribe(): Starting Thread for endpoint %s", 102 | endpoint) 103 | self.threads_running[endpoint] = True 104 | t = Thread(target=self._subscription_thread, 105 | args=(endpoint,), name=endpoint) 106 | t.daemon = True 107 | t.start() 108 | self.endpoint_threads[endpoint] = t 109 | 110 | def unsubscribe(self, endpoint): 111 | self.threads_running[endpoint] = False 112 | try: 113 | self.endpoint_threads[endpoint].join(timeout=1) 114 | except TimeoutError: 115 | self.endpoint_threads.pop(endpoint) 116 | 117 | self.garbage_collector() 118 | 119 | def garbage_collector(self): 120 | for endpoint in self.endpoints: 121 | try: 122 | if self.endpoint_threads[endpoint].is_alive(): 123 | continue 124 | else: 125 | self.endpoint_threads.pop(endpoint) 126 | except KeyError: 127 | pass 128 | 129 | def eval_command(self, cmd): 130 | if cmd in self.endpoints: 131 | self.subscribe(cmd) 132 | elif cmd == 'stop': 133 | self.stop() 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /bitex/api/WSS/hitbtc.py: -------------------------------------------------------------------------------- 1 | #import Built-Ins 2 | import logging 3 | from threading import Thread 4 | from queue import Queue, Empty 5 | import json 6 | import time 7 | import hmac 8 | import hashlib 9 | # Import Third-Party 10 | from websocket import create_connection, WebSocketTimeoutException 11 | 12 | # Import Homebrew 13 | from bitex.api.WSS.base import WSSAPI 14 | 15 | # Init Logging Facilities 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class HitBTCWSS(WSSAPI): 20 | def __init__(self, key=None, secret=None): 21 | data_addr = 'ws://api.hitbtc.com:80' 22 | super(HitBTCWSS, self).__init__(data_addr, 'HitBTC') 23 | self.trader_addr = 'ws://api.hitbtc.com:8080' 24 | 25 | self.data_thread = None 26 | self.supervisor_thread = None 27 | self.trade_thread = None 28 | 29 | self.key = key 30 | self.secret = secret 31 | 32 | self.trade_command_q = Queue() 33 | 34 | def start(self, duplex=False): 35 | super(HitBTCWSS, self).start() 36 | 37 | if duplex: 38 | self.trade_thread = Thread(target=self._trade_thread, 39 | name='Trader Thread') 40 | self.trade_thread.daemon = True 41 | self.trade_thread.start() 42 | 43 | self.data_thread = Thread(target=self._data_thread, name='Data Thread') 44 | self.data_thread.daemon = True 45 | self.data_thread.start() 46 | 47 | def stop(self): 48 | super(HitBTCWSS, self).stop() 49 | self.data_thread.join() 50 | if self.trade_thread: 51 | self.trade_thread.join() 52 | 53 | def eval_command(self, cmd): 54 | if cmd == 'restart_data': 55 | self.data_thread.join() 56 | self.data_thread = Thread(target=self._data_thread, 57 | name='Data Thread') 58 | self.data_thread.start() 59 | 60 | def _data_thread(self): 61 | try: 62 | conn = create_connection(self.addr) 63 | except Exception: 64 | self._controller_q.put('restart_data') 65 | return 66 | 67 | while self.running: 68 | try: 69 | data = conn.recv() 70 | data = json.loads(data) 71 | except WebSocketTimeoutException: 72 | self._controller_q.put('restart_data') 73 | return 74 | try: 75 | pair = data['MarketDataIncrementalRefresh']['symbol'] 76 | endpoint = 'MarketDataIncrementalRefresh' 77 | except KeyError: 78 | pair = data['MarketDataSnapshotFullRefresh']['symbol'] 79 | endpoint = 'MarketDataSnapshotFullRefresh' 80 | self.data_q.put((endpoint, pair, data[endpoint], time.time())) 81 | 82 | def _trade_thread(self): 83 | try: 84 | conn = create_connection(self.trader_addr) 85 | except Exception: 86 | log.exception('Trader Thread Error!') 87 | self._controller_q.put('restart_trader') 88 | return 89 | 90 | while self.running: 91 | try: 92 | data = conn.recv() 93 | except WebSocketTimeoutException: 94 | self._controller_q.put('restart_data') 95 | return 96 | self.data_q.put(json.loads(data)) 97 | 98 | try: 99 | payload = self.trade_command_q.get() 100 | except Empty: 101 | continue 102 | 103 | try: 104 | conn.send(self.sign(payload)) 105 | except (WebSocketTimeoutException, ConnectionResetError): 106 | continue 107 | 108 | def sign(self, payload): 109 | """ 110 | Signature method which wraps signature and nonce parameters around a 111 | payload dictionary. 112 | :param payload: 113 | :return: 114 | """ 115 | nonce = str(int(time.time() * 1000)) 116 | package = {'apikey': self.key, 117 | 'message': {'nonce': nonce, 'payload': payload}} 118 | 119 | signature = hmac.new(self.secret, json.dumps(payload).hexdigest, 120 | hashlib.sha512).hexdigest() 121 | package['signature'] = signature 122 | 123 | return json.dumps(package) 124 | 125 | def send(self, payload, auth=False): 126 | pkg = self.sign(payload) if auth else payload 127 | self.trade_command_q.put(pkg) 128 | -------------------------------------------------------------------------------- /bitex/api/WSS/okcoin.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | import json 4 | import threading 5 | import time 6 | 7 | # Import Third-Party 8 | from websocket import create_connection, WebSocketTimeoutException 9 | import requests 10 | 11 | # Import Homebrew 12 | from bitex.api.WSS.base import WSSAPI 13 | 14 | # Init Logging Facilities 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | class OKCoinWSS(WSSAPI): 19 | def __init__(self): 20 | super(OKCoinWSS, self).__init__('wss://real.okcoin.com:10440/websocket/okcoinapi ', 21 | 'OKCoin') 22 | self.conn = None 23 | 24 | self.pairs = ['BTC', 'LTC'] 25 | self._data_thread = None 26 | 27 | def start(self): 28 | super(OKCoinWSS, self).start() 29 | 30 | self._data_thread = threading.Thread(target=self._process_data) 31 | self._data_thread.daemon = True 32 | self._data_thread.start() 33 | 34 | def stop(self): 35 | super(OKCoinWSS, self).stop() 36 | 37 | self._data_thread.join() 38 | 39 | def _process_data(self): 40 | self.conn = create_connection(self.addr, timeout=4) 41 | for pair in self.pairs: 42 | payload = [{'event': 'addChannel', 43 | 'channel': 'ok_sub_spotusd_%s_ticker' % pair}, 44 | {'event': 'addChannel', 45 | 'channel': 'ok_sub_spotusd_%s_depth_60' % pair}, 46 | {'event': 'addChannel', 47 | 'channel': 'ok_sub_spotusd_%s_trades' % pair}, 48 | {'event': 'addChannel', 49 | 'channel': 'ok_sub_spotusd_%s_kline_1min' % pair}] 50 | log.debug(payload) 51 | self.conn.send(json.dumps(payload)) 52 | while self.running: 53 | try: 54 | data = json.loads(self.conn.recv()) 55 | except (WebSocketTimeoutException, ConnectionResetError): 56 | self._controller_q.put('restart') 57 | 58 | if 'data' in data: 59 | pair = ''.join(data['channel'].split('spot')[1].split('_')[:2]).upper() 60 | self.data_q.put((data['channel'], pair, data['data'], 61 | time.time())) 62 | else: 63 | log.debug(data) 64 | self.conn = None -------------------------------------------------------------------------------- /bitex/api/WSS/poloniex.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import signal 3 | import logging 4 | import multiprocessing as mp 5 | import time 6 | 7 | # Import Third-Party 8 | from autobahn.asyncio.wamp import ApplicationRunner, ApplicationSession 9 | from asyncio import coroutine, get_event_loop 10 | import requests 11 | 12 | # Import Homebrew 13 | from bitex.api.WSS.base import WSSAPI 14 | 15 | # Init Logging Facilities 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class PoloniexSession(ApplicationSession): 20 | 21 | @coroutine 22 | def onJoin(self, *args, **kwargs): 23 | channel = self.config.extra['channel'] 24 | 25 | def onTicker(*args, **kwargs): 26 | self.config.extra['queue'].put((channel, (args, kwargs, time.time()))) 27 | 28 | if self.config.extra['is_killed'].is_set(): 29 | raise KeyboardInterrupt() 30 | try: 31 | yield from self.subscribe(onTicker, self.config.extra['channel']) 32 | 33 | except Exception as e: 34 | raise 35 | 36 | 37 | class PlnxEndpoint(mp.Process): 38 | def __init__(self, endpoint, q, **kwargs): 39 | super(PlnxEndpoint, self).__init__(name='%s Endpoint Process' % 40 | endpoint, **kwargs) 41 | self.endpoint = endpoint 42 | self.q = q 43 | self.is_killed = mp.Event() 44 | 45 | def run(self): 46 | self.runner = ApplicationRunner("wss://api.poloniex.com:443", 'realm1', 47 | extra={'channel': self.endpoint, 48 | 'queue': self.q, 49 | 'is_killed': self.is_killed}) 50 | self.runner.run(PoloniexSession) 51 | 52 | def join(self, *args, **kwargs): 53 | self.is_killed.set() 54 | super(PlnxEndpoint, self).join(*args, **kwargs) 55 | 56 | 57 | class PoloniexWSS(WSSAPI): 58 | def __init__(self, endpoints=None): 59 | super(PoloniexWSS, self).__init__(None, 'Poloniex') 60 | self.data_q = mp.Queue() 61 | self.connections = {} 62 | if endpoints: 63 | self.endpoints = endpoints 64 | else: 65 | r = requests.get('https://poloniex.com/public?command=returnTicker') 66 | self.endpoints = list(r.json().keys()) 67 | self.endpoints.append('ticker') 68 | 69 | for endpoint in self.endpoints: 70 | self.connections[endpoint] = PlnxEndpoint(endpoint, self.data_q) 71 | 72 | def start(self): 73 | super(PoloniexWSS, self).start() 74 | for conn in self.connections: 75 | self.connections[conn].start() 76 | 77 | def stop(self): 78 | for conn in self.connections: 79 | self.connections[conn].join() 80 | super(PoloniexWSS, self).stop() 81 | 82 | 83 | if __name__ == "__main__": 84 | 85 | wss = PoloniexWSS() 86 | wss.start() 87 | time.sleep(5) 88 | wss.stop() 89 | while not wss.data_q.empty(): 90 | print(wss.data_q.get()) -------------------------------------------------------------------------------- /bitex/api/__init__.py: -------------------------------------------------------------------------------- 1 | import bitex.api.REST 2 | import bitex.api.WSS 3 | 4 | -------------------------------------------------------------------------------- /bitex/api/response.py: -------------------------------------------------------------------------------- 1 | # Import Third-Party 2 | from requests import Response 3 | 4 | 5 | class APIResponse(Response): 6 | __attrs__ = ['_content', 'status_code', 'headers', 'url', 'history', 7 | 'encoding', 'reason', 'cookies', 'elapsed', 'request', 8 | '_formatted'] 9 | 10 | def __init__(self, req_response, formatted_json=None): 11 | for k, v in req_response.__dict__.items(): 12 | self.__dict__[k] = v 13 | self._formatted = formatted_json 14 | 15 | @property 16 | def formatted(self): 17 | return self._formatted 18 | 19 | @formatted.setter 20 | def formatted(self, val): 21 | self._formatted = val 22 | 23 | 24 | if __name__ == '__main__': 25 | from bitex import Kraken 26 | 27 | k = Kraken() 28 | resp = k.ticker('XXBTZEUR') 29 | print(resp.formatted) 30 | print(resp.json()) -------------------------------------------------------------------------------- /bitex/formatters/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/bitex/formatters/__init__.py -------------------------------------------------------------------------------- /bitex/formatters/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | Base Class for formatters. Does nothing with passed data by default; 3 | Children should implement formatters as necessary 4 | """ 5 | 6 | # Import Built-Ins 7 | import logging 8 | from abc import ABCMeta, abstractmethod 9 | # Import Third-Party 10 | 11 | # Import Homebrew 12 | 13 | # Init Logging Facilities 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | class Formatter: 18 | """ 19 | ABC Class to provide formatters for `bitex.utils.return_api_response()`. 20 | """ 21 | def __init__(self): 22 | pass 23 | 24 | @staticmethod 25 | def format_pair(input_pair): 26 | """ 27 | Returns the pair properly formatted for the exchange's API. 28 | :param input_pair: str 29 | :return: str 30 | """ 31 | return input_pair 32 | 33 | @staticmethod 34 | def ticker(data, *args, **kwargs): 35 | """ 36 | Returns list of ticker data in following format: 37 | [bid_price, ask_price, high, low, open, close, last, 24h_vol, ts] 38 | :param data: requests.response() obj 39 | :param args: 40 | :param kwargs: 41 | :return: list 42 | """ 43 | return data 44 | 45 | @staticmethod 46 | def order_book(data, *args, **kwargs): 47 | """ 48 | Returns dict of lists of lists of quotes in format [ts, price, size] 49 | ex.: 50 | {'bids': [['1480941692', '0.014', '10'], 51 | ['1480941690', '0.013', '0.66'], 52 | ['1480941688', '0.012', '3']], 53 | 'asks': [['1480941691', '0.015', '1'], 54 | ['1480941650', '0.016', '0.67'], 55 | ['1480941678', '0.017', '23']]} 56 | :param data: requests.response() obj 57 | :param args: 58 | :param kwargs: 59 | :return: dict 60 | """ 61 | return data 62 | 63 | @staticmethod 64 | def trades(data, *args, **kwargs): 65 | """ 66 | Returns list of trades in format [ts, price, size, side] 67 | ex.: 68 | [['1480941692', '0.014', '10', 'sell'], 69 | ['1480941690', '0.013', '0.66', 'buy'], 70 | ['1480941688', '0.012', '3', 'buy']] 71 | :param data: requests.response() obj 72 | :param args: 73 | :param kwargs: 74 | :return: list 75 | """ 76 | return data 77 | 78 | @staticmethod 79 | def order(data, *args, **kwargs): 80 | """ 81 | Returns the order id as str if successful, else "" 82 | :param data: requests.response() obj 83 | :param args: 84 | :param kwargs: 85 | :return: str 86 | """ 87 | return data 88 | 89 | @staticmethod 90 | def order_status(data, *args, **kwargs): 91 | """ 92 | Returns True if it exists, False if it doesn't exist 93 | :param data: requests.response() obj 94 | :param args: 95 | :param kwargs: 96 | :return: bool 97 | """ 98 | return data 99 | 100 | @staticmethod 101 | def cancel(data, *args, **kwargs): 102 | """ 103 | returns True if it was cancelled successfully, else False 104 | :param data: requests.response() obj 105 | :param args: 106 | :param kwargs: 107 | :return: bool 108 | """ 109 | return data 110 | 111 | @staticmethod 112 | def balance(data, *args, **kwargs): 113 | """ 114 | Returns dict of available balances, with currency names as keys - this ignores 115 | any amount already involved in a trade (i.e. margin) 116 | ex.: 117 | {'BTC': '12.04', 'LTC': '444.12'} 118 | :param data: requests.response() obj 119 | :param args: 120 | :param kwargs: 121 | :return: dict 122 | """ 123 | return data 124 | 125 | @staticmethod 126 | def withdraw(data, *args, **kwargs): 127 | """ 128 | Returns a list giving details of success and transaction details, or failure 129 | and reason thererof 130 | ex.: 131 | [True, currency, amount, target_address, txid] 132 | [False, 'Reason for failure/ error message'] 133 | :param data: requests.response() obj 134 | :param args: 135 | :param kwargs: 136 | :return: list 137 | """ 138 | return data 139 | 140 | @staticmethod 141 | def deposit(data, *args, **kwargs): 142 | """ 143 | Returns deposit address as str 144 | :param data: requests.response() obj 145 | :param args: 146 | :param kwargs: 147 | :return: str 148 | """ 149 | return data 150 | -------------------------------------------------------------------------------- /bitex/formatters/bitfinex.py: -------------------------------------------------------------------------------- 1 | # Import Built-ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class BtfxFormatter(Formatter): 14 | 15 | @staticmethod 16 | def ticker(data, *args, **kwargs): 17 | return (data['bid'], data['ask'], data['high'], data['low'], None, None, 18 | data['last_price'], data['volume'], data['timestamp']) 19 | 20 | @staticmethod 21 | def order(data, *args, **kwargs): 22 | try: 23 | return data['order_id'] 24 | except KeyError: 25 | return False 26 | 27 | @staticmethod 28 | def cancel(data, *args, **kwargs): 29 | return True if 'message' not in data else False 30 | 31 | @staticmethod 32 | def order_status(data, *args, **kwargs): 33 | return data['is_live'] 34 | -------------------------------------------------------------------------------- /bitex/formatters/bitstamp.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | # Init Logging Facilities 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class BtstFormatter(Formatter): 14 | 15 | @staticmethod 16 | def ticker(data, *args, **kwargs): 17 | return (data['bid'], data['ask'], data['high'], data['low'], data['open'], 18 | None, data['last'], data['volume'], data['timestamp']) 19 | 20 | 21 | -------------------------------------------------------------------------------- /bitex/formatters/bittrex.py: -------------------------------------------------------------------------------- 1 | # Import Built-ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | log = logging.getLogger(__name__) 10 | 11 | 12 | class BtrxFormatter(Formatter): 13 | 14 | @staticmethod 15 | def ticker(data, *args, **kwargs): 16 | data = data['result'][0] 17 | return (data['Bid'], data['Ask'], data['High'], data['Low'], None, None, 18 | data['Last'], data['Volume'], data['TimeStamp']) 19 | 20 | @staticmethod 21 | def order(data, *args, **kwargs): 22 | if data['success']: 23 | return data['result']['uuid'] 24 | else: 25 | return False 26 | 27 | @staticmethod 28 | def order_book(data, *args, **kwargs): 29 | if data['success']: 30 | return data['result'] 31 | else: 32 | return None 33 | 34 | @staticmethod 35 | def cancel(data, *args, **kwargs): 36 | return True if data['success'] else False 37 | -------------------------------------------------------------------------------- /bitex/formatters/bter.py: -------------------------------------------------------------------------------- 1 | # Import Built-ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class BterFormatter(Formatter): 14 | 15 | pass -------------------------------------------------------------------------------- /bitex/formatters/ccex.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | # Init Logging Facilities 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class CcexFormatter(Formatter): 14 | 15 | @staticmethod 16 | def ticker(data, *args, **kwargs): 17 | return (data['buy'], data['sell'], data['high'], data['low'], None, 18 | None, data['lastprice'], None, data['updated']) -------------------------------------------------------------------------------- /bitex/formatters/coincheck.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | # Init Logging Facilities 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class CnckFormatter(Formatter): 14 | 15 | @staticmethod 16 | def ticker(data, *args, **kwargs): 17 | return (data['bid'], data['ask'], data['high'], data['low'], None, None, 18 | data['last'], data['volume'], data['timestamp']) -------------------------------------------------------------------------------- /bitex/formatters/cryptopia.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | # Init Logging Facilities 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class CrptFormatter(Formatter): 14 | 15 | @staticmethod 16 | def ticker(data, *args, **kwargs): 17 | return (data['BidPrice'], data['AskPrice'], data['High'], data['Low'], 18 | None, None, data['LastPrice'], None, data['timestamp']) -------------------------------------------------------------------------------- /bitex/formatters/gdax.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | # Init Logging Facilities 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class GdaxFormatter(Formatter): 14 | 15 | @staticmethod 16 | def ticker(data, *args, **kwargs): 17 | return (data['bid'], data['ask'], None, None, None, None, data['price'], 18 | data['volume'], data['time']) -------------------------------------------------------------------------------- /bitex/formatters/gemini.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | # Init Logging Facilities 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class GmniFormatter(Formatter): 14 | 15 | @staticmethod 16 | def ticker(data, *args, **kwargs): 17 | return (data['bid'], data['ask'], None, None, None, None, data['last'], 18 | data['volume'][args[0][:3].upper()], data['volume']['time']) -------------------------------------------------------------------------------- /bitex/formatters/hitbtc.py: -------------------------------------------------------------------------------- 1 | # Import Built-ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class HitBtcFormatter(Formatter): 14 | 15 | pass -------------------------------------------------------------------------------- /bitex/formatters/itbit.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | # Init Logging Facilities 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class itbtFormatter(Formatter): 14 | 15 | @staticmethod 16 | def ticker(data, *args, **kwargs): 17 | return (data['bid'], data['ask'], data['high24h'], data['low24h'], 18 | data['openToday'], None, data['lastPrice'], data['volume24h'], 19 | data['serverTimeUTC']) -------------------------------------------------------------------------------- /bitex/formatters/kraken.py: -------------------------------------------------------------------------------- 1 | # Import Built-ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class KrknFormatter(Formatter): 14 | 15 | @staticmethod 16 | def format_pair(input_pair): 17 | """ 18 | Formats input to conform with kraken pair format. The API expects one of 19 | two formats: 20 | XBTXLT 21 | or 22 | XXBTXLTC 23 | 24 | Where crypto currencies have an X prepended, and fiat currencies have 25 | a Z prepended. Since the API returns the 8 character format, that's what 26 | we will format into as well. 27 | 28 | We expect 6 or 8 character strings, but do not explicitly check for it. 29 | Should the string be of uneven length, we'll split the pair in the middle 30 | like so: 31 | BTC-LTC -> BTC, LTC. 32 | 33 | Furthermore, since Kraken uses 'XBT' as Bitcoins symbol, we look for, and 34 | replace occurrences of 'btc' with 'XBT'. 35 | 36 | In addition there are some exceptions from this rules. Kraken is using 37 | the pairs 'BCHEUR', 'BCHUSD', 'BCHXBT', 'DASHEUR', 'DASHUSD', 'DASHXBT', 38 | 'EOSETH', 'EOSXBT', 'GNOETH', 'GNOXBT' and 'USDTZUSD' as they are. 39 | If the input matches one of this pairs, we just return the uppercase 40 | representation of it. 41 | 42 | :param input_pair: str 43 | :return: str 44 | """ 45 | # There are some exceptions from the general formatting rule 46 | # see https://api.kraken.com/0/public/AssetPairs 47 | format_exceptions = ['BCHEUR', 'BCHUSD', 'BCHXBT', 'DASHEUR', 'DASHUSD', 'DASHXBT', 'EOSETH', 'EOSXBT', 'GNOETH', 'GNOXBT', 'USDTZUSD'] 48 | if input_pair.upper() in format_exceptions: 49 | return input_pair.upper() 50 | 51 | if len(input_pair) % 2 == 0: 52 | base_cur, quote_cur = input_pair[:len(input_pair)//2], input_pair[len(input_pair)//2:] 53 | else: 54 | base_cur, quote_cur = input_pair.split(input_pair[len(input_pair)//2]) 55 | 56 | def add_prefix(input_string): 57 | input_string = input_string.lower() 58 | if any(x in input_string for x in ['usd', 'eur', 'jpy', 'gbp', 'cad']): 59 | # appears to be fiat currency 60 | if not input_string.startswith('z'): 61 | input_string = 'z' + input_string 62 | 63 | else: 64 | # Appears to be Crypto currency 65 | if 'btc' in input_string: 66 | input_string = input_string.replace('btc', 'xbt') 67 | 68 | if not input_string.startswith('x') or len(input_string) == 3: 69 | input_string = 'x' + input_string 70 | return input_string 71 | 72 | base_cur = add_prefix(base_cur) 73 | quote_cur = add_prefix(quote_cur) 74 | 75 | return (base_cur + quote_cur).upper() 76 | 77 | @staticmethod 78 | def ticker(data, *args, **kwargs): 79 | tickers = [] 80 | for k in data['result']: 81 | d = data['result'][k] 82 | tickers.append((d['b'][0], d['a'][0], d['h'][1], d['l'][1], d['o'], 83 | None, d['c'][0], d['v'][1], None)) 84 | if len(tickers) > 1: 85 | return tickers 86 | else: 87 | return tickers[0] 88 | 89 | @staticmethod 90 | def order(data, *args, **kwargs): 91 | if not data['error']: 92 | return data['result']['txid'] 93 | else: 94 | return False 95 | 96 | @staticmethod 97 | def order_book(data, *args, **kwargs): 98 | pair = args[1] 99 | 100 | # There are some exceptions from the general formatting rule 101 | # see https://api.kraken.com/0/public/AssetPairs 102 | format_exceptions = ['BCHEUR', 'BCHUSD', 'BCHXBT', 'DASHEUR', 'DASHUSD', 'DASHXBT', 'EOSETH', 'EOSXBT', 'GNOETH', 'GNOXBT', 'USDTZUSD'] 103 | 104 | forex = ['EUR', 'USD', 'GBP', 'JPY', 'CAD'] 105 | if len(pair) == 6 and pair.upper() not in format_exceptions: 106 | base_cur = pair[:3] 107 | quote_cur = pair[3:] 108 | if base_cur.upper() in forex: 109 | base_cur = 'Z' + base_cur 110 | else: 111 | base_cur = 'X' + base_cur 112 | 113 | if quote_cur.upper() in forex: 114 | quote_cur = 'Z' + quote_cur 115 | else: 116 | quote_cur = 'X' + quote_cur 117 | return data['result'][base_cur+quote_cur] 118 | else: 119 | return data['result'][pair] 120 | 121 | @staticmethod 122 | def cancel(data, *args, **kwargs): 123 | if int(data['result']['count']) == 1: 124 | return True 125 | else: 126 | return False 127 | -------------------------------------------------------------------------------- /bitex/formatters/okcoin.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | # Init Logging Facilities 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class OkcnFormatter(Formatter): 14 | 15 | @staticmethod 16 | def ticker(data, *args, **kwargs): 17 | date = data['date'] 18 | data = data['ticker'] 19 | return (data['buy'], data['sell'], data['high'], data['low'], 20 | None, None, data['last'], data['vol'], date) 21 | -------------------------------------------------------------------------------- /bitex/formatters/poloniex.py: -------------------------------------------------------------------------------- 1 | # Import Built-ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class PlnxFormatter(Formatter): 14 | 15 | @staticmethod 16 | def ticker(data, *args, **kwargs): 17 | data = data[args[0]] 18 | return (data['highestBid'], data['lowestAsk'], None, None, None, None, 19 | data['last'], None, None) 20 | 21 | @staticmethod 22 | def order(data, *args, **kwargs): 23 | try: 24 | return data['orderNumber'] 25 | except KeyError: 26 | return False 27 | 28 | @staticmethod 29 | def cancel(data, *args, **kwargs): 30 | return True if data['success'] else False 31 | -------------------------------------------------------------------------------- /bitex/formatters/quadriga.py: -------------------------------------------------------------------------------- 1 | # Import Built-ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class QuadrigaCXFormatter(Formatter): 14 | 15 | @staticmethod 16 | def ticker(data, *args, **kwargs): 17 | return (data['bid'], data['ask'], data['high'], data['low'], 18 | None, None, data['last'], data['volume'], None) 19 | 20 | @staticmethod 21 | def order(data, *args, **kwargs): 22 | return data 23 | 24 | @staticmethod 25 | def cancel(data, *args, **kwargs): 26 | return data 27 | 28 | @staticmethod 29 | def order_status(data, *args, **kwargs): 30 | return data 31 | -------------------------------------------------------------------------------- /bitex/formatters/quoine.py: -------------------------------------------------------------------------------- 1 | # Import Built-ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class QoinFormatter(Formatter): 14 | 15 | @staticmethod 16 | def ticker(data, *args, **kwargs): 17 | return (data['market_bid'], data['market_ask'], data['high_market_ask'], 18 | data['low_market_bid'], None, None, data['last_traded_price'], 19 | data['volume_24h'], None) 20 | 21 | @staticmethod 22 | def order(data, *args, **kwargs): 23 | return data 24 | 25 | @staticmethod 26 | def cancel(data, *args, **kwargs): 27 | return data 28 | 29 | @staticmethod 30 | def order_status(data, *args, **kwargs): 31 | return data 32 | -------------------------------------------------------------------------------- /bitex/formatters/rocktrading.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | # Init Logging Facilities 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class RockFormatter(Formatter): 14 | 15 | @staticmethod 16 | def ticker(data, *args, **kwargs): 17 | return (data['bid'], data['ask'], data['high'], data['low'], 18 | data['open'], data['close'], data['last'], 19 | data['volume_traded'], data['date']) -------------------------------------------------------------------------------- /bitex/formatters/vaultoro.py: -------------------------------------------------------------------------------- 1 | # Import Built-ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class VaultoroFormatter(Formatter): 14 | 15 | pass 16 | -------------------------------------------------------------------------------- /bitex/formatters/yunbi.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.base import Formatter 8 | 9 | # Init Logging Facilities 10 | log = logging.getLogger(__name__) 11 | 12 | 13 | class YnbiFormatter(Formatter): 14 | 15 | @staticmethod 16 | def ticker(data, *args, **kwargs): 17 | date = data['at'] 18 | data = data['ticker'] 19 | return (data['buy'], data['sell'], data['high'], data['low'], 20 | None, None, data['last'], data['vol'], date) -------------------------------------------------------------------------------- /bitex/interfaces/__init__.py: -------------------------------------------------------------------------------- 1 | from bitex.interfaces.bitfinex import Bitfinex 2 | from bitex.interfaces.bitstamp import Bitstamp 3 | from bitex.interfaces.bittrex import Bittrex 4 | from bitex.interfaces.ccex import CCEX 5 | from bitex.interfaces.coincheck import Coincheck 6 | from bitex.interfaces.cryptopia import Cryptopia 7 | from bitex.interfaces.gemini import Gemini 8 | from bitex.interfaces.itbit import ItBit 9 | from bitex.interfaces.kraken import Kraken 10 | from bitex.interfaces.okcoin import OKCoin 11 | from bitex.interfaces.rocktrading import RockTradingLtd 12 | from bitex.interfaces.yunbi import Yunbi 13 | from bitex.interfaces.poloniex import Poloniex 14 | from bitex.interfaces.quoine import Quoine 15 | from bitex.interfaces.quadriga import QuadrigaCX 16 | from bitex.interfaces.gdax import GDAX 17 | from bitex.interfaces.vaultoro import Vaultoro 18 | from bitex.interfaces.hitbtc import HitBtc 19 | from bitex.interfaces.bter import Bter 20 | -------------------------------------------------------------------------------- /bitex/interfaces/bitfinex.py: -------------------------------------------------------------------------------- 1 | """ 2 | http://docs.bitfinex.com/ 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import BitfinexREST 12 | from bitex.api.WSS.bitfinex import BitfinexWSS 13 | from bitex.utils import return_api_response 14 | from bitex.formatters.bitfinex import BtfxFormatter as fmt 15 | # Init Logging Facilities 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class Bitfinex(BitfinexREST): 20 | def __init__(self, key='', secret='', key_file='', websocket=False): 21 | super(Bitfinex, self).__init__(key, secret) 22 | if key_file: 23 | self.load_key(key_file) 24 | if websocket: 25 | self.wss = BitfinexWSS() 26 | self.wss.start() 27 | else: 28 | self.wss = None 29 | 30 | def public_query(self, endpoint, **kwargs): 31 | return self.query('GET', endpoint, **kwargs) 32 | 33 | def private_query(self, endpoint, **kwargs): 34 | return self.query('POST', endpoint, authenticate=True, **kwargs) 35 | 36 | """ 37 | BitEx Standardized Methods 38 | """ 39 | @return_api_response(fmt.order_book) 40 | def order_book(self, pair, **kwargs): 41 | return self.public_query('book/%s' % pair, params=kwargs) 42 | 43 | @return_api_response(fmt.ticker) 44 | def ticker(self, pair, **kwargs): 45 | return self.public_query('pubticker/%s' % pair, params=kwargs) 46 | 47 | @return_api_response(fmt.trades) 48 | def trades(self, pair, **kwargs): 49 | return self.public_query('trades/%s' % pair, params=kwargs) 50 | 51 | def _place_order(self, pair, size, price, side, replace, **kwargs): 52 | q = {'symbol': pair, 'amount': size, 'price': price, 'side': side, 53 | 'type': 'exchange limit'} 54 | q.update(kwargs) 55 | if replace: 56 | return self.private_query('order/cancel/replace', params=q) 57 | else: 58 | return self.private_query('order/new', params=q) 59 | 60 | @return_api_response(fmt.order) 61 | def bid(self, pair, price, size, replace=False, **kwargs): 62 | return self._place_order(pair, size, price, 'buy', replace=replace, 63 | **kwargs) 64 | 65 | @return_api_response(fmt.order) 66 | def ask(self, pair, price, size, replace=False, **kwargs): 67 | return self._place_order(pair, str(size), str(price), 'sell', 68 | replace=replace, **kwargs) 69 | 70 | @return_api_response(fmt.cancel) 71 | def cancel_order(self, order_id, all=False, **kwargs): 72 | 73 | q = {'order_id': int(order_id)} 74 | q.update(kwargs) 75 | if not all: 76 | return self.private_query('order/cancel', params=q) 77 | else: 78 | endpoint = 'order/cancel/all' 79 | return self.private_query(endpoint) 80 | 81 | @return_api_response(fmt.order_status) 82 | def order(self, order_id, **kwargs): 83 | q = {'order_id': order_id} 84 | q.update(kwargs) 85 | return self.private_query('order/status', params=q) 86 | 87 | @return_api_response(fmt.balance) 88 | def balance(self, **kwargs): 89 | return self.private_query('balances', params=kwargs) 90 | 91 | @return_api_response(fmt.withdraw) 92 | def withdraw(self, size, tar_addr, **kwargs): 93 | q = {'withdraw_type': kwargs.pop('withdraw_type'), 94 | 'walletselected': kwargs.pop('walletselected'), 95 | 'amount': size, 'address': tar_addr} 96 | q.update(kwargs) 97 | return self.private_query('withdraw', params=q) 98 | 99 | @return_api_response(fmt.deposit) 100 | def deposit_address(self, **kwargs): 101 | q = {} 102 | q.update(kwargs) 103 | return self.private_query('deposit/new', params=kwargs) 104 | 105 | """ 106 | Exchange Specific Methods 107 | """ 108 | 109 | @return_api_response(None) 110 | def statistics(self, pair): 111 | return self.public_query('stats/%s' % pair) 112 | 113 | @return_api_response(None) 114 | def funding_book(self, currency, **kwargs): 115 | return self.public_query('lendbook/%s' % currency, params=kwargs) 116 | 117 | @return_api_response(None) 118 | def lends(self, currency, **kwargs): 119 | return self.public_query('lends/%s' % currency, params=kwargs) 120 | 121 | @return_api_response(None) 122 | def pairs(self, details=False): 123 | if details: 124 | return self.public_query('symbols_details') 125 | else: 126 | return self.public_query('symbols') 127 | 128 | @return_api_response(None) 129 | def fees(self): 130 | return self.private_query('account_infos') 131 | 132 | @return_api_response(None) 133 | def orders(self): 134 | return self.private_query('orders') 135 | 136 | @return_api_response(None) 137 | def balance_history(self, currency, **kwargs): 138 | q = {'currency': currency} 139 | q.update(kwargs) 140 | return self.private_query('history/movements', params=q) 141 | 142 | @return_api_response(None) 143 | def trade_history(self, pair, since, **kwargs): 144 | q = {'symbol': pair, 'timestamp': since} 145 | q.update(kwargs) 146 | return self.private_query('mytrades', params=q) 147 | 148 | @return_api_response(None) 149 | def positions(self): 150 | return self.private_query('positions') 151 | 152 | @return_api_response(None) 153 | def credits(self): 154 | return self.private_query('credits') 155 | 156 | @return_api_response(None) 157 | def new_offer(self, currency, amount, rate, period, direction, **kwargs): 158 | q = {'currency': currency, 'amount': amount, 'rate': rate, 'period': period, 'period': direction } 159 | q.update(kwargs) 160 | return self.private_query('offer/new', params=q) 161 | 162 | @return_api_response(None) 163 | def cancel_offer(self, id, **kwargs): 164 | q = {'id': id} 165 | q.update(kwargs) 166 | return self.private_query('offer/cancel', params=q) 167 | -------------------------------------------------------------------------------- /bitex/interfaces/bitstamp.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://www.bitstamp.net/api/ 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import BitstampREST 12 | from bitex.api.WSS.bitstamp import BitstampWSS 13 | from bitex.utils import return_api_response 14 | from bitex.formatters.bitstamp import BtstFormatter as fmt 15 | 16 | # Init Logging Facilities 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | class Bitstamp(BitstampREST): 21 | def __init__(self, key='', secret='', key_file='', websocket=False): 22 | super(Bitstamp, self).__init__(key, secret) 23 | if key_file: 24 | self.load_key(key_file) 25 | 26 | if websocket: 27 | self.wss = BitstampWSS() 28 | self.wss.start() 29 | else: 30 | self.wss = None 31 | 32 | def public_query(self, endpoint, **kwargs): 33 | return self.query('GET', endpoint, **kwargs) 34 | 35 | def private_query(self, endpoint, **kwargs): 36 | return self.query('POST', endpoint, authenticate=True, **kwargs) 37 | 38 | """ 39 | BitEx Standardized Methods 40 | """ 41 | 42 | @return_api_response(fmt.ticker) 43 | def ticker(self, pair, **kwargs): 44 | return self.public_query('v2/ticker/%s/' % pair, params=kwargs) 45 | 46 | @return_api_response(fmt.order_book) 47 | def order_book(self, pair, **kwargs): 48 | return self.public_query('v2/order_book/%s' % pair, params=kwargs) 49 | 50 | @return_api_response(fmt.trades) 51 | def trades(self, pair, **kwargs): 52 | return self.public_query('v2/transactions/%s' % pair, params=kwargs) 53 | 54 | @return_api_response(fmt.order) 55 | def bid(self, pair, price, size, **kwargs): 56 | q = {'amount': size, 'price': price} 57 | q.update(kwargs) 58 | return self.private_query('v2/buy/%s/' % pair, params=q) 59 | 60 | @return_api_response(fmt.order) 61 | def ask(self, pair, price, size, **kwargs): 62 | q = {'amount': size, 'price': price} 63 | q.update(kwargs) 64 | return self.private_query('v2/sell/%s/' % pair, params=q) 65 | 66 | @return_api_response(fmt.cancel) 67 | def cancel_order(self, order_id, all=False, **kwargs): 68 | return self.private_query('cancel_order/', params={'id': order_id}) 69 | 70 | @return_api_response(fmt.order_status) 71 | def order(self, order_id, **kwargs): 72 | q = {'id': order_id} 73 | q.update(kwargs) 74 | return self.private_query('order_status/', params=q) 75 | 76 | @return_api_response(fmt.balance) 77 | def balance(self, **kwargs): 78 | return self.private_query('v2/balance/') 79 | 80 | @return_api_response(fmt.withdraw) 81 | def withdraw(self, size, tar_addr, **kwargs): 82 | q = {'amount': size, 'address': tar_addr} 83 | q.update(kwargs) 84 | return self.private_query('bitcoin_withdrawal/', params=q) 85 | 86 | @return_api_response(fmt.deposit) 87 | def deposit_address(self, **kwargs): 88 | return self.private_query('bitcoin_deposit_address/') 89 | 90 | """ 91 | Exchange Specific Methods 92 | """ 93 | 94 | @return_api_response(None) 95 | def hourly_ticker(self, pair): 96 | return self.public_query('v2/ticker_hour/%s' % pair) 97 | 98 | @return_api_response(None) 99 | def eurusd_rate(self): 100 | return self.public_query('eur_usd') 101 | 102 | def pairs(self): 103 | return ['btcusd', 'btceur', 'eurusd'] 104 | -------------------------------------------------------------------------------- /bitex/interfaces/bittrex.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://bittrex.com/Home/Api 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import BittrexREST 12 | from bitex.utils import return_api_response 13 | from bitex.formatters.bittrex import BtrxFormatter as fmt 14 | # Init Logging Facilities 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | class Bittrex(BittrexREST): 19 | def __init__(self, key='', secret='', key_file=''): 20 | super(Bittrex, self).__init__(key, secret) 21 | if key_file: 22 | self.load_key(key_file) 23 | 24 | def public_query(self, endpoint, **kwargs): 25 | return self.query('GET', 'public/' + endpoint, **kwargs) 26 | 27 | def private_query(self, endpoint, **kwargs): 28 | return self.query('GET', endpoint, authenticate=True, **kwargs) 29 | 30 | """ 31 | BitEx Standardized Methods 32 | """ 33 | 34 | @return_api_response(fmt.ticker) 35 | def ticker(self, pair, **kwargs): 36 | q = {'market': pair} 37 | q.update(kwargs) 38 | return self.public_query('getmarketsummary', params=q) 39 | 40 | @return_api_response(fmt.order_book) 41 | def order_book(self, pair, side='both', **kwargs): 42 | q = {'market': pair, 'type': side} 43 | q.update(kwargs) 44 | return self.public_query('getorderbook', params=q) 45 | 46 | @return_api_response(fmt.trades) 47 | def trades(self, pair, **kwargs): 48 | q = {'market': pair} 49 | q.update(kwargs) 50 | return self.public_query('getmarkethistory', params=q) 51 | 52 | @return_api_response(fmt.order) 53 | def bid(self, pair, price, vol, market=False, **kwargs): 54 | q = {'market': pair, 'rate': price, 'quantity': vol} 55 | q.update(kwargs) 56 | if market: 57 | # send market order 58 | return self.private_query('market/buymarket', params=q) 59 | else: 60 | # send limit order 61 | return self.private_query('market/buylimit', params=q) 62 | 63 | @return_api_response(fmt.order) 64 | def ask(self, pair, price, vol, market=False, **kwargs): 65 | q = {'market': pair, 'rate': price, 'quantity': vol} 66 | q.update(kwargs) 67 | if market: 68 | # send market order 69 | return self.private_query('market/sellmarket', params=q) 70 | else: 71 | # send limit order 72 | return self.private_query('market/selllimit', params=q) 73 | 74 | @return_api_response(fmt.cancel) 75 | def cancel_order(self, txid): 76 | q = {'uuid': txid} 77 | return self.private_query('market/cancel', params=q) 78 | 79 | @return_api_response(fmt.order_status) 80 | def order(self, order_id, **kwargs): 81 | q = {'uuid': order_id} 82 | q.update(kwargs) 83 | return self.private_query('account/getorder', params=q) 84 | 85 | @return_api_response(fmt.balance) 86 | def balance(self): 87 | return self.private_query('account/getbalances') 88 | 89 | @return_api_response(fmt.withdraw) 90 | def withdraw(self, size, tar_addr, **kwargs): 91 | q = {'quantity': size, 'address': tar_addr} 92 | q.update(kwargs) 93 | return self.private_query('account/withdraw', params=q) 94 | 95 | @return_api_response(fmt.deposit) 96 | def deposit_address(self, **kwargs): 97 | return self.private_query('account/getdepositaddress', params=kwargs) 98 | 99 | """ 100 | Exchange Specific Methods 101 | """ 102 | 103 | @return_api_response(None) 104 | def pairs(self): 105 | return self.public_query('getmarkets') 106 | 107 | @return_api_response(None) 108 | def currencies(self): 109 | return self.public_query('getcurrencies') 110 | 111 | @return_api_response(None) 112 | def statistics(self, pair=None): 113 | if pair: 114 | return self.public_query('getmarketsummary', params={'market': pair}) 115 | else: 116 | return self.public_query('getmarketsummaries') 117 | -------------------------------------------------------------------------------- /bitex/interfaces/bter.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://bter.com/api 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import BterREST 12 | from bitex.utils import return_api_response 13 | from bitex.formatters.bter import BterFormatter as fmt 14 | 15 | # Init Logging Facilities 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class Bter(BterREST): 20 | def __init__(self, key='', secret='', key_file=''): 21 | super(Bter, self).__init__(key, secret) 22 | if key_file: 23 | self.load_key(key_file) 24 | 25 | def public_query(self, endpoint, **kwargs): 26 | return self.query('GET', endpoint, **kwargs) 27 | 28 | def private_query(self, endpoint, method_verb=None, **kwargs): 29 | if not method_verb: 30 | method_verb = 'POST' 31 | endpoint = 'private/' + endpoint 32 | return self.query(method_verb, endpoint, authenticate=True, **kwargs) 33 | 34 | """ 35 | BitEx Standardized Methods 36 | """ 37 | 38 | @return_api_response(fmt.order_book) 39 | def order_book(self, pair, **kwargs): 40 | return self.public_query('depth/%s' % pair, params=kwargs) 41 | 42 | @return_api_response(fmt.ticker) 43 | def ticker(self, pair, **kwargs): 44 | if pair == 'all': 45 | return self.public_query('tickers', params=kwargs) 46 | else: 47 | return self.public_query('ticker/%s' % pair, params=kwargs) 48 | 49 | @return_api_response(fmt.trades) 50 | def trades(self, pair, **kwargs): 51 | return self.public_query('trade/%s' % pair, params=kwargs) 52 | 53 | def _place_order(self, pair, size, price, side, **kwargs): 54 | q = {'pair': pair, 'type': side, 'price': price, 'amount': size} 55 | q.update(kwargs) 56 | return self.private_query('placeorder', params=q) 57 | 58 | @return_api_response(fmt.order) 59 | def bid(self, pair, price, size, **kwargs): 60 | return self._place_order(pair, size, price, 'BUY', **kwargs) 61 | 62 | @return_api_response(fmt.order) 63 | def ask(self, pair, price, size, **kwargs): 64 | return self._place_order(pair, size, price, 'SELL', **kwargs) 65 | 66 | @return_api_response(fmt.cancel) 67 | def cancel_order(self, order_id, **kwargs): 68 | q = {'order_id': order_id} 69 | q.update(kwargs) 70 | return self.private_query('cancelorder', params=q) 71 | 72 | @return_api_response(fmt.order_status) 73 | def order(self, order_id, **kwargs): 74 | q = {'order_id': order_id} 75 | q.update(kwargs) 76 | return self.private_query('getorder', params=q) 77 | 78 | @return_api_response(fmt.balance) 79 | def balance(self, **kwargs): 80 | return self.private_query('getfunds', params=kwargs) 81 | 82 | @return_api_response(fmt.withdraw) 83 | def withdraw(self, size, tar_addr, **kwargs): 84 | raise NotImplementedError 85 | 86 | @return_api_response(fmt.deposit) 87 | def deposit_address(self, **kwargs): 88 | raise NotImplementedError -------------------------------------------------------------------------------- /bitex/interfaces/ccex.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://c-cex.com/?id=api 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import CCEXRest 12 | from bitex.utils import return_api_response 13 | from bitex.formatters.ccex import CcexFormatter as fmt 14 | 15 | # Init Logging Facilities 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class CCEX(CCEXRest): 20 | def __init__(self, key='', secret='', key_file=''): 21 | super(CCEX, self).__init__(key, secret) 22 | if key_file: 23 | self.load_key(key_file) 24 | print(self.uri) 25 | 26 | def public_query(self, endpoint, **kwargs): 27 | return self.query('GET', 'api_pub.html?a=' + endpoint, **kwargs) 28 | 29 | def private_query(self, endpoint, **kwargs): 30 | return self.query('POST', 'api.html?a=' + endpoint, authenticate=True, **kwargs) 31 | 32 | """ 33 | BitEx Standardized Methods 34 | """ 35 | 36 | @return_api_response(fmt.ticker) 37 | def ticker(self, pair, **kwargs): 38 | return self.public_query('%s.json' % pair, params=kwargs) 39 | 40 | @return_api_response(fmt.order_book) 41 | def order_book(self, pair, type='both', **kwargs): 42 | q = {'market': pair, 'type': type} 43 | q.update(kwargs) 44 | return self.public_query('getorderbook', params=q) 45 | 46 | @return_api_response(fmt.trades) 47 | def trades(self, pair, **kwargs): 48 | q = {'market': pair} 49 | q.update(kwargs) 50 | return self.public_query('getmarkethistory', 51 | params=q) 52 | 53 | @return_api_response(fmt.order) 54 | def bid(self, pair, price, size, **kwargs): 55 | q = {'market': pair, 'rate': price, 'quantity': size} 56 | q.update(kwargs) 57 | return self.private_query('buylimit', params=q) 58 | 59 | @return_api_response(fmt.order) 60 | def ask(self, pair, price, size, **kwargs): 61 | q = {'market': pair, 'rate': price, 'quantity': size} 62 | q.update(kwargs) 63 | return self.private_query('buylimit', params=q) 64 | 65 | @return_api_response(fmt.cancel) 66 | def cancel_order(self, order_id, **kwargs): 67 | q = {'uuid': order_id} 68 | q.update(kwargs) 69 | return self.private_query('cancel', params=q) 70 | 71 | @return_api_response(fmt.order_status) 72 | def order(self, order_id, **kwargs): 73 | q = {'uuid': order_id} 74 | q.update(kwargs) 75 | return self.private_query('getorder', params=q) 76 | 77 | @return_api_response(fmt.balance) 78 | def balance(self, **kwargs): 79 | return self.private_query('getbalance', params=kwargs) 80 | 81 | @return_api_response(fmt.withdraw) 82 | def withdraw(self, _type, source_wallet, size, tar_addr, **kwargs): 83 | raise NotImplementedError() 84 | 85 | @return_api_response(fmt.deposit) 86 | def deposit_address(self, **kwargs): 87 | raise NotImplementedError() 88 | 89 | """ 90 | Exchange Specific Methods 91 | """ 92 | 93 | @return_api_response(None) 94 | def pairs(self, names_only=True): 95 | if names_only: 96 | return self.public_query('api_pub.html?a=getmarkets') 97 | else: 98 | return self.public_query('pairs.json') 99 | 100 | @return_api_response(None) 101 | def prices(self): 102 | return self.public_query('prices.json') 103 | 104 | @return_api_response(None) 105 | def coin_names(self): 106 | return self.public_query('coinnames.json') 107 | 108 | @return_api_response(None) 109 | def volume(self, pair): 110 | return self.public_query('volume_%s.json' % pair) 111 | 112 | @return_api_response(None) 113 | def statistics(self, all=True): 114 | if all: 115 | return self.public_query('api_pub.html?a=getmarketsummaries') 116 | 117 | @return_api_response(None) 118 | def balance_distribution(self, currency): 119 | return self.public_query('api_pub.html?a=getbalancedistribution', 120 | params={'currencyname': currency}) 121 | -------------------------------------------------------------------------------- /bitex/interfaces/coincheck.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://coincheck.com/documents/exchange/api 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import CoincheckREST 12 | from bitex.utils import return_api_response 13 | from bitex.formatters.coincheck import CnckFormatter as fmt 14 | 15 | # Init Logging Facilities 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class Coincheck(CoincheckREST): 20 | def __init__(self, key='', secret='', key_file=''): 21 | super(Coincheck, self).__init__(key, secret) 22 | if key_file: 23 | self.load_key(key_file) 24 | 25 | def public_query(self, endpoint, **kwargs): 26 | return self.query('GET', endpoint, **kwargs) 27 | 28 | def private_query(self, endpoint, **kwargs): 29 | return self.query('POST', endpoint, authenticate=True, **kwargs) 30 | 31 | """ 32 | BitEx Standardized Methods 33 | """ 34 | 35 | @return_api_response(fmt.ticker) 36 | def ticker(self, pair, **kwargs): 37 | q = {'pair': pair} 38 | q.update(kwargs) 39 | return self.public_query('ticker', params=q) 40 | 41 | @return_api_response(fmt.trades) 42 | def trades(self, pair, **kwargs): 43 | q = {'pair': pair} 44 | q.update(kwargs) 45 | return self.public_query('trades', params=q) 46 | 47 | @return_api_response(fmt.order_book) 48 | def order_book(self, pair, **kwargs): 49 | q = {'pair': pair} 50 | q.update(kwargs) 51 | return self.public_query('order_books', params={'pair': pair}) 52 | 53 | @return_api_response(fmt.order) 54 | def bid(self, pair, price, size, **kwargs): 55 | q = {'rate': price, 'amount': size, 'pair': pair, 56 | 'order_type': 'buy'} 57 | q.update(kwargs) 58 | return self.private_query('orders', params=q) 59 | 60 | @return_api_response(fmt.order) 61 | def ask(self, pair, price, size, **kwargs): 62 | q = {'rate': price, 'amount': size, 'pair': pair, 63 | 'order_type': 'sell'} 64 | q.update(kwargs) 65 | return self.private_query('orders', params=q) 66 | 67 | @return_api_response(fmt.cancel) 68 | def cancel_order(self, order_id, **kwargs): 69 | return self.private_query('exchange/orders/%s' % order_id, params=kwargs) 70 | 71 | @return_api_response(fmt.order_status) 72 | def order(self, order_id, **kwargs): 73 | raise NotImplementedError() 74 | 75 | @return_api_response(fmt.balance) 76 | def balance(self, **kwargs): 77 | return self.private_query('accounts/balance') 78 | 79 | @return_api_response(fmt.withdraw) 80 | def withdraw(self, size, tar_addr, **kwargs): 81 | q = {'address': tar_addr, 'amount': size} 82 | q.update(kwargs) 83 | return self.private_query('send_money', params=q) 84 | 85 | @return_api_response(fmt.deposit) 86 | def deposit_address(self, **kwargs): 87 | raise NotImplementedError() 88 | 89 | """ 90 | Exchange Specific Methods 91 | """ 92 | -------------------------------------------------------------------------------- /bitex/interfaces/cryptopia.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://www.cryptopia.co.nz/Forum/Category/45 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import CryptopiaREST 12 | from bitex.utils import return_api_response 13 | from bitex.formatters.cryptopia import CrptFormatter as fmt 14 | # Init Logging Facilities 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | class Cryptopia(CryptopiaREST): 19 | def __init__(self, key='', secret='', key_file=''): 20 | super(Cryptopia, self).__init__(key, secret) 21 | if key_file: 22 | self.load_key(key_file) 23 | print(self.uri) 24 | 25 | def public_query(self, endpoint, **kwargs): 26 | return self.query('GET', endpoint, **kwargs) 27 | 28 | def private_query(self, endpoint, **kwargs): 29 | return self.query('POST', endpoint, authenticate=True, **kwargs) 30 | 31 | """ 32 | BitEx Standardized Methods 33 | """ 34 | 35 | @return_api_response(fmt.ticker) 36 | def ticker(self, pair, *args, **kwargs): 37 | endpoint = 'GetMarket/%s' % pair 38 | for k in args: 39 | endpoint += '/' + k 40 | return self.public_query(endpoint, params=kwargs) 41 | 42 | @return_api_response(fmt.order_book) 43 | def order_book(self, pair, *args, **kwargs): 44 | endpoint = 'GetMarketOrders/%s' % pair 45 | for k in args: 46 | endpoint += '/' + k 47 | return self.public_query(endpoint, params=kwargs) 48 | 49 | @return_api_response(fmt.trades) 50 | def trades(self, pair, *args, **kwargs): 51 | endpoint = 'GetMarkets/%s' % pair 52 | for k in args: 53 | endpoint += '/' + k 54 | return self.public_query(endpoint, params=kwargs) 55 | 56 | @return_api_response(fmt.order) 57 | def bid(self, pair, price, size, **kwargs): 58 | q = {'Market': pair, 'Type': 'Buy', 'Rate': price, 'Amount': size} 59 | q.update(kwargs) 60 | return self.private_query('SubmitTrade', params=q) 61 | 62 | @return_api_response(fmt.order) 63 | def ask(self, pair, price, size, **kwargs): 64 | q = {'Market': pair, 'Type': 'Sell', 'Rate': price, 'Amount': size} 65 | q.update(kwargs) 66 | return self.private_query('SubmitTrade', params=q) 67 | 68 | @return_api_response(fmt.cancel) 69 | def cancel_order(self, order_id, **kwargs): 70 | q = {'OrderId': order_id} 71 | q.update(kwargs) 72 | return self.private_query('CancelTrade', params=q) 73 | 74 | @return_api_response(fmt.order_status) 75 | def order(self, order_id, **kwargs): 76 | raise NotImplementedError() 77 | 78 | @return_api_response(fmt.balance) 79 | def balance(self, **kwargs): 80 | return self.private_query('GetBalance', params=kwargs) 81 | 82 | @return_api_response(fmt.withdraw) 83 | def withdraw(self, size, tar_addr, **kwargs): 84 | q = {'Amount': size, 'Address': tar_addr} 85 | q.update(kwargs) 86 | return self.private_query('SubmitWithdraw', params=q) 87 | 88 | @return_api_response(fmt.deposit) 89 | def deposit_address(self, **kwargs): 90 | return self.private_query('GetDepositAddress', params=kwargs) 91 | 92 | """ 93 | Exchange Specific Methods 94 | """ 95 | 96 | @return_api_response(None) 97 | def currencies(self): 98 | return self.public_query('GetCurrencies') 99 | 100 | @return_api_response(None) 101 | def pairs(self): 102 | return self.public_query('GetTradePairs') 103 | 104 | @return_api_response(None) 105 | def markets(self, **kwargs): 106 | endpoint = 'GetMarkets' 107 | for k in kwargs: 108 | endpoint += '/' + kwargs[k] 109 | return self.public_query(endpoint, params=kwargs) 110 | -------------------------------------------------------------------------------- /bitex/interfaces/gdax.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://docs.gdax.com/ 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import GDAXRest 12 | from bitex.api.WSS.gdax import GDAXWSS 13 | from bitex.utils import return_api_response 14 | from bitex.formatters.gdax import GdaxFormatter as fmt 15 | 16 | # Init Logging Facilities 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | class GDAX(GDAXRest): 21 | def __init__(self, key='', secret='', key_file='', websocket=False): 22 | super(GDAX, self).__init__(key, secret) 23 | if key_file: 24 | self.load_key(key_file) 25 | if websocket: 26 | self.wss = GDAXWSS() 27 | self.wss.start() 28 | else: 29 | self.wss = None 30 | 31 | def public_query(self, endpoint, **kwargs): 32 | return self.query('GET', endpoint, **kwargs) 33 | 34 | def private_query(self, endpoint, method_verb='POST', **kwargs): 35 | return self.query(method_verb, endpoint, authenticate=True, **kwargs) 36 | 37 | """ 38 | BitEx Standardized Methods 39 | """ 40 | 41 | @return_api_response(fmt.ticker) 42 | def ticker(self, pair, **kwargs): 43 | return self.public_query('products/%s/ticker' % pair, params=kwargs) 44 | 45 | @return_api_response(fmt.order_book) 46 | def order_book(self, pair, **kwargs): 47 | return self.public_query('products/%s/book' % pair, params=kwargs) 48 | 49 | @return_api_response(fmt.trades) 50 | def trades(self, pair, **kwargs): 51 | return self.public_query('products/%s/trades' % pair, params=kwargs) 52 | 53 | @return_api_response(fmt.order) 54 | def bid(self, pair, price, size, **kwargs): 55 | q = {'side': 'buy', 'type': 'market', 'product_id': pair, 56 | 'price': price, 'size': size} 57 | q.update(kwargs) 58 | return self.private_query('orders', params=q) 59 | 60 | @return_api_response(fmt.order) 61 | def ask(self, pair, price, size, **kwargs): 62 | q = {'side': 'sell', 'type': 'market', 'product_id': pair, 63 | 'price': price, 'size': size} 64 | q.update(kwargs) 65 | return self.private_query('orders', params=q) 66 | 67 | @return_api_response(fmt.cancel) 68 | def cancel_order(self, order_id, all=False, **kwargs): 69 | 70 | if not all: 71 | return self.private_query('orders/%s' % order_id, 72 | method_verb='DELETE', params=kwargs) 73 | else: 74 | return self.private_query('orders', method_verb='DELETE', 75 | params=kwargs) 76 | 77 | @return_api_response(fmt.order_status) 78 | def order(self, order_id, **kwargs): 79 | return self.private_query('orders/%s' % order_id, method_verb='GET', 80 | params=kwargs) 81 | 82 | @return_api_response(fmt.balance) 83 | def balance(self, **kwargs): 84 | return self.private_query('accounts', method_verb='GET', params=kwargs) 85 | 86 | @return_api_response(fmt.withdraw) 87 | def withdraw(self, size, tar_addr, **kwargs): 88 | raise NotImplementedError() 89 | 90 | @return_api_response(fmt.deposit) 91 | def deposit_address(self, **kwargs): 92 | raise NotImplementedError() 93 | 94 | """ 95 | Exchange Specific Methods 96 | """ 97 | 98 | @return_api_response 99 | def time(self): 100 | return self.public_query('time') 101 | 102 | @return_api_response(None) 103 | def currencies(self): 104 | return self.public_query('currencies') 105 | 106 | @return_api_response(None) 107 | def pairs(self): 108 | return self.public_query('products') 109 | 110 | @return_api_response(None) 111 | def ohlc(self, pair, **kwargs): 112 | return self.public_query('products/%s/candles' % pair, params=kwargs) 113 | 114 | @return_api_response(None) 115 | def stats(self, pair, **kwargs): 116 | return self.public_query('products/%s/stats' % pair, params=kwargs) 117 | -------------------------------------------------------------------------------- /bitex/interfaces/gemini.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://docs.gemini.com/rest-api/ 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import GeminiREST 12 | from bitex.api.WSS.gemini import GeminiWSS 13 | from bitex.utils import return_api_response 14 | from bitex.formatters.gemini import GmniFormatter as fmt 15 | 16 | # Init Logging Facilities 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | class Gemini(GeminiREST): 21 | def __init__(self, key='', secret='', key_file='', websocket=False): 22 | super(Gemini, self).__init__(key, secret) 23 | if key_file: 24 | self.load_key(key_file) 25 | if websocket: 26 | self.wss = GeminiWSS() 27 | self.wss.start() 28 | else: 29 | self.wss = None 30 | 31 | def public_query(self, endpoint, **kwargs): 32 | return self.query('GET', endpoint, **kwargs) 33 | 34 | def private_query(self, endpoint, method_verb='POST', **kwargs): 35 | return self.query(method_verb, endpoint, authenticate=True, **kwargs) 36 | 37 | """ 38 | BitEx Standardized Methods 39 | """ 40 | 41 | @return_api_response(fmt.ticker) 42 | def ticker(self, pair, **kwargs): 43 | return self.public_query('pubticker/%s' % pair, params=kwargs) 44 | 45 | @return_api_response(fmt.order_book) 46 | def order_book(self, pair, **kwargs): 47 | return self.public_query('book/%s' % pair, params=kwargs) 48 | 49 | @return_api_response(fmt.trades) 50 | def trades(self, pair, **kwargs): 51 | return self.public_query('trades/%s' % pair, params=kwargs) 52 | 53 | @return_api_response(fmt.order) 54 | def bid(self, pair, price, size, **kwargs): 55 | q = {'symbol': pair, 'amount': size, 'price': price, 'side': 'buy'} 56 | q.update(kwargs) 57 | return self.private_query('order/new', params=q) 58 | 59 | @return_api_response(fmt.order) 60 | def ask(self, pair, price, size, **kwargs): 61 | q = {'symbol': pair, 'amount': size, 'price': price, 'side': 'sell'} 62 | q.update(kwargs) 63 | return self.private_query('order/new', params=q) 64 | 65 | @return_api_response(fmt.cancel) 66 | def cancel_order(self, order_id, **kwargs): 67 | q = {'order_id': order_id} 68 | q.update(kwargs) 69 | return self.private_query('order/cancel', params=q) 70 | 71 | @return_api_response(fmt.order_status) 72 | def order(self, order_id, **kwargs): 73 | q = {'order_id': order_id} 74 | q.update(kwargs) 75 | return self.private_query('order/status', params=q) 76 | 77 | @return_api_response(fmt.balance) 78 | def balance(self, **kwargs): 79 | return self.private_query('balances', params=kwargs) 80 | 81 | @return_api_response(fmt.withdraw) 82 | def withdraw(self, size, tar_addr, **kwargs): 83 | raise NotImplementedError() 84 | 85 | @return_api_response(fmt.deposit) 86 | def deposit_address(self, **kwargs): 87 | raise NotImplementedError() 88 | 89 | """ 90 | Exchange Specific Methods 91 | """ 92 | 93 | @return_api_response(None) 94 | def pairs(self): 95 | return self.public_query('symbols') 96 | 97 | @return_api_response(None) 98 | def auction(self, pair): 99 | return self.public_query('auction/%s' % pair) 100 | 101 | @return_api_response(None) 102 | def auction_history(self, pair, **kwargs): 103 | return self.public_query('auction/%s/history' % pair, params=kwargs) 104 | -------------------------------------------------------------------------------- /bitex/interfaces/hitbtc.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://hitbtc.com/api 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | import time 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import HitBTCREST 12 | from bitex.api.WSS.hitbtc import HitBTCWSS 13 | from bitex.utils import return_api_response 14 | from bitex.formatters.hitbtc import HitBtcFormatter as fmt 15 | 16 | # Init Logging Facilities 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | class HitBtc(HitBTCREST): 21 | def __init__(self, key='', secret='', key_file='', websocket=False): 22 | super(HitBtc, self).__init__(key, secret) 23 | if key_file: 24 | self.load_key(key_file) 25 | if websocket: 26 | self.wss = HitBTCWSS() 27 | self.wss.start() 28 | else: 29 | self.wss = None 30 | 31 | def public_query(self, endpoint, method_verb=None, **kwargs): 32 | if not method_verb: 33 | method_verb = 'GET' 34 | endpoint = 'public/' + endpoint 35 | return self.query(method_verb, endpoint, **kwargs) 36 | 37 | def private_query(self, endpoint, method_verb=None, **kwargs): 38 | if not method_verb: 39 | method_verb = 'GET' 40 | return self.query(method_verb, endpoint, authenticate=True, **kwargs) 41 | 42 | """ 43 | BitEx Standardized Methods 44 | """ 45 | 46 | @return_api_response(fmt.order_book) 47 | def order_book(self, pair, **kwargs): 48 | q = kwargs 49 | return self.public_query('api/%s/orderbook' % pair, params=q) 50 | 51 | @return_api_response(fmt.ticker) 52 | def ticker(self, pair, **kwargs): 53 | q = kwargs 54 | if pair == 'all': 55 | return self.public_query('api/ticker', params=q) 56 | else: 57 | return self.public_query('api/%s/ticker' % pair, params=q) 58 | 59 | @return_api_response(fmt.trades) 60 | def trades(self, pair, **kwargs): 61 | q = kwargs 62 | return self.public_query('api/%s/trades' % pair, params=q) 63 | 64 | def _place_order(self, pair, size, price, side, order_id, **kwargs): 65 | q = {'symbol': pair, 'price': price, 'quantity': size, 'side': side, 66 | 'clientOrderId': order_id} 67 | q.update(kwargs) 68 | return self.private_query('trading/new_order', method_verb='POST', params=q) 69 | 70 | @return_api_response(fmt.order) 71 | def bid(self, pair, price, size, order_id=None, **kwargs): 72 | order_id = order_id if order_id else str(time.time()) 73 | self._place_order(pair, size, price, 'buy', order_id, **kwargs) 74 | 75 | @return_api_response(fmt.order) 76 | def ask(self, pair, price, size, order_id=None, **kwargs): 77 | order_id = order_id if order_id else str(time.time()) 78 | self._place_order(pair, size, price, 'sell', order_id, **kwargs) 79 | 80 | @return_api_response(fmt.cancel) 81 | def cancel_order(self, order_id, all=False, **kwargs): 82 | q = {'clientOrderId': order_id} 83 | q.update(kwargs) 84 | if all: 85 | return self.private_query('trading/cancel_orders', params=kwargs) 86 | else: 87 | return self.private_query('trading/cancel_order', params=q) 88 | 89 | @return_api_response(fmt.order_status) 90 | def order(self, order_id, **kwargs): 91 | q = {'clientOrderId': order_id} 92 | q.update(kwargs) 93 | return self.private_query('order', params=q) 94 | 95 | @return_api_response(fmt.balance) 96 | def balance(self, **kwargs): 97 | return self.private_query('trading/balance', params=kwargs) 98 | 99 | @return_api_response(fmt.withdraw) 100 | def withdraw(self, size, tar_addr, currency=None, **kwargs): 101 | currency = 'BTC' if not currency else currency 102 | q = {'amount': size, 'currency_code': currency, 'address': tar_addr} 103 | q.update(kwargs) 104 | return self.private_query('payment/payout', params=q) 105 | 106 | @return_api_response(fmt.deposit) 107 | def deposit_address(self, currency=None, **kwargs): 108 | return self.private_query('payment/address/%s' % currency, params=kwargs) 109 | 110 | if __name__ == ' __main__': 111 | k = HitBtc() 112 | print(k.ticker('BTCUSD')) 113 | -------------------------------------------------------------------------------- /bitex/interfaces/itbit.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://api.itbit.com/docs 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import ItbitREST 12 | from bitex.utils import return_api_response 13 | from bitex.formatters.itbit import itbtFormatter as fmt 14 | 15 | # Init Logging Facilities 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class ItBit(ItbitREST): 20 | def __init__(self, key='', secret='', key_file=''): 21 | super(ItbitREST, self).__init__(key, secret) 22 | if key_file: 23 | self.load_key(key_file) 24 | 25 | def public_query(self, endpoint, **kwargs): 26 | return self.query('GET', 'markets/' + endpoint, **kwargs) 27 | 28 | def private_query(self, endpoint, **kwargs): 29 | return self.query('POST', endpoint, authenticate=True, **kwargs) 30 | 31 | """ 32 | BitEx Standardized Methods 33 | """ 34 | 35 | @return_api_response(fmt.ticker) 36 | def ticker(self, pair, **kwargs): 37 | q = {'pair': pair} 38 | q.update(kwargs) 39 | return self.public_query('%s/ticker' % pair, params=q) 40 | 41 | @return_api_response(fmt.order_book) 42 | def order_book(self, pair, **kwargs): 43 | q = {'pair': pair} 44 | q.update(kwargs) 45 | return self.public_query('%s/order_book' % pair, params=q) 46 | 47 | @return_api_response(fmt.trades) 48 | def trades(self, pair, **kwargs): 49 | q = {'pair': pair} 50 | q.update(kwargs) 51 | return self.public_query('%s/trades' % pair, params=q) 52 | 53 | @return_api_response(fmt.order) 54 | def bid(self, pair, price, size, **kwargs): 55 | wallet_id = kwargs.pop('wallet') 56 | q = {'side': 'buy', 'type': 'limit', 'amount': size, 'price': price, 57 | 'instrument': pair} 58 | q.update(kwargs) 59 | return self.private_query('wallets/%s/orders' % wallet_id, params=q) 60 | 61 | @return_api_response(fmt.order) 62 | def ask(self, pair, price, size, **kwargs): 63 | wallet_id = kwargs.pop('wallet') 64 | q = {'side': 'buy', 'type': 'limit', 'amount': size, 'price': price, 65 | 'instrument': pair} 66 | q.update(kwargs) 67 | return self.private_query('wallets/%s/orders' % wallet_id, params=q) 68 | 69 | @return_api_response(fmt.cancel) 70 | def cancel_order(self, order_id, all=False, **kwargs): 71 | raise NotImplementedError() 72 | 73 | @return_api_response(fmt.order_status) 74 | def order(self, order_id, **kwargs): 75 | wallet_id = kwargs.pop('wallet_id') 76 | return self.private_query('wallets/%s/orders/%s' % (wallet_id, order_id), 77 | params=kwargs) 78 | 79 | @return_api_response(fmt.balance) 80 | def balance(self, **kwargs): 81 | raise NotImplementedError() 82 | 83 | @return_api_response(fmt.withdraw) 84 | def withdraw(self, size, tar_addr, **kwargs): 85 | wallet_id = kwargs.pop('wallet_id') 86 | q = {'address': tar_addr, 'amount': size} 87 | return self.private_query('wallets/%s/cryptocurrency_withdrawals' % wallet_id, 88 | params=q) 89 | 90 | @return_api_response(fmt.deposit) 91 | def deposit_address(self, **kwargs): 92 | wallet_id = kwargs.pop('wallet_id') 93 | return self.private_query('wallets/%s/cryptocurrency_deposits' % wallet_id, 94 | params=kwargs) 95 | 96 | """ 97 | Exchange Specific Methods 98 | """ 99 | -------------------------------------------------------------------------------- /bitex/interfaces/kraken.py: -------------------------------------------------------------------------------- 1 | """ 2 | https:/kraken.com/help/api 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import KrakenREST 12 | from bitex.utils import return_api_response 13 | from bitex.formatters.kraken import KrknFormatter as fmt 14 | # Init Logging Facilities 15 | log = logging.getLogger(__name__) 16 | 17 | 18 | class Kraken(KrakenREST): 19 | def __init__(self, key='', secret='', key_file=''): 20 | super(Kraken, self).__init__(key, secret) 21 | if key_file: 22 | self.load_key(key_file) 23 | 24 | def make_params(self, *pairs, **kwargs): 25 | q = {'pair': ','.join(pairs)} 26 | q.update(kwargs) 27 | return q 28 | 29 | def public_query(self, endpoint, **kwargs): 30 | path = 'public/' + endpoint 31 | return self.query('GET', path, **kwargs) 32 | 33 | def private_query(self, endpoint, **kwargs): 34 | path = 'private/' + endpoint 35 | return self.query('POST', path, authenticate=True, **kwargs) 36 | 37 | """ 38 | BitEx Standardized Methods 39 | """ 40 | 41 | @return_api_response(fmt.ticker) 42 | def ticker(self, *pairs, **kwargs): 43 | q = self.make_params(*pairs, **kwargs) 44 | return self.public_query('Ticker', params=q) 45 | 46 | @return_api_response(fmt.order_book) 47 | def order_book(self, pair, **kwargs): 48 | q = self.make_params(pair, **kwargs) 49 | return self.public_query('Depth', params=q) 50 | 51 | @return_api_response(fmt.trades) 52 | def trades(self, pair, **kwargs): 53 | q = self.make_params(pair, **kwargs) 54 | return self.public_query('Trades', params=q) 55 | 56 | def _add_order(self, pair, side, price, size, **kwargs): 57 | q = {'pair': pair, 'type': side, 'price': price, 58 | 'ordertype': 'limit', 'volume': size, 59 | 'trading_agreement': 'agree'} 60 | q.update(kwargs) 61 | return self.private_query('AddOrder', params=q) 62 | 63 | @return_api_response(fmt.order) 64 | def bid(self, pair, price, size, **kwargs): 65 | return self._add_order(pair, 'buy', price, size, **kwargs) 66 | 67 | @return_api_response(fmt.order) 68 | def ask(self, pair, price, size, **kwargs): 69 | return self._add_order(pair, 'sell', price, size, **kwargs) 70 | 71 | @return_api_response(fmt.cancel) 72 | def cancel_order(self, order_id, **kwargs): 73 | q = {'txid': order_id} 74 | q.update(kwargs) 75 | return self.private_query('CancelOrder', params=q) 76 | 77 | @return_api_response(fmt.order_status) 78 | def order(self, *txids, **kwargs): 79 | if len(txids) > 1: 80 | q = {'txid': txids} 81 | elif txids: 82 | txid, *_ = txids 83 | q = {'txid': txid} 84 | else: 85 | q = {} 86 | q.update(kwargs) 87 | return self.private_query('QueryOrders', params=q) 88 | 89 | @return_api_response(fmt.balance) 90 | def balance(self, **kwargs): 91 | return self.private_query('Balance') 92 | 93 | @return_api_response(fmt.withdraw) 94 | def withdraw(self, size, tar_addr, **kwargs): 95 | q = {'amount': size, 'key': tar_addr} 96 | q.update(kwargs) 97 | return self.private_query('Withdraw', params=q) 98 | 99 | @return_api_response(fmt.deposit) 100 | def deposit_address(self, **kwargs): 101 | return self.private_query('DepositAddresses', params=kwargs) 102 | 103 | """ 104 | Exchange Specific Methods 105 | """ 106 | 107 | @return_api_response(None) 108 | def time(self): 109 | return self.public_query('Time') 110 | 111 | @return_api_response(None) 112 | def assets(self, **kwargs): 113 | return self.public_query('Assets', params=kwargs) 114 | 115 | @return_api_response(None) 116 | def pairs(self, **kwargs): 117 | return self.public_query('AssetPairs', params=kwargs) 118 | 119 | @return_api_response(None) 120 | def ohlc(self, pair, **kwargs): 121 | q = self.make_params(pair, **kwargs) 122 | return self.public_query('OHLC', params=q) 123 | 124 | @return_api_response(None) 125 | def spread(self, pair, **kwargs): 126 | q = self.make_params(pair, **kwargs) 127 | return self.public_query('Spread', params=q) 128 | 129 | @return_api_response(None) 130 | def orders(self, **kwargs): 131 | q = kwargs 132 | return self.private_query('OpenOrders', params=q) 133 | 134 | @return_api_response(None) 135 | def closed_orders(self, **kwargs): 136 | q = kwargs 137 | return self.private_query('ClosedOrders', params=q) 138 | 139 | @return_api_response(None) 140 | def trade_history(self, **kwargs): 141 | q = kwargs 142 | return self.private_query('TradesHistory', params=q) 143 | 144 | @return_api_response(None) 145 | def fees(self, pair=None): 146 | q = {'fee-info': True} 147 | 148 | if pair: 149 | q['pair'] = pair 150 | 151 | return self.private_query('TradeVolume', params=q) 152 | 153 | def deposit_methods(self, **kwargs): 154 | return self.private_query('DepositMethods', params=kwargs) 155 | 156 | def withdraw_info(self, size, tar_addr, **kwargs): 157 | q = {'amount': size, 'key': tar_addr} 158 | q.update(kwargs) 159 | return self.private_query('WithdrawInfo', params=q) 160 | -------------------------------------------------------------------------------- /bitex/interfaces/okcoin.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://www.okcoin.com/about/rest_api.do 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import OKCoinREST 12 | from bitex.utils import return_api_response 13 | from bitex.formatters.okcoin import OkcnFormatter as fmt 14 | 15 | # Init Logging Facilities 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class OKCoin(OKCoinREST): 20 | def __init__(self, key='', secret='', key_file=''): 21 | super(OKCoin, self).__init__(key, secret) 22 | if key_file: 23 | self.load_key(key_file) 24 | 25 | def public_query(self, endpoint, **kwargs): 26 | return self.query('GET', endpoint, **kwargs) 27 | 28 | def private_query(self, endpoint, **kwargs): 29 | return self.query('POST', endpoint, authenticate=True, **kwargs) 30 | 31 | """ 32 | BitEx Standardized Methods 33 | """ 34 | @return_api_response(fmt.ticker) 35 | def ticker(self, pair, **kwargs): 36 | q = {'pair': pair} 37 | q.update(kwargs) 38 | return self.public_query('ticker.do', params=q) 39 | 40 | @return_api_response(fmt.order_book) 41 | def order_book(self, pair, **kwargs): 42 | q = {'pair': pair} 43 | q.update(kwargs) 44 | return self.public_query('depth.do', params=q) 45 | 46 | @return_api_response(fmt.trades) 47 | def trades(self, pair, **kwargs): 48 | q = {'pair': pair} 49 | q.update(kwargs) 50 | return self.public_query('trades.do', params=q) 51 | 52 | @return_api_response(fmt.order) 53 | def bid(self, pair, price, size, **kwargs): 54 | q = {'symbol': pair, 'price': price, 'amount': size, 'type': 'buy'} 55 | q.update(kwargs) 56 | return self.private_query('trade.do', params=q) 57 | 58 | @return_api_response(fmt.order) 59 | def ask(self, pair, price, size, **kwargs): 60 | q = {'symbol': pair, 'price': price, 'amount': size, 'type': 'sell'} 61 | q.update(kwargs) 62 | return self.private_query('trade.do', params=q) 63 | 64 | @return_api_response(fmt.cancel) 65 | def cancel_order(self, order_id, **kwargs): 66 | q = {'order_id': order_id} 67 | q.update(kwargs) 68 | return self.private_query('cancel_order.do', params=q) 69 | 70 | @return_api_response(fmt.order_status) 71 | def order(self, order_id, **kwargs): 72 | q = {'order_id': order_id} 73 | q.update(kwargs) 74 | return self.private_query('orders.info', params=q) 75 | 76 | @return_api_response(fmt.balance) 77 | def balance(self, **kwargs): 78 | return self.private_query('userinfo.do', params=kwargs) 79 | 80 | @return_api_response(fmt.withdraw) 81 | def withdraw(self, size, tar_addr, **kwargs): 82 | q = {'withdraw_address': tar_addr, 'withdraw_amount': size} 83 | q.update(kwargs) 84 | return self.private_query('withdraw.do', params=q) 85 | 86 | @return_api_response(fmt.deposit) 87 | def deposit_address(self, **kwargs): 88 | raise NotImplementedError() 89 | 90 | """ 91 | Exchange Specific Methods 92 | """ 93 | 94 | @return_api_response(None) 95 | def ohlc(self, pair): 96 | return self.public_query('kline.do', params={'pair': pair}) 97 | 98 | @return_api_response(None) 99 | def future_ticker(self, pair, **kwargs): 100 | q = {'pair': pair} 101 | q.update(kwargs) 102 | return self.public_query('future_ticker.do', params=q) 103 | 104 | @return_api_response(None) 105 | def future_order_book(self, pair, **kwargs): 106 | q = {'pair': pair} 107 | q.update(kwargs) 108 | return self.public_query('future_order_book.do', params=q) 109 | 110 | @return_api_response(None) 111 | def future_trades(self, pair, **kwargs): 112 | q = {'pair': pair} 113 | q.update(kwargs) 114 | return self.public_query('future_trades.do', params=q) 115 | 116 | @return_api_response(None) 117 | def future_index(self, pair, **kwargs): 118 | q = {'pair': pair} 119 | q.update(kwargs) 120 | return self.public_query('future_index.do', params=q) 121 | 122 | @return_api_response(None) 123 | def usd_cny_rate(self): 124 | return self.public_query('exchange_rate.do', params=q) 125 | 126 | @return_api_response(None) 127 | def future_estimate(self, pair, **kwargs): 128 | q = {'pair': pair} 129 | q.update(kwargs) 130 | return self.public_query('future_estimated_price.do', params=q) 131 | 132 | @return_api_response(None) 133 | def future_ohlc(self, pair, **kwargs): 134 | q = {'pair': pair} 135 | q.update(kwargs) 136 | return self.public_query('future_kline.do', params=q) 137 | 138 | @return_api_response(None) 139 | def future_holds(self, pair, **kwargs): 140 | q = {'pair': pair} 141 | q.update(kwargs) 142 | return self.public_query('future_hold_amount.do', params=q) 143 | 144 | @return_api_response(None) 145 | def future_limit_price(self, pair, **kwargs): 146 | q = {'pair': pair} 147 | q.update(kwargs) 148 | return self.public_query('future_price_limit.do', params=q) 149 | 150 | @return_api_response(None) 151 | def otc_order_book(self, pair, **kwargs): 152 | q = {'pair': pair} 153 | q.update(kwargs) 154 | return self.public_query('otcs.do', params=q) 155 | -------------------------------------------------------------------------------- /bitex/interfaces/poloniex.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://poloniex.com/support/api/ 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import PoloniexREST 12 | from bitex.api.WSS.poloniex import PoloniexWSS 13 | from bitex.utils import return_api_response 14 | from bitex.formatters.poloniex import PlnxFormatter as fmt 15 | # Init Logging Facilities 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class Poloniex(PoloniexREST): 20 | def __init__(self, key='', secret='', key_file='', websocket=False): 21 | super(Poloniex, self).__init__(key, secret) 22 | if key_file: 23 | self.load_key(key_file) 24 | if websocket: 25 | self.wss = PoloniexWSS() 26 | self.wss.start() 27 | else: 28 | self.wss = None 29 | 30 | def public_query(self, endpoint, **kwargs): 31 | return self.query('GET', 'public?command=' + endpoint, **kwargs) 32 | 33 | def private_query(self, endpoint, **kwargs): 34 | return self.query('POST', endpoint, 35 | authenticate=True, **kwargs) 36 | 37 | """ 38 | BitEx Standardized Methods 39 | """ 40 | 41 | @return_api_response(fmt.ticker) 42 | def ticker(self, pair, **kwargs): 43 | return self.public_query('returnTicker', params=kwargs) 44 | 45 | @return_api_response(fmt.order_book) 46 | def order_book(self, pair, **kwargs): 47 | kwargs['currencyPair'] = pair 48 | return self.public_query('returnOrderBook', params=kwargs) 49 | 50 | @return_api_response(fmt.trades) 51 | def trades(self, pair, **kwargs): 52 | kwargs['currencyPair'] = pair 53 | return self.public_query('returnTradeHistory', params=kwargs) 54 | 55 | @return_api_response(fmt.order) 56 | def bid(self, pair, rate, size, **kwargs): 57 | q = {'command': 'buy', 'currencyPair': pair, 'amount': size, 58 | 'rate': rate} 59 | q.update(kwargs) 60 | return self.private_query('tradingApi', params=q) 61 | 62 | @return_api_response(fmt.order) 63 | def ask(self, pair, rate, size, **kwargs): 64 | q = {'command': 'sell', 'currencyPair': pair, 'amount': size, 65 | 'rate': rate} 66 | q.update(kwargs) 67 | return self.private_query('tradingApi', params=q) 68 | 69 | @return_api_response(fmt.cancel) 70 | def cancel_order(self, txid, **kwargs): 71 | q = {'orderNumber': txid, 'command': 'cancelOrder'} 72 | q.update(kwargs) 73 | return self.private_query('tradingApi', params=q) 74 | 75 | @return_api_response(fmt.order_status) 76 | def order(self, order_id, **kwargs): 77 | raise NotImplementedError() 78 | 79 | @return_api_response(fmt.balance) 80 | def balance(self, detailed=False, **kwargs): 81 | q = {} 82 | if detailed: 83 | q['command'] = 'returnCompleteBalances' 84 | else: 85 | q['command'] = 'returnBalances' 86 | q.update(kwargs) 87 | return self.private_query('tradingApi', params=q) 88 | 89 | @return_api_response(fmt.withdraw) 90 | def withdraw(self, size, tar_addr, **kwargs): 91 | q = {'currency': kwargs.pop('currency'), 'amount': size, 'address': tar_addr} 92 | q.update(kwargs) 93 | return self.private_query('tradingApi', params=q) 94 | 95 | @return_api_response(fmt.deposit) 96 | def deposit_address(self, currency, **kwargs): 97 | q = {'command': 'returnDepositAddresses'} 98 | q.update(kwargs) 99 | return self.private_query('tradingApi', params=q) 100 | 101 | 102 | """ 103 | Exchange Specific Methods 104 | """ 105 | 106 | @return_api_response(None) 107 | def currencies(self): 108 | return self.public_query('returnCurrencies') 109 | 110 | @return_api_response(None) 111 | def hloc(self, pair, **kwargs): 112 | kwargs['currencyPair'] = pair 113 | return self.public_query('returnChartData') 114 | 115 | 116 | @return_api_response(None) 117 | def balance_history(self, **kwargs): 118 | q = {'command': 'returnDepositsWithdrawals'} 119 | q.update(kwargs) 120 | return self.private_query('tradingApi', params=q) 121 | 122 | @return_api_response(None) 123 | def orders(self, pair='all', **kwargs): 124 | q = {'command': 'returnOpenOrders', 'currencyPair': pair} 125 | q.update(kwargs) 126 | return self.private_query('tradingApi', params=q) 127 | 128 | @return_api_response(None) 129 | def trade_history(self, pair='all', **kwargs): 130 | q = {'currencyPair': pair, 'command': 'returnTradeHistory'} 131 | q.update(kwargs) 132 | return self.private_query('tradingApi', params=q) 133 | 134 | @return_api_response(None) 135 | def update_order(self, txid, rate, **kwargs): 136 | q = {'command': 'moveOrder', 'rate': rate, 'orderNumber': txid} 137 | q.update(kwargs) 138 | return self.query('tradingApi', params=q) 139 | 140 | @return_api_response(None) 141 | def fees(self): 142 | return self.private_query('tradingApi', 143 | params={'command': 'returnFeeInfo'}) 144 | -------------------------------------------------------------------------------- /bitex/interfaces/quadriga.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://www.quadrigacx.com/api_info 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import QuadrigaCXREST 12 | from bitex.utils import return_api_response 13 | from bitex.formatters.quadriga import QuadrigaCXFormatter as fmt 14 | 15 | # Init Logging Facilities 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class QuadrigaCX(QuadrigaCXREST): 20 | def __init__(self, key='', secret='', key_file=''): 21 | super(QuadrigaCX, self).__init__(key, secret) 22 | if key_file: 23 | self.load_key(key_file) 24 | 25 | def public_query(self, endpoint, **kwargs): 26 | return self.query('GET', endpoint, **kwargs) 27 | 28 | def private_query(self, endpoint, method='POST', **kwargs): 29 | return self.query(method, endpoint, authenticate=True, **kwargs) 30 | 31 | """ 32 | BitEx Standardized Methods 33 | """ 34 | 35 | @return_api_response(fmt.ticker) 36 | def ticker(self, pair, **kwargs): 37 | q = {'book': pair} 38 | q.update(kwargs) 39 | return self.public_query('ticker', params=q) 40 | 41 | @return_api_response(fmt.order_book) 42 | def order_book(self, pair, **kwargs): 43 | q = {'book': pair} 44 | q.update(kwargs) 45 | return self.public_query('order_book', params=q) 46 | 47 | @return_api_response(fmt.trades) 48 | def trades(self, pair, **kwargs): 49 | q = {'book': pair} 50 | q.update(kwargs) 51 | return self.public_query('transactions', params=q) 52 | 53 | @return_api_response(fmt.order) 54 | def bid(self, pair, price, size, **kwargs): 55 | q = {'amount': size, 'price': price, 'book': pair} 56 | q.update(kwargs) 57 | return self.private_query('buy', params=q) 58 | 59 | @return_api_response(fmt.order) 60 | def ask(self, pair, price, size, **kwargs): 61 | q = {'amount': size, 'price': price, 'book': pair} 62 | q.update(kwargs) 63 | return self.private_query('sell', params=q) 64 | 65 | @return_api_response(fmt.cancel) 66 | def cancel_order(self, order_id, **kwargs): 67 | q = {'id': order_id} 68 | q.update(kwargs) 69 | return self.private_query('cancel_order', params=q) 70 | 71 | @return_api_response(fmt.order_status) 72 | def order(self, order_id, **kwargs): 73 | q = {'id': order_id} 74 | q.update(kwargs) 75 | return self.private_query('lookup_order', params=q) 76 | 77 | @return_api_response(fmt.balance) 78 | def balance(self, **kwargs): 79 | return self.private_query('balance', params=kwargs) 80 | 81 | @return_api_response(fmt.withdraw) 82 | def withdraw(self, size, tar_addr, cur='bitcoin', **kwargs): 83 | q = {'amount': size, 'address': tar_addr} 84 | q.update(kwargs) 85 | if cur in ['bitcoin', 'ether']: 86 | return self.private_query('%s_withdrawal' % cur, params=q) 87 | 88 | @return_api_response(fmt.deposit) 89 | def deposit_address(self, cur='bitcoin', **kwargs): 90 | if cur in ['bitcoin', 'ether']: 91 | return self.private_query('%s_deposit_address' % cur, params=kwargs) 92 | else: 93 | raise NotImplementedError("Invalid Currency! Must be %s|%s" % 94 | ('bitcoin', 'ether')) 95 | 96 | 97 | """ 98 | Exchange Specific Methods 99 | """ 100 | -------------------------------------------------------------------------------- /bitex/interfaces/quoine.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://developers.quoine.com/ 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import QuoineREST 12 | from bitex.utils import return_api_response 13 | from bitex.formatters.quoine import QoinFormatter as fmt 14 | 15 | # Init Logging Facilities 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class Quoine(QuoineREST): 20 | def __init__(self, key='', secret='', key_file=''): 21 | super(Quoine, self).__init__(key, secret) 22 | if key_file: 23 | self.load_key(key_file) 24 | 25 | self.pairs = {d['currency_pair_code']: d['id'] 26 | for d in self.public_query('products').json()} 27 | 28 | def public_query(self, endpoint, **kwargs): 29 | return self.query('GET', endpoint, **kwargs) 30 | 31 | def private_query(self, endpoint, method='POST', **kwargs): 32 | return self.query(method, endpoint, authenticate=True, **kwargs) 33 | 34 | """ 35 | BitEx Standardized Methods 36 | """ 37 | 38 | @return_api_response(fmt.ticker) 39 | def ticker(self, pair, **kwargs): 40 | pair = self.pairs[pair] 41 | return self.public_query('products/%s' % pair, params=kwargs) 42 | 43 | @return_api_response(fmt.order_book) 44 | def order_book(self, pair, **kwargs): 45 | pair = self.pairs[pair] 46 | return self.public_query('products/%s/price_levels' % pair, params=kwargs) 47 | 48 | @return_api_response(fmt.trades) 49 | def trades(self, pair, **kwargs): 50 | q = {'currency_pair_code': pair} 51 | q.update(kwargs) 52 | return self.public_query('executions/', params=q) 53 | 54 | @return_api_response(fmt.order) 55 | def bid(self, pair, price, size, **kwargs): 56 | q = {'quantity': size, 'price': price, 'order_type': 'limit', 57 | 'product_id': self.pairs[pair], 'side': 'buy'} 58 | q.update(kwargs) 59 | return self.private_query('orders', params={'order': q}) 60 | 61 | @return_api_response(fmt.order) 62 | def ask(self, pair, price, size, **kwargs): 63 | q = {'quantity': size, 'price': price, 'order_type': 'limit', 64 | 'product_id': self.pairs[pair], 'side': 'sell'} 65 | q.update(kwargs) 66 | return self.private_query('orders', params={'order': q}) 67 | 68 | @return_api_response(fmt.cancel) 69 | def cancel_order(self, order_id, **kwargs): 70 | return self.private_query('orders/%s/cancel' % order_id, 71 | method='PUT') 72 | 73 | @return_api_response(fmt.order_status) 74 | def order(self, order_id, **kwargs): 75 | return self.private_query('orders/%s' % order_id, method='GET') 76 | 77 | @return_api_response(fmt.balance) 78 | def balance(self, **kwargs): 79 | return self.private_query('/accounts/balance/', method='GET') 80 | 81 | @return_api_response(fmt.withdraw) 82 | def withdraw(self, size, tar_addr, **kwargs): 83 | raise NotImplementedError() 84 | 85 | @return_api_response(fmt.deposit) 86 | def deposit_address(self, **kwargs): 87 | raise NotImplementedError() 88 | 89 | """ 90 | Exchange Specific Methods 91 | """ 92 | -------------------------------------------------------------------------------- /bitex/interfaces/rocktrading.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://www.therocktrading.com/pages/api 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import RockTradingREST 12 | from bitex.utils import return_api_response 13 | from bitex.formatters.rocktrading import RockFormatter as fmt 14 | 15 | # Init Logging Facilities 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class RockTradingLtd(RockTradingREST): 20 | def __init__(self, key='', secret='', key_file=''): 21 | super(RockTradingLtd, self).__init__(key, secret) 22 | if key_file: 23 | self.load_key(key_file) 24 | 25 | def public_query(self, endpoint, **kwargs): 26 | return self.query('GET', endpoint, **kwargs) 27 | 28 | def private_query(self, endpoint, method='GET', **kwargs): 29 | return self.query(method, endpoint, authenticate=True, **kwargs) 30 | 31 | """ 32 | BitEx Standardized Methods 33 | """ 34 | 35 | @return_api_response(fmt.ticker) 36 | def ticker(self, pair=None, **kwargs): 37 | if pair: 38 | return self.public_query('funds/%s/ticker' % pair, params=kwargs) 39 | else: 40 | return self.public_query('tickers') 41 | 42 | @return_api_response(fmt.order_book) 43 | def order_book(self, pair, **kwargs): 44 | return self.public_query('funds/%s/orderbook' % pair, params=kwargs) 45 | 46 | @return_api_response(fmt.trades) 47 | def trades(self, pair, **kwargs): 48 | return self.public_query('funds/%s/trades' % pair, params=kwargs) 49 | 50 | def _place_order(self, side, pair, price, size, **kwargs): 51 | q = {'fund_id': pair, 'side': side, 'amount': size, 'price': price} 52 | q.update(kwargs) 53 | return self.private_query('funds/%s/orders' % pair, method='POST', 54 | params=q) 55 | 56 | @return_api_response(fmt.order) 57 | def bid(self, pair, price, size, **kwargs): 58 | return self._place_order('buy', pair, price, size, **kwargs) 59 | 60 | @return_api_response(fmt.order) 61 | def ask(self, *, pair, price, size, **kwargs): 62 | return self._place_order('sell', pair, price, size, **kwargs) 63 | 64 | @return_api_response(fmt.cancel) 65 | def cancel_order(self, id, market, **kwargs): 66 | return self.private_query('funds/%s/orders/%s' % (market, id), 67 | method='DELETE', params=kwargs) 68 | 69 | @return_api_response(fmt.order_status) 70 | def order(self, order_id, **kwargs): 71 | try: 72 | fund_id = kwargs.pop('fund_id') 73 | except KeyError: 74 | raise 75 | 76 | return self.private_query('funds/%s/orders/%s' % (fund_id, order_id), 77 | params=kwargs) 78 | 79 | @return_api_response(fmt.balance) 80 | def balance(self, **kwargs): 81 | return self.private_query('balances', params=kwargs) 82 | 83 | @return_api_response(fmt.withdraw) 84 | def withdraw(self, size, tar_addr, **kwargs): 85 | q = {'destination_address': tar_addr, 'amount': size} 86 | q.update(kwargs) 87 | return self.private_query('atms/withdraw', params=q) 88 | 89 | @return_api_response(fmt.deposit) 90 | def deposit_address(self, **kwargs): 91 | raise NotImplementedError() 92 | 93 | """ 94 | Exchange Specific Methods 95 | """ 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /bitex/interfaces/vaultoro.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://api.vaultoro.com/#api-Basic_API 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import VaultoroREST 12 | from bitex.utils import return_api_response 13 | from bitex.formatters.vaultoro import VaultoroFormatter as fmt 14 | 15 | # Init Logging Facilities 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class Vaultoro(VaultoroREST): 20 | def __init__(self, key='', secret='', key_file=''): 21 | super(Vaultoro, self).__init__(key, secret) 22 | if key_file: 23 | self.load_key(key_file) 24 | 25 | def public_query(self, endpoint, method_verb=None, **kwargs): 26 | if not method_verb: 27 | method_verb = 'GET' 28 | return self.query(method_verb, endpoint, **kwargs) 29 | 30 | def private_query(self, endpoint, method_verb=None, **kwargs): 31 | if not method_verb: 32 | method_verb = 'GET' 33 | return self.query(method_verb, endpoint, authenticate=True, 34 | **kwargs) 35 | 36 | """ 37 | BitEx Standardized Methods 38 | """ 39 | 40 | @return_api_response(fmt.order_book) 41 | def order_book(self, pair, **kwargs): 42 | return self.public_query('orderbook') 43 | 44 | @return_api_response(fmt.ticker) 45 | def ticker(self, pair, **kwargs): 46 | return self.public_query('markets', params=kwargs) 47 | 48 | @return_api_response(fmt.trades) 49 | def trades(self, pair, count=250, **kwargs): 50 | q = {'count': count} 51 | q.update(kwargs) 52 | return self.public_query('latesttrades', params=q) 53 | 54 | def _place_order(self, pair, size, price, side, order_type, **kwargs): 55 | args = side, pair, order_type 56 | q = {'gld': size, 'price': price} 57 | return self.private_query('1/%s/%s/%s' % args, params=kwargs, 58 | method_verb='POST') 59 | 60 | @return_api_response(fmt.order) 61 | def bid(self, pair, price, size, order_type=None, **kwargs): 62 | if not order_type: 63 | order_type = 'limit' 64 | return self._place_order(pair, size, price, 'buy', order_type, 65 | **kwargs) 66 | 67 | @return_api_response(fmt.order) 68 | def ask(self, pair, price, size, order_type=None, **kwargs): 69 | if not order_type: 70 | order_type = 'limit' 71 | return self._place_order(pair, size, price, 'sell', order_type, 72 | **kwargs) 73 | 74 | @return_api_response(fmt.cancel) 75 | def cancel_order(self, order_id, **kwargs): 76 | return self.private_query('1/cancel/%s' % order_id, method_verb='POST', 77 | params=kwargs) 78 | 79 | @return_api_response(fmt.order_status) 80 | def order(self, order_id, **kwargs): 81 | return self.private_query('1/orders', params=kwargs) 82 | 83 | @return_api_response(fmt.balance) 84 | def balance(self, **kwargs): 85 | return self.private_query('1/balance', params=kwargs) 86 | 87 | @return_api_response(fmt.withdraw) 88 | def withdraw(self, size, *args, **kwargs): 89 | q = {'btc': size} 90 | q.update(kwargs) 91 | return self.private_query('1/withdraw', method_verb='POST', params=q) 92 | 93 | @return_api_response(fmt.deposit) 94 | def deposit_address(self, **kwargs): 95 | raise NotImplementedError -------------------------------------------------------------------------------- /bitex/interfaces/yunbi.py: -------------------------------------------------------------------------------- 1 | """ 2 | https://yunbi.com/documents/api/guide 3 | """ 4 | 5 | # Import Built-Ins 6 | import logging 7 | 8 | # Import Third-Party 9 | 10 | # Import Homebrew 11 | from bitex.api.REST import YunbiREST 12 | from bitex.utils import return_api_response 13 | from bitex.formatters.yunbi import YnbiFormatter as fmt 14 | 15 | # Init Logging Facilities 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class Yunbi(YunbiREST): 20 | def __init__(self, key='', secret='', key_file=''): 21 | super(Yunbi, self).__init__(key, secret) 22 | if key_file: 23 | self.load_key(key_file) 24 | 25 | def public_query(self, endpoint, **kwargs): 26 | return self.query('GET', endpoint + '.json', **kwargs) 27 | 28 | def private_query(self, endpoint, method_verb='POST', **kwargs): 29 | return self.query(method_verb, endpoint, authenticate=True, **kwargs) 30 | 31 | """ 32 | BitEx Standardized Methods 33 | """ 34 | 35 | @return_api_response(fmt.ticker) 36 | def ticker(self, pair=None, **kwargs): 37 | if pair: 38 | return self.public_query('tickers/%s' % pair, param=kwargs) 39 | else: 40 | return self.public_query('tickers', param=kwargs) 41 | 42 | @return_api_response(fmt.order_book) 43 | def order_book(self, pair, **kwargs): 44 | q = {'market': pair} 45 | q.update(kwargs) 46 | return self.public_query('order_book', params=q) 47 | 48 | @return_api_response(fmt.trades) 49 | def trades(self, pair, **kwargs): 50 | q = {'market': pair} 51 | q.update(kwargs) 52 | return self.public_query('trades', params=q) 53 | 54 | @return_api_response(fmt.order) 55 | def bid(self, pair, price, size, **kwargs): 56 | q = {'market': pair, 'side': 'buy', 'volume': size, 'price': price} 57 | q.update(kwargs) 58 | return self.private_query('orders.json', params=q) 59 | 60 | @return_api_response(fmt.order) 61 | def ask(self, pair, price, size, **kwargs): 62 | q = {'market': pair, 'side': 'sell', 'volume': size, 'price': price} 63 | q.update(kwargs) 64 | return self.private_query('orders.json', params=q) 65 | 66 | @return_api_response(fmt.cancel) 67 | def cancel_order(self, order_id, **kwargs): 68 | q = {'id': order_id} 69 | q.update(kwargs) 70 | return self.private_query('delete.json', params=q) 71 | 72 | @return_api_response(fmt.order_status) 73 | def order(self, order_id, **kwargs): 74 | q = {'id': order_id} 75 | q.update(kwargs) 76 | return self.private_query('delete.json', method_verb='GET', params=q) 77 | 78 | @return_api_response(fmt.balance) 79 | def balance(self, **kwargs): 80 | return self.private_query('members/me.json', params=kwargs) 81 | 82 | @return_api_response(fmt.withdraw) 83 | def withdraw(self, size, tar_addr, **kwargs): 84 | raise NotImplementedError() 85 | 86 | @return_api_response(fmt.deposit) 87 | def deposit_address(self, **kwargs): 88 | return self.private_query('deposit_addres.json', method_verb='GET', 89 | params=kwargs) 90 | 91 | """ 92 | Exchange Specific Methods 93 | """ 94 | 95 | @return_api_response(None) 96 | def pairs(self): 97 | return self.public_query('symbols') 98 | 99 | @return_api_response(None) 100 | def ohlc(self, pair, **kwargs): 101 | q = {'market': pair} 102 | q.update(kwargs) 103 | return self.public_query('k', params=q) 104 | 105 | @return_api_response(None) 106 | def auction(self, pair): 107 | return self.public_query('auction/%s' % pair) 108 | 109 | @return_api_response(None) 110 | def auction_history(self, pair, **kwargs): 111 | return self.public_query('auction/%s/history' % pair, params=kwargs) 112 | -------------------------------------------------------------------------------- /bitex/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Provides utility functions used across more than one module or sub module. 3 | 4 | """ 5 | 6 | # Import Built-Ins 7 | import logging 8 | import json 9 | from functools import wraps 10 | 11 | # Import Third-Party 12 | import requests 13 | 14 | # Import Homebrew 15 | 16 | # Init Logging Facilities 17 | log = logging.getLogger(__name__) 18 | 19 | 20 | def return_api_response(formatter=None): 21 | """ 22 | Decorator, which Applies the referenced formatter (if available) to the 23 | function output and adds it to the APIResponse Object's `formatted` 24 | attribute. 25 | :param formatter: bitex.formatters.Formatter() obj 26 | :return: bitex.api.response.APIResponse() 27 | """ 28 | def decorator(func): 29 | @wraps(func) 30 | def wrapper(*args, **kwargs): 31 | try: 32 | r = func(*args, **kwargs) 33 | except Exception: 34 | log.exception("return_api_response(): Error during call to %s(%s, %s)", 35 | func.__name__, args, kwargs) 36 | raise 37 | 38 | # Check Status 39 | try: 40 | r.raise_for_status() 41 | except requests.HTTPError: 42 | log.exception("return_api_response: HTTPError for url %s", 43 | r.request.url) 44 | 45 | # Verify json data 46 | try: 47 | data = r.json() 48 | except json.JSONDecodeError: 49 | log.error('return_api_response: Error while parsing json. ' 50 | 'Request url was: %s, result is: ' 51 | '%s', r.request.url, r.text) 52 | data = None 53 | except Exception: 54 | log.exception("return_api_response(): Unexpected error while parsing " 55 | "json from %s", r.request.url) 56 | raise 57 | 58 | # Format, if available 59 | if formatter is not None and data: 60 | try: 61 | r.formatted = formatter(data, *args, **kwargs) 62 | except Exception: 63 | log.exception("Error while applying formatter!") 64 | 65 | return r 66 | 67 | return wrapper 68 | return decorator 69 | -------------------------------------------------------------------------------- /dist/BitEx-1.1.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/dist/BitEx-1.1.0.tar.gz -------------------------------------------------------------------------------- /dist/BitEx-1.1.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/dist/BitEx-1.1.1.tar.gz -------------------------------------------------------------------------------- /dist/BitEx-1.1.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/dist/BitEx-1.1.2.tar.gz -------------------------------------------------------------------------------- /dist/BitEx-1.1.3.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/dist/BitEx-1.1.3.tar.gz -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help 23 | help: 24 | @echo "Please use \`make ' where is one of" 25 | @echo " html to make standalone HTML files" 26 | @echo " dirhtml to make HTML files named index.html in directories" 27 | @echo " singlehtml to make a single large HTML file" 28 | @echo " pickle to make pickle files" 29 | @echo " json to make JSON files" 30 | @echo " htmlhelp to make HTML files and a HTML help project" 31 | @echo " qthelp to make HTML files and a qthelp project" 32 | @echo " applehelp to make an Apple Help Book" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | @echo " coverage to run coverage check of the documentation (if enabled)" 49 | 50 | .PHONY: clean 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | .PHONY: html 55 | html: 56 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 57 | @echo 58 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 59 | 60 | .PHONY: dirhtml 61 | dirhtml: 62 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 63 | @echo 64 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 65 | 66 | .PHONY: singlehtml 67 | singlehtml: 68 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 69 | @echo 70 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 71 | 72 | .PHONY: pickle 73 | pickle: 74 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 75 | @echo 76 | @echo "Build finished; now you can process the pickle files." 77 | 78 | .PHONY: json 79 | json: 80 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 81 | @echo 82 | @echo "Build finished; now you can process the JSON files." 83 | 84 | .PHONY: htmlhelp 85 | htmlhelp: 86 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 87 | @echo 88 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 89 | ".hhp project file in $(BUILDDIR)/htmlhelp." 90 | 91 | .PHONY: qthelp 92 | qthelp: 93 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 94 | @echo 95 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 96 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 97 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/BitEx.qhcp" 98 | @echo "To view the help file:" 99 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/BitEx.qhc" 100 | 101 | .PHONY: applehelp 102 | applehelp: 103 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 104 | @echo 105 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 106 | @echo "N.B. You won't be able to view it unless you put it in" \ 107 | "~/Library/Documentation/Help or install it in your application" \ 108 | "bundle." 109 | 110 | .PHONY: devhelp 111 | devhelp: 112 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 113 | @echo 114 | @echo "Build finished." 115 | @echo "To view the help file:" 116 | @echo "# mkdir -p $$HOME/.local/share/devhelp/BitEx" 117 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/BitEx" 118 | @echo "# devhelp" 119 | 120 | .PHONY: epub 121 | epub: 122 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 123 | @echo 124 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 125 | 126 | .PHONY: latex 127 | latex: 128 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 129 | @echo 130 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 131 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 132 | "(use \`make latexpdf' here to do that automatically)." 133 | 134 | .PHONY: latexpdf 135 | latexpdf: 136 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 137 | @echo "Running LaTeX files through pdflatex..." 138 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 139 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 140 | 141 | .PHONY: latexpdfja 142 | latexpdfja: 143 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 144 | @echo "Running LaTeX files through platex and dvipdfmx..." 145 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 146 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 147 | 148 | .PHONY: text 149 | text: 150 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 151 | @echo 152 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 153 | 154 | .PHONY: man 155 | man: 156 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 157 | @echo 158 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 159 | 160 | .PHONY: texinfo 161 | texinfo: 162 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 163 | @echo 164 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 165 | @echo "Run \`make' in that directory to run these through makeinfo" \ 166 | "(use \`make info' here to do that automatically)." 167 | 168 | .PHONY: info 169 | info: 170 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 171 | @echo "Running Texinfo files through makeinfo..." 172 | make -C $(BUILDDIR)/texinfo info 173 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 174 | 175 | .PHONY: gettext 176 | gettext: 177 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 178 | @echo 179 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 180 | 181 | .PHONY: changes 182 | changes: 183 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 184 | @echo 185 | @echo "The overview file is in $(BUILDDIR)/changes." 186 | 187 | .PHONY: linkcheck 188 | linkcheck: 189 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 190 | @echo 191 | @echo "Link check complete; look for any errors in the above output " \ 192 | "or in $(BUILDDIR)/linkcheck/output.txt." 193 | 194 | .PHONY: doctest 195 | doctest: 196 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 197 | @echo "Testing of doctests in the sources finished, look at the " \ 198 | "results in $(BUILDDIR)/doctest/output.txt." 199 | 200 | .PHONY: coverage 201 | coverage: 202 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 203 | @echo "Testing of coverage in the sources finished, look at the " \ 204 | "results in $(BUILDDIR)/coverage/python.txt." 205 | 206 | .PHONY: xml 207 | xml: 208 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 209 | @echo 210 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 211 | 212 | .PHONY: pseudoxml 213 | pseudoxml: 214 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 215 | @echo 216 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 217 | -------------------------------------------------------------------------------- /docs/_build/doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/docs/_build/doctrees/environment.pickle -------------------------------------------------------------------------------- /docs/_build/doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/docs/_build/doctrees/index.doctree -------------------------------------------------------------------------------- /docs/_build/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 181cc22a652c8dc72ab89c4b76f4316e 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/index.txt: -------------------------------------------------------------------------------- 1 | .. BitEx documentation master file, created by 2 | sphinx-quickstart on Thu Nov 24 10:29:19 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to BitEx's documentation! 7 | ================================= 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | 15 | 16 | Indices and tables 17 | ================== 18 | 19 | * :ref:`genindex` 20 | * :ref:`modindex` 21 | * :ref:`search` 22 | 23 | -------------------------------------------------------------------------------- /docs/_build/html/_static/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/docs/_build/html/_static/ajax-loader.gif -------------------------------------------------------------------------------- /docs/_build/html/_static/comment-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/docs/_build/html/_static/comment-bright.png -------------------------------------------------------------------------------- /docs/_build/html/_static/comment-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/docs/_build/html/_static/comment-close.png -------------------------------------------------------------------------------- /docs/_build/html/_static/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/docs/_build/html/_static/comment.png -------------------------------------------------------------------------------- /docs/_build/html/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Sphinx JavaScript utilities for all documentation. 6 | * 7 | * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | /** 13 | * select a different prefix for underscore 14 | */ 15 | $u = _.noConflict(); 16 | 17 | /** 18 | * make the code below compatible with browsers without 19 | * an installed firebug like debugger 20 | if (!window.console || !console.firebug) { 21 | var names = ["log", "debug", "info", "warn", "error", "assert", "dir", 22 | "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", 23 | "profile", "profileEnd"]; 24 | window.console = {}; 25 | for (var i = 0; i < names.length; ++i) 26 | window.console[names[i]] = function() {}; 27 | } 28 | */ 29 | 30 | /** 31 | * small helper function to urldecode strings 32 | */ 33 | jQuery.urldecode = function(x) { 34 | return decodeURIComponent(x).replace(/\+/g, ' '); 35 | }; 36 | 37 | /** 38 | * small helper function to urlencode strings 39 | */ 40 | jQuery.urlencode = encodeURIComponent; 41 | 42 | /** 43 | * This function returns the parsed url parameters of the 44 | * current request. Multiple values per key are supported, 45 | * it will always return arrays of strings for the value parts. 46 | */ 47 | jQuery.getQueryParameters = function(s) { 48 | if (typeof s == 'undefined') 49 | s = document.location.search; 50 | var parts = s.substr(s.indexOf('?') + 1).split('&'); 51 | var result = {}; 52 | for (var i = 0; i < parts.length; i++) { 53 | var tmp = parts[i].split('=', 2); 54 | var key = jQuery.urldecode(tmp[0]); 55 | var value = jQuery.urldecode(tmp[1]); 56 | if (key in result) 57 | result[key].push(value); 58 | else 59 | result[key] = [value]; 60 | } 61 | return result; 62 | }; 63 | 64 | /** 65 | * highlight a given string on a jquery object by wrapping it in 66 | * span elements with the given class name. 67 | */ 68 | jQuery.fn.highlightText = function(text, className) { 69 | function highlight(node) { 70 | if (node.nodeType == 3) { 71 | var val = node.nodeValue; 72 | var pos = val.toLowerCase().indexOf(text); 73 | if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { 74 | var span = document.createElement("span"); 75 | span.className = className; 76 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 77 | node.parentNode.insertBefore(span, node.parentNode.insertBefore( 78 | document.createTextNode(val.substr(pos + text.length)), 79 | node.nextSibling)); 80 | node.nodeValue = val.substr(0, pos); 81 | } 82 | } 83 | else if (!jQuery(node).is("button, select, textarea")) { 84 | jQuery.each(node.childNodes, function() { 85 | highlight(this); 86 | }); 87 | } 88 | } 89 | return this.each(function() { 90 | highlight(this); 91 | }); 92 | }; 93 | 94 | /* 95 | * backward compatibility for jQuery.browser 96 | * This will be supported until firefox bug is fixed. 97 | */ 98 | if (!jQuery.browser) { 99 | jQuery.uaMatch = function(ua) { 100 | ua = ua.toLowerCase(); 101 | 102 | var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 103 | /(webkit)[ \/]([\w.]+)/.exec(ua) || 104 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 105 | /(msie) ([\w.]+)/.exec(ua) || 106 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 107 | []; 108 | 109 | return { 110 | browser: match[ 1 ] || "", 111 | version: match[ 2 ] || "0" 112 | }; 113 | }; 114 | jQuery.browser = {}; 115 | jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; 116 | } 117 | 118 | /** 119 | * Small JavaScript module for the documentation. 120 | */ 121 | var Documentation = { 122 | 123 | init : function() { 124 | this.fixFirefoxAnchorBug(); 125 | this.highlightSearchWords(); 126 | this.initIndexTable(); 127 | }, 128 | 129 | /** 130 | * i18n support 131 | */ 132 | TRANSLATIONS : {}, 133 | PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, 134 | LOCALE : 'unknown', 135 | 136 | // gettext and ngettext don't access this so that the functions 137 | // can safely bound to a different name (_ = Documentation.gettext) 138 | gettext : function(string) { 139 | var translated = Documentation.TRANSLATIONS[string]; 140 | if (typeof translated == 'undefined') 141 | return string; 142 | return (typeof translated == 'string') ? translated : translated[0]; 143 | }, 144 | 145 | ngettext : function(singular, plural, n) { 146 | var translated = Documentation.TRANSLATIONS[singular]; 147 | if (typeof translated == 'undefined') 148 | return (n == 1) ? singular : plural; 149 | return translated[Documentation.PLURALEXPR(n)]; 150 | }, 151 | 152 | addTranslations : function(catalog) { 153 | for (var key in catalog.messages) 154 | this.TRANSLATIONS[key] = catalog.messages[key]; 155 | this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); 156 | this.LOCALE = catalog.locale; 157 | }, 158 | 159 | /** 160 | * add context elements like header anchor links 161 | */ 162 | addContextElements : function() { 163 | $('div[id] > :header:first').each(function() { 164 | $('\u00B6'). 165 | attr('href', '#' + this.id). 166 | attr('title', _('Permalink to this headline')). 167 | appendTo(this); 168 | }); 169 | $('dt[id]').each(function() { 170 | $('\u00B6'). 171 | attr('href', '#' + this.id). 172 | attr('title', _('Permalink to this definition')). 173 | appendTo(this); 174 | }); 175 | }, 176 | 177 | /** 178 | * workaround a firefox stupidity 179 | * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 180 | */ 181 | fixFirefoxAnchorBug : function() { 182 | if (document.location.hash) 183 | window.setTimeout(function() { 184 | document.location.href += ''; 185 | }, 10); 186 | }, 187 | 188 | /** 189 | * highlight the search words provided in the url in the text 190 | */ 191 | highlightSearchWords : function() { 192 | var params = $.getQueryParameters(); 193 | var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; 194 | if (terms.length) { 195 | var body = $('div.body'); 196 | if (!body.length) { 197 | body = $('body'); 198 | } 199 | window.setTimeout(function() { 200 | $.each(terms, function() { 201 | body.highlightText(this.toLowerCase(), 'highlighted'); 202 | }); 203 | }, 10); 204 | $('') 206 | .appendTo($('#searchbox')); 207 | } 208 | }, 209 | 210 | /** 211 | * init the domain index toggle buttons 212 | */ 213 | initIndexTable : function() { 214 | var togglers = $('img.toggler').click(function() { 215 | var src = $(this).attr('src'); 216 | var idnum = $(this).attr('id').substr(7); 217 | $('tr.cg-' + idnum).toggle(); 218 | if (src.substr(-9) == 'minus.png') 219 | $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); 220 | else 221 | $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); 222 | }).css('display', ''); 223 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { 224 | togglers.click(); 225 | } 226 | }, 227 | 228 | /** 229 | * helper function to hide the search marks again 230 | */ 231 | hideSearchWords : function() { 232 | $('#searchbox .highlight-link').fadeOut(300); 233 | $('span.highlighted').removeClass('highlighted'); 234 | }, 235 | 236 | /** 237 | * make the url absolute 238 | */ 239 | makeURL : function(relativeURL) { 240 | return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; 241 | }, 242 | 243 | /** 244 | * get the current relative url 245 | */ 246 | getCurrentURL : function() { 247 | var path = document.location.pathname; 248 | var parts = path.split(/\//); 249 | $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { 250 | if (this == '..') 251 | parts.pop(); 252 | }); 253 | var url = parts.join('/'); 254 | return path.substring(url.lastIndexOf('/') + 1, path.length - 1); 255 | } 256 | }; 257 | 258 | // quick alias for translations 259 | _ = Documentation.gettext; 260 | 261 | $(document).ready(function() { 262 | Documentation.init(); 263 | }); 264 | -------------------------------------------------------------------------------- /docs/_build/html/_static/down-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/docs/_build/html/_static/down-pressed.png -------------------------------------------------------------------------------- /docs/_build/html/_static/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/docs/_build/html/_static/down.png -------------------------------------------------------------------------------- /docs/_build/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/docs/_build/html/_static/file.png -------------------------------------------------------------------------------- /docs/_build/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/docs/_build/html/_static/minus.png -------------------------------------------------------------------------------- /docs/_build/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/docs/_build/html/_static/plus.png -------------------------------------------------------------------------------- /docs/_build/html/_static/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #eeffcc; } 3 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ 8 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 9 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 10 | .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ 11 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 12 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 13 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 14 | .highlight .ge { font-style: italic } /* Generic.Emph */ 15 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 16 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 17 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 18 | .highlight .go { color: #333333 } /* Generic.Output */ 19 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 20 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 21 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 22 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 23 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 24 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 25 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 26 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 27 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 28 | .highlight .kt { color: #902000 } /* Keyword.Type */ 29 | .highlight .m { color: #208050 } /* Literal.Number */ 30 | .highlight .s { color: #4070a0 } /* Literal.String */ 31 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 32 | .highlight .nb { color: #007020 } /* Name.Builtin */ 33 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 34 | .highlight .no { color: #60add5 } /* Name.Constant */ 35 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 36 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 37 | .highlight .ne { color: #007020 } /* Name.Exception */ 38 | .highlight .nf { color: #06287e } /* Name.Function */ 39 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 40 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 41 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 42 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 43 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 44 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 45 | .highlight .mb { color: #208050 } /* Literal.Number.Bin */ 46 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 47 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 48 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 49 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 50 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 51 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 52 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 53 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 54 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 55 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 56 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 57 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 58 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 59 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 60 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 61 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 62 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 63 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 64 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 65 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/_build/html/_static/up-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/docs/_build/html/_static/up-pressed.png -------------------------------------------------------------------------------- /docs/_build/html/_static/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/docs/_build/html/_static/up.png -------------------------------------------------------------------------------- /docs/_build/html/genindex.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Index — BitEx 0.08 documentation 11 | 12 | 13 | 14 | 15 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 |
37 |
38 |
39 | 40 | 41 |

Index

42 | 43 |
44 | 45 |
46 | 47 | 48 |
49 |
50 |
51 | 77 |
78 |
79 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /docs/_build/html/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Welcome to BitEx’s documentation! — BitEx 0.08 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 |
37 |
38 | 39 |
40 |

Welcome to BitEx’s documentation!

41 |

Contents:

42 |
43 |
    44 |
45 |
46 |
47 |
48 |

Indices and tables

49 | 54 |
55 | 56 | 57 |
58 |
59 |
60 | 96 |
97 |
98 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /docs/_build/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crypto-toolbox/bitex/56d46ea3db6de5219a72dad9b052fbabc921232f/docs/_build/html/objects.inv -------------------------------------------------------------------------------- /docs/_build/html/search.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Search — BitEx 0.08 documentation 10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 |
44 |
45 |
46 | 47 |

Search

48 |
49 | 50 |

51 | Please activate JavaScript to enable the search 52 | functionality. 53 |

54 |
55 |

56 | From here you can search these documents. Enter your search 57 | words into the box below and click "search". Note that the search 58 | function will automatically search for all of the words. Pages 59 | containing fewer words won't appear in the result list. 60 |

61 |
62 | 63 | 64 | 65 |
66 | 67 |
68 | 69 |
70 | 71 |
72 |
73 |
74 | 84 |
85 |
86 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /docs/_build/html/searchindex.js: -------------------------------------------------------------------------------- 1 | Search.setIndex({envversion:47,filenames:["index"],objects:{},objnames:{},objtypes:{},terms:{content:0,index:0,modul:0,page:0,search:0},titles:["Welcome to BitEx’s documentation!"],titleterms:{bitex:0,document:0,indic:0,tabl:0,welcom:0}}) -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. BitEx documentation master file, created by 2 | sphinx-quickstart on Thu Nov 24 10:29:19 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to BitEx's documentation! 7 | ================================= 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | 15 | 16 | Indices and tables 17 | ================== 18 | 19 | * :ref:`genindex` 20 | * :ref:`modindex` 21 | * :ref:`search` 22 | 23 | -------------------------------------------------------------------------------- /docs/rtd-requirements.txt: -------------------------------------------------------------------------------- 1 | bitex 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | VERSION = '1.2.12' 4 | 5 | 6 | setup(name='BitEx', version=VERSION, author='Nils Diefenbach', 7 | author_email='23okrs20+pypi@mykolab.com', 8 | url="https://github.com/nlsdfnbch/bitex.git", 9 | test_suite='nose.collector', tests_require=['nose'], 10 | packages=find_packages(exclude=['contrib', 'docs', 'tests*', 'travis']), 11 | install_requires=['requests', 'websocket-client', 'autobahn', 'pusherclient'], 12 | description='Python3-based API Framework for Crypto Exchanges', 13 | license='MIT', classifiers=['Development Status :: 4 - Beta', 14 | 'Intended Audience :: Developers'], 15 | ) -------------------------------------------------------------------------------- /tests/kraken.key: -------------------------------------------------------------------------------- 1 | This_is_a_key 2 | This_is_a_secret -------------------------------------------------------------------------------- /tests/test.key: -------------------------------------------------------------------------------- 1 | This_is_a_key 2 | This_is_a_secret -------------------------------------------------------------------------------- /tests/test_api.py: -------------------------------------------------------------------------------- 1 | # Import Built-ins 2 | import logging 3 | import unittest 4 | import requests 5 | import json 6 | # Import Third-Party 7 | 8 | # Import Homebrew 9 | from bitex.api.REST.api import APIClient 10 | from bitex.api.REST import KrakenREST, CryptopiaREST, CCEXRest, GeminiREST 11 | from bitex.api.REST import YunbiREST, RockTradingREST 12 | 13 | log = logging.getLogger(__name__) 14 | 15 | 16 | class APITests(unittest.TestCase): 17 | """ 18 | Tests APIs for connection establishment, authentication, key loading. 19 | """ 20 | def setUp(self): 21 | self.api = APIClient('http://google.com/api', api_version='v1', 22 | key='12345', secret='abcde') 23 | 24 | def tearDown(self): 25 | self.api = None 26 | 27 | def test_restapi_load_key(self): 28 | self.api.load_key("test.key") 29 | self.assertTrue(self.api.secret, "This_is_a_secret") 30 | self.assertTrue(self.api.key, "This_is_a_key") 31 | 32 | def test_restapi_nonce(self): 33 | n = self.api.nonce() 34 | self.assertTrue(n.strip().isdigit()) 35 | 36 | def test_restapi_query(self): 37 | # Test that the unauthenticated request is built correctly 38 | r = self.api.query('testing/endpoint/', authenticate=False, 39 | request_method=requests.get, 40 | params={'test_param': "chimichanga"}) 41 | url = 'http://google.com/api/v1/testing/endpoint/?test_param=chimichanga' 42 | self.assertTrue(r.request.url == url) 43 | 44 | # Test that authentication requests are built correctly 45 | r = self.api.query('testing/endpoint/', authenticate=True, 46 | request_method=requests.get, 47 | params={'test_param': "chimichanga"}) 48 | url = 'http://google.com/api/v1/testing/endpoint/?test_param=authenticated_chimichanga' 49 | self.assertTrue(r.request.url == url) 50 | 51 | def test_sign_returns_tuple_of_str_and_dict(self): 52 | r = self.api.sign() 53 | self.assertIsInstance(r, tuple) 54 | self.assertIsInstance(r[0], str) 55 | self.assertIsInstance(r[1], dict) 56 | 57 | 58 | class KrakenAPITest(APITests): 59 | def setUp(self): 60 | self.api = KrakenREST() 61 | self.api.load_key('kraken.key') 62 | 63 | def test_public_query(self): 64 | # query() returns a valid requests.Response object 65 | r = self.api.query('GET', 'Time') 66 | self.assertIsInstance(r, requests.Response) 67 | self.assertEqual(r.status_code, 200) 68 | 69 | # query() is successful (No errors) 70 | self.assertTrue(r.json()['error'] == [], 71 | "Error in Response: %s" % r.json()['error']) 72 | 73 | def test_private_query(self): 74 | # API Key and secret are loaded? 75 | self.assertTrue(self.api.key, 'API Key is empty!') 76 | self.assertTrue(self.api.secret, 'Secret Key is empty!') 77 | 78 | # query() returns a valid request object 79 | r = self.api.query('POST', 'private/OpenOrders', authenticate=True) 80 | self.assertIsInstance(r, requests.Response) 81 | 82 | # query() with flag authenticate=True builds valid signature (No errors) 83 | self.assertTrue(r.json()['error'] == [], 84 | "Error in Response: %s" % r.json()['error']) 85 | 86 | 87 | class CryptopiaAPITest(APITests): 88 | def setUp(self): 89 | self.api = CryptopiaREST() 90 | 91 | def test_public_query(self): 92 | # query() returns a valid requests.Response object 93 | r = self.api.query('GET', 'GetMarketOrders/101') 94 | self.assertIsInstance(r, requests.Response) 95 | self.assertEqual(r.status_code, 200) 96 | 97 | # query() is successful (No errors) 98 | self.assertTrue(r.json()['Success'], 99 | "Error in Response: %s" % r.request.url) 100 | 101 | def test_private_query(self): 102 | self.fail("Test not implemented!") 103 | 104 | 105 | class CCEXAPITest(APITests): 106 | def setUp(self): 107 | self.api = CCEXRest() 108 | 109 | def test_public_query(self): 110 | # query() returns a valid requests.Response object 111 | r = self.api.query('GET', 'api_pub.html?a=getorderbook', 112 | params={'market': 'ltc-btc', 'type': 'both'}) 113 | self.assertIsInstance(r, requests.Response) 114 | self.assertEqual(r.status_code, 200) 115 | 116 | # query() is successful (No errors) 117 | self.assertTrue(r.json()['success'], 118 | "Error in Response: %s" % r.request.url) 119 | 120 | def test_private_query(self): 121 | self.fail("Test not implemented!") 122 | 123 | 124 | class GeminiAPITest(APITests): 125 | def setUp(self): 126 | self.api = GeminiREST() 127 | 128 | def test_public_query(self): 129 | # query() returns a valid requests.Response object 130 | r = self.api.query('GET', 'book/ETHBTC') 131 | self.assertIsInstance(r, requests.Response) 132 | self.assertEqual(r.status_code, 200) 133 | 134 | # query() is successful (No error message) 135 | self.assertNotIn('message', r.json(), "Error in Response: %s" % r.request.url) 136 | 137 | def test_private_query(self): 138 | self.fail("Test not implemented!") 139 | 140 | 141 | class YunbiAPITest(APITests): 142 | def setUp(self): 143 | self.api = YunbiREST() 144 | 145 | def test_public_query(self): 146 | # query() returns a valid requests.Response object 147 | r = self.api.query('GET', 'markets.json') 148 | self.assertIsInstance(r, requests.Response) 149 | self.assertEqual(r.status_code, 200) 150 | 151 | # query() is successful (No error message) 152 | self.assertNotIn('message', r.json()) 153 | 154 | def test_private_query(self): 155 | self.fail("Test not implemented!") 156 | 157 | 158 | class TheRockTradingAPITest(APITests): 159 | def setUp(self): 160 | self.api = RockTradingREST() 161 | 162 | def test_public_query(self): 163 | # query() returns a valid requests.Response object 164 | r = self.api.query('GET', 'funds/BTCEUR/orderbook') 165 | self.assertIsInstance(r, requests.Response) 166 | self.assertEqual(r.status_code, 200) 167 | 168 | # query() is successful (No error message) 169 | self.assertNotIn('errors', r.json()) 170 | 171 | def test_private_query(self): 172 | self.fail("Test not implemented!") 173 | 174 | -------------------------------------------------------------------------------- /tests/test_formatters.py: -------------------------------------------------------------------------------- 1 | # Import Built-Ins 2 | import logging 3 | from unittest import TestCase 4 | # Import Third-Party 5 | 6 | # Import Homebrew 7 | from bitex.formatters.kraken import KrknFormatter 8 | from bitex.formatters.bitfinex import BtfxFormatter 9 | from bitex.formatters.bitstamp import BtstFormatter 10 | from bitex.formatters.bittrex import BtrxFormatter 11 | 12 | 13 | # Init Logging Facilities 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | class FormatterTest(TestCase): 18 | def test_krknFormatter_format_pair_works_correctly(self): 19 | fmt = KrknFormatter() 20 | test_pairs = ['btcusd', 'ltcbtc', 'xmr_btc', 'xxbtxltc'] 21 | expected_output = ['XXBTZUSD', 'XLTCXXBT', 'XXMRXXBT', 'XXBTXLTC'] 22 | fmt_output = [fmt.format_pair(pair) for pair in test_pairs] 23 | self.assertEqual(fmt_output, expected_output) 24 | 25 | def test_BtstFormatter_format_pair_works_correctly(self): 26 | fmt = BtstFormatter() 27 | test_pairs = ['btcusd', 'ltcbtc', 'xmr_btc', 'BTCEUR'] 28 | expected_output = ['btcusd', 'ltcbtc', 'xmrbtc', 'btceur'] 29 | fmt_output = [fmt.format_pair(pair) for pair in test_pairs] 30 | self.assertEqual(fmt_output, expected_output) 31 | 32 | def test_BtfxFormatter_format_pair_works_correctly(self): 33 | fmt = BtfxFormatter() 34 | test_pairs = ['btcusd', 'ltcbtc', 'xmr_btc', 'BTCEUR'] 35 | expected_output = ['BTCUSD', 'LTCBTC', 'XMRBTC', 'BTCEUR'] 36 | fmt_output = [fmt.format_pair(pair) for pair in test_pairs] 37 | self.assertEqual(fmt_output, expected_output) 38 | 39 | def test_BtrxFormatter_format_pair_works_correctly(self): 40 | fmt = BtrxFormatter() 41 | test_pairs = ['btcusd', 'ltcbtc', 'xmr_btc', 'BTCEUR'] 42 | expected_output = ['BTC-USD', 'BTC-LTC', 'XMR-BTC', 'BTC-EUR'] 43 | fmt_output = [fmt.format_pair(pair) for pair in test_pairs] 44 | self.assertEqual(fmt_output, expected_output) --------------------------------------------------------------------------------