mirror of
https://github.com/aljazceru/bitfinex-api-py.git
synced 2026-01-07 16:04:21 +01:00
fix all linting for pylint3
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
"""
|
||||
Module used to house the bitfine websocket client
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import time
|
||||
@@ -10,397 +14,410 @@ from .OrderManager import OrderManager
|
||||
from ..utils.auth import generate_auth_payload
|
||||
from ..models import Order, Trade, OrderBook
|
||||
|
||||
class Flags:
|
||||
DEC_S = 9
|
||||
TIME_S = 32
|
||||
TIMESTAMP = 32768
|
||||
SEQ_ALL = 65536
|
||||
CHECKSUM = 131072
|
||||
|
||||
strings = {
|
||||
9: 'DEC_S',
|
||||
32: 'TIME_S',
|
||||
32768: 'TIMESTAMP',
|
||||
65536: 'SEQ_ALL',
|
||||
131072: 'CHECKSUM'
|
||||
}
|
||||
class Flags:
|
||||
"""
|
||||
Enum used to index the available flags used in the authentication
|
||||
websocket packet
|
||||
"""
|
||||
DEC_S = 9
|
||||
TIME_S = 32
|
||||
TIMESTAMP = 32768
|
||||
SEQ_ALL = 65536
|
||||
CHECKSUM = 131072
|
||||
|
||||
strings = {
|
||||
9: 'DEC_S',
|
||||
32: 'TIME_S',
|
||||
32768: 'TIMESTAMP',
|
||||
65536: 'SEQ_ALL',
|
||||
131072: 'CHECKSUM'
|
||||
}
|
||||
|
||||
|
||||
def _parse_candle(cData, symbol, tf):
|
||||
return {
|
||||
'mts': cData[0],
|
||||
'open': cData[1],
|
||||
'close': cData[2],
|
||||
'high': cData[3],
|
||||
'low': cData[4],
|
||||
'volume': cData[5],
|
||||
'symbol': symbol,
|
||||
'tf': tf
|
||||
}
|
||||
return {
|
||||
'mts': cData[0],
|
||||
'open': cData[1],
|
||||
'close': cData[2],
|
||||
'high': cData[3],
|
||||
'low': cData[4],
|
||||
'volume': cData[5],
|
||||
'symbol': symbol,
|
||||
'tf': tf
|
||||
}
|
||||
|
||||
|
||||
def _parse_trade_snapshot_item(tData, symbol):
|
||||
return {
|
||||
'mts': tData[3],
|
||||
'price': tData[4],
|
||||
'amount': tData[5],
|
||||
'symbol': symbol
|
||||
}
|
||||
return {
|
||||
'mts': tData[3],
|
||||
'price': tData[4],
|
||||
'amount': tData[5],
|
||||
'symbol': symbol
|
||||
}
|
||||
|
||||
|
||||
def _parse_trade(tData, symbol):
|
||||
return {
|
||||
'mts': tData[1],
|
||||
'price': tData[3],
|
||||
'amount': tData[2],
|
||||
'symbol': symbol
|
||||
}
|
||||
return {
|
||||
'mts': tData[1],
|
||||
'price': tData[3],
|
||||
'amount': tData[2],
|
||||
'symbol': symbol
|
||||
}
|
||||
|
||||
|
||||
class BfxWebsocket(GenericWebsocket):
|
||||
"""
|
||||
More complex websocket that heavily relies on the btfxwss module. This websocket requires
|
||||
authentication and is capable of handling orders.
|
||||
https://github.com/Crypto-toolbox/btfxwss
|
||||
"""
|
||||
"""
|
||||
More complex websocket that heavily relies on the btfxwss module.
|
||||
This websocket requires authentication and is capable of handling orders.
|
||||
https://github.com/Crypto-toolbox/btfxwss
|
||||
"""
|
||||
|
||||
ERRORS = {
|
||||
10000: 'Unknown event',
|
||||
10001: 'Generic error',
|
||||
10008: 'Concurrency error',
|
||||
10020: 'Request parameters error',
|
||||
10050: 'Configuration setup failed',
|
||||
10100: 'Failed authentication',
|
||||
10111: 'Error in authentication request payload',
|
||||
10112: 'Error in authentication request signature',
|
||||
10113: 'Error in authentication request encryption',
|
||||
10114: 'Error in authentication request nonce',
|
||||
10200: 'Error in un-authentication request',
|
||||
10300: 'Subscription Failed (generic)',
|
||||
10301: 'Already Subscribed',
|
||||
10302: 'Unknown channel',
|
||||
10400: 'Subscription Failed (generic)',
|
||||
10401: 'Not subscribed',
|
||||
11000: 'Not ready, try again later',
|
||||
20000: 'User is invalid!',
|
||||
20051: 'Websocket server stopping',
|
||||
20060: 'Websocket server resyncing',
|
||||
20061: 'Websocket server resync complete'
|
||||
}
|
||||
|
||||
def __init__(self, API_KEY=None, API_SECRET=None, host='wss://api.bitfinex.com/ws/2', manageOrderBooks=False,
|
||||
dead_man_switch=False, logLevel='INFO', *args, **kwargs):
|
||||
self.API_KEY=API_KEY
|
||||
self.API_SECRET=API_SECRET
|
||||
self.manageOrderBooks = manageOrderBooks
|
||||
self.dead_man_switch = dead_man_switch
|
||||
self.pendingOrders = {}
|
||||
self.orderBooks = {}
|
||||
|
||||
super(BfxWebsocket, self).__init__(host, logLevel=logLevel, *args, **kwargs)
|
||||
self.subscriptionManager = SubscriptionManager(self, logLevel=logLevel)
|
||||
self.orderManager = OrderManager(self, logLevel=logLevel)
|
||||
self.wallets = WalletManager()
|
||||
|
||||
self._WS_DATA_HANDLERS = {
|
||||
'tu': self._trade_update_handler,
|
||||
'wu': self._wallet_update_handler,
|
||||
'hb': self._heart_beat_handler,
|
||||
'te': self._trade_executed_handler,
|
||||
'oc': self._order_closed_handler,
|
||||
'ou': self._order_update_handler,
|
||||
'on': self._order_new_handler,
|
||||
'os': self._order_snapshot_handler,
|
||||
'ws': self._wallet_snapshot_handler,
|
||||
'ps': self._position_snapshot_handler,
|
||||
'fos': self._funding_offer_snapshot_handler,
|
||||
'fcs': self._funding_credit_snapshot_handler,
|
||||
'fls': self._funding_load_snapshot_handler,
|
||||
'bu': self._balance_update_handler,
|
||||
'n': self._notification_handler,
|
||||
'miu': self._margin_info_update_handler,
|
||||
'fiu': self._funding_info_update_handler
|
||||
ERRORS = {
|
||||
10000: 'Unknown event',
|
||||
10001: 'Generic error',
|
||||
10008: 'Concurrency error',
|
||||
10020: 'Request parameters error',
|
||||
10050: 'Configuration setup failed',
|
||||
10100: 'Failed authentication',
|
||||
10111: 'Error in authentication request payload',
|
||||
10112: 'Error in authentication request signature',
|
||||
10113: 'Error in authentication request encryption',
|
||||
10114: 'Error in authentication request nonce',
|
||||
10200: 'Error in un-authentication request',
|
||||
10300: 'Subscription Failed (generic)',
|
||||
10301: 'Already Subscribed',
|
||||
10302: 'Unknown channel',
|
||||
10400: 'Subscription Failed (generic)',
|
||||
10401: 'Not subscribed',
|
||||
11000: 'Not ready, try again later',
|
||||
20000: 'User is invalid!',
|
||||
20051: 'Websocket server stopping',
|
||||
20060: 'Websocket server resyncing',
|
||||
20061: 'Websocket server resync complete'
|
||||
}
|
||||
|
||||
self._WS_SYSTEM_HANDLERS = {
|
||||
'info': self._system_info_handler,
|
||||
'subscribed': self._system_subscribed_handler,
|
||||
'unsubscribed': self._system_unsubscribe_handler,
|
||||
'error': self._system_error_handler,
|
||||
'auth': self._system_auth_handler,
|
||||
'conf': self._system_conf_handler
|
||||
}
|
||||
def __init__(self, API_KEY=None, API_SECRET=None, host='wss://api.bitfinex.com/ws/2',
|
||||
manageOrderBooks=False, dead_man_switch=False, logLevel='INFO', *args, **kwargs):
|
||||
self.API_KEY = API_KEY
|
||||
self.API_SECRET = API_SECRET
|
||||
self.manageOrderBooks = manageOrderBooks
|
||||
self.dead_man_switch = dead_man_switch
|
||||
self.pendingOrders = {}
|
||||
self.orderBooks = {}
|
||||
|
||||
async def _ws_system_handler(self, msg):
|
||||
eType = msg.get('event')
|
||||
if eType in self._WS_SYSTEM_HANDLERS:
|
||||
await self._WS_SYSTEM_HANDLERS[eType](msg)
|
||||
else:
|
||||
self.logger.warn("Unknown websocket event: '{}' {}".format(eType, msg))
|
||||
super(BfxWebsocket, self).__init__(
|
||||
host, logLevel=logLevel, *args, **kwargs)
|
||||
self.subscriptionManager = SubscriptionManager(self, logLevel=logLevel)
|
||||
self.orderManager = OrderManager(self, logLevel=logLevel)
|
||||
self.wallets = WalletManager()
|
||||
|
||||
async def _ws_data_handler(self, data):
|
||||
dataEvent = data[1]
|
||||
chanId = data[0]
|
||||
|
||||
if type(dataEvent) is str and dataEvent in self._WS_DATA_HANDLERS:
|
||||
return await self._WS_DATA_HANDLERS[dataEvent](data)
|
||||
elif self.subscriptionManager.is_subscribed(chanId):
|
||||
subscription = self.subscriptionManager.get(chanId)
|
||||
# candles do not have an event
|
||||
if subscription.channel_name == 'candles':
|
||||
await self._candle_handler(data)
|
||||
if subscription.channel_name == 'book':
|
||||
await self._order_book_handler(data)
|
||||
if subscription.channel_name == 'trades':
|
||||
await self._trade_handler(data)
|
||||
else:
|
||||
self.logger.warn("Unknown data event: '{}' {}".format(dataEvent, data))
|
||||
|
||||
async def _system_info_handler(self, data):
|
||||
self.logger.info(data)
|
||||
if data.get('serverId', None):
|
||||
## connection has been established
|
||||
await self.on_open()
|
||||
|
||||
async def _system_conf_handler(self, data):
|
||||
flag = data.get('flags')
|
||||
status = data.get('status')
|
||||
if flag not in Flags.strings:
|
||||
self.logger.warn("Unknown config value set {}".format(flag))
|
||||
return
|
||||
flagString = Flags.strings[flag]
|
||||
if status == "OK":
|
||||
self.logger.info("Enabled config flag {}".format(flagString))
|
||||
else:
|
||||
self.logger.error("Unable to enable config flag {}".format(flagString))
|
||||
|
||||
async def _system_subscribed_handler(self, data):
|
||||
await self.subscriptionManager.confirm_subscription(data)
|
||||
|
||||
async def _system_unsubscribe_handler(self, data):
|
||||
await self.subscriptionManager.confirm_unsubscribe(data)
|
||||
|
||||
async def _system_error_handler(self, data):
|
||||
self._emit('error', data)
|
||||
|
||||
async def _system_auth_handler(self, data):
|
||||
if data.get('status') == 'FAILED':
|
||||
raise AuthError(self.ERRORS[data.get('code')])
|
||||
else:
|
||||
self._emit('authenticated', data)
|
||||
self.logger.info("Authentication successful.")
|
||||
|
||||
async def _trade_update_handler(self, data):
|
||||
tData = data[2]
|
||||
# [209, 'tu', [312372989, 1542303108930, 0.35, 5688.61834032]]
|
||||
if self.subscriptionManager.is_subscribed(data[0]):
|
||||
symbol = self.subscriptionManager.get(data[0]).symbol
|
||||
tradeObj = _parse_trade(tData, symbol)
|
||||
self._emit('new_trade', tradeObj)
|
||||
|
||||
async def _trade_executed_handler(self, data):
|
||||
tData = data[2]
|
||||
# [209, 'te', [312372989, 1542303108930, 0.35, 5688.61834032]]
|
||||
if self.subscriptionManager.is_subscribed(data[0]):
|
||||
symbol = self.subscriptionManager.get(data[0]).symbol
|
||||
tradeObj = _parse_trade(tData, symbol)
|
||||
self._emit('new_trade', tradeObj)
|
||||
|
||||
async def _wallet_update_handler(self, data):
|
||||
# [0,"wu",["exchange","USD",89134.66933283,0]]
|
||||
uw = self.wallets._update_from_event(data)
|
||||
self._emit('wallet_update', uw)
|
||||
self.logger.info("Wallet update: {}".format(uw))
|
||||
|
||||
async def _heart_beat_handler(self, data):
|
||||
self.logger.debug("Heartbeat - {}".format(self.host))
|
||||
|
||||
async def _margin_info_update_handler(self, data):
|
||||
self._emit('margin_info_update', data)
|
||||
self.logger.info("Margin info update: {}".format(data))
|
||||
|
||||
async def _funding_info_update_handler(self, data):
|
||||
self._emit('funding_info_update', data)
|
||||
self.logger.info("Funding info update: {}".format(data))
|
||||
|
||||
async def _notification_handler(self, data):
|
||||
# [0, 'n', [1542289340429, 'on-req', None, None,
|
||||
# [1151350600, None, 1542289341196, 'tBTCUSD', None, None, 0.01, None, 'EXCHANGE MARKET',
|
||||
# None, None, None, None, None, None, None, 18970, None, 0, 0, None, None, None, 0, None,
|
||||
# None, None, None, None, None, None, None], None, 'SUCCESS', 'Submitting exchange market buy order for 0.01 BTC.']]
|
||||
nInfo = data[2]
|
||||
self._emit('notification', nInfo)
|
||||
notificationType = nInfo[6]
|
||||
notificationText = nInfo[7]
|
||||
if notificationType == 'ERROR':
|
||||
# self._emit('error', notificationText)
|
||||
self.logger.error("Notification ERROR: {}".format(notificationText))
|
||||
else:
|
||||
self.logger.info("Notification SUCCESS: {}".format(notificationText))
|
||||
|
||||
async def _balance_update_handler(self, data):
|
||||
self.logger.info('Balance update: {}'.format(data[2]))
|
||||
self._emit('balance_update', data[2])
|
||||
|
||||
async def _order_closed_handler(self, data):
|
||||
await self.orderManager.confirm_order_closed(data)
|
||||
|
||||
async def _order_update_handler(self, data):
|
||||
await self.orderManager.confirm_order_update(data)
|
||||
|
||||
async def _order_new_handler(self, data):
|
||||
await self.orderManager.confirm_order_new(data)
|
||||
|
||||
async def _order_snapshot_handler(self, data):
|
||||
await self.orderManager.build_from_order_snapshot(data)
|
||||
|
||||
async def _wallet_snapshot_handler(self, data):
|
||||
wallets = self.wallets._update_from_snapshot(data)
|
||||
self._emit('wallet_snapshot', wallets)
|
||||
|
||||
async def _position_snapshot_handler(self, data):
|
||||
self._emit('position_snapshot', data)
|
||||
self.logger.info("Position snapshot: {}".format(data))
|
||||
|
||||
async def _funding_offer_snapshot_handler(self, data):
|
||||
self._emit('funding_offer_snapshot', data)
|
||||
self.logger.info("Funding offer snapshot: {}".format(data))
|
||||
|
||||
async def _funding_load_snapshot_handler(self, data):
|
||||
self._emit('funding_loan_snapshot', data[2])
|
||||
self.logger.info("Funding loan snapshot: {}".format(data))
|
||||
|
||||
async def _funding_credit_snapshot_handler(self, data):
|
||||
self._emit('funding_credit_snapshot', data[2])
|
||||
self.logger.info("Funding credit snapshot: {}".format(data))
|
||||
|
||||
async def _trade_handler(self, data):
|
||||
symbol = self.subscriptionManager.get(data[0]).symbol
|
||||
if type(data[1]) is list:
|
||||
data = data[1]
|
||||
# Process the batch of seed trades on
|
||||
# connection
|
||||
data.reverse()
|
||||
for t in data:
|
||||
trade = {
|
||||
'mts': t[1],
|
||||
'amount': t[2],
|
||||
'price': t[3],
|
||||
'symbol': symbol
|
||||
self._WS_DATA_HANDLERS = {
|
||||
'tu': self._trade_update_handler,
|
||||
'wu': self._wallet_update_handler,
|
||||
'hb': self._heart_beat_handler,
|
||||
'te': self._trade_executed_handler,
|
||||
'oc': self._order_closed_handler,
|
||||
'ou': self._order_update_handler,
|
||||
'on': self._order_new_handler,
|
||||
'os': self._order_snapshot_handler,
|
||||
'ws': self._wallet_snapshot_handler,
|
||||
'ps': self._position_snapshot_handler,
|
||||
'fos': self._funding_offer_snapshot_handler,
|
||||
'fcs': self._funding_credit_snapshot_handler,
|
||||
'fls': self._funding_load_snapshot_handler,
|
||||
'bu': self._balance_update_handler,
|
||||
'n': self._notification_handler,
|
||||
'miu': self._margin_info_update_handler,
|
||||
'fiu': self._funding_info_update_handler
|
||||
}
|
||||
self._emit('seed_trade', trade)
|
||||
|
||||
async def _candle_handler(self, data):
|
||||
subscription = self.subscriptionManager.get(data[0])
|
||||
if type(data[1][0]) is list:
|
||||
# Process the batch of seed candles on
|
||||
# websocket subscription
|
||||
candlesSnapshot = data[1]
|
||||
candlesSnapshot.reverse()
|
||||
for c in candlesSnapshot:
|
||||
candle = _parse_candle(c, subscription.symbol, subscription.timeframe)
|
||||
self._emit('seed_candle', candle)
|
||||
else:
|
||||
candle = _parse_candle(data[1], subscription.symbol, subscription.timeframe)
|
||||
self._emit('new_candle', candle)
|
||||
|
||||
async def _order_book_handler(self, data):
|
||||
obInfo = data[1]
|
||||
chanId = data[0]
|
||||
subscription = self.subscriptionManager.get(data[0])
|
||||
symbol = subscription.symbol
|
||||
if data[1] == "cs":
|
||||
dChecksum = data[2] & 0xffffffff # force to signed int
|
||||
checksum = self.orderBooks[symbol].checksum()
|
||||
# force checksums to signed integers
|
||||
isValid = (dChecksum) == (checksum)
|
||||
if isValid:
|
||||
self.logger.debug("Checksum orderbook validation for '{}' successful."
|
||||
.format(symbol))
|
||||
else:
|
||||
self.logger.warn("Checksum orderbook invalid for '{}'. Resetting subscription."
|
||||
.format(symbol))
|
||||
# re-build orderbook with snapshot
|
||||
await self.subscriptionManager.resubscribe(chanId)
|
||||
return
|
||||
if obInfo == []:
|
||||
self.orderBooks[symbol] = OrderBook()
|
||||
return
|
||||
isSnapshot = type(obInfo[0]) is list
|
||||
if isSnapshot:
|
||||
self.orderBooks[symbol] = OrderBook()
|
||||
self.orderBooks[symbol].updateFromSnapshot(obInfo)
|
||||
self._emit('order_book_snapshot', { 'symbol': symbol, 'data': obInfo })
|
||||
else:
|
||||
self.orderBooks[symbol].updateWith(obInfo)
|
||||
self._emit('order_book_update', { 'symbol': symbol, 'data': obInfo })
|
||||
self._WS_SYSTEM_HANDLERS = {
|
||||
'info': self._system_info_handler,
|
||||
'subscribed': self._system_subscribed_handler,
|
||||
'unsubscribed': self._system_unsubscribe_handler,
|
||||
'error': self._system_error_handler,
|
||||
'auth': self._system_auth_handler,
|
||||
'conf': self._system_conf_handler
|
||||
}
|
||||
|
||||
async def on_message(self, message):
|
||||
self.logger.debug(message)
|
||||
msg = json.loads(message)
|
||||
self._emit('all', msg)
|
||||
if type(msg) is dict:
|
||||
# System messages are received as json
|
||||
await self._ws_system_handler(msg)
|
||||
elif type(msg) is list:
|
||||
# All data messages are received as a list
|
||||
await self._ws_data_handler(msg)
|
||||
else:
|
||||
self.logger.warn('Unknown websocket response: {}'.format(msg))
|
||||
async def _ws_system_handler(self, msg):
|
||||
eType = msg.get('event')
|
||||
if eType in self._WS_SYSTEM_HANDLERS:
|
||||
await self._WS_SYSTEM_HANDLERS[eType](msg)
|
||||
else:
|
||||
self.logger.warn(
|
||||
"Unknown websocket event: '{}' {}".format(eType, msg))
|
||||
|
||||
async def _ws_authenticate_socket(self):
|
||||
jdata = generate_auth_payload(self.API_KEY, self.API_SECRET)
|
||||
if self.dead_man_switch:
|
||||
jdata['dms'] = 4
|
||||
await self.ws.send(json.dumps(jdata))
|
||||
async def _ws_data_handler(self, data):
|
||||
dataEvent = data[1]
|
||||
chan_id = data[0]
|
||||
|
||||
async def on_open(self):
|
||||
self.logger.info("Websocket opened.")
|
||||
self._emit('connected')
|
||||
# Orders are simulated in backtest mode
|
||||
if self.API_KEY and self.API_SECRET:
|
||||
await self._ws_authenticate_socket()
|
||||
# enable order book checksums
|
||||
if self.manageOrderBooks:
|
||||
await self.enable_flag(Flags.CHECKSUM)
|
||||
if type(dataEvent) is str and dataEvent in self._WS_DATA_HANDLERS:
|
||||
return await self._WS_DATA_HANDLERS[dataEvent](data)
|
||||
elif self.subscriptionManager.is_subscribed(chan_id):
|
||||
subscription = self.subscriptionManager.get(chan_id)
|
||||
# candles do not have an event
|
||||
if subscription.channel_name == 'candles':
|
||||
await self._candle_handler(data)
|
||||
if subscription.channel_name == 'book':
|
||||
await self._order_book_handler(data)
|
||||
if subscription.channel_name == 'trades':
|
||||
await self._trade_handler(data)
|
||||
else:
|
||||
self.logger.warn(
|
||||
"Unknown data event: '{}' {}".format(dataEvent, data))
|
||||
|
||||
async def _send_auth_command(self, channel_name, data):
|
||||
payload = [0, channel_name, None, data]
|
||||
await self.ws.send(json.dumps(payload))
|
||||
async def _system_info_handler(self, data):
|
||||
self.logger.info(data)
|
||||
if data.get('serverId', None):
|
||||
# connection has been established
|
||||
await self.on_open()
|
||||
|
||||
async def enable_flag(self, flag):
|
||||
payload = {
|
||||
"event": 'conf',
|
||||
"flags": flag
|
||||
}
|
||||
await self.ws.send(json.dumps(payload))
|
||||
async def _system_conf_handler(self, data):
|
||||
flag = data.get('flags')
|
||||
status = data.get('status')
|
||||
if flag not in Flags.strings:
|
||||
self.logger.warn("Unknown config value set {}".format(flag))
|
||||
return
|
||||
flagString = Flags.strings[flag]
|
||||
if status == "OK":
|
||||
self.logger.info("Enabled config flag {}".format(flagString))
|
||||
else:
|
||||
self.logger.error(
|
||||
"Unable to enable config flag {}".format(flagString))
|
||||
|
||||
def get_orderbook(self, symbol):
|
||||
return self.orderBooks.get(symbol, None)
|
||||
async def _system_subscribed_handler(self, data):
|
||||
await self.subscriptionManager.confirm_subscription(data)
|
||||
|
||||
async def subscribe(self, *args, **kwargs):
|
||||
return await self.subscriptionManager.subscribe(*args, **kwargs)
|
||||
async def _system_unsubscribe_handler(self, data):
|
||||
await self.subscriptionManager.confirm_unsubscribe(data)
|
||||
|
||||
async def unsubscribe(self, *args, **kwargs):
|
||||
return await self.subscriptionManager.unsubscribe(*args, **kwargs)
|
||||
async def _system_error_handler(self, data):
|
||||
self._emit('error', data)
|
||||
|
||||
async def resubscribe(self, *args, **kwargs):
|
||||
return await self.subscriptionManager.resubscribe(*args, **kwargs)
|
||||
async def _system_auth_handler(self, data):
|
||||
if data.get('status') == 'FAILED':
|
||||
raise AuthError(self.ERRORS[data.get('code')])
|
||||
else:
|
||||
self._emit('authenticated', data)
|
||||
self.logger.info("Authentication successful.")
|
||||
|
||||
async def unsubscribe_all(self, *args, **kwargs):
|
||||
return await self.subscriptionManager.unsubscribe_all(*args, **kwargs)
|
||||
async def _trade_update_handler(self, data):
|
||||
tData = data[2]
|
||||
# [209, 'tu', [312372989, 1542303108930, 0.35, 5688.61834032]]
|
||||
if self.subscriptionManager.is_subscribed(data[0]):
|
||||
symbol = self.subscriptionManager.get(data[0]).symbol
|
||||
tradeObj = _parse_trade(tData, symbol)
|
||||
self._emit('new_trade', tradeObj)
|
||||
|
||||
async def resubscribe_all(self, *args, **kwargs):
|
||||
return await self.subscriptionManager.resubscribe_all(*args, **kwargs)
|
||||
async def _trade_executed_handler(self, data):
|
||||
tData = data[2]
|
||||
# [209, 'te', [312372989, 1542303108930, 0.35, 5688.61834032]]
|
||||
if self.subscriptionManager.is_subscribed(data[0]):
|
||||
symbol = self.subscriptionManager.get(data[0]).symbol
|
||||
tradeObj = _parse_trade(tData, symbol)
|
||||
self._emit('new_trade', tradeObj)
|
||||
|
||||
async def submit_order(self, *args, **kwargs):
|
||||
return await self.orderManager.submit_order(*args, **kwargs)
|
||||
async def _wallet_update_handler(self, data):
|
||||
# [0,"wu",["exchange","USD",89134.66933283,0]]
|
||||
uw = self.wallets._update_from_event(data)
|
||||
self._emit('wallet_update', uw)
|
||||
self.logger.info("Wallet update: {}".format(uw))
|
||||
|
||||
async def update_order(self, *args, **kwargs):
|
||||
return await self.orderManager.update_order(*args, **kwargs)
|
||||
async def _heart_beat_handler(self, data):
|
||||
self.logger.debug("Heartbeat - {}".format(self.host))
|
||||
|
||||
async def cancel_order(self, *args, **kwargs):
|
||||
return await self.orderManager.cancel_order(*args, **kwargs)
|
||||
async def _margin_info_update_handler(self, data):
|
||||
self._emit('margin_info_update', data)
|
||||
self.logger.info("Margin info update: {}".format(data))
|
||||
|
||||
async def cancel_all_orders(self, *args, **kwargs):
|
||||
return await self.orderManager.cancel_all_orders(*args, **kwargs)
|
||||
|
||||
async def cancel_order_multi(self, *args, **kwargs):
|
||||
return await self.cancel_order_multi(*args, **kwargs)
|
||||
async def _funding_info_update_handler(self, data):
|
||||
self._emit('funding_info_update', data)
|
||||
self.logger.info("Funding info update: {}".format(data))
|
||||
|
||||
async def _notification_handler(self, data):
|
||||
nInfo = data[2]
|
||||
self._emit('notification', nInfo)
|
||||
notificationType = nInfo[6]
|
||||
notificationText = nInfo[7]
|
||||
if notificationType == 'ERROR':
|
||||
# self._emit('error', notificationText)
|
||||
self.logger.error(
|
||||
"Notification ERROR: {}".format(notificationText))
|
||||
else:
|
||||
self.logger.info(
|
||||
"Notification SUCCESS: {}".format(notificationText))
|
||||
|
||||
async def _balance_update_handler(self, data):
|
||||
self.logger.info('Balance update: {}'.format(data[2]))
|
||||
self._emit('balance_update', data[2])
|
||||
|
||||
async def _order_closed_handler(self, data):
|
||||
await self.orderManager.confirm_order_closed(data)
|
||||
|
||||
async def _order_update_handler(self, data):
|
||||
await self.orderManager.confirm_order_update(data)
|
||||
|
||||
async def _order_new_handler(self, data):
|
||||
await self.orderManager.confirm_order_new(data)
|
||||
|
||||
async def _order_snapshot_handler(self, data):
|
||||
await self.orderManager.build_from_order_snapshot(data)
|
||||
|
||||
async def _wallet_snapshot_handler(self, data):
|
||||
wallets = self.wallets._update_from_snapshot(data)
|
||||
self._emit('wallet_snapshot', wallets)
|
||||
|
||||
async def _position_snapshot_handler(self, data):
|
||||
self._emit('position_snapshot', data)
|
||||
self.logger.info("Position snapshot: {}".format(data))
|
||||
|
||||
async def _funding_offer_snapshot_handler(self, data):
|
||||
self._emit('funding_offer_snapshot', data)
|
||||
self.logger.info("Funding offer snapshot: {}".format(data))
|
||||
|
||||
async def _funding_load_snapshot_handler(self, data):
|
||||
self._emit('funding_loan_snapshot', data[2])
|
||||
self.logger.info("Funding loan snapshot: {}".format(data))
|
||||
|
||||
async def _funding_credit_snapshot_handler(self, data):
|
||||
self._emit('funding_credit_snapshot', data[2])
|
||||
self.logger.info("Funding credit snapshot: {}".format(data))
|
||||
|
||||
async def _trade_handler(self, data):
|
||||
symbol = self.subscriptionManager.get(data[0]).symbol
|
||||
if type(data[1]) is list:
|
||||
data = data[1]
|
||||
# Process the batch of seed trades on
|
||||
# connection
|
||||
data.reverse()
|
||||
for t in data:
|
||||
trade = {
|
||||
'mts': t[1],
|
||||
'amount': t[2],
|
||||
'price': t[3],
|
||||
'symbol': symbol
|
||||
}
|
||||
self._emit('seed_trade', trade)
|
||||
|
||||
async def _candle_handler(self, data):
|
||||
subscription = self.subscriptionManager.get(data[0])
|
||||
if type(data[1][0]) is list:
|
||||
# Process the batch of seed candles on
|
||||
# websocket subscription
|
||||
candlesSnapshot = data[1]
|
||||
candlesSnapshot.reverse()
|
||||
for c in candlesSnapshot:
|
||||
candle = _parse_candle(
|
||||
c, subscription.symbol, subscription.timeframe)
|
||||
self._emit('seed_candle', candle)
|
||||
else:
|
||||
candle = _parse_candle(
|
||||
data[1], subscription.symbol, subscription.timeframe)
|
||||
self._emit('new_candle', candle)
|
||||
|
||||
async def _order_book_handler(self, data):
|
||||
obInfo = data[1]
|
||||
chan_id = data[0]
|
||||
subscription = self.subscriptionManager.get(data[0])
|
||||
symbol = subscription.symbol
|
||||
if data[1] == "cs":
|
||||
dChecksum = data[2] & 0xffffffff # force to signed int
|
||||
checksum = self.orderBooks[symbol].checksum()
|
||||
# force checksums to signed integers
|
||||
isValid = (dChecksum) == (checksum)
|
||||
if isValid:
|
||||
msg = "Checksum orderbook validation for '{}' successful."
|
||||
self.logger.debug(msg.format(symbol))
|
||||
else:
|
||||
msg = "Checksum orderbook invalid for '{}'. Resetting subscription."
|
||||
self.logger.warn(msg.format(symbol))
|
||||
# re-build orderbook with snapshot
|
||||
await self.subscriptionManager.resubscribe(chan_id)
|
||||
return
|
||||
if obInfo == []:
|
||||
self.orderBooks[symbol] = OrderBook()
|
||||
return
|
||||
isSnapshot = type(obInfo[0]) is list
|
||||
if isSnapshot:
|
||||
self.orderBooks[symbol] = OrderBook()
|
||||
self.orderBooks[symbol].update_from_snapshot(obInfo)
|
||||
self._emit('order_book_snapshot', {
|
||||
'symbol': symbol, 'data': obInfo})
|
||||
else:
|
||||
self.orderBooks[symbol].update_with(obInfo)
|
||||
self._emit('order_book_update', {'symbol': symbol, 'data': obInfo})
|
||||
|
||||
async def on_message(self, message):
|
||||
self.logger.debug(message)
|
||||
msg = json.loads(message)
|
||||
self._emit('all', msg)
|
||||
if type(msg) is dict:
|
||||
# System messages are received as json
|
||||
await self._ws_system_handler(msg)
|
||||
elif type(msg) is list:
|
||||
# All data messages are received as a list
|
||||
await self._ws_data_handler(msg)
|
||||
else:
|
||||
self.logger.warn('Unknown websocket response: {}'.format(msg))
|
||||
|
||||
async def _ws_authenticate_socket(self):
|
||||
jdata = generate_auth_payload(self.API_KEY, self.API_SECRET)
|
||||
if self.dead_man_switch:
|
||||
jdata['dms'] = 4
|
||||
await self.ws.send(json.dumps(jdata))
|
||||
|
||||
async def on_open(self):
|
||||
self.logger.info("Websocket opened.")
|
||||
self._emit('connected')
|
||||
# Orders are simulated in backtest mode
|
||||
if self.API_KEY and self.API_SECRET:
|
||||
await self._ws_authenticate_socket()
|
||||
# enable order book checksums
|
||||
if self.manageOrderBooks:
|
||||
await self.enable_flag(Flags.CHECKSUM)
|
||||
|
||||
async def _send_auth_command(self, channel_name, data):
|
||||
payload = [0, channel_name, None, data]
|
||||
await self.ws.send(json.dumps(payload))
|
||||
|
||||
async def enable_flag(self, flag):
|
||||
payload = {
|
||||
"event": 'conf',
|
||||
"flags": flag
|
||||
}
|
||||
await self.ws.send(json.dumps(payload))
|
||||
|
||||
def get_orderbook(self, symbol):
|
||||
return self.orderBooks.get(symbol, None)
|
||||
|
||||
async def subscribe(self, *args, **kwargs):
|
||||
return await self.subscriptionManager.subscribe(*args, **kwargs)
|
||||
|
||||
async def unsubscribe(self, *args, **kwargs):
|
||||
return await self.subscriptionManager.unsubscribe(*args, **kwargs)
|
||||
|
||||
async def resubscribe(self, *args, **kwargs):
|
||||
return await self.subscriptionManager.resubscribe(*args, **kwargs)
|
||||
|
||||
async def unsubscribe_all(self, *args, **kwargs):
|
||||
return await self.subscriptionManager.unsubscribe_all(*args, **kwargs)
|
||||
|
||||
async def resubscribe_all(self, *args, **kwargs):
|
||||
return await self.subscriptionManager.resubscribe_all(*args, **kwargs)
|
||||
|
||||
async def submit_order(self, *args, **kwargs):
|
||||
return await self.orderManager.submit_order(*args, **kwargs)
|
||||
|
||||
async def update_order(self, *args, **kwargs):
|
||||
return await self.orderManager.update_order(*args, **kwargs)
|
||||
|
||||
async def cancel_order(self, *args, **kwargs):
|
||||
return await self.orderManager.cancel_order(*args, **kwargs)
|
||||
|
||||
async def cancel_all_orders(self, *args, **kwargs):
|
||||
return await self.orderManager.cancel_all_orders(*args, **kwargs)
|
||||
|
||||
async def cancel_order_multi(self, *args, **kwargs):
|
||||
return await self.cancel_order_multi(*args, **kwargs)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
"""
|
||||
Module used as a interfeace to describe a generick websocket client
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import websockets
|
||||
import json
|
||||
@@ -5,66 +9,106 @@ import json
|
||||
from pyee import EventEmitter
|
||||
from ..utils.CustomLogger import CustomLogger
|
||||
|
||||
class AuthError(Exception): pass
|
||||
|
||||
class AuthError(Exception):
|
||||
"""
|
||||
Thrown whenever there is a problem with the authentication packet
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def is_json(myjson):
|
||||
try:
|
||||
json_object = json.loads(myjson)
|
||||
except ValueError as e:
|
||||
return False
|
||||
return True
|
||||
try:
|
||||
json_object = json.loads(myjson)
|
||||
except ValueError as e:
|
||||
return False
|
||||
return True
|
||||
|
||||
class GenericWebsocket(object):
|
||||
|
||||
def __init__(self, host, logLevel='INFO', loop=None):
|
||||
self.host = host
|
||||
self.logger = CustomLogger('BfxWebsocket', logLevel=logLevel)
|
||||
self.loop = loop or asyncio.get_event_loop()
|
||||
self.events = EventEmitter(scheduler=asyncio.ensure_future, loop=self.loop)
|
||||
self.ws = None
|
||||
class GenericWebsocket:
|
||||
"""
|
||||
Websocket object used to contain the base functionality of a websocket.
|
||||
Inlcudes an event emitter and a standard websocket client.
|
||||
"""
|
||||
|
||||
def run(self):
|
||||
self.loop.run_until_complete(self._main(self.host))
|
||||
def __init__(self, host, logLevel='INFO', loop=None):
|
||||
self.host = host
|
||||
self.logger = CustomLogger('BfxWebsocket', logLevel=logLevel)
|
||||
self.loop = loop or asyncio.get_event_loop()
|
||||
self.events = EventEmitter(
|
||||
scheduler=asyncio.ensure_future, loop=self.loop)
|
||||
self.ws = None
|
||||
|
||||
def get_task_executable(self):
|
||||
return self._main(self.host)
|
||||
def run(self):
|
||||
"""
|
||||
Run the websocket connection indefinitely
|
||||
"""
|
||||
self.loop.run_until_complete(self._main(self.host))
|
||||
|
||||
async def _main(self, host):
|
||||
async with websockets.connect(host) as websocket:
|
||||
self.ws = websocket
|
||||
self.logger.info("Wesocket connectedt to {}".format(self.host))
|
||||
while True:
|
||||
await asyncio.sleep(0)
|
||||
message = await websocket.recv()
|
||||
await self.on_message(message)
|
||||
def get_task_executable(self):
|
||||
"""
|
||||
Get the run indefinitely asyncio task
|
||||
"""
|
||||
return self._main(self.host)
|
||||
|
||||
def remove_all_listeners(self, event):
|
||||
self.events.remove_all_listeners(event)
|
||||
async def _main(self, host):
|
||||
async with websockets.connect(host) as websocket:
|
||||
self.ws = websocket
|
||||
self.logger.info("Wesocket connectedt to {}".format(self.host))
|
||||
while True:
|
||||
await asyncio.sleep(0)
|
||||
message = await websocket.recv()
|
||||
await self.on_message(message)
|
||||
|
||||
def on(self, event, func=None):
|
||||
if not func:
|
||||
return self.events.on(event)
|
||||
self.events.on(event, func)
|
||||
def remove_all_listeners(self, event):
|
||||
"""
|
||||
Remove all listeners from event emitter
|
||||
"""
|
||||
self.events.remove_all_listeners(event)
|
||||
|
||||
def once(self, event, func=None):
|
||||
if not func:
|
||||
return self.events.once(event)
|
||||
self.events.once(event, func)
|
||||
def on(self, event, func=None):
|
||||
"""
|
||||
Add a new event to the event emitter
|
||||
"""
|
||||
if not func:
|
||||
return self.events.on(event)
|
||||
self.events.on(event, func)
|
||||
|
||||
def _emit(self, event, *args, **kwargs):
|
||||
self.events.emit(event, *args, **kwargs)
|
||||
def once(self, event, func=None):
|
||||
"""
|
||||
Add a new event to only fire once to the event
|
||||
emitter
|
||||
"""
|
||||
if not func:
|
||||
return self.events.once(event)
|
||||
self.events.once(event, func)
|
||||
|
||||
async def on_error(self, error):
|
||||
self.logger.error(error)
|
||||
self.events.emit('error', error)
|
||||
def _emit(self, event, *args, **kwargs):
|
||||
self.events.emit(event, *args, **kwargs)
|
||||
|
||||
async def on_close(self):
|
||||
self.logger.info("Websocket closed.")
|
||||
await self.ws.close()
|
||||
self._emit('done')
|
||||
async def on_error(self, error):
|
||||
"""
|
||||
On websocket error print and fire event
|
||||
"""
|
||||
self.logger.error(error)
|
||||
self.events.emit('error', error)
|
||||
|
||||
async def on_open(self):
|
||||
pass
|
||||
async def on_close(self):
|
||||
"""
|
||||
On websocket close print and fire event
|
||||
"""
|
||||
self.logger.info("Websocket closed.")
|
||||
await self.ws.close()
|
||||
self._emit('done')
|
||||
|
||||
async def on_message(self, message):
|
||||
pass
|
||||
async def on_open(self):
|
||||
"""
|
||||
On websocket open
|
||||
"""
|
||||
pass
|
||||
|
||||
async def on_message(self, message):
|
||||
"""
|
||||
On websocket message
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -1,268 +1,264 @@
|
||||
"""
|
||||
Module used to house all of the functions/classes used to handle orders
|
||||
"""
|
||||
|
||||
import time
|
||||
import asyncio
|
||||
|
||||
from ..utils.CustomLogger import CustomLogger
|
||||
from ..models import Order
|
||||
|
||||
|
||||
class OrderManager:
|
||||
|
||||
def __init__(self, bfxapi, logLevel='INFO'):
|
||||
self.bfxapi = bfxapi
|
||||
self.pending_orders = {}
|
||||
self.pending_callbacks = {}
|
||||
self.closed_orders = {}
|
||||
self.open_orders = {}
|
||||
self.logger = CustomLogger('BfxOrderManager', logLevel=logLevel)
|
||||
|
||||
def get_open_orders(self):
|
||||
return list(self.open_orders.values())
|
||||
|
||||
def get_closed_orders(self):
|
||||
return list(self.closed_orders.values())
|
||||
|
||||
def get_pending_orders(self):
|
||||
return list(self.pending_orders.values())
|
||||
|
||||
async def _confirm_order(self, order, isClosed=False):
|
||||
"""
|
||||
Called every time an order signal has been received. This function
|
||||
manages the local list of open orders.
|
||||
Handles all of the functionality for opening, updating and closing order.
|
||||
Also contains state such as all of your open orders and orders that have
|
||||
closed.
|
||||
"""
|
||||
if order.cId in self.pending_orders:
|
||||
await self._execute_confirm_callback(order.cId, order)
|
||||
if isClosed:
|
||||
await self._execute_close_callback(order.cId, order)
|
||||
order.set_confirmed()
|
||||
# remove from pending orders list
|
||||
del self.pending_orders[order.cId]
|
||||
self.bfxapi._emit('order_confirmed', order)
|
||||
else:
|
||||
await self._execute_confirm_callback(order.id, order)
|
||||
if isClosed:
|
||||
await self._execute_close_callback(order.id, order)
|
||||
|
||||
async def confirm_order_closed(self, raw_ws_data):
|
||||
# order created and executed
|
||||
# [0,"oc",[1151349678,null,1542203391995,"tBTCUSD",1542203389940,1542203389966,0,0.1,
|
||||
# "EXCHANGE MARKET",null,null,null,0,"EXECUTED @ 18922.0(0.03299997): was PARTIALLY FILLED
|
||||
# @ 18909.0(0.06700003)",null,null,18909,18913.2899961,0,0,null,null,null,0,0,null,null,null,
|
||||
# "API>BFX",null,null,null]]
|
||||
order = Order.from_raw_order(raw_ws_data[2])
|
||||
order.set_open_state(False)
|
||||
if order.id in self.open_orders:
|
||||
del self.open_orders[order.id]
|
||||
await self._confirm_order(order, isClosed=True)
|
||||
self.logger.info("Order closed: {} {}".format(order.symbol, order.status))
|
||||
self.bfxapi._emit('order_closed', order)
|
||||
def __init__(self, bfxapi, logLevel='INFO'):
|
||||
self.bfxapi = bfxapi
|
||||
self.pending_orders = {}
|
||||
self.pending_callbacks = {}
|
||||
self.closed_orders = {}
|
||||
self.open_orders = {}
|
||||
self.logger = CustomLogger('BfxOrderManager', logLevel=logLevel)
|
||||
|
||||
async def build_from_order_snapshot(self, raw_ws_data):
|
||||
print (raw_ws_data)
|
||||
#[0, 'os', [[1151363978, None, 1544460962979, 'tBTCUSD', 1544460959604, 1544460959626,
|
||||
# -0.12620639, -0.12620639, 'EXCHANGE LIMIT', None, None,None, 0, 'ACTIVE', None, None, 18793,
|
||||
# 0, 0, 0, None, None, None, 0, 0, None, None, None, 'API>BFX', None, None, None]]]
|
||||
'''
|
||||
Rebuild the user orderbook based on an incoming snapshot
|
||||
'''
|
||||
osData = raw_ws_data[2]
|
||||
self.open_orders = {}
|
||||
for raw_order in osData:
|
||||
order = Order.from_raw_order(raw_order)
|
||||
order.set_open_state(True)
|
||||
self.open_orders[order.id] = order
|
||||
self.bfxapi._emit('order_snapshot', self.get_open_orders())
|
||||
def get_open_orders(self):
|
||||
return list(self.open_orders.values())
|
||||
|
||||
async def confirm_order_update(self, raw_ws_data):
|
||||
# order created but partially filled
|
||||
# [0, 'ou', [1151351581, None, 1542629457873, 'tBTCUSD', 1542629458071,
|
||||
# 1542629458189, 0.01, 0.01, 'EXCHANGE LIMIT', None, None, None, 0, 'ACTIVE',
|
||||
# None, None, 100, 0, 0, 0, None, None, None, 0, 0, None, None, None, 'API>BFX',
|
||||
# None, None, None]]
|
||||
order = Order.from_raw_order(raw_ws_data[2])
|
||||
order.set_open_state(True)
|
||||
self.open_orders[order.id] = order
|
||||
await self._confirm_order(order)
|
||||
self.logger.info("Order update: {}".format(order))
|
||||
self.bfxapi._emit('order_update', order)
|
||||
def get_closed_orders(self):
|
||||
return list(self.closed_orders.values())
|
||||
|
||||
async def confirm_order_new(self, raw_ws_data):
|
||||
# order created but not executed / created but partially filled
|
||||
# [0, 'on', [1151351563, None, 1542624024383, 'tBTCUSD', 1542624024596,
|
||||
# 1542624024617, 0.01, 0.01, 'EXCHANGE LIMIT', None, None, None, 0, 'ACTIVE',
|
||||
# None, None, 100, 0, 0, 0, None, None, None, 0, 0, None, None, None, 'API>BFX',
|
||||
# None, None, None]]
|
||||
order = Order.from_raw_order(raw_ws_data[2])
|
||||
order.set_open_state(True)
|
||||
self.open_orders[order.id] = order
|
||||
await self._confirm_order(order)
|
||||
self.logger.info("Order new: {}".format(order))
|
||||
self.bfxapi._emit('order_new', order)
|
||||
def get_pending_orders(self):
|
||||
return list(self.pending_orders.values())
|
||||
|
||||
def _gen_unqiue_cid(self):
|
||||
return int(round(time.time() * 1000))
|
||||
async def _confirm_order(self, order, isClosed=False):
|
||||
"""
|
||||
Called every time an order signal has been received. This function
|
||||
manages the local list of open orders.
|
||||
"""
|
||||
if order.cId in self.pending_orders:
|
||||
await self._execute_confirm_callback(order.cId, order)
|
||||
if isClosed:
|
||||
await self._execute_close_callback(order.cId, order)
|
||||
order.set_confirmed()
|
||||
# remove from pending orders list
|
||||
del self.pending_orders[order.cId]
|
||||
self.bfxapi._emit('order_confirmed', order)
|
||||
else:
|
||||
await self._execute_confirm_callback(order.id, order)
|
||||
if isClosed:
|
||||
await self._execute_close_callback(order.id, order)
|
||||
|
||||
async def submit_order(self, symbol, price, amount, market_type=Order.Type.LIMIT,
|
||||
hidden=False, price_trailing=None, price_aux_limit=None, oco_stop_price=None,
|
||||
close=False, reduce_only=False, post_only=False, oco=False, time_in_force=None,
|
||||
onConfirm=None, onClose=None, *args, **kwargs):
|
||||
"""
|
||||
Submit a new order
|
||||
async def confirm_order_closed(self, raw_ws_data):
|
||||
order = Order.from_raw_order(raw_ws_data[2])
|
||||
order.set_open_state(False)
|
||||
if order.id in self.open_orders:
|
||||
del self.open_orders[order.id]
|
||||
await self._confirm_order(order, isClosed=True)
|
||||
self.logger.info("Order closed: {} {}".format(
|
||||
order.symbol, order.status))
|
||||
self.bfxapi._emit('order_closed', order)
|
||||
|
||||
@param symbol: the name of the symbol i.e 'tBTCUSD
|
||||
@param price: the price you want to buy/sell at (must be positive)
|
||||
@param amount: order size: how much you want to buy/sell,
|
||||
a negative amount indicates a sell order and positive a buy order
|
||||
@param market_type Order.Type: please see Order.Type enum
|
||||
amount decimal string Positive for buy, Negative for sell
|
||||
@param hidden: if True, order should be hidden from orderbooks
|
||||
@param price_trailing: decimal trailing price
|
||||
@param price_aux_limit: decimal auxiliary Limit price (only for STOP LIMIT)
|
||||
@param oco_stop_price: set the oco stop price (requires oco = True)
|
||||
@param close: if True, close position if position present
|
||||
@param reduce_only: if True, ensures that the executed order does not flip the opened position
|
||||
@param post_only: if True, ensures the limit order will be added to the order book and not
|
||||
match with a pre-existing order
|
||||
@param oco: cancels other order option allows you to place a pair of orders stipulating
|
||||
that if one order is executed fully or partially, then the other is automatically canceled
|
||||
async def build_from_order_snapshot(self, raw_ws_data):
|
||||
'''
|
||||
Rebuild the user orderbook based on an incoming snapshot
|
||||
'''
|
||||
osData = raw_ws_data[2]
|
||||
self.open_orders = {}
|
||||
for raw_order in osData:
|
||||
order = Order.from_raw_order(raw_order)
|
||||
order.set_open_state(True)
|
||||
self.open_orders[order.id] = order
|
||||
self.bfxapi._emit('order_snapshot', self.get_open_orders())
|
||||
|
||||
@param time_in_force: datetime for automatic order cancellation ie. 2020-01-01 10:45:23
|
||||
@param onConfirm: function called when the bitfinex websocket receives signal that the order
|
||||
was confirmed
|
||||
@param onClose: function called when the bitfinex websocket receives signal that the order
|
||||
was closed due to being filled or cancelled
|
||||
"""
|
||||
cId = self._gen_unqiue_cid()
|
||||
# create base payload with required data
|
||||
payload = {
|
||||
"cid": cId,
|
||||
"type": str(market_type),
|
||||
"symbol": symbol,
|
||||
"amount": str(amount),
|
||||
"price": str(price),
|
||||
}
|
||||
# caclulate and add flags
|
||||
flags = self._calculate_flags(hidden, close, reduce_only, post_only, oco)
|
||||
payload['flags'] = flags
|
||||
# add extra parameters
|
||||
if (price_trailing):
|
||||
payload['price_trailing'] = price_trailing
|
||||
if (price_aux_limit):
|
||||
payload['price_aux_limit'] = price_aux_limit
|
||||
if (oco_stop_price):
|
||||
payload['price_oco_stop'] = oco_stop_price
|
||||
if (time_in_force):
|
||||
payload['tif'] = time_in_force
|
||||
# submit the order
|
||||
self.pending_orders[cId] = payload
|
||||
self._create_callback(cId, onConfirm=onConfirm, onClose=onClose)
|
||||
await self.bfxapi._send_auth_command('on', payload)
|
||||
self.logger.info("Order cid={} ({} {} @ {}) dispatched".format(
|
||||
cId, symbol, amount, price))
|
||||
async def confirm_order_update(self, raw_ws_data):
|
||||
order = Order.from_raw_order(raw_ws_data[2])
|
||||
order.set_open_state(True)
|
||||
self.open_orders[order.id] = order
|
||||
await self._confirm_order(order)
|
||||
self.logger.info("Order update: {}".format(order))
|
||||
self.bfxapi._emit('order_update', order)
|
||||
|
||||
async def update_order(self, orderId, price=None, amount=None, delta=None, price_aux_limit=None,
|
||||
price_trailing=None, hidden=False, close=False, reduce_only=False, post_only=False,
|
||||
time_in_force=None, onConfirm=None, onClose=None):
|
||||
"""
|
||||
Update an existing order
|
||||
async def confirm_order_new(self, raw_ws_data):
|
||||
order = Order.from_raw_order(raw_ws_data[2])
|
||||
order.set_open_state(True)
|
||||
self.open_orders[order.id] = order
|
||||
await self._confirm_order(order)
|
||||
self.logger.info("Order new: {}".format(order))
|
||||
self.bfxapi._emit('order_new', order)
|
||||
|
||||
@param orderId: the id of the order that you want to update
|
||||
@param price: the price you want to buy/sell at (must be positive)
|
||||
@param amount: order size: how much you want to buy/sell,
|
||||
a negative amount indicates a sell order and positive a buy order
|
||||
@param delta: change of amount
|
||||
@param price_trailing: decimal trailing price
|
||||
@param price_aux_limit: decimal auxiliary Limit price (only for STOP LIMIT)
|
||||
@param hidden: if True, order should be hidden from orderbooks
|
||||
@param close: if True, close position if position present
|
||||
@param reduce_only: if True, ensures that the executed order does not flip the opened position
|
||||
@param post_only: if True, ensures the limit order will be added to the order book and not
|
||||
match with a pre-existing order
|
||||
@param time_in_force: datetime for automatic order cancellation ie. 2020-01-01 10:45:23
|
||||
@param onConfirm: function called when the bitfinex websocket receives signal that the order
|
||||
was confirmed
|
||||
@param onClose: function called when the bitfinex websocket receives signal that the order
|
||||
was closed due to being filled or cancelled
|
||||
"""
|
||||
order = self.open_orders[orderId]
|
||||
self._create_callback(order.cId, onConfirm=onConfirm, onClose=onClose)
|
||||
payload = { "id": orderId }
|
||||
if price is not None:
|
||||
payload['price'] = str(price)
|
||||
if amount is not None:
|
||||
payload['amount'] = str(amount)
|
||||
if delta is not None:
|
||||
payload['delta'] = str(delta)
|
||||
if price_aux_limit is not None:
|
||||
payload['price_aux_limit'] = str(price_aux_limit)
|
||||
if price_trailing is not None:
|
||||
payload['price_trailing'] = str(price_trailing)
|
||||
if time_in_force is not None:
|
||||
payload['time_in_force'] = str(time_in_force)
|
||||
flags = self._calculate_flags(hidden, close, reduce_only, post_only, False)
|
||||
payload['flags'] = flags
|
||||
await self.bfxapi._send_auth_command('ou', payload)
|
||||
self.logger.info("Update Order order_id={} dispatched".format(orderId))
|
||||
def _gen_unqiue_cid(self):
|
||||
return int(round(time.time() * 1000))
|
||||
|
||||
async def cancel_order(self, orderId, onConfirm=None, onClose=None):
|
||||
"""
|
||||
Cancel an existing open order
|
||||
async def submit_order(self, symbol, price, amount, market_type=Order.Type.LIMIT,
|
||||
hidden=False, price_trailing=None, price_aux_limit=None,
|
||||
oco_stop_price=None, close=False, reduce_only=False,
|
||||
post_only=False, oco=False, time_in_force=None,
|
||||
onConfirm=None, onClose=None, *args, **kwargs):
|
||||
"""
|
||||
Submit a new order
|
||||
|
||||
@param orderId: the id of the order that you want to update
|
||||
@param onConfirm: function called when the bitfinex websocket receives signal that the order
|
||||
was confirmed
|
||||
@param onClose: function called when the bitfinex websocket receives signal that the order
|
||||
was closed due to being filled or cancelled
|
||||
"""
|
||||
# order = self.open_orders[orderId]
|
||||
self._create_callback(orderId, onConfirm=onConfirm, onClose=onClose)
|
||||
await self.bfxapi._send_auth_command('oc', { 'id': orderId })
|
||||
self.logger.info("Order cancel order_id={} dispatched".format(orderId))
|
||||
@param symbol: the name of the symbol i.e 'tBTCUSD
|
||||
@param price: the price you want to buy/sell at (must be positive)
|
||||
@param amount: order size: how much you want to buy/sell,
|
||||
a negative amount indicates a sell order and positive a buy order
|
||||
@param market_type Order.Type: please see Order.Type enum
|
||||
amount decimal string Positive for buy, Negative for sell
|
||||
@param hidden: if True, order should be hidden from orderbooks
|
||||
@param price_trailing: decimal trailing price
|
||||
@param price_aux_limit: decimal auxiliary Limit price (only for STOP LIMIT)
|
||||
@param oco_stop_price: set the oco stop price (requires oco = True)
|
||||
@param close: if True, close position if position present
|
||||
@param reduce_only: if True, ensures that the executed order does not flip the opened position
|
||||
@param post_only: if True, ensures the limit order will be added to the order book and not
|
||||
match with a pre-existing order
|
||||
@param oco: cancels other order option allows you to place a pair of orders stipulating
|
||||
that if one order is executed fully or partially, then the other is automatically canceled
|
||||
|
||||
async def cancel_all_orders(self):
|
||||
"""
|
||||
Cancel all existing open orders
|
||||
@param time_in_force: datetime for automatic order cancellation ie. 2020-01-01 10:45:23
|
||||
@param onConfirm: function called when the bitfinex websocket receives signal that the order
|
||||
was confirmed
|
||||
@param onClose: function called when the bitfinex websocket receives signal that the order
|
||||
was closed due to being filled or cancelled
|
||||
"""
|
||||
cId = self._gen_unqiue_cid()
|
||||
# create base payload with required data
|
||||
payload = {
|
||||
"cid": cId,
|
||||
"type": str(market_type),
|
||||
"symbol": symbol,
|
||||
"amount": str(amount),
|
||||
"price": str(price),
|
||||
}
|
||||
# caclulate and add flags
|
||||
flags = self._calculate_flags(
|
||||
hidden, close, reduce_only, post_only, oco)
|
||||
payload['flags'] = flags
|
||||
# add extra parameters
|
||||
if (price_trailing):
|
||||
payload['price_trailing'] = price_trailing
|
||||
if (price_aux_limit):
|
||||
payload['price_aux_limit'] = price_aux_limit
|
||||
if (oco_stop_price):
|
||||
payload['price_oco_stop'] = oco_stop_price
|
||||
if (time_in_force):
|
||||
payload['tif'] = time_in_force
|
||||
# submit the order
|
||||
self.pending_orders[cId] = payload
|
||||
self._create_callback(cId, onConfirm=onConfirm, onClose=onClose)
|
||||
await self.bfxapi._send_auth_command('on', payload)
|
||||
self.logger.info("Order cid={} ({} {} @ {}) dispatched".format(
|
||||
cId, symbol, amount, price))
|
||||
|
||||
This function closes orders that have been tracked locally by the OrderManager.
|
||||
"""
|
||||
ids = [self.open_orders[x].id for x in self.open_orders]
|
||||
await self.cancel_order_multi(ids)
|
||||
async def update_order(self, orderId, price=None, amount=None, delta=None, price_aux_limit=None,
|
||||
price_trailing=None, hidden=False, close=False, reduce_only=False,
|
||||
post_only=False, time_in_force=None, onConfirm=None, onClose=None):
|
||||
"""
|
||||
Update an existing order
|
||||
|
||||
async def cancel_order_multi(self, orderIds):
|
||||
"""
|
||||
Cancel existing open orders as a batch
|
||||
@param orderId: the id of the order that you want to update
|
||||
@param price: the price you want to buy/sell at (must be positive)
|
||||
@param amount: order size: how much you want to buy/sell,
|
||||
a negative amount indicates a sell order and positive a buy order
|
||||
@param delta: change of amount
|
||||
@param price_trailing: decimal trailing price
|
||||
@param price_aux_limit: decimal auxiliary Limit price (only for STOP LIMIT)
|
||||
@param hidden: if True, order should be hidden from orderbooks
|
||||
@param close: if True, close position if position present
|
||||
@param reduce_only: if True, ensures that the executed order does not flip the opened position
|
||||
@param post_only: if True, ensures the limit order will be added to the order book and not
|
||||
match with a pre-existing order
|
||||
@param time_in_force: datetime for automatic order cancellation ie. 2020-01-01 10:45:23
|
||||
@param onConfirm: function called when the bitfinex websocket receives signal that the order
|
||||
was confirmed
|
||||
@param onClose: function called when the bitfinex websocket receives signal that the order
|
||||
was closed due to being filled or cancelled
|
||||
"""
|
||||
order = self.open_orders[orderId]
|
||||
self._create_callback(order.cId, onConfirm=onConfirm, onClose=onClose)
|
||||
payload = {"id": orderId}
|
||||
if price is not None:
|
||||
payload['price'] = str(price)
|
||||
if amount is not None:
|
||||
payload['amount'] = str(amount)
|
||||
if delta is not None:
|
||||
payload['delta'] = str(delta)
|
||||
if price_aux_limit is not None:
|
||||
payload['price_aux_limit'] = str(price_aux_limit)
|
||||
if price_trailing is not None:
|
||||
payload['price_trailing'] = str(price_trailing)
|
||||
if time_in_force is not None:
|
||||
payload['time_in_force'] = str(time_in_force)
|
||||
flags = self._calculate_flags(
|
||||
hidden, close, reduce_only, post_only, False)
|
||||
payload['flags'] = flags
|
||||
await self.bfxapi._send_auth_command('ou', payload)
|
||||
self.logger.info("Update Order order_id={} dispatched".format(orderId))
|
||||
|
||||
@param orderIds: an array of order ids
|
||||
"""
|
||||
task_batch = []
|
||||
for oid in orderIds:
|
||||
task_batch += [
|
||||
asyncio.ensure_future(self.open_orders[oid].close())
|
||||
]
|
||||
await asyncio.wait(*[ task_batch ])
|
||||
async def cancel_order(self, orderId, onConfirm=None, onClose=None):
|
||||
"""
|
||||
Cancel an existing open order
|
||||
|
||||
def _create_callback(self, order_identifier, onConfirm=None, onClose=None):
|
||||
if order_identifier in self.pending_callbacks:
|
||||
self.pending_callbacks[order_identifier] += [(onClose, onConfirm)]
|
||||
else:
|
||||
self.pending_callbacks[order_identifier] = [(onClose, onConfirm)]
|
||||
@param orderId: the id of the order that you want to update
|
||||
@param onConfirm: function called when the bitfinex websocket receives signal that the
|
||||
order
|
||||
was confirmed
|
||||
@param onClose: function called when the bitfinex websocket receives signal that the order
|
||||
was closed due to being filled or cancelled
|
||||
"""
|
||||
# order = self.open_orders[orderId]
|
||||
self._create_callback(orderId, onConfirm=onConfirm, onClose=onClose)
|
||||
await self.bfxapi._send_auth_command('oc', {'id': orderId})
|
||||
self.logger.info("Order cancel order_id={} dispatched".format(orderId))
|
||||
|
||||
async def _execute_close_callback(self, order_identifier, *args, **kwargs):
|
||||
if order_identifier in self.pending_callbacks:
|
||||
for c in self.pending_callbacks[order_identifier]:
|
||||
if c[0]:
|
||||
await c[0](*args, **kwargs)
|
||||
del self.pending_callbacks[order_identifier]
|
||||
async def cancel_all_orders(self):
|
||||
"""
|
||||
Cancel all existing open orders
|
||||
|
||||
async def _execute_confirm_callback(self, order_identifier, *args, **kwargs):
|
||||
if order_identifier in self.pending_callbacks:
|
||||
for c in self.pending_callbacks[order_identifier]:
|
||||
if c[1]:
|
||||
await c[1](*args, **kwargs)
|
||||
This function closes orders that have been tracked locally by the OrderManager.
|
||||
"""
|
||||
ids = [self.open_orders[x].id for x in self.open_orders]
|
||||
await self.cancel_order_multi(ids)
|
||||
|
||||
def _calculate_flags(self, hidden, close, reduce_only, post_only, oco):
|
||||
flags = 0
|
||||
flags = flags + Order.Flags.HIDDEN if hidden else flags
|
||||
flags = flags + Order.Flags.CLOSE if close else flags
|
||||
flags = flags + Order.Flags.REDUUCE_ONLY if reduce_only else flags
|
||||
flags = flags + Order.Flags.POST_ONLY if post_only else flags
|
||||
flags = flags + Order.Flags.OCO if oco else flags
|
||||
return flags
|
||||
async def cancel_order_multi(self, orderIds):
|
||||
"""
|
||||
Cancel existing open orders as a batch
|
||||
|
||||
@param orderIds: an array of order ids
|
||||
"""
|
||||
task_batch = []
|
||||
for oid in orderIds:
|
||||
task_batch += [
|
||||
asyncio.ensure_future(self.open_orders[oid].close())
|
||||
]
|
||||
await asyncio.wait(*[task_batch])
|
||||
|
||||
def _create_callback(self, order_identifier, onConfirm=None, onClose=None):
|
||||
if order_identifier in self.pending_callbacks:
|
||||
self.pending_callbacks[order_identifier] += [(onClose, onConfirm)]
|
||||
else:
|
||||
self.pending_callbacks[order_identifier] = [(onClose, onConfirm)]
|
||||
|
||||
async def _execute_close_callback(self, order_identifier, *args, **kwargs):
|
||||
if order_identifier in self.pending_callbacks:
|
||||
for c in self.pending_callbacks[order_identifier]:
|
||||
if c[0]:
|
||||
await c[0](*args, **kwargs)
|
||||
del self.pending_callbacks[order_identifier]
|
||||
|
||||
async def _execute_confirm_callback(self, order_identifier, *args, **kwargs):
|
||||
if order_identifier in self.pending_callbacks:
|
||||
for c in self.pending_callbacks[order_identifier]:
|
||||
if c[1]:
|
||||
await c[1](*args, **kwargs)
|
||||
|
||||
def _calculate_flags(self, hidden, close, reduce_only, post_only, oco):
|
||||
flags = 0
|
||||
flags = flags + Order.Flags.HIDDEN if hidden else flags
|
||||
flags = flags + Order.Flags.CLOSE if close else flags
|
||||
flags = flags + Order.Flags.REDUUCE_ONLY if reduce_only else flags
|
||||
flags = flags + Order.Flags.POST_ONLY if post_only else flags
|
||||
flags = flags + Order.Flags.OCO if oco else flags
|
||||
return flags
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
"""
|
||||
Module used to house all of the functions/classes used to handle
|
||||
subscriptions
|
||||
"""
|
||||
|
||||
import json
|
||||
import asyncio
|
||||
import time
|
||||
@@ -5,126 +10,126 @@ import time
|
||||
from ..utils.CustomLogger import CustomLogger
|
||||
from ..models import Subscription
|
||||
|
||||
|
||||
class SubscriptionManager:
|
||||
|
||||
def __init__(self, bfxapi, logLevel='INFO'):
|
||||
self.pending_subscriptions = {}
|
||||
self.subscriptions_chanid = {}
|
||||
self.subscriptions_subid = {}
|
||||
self.unsubscribe_callbacks = {}
|
||||
self.bfxapi = bfxapi
|
||||
self.logger = CustomLogger('BfxSubscriptionManager', logLevel=logLevel)
|
||||
def __init__(self, bfxapi, logLevel='INFO'):
|
||||
self.pending_subscriptions = {}
|
||||
self.subscriptions_chanid = {}
|
||||
self.subscriptions_subid = {}
|
||||
self.unsubscribe_callbacks = {}
|
||||
self.bfxapi = bfxapi
|
||||
self.logger = CustomLogger('BfxSubscriptionManager', logLevel=logLevel)
|
||||
|
||||
async def subscribe(self, channel_name, symbol, timeframe=None, **kwargs):
|
||||
"""
|
||||
Subscribe to a new channel
|
||||
async def subscribe(self, channel_name, symbol, timeframe=None, **kwargs):
|
||||
"""
|
||||
Subscribe to a new channel
|
||||
|
||||
@param channel_name: the name of the channel i.e 'books', 'candles'
|
||||
@param symbol: the trading symbol i.e 'tBTCUSD'
|
||||
@param timeframe: sepecifies the data timeframe between each candle (only required
|
||||
for the candles channel)
|
||||
"""
|
||||
# create a new subscription
|
||||
subscription = Subscription(self.bfxapi.ws, channel_name, symbol, timeframe, **kwargs)
|
||||
self.logger.info("Subscribing to channel {}".format(channel_name))
|
||||
key = "{}_{}".format(channel_name, subscription.key or symbol)
|
||||
self.pending_subscriptions[key] = subscription
|
||||
await subscription.subscribe()
|
||||
@param channel_name: the name of the channel i.e 'books', 'candles'
|
||||
@param symbol: the trading symbol i.e 'tBTCUSD'
|
||||
@param timeframe: sepecifies the data timeframe between each candle (only required
|
||||
for the candles channel)
|
||||
"""
|
||||
# create a new subscription
|
||||
subscription = Subscription(
|
||||
self.bfxapi.ws, channel_name, symbol, timeframe, **kwargs)
|
||||
self.logger.info("Subscribing to channel {}".format(channel_name))
|
||||
key = "{}_{}".format(channel_name, subscription.key or symbol)
|
||||
self.pending_subscriptions[key] = subscription
|
||||
await subscription.subscribe()
|
||||
|
||||
async def confirm_subscription(self, raw_ws_data):
|
||||
# {"event":"subscribed","channel":"trades","chanId":1,"symbol":"tBTCUSD","pair":"BTCUSD"}
|
||||
# {"event":"subscribed","channel":"candles","chanId":351,"key":"trade:1m:tBTCUSD"}
|
||||
# {"event":"subscribed","channel":"book","chanId":4,"symbol":"tBTCUSD","prec":"P0","freq":"F0","len":"25","pair":"BTCUSD"}
|
||||
symbol = raw_ws_data.get("symbol", None)
|
||||
channel = raw_ws_data.get("channel")
|
||||
chanId = raw_ws_data.get("chanId")
|
||||
key = raw_ws_data.get("key", None)
|
||||
get_key = "{}_{}".format(channel, key or symbol)
|
||||
async def confirm_subscription(self, raw_ws_data):
|
||||
symbol = raw_ws_data.get("symbol", None)
|
||||
channel = raw_ws_data.get("channel")
|
||||
chan_id = raw_ws_data.get("chanId")
|
||||
key = raw_ws_data.get("key", None)
|
||||
get_key = "{}_{}".format(channel, key or symbol)
|
||||
|
||||
if chanId in self.subscriptions_chanid:
|
||||
# subscription has already existed in the past
|
||||
p_sub = self.subscriptions_chanid[chanId]
|
||||
else:
|
||||
# has just been created and is pending
|
||||
p_sub = self.pending_subscriptions[get_key]
|
||||
# remove from pending list
|
||||
del self.pending_subscriptions[get_key]
|
||||
p_sub.confirm_subscription(chanId)
|
||||
# add to confirmed list
|
||||
self.subscriptions_chanid[chanId] = p_sub
|
||||
self.subscriptions_subid[p_sub.sub_id] = p_sub
|
||||
self.bfxapi._emit('subscribed', p_sub)
|
||||
if chan_id in self.subscriptions_chanid:
|
||||
# subscription has already existed in the past
|
||||
p_sub = self.subscriptions_chanid[chan_id]
|
||||
else:
|
||||
# has just been created and is pending
|
||||
p_sub = self.pending_subscriptions[get_key]
|
||||
# remove from pending list
|
||||
del self.pending_subscriptions[get_key]
|
||||
p_sub.confirm_subscription(chan_id)
|
||||
# add to confirmed list
|
||||
self.subscriptions_chanid[chan_id] = p_sub
|
||||
self.subscriptions_subid[p_sub.sub_id] = p_sub
|
||||
self.bfxapi._emit('subscribed', p_sub)
|
||||
|
||||
async def confirm_unsubscribe(self, raw_ws_data):
|
||||
chanId = raw_ws_data.get("chanId")
|
||||
sub = self.subscriptions_chanid[chanId]
|
||||
sub.confirm_unsubscribe()
|
||||
self.bfxapi._emit('unsubscribed', sub)
|
||||
# call onComplete callback if exists
|
||||
if sub.sub_id in self.unsubscribe_callbacks:
|
||||
await self.unsubscribe_callbacks[sub.sub_id]()
|
||||
del self.unsubscribe_callbacks[sub.sub_id]
|
||||
async def confirm_unsubscribe(self, raw_ws_data):
|
||||
chan_id = raw_ws_data.get("chanId")
|
||||
sub = self.subscriptions_chanid[chan_id]
|
||||
sub.confirm_unsubscribe()
|
||||
self.bfxapi._emit('unsubscribed', sub)
|
||||
# call onComplete callback if exists
|
||||
if sub.sub_id in self.unsubscribe_callbacks:
|
||||
await self.unsubscribe_callbacks[sub.sub_id]()
|
||||
del self.unsubscribe_callbacks[sub.sub_id]
|
||||
|
||||
def get(self, chanId):
|
||||
return self.subscriptions_chanid[chanId]
|
||||
def get(self, chan_id):
|
||||
return self.subscriptions_chanid[chan_id]
|
||||
|
||||
async def unsubscribe(self, chanId, onComplete=None):
|
||||
"""
|
||||
Unsubscribe from the channel with the given chanId
|
||||
async def unsubscribe(self, chan_id, onComplete=None):
|
||||
"""
|
||||
Unsubscribe from the channel with the given chanId
|
||||
|
||||
@param onComplete: function called when the bitfinex websocket resoponds with
|
||||
a signal that confirms the subscription has been unsubscribed to
|
||||
"""
|
||||
sub = self.subscriptions_chanid[chanId]
|
||||
if onComplete:
|
||||
self.unsubscribe_callbacks[sub.sub_id] = onComplete
|
||||
if sub.is_subscribed():
|
||||
await self.subscriptions_chanid[chanId].unsubscribe()
|
||||
@param onComplete: function called when the bitfinex websocket resoponds with
|
||||
a signal that confirms the subscription has been unsubscribed to
|
||||
"""
|
||||
sub = self.subscriptions_chanid[chan_id]
|
||||
if onComplete:
|
||||
self.unsubscribe_callbacks[sub.sub_id] = onComplete
|
||||
if sub.is_subscribed():
|
||||
await self.subscriptions_chanid[chan_id].unsubscribe()
|
||||
|
||||
async def resubscribe(self, chanId):
|
||||
"""
|
||||
Unsubscribes and then subscribes to the channel with the given Id
|
||||
async def resubscribe(self, chan_id):
|
||||
"""
|
||||
Unsubscribes and then subscribes to the channel with the given Id
|
||||
|
||||
This function is mostly used to force the channel to produce a fresh snapshot.
|
||||
"""
|
||||
sub = self.subscriptions_chanid[chanId]
|
||||
async def re_sub():
|
||||
await sub.subscribe()
|
||||
if sub.is_subscribed():
|
||||
# unsubscribe first and call callback to subscribe
|
||||
await self.unsubscribe(chanId, re_sub)
|
||||
else:
|
||||
# already unsibscribed, so just subscribe
|
||||
await sub.subscribe()
|
||||
This function is mostly used to force the channel to produce a fresh snapshot.
|
||||
"""
|
||||
sub = self.subscriptions_chanid[chan_d]
|
||||
|
||||
def is_subscribed(self, chanId):
|
||||
"""
|
||||
Returns True if the channel with the given chanId is currenly subscribed to
|
||||
"""
|
||||
if chanId not in self.subscriptions_chanid:
|
||||
return False
|
||||
return self.subscriptions_chanid[chanId].is_subscribed()
|
||||
async def re_sub():
|
||||
await sub.subscribe()
|
||||
if sub.is_subscribed():
|
||||
# unsubscribe first and call callback to subscribe
|
||||
await self.unsubscribe(chan_id, re_sub)
|
||||
else:
|
||||
# already unsibscribed, so just subscribe
|
||||
await sub.subscribe()
|
||||
|
||||
async def unsubscribe_all(self):
|
||||
"""
|
||||
Unsubscribe from all channels.
|
||||
"""
|
||||
task_batch = []
|
||||
for chanId in self.subscriptions_chanid:
|
||||
sub = self.get(chanId)
|
||||
if sub.is_subscribed():
|
||||
task_batch += [
|
||||
asyncio.ensure_future(self.unsubscribe(chanId))
|
||||
]
|
||||
await asyncio.wait(*[ task_batch ])
|
||||
def is_subscribed(self, chan_id):
|
||||
"""
|
||||
Returns True if the channel with the given chanId is currenly subscribed to
|
||||
"""
|
||||
if chan_id not in self.subscriptions_chanid:
|
||||
return False
|
||||
return self.subscriptions_chanid[chan_id].is_subscribed()
|
||||
|
||||
async def resubscribe_all(self):
|
||||
"""
|
||||
Unsubscribe and then subscribe to all channels
|
||||
"""
|
||||
task_batch = []
|
||||
for chanId in self.subscriptions_chanid:
|
||||
task_batch += [
|
||||
asyncio.ensure_future(self.resubscribe(chanId))
|
||||
]
|
||||
await asyncio.wait(*[ task_batch ])
|
||||
async def unsubscribe_all(self):
|
||||
"""
|
||||
Unsubscribe from all channels.
|
||||
"""
|
||||
task_batch = []
|
||||
for chan_id in self.subscriptions_chanid:
|
||||
sub = self.get(chan_id)
|
||||
if sub.is_subscribed():
|
||||
task_batch += [
|
||||
asyncio.ensure_future(self.unsubscribe(chan_id))
|
||||
]
|
||||
await asyncio.wait(*[task_batch])
|
||||
|
||||
async def resubscribe_all(self):
|
||||
"""
|
||||
Unsubscribe and then subscribe to all channels
|
||||
"""
|
||||
task_batch = []
|
||||
for chan_id in self.subscriptions_chanid:
|
||||
task_batch += [
|
||||
asyncio.ensure_future(self.resubscribe(chan_id))
|
||||
]
|
||||
await asyncio.wait(*[task_batch])
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
"""
|
||||
Module used to handle wallet updates and data types
|
||||
"""
|
||||
|
||||
from ..models import Wallet
|
||||
|
||||
|
||||
class WalletManager:
|
||||
"""
|
||||
This class is used to interact with all of the different wallets
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.wallets = {}
|
||||
def __init__(self):
|
||||
self.wallets = {}
|
||||
|
||||
def _update_from_snapshot(self, raw_ws_data):
|
||||
# [0, 'ws', [['exchange', 'BTC', 41.25809589, 0, None], ['exchange', 'USD', 62761.86070104, 0, None]]]
|
||||
wData = raw_ws_data[2]
|
||||
self.wallets = {}
|
||||
for wallet in wData:
|
||||
new_wallet = Wallet(wallet[0], wallet[1], wallet[2], wallet[3])
|
||||
self.wallets[new_wallet.key] = new_wallet
|
||||
return self.get_wallets()
|
||||
def _update_from_snapshot(self, raw_ws_data):
|
||||
wData = raw_ws_data[2]
|
||||
self.wallets = {}
|
||||
for wallet in wData:
|
||||
new_wallet = Wallet(wallet[0], wallet[1], wallet[2], wallet[3])
|
||||
self.wallets[new_wallet.key] = new_wallet
|
||||
return self.get_wallets()
|
||||
|
||||
def _update_from_event(self, raw_ws_data):
|
||||
# [0,"wu",["exchange","USD",62761.86070104,0,61618.66070104]]
|
||||
wallet = raw_ws_data[2]
|
||||
new_wallet = Wallet(wallet[0], wallet[1], wallet[2], wallet[3])
|
||||
self.wallets[new_wallet.key] = new_wallet
|
||||
return new_wallet
|
||||
|
||||
def get_wallets(self):
|
||||
return list(self.wallets.values())
|
||||
def _update_from_event(self, raw_ws_data):
|
||||
wallet = raw_ws_data[2]
|
||||
new_wallet = Wallet(wallet[0], wallet[1], wallet[2], wallet[3])
|
||||
self.wallets[new_wallet.key] = new_wallet
|
||||
return new_wallet
|
||||
|
||||
def get_wallets(self):
|
||||
return list(self.wallets.values())
|
||||
|
||||
Reference in New Issue
Block a user