add
This commit is contained in:
571
ccxt/pro/bittrade.py
Normal file
571
ccxt/pro/bittrade.py
Normal file
@@ -0,0 +1,571 @@
|
||||
# -*- 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, ArrayCacheByTimestamp
|
||||
from ccxt.base.types import Any, Bool, Int, OrderBook, Ticker, Trade
|
||||
from ccxt.async_support.base.ws.client import Client
|
||||
from typing import List
|
||||
from ccxt.base.errors import ExchangeError
|
||||
|
||||
|
||||
class bittrade(ccxt.async_support.bittrade):
|
||||
|
||||
def describe(self) -> Any:
|
||||
return self.deep_extend(super(bittrade, self).describe(), {
|
||||
'has': {
|
||||
'ws': True,
|
||||
'watchOrderBook': True,
|
||||
'watchTickers': False, # for now
|
||||
'watchTicker': True,
|
||||
'watchTrades': True,
|
||||
'watchTradesForSymbols': False,
|
||||
'watchBalance': False, # for now
|
||||
'watchOHLCV': True,
|
||||
},
|
||||
'urls': {
|
||||
'api': {
|
||||
'ws': {
|
||||
'api': {
|
||||
'public': 'wss://{hostname}/ws',
|
||||
'private': 'wss://{hostname}/ws/v2',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'options': {
|
||||
'tradesLimit': 1000,
|
||||
'OHLCVLimit': 1000,
|
||||
'api': 'api', # or api-aws for clients hosted on AWS
|
||||
'ws': {
|
||||
'gunzip': True,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
def request_id(self):
|
||||
requestId = self.sum(self.safe_integer(self.options, 'requestId', 0), 1)
|
||||
self.options['requestId'] = requestId
|
||||
return str(requestId)
|
||||
|
||||
async def watch_ticker(self, symbol: str, params={}) -> Ticker:
|
||||
"""
|
||||
watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
||||
:param str symbol: unified symbol of the market to fetch the ticker for
|
||||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||||
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
||||
"""
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
# only supports a limit of 150 at self time
|
||||
messageHash = 'market.' + market['id'] + '.detail'
|
||||
api = self.safe_string(self.options, 'api', 'api')
|
||||
hostname: dict = {'hostname': self.hostname}
|
||||
url = self.implode_params(self.urls['api']['ws'][api]['public'], hostname)
|
||||
requestId = self.request_id()
|
||||
request: dict = {
|
||||
'sub': messageHash,
|
||||
'id': requestId,
|
||||
}
|
||||
subscription: dict = {
|
||||
'id': requestId,
|
||||
'messageHash': messageHash,
|
||||
'symbol': symbol,
|
||||
'params': params,
|
||||
}
|
||||
return await self.watch(url, messageHash, self.extend(request, params), messageHash, subscription)
|
||||
|
||||
def handle_ticker(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "ch": "market.btcusdt.detail",
|
||||
# "ts": 1583494163784,
|
||||
# "tick": {
|
||||
# "id": 209988464418,
|
||||
# "low": 8988,
|
||||
# "high": 9155.41,
|
||||
# "open": 9078.91,
|
||||
# "close": 9136.46,
|
||||
# "vol": 237813910.5928412,
|
||||
# "amount": 26184.202558551195,
|
||||
# "version": 209988464418,
|
||||
# "count": 265673
|
||||
# }
|
||||
# }
|
||||
#
|
||||
tick = self.safe_value(message, 'tick', {})
|
||||
ch = self.safe_string(message, 'ch')
|
||||
parts = ch.split('.')
|
||||
marketId = self.safe_string(parts, 1)
|
||||
market = self.safe_market(marketId)
|
||||
ticker = self.parse_ticker(tick, market)
|
||||
timestamp = self.safe_value(message, 'ts')
|
||||
ticker['timestamp'] = timestamp
|
||||
ticker['datetime'] = self.iso8601(timestamp)
|
||||
symbol = ticker['symbol']
|
||||
self.tickers[symbol] = ticker
|
||||
client.resolve(ticker, ch)
|
||||
return message
|
||||
|
||||
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
|
||||
: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>`
|
||||
"""
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
# only supports a limit of 150 at self time
|
||||
messageHash = 'market.' + market['id'] + '.trade.detail'
|
||||
api = self.safe_string(self.options, 'api', 'api')
|
||||
hostname: dict = {'hostname': self.hostname}
|
||||
url = self.implode_params(self.urls['api']['ws'][api]['public'], hostname)
|
||||
requestId = self.request_id()
|
||||
request: dict = {
|
||||
'sub': messageHash,
|
||||
'id': requestId,
|
||||
}
|
||||
subscription: dict = {
|
||||
'id': requestId,
|
||||
'messageHash': messageHash,
|
||||
'symbol': symbol,
|
||||
'params': params,
|
||||
}
|
||||
trades = await self.watch(url, messageHash, self.extend(request, params), messageHash, 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):
|
||||
#
|
||||
# {
|
||||
# "ch": "market.btcusdt.trade.detail",
|
||||
# "ts": 1583495834011,
|
||||
# "tick": {
|
||||
# "id": 105004645372,
|
||||
# "ts": 1583495833751,
|
||||
# "data": [
|
||||
# {
|
||||
# "id": 1.050046453727319e+22,
|
||||
# "ts": 1583495833751,
|
||||
# "tradeId": 102090727790,
|
||||
# "amount": 0.003893,
|
||||
# "price": 9150.01,
|
||||
# "direction": "sell"
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
# }
|
||||
#
|
||||
tick = self.safe_value(message, 'tick', {})
|
||||
data = self.safe_value(tick, 'data', {})
|
||||
ch = self.safe_string(message, 'ch')
|
||||
parts = ch.split('.')
|
||||
marketId = self.safe_string(parts, 1)
|
||||
market = self.safe_market(marketId)
|
||||
symbol = market['symbol']
|
||||
tradesCache = self.safe_value(self.trades, symbol)
|
||||
if tradesCache is None:
|
||||
limit = self.safe_integer(self.options, 'tradesLimit', 1000)
|
||||
tradesCache = ArrayCache(limit)
|
||||
self.trades[symbol] = tradesCache
|
||||
for i in range(0, len(data)):
|
||||
trade = self.parse_trade(data[i], market)
|
||||
tradesCache.append(trade)
|
||||
client.resolve(tradesCache, ch)
|
||||
return message
|
||||
|
||||
async def watch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
||||
"""
|
||||
watches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
||||
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
||||
:param str timeframe: the length of time each candle represents
|
||||
:param int [since]: timestamp in ms of the earliest candle to fetch
|
||||
:param int [limit]: the maximum amount of candles to fetch
|
||||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||||
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
||||
"""
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
interval = self.safe_string(self.timeframes, timeframe, timeframe)
|
||||
messageHash = 'market.' + market['id'] + '.kline.' + interval
|
||||
api = self.safe_string(self.options, 'api', 'api')
|
||||
hostname: dict = {'hostname': self.hostname}
|
||||
url = self.implode_params(self.urls['api']['ws'][api]['public'], hostname)
|
||||
requestId = self.request_id()
|
||||
request: dict = {
|
||||
'sub': messageHash,
|
||||
'id': requestId,
|
||||
}
|
||||
subscription: dict = {
|
||||
'id': requestId,
|
||||
'messageHash': messageHash,
|
||||
'symbol': symbol,
|
||||
'timeframe': timeframe,
|
||||
'params': params,
|
||||
}
|
||||
ohlcv = await self.watch(url, messageHash, self.extend(request, params), messageHash, subscription)
|
||||
if self.newUpdates:
|
||||
limit = ohlcv.getLimit(symbol, limit)
|
||||
return self.filter_by_since_limit(ohlcv, since, limit, 0, True)
|
||||
|
||||
def handle_ohlcv(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "ch": "market.btcusdt.kline.1min",
|
||||
# "ts": 1583501786794,
|
||||
# "tick": {
|
||||
# "id": 1583501760,
|
||||
# "open": 9094.5,
|
||||
# "close": 9094.51,
|
||||
# "low": 9094.5,
|
||||
# "high": 9094.51,
|
||||
# "amount": 0.44639786263800907,
|
||||
# "vol": 4059.76919054,
|
||||
# "count": 16
|
||||
# }
|
||||
# }
|
||||
#
|
||||
ch = self.safe_string(message, 'ch')
|
||||
parts = ch.split('.')
|
||||
marketId = self.safe_string(parts, 1)
|
||||
market = self.safe_market(marketId)
|
||||
symbol = market['symbol']
|
||||
interval = self.safe_string(parts, 3)
|
||||
timeframe = self.find_timeframe(interval)
|
||||
self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {})
|
||||
stored = self.safe_value(self.ohlcvs[symbol], timeframe)
|
||||
if stored is None:
|
||||
limit = self.safe_integer(self.options, 'OHLCVLimit', 1000)
|
||||
stored = ArrayCacheByTimestamp(limit)
|
||||
self.ohlcvs[symbol][timeframe] = stored
|
||||
tick = self.safe_value(message, 'tick')
|
||||
parsed = self.parse_ohlcv(tick, market)
|
||||
stored.append(parsed)
|
||||
client.resolve(stored, ch)
|
||||
|
||||
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 dict [params]: extra parameters specific to the exchange API endpoint
|
||||
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
||||
"""
|
||||
if (limit is not None) and (limit != 150):
|
||||
raise ExchangeError(self.id + ' watchOrderBook accepts limit = 150 only')
|
||||
await self.load_markets()
|
||||
market = self.market(symbol)
|
||||
symbol = market['symbol']
|
||||
# only supports a limit of 150 at self time
|
||||
limit = 150 if (limit is None) else limit
|
||||
messageHash = 'market.' + market['id'] + '.mbp.' + str(limit)
|
||||
api = self.safe_string(self.options, 'api', 'api')
|
||||
hostname: dict = {'hostname': self.hostname}
|
||||
url = self.implode_params(self.urls['api']['ws'][api]['public'], hostname)
|
||||
requestId = self.request_id()
|
||||
request: dict = {
|
||||
'sub': messageHash,
|
||||
'id': requestId,
|
||||
}
|
||||
subscription: dict = {
|
||||
'id': requestId,
|
||||
'messageHash': messageHash,
|
||||
'symbol': symbol,
|
||||
'limit': limit,
|
||||
'params': params,
|
||||
'method': self.handle_order_book_subscription,
|
||||
}
|
||||
orderbook = await self.watch(url, messageHash, self.extend(request, params), messageHash, subscription)
|
||||
return orderbook.limit()
|
||||
|
||||
def handle_order_book_snapshot(self, client: Client, message, subscription):
|
||||
#
|
||||
# {
|
||||
# "id": 1583473663565,
|
||||
# "rep": "market.btcusdt.mbp.150",
|
||||
# "status": "ok",
|
||||
# "data": {
|
||||
# "seqNum": 104999417756,
|
||||
# "bids": [
|
||||
# [9058.27, 0],
|
||||
# [9058.43, 0],
|
||||
# [9058.99, 0],
|
||||
# ],
|
||||
# "asks": [
|
||||
# [9084.27, 0.2],
|
||||
# [9085.69, 0],
|
||||
# [9085.81, 0],
|
||||
# ]
|
||||
# }
|
||||
# }
|
||||
#
|
||||
symbol = self.safe_string(subscription, 'symbol')
|
||||
messageHash = self.safe_string(subscription, 'messageHash')
|
||||
orderbook = self.orderbooks[symbol]
|
||||
data = self.safe_value(message, 'data')
|
||||
snapshot = self.parse_order_book(data, symbol)
|
||||
snapshot['nonce'] = self.safe_integer(data, 'seqNum')
|
||||
orderbook.reset(snapshot)
|
||||
# unroll the accumulated deltas
|
||||
messages = orderbook.cache
|
||||
for i in range(0, len(messages)):
|
||||
self.handle_order_book_message(client, messages[i], orderbook)
|
||||
self.orderbooks[symbol] = orderbook
|
||||
client.resolve(orderbook, messageHash)
|
||||
|
||||
async def watch_order_book_snapshot(self, client, message, subscription):
|
||||
messageHash = self.safe_string(subscription, 'messageHash')
|
||||
try:
|
||||
symbol = self.safe_string(subscription, 'symbol')
|
||||
limit = self.safe_integer(subscription, 'limit')
|
||||
params = self.safe_value(subscription, 'params')
|
||||
api = self.safe_string(self.options, 'api', 'api')
|
||||
hostname: dict = {'hostname': self.hostname}
|
||||
url = self.implode_params(self.urls['api']['ws'][api]['public'], hostname)
|
||||
requestId = self.request_id()
|
||||
request: dict = {
|
||||
'req': messageHash,
|
||||
'id': requestId,
|
||||
}
|
||||
# self is a temporary subscription by a specific requestId
|
||||
# it has a very short lifetime until the snapshot is received over ws
|
||||
snapshotSubscription: dict = {
|
||||
'id': requestId,
|
||||
'messageHash': messageHash,
|
||||
'symbol': symbol,
|
||||
'limit': limit,
|
||||
'params': params,
|
||||
'method': self.handle_order_book_snapshot,
|
||||
}
|
||||
orderbook = await self.watch(url, requestId, request, requestId, snapshotSubscription)
|
||||
return orderbook.limit()
|
||||
except Exception as e:
|
||||
del client.subscriptions[messageHash]
|
||||
client.reject(e, messageHash)
|
||||
return None
|
||||
|
||||
def handle_delta(self, bookside, delta):
|
||||
price = self.safe_float(delta, 0)
|
||||
amount = self.safe_float(delta, 1)
|
||||
bookside.store(price, amount)
|
||||
|
||||
def handle_deltas(self, bookside, deltas):
|
||||
for i in range(0, len(deltas)):
|
||||
self.handle_delta(bookside, deltas[i])
|
||||
|
||||
def handle_order_book_message(self, client: Client, message, orderbook):
|
||||
#
|
||||
# {
|
||||
# "ch": "market.btcusdt.mbp.150",
|
||||
# "ts": 1583472025885,
|
||||
# "tick": {
|
||||
# "seqNum": 104998984994,
|
||||
# "prevSeqNum": 104998984977,
|
||||
# "bids": [
|
||||
# [9058.27, 0],
|
||||
# [9058.43, 0],
|
||||
# [9058.99, 0],
|
||||
# ],
|
||||
# "asks": [
|
||||
# [9084.27, 0.2],
|
||||
# [9085.69, 0],
|
||||
# [9085.81, 0],
|
||||
# ]
|
||||
# }
|
||||
# }
|
||||
#
|
||||
tick = self.safe_value(message, 'tick', {})
|
||||
seqNum = self.safe_integer(tick, 'seqNum')
|
||||
prevSeqNum = self.safe_integer(tick, 'prevSeqNum')
|
||||
if (prevSeqNum <= orderbook['nonce']) and (seqNum > orderbook['nonce']):
|
||||
asks = self.safe_value(tick, 'asks', [])
|
||||
bids = self.safe_value(tick, 'bids', [])
|
||||
self.handle_deltas(orderbook['asks'], asks)
|
||||
self.handle_deltas(orderbook['bids'], bids)
|
||||
orderbook['nonce'] = seqNum
|
||||
timestamp = self.safe_integer(message, 'ts')
|
||||
orderbook['timestamp'] = timestamp
|
||||
orderbook['datetime'] = self.iso8601(timestamp)
|
||||
return orderbook
|
||||
|
||||
def handle_order_book(self, client: Client, message):
|
||||
#
|
||||
# deltas
|
||||
#
|
||||
# {
|
||||
# "ch": "market.btcusdt.mbp.150",
|
||||
# "ts": 1583472025885,
|
||||
# "tick": {
|
||||
# "seqNum": 104998984994,
|
||||
# "prevSeqNum": 104998984977,
|
||||
# "bids": [
|
||||
# [9058.27, 0],
|
||||
# [9058.43, 0],
|
||||
# [9058.99, 0],
|
||||
# ],
|
||||
# "asks": [
|
||||
# [9084.27, 0.2],
|
||||
# [9085.69, 0],
|
||||
# [9085.81, 0],
|
||||
# ]
|
||||
# }
|
||||
# }
|
||||
#
|
||||
messageHash = self.safe_string(message, 'ch')
|
||||
ch = self.safe_value(message, 'ch')
|
||||
parts = ch.split('.')
|
||||
marketId = self.safe_string(parts, 1)
|
||||
symbol = self.safe_symbol(marketId)
|
||||
orderbook = self.orderbooks[symbol]
|
||||
if orderbook['nonce'] is None:
|
||||
orderbook.cache.append(message)
|
||||
else:
|
||||
self.handle_order_book_message(client, message, orderbook)
|
||||
client.resolve(orderbook, messageHash)
|
||||
|
||||
def handle_order_book_subscription(self, client: Client, message, subscription):
|
||||
symbol = self.safe_string(subscription, 'symbol')
|
||||
limit = self.safe_integer(subscription, 'limit')
|
||||
if symbol in self.orderbooks:
|
||||
del self.orderbooks[symbol]
|
||||
self.orderbooks[symbol] = self.order_book({}, limit)
|
||||
# watch the snapshot in a separate async call
|
||||
self.spawn(self.watch_order_book_snapshot, client, message, subscription)
|
||||
|
||||
def handle_subscription_status(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "id": 1583414227,
|
||||
# "status": "ok",
|
||||
# "subbed": "market.btcusdt.mbp.150",
|
||||
# "ts": 1583414229143
|
||||
# }
|
||||
#
|
||||
id = self.safe_string(message, 'id')
|
||||
subscriptionsById = self.index_by(client.subscriptions, 'id')
|
||||
subscription = self.safe_value(subscriptionsById, id)
|
||||
if subscription is not None:
|
||||
method = self.safe_value(subscription, 'method')
|
||||
if method is not None:
|
||||
return method(client, message, subscription)
|
||||
# clean up
|
||||
if id in client.subscriptions:
|
||||
del client.subscriptions[id]
|
||||
return message
|
||||
|
||||
def handle_system_status(self, client: Client, message):
|
||||
#
|
||||
# todo: answer the question whether handleSystemStatus should be renamed
|
||||
# and unified for any usage pattern that
|
||||
# involves system status and maintenance updates
|
||||
#
|
||||
# {
|
||||
# "id": "1578090234088", # connectId
|
||||
# "type": "welcome",
|
||||
# }
|
||||
#
|
||||
return message
|
||||
|
||||
def handle_subject(self, client: Client, message):
|
||||
#
|
||||
# {
|
||||
# "ch": "market.btcusdt.mbp.150",
|
||||
# "ts": 1583472025885,
|
||||
# "tick": {
|
||||
# "seqNum": 104998984994,
|
||||
# "prevSeqNum": 104998984977,
|
||||
# "bids": [
|
||||
# [9058.27, 0],
|
||||
# [9058.43, 0],
|
||||
# [9058.99, 0],
|
||||
# ],
|
||||
# "asks": [
|
||||
# [9084.27, 0.2],
|
||||
# [9085.69, 0],
|
||||
# [9085.81, 0],
|
||||
# ]
|
||||
# }
|
||||
# }
|
||||
#
|
||||
ch = self.safe_value(message, 'ch')
|
||||
parts = ch.split('.')
|
||||
type = self.safe_string(parts, 0)
|
||||
if type == 'market':
|
||||
methodName = self.safe_string(parts, 2)
|
||||
methods: dict = {
|
||||
'mbp': self.handle_order_book,
|
||||
'detail': self.handle_ticker,
|
||||
'trade': self.handle_trades,
|
||||
'kline': self.handle_ohlcv,
|
||||
# ...
|
||||
}
|
||||
method = self.safe_value(methods, methodName)
|
||||
if method is not None:
|
||||
method(client, message)
|
||||
|
||||
async def pong(self, client, message):
|
||||
#
|
||||
# {ping: 1583491673714}
|
||||
#
|
||||
await client.send({'pong': self.safe_integer(message, 'ping')})
|
||||
|
||||
def handle_ping(self, client: Client, message):
|
||||
self.spawn(self.pong, client, message)
|
||||
|
||||
def handle_error_message(self, client: Client, message) -> Bool:
|
||||
#
|
||||
# {
|
||||
# "ts": 1586323747018,
|
||||
# "status": "error",
|
||||
# 'err-code': "bad-request",
|
||||
# 'err-msg': "invalid mbp.150.symbol linkusdt",
|
||||
# "id": "2"
|
||||
# }
|
||||
#
|
||||
status = self.safe_string(message, 'status')
|
||||
if status == 'error':
|
||||
id = self.safe_string(message, 'id')
|
||||
subscriptionsById = self.index_by(client.subscriptions, 'id')
|
||||
subscription = self.safe_value(subscriptionsById, id)
|
||||
if subscription is not None:
|
||||
errorCode = self.safe_string(message, 'err-code')
|
||||
try:
|
||||
self.throw_exactly_matched_exception(self.exceptions['exact'], errorCode, self.json(message))
|
||||
except Exception as e:
|
||||
messageHash = self.safe_string(subscription, 'messageHash')
|
||||
client.reject(e, messageHash)
|
||||
client.reject(e, id)
|
||||
if id in client.subscriptions:
|
||||
del client.subscriptions[id]
|
||||
return False
|
||||
return message
|
||||
|
||||
def handle_message(self, client: Client, message):
|
||||
if self.handle_error_message(client, message):
|
||||
#
|
||||
# {"id":1583414227,"status":"ok","subbed":"market.btcusdt.mbp.150","ts":1583414229143}
|
||||
#
|
||||
# ________________________
|
||||
#
|
||||
# sometimes bittrade responds with half of a JSON response like
|
||||
#
|
||||
# " {"ch":"market.ethbtc.m "
|
||||
#
|
||||
# self is passed to handleMessage string since it failed to be decoded
|
||||
#
|
||||
if self.safe_string(message, 'id') is not None:
|
||||
self.handle_subscription_status(client, message)
|
||||
elif self.safe_string(message, 'ch') is not None:
|
||||
# route by channel aka topic aka subject
|
||||
self.handle_subject(client, message)
|
||||
elif self.safe_string(message, 'ping') is not None:
|
||||
self.handle_ping(client, message)
|
||||
Reference in New Issue
Block a user