OKEX api v3 SDK Python實現

#!/usr/bin/python
# -*- coding: utf-8 -*-

import json, datetime, hmac, base64, zlib, threading, requests, time
from common.Common import Common
from common.WebSocketProtocol import WebSocketProtocol
from retrying import retry

CONTENT_TYPE = 'Content-Type'
OK_ACCESS_KEY = 'OK-ACCESS-KEY'
OK_ACCESS_SIGN = 'OK-ACCESS-SIGN'
OK_ACCESS_TIMESTAMP = 'OK-ACCESS-TIMESTAMP'
OK_ACCESS_PASSPHRASE = 'OK-ACCESS-PASSPHRASE'
APPLICATION_JSON = 'application/json'
BASE_URL = 'https://www.okex.com'
BASE_DEBUG_URL = 'https://www.okex.me'
WS_URL = 'wss://real.okex.com:8443/ws/v3'
API_KEY = ''
SECRET_KEY = ''
PASS_PHRASE = ''


class OkAPI:

    def __init__(self, client):

        self.client = client
        self.__baseUrl = BASE_URL if Common.is_release else BASE_DEBUG_URL
        self.__apikey = API_KEY
        self.__secretkey = SECRET_KEY
        self.__passphrase = PASS_PHRASE
        self.__sub = None
        self.__ws_subs = dict()
        self.__direct = None

    def get_header(self, api_key, sign, timestamp, passphrase):
        header = dict()
        header[CONTENT_TYPE] = APPLICATION_JSON
        header[OK_ACCESS_KEY] = api_key
        header[OK_ACCESS_SIGN] = sign
        header[OK_ACCESS_TIMESTAMP] = str(timestamp)
        header[OK_ACCESS_PASSPHRASE] = passphrase
        return header

    def parse_params_to_str(seff, params):
        url = '?'
        for key, value in params.items():
            url = url + str(key) + '=' + str(value) + '&'
        return url[0:-1]

    def timestamp(self):
        timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')
        timestamp = timestamp[0:-3] + 'Z'
        return timestamp

    def transfer(self, coin, fund, amount):

        path = '/api/account/v3/transfer'
        params = {'currency': fund, 'amount': amount, 'from': '1', 'to': '5',
                  'instrument_id': '{}-{}'.format(coin, fund)}
        return self.httpPost(path, params)

    def ticker(self, coin):

        path = '/api/spot/v3/instruments/{}/ticker'.format(coin)
        return self.httpGet(path)

    def tickers(self):

        path = '/api/spot/v3/instruments/ticker'
        return self.httpGet(path)

    def f_ticker(self, coin):

        path = '/api/futures/v3/instruments/{}/ticker'.format(coin)
        return self.httpGet(path)

    def sf_ticker(self, coin):

        path = '/api/swap/v3/instruments/{}/ticker'.format(coin)
        return self.httpGet(path)

    def f_depth(self, coin):

        path = '/api/futures/v3/instruments/{}/book'.format(coin)
        params = {'instrument_id': coin, 'size': 10}
        return self.httpGet(path, params)

    def f_position(self, pair=None):

        path = '/api/futures/v3/{}/position'.format(pair) if pair else '/api/futures/v3/position'
        return self.httpGet(path)

    def sf_position(self, pair=None):

        path = '/api/swap/v3/{}/position'.format(pair) if pair else '/api/swap/v3/position'
        return self.httpGet(path)

    def coin_pair(self):

        path = '/api/spot/v3/instruments'
        return self.httpGet(path)

    def f_coin_pair(self):
        path = '/api/futures/v3/instruments'
        return self.httpGet(path)

    def test(self):
        path = '/api/futures/v3/instruments/ETH-USD-190329/price_limit'
        return self.httpGet(path)

    def trade(self, side, coin, price, amount):

        path = '/api/spot/v3/orders'
        params = {'type': 'limit', 'side': side, 'instrument_id': coin, 'size': amount, 'price': price,
                  'margin_trading': 1}
        return self.httpPost(path, params)

    def l_trade(self, side, coin, price, amount):

        path = '/api/margin/v3/orders'
        params = {'type': 'limit', 'side': side, 'instrument_id': coin, 'size': amount, 'price': price,
                  'margin_trading': 2}
        return self.httpPost(path, params)

    def f_trade(self, coin, side, price, amount, client_oid=''):

        path = '/api/futures/v3/order'
        params = {'instrument_id': coin, 'type': side, 'price': price, 'size': amount, 'match_price': '0',
                  'client_oid': client_oid}
        return self.httpPost(path, params)

    def sf_trade(self, coin, side, price, amount, client_oid=''):

        path = '/api/swap/v3/order'
        params = {'instrument_id': coin, 'type': side, 'price': price, 'size': amount, 'match_price': '0',
                  'client_oid': client_oid}
        return self.httpPost(path, params)

    def l_borrow(self, coin, currency, amount):

        path = '/api/margin/v3/accounts/borrow'
        params = {'instrument_id': coin, 'currency': currency, 'amount': amount}
        return self.httpPost(path, params)

    def l_borrowed(self, coin, status=0):

        path = '/api/margin/v3/accounts/{}/borrowed'.format(coin)
        params = {'status': status}
        return self.httpGet(path, params)

    def l_repay(self, coin, currency, amount, borrow_id=None):

        path = '/api/margin/v3/accounts/repayment'
        params = {'instrument_id': coin, 'currency': currency, 'amount': str(amount)}
        if borrow_id:
            params['borrow_id'] = borrow_id
        return self.httpPost(path, params)

    def order(self, coin, orderid):
        path = '/api/spot/v3/orders/' + orderid
        params = {'instrument_id': coin}
        return self.httpGet(path, params)

    def l_order(self, coin, orderid):
        path = '/api/margin/v3/orders/' + orderid
        params = {'instrument_id': coin}
        return self.httpGet(path, params)

    def orders(self):

        path = '/api/spot/v3/orders_pending'
        return self.httpGet(path)

    def kline(self, coin, interval, start='', end=''):

        path = '/api/spot/v3/instruments/{}/candles'.format(coin)
        params = {'granularity': interval, 'start': start, 'end': end}
        return self.httpGet(path, params)

    def f_kline(self, coin, interval, end=''):

        path = '/api/futures/v3/instruments/{}/candles'.format(coin)
        params = {'granularity': interval, 'end': end}
        return self.httpGet(path, params)

    def sf_kline(self, coin, interval, end=''):

        path = '/api/swap/v3/instruments/{}/candles'.format(coin)
        params = {'granularity': interval, 'end': end}
        return self.httpGet(path, params)

    def l_accounts(self):

        path = '/api/margin/v3/accounts'
        return self.httpGet(path)

    def l_account(self, coin):

        path = '/api/margin/v3/accounts/' + coin
        return self.httpGet(path)

    def account(self, coin):

        path = '/api/spot/v3/accounts/' + coin
        return self.httpGet(path)

    def l_availability(self, coin):

        path = '/api/margin/v3/accounts/{}/availability'.format(coin)
        return self.httpGet(path)

    def f_account(self, coin):

        path = '/api/futures/v3/accounts/{}'.format(coin)
        return self.httpGet(path)

    def cancelOrder(self, coin, orderid):
        path = '/api/spot/v3/cancel_orders/' + orderid
        params = {'instrument_id': coin}
        return self.httpPost(path, params)

    def l_cancelOrder(self, coin, orderid):
        path = '/api/margin/v3/cancel_orders/' + orderid
        params = {'instrument_id': coin, 'order_id': orderid}
        return self.httpPost(path, params)

    def f_cancel_order(self, coin, client_oid):

        path = '/api/futures/v3/cancel_order/{}/{}'.format(coin, client_oid)
        params = {'instrument_id': coin, 'client_oid': client_oid}
        return self.httpPost(path, params)

    def sf_cancel_order(self, coin, client_oid):

        path = '/api/swap/v3/cancel_order/{}/{}'.format(coin, client_oid)
        params = {'instrument_id': coin, 'client_oid': client_oid}
        return self.httpPost(path, params)

    def signature(self, timestamp, method, request_path, body, secret_key):

        if str(body) == '{}' or str(body) == 'None':
            body = ''
        message = str(timestamp) + str.upper(method) + request_path + str(body)
        mac = hmac.new(bytes(secret_key, encoding='utf8'), bytes(message, encoding='utf-8'), digestmod='sha256')
        d = mac.digest()
        return base64.b64encode(d)

    @retry(stop_max_attempt_number=3)
    def httpGet(self, path, data=None):

        if data: path = path + self.parse_params_to_str(data)
        url = self.__baseUrl + path
        timestamp = self.timestamp()
        header = self.get_header(self.__apikey, self.signature(timestamp, 'GET', path, '', self.__secretkey),
                                 timestamp, self.__passphrase)
        try:
            response = requests.get(url, headers=header, timeout=15)
        except Exception as e:
            raise Exception(e)

        return response.json()

    @retry(stop_max_attempt_number=3)
    def httpPost(self, path, data):

        url = self.__baseUrl + path
        body = json.dumps(data)
        timestamp = self.timestamp()
        header = self.get_header(self.__apikey, self.signature(timestamp, 'POST', path, body, self.__secretkey),
                                 timestamp, self.__passphrase)
        try:
            response = requests.post(url, data=body, headers=header)
        except Exception as e:
            raise Exception(e)

        return response.json()

    def httpDelete(self, path, data):

        if data: path = path + self.parse_params_to_str(data)
        url = self.__baseUrl + path
        timestamp = self.timestamp()
        header = self.get_header(self.__apikey, self.signature(timestamp, 'DELETE', path, '', self.__secretkey),
                                 timestamp, self.__passphrase)
        try:
            response = requests.delete(url, headers=header)
        except Exception as e:
            raise Exception(e)

        return response.json()

    def inflate(self, data):
        decompress = zlib.decompressobj(
            -zlib.MAX_WBITS  # see above
        )
        inflated = decompress.decompress(data)
        inflated += decompress.flush()
        return inflated

    def on_open(self, ws):

        print('ok_on_open', self.__ws_subs)

        if not Common.is_analyze:
            ts = str(int(datetime.datetime.now().timestamp()))
            sign = self.signature(ts, 'GET', '/users/self/verify', None, self.__secretkey)
            sub = {'op': 'login', 'args': [self.__apikey, self.__passphrase, ts, sign.decode("utf-8")]}
            ws.send(json.dumps(sub))
            time.sleep(1)

        s = []
        for p_k, p_v in self.__ws_subs.items():
            for k, v in p_v.items():
                if k == Common.ws_ticker:
                    s.append('spot/ticker:{}'.format(p_k))
                elif k == Common.ws_kline:
                    s.append('spot/candle{}s:{}'.format(v, p_k))
                elif k == Common.ws_trade:
                    s.append('spot/trade:{}'.format(p_k))
                elif k == Common.ws_f_ticker:
                    s.append('futures/ticker:{}'.format(p_k))
                elif k == Common.ws_f_kline:
                    for k_v in v:
                        s.append('futures/candle{}s:{}'.format(k_v, p_k))
                elif k == Common.ws_sf_ticker:
                    s.append('swap/ticker:{}'.format(p_k))
                elif k == Common.ws_sf_kline:
                    for k_v in v:
                        s.append('swap/candle{}s:{}'.format(k_v, p_k))

        ws.send(json.dumps({'op': 'subscribe', 'args': s}))

    def on_message(self, ws, message):

        data = json.loads(self.inflate(message))

        if 'table' in data:
            table = data['table']
            if table.find('spot/ticker') != -1:
                if self.__direct:
                    self.client.d_ws_ticker(data['data'])
                else:
                    self.client.ws_ticker(data['data'])
            elif table.find('futures/ticker') != -1:
                self.client.ws_f_ticker(data['data'])
            elif table.find('spot/candle') != -1:
                self.client.ws_kline(data)
            elif table.find('futures/candle') != -1:
                self.client.ws_f_kline(data)
            elif table.find('swap/candle') != -1:
                self.client.ws_sf_kline(data)
            elif table.find('spot/trade') != -1:
                if self.__direct:
                    self.client.d_ws_trade(data['data'])
                else:
                    self.client.ws_trade(data['data'])
            elif table.find('spot/account') != -1:
                if self.__direct:
                    self.client.d_ws_account(data['data'])
                else:
                    self.client.ws_account(data['data'])
            elif table.find('spot/margin_account') != -1:
                self.client.ws_l_account(data['data'])
            elif table.find('futures/account') != -1:
                self.client.ws_f_account(data['data'])
            elif table.find('swap/account') != -1:
                self.client.ws_sf_account(data['data'])
            elif table.find('spot/order') != -1:
                if self.__direct:
                    self.client.d_ws_order(data['data'])
                else:
                    self.client.ws_order(data['data'])
            elif table.find('futures/order') != -1:
                self.client.ws_f_order(data['data'])
            elif table.find('swap/order') != -1:
                self.client.ws_sf_order(data['data'])
            elif table.find('futures/position') != -1:
                self.client.ws_f_position(data['data'])
            elif table.find('swap/position') != -1:
                self.client.ws_sf_position(data['data'])
            elif table.find('depth') != -1:
                self.client.ws_depth(data['data'])
            elif table.find('depth5') != -1:
                pass

        elif 'event' in data:
            print(data)
            if data['event'] == 'login':
                s = []
                for p_k, p_v in self.__ws_subs.items():
                    for k, v in p_v.items():
                        if k == Common.ws_order:
                            s.append('spot/order:{}'.format(p_k))
                        elif k == Common.ws_account:
                            c, f = p_k.split('-')
                            s.extend(['spot/account:{}'.format(c), 'spot/account:{}'.format(f)])
                        elif k == Common.ws_l_account:
                            s.append('spot/margin_account:{}'.format(p_k))
                        elif k == Common.ws_f_account:
                            s.append('futures/account:{}'.format(p_k.split('-')[0]))
                        elif k == Common.ws_f_order:
                            s.append('futures/order:{}'.format(p_k))
                        elif k == Common.ws_f_position:
                            s.append('futures/position:{}'.format(p_k))
                        elif k == Common.ws_sf_account:
                            s.append('swap/account:{}'.format(p_k.split('-')[0]))
                        elif k == Common.ws_sf_order:
                            s.append('swap/order:{}'.format(p_k))
                        elif k == Common.ws_sf_position:
                            s.append('swap/position:{}'.format(p_k))

                ws.send(json.dumps({'op': 'subscribe', 'args': s}))

    def on_close(self, ws):
        print('ok_on_close', ws, self.__ws_subs)
        self.__sub = None

        if len(self.__ws_subs) > 0:
            time.sleep(1)
            self.__ws_sub_create()

    def ws_sub(self, pair, subs):

        s = []
        for k, v in subs.items():
            if pair in self.__ws_subs.keys():
                if k in self.__ws_subs[pair].keys():
                    if k == Common.ws_f_kline and self.__ws_subs[pair][k] != v:
                        self.__ws_subs[pair][k].extend(v)
                        s.append(k)
                    continue
                else:
                    self.__ws_subs[pair][k] = v
                    s.append(k)
            else:
                self.__ws_subs[pair] = {k: v}
                s.append(k)

        if self.__sub:
            w_s = []
            for k in s:
                if k == Common.ws_ticker:
                    w_s.append('spot/ticker:{}'.format(pair))
                elif k == Common.ws_trade:
                    w_s.append('spot/trade:{}'.format(pair))
                elif k == Common.ws_kline:
                    w_s.append('spot/candle{}s:{}'.format(subs[k], pair))
                elif k == Common.ws_order:
                    w_s.append('spot/order:{}'.format(pair))
                elif k == Common.ws_account:
                    c, f = pair.split('-')
                    w_s.extend(['spot/account:{}'.format(c), 'spot/account:{}'.format(f)])
                elif k == Common.ws_l_account:
                    w_s.append('spot/margin_account:{}'.format(pair))
                elif k == Common.ws_f_ticker:
                    w_s.append('futures/ticker:{}'.format(pair))
                elif k == Common.ws_f_kline:
                    for k_v in subs[k]:
                        w_s.append('futures/candle{}s:{}'.format(k_v, pair))
                elif k == Common.ws_f_account:
                    w_s.append('futures/account:{}'.format(pair.split('-')[0]))
                elif k == Common.ws_f_position:
                    w_s.append('futures/position:{}'.format(pair))
                elif k == Common.ws_f_order:
                    w_s.append('futures/order:{}'.format(pair))
                elif k == Common.ws_sf_ticker:
                    w_s.append('swap/ticker:{}'.format(pair))
                elif k == Common.ws_sf_kline:
                    for k_v in subs[k]:
                        w_s.append('swap/candle{}s:{}'.format(k_v, pair))
                elif k == Common.ws_sf_account:
                    w_s.append('swap/account:{}'.format(pair.split('-')[0]))
                elif k == Common.ws_sf_position:
                    w_s.append('swap/position:{}'.format(pair))
                elif k == Common.ws_sf_order:
                    w_s.append('swap/order:{}'.format(pair))

            self.__sub.send(json.dumps({'op': 'subscribe', 'args': w_s}))
        else:
            # threading.stack_size(1024 * 1024 * 100)
            t = threading.Thread(target=self.__ws_sub_create)
            t.start()

    def d_ws_sub(self, coins, subs):

        self.__direct = 1

        subs.update({Common.ws_order: ''})
        for i in coins:
            pair = i.replace('_', '-')
            self.__ws_subs[pair] = subs

        t = threading.Thread(target=self.__ws_sub_create)
        t.start()

    def ws_unsub(self, pair, subs):

        exist_subs = self.__ws_subs[pair]

        s = []
        for k, v in subs.items():
            exist_subs.pop(k)
            if k == Common.ws_ticker:
                s.append('spot/ticker:{}'.format(pair))
            if k == Common.ws_trade:
                s.append('spot/trade:{}'.format(pair))
            elif k == Common.ws_kline:
                s.append('spot/candle{}s:{}'.format(v, pair))

        self.__sub.send(json.dumps({'op': 'unsubscribe', 'args': s}))

        if len(exist_subs) == 0:
            self.__ws_subs.pop(pair)

        if len(self.__ws_subs) == 0:
            self.__sub.close()
            # self.__sub.close_connection()

    def __ws_sub_create(self):

        try:
            self.__sub = WebSocketProtocol(WS_URL, self.on_open, self.on_message, self.on_close)
            self.__sub.connect()
            self.__sub.run_forever(ping_interval=25)
        except Exception as e:
            print('異常了--ok--__ws_sub_create', e)
            time.sleep(5)
            self.__ws_sub_create()


