#!/usr/bin/python#-*- coding: utf-8 -*-#Note: it is okex v1. okex v3 api has many bugs, for example kline don't support 1hour when set start or end
# https://www.cnblogs.com/fangbei/p/okex-api-v1.html
importtime,datetimeimportjsonimportmathimporthashlib,hmacimportnumpy as npimportwebsocketimportsysfrom base.exchange importEXCHANGEclassOKEX(EXCHANGE):
_api_url= "https://www.okex.com"_ws_url= "wss://real.okex.com:10441/websocket"
'''初始化
def __init__(self, apikey="", secret=""):
self.__apikey = apikey
self.__secret = secret'''
#货币对列表
defmarkets(self):
url= self._api_url + '/v2/spot/markets/products'result=self.http_request(url)
list=[]for item in result["data"]:if item["online"] == 1:
temp= item["symbol"].split("_")
list.append({'symbol': item["symbol"], 'base': temp[0].upper(), 'quote': temp[1].upper()})returnlist#精度
defprecisions(self):
url= self._api_url + '/api/spot/v3/instruments'result=self.http_request(url)
dict={}for item inresult:
symbol= item["base_currency"] + "_" + item["quote_currency"]
filter_dict={}
filter_dict['price'] = abs(round(math.log(float(item["tick_size"]), 10)))
filter_dict['quantity'] = abs(round(math.log(float(item["size_increment"]), 10)))
dict[symbol]=filter_dictreturndict#获取行情
def ticker(self, symbol = 'btc_usdt'):
url= self._api_url + '/api/v1/ticker.do?symbol=%s'%(self._x_symbol(symbol))
result=self.http_request(url)
dict= {'bid': float(result["ticker"]["buy"]), 'ask': float(result["ticker"]["sell"]), 'last': float(result["ticker"]["last"])}returndict#获取市场深度
def depth(self, symbol = 'btc_usdt'):
url= self._api_url + '/api/v1/depth.do?symbol=%s'%(self._x_symbol(symbol))
depth=self.http_request(url)
depth['asks'].sort(key=lambda x:x[0], reverse=False)
depth['bids'].sort(key=lambda x:x[0], reverse=True)return depth #标准格式,不用转换
## 获取交易信息(60条)
def trades(self, symbol = 'btc_usdt', limit = 60):
url= self._api_url + '/api/v1/trades.do?symbol=%s'%(self._x_symbol(symbol))#print(url)
result =self.http_request(url)
list=[]for item inresult:
list.append({'id': int(item["tid"]), 'time': item["date_ms"], 'price': item["price"], 'quantity': item["amount"], 'direction': item["type"]})returnlist#获取的K线数据
def kline(self, symbol = 'btc_usdt', interval = '1hour', limit = 120, start = None, end =None):
xdict={"1min":"1min","3min":"3min","5min":"5min","10min":"","15min":"15min","30min":"30min","1hour":"1hour","2hour":"2hour","3hour":"","4hour":"4hour","6hour":"6hour","8hour":"","12hour":"12hour","1day":"1day","3day":"3day","1week":"1week","2week":"","1month":""}if notxdict[interval]:print('不支持的K线周期 %s not support %s'%(self._exchange, interval))returnNone
url= self._api_url + '/api/v1/kline.do?symbol=%s&type=%s&size=%s'%(self._x_symbol(symbol), xdict[interval], limit)if start: #如果指定了size,则优先size
url += '&since=%s'%(start) + "000" #毫秒级时间戳
print(url)
result=self.http_request(url)
npa= np.array(result, dtype='float64')if (npa.shape[0] < 60):
self._x_print(symbol, sys._getframe().f_code.co_name, interval+ "数组维度过小" +str(npa.shape[0]))
stamp, open, high, low, close, volume= npa.T[0] / 1000, npa.T[1], npa.T[2], npa.T[3], npa.T[4], npa.T[5]returnstamp, open, high, low, close, volume#获取用户账户资产信息
defaccount(self):
url= self._api_url + '/api/v1/userinfo.do'params={'api_key': self._apikey
}
params['sign'] =self.signature(params)
result= self.http_request(url, "POST", params)
dict={}
i= 1
for item in result["info"]["funds"]["free"]:
dict[item.upper()]= {'free': float(result["info"]["funds"]["free"][item]), 'locked': float(result["info"]["funds"]["freezed"][item]), 'total': float(result["info"]["funds"]["free"][item]) + float(result["info"]["funds"]["freezed"][item])}returndict#下单 {'result': True, 'orderid': 342856847}
def order(self, symbol, side, type, quantity=None, price=None):if side not in ["BUY", "SELL"] or type not in ["LIMIT", "MARKET"]:raise Exception("ERROR params side %s type %s"%(side, type))returnurl= self._api_url + '/api/v1/trade.do'
#买卖类型:限价单(buy/sell) 市价单(buy_market/sell_market)
new_type = "_".join([side, type]).lower() if type == "MARKET" elseside.lower()
params={'api_key':self._apikey,'symbol':self._x_symbol(symbol),'type':new_type,'price':price,'amount':quantity
}
params.pop('amount') if side in ["BUY"] and type in ["MARKET"] else None #市价买单不传amount
params.pop('price') if side in ["SELL"] and type in ["MARKET"] else None #市价卖单不传price
#print(params)
params['sign'] =self.signature(params)
result= self.http_request(url, "POST", params)if 'result' inresult:return {'result': result['result'], 'orderid': result['order_id']}elif 'error_code' inresult:return {'result': False, 'errcode': result['error_code'], 'errmsg': "https://github.com/okcoin-okex/API-docs-OKEx.com/tree/master/API-For-Spot-CN"}else:returnresult#撤销订单 {'result': True}
deforder_cancel(self, symbol, orderid):
url= self._api_url + '/api/v1/cancel_order.do'params={'api_key':self._apikey,'symbol':self._x_symbol(symbol),'order_id':orderid
}
params['sign'] =self.signature(params)
result= self.http_request(url, "POST", params)return {'result': result['result']}#查询订单
deforder_query(self, symbol, orderid):
url= self._api_url + '/api/v1/order_info.do'params={'api_key':self._apikey,'symbol':self._x_symbol(symbol),'order_id':orderid
}
params['sign'] =self.signature(params)
result= self.http_request(url, "POST", params)#print(result)
types ={
0:"SUBMITTED",1:"PARTIALLY_FILLED",2:"FILLED",-1:"CANCELED",4:"OTHER"}#print(len(result["orders"])) > 0
if (result["result"]):
dict={'symbol': result["orders"][0]["symbol"],'orderid': result["orders"][0]["order_id"],'price': result["orders"][0]["price"],'avg_price':result["orders"][0]["avg_price"],'quantity': result["orders"][0]["amount"],'status': types[result["orders"][0]["status"]],'type': result["orders"][0]["type"],'time': result["orders"][0]["create_date"],
}returndictelse:return {'result': False, 'errcode': result["error_code"], 'errmsg': "empty data"}#签名函数
defsignature(self, params):
sign= ''
for key insorted(params.keys()):
sign+= key + '=' + str(params[key]) +'&'data= sign+'secret_key='+self._secretreturn hashlib.md5(data.encode("utf8")).hexdigest().upper()#websocket 连接 https://www.cnblogs.com/fangbei/p/okex-api-v1.html
defwebsocket_conn(self):try:
self.ws=websocket.create_connection(self._ws_url)
time.sleep(1)except:print("WebSocket Connect Error %s" %(self._exchange))else:returnself.ws#websocket订阅函数,目前支持ticker, depth两种频道
def websocket_sub(self, symbol = 'BTC_USDT', channel="depth"):if channel == "depth":
channel= channel + "_5"sub= {"event": "addChannel", "channel": '_'.join(['ok_sub_spot', self._x_symbol(symbol), channel])}#print(sub)
self.ws.send(json.dumps(sub))returnself.ws#websocket 接收数据格式化,格式和REST统一
'''while True:
data = exchange.websocket_fmt(channel, ws.recv())
print(data)'''
defwebsocket_fmt(self, channel, data):
result=json.loads(data)[0]
dict={}if channel == "depth" and "channel" in result and "depth" in result["channel"]:#print(result)
bids, asks =[], []for i in range(len(result["data"]["bids"])):
bids.append(list(map(float, [result["data"]["bids"][i][0], result["data"]["bids"][i][1]])))
asks.append(list(map(float, [result["data"]["asks"][i][0], result["data"]["asks"][i][1]])))
dict= {'asks': asks, 'bids': bids}#self.print_depth(dict)
if channel == "ticker" and "channel" in result and "ticker" in result["channel"]:
dict= {'bid': float(result["data"]["buy"]), 'ask': float(result["data"]["sell"]), 'last': float(result["data"]["last"])}if 'event' inresult:
pong= {"event" : "pong"}
self.ws.send(json.dumps(pong))returndict#websocket 接收数据 带 币种 组成字典,用于同频道多个币种的订阅,如三角套利
defwebsocket_fmt_multi(self, channel, data):
result=json.loads(data)[0]
dict={}if 'event' inresult:
pong= {"event" : "pong"}
self.ws.send(json.dumps(pong))if channel == "depth" and "data" in result and "depth" in result["channel"] and "bids" in result["data"] and "asks" in result["data"]:
symbol= result['channel'].split("spot_")[-1].split("_depth")[0].upper()#symbol = result['channel'].replace("_depth_5","").replace("ok_sub_spot_","").upper()
values =self.websocket_fmt(channel, data)
dict={symbol: values}return dict