add
This commit is contained in:
307
ccxt/pro/luno.py
Normal file
307
ccxt/pro/luno.py
Normal file
@@ -0,0 +1,307 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||||
|
||||
import ccxt.async_support
|
||||
from ccxt.async_support.base.ws.cache import ArrayCache
|
||||
from ccxt.base.types import Any, IndexType, Int, OrderBook, Trade
|
||||
from ccxt.async_support.base.ws.client import Client
|
||||
from typing import List
|
||||
|
||||
|
||||
class luno(ccxt.async_support.luno):
|
||||
|
||||
def describe(self) -> Any:
|
||||
return self.deep_extend(super(luno, self).describe(), {
|
||||
'has': {
|
||||
'ws': True,
|
||||
'watchTicker': False,
|
||||
'watchTickers': False,
|
||||
'watchTrades': True,
|
||||
'watchTradesForSymbols': False,
|
||||
'watchMyTrades': False,
|
||||
'watchOrders': None, # is in beta
|
||||
'watchOrderBook': True,
|
||||
'watchOHLCV': False,
|
||||
},
|
||||
'urls': {
|
||||
'api': {
|
||||
'ws': 'wss://ws.luno.com/api/1',
|
||||
},
|
||||
},
|
||||
'options': {
|
||||
'sequenceNumbers': {},
|
||||
},
|
||||
'streaming': {
|
||||
},
|
||||
'exceptions': {
|
||||
},
|
||||
})
|
||||
|
||||
async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
||||
"""
|
||||
get the list of most recent trades for a particular symbol
|
||||
|
||||
https://www.luno.com/en/developers/api#tag/Streaming-API
|
||||
|
||||
:param str symbol: unified symbol of the market to fetch trades for
|
||||
:param int [since]: timestamp in ms of the earliest trade to fetch
|
||||
:param int [limit]: the maximum amount of trades to fetch
|
||||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||||
:returns dict[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
||||
"""
|
||||
self.check_required_credentials()
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
subscriptionHash = '/stream/' + market['id']
|
||||
subscription: dict = {'symbol': symbol}
|
||||
url = self.urls['api']['ws'] + subscriptionHash
|
||||
messageHash = 'trades:' + symbol
|
||||
subscribe: dict = {
|
||||
'api_key_id': self.apiKey,
|
||||
'api_key_secret': self.secret,
|
||||
}
|
||||
request = self.deep_extend(subscribe, params)
|
||||
trades = await self.watch(url, messageHash, request, subscriptionHash, subscription)
|
||||
if self.newUpdates:
|
||||
limit = trades.getLimit(symbol, limit)
|
||||
return self.filter_by_since_limit(trades, since, limit, 'timestamp', True)
|
||||
|
||||
def handle_trades(self, client: Client, message, subscription):
|
||||
#
|
||||
# {
|
||||
# "sequence": "110980825",
|
||||
# "trade_updates": [],
|
||||
# "create_update": {
|
||||
# "order_id": "BXHSYXAUMH8C2RW",
|
||||
# "type": "ASK",
|
||||
# "price": "24081.09000000",
|
||||
# "volume": "0.07780000"
|
||||
# },
|
||||
# "delete_update": null,
|
||||
# "status_update": null,
|
||||
# "timestamp": 1660598775360
|
||||
# }
|
||||
#
|
||||
rawTrades = self.safe_value(message, 'trade_updates', [])
|
||||
length = len(rawTrades)
|
||||
if length == 0:
|
||||
return
|
||||
symbol = subscription['symbol']
|
||||
market = self.market(symbol)
|
||||
messageHash = 'trades:' + symbol
|
||||
stored = self.safe_value(self.trades, symbol)
|
||||
if stored is None:
|
||||
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
||||
stored = ArrayCache(limit)
|
||||
self.trades[symbol] = stored
|
||||
for i in range(0, len(rawTrades)):
|
||||
rawTrade = rawTrades[i]
|
||||
trade = self.parse_trade(rawTrade, market)
|
||||
stored.append(trade)
|
||||
self.trades[symbol] = stored
|
||||
client.resolve(self.trades[symbol], messageHash)
|
||||
|
||||
def parse_trade(self, trade, market=None) -> Trade:
|
||||
#
|
||||
# watchTrades(public)
|
||||
#
|
||||
# {
|
||||
# "base": "69.00000000",
|
||||
# "counter": "113.6499000000000000",
|
||||
# "maker_order_id": "BXEEU4S2BWF5WRB",
|
||||
# "taker_order_id": "BXKNCSF7JDHXY3H",
|
||||
# "order_id": "BXEEU4S2BWF5WRB"
|
||||
# }
|
||||
#
|
||||
return self.safe_trade({
|
||||
'info': trade,
|
||||
'id': None,
|
||||
'timestamp': None,
|
||||
'datetime': None,
|
||||
'symbol': market['symbol'],
|
||||
'order': None,
|
||||
'type': None,
|
||||
'side': None,
|
||||
# takerOrMaker has no meaning for public trades
|
||||
'takerOrMaker': None,
|
||||
'price': None,
|
||||
'amount': self.safe_string(trade, 'base'),
|
||||
'cost': self.safe_string(trade, 'counter'),
|
||||
'fee': None,
|
||||
}, market)
|
||||
|
||||
async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
||||
"""
|
||||
watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
||||
:param str symbol: unified symbol of the market to fetch the order book for
|
||||
:param int [limit]: the maximum amount of order book entries to return
|
||||
:param dictConstructor [params]: extra parameters specific to the exchange API endpoint
|
||||
:param str [params.type]: accepts l2 or l3 for level 2 or level 3 order book
|
||||
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
||||
"""
|
||||
self.check_required_credentials()
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
subscriptionHash = '/stream/' + market['id']
|
||||
subscription: dict = {'symbol': symbol}
|
||||
url = self.urls['api']['ws'] + subscriptionHash
|
||||
messageHash = 'orderbook:' + symbol
|
||||
subscribe: dict = {
|
||||
'api_key_id': self.apiKey,
|
||||
'api_key_secret': self.secret,
|
||||
}
|
||||
request = self.deep_extend(subscribe, params)
|
||||
orderbook = await self.watch(url, messageHash, request, subscriptionHash, subscription)
|
||||
return orderbook.limit()
|
||||
|
||||
def handle_order_book(self, client: Client, message, subscription):
|
||||
#
|
||||
# {
|
||||
# "sequence": "24352",
|
||||
# "asks": [{
|
||||
# "id": "BXMC2CJ7HNB88U4",
|
||||
# "price": "1234.00",
|
||||
# "volume": "0.93"
|
||||
# }],
|
||||
# "bids": [{
|
||||
# "id": "BXMC2CJ7HNB88U5",
|
||||
# "price": "1201.00",
|
||||
# "volume": "1.22"
|
||||
# }],
|
||||
# "status": "ACTIVE",
|
||||
# "timestamp": 1528884331021
|
||||
# }
|
||||
#
|
||||
# update
|
||||
# {
|
||||
# "sequence": "110980825",
|
||||
# "trade_updates": [],
|
||||
# "create_update": {
|
||||
# "order_id": "BXHSYXAUMH8C2RW",
|
||||
# "type": "ASK",
|
||||
# "price": "24081.09000000",
|
||||
# "volume": "0.07780000"
|
||||
# },
|
||||
# "delete_update": null,
|
||||
# "status_update": null,
|
||||
# "timestamp": 1660598775360
|
||||
# }
|
||||
#
|
||||
symbol = subscription['symbol']
|
||||
messageHash = 'orderbook:' + symbol
|
||||
timestamp = self.safe_integer(message, 'timestamp')
|
||||
if not (symbol in self.orderbooks):
|
||||
self.orderbooks[symbol] = self.indexed_order_book({})
|
||||
asks = self.safe_value(message, 'asks')
|
||||
if asks is not None:
|
||||
snapshot = self.custom_parse_order_book(message, symbol, timestamp, 'bids', 'asks', 'price', 'volume', 'id')
|
||||
self.orderbooks[symbol] = self.indexed_order_book(snapshot)
|
||||
else:
|
||||
ob = self.orderbooks[symbol]
|
||||
self.handle_delta(ob, message)
|
||||
ob['timestamp'] = timestamp
|
||||
ob['datetime'] = self.iso8601(timestamp)
|
||||
orderbook = self.orderbooks[symbol]
|
||||
nonce = self.safe_integer(message, 'sequence')
|
||||
orderbook['nonce'] = nonce
|
||||
client.resolve(orderbook, messageHash)
|
||||
|
||||
def custom_parse_order_book(self, orderbook, symbol, timestamp=None, bidsKey='bids', asksKey: IndexType = 'asks', priceKey: IndexType = 'price', amountKey: IndexType = 'volume', countOrIdKey: IndexType = 2):
|
||||
bids = self.parse_bids_asks(self.safe_value(orderbook, bidsKey, []), priceKey, amountKey, countOrIdKey)
|
||||
asks = self.parse_bids_asks(self.safe_value(orderbook, asksKey, []), priceKey, amountKey, countOrIdKey)
|
||||
return {
|
||||
'symbol': symbol,
|
||||
'bids': self.sort_by(bids, 0, True),
|
||||
'asks': self.sort_by(asks, 0),
|
||||
'timestamp': timestamp,
|
||||
'datetime': self.iso8601(timestamp),
|
||||
'nonce': None,
|
||||
}
|
||||
|
||||
def parse_bids_asks(self, bidasks, priceKey: IndexType = 'price', amountKey: IndexType = 'volume', thirdKey: IndexType = 2):
|
||||
bidasks = self.to_array(bidasks)
|
||||
result = []
|
||||
for i in range(0, len(bidasks)):
|
||||
result.append(self.custom_parse_bid_ask(bidasks[i], priceKey, amountKey, thirdKey))
|
||||
return result
|
||||
|
||||
def custom_parse_bid_ask(self, bidask, priceKey: IndexType = 'price', amountKey: IndexType = 'volume', thirdKey: IndexType = 2):
|
||||
price = self.safe_number(bidask, priceKey)
|
||||
amount = self.safe_number(bidask, amountKey)
|
||||
result = [price, amount]
|
||||
if thirdKey is not None:
|
||||
thirdValue = self.safe_string(bidask, thirdKey)
|
||||
result.append(thirdValue)
|
||||
return result
|
||||
|
||||
def handle_delta(self, orderbook, message):
|
||||
#
|
||||
# create
|
||||
# {
|
||||
# "sequence": "110980825",
|
||||
# "trade_updates": [],
|
||||
# "create_update": {
|
||||
# "order_id": "BXHSYXAUMH8C2RW",
|
||||
# "type": "ASK",
|
||||
# "price": "24081.09000000",
|
||||
# "volume": "0.07780000"
|
||||
# },
|
||||
# "delete_update": null,
|
||||
# "status_update": null,
|
||||
# "timestamp": 1660598775360
|
||||
# }
|
||||
# del # {
|
||||
# "sequence": "110980825",
|
||||
# "trade_updates": [],
|
||||
# "create_update": null,
|
||||
# "delete_update": {
|
||||
# "order_id": "BXMC2CJ7HNB88U4"
|
||||
# },
|
||||
# "status_update": null,
|
||||
# "timestamp": 1660598775360
|
||||
# }
|
||||
# trade
|
||||
# {
|
||||
# "sequence": "110980825",
|
||||
# "trade_updates": [
|
||||
# {
|
||||
# "base": "0.1",
|
||||
# "counter": "5232.00",
|
||||
# "maker_order_id": "BXMC2CJ7HNB88U4",
|
||||
# "taker_order_id": "BXMC2CJ7HNB88U5"
|
||||
# }
|
||||
# ],
|
||||
# "create_update": null,
|
||||
# "delete_update": null,
|
||||
# "status_update": null,
|
||||
# "timestamp": 1660598775360
|
||||
# }
|
||||
#
|
||||
createUpdate = self.safe_value(message, 'create_update')
|
||||
asksOrderSide = orderbook['asks']
|
||||
bidsOrderSide = orderbook['bids']
|
||||
if createUpdate is not None:
|
||||
bidAskArray = self.custom_parse_bid_ask(createUpdate, 'price', 'volume', 'order_id')
|
||||
type = self.safe_string(createUpdate, 'type')
|
||||
if type == 'ASK':
|
||||
asksOrderSide.storeArray(bidAskArray)
|
||||
elif type == 'BID':
|
||||
bidsOrderSide.storeArray(bidAskArray)
|
||||
deleteUpdate = self.safe_value(message, 'delete_update')
|
||||
if deleteUpdate is not None:
|
||||
orderId = self.safe_string(deleteUpdate, 'order_id')
|
||||
asksOrderSide.storeArray([0, 0, orderId])
|
||||
bidsOrderSide.storeArray([0, 0, orderId])
|
||||
|
||||
def handle_message(self, client: Client, message):
|
||||
if message == '':
|
||||
return
|
||||
subscriptions = list(client.subscriptions.values())
|
||||
handlers = [self.handle_order_book, self.handle_trades]
|
||||
for j in range(0, len(handlers)):
|
||||
handler = handlers[j]
|
||||
handler(client, message, subscriptions[0])
|
||||
Reference in New Issue
Block a user