from ws4py.client.threadedclient import WebSocketClient
import threading


class WebSocketProtocol(WebSocketClient):

    def __init__(self,url,on_open,on_message,on_closed):

        super().__init__(url)

        self._ping_interval = None
        self._event = None
        self._flag = None
        self.on_open = on_open
        self.on_message = on_message
        self.on_closed = on_closed
        self.on_ping = None

    def _send_ping(self):

        while self._flag:
            if not self._event.wait(self._ping_interval):
                if self.on_ping:
                    self.on_ping(self)
                else:
                    self.ping('ping')
            else:
                self._event.clear()

    def opened(self):

        self.on_open(self)

        if self._ping_interval:
            thread = threading.Thread(target=self._send_ping)
            thread.setDaemon(True)
            thread.start()

    def received_message(self, message):

        if self._ping_interval:
            self._event.set()

        self.on_message(self,message.data)

    def closed(self, code, reason=None):

        if self._ping_interval:
            self._flag = False
            self._event.set()

        self.on_closed(self)

    def ponged(self, pong):

        print('ponged:',pong)

    def run_forever(self,ping_interval=None,on_ping=None):

        if ping_interval:
            self._ping_interval = ping_interval
            self._event = threading.Event()
            self._flag = True
            self.on_ping = on_ping

        super().run_forever()



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章