mirror of
https://github.com/aljazceru/bitfinex-api-py.git
synced 2025-12-19 14:54:21 +01:00
Fix camel-casing filenames and remove filename conflicts
This commit is contained in:
@@ -121,6 +121,7 @@ The websocket exposes a collection of events that are triggered when certain dat
|
|||||||
- `funding_credit_snapshot` (array): opening funding credit balances
|
- `funding_credit_snapshot` (array): opening funding credit balances
|
||||||
- `balance_update` (array): when the state of a balance is changed
|
- `balance_update` (array): when the state of a balance is changed
|
||||||
- `new_trade` (array): a new trade on the market has been executed
|
- `new_trade` (array): a new trade on the market has been executed
|
||||||
|
- `trade_update` (array): a trade on the market has been updated
|
||||||
- `new_candle` (array): a new candle has been produced
|
- `new_candle` (array): a new candle has been produced
|
||||||
- `margin_info_updates` (array): new margin information has been broadcasted
|
- `margin_info_updates` (array): new margin information has been broadcasted
|
||||||
- `funding_info_updates` (array): new funding information has been broadcasted
|
- `funding_info_updates` (array): new funding information has been broadcasted
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
"""
|
|
||||||
This module exposes the core bitfinex clients which includes both
|
|
||||||
a websocket client and a rest interface client
|
|
||||||
"""
|
|
||||||
|
|
||||||
# pylint: disable-all
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
from .websockets.BfxWebsocket import BfxWebsocket
|
|
||||||
from .rest.BfxRest import BfxRest
|
|
||||||
|
|
||||||
REST_HOST = 'https://api-pub.bitfinex.com/v2'
|
|
||||||
WS_HOST = 'wss://api-pub.bitfinex.com/ws/2'
|
|
||||||
|
|
||||||
class Client:
|
|
||||||
"""
|
|
||||||
The bfx client exposes rest and websocket objects
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, API_KEY=None, API_SECRET=None, rest_host=REST_HOST,
|
|
||||||
ws_host=WS_HOST, loop=None, logLevel='INFO', dead_man_switch=False,
|
|
||||||
ws_capacity=25, *args, **kwargs):
|
|
||||||
self.loop = loop or asyncio.get_event_loop()
|
|
||||||
self.ws = BfxWebsocket(API_KEY=API_KEY, API_SECRET=API_SECRET, host=ws_host,
|
|
||||||
loop=self.loop, logLevel=logLevel, dead_man_switch=dead_man_switch,
|
|
||||||
ws_capacity=ws_capacity, *args, **kwargs)
|
|
||||||
self.rest = BfxRest(API_KEY=API_KEY, API_SECRET=API_SECRET, host=rest_host,
|
|
||||||
loop=self.loop, logLevel=logLevel, *args, **kwargs)
|
|
||||||
@@ -6,8 +6,8 @@ from .version import __version__
|
|||||||
from .client import Client
|
from .client import Client
|
||||||
from .models import (Order, Trade, OrderBook, Subscription, Wallet,
|
from .models import (Order, Trade, OrderBook, Subscription, Wallet,
|
||||||
Position, FundingLoan, FundingOffer, FundingCredit)
|
Position, FundingLoan, FundingOffer, FundingCredit)
|
||||||
from .websockets.GenericWebsocket import GenericWebsocket, Socket
|
from .websockets.generic_websocket import GenericWebsocket, Socket
|
||||||
from .websockets.BfxWebsocket import BfxWebsocket
|
from .websockets.bfx_websocket import BfxWebsocket
|
||||||
from .utils.Decimal import Decimal
|
from .utils.decimal import Decimal
|
||||||
|
|
||||||
NAME = 'bfxapi'
|
NAME = 'bfxapi'
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ a websocket client and a rest interface client
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from .websockets.BfxWebsocket import BfxWebsocket
|
from .websockets.bfx_websocket import BfxWebsocket
|
||||||
from .rest.BfxRest import BfxRest
|
from .rest.bfx_rest import BfxRest
|
||||||
|
|
||||||
REST_HOST = 'https://api-pub.bitfinex.com/v2'
|
REST_HOST = 'https://api-pub.bitfinex.com/v2'
|
||||||
WS_HOST = 'wss://api-pub.bitfinex.com/ws/2'
|
WS_HOST = 'wss://api-pub.bitfinex.com/ws/2'
|
||||||
|
|||||||
@@ -1,222 +0,0 @@
|
|||||||
"""
|
|
||||||
Module used to describe all of the different data types
|
|
||||||
"""
|
|
||||||
|
|
||||||
import time
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
class OrderType:
|
|
||||||
"""
|
|
||||||
Enum used to describe all of the different order types available for use
|
|
||||||
"""
|
|
||||||
MARKET = 'MARKET'
|
|
||||||
LIMIT = 'LIMIT'
|
|
||||||
STOP = 'STOP'
|
|
||||||
STOP_LIMIT = 'STOP LIMIT'
|
|
||||||
TRAILING_STOP = 'TRAILING STOP'
|
|
||||||
FILL_OR_KILL = 'FOK'
|
|
||||||
EXCHANGE_MARKET = 'EXCHANGE MARKET'
|
|
||||||
EXCHANGE_LIMIT = 'EXCHANGE LIMIT'
|
|
||||||
EXCHANGE_STOP = 'EXCHANGE STOP'
|
|
||||||
EXCHANGE_STOP_LIMIT = 'EXCHANGE STOP LIMIT'
|
|
||||||
EXCHANGE_TRAILING_STOP = 'EXCHANGE TRAILING STOP'
|
|
||||||
EXCHANGE_FILL_OR_KILL = 'EXCHANGE FOK'
|
|
||||||
|
|
||||||
|
|
||||||
LIMIT_ORDERS = [OrderType.LIMIT, OrderType.STOP_LIMIT, OrderType.EXCHANGE_LIMIT,
|
|
||||||
OrderType.EXCHANGE_STOP_LIMIT, OrderType.FILL_OR_KILL,
|
|
||||||
OrderType.EXCHANGE_FILL_OR_KILL]
|
|
||||||
|
|
||||||
|
|
||||||
class OrderSide:
|
|
||||||
"""
|
|
||||||
Enum used to describe the different directions of an order
|
|
||||||
"""
|
|
||||||
BUY = 'buy'
|
|
||||||
SELL = 'sell'
|
|
||||||
|
|
||||||
|
|
||||||
class OrderClosedModel:
|
|
||||||
"""
|
|
||||||
Enum used ad an index match to locate the different values in a
|
|
||||||
raw order array
|
|
||||||
"""
|
|
||||||
ID = 0
|
|
||||||
GID = 1
|
|
||||||
CID = 2
|
|
||||||
SYMBOL = 3
|
|
||||||
MTS_CREATE = 4
|
|
||||||
MTS_UPDATE = 5
|
|
||||||
AMOUNT = 6
|
|
||||||
AMOUNT_ORIG = 7
|
|
||||||
TYPE = 8
|
|
||||||
TYPE_PREV = 9
|
|
||||||
FLAGS = 12
|
|
||||||
STATUS = 13
|
|
||||||
PRICE = 16
|
|
||||||
PRICE_AVG = 17
|
|
||||||
PRICE_TRAILING = 18
|
|
||||||
PRICE_AUX_LIMIT = 19
|
|
||||||
NOTIFY = 23
|
|
||||||
PLACE_ID = 25
|
|
||||||
|
|
||||||
|
|
||||||
class OrderFlags:
|
|
||||||
"""
|
|
||||||
Enum used to explain the different values that can be passed in
|
|
||||||
as flags
|
|
||||||
"""
|
|
||||||
HIDDEN = 64
|
|
||||||
CLOSE = 12
|
|
||||||
REDUCE_ONLY = 1024
|
|
||||||
POST_ONLY = 4096
|
|
||||||
OCO = 16384
|
|
||||||
|
|
||||||
|
|
||||||
def now_in_mills():
|
|
||||||
"""
|
|
||||||
Gets the current time in milliseconds
|
|
||||||
"""
|
|
||||||
return int(round(time.time() * 1000))
|
|
||||||
|
|
||||||
|
|
||||||
class Order:
|
|
||||||
"""
|
|
||||||
ID int64 Order ID
|
|
||||||
GID int Group ID
|
|
||||||
CID int Client Order ID
|
|
||||||
SYMBOL string Pair (tBTCUSD, ...)
|
|
||||||
MTS_CREATE int Millisecond timestamp of creation
|
|
||||||
MTS_UPDATE int Millisecond timestamp of update
|
|
||||||
AMOUNT float Positive means buy, negative means sell.
|
|
||||||
AMOUNT_ORIG float Original amount
|
|
||||||
TYPE string The type of the order: LIMIT, MARKET, STOP, TRAILING STOP,
|
|
||||||
EXCHANGE MARKET, EXCHANGE LIMIT, EXCHANGE STOP, EXCHANGE TRAILING STOP, FOK, EXCHANGE FOK.
|
|
||||||
TYPE_PREV string Previous order type
|
|
||||||
FLAGS int Upcoming Params Object (stay tuned)
|
|
||||||
ORDER_STATUS string Order Status: ACTIVE, EXECUTED, PARTIALLY FILLED, CANCELED
|
|
||||||
PRICE float Price
|
|
||||||
PRICE_AVG float Average price
|
|
||||||
PRICE_TRAILING float The trailing price
|
|
||||||
PRICE_AUX_LIMIT float Auxiliary Limit price (for STOP LIMIT)
|
|
||||||
HIDDEN int 1 if Hidden, 0 if not hidden
|
|
||||||
PLACED_ID int If another order caused this order to be placed (OCO) this will be that other
|
|
||||||
order's ID
|
|
||||||
"""
|
|
||||||
|
|
||||||
Type = OrderType()
|
|
||||||
Side = OrderSide()
|
|
||||||
Flags = OrderFlags()
|
|
||||||
|
|
||||||
def __init__(self, oid, gid, cid, symbol, mts_create, mts_update, amount,
|
|
||||||
amount_orig, o_type, typePrev, flags, status, price, price_avg,
|
|
||||||
price_trailing, price_aux_limit, notfiy, place_id):
|
|
||||||
self.id = oid # pylint: disable=invalid-name
|
|
||||||
self.gid = gid
|
|
||||||
self.cid = cid
|
|
||||||
self.symbol = symbol
|
|
||||||
self.mts_create = mts_create
|
|
||||||
self.mts_update = mts_update
|
|
||||||
self.amount = amount
|
|
||||||
self.amount_orig = amount_orig
|
|
||||||
if self.amount_orig > 0:
|
|
||||||
self.amount_filled = amount_orig - amount
|
|
||||||
else:
|
|
||||||
self.amount_filled = -(abs(amount_orig) - abs(amount))
|
|
||||||
self.type = o_type
|
|
||||||
self.type_prev = typePrev
|
|
||||||
self.flags = flags
|
|
||||||
self.status = status
|
|
||||||
self.price = price
|
|
||||||
self.price_avg = price_avg
|
|
||||||
self.price_trailing = price_trailing
|
|
||||||
self.price_aux_limit = price_aux_limit
|
|
||||||
self.notfiy = notfiy
|
|
||||||
self.place_id = place_id
|
|
||||||
self.tag = ""
|
|
||||||
self.fee = 0
|
|
||||||
self.is_pending_bool = True
|
|
||||||
self.is_confirmed_bool = False
|
|
||||||
self.is_open_bool = False
|
|
||||||
|
|
||||||
self.date = datetime.datetime.fromtimestamp(mts_create/1000.0)
|
|
||||||
# if cancelled then priceAvg wont exist
|
|
||||||
if price_avg:
|
|
||||||
# check if order is taker or maker
|
|
||||||
if self.type in LIMIT_ORDERS:
|
|
||||||
self.fee = (price_avg * abs(self.amount_filled)) * 0.001
|
|
||||||
else:
|
|
||||||
self.fee = (price_avg * abs(self.amount_filled)) * 0.002
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_raw_order(raw_order):
|
|
||||||
"""
|
|
||||||
Parse a raw order object into an Order oject
|
|
||||||
|
|
||||||
@return Order
|
|
||||||
"""
|
|
||||||
oid = raw_order[OrderClosedModel.ID]
|
|
||||||
gid = raw_order[OrderClosedModel.GID]
|
|
||||||
cid = raw_order[OrderClosedModel.CID]
|
|
||||||
symbol = raw_order[OrderClosedModel.SYMBOL]
|
|
||||||
mts_create = raw_order[OrderClosedModel.MTS_CREATE]
|
|
||||||
mts_update = raw_order[OrderClosedModel.MTS_UPDATE]
|
|
||||||
amount = raw_order[OrderClosedModel.AMOUNT]
|
|
||||||
amount_orig = raw_order[OrderClosedModel.AMOUNT_ORIG]
|
|
||||||
o_type = raw_order[OrderClosedModel.TYPE]
|
|
||||||
type_prev = raw_order[OrderClosedModel.TYPE_PREV]
|
|
||||||
flags = raw_order[OrderClosedModel.FLAGS]
|
|
||||||
status = raw_order[OrderClosedModel.STATUS]
|
|
||||||
price = raw_order[OrderClosedModel.PRICE]
|
|
||||||
price_avg = raw_order[OrderClosedModel.PRICE_AVG]
|
|
||||||
price_trailing = raw_order[OrderClosedModel.PRICE_TRAILING]
|
|
||||||
price_aux_limit = raw_order[OrderClosedModel.PRICE_AUX_LIMIT]
|
|
||||||
notfiy = raw_order[OrderClosedModel.NOTIFY]
|
|
||||||
place_id = raw_order[OrderClosedModel.PLACE_ID]
|
|
||||||
|
|
||||||
return Order(oid, gid, cid, symbol, mts_create, mts_update, amount,
|
|
||||||
amount_orig, o_type, type_prev, flags, status, price, price_avg,
|
|
||||||
price_trailing, price_aux_limit, notfiy, place_id)
|
|
||||||
|
|
||||||
def set_confirmed(self):
|
|
||||||
"""
|
|
||||||
Set the state of the order to be confirmed
|
|
||||||
"""
|
|
||||||
self.is_pending_bool = False
|
|
||||||
self.is_confirmed_bool = True
|
|
||||||
|
|
||||||
def set_open_state(self, is_open):
|
|
||||||
"""
|
|
||||||
Set the is_open state of the order
|
|
||||||
"""
|
|
||||||
self.is_open_bool = is_open
|
|
||||||
|
|
||||||
def is_open(self):
|
|
||||||
"""
|
|
||||||
Check if the order is still open
|
|
||||||
|
|
||||||
@return bool: Ture if order open else False
|
|
||||||
"""
|
|
||||||
return self.is_open_bool
|
|
||||||
|
|
||||||
def is_pending(self):
|
|
||||||
"""
|
|
||||||
Check if the state of the order is still pending
|
|
||||||
|
|
||||||
@return bool: True if is pending else False
|
|
||||||
"""
|
|
||||||
return self.is_pending_bool
|
|
||||||
|
|
||||||
def is_confirmed(self):
|
|
||||||
"""
|
|
||||||
Check if the order has been confirmed by the bitfinex api
|
|
||||||
|
|
||||||
@return bool: True if has been confirmed else False
|
|
||||||
"""
|
|
||||||
return self.is_confirmed_bool
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
''' Allow us to print the Order object in a pretty format '''
|
|
||||||
text = "Order <'{}' amount_orig={} amount_filled={} mts_create={} status='{}' id={}>"
|
|
||||||
return text.format(self.symbol, self.amount_orig, self.amount_filled,
|
|
||||||
self.mts_create, self.status, self.id)
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
"""
|
|
||||||
Module used to describe all of the different data types
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class Position:
|
|
||||||
"""
|
|
||||||
SYMBOL string Pair (tBTCUSD, ...).
|
|
||||||
STATUS string Status (ACTIVE, CLOSED).
|
|
||||||
AMOUNT float Size of the position. Positive values means a long position,
|
|
||||||
negative values means a short position.
|
|
||||||
BASE_PRICE float The price at which you entered your position.
|
|
||||||
MARGIN_FUNDING float The amount of funding being used for this position.
|
|
||||||
MARGIN_FUNDING_TYPE int 0 for daily, 1 for term.
|
|
||||||
PL float Profit & Loss
|
|
||||||
PL_PERC float Profit & Loss Percentage
|
|
||||||
PRICE_LIQ float Liquidation price
|
|
||||||
LEVERAGE float Beta value
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, symbol, status, amount, b_price, m_funding, m_funding_type,
|
|
||||||
profit_loss, profit_loss_perc, l_price, lev):
|
|
||||||
self.symbol = symbol
|
|
||||||
self.status = status
|
|
||||||
self.amount = amount
|
|
||||||
self.base_price = b_price
|
|
||||||
self.margin_funding = m_funding
|
|
||||||
self.margin_funding_type = m_funding_type
|
|
||||||
self.profit_loss = profit_loss
|
|
||||||
self.profit_loss_percentage = profit_loss_perc
|
|
||||||
self.liquidation_price = l_price
|
|
||||||
self.leverage = lev
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_raw_rest_position(raw_position):
|
|
||||||
"""
|
|
||||||
Generate a Position object from a raw position array
|
|
||||||
|
|
||||||
@return Position
|
|
||||||
"""
|
|
||||||
return Position(*raw_position)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
''' Allow us to print the Trade object in a pretty format '''
|
|
||||||
text = "Position '{}' {} x {} <status='{}' pl={}>"
|
|
||||||
return text.format(self.symbol, self.base_price, self.amount,
|
|
||||||
self.status, self.profit_loss)
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
"""
|
|
||||||
Module used to describe all of the different data types
|
|
||||||
"""
|
|
||||||
|
|
||||||
import time
|
|
||||||
import json
|
|
||||||
from random import randint
|
|
||||||
|
|
||||||
def generate_sub_id():
|
|
||||||
"""
|
|
||||||
Generates a unique id in the form of 12345566-12334556
|
|
||||||
"""
|
|
||||||
prefix = str(int(round(time.time() * 1000)))
|
|
||||||
suffix = str(randint(0, 9999999))
|
|
||||||
return "{}-{}".format(prefix, suffix)
|
|
||||||
|
|
||||||
class Subscription:
|
|
||||||
"""
|
|
||||||
Object used to represent an individual subscription to the websocket.
|
|
||||||
This class also exposes certain functions which helps to manage the subscription
|
|
||||||
such as unsibscribe and subscribe.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, socket, channel_name, symbol, timeframe=None, **kwargs):
|
|
||||||
self.socket = socket
|
|
||||||
self.channel_name = channel_name
|
|
||||||
self.symbol = symbol
|
|
||||||
self.timeframe = timeframe
|
|
||||||
self.is_subscribed_bool = False
|
|
||||||
self.key = None
|
|
||||||
self.chan_id = None
|
|
||||||
if timeframe:
|
|
||||||
self.key = 'trade:{}:{}'.format(self.timeframe, self.symbol)
|
|
||||||
self.sub_id = generate_sub_id()
|
|
||||||
self.send_payload = self._generate_payload(**kwargs)
|
|
||||||
|
|
||||||
def get_key(self):
|
|
||||||
"""
|
|
||||||
Generates a unique key string for the subscription
|
|
||||||
"""
|
|
||||||
return "{}_{}".format(self.channel_name, self.key or self.symbol)
|
|
||||||
|
|
||||||
def confirm_subscription(self, chan_id):
|
|
||||||
"""
|
|
||||||
Update the subscription to confirmed state
|
|
||||||
"""
|
|
||||||
self.is_subscribed_bool = True
|
|
||||||
self.chan_id = chan_id
|
|
||||||
|
|
||||||
async def unsubscribe(self):
|
|
||||||
"""
|
|
||||||
Send an unsubscription request to the bitfinex socket
|
|
||||||
"""
|
|
||||||
if not self.is_subscribed():
|
|
||||||
raise Exception("Subscription is not subscribed to websocket")
|
|
||||||
payload = {'event': 'unsubscribe', 'chanId': self.chan_id}
|
|
||||||
await self.socket.ws.send(json.dumps(payload))
|
|
||||||
|
|
||||||
async def subscribe(self):
|
|
||||||
"""
|
|
||||||
Send a subscription request to the bitfinex socket
|
|
||||||
"""
|
|
||||||
await self.socket.ws.send(json.dumps(self._get_send_payload()))
|
|
||||||
|
|
||||||
def confirm_unsubscribe(self):
|
|
||||||
"""
|
|
||||||
Update the subscription to unsubscribed state
|
|
||||||
"""
|
|
||||||
self.is_subscribed_bool = False
|
|
||||||
|
|
||||||
def is_subscribed(self):
|
|
||||||
"""
|
|
||||||
Check if the subscription is currently subscribed
|
|
||||||
|
|
||||||
@return bool: True if subscribed else False
|
|
||||||
"""
|
|
||||||
return self.is_subscribed_bool
|
|
||||||
|
|
||||||
def _generate_payload(self, **kwargs):
|
|
||||||
payload = {'event': 'subscribe',
|
|
||||||
'channel': self.channel_name, 'symbol': self.symbol}
|
|
||||||
if self.timeframe:
|
|
||||||
payload['key'] = self.key
|
|
||||||
payload.update(**kwargs)
|
|
||||||
return payload
|
|
||||||
|
|
||||||
def _get_send_payload(self):
|
|
||||||
return self.send_payload
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
"""
|
|
||||||
Module used to describe all of the different data types
|
|
||||||
"""
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
|
|
||||||
class Trade:
|
|
||||||
"""
|
|
||||||
ID integer Trade database id
|
|
||||||
PAIR string Pair (BTCUSD, ...)
|
|
||||||
MTS_CREATE integer Execution timestamp
|
|
||||||
ORDER_ID integer Order id
|
|
||||||
EXEC_AMOUNT float Positive means buy, negative means sell
|
|
||||||
EXEC_PRICE float Execution price
|
|
||||||
ORDER_TYPE string Order type
|
|
||||||
ORDER_PRICE float Order price
|
|
||||||
MAKER int 1 if true, 0 if false
|
|
||||||
FEE float Fee
|
|
||||||
FEE_CURRENCY string Fee currency
|
|
||||||
"""
|
|
||||||
|
|
||||||
SHORT = 'SHORT'
|
|
||||||
LONG = 'LONG'
|
|
||||||
|
|
||||||
def __init__(self, tid, pair, mts_create, order_id, amount, price, order_type,
|
|
||||||
order_price, maker, fee, fee_currency):
|
|
||||||
# pylint: disable=invalid-name
|
|
||||||
self.id = tid
|
|
||||||
self.pair = pair
|
|
||||||
self.mts_create = mts_create
|
|
||||||
self.date = datetime.datetime.fromtimestamp(mts_create/1000.0)
|
|
||||||
self.order_id = order_id
|
|
||||||
self.amount = amount
|
|
||||||
self.direction = Trade.SHORT if amount < 0 else Trade.LONG
|
|
||||||
self.price = price
|
|
||||||
self.order_type = order_type
|
|
||||||
self.order_price = order_price
|
|
||||||
self.maker = maker
|
|
||||||
self.fee = fee
|
|
||||||
self.fee_currency = fee_currency
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_raw_rest_trade(raw_trade):
|
|
||||||
"""
|
|
||||||
Generate a Trade object from a raw trade array
|
|
||||||
"""
|
|
||||||
# [24224048, 'tBTCUSD', 1542800024000, 1151353484, 0.09399997, 19963, None, None,
|
|
||||||
# -1, -0.000188, 'BTC']
|
|
||||||
return Trade(*raw_trade)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "Trade '{}' x {} @ {} <direction='{}' fee={}>".format(
|
|
||||||
self.pair, self.amount, self.price, self.direction, self.fee)
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
"""
|
|
||||||
Module used to describe all of the different data types
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class Wallet:
|
|
||||||
"""
|
|
||||||
Stores data relevant to a users wallet such as balance and
|
|
||||||
currency
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, wType, currency, balance, unsettled_interest):
|
|
||||||
self.type = wType
|
|
||||||
self.currency = currency
|
|
||||||
self.balance = balance
|
|
||||||
self.unsettled_interest = unsettled_interest
|
|
||||||
self.key = "{}_{}".format(wType, currency)
|
|
||||||
|
|
||||||
def set_balance(self, data):
|
|
||||||
"""
|
|
||||||
Set the balance of the wallet
|
|
||||||
"""
|
|
||||||
self.balance = data
|
|
||||||
|
|
||||||
def set_unsettled_interest(self, data):
|
|
||||||
"""
|
|
||||||
Set the unsettled interest of the wallet
|
|
||||||
"""
|
|
||||||
self.unsettled_interest = data
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "Wallet <'{}_{}' balance='{}' unsettled='{}'>".format(
|
|
||||||
self.type, self.currency, self.balance, self.unsettled_interest)
|
|
||||||
@@ -7,7 +7,7 @@ import aiohttp
|
|||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from ..utils.CustomLogger import CustomLogger
|
from ..utils.custom_logger import CustomLogger
|
||||||
from ..utils.auth import generate_auth_headers
|
from ..utils.auth import generate_auth_headers
|
||||||
from ..models import Wallet, Order, Position, Trade, FundingLoan, FundingOffer
|
from ..models import Wallet, Order, Position, Trade, FundingLoan, FundingOffer
|
||||||
from ..models import FundingCredit
|
from ..models import FundingCredit
|
||||||
@@ -27,7 +27,7 @@ class BfxRest:
|
|||||||
self.API_KEY = API_KEY
|
self.API_KEY = API_KEY
|
||||||
self.API_SECRET = API_SECRET
|
self.API_SECRET = API_SECRET
|
||||||
self.host = host
|
self.host = host
|
||||||
# this value can also be set to bfxapi.Decimal for much higher precision
|
# this value can also be set to bfxapi.decimal for much higher precision
|
||||||
self.parse_float = parse_float
|
self.parse_float = parse_float
|
||||||
self.logger = CustomLogger('BfxRest', logLevel=logLevel)
|
self.logger = CustomLogger('BfxRest', logLevel=logLevel)
|
||||||
|
|
||||||
@@ -1,96 +1,96 @@
|
|||||||
"""
|
"""
|
||||||
Module used to describe all of the different data types
|
Module used to describe all of the different data types
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
RESET_SEQ = "\033[0m"
|
RESET_SEQ = "\033[0m"
|
||||||
COLOR_SEQ = "\033[1;%dm"
|
COLOR_SEQ = "\033[1;%dm"
|
||||||
BOLD_SEQ = "\033[1m"
|
BOLD_SEQ = "\033[1m"
|
||||||
UNDERLINE_SEQ = "\033[04m"
|
UNDERLINE_SEQ = "\033[04m"
|
||||||
|
|
||||||
YELLOW = '\033[93m'
|
YELLOW = '\033[93m'
|
||||||
WHITE = '\33[37m'
|
WHITE = '\33[37m'
|
||||||
BLUE = '\033[34m'
|
BLUE = '\033[34m'
|
||||||
LIGHT_BLUE = '\033[94m'
|
LIGHT_BLUE = '\033[94m'
|
||||||
RED = '\033[91m'
|
RED = '\033[91m'
|
||||||
GREY = '\33[90m'
|
GREY = '\33[90m'
|
||||||
|
|
||||||
KEYWORD_COLORS = {
|
KEYWORD_COLORS = {
|
||||||
'WARNING': YELLOW,
|
'WARNING': YELLOW,
|
||||||
'INFO': LIGHT_BLUE,
|
'INFO': LIGHT_BLUE,
|
||||||
'DEBUG': WHITE,
|
'DEBUG': WHITE,
|
||||||
'CRITICAL': YELLOW,
|
'CRITICAL': YELLOW,
|
||||||
'ERROR': RED,
|
'ERROR': RED,
|
||||||
'TRADE': '\33[102m\33[30m'
|
'TRADE': '\33[102m\33[30m'
|
||||||
}
|
}
|
||||||
|
|
||||||
def formatter_message(message, use_color = True):
|
def formatter_message(message, use_color = True):
|
||||||
"""
|
"""
|
||||||
Syntax highlight certain keywords
|
Syntax highlight certain keywords
|
||||||
"""
|
"""
|
||||||
if use_color:
|
if use_color:
|
||||||
message = message.replace("$RESET", RESET_SEQ).replace("$BOLD", BOLD_SEQ)
|
message = message.replace("$RESET", RESET_SEQ).replace("$BOLD", BOLD_SEQ)
|
||||||
else:
|
else:
|
||||||
message = message.replace("$RESET", "").replace("$BOLD", "")
|
message = message.replace("$RESET", "").replace("$BOLD", "")
|
||||||
return message
|
return message
|
||||||
|
|
||||||
def format_word(message, word, color_seq, bold=False, underline=False):
|
def format_word(message, word, color_seq, bold=False, underline=False):
|
||||||
"""
|
"""
|
||||||
Surround the fiven word with a sequence
|
Surround the fiven word with a sequence
|
||||||
"""
|
"""
|
||||||
replacer = color_seq + word + RESET_SEQ
|
replacer = color_seq + word + RESET_SEQ
|
||||||
if underline:
|
if underline:
|
||||||
replacer = UNDERLINE_SEQ + replacer
|
replacer = UNDERLINE_SEQ + replacer
|
||||||
if bold:
|
if bold:
|
||||||
replacer = BOLD_SEQ + replacer
|
replacer = BOLD_SEQ + replacer
|
||||||
return message.replace(word, replacer)
|
return message.replace(word, replacer)
|
||||||
|
|
||||||
class Formatter(logging.Formatter):
|
class Formatter(logging.Formatter):
|
||||||
'''
|
'''
|
||||||
This Formatted simply colors in the levelname i.e 'INFO', 'DEBUG'
|
This Formatted simply colors in the levelname i.e 'INFO', 'DEBUG'
|
||||||
'''
|
'''
|
||||||
def __init__(self, msg, use_color = True):
|
def __init__(self, msg, use_color = True):
|
||||||
logging.Formatter.__init__(self, msg)
|
logging.Formatter.__init__(self, msg)
|
||||||
self.use_color = use_color
|
self.use_color = use_color
|
||||||
|
|
||||||
def format(self, record):
|
def format(self, record):
|
||||||
"""
|
"""
|
||||||
Format and highlight certain keywords
|
Format and highlight certain keywords
|
||||||
"""
|
"""
|
||||||
levelname = record.levelname
|
levelname = record.levelname
|
||||||
if self.use_color and levelname in KEYWORD_COLORS:
|
if self.use_color and levelname in KEYWORD_COLORS:
|
||||||
levelname_color = KEYWORD_COLORS[levelname] + levelname + RESET_SEQ
|
levelname_color = KEYWORD_COLORS[levelname] + levelname + RESET_SEQ
|
||||||
record.levelname = levelname_color
|
record.levelname = levelname_color
|
||||||
record.name = GREY + record.name + RESET_SEQ
|
record.name = GREY + record.name + RESET_SEQ
|
||||||
return logging.Formatter.format(self, record)
|
return logging.Formatter.format(self, record)
|
||||||
|
|
||||||
class CustomLogger(logging.Logger):
|
class CustomLogger(logging.Logger):
|
||||||
'''
|
'''
|
||||||
This adds extra logging functions such as logger.trade and also
|
This adds extra logging functions such as logger.trade and also
|
||||||
sets the logger to use the custom formatter
|
sets the logger to use the custom formatter
|
||||||
'''
|
'''
|
||||||
FORMAT = "[$BOLD%(name)s$RESET] [%(levelname)s] %(message)s"
|
FORMAT = "[$BOLD%(name)s$RESET] [%(levelname)s] %(message)s"
|
||||||
COLOR_FORMAT = formatter_message(FORMAT, True)
|
COLOR_FORMAT = formatter_message(FORMAT, True)
|
||||||
TRADE = 50
|
TRADE = 50
|
||||||
|
|
||||||
def __init__(self, name, logLevel='DEBUG'):
|
def __init__(self, name, logLevel='DEBUG'):
|
||||||
logging.Logger.__init__(self, name, logLevel)
|
logging.Logger.__init__(self, name, logLevel)
|
||||||
color_formatter = Formatter(self.COLOR_FORMAT)
|
color_formatter = Formatter(self.COLOR_FORMAT)
|
||||||
console = logging.StreamHandler()
|
console = logging.StreamHandler()
|
||||||
console.setFormatter(color_formatter)
|
console.setFormatter(color_formatter)
|
||||||
self.addHandler(console)
|
self.addHandler(console)
|
||||||
logging.addLevelName(self.TRADE, "TRADE")
|
logging.addLevelName(self.TRADE, "TRADE")
|
||||||
return
|
return
|
||||||
|
|
||||||
def trade(self, message, *args, **kws):
|
def trade(self, message, *args, **kws):
|
||||||
"""
|
"""
|
||||||
Print a syntax highlighted trade signal
|
Print a syntax highlighted trade signal
|
||||||
"""
|
"""
|
||||||
if self.isEnabledFor(self.TRADE):
|
if self.isEnabledFor(self.TRADE):
|
||||||
message = format_word(message, 'CLOSED ', YELLOW, bold=True)
|
message = format_word(message, 'CLOSED ', YELLOW, bold=True)
|
||||||
message = format_word(message, 'OPENED ', LIGHT_BLUE, bold=True)
|
message = format_word(message, 'OPENED ', LIGHT_BLUE, bold=True)
|
||||||
message = format_word(message, 'UPDATED ', BLUE, bold=True)
|
message = format_word(message, 'UPDATED ', BLUE, bold=True)
|
||||||
message = format_word(message, 'CLOSED_ALL ', RED, bold=True)
|
message = format_word(message, 'CLOSED_ALL ', RED, bold=True)
|
||||||
# Yes, logger takes its '*args' as 'args'.
|
# Yes, logger takes its '*args' as 'args'.
|
||||||
self._log(self.TRADE, message, args, **kws)
|
self._log(self.TRADE, message, args, **kws)
|
||||||
@@ -7,10 +7,10 @@ import json
|
|||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from .GenericWebsocket import GenericWebsocket, AuthError
|
from .generic_websocket import GenericWebsocket, AuthError
|
||||||
from .SubscriptionManager import SubscriptionManager
|
from .subscription_manager import SubscriptionManager
|
||||||
from .WalletManager import WalletManager
|
from .wallet_manager import WalletManager
|
||||||
from .OrderManager import OrderManager
|
from .order_manager import OrderManager
|
||||||
from ..utils.auth import generate_auth_payload
|
from ..utils.auth import generate_auth_payload
|
||||||
from ..models import Order, Trade, OrderBook
|
from ..models import Order, Trade, OrderBook
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ class BfxWebsocket(GenericWebsocket):
|
|||||||
self.pendingOrders = {}
|
self.pendingOrders = {}
|
||||||
self.orderBooks = {}
|
self.orderBooks = {}
|
||||||
self.ws_capacity = ws_capacity
|
self.ws_capacity = ws_capacity
|
||||||
# How should we store float values? could also be bfxapi.Decimal
|
# How should we store float values? could also be bfxapi.decimal
|
||||||
# which is slower but has higher precision.
|
# which is slower but has higher precision.
|
||||||
self.parse_float = parse_float
|
self.parse_float = parse_float
|
||||||
super(BfxWebsocket, self).__init__(host, logLevel=logLevel, *args, **kwargs)
|
super(BfxWebsocket, self).__init__(host, logLevel=logLevel, *args, **kwargs)
|
||||||
@@ -217,7 +217,7 @@ class BfxWebsocket(GenericWebsocket):
|
|||||||
if self.subscriptionManager.is_subscribed(data[0]):
|
if self.subscriptionManager.is_subscribed(data[0]):
|
||||||
symbol = self.subscriptionManager.get(data[0]).symbol
|
symbol = self.subscriptionManager.get(data[0]).symbol
|
||||||
tradeObj = _parse_trade(tData, symbol)
|
tradeObj = _parse_trade(tData, symbol)
|
||||||
self._emit('new_trade', tradeObj)
|
self._emit('trade_update', tradeObj)
|
||||||
|
|
||||||
async def _trade_executed_handler(self, data):
|
async def _trade_executed_handler(self, data):
|
||||||
tData = data[2]
|
tData = data[2]
|
||||||
@@ -1,214 +1,214 @@
|
|||||||
"""
|
"""
|
||||||
Module used as a interfeace to describe a generick websocket client
|
Module used as a interfeace to describe a generick websocket client
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import websockets
|
import websockets
|
||||||
import socket
|
import socket
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from pyee import EventEmitter
|
from pyee import EventEmitter
|
||||||
from ..utils.CustomLogger import CustomLogger
|
from ..utils.custom_logger import CustomLogger
|
||||||
|
|
||||||
# websocket exceptions
|
# websocket exceptions
|
||||||
from websockets.exceptions import ConnectionClosed
|
from websockets.exceptions import ConnectionClosed
|
||||||
|
|
||||||
class AuthError(Exception):
|
class AuthError(Exception):
|
||||||
"""
|
"""
|
||||||
Thrown whenever there is a problem with the authentication packet
|
Thrown whenever there is a problem with the authentication packet
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def is_json(myjson):
|
def is_json(myjson):
|
||||||
try:
|
try:
|
||||||
json_object = json.loads(myjson)
|
json_object = json.loads(myjson)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
class Socket():
|
class Socket():
|
||||||
def __init__(self, sId):
|
def __init__(self, sId):
|
||||||
self.ws = None
|
self.ws = None
|
||||||
self.isConnected = False
|
self.isConnected = False
|
||||||
self.isAuthenticated = False
|
self.isAuthenticated = False
|
||||||
self.id = sId
|
self.id = sId
|
||||||
|
|
||||||
def set_connected(self):
|
def set_connected(self):
|
||||||
self.isConnected = True
|
self.isConnected = True
|
||||||
|
|
||||||
def set_disconnected(self):
|
def set_disconnected(self):
|
||||||
self.isConnected = False
|
self.isConnected = False
|
||||||
|
|
||||||
def set_authenticated(self):
|
def set_authenticated(self):
|
||||||
self.isAuthenticated = True
|
self.isAuthenticated = True
|
||||||
|
|
||||||
def set_websocket(self, ws):
|
def set_websocket(self, ws):
|
||||||
self.ws = ws
|
self.ws = ws
|
||||||
|
|
||||||
def _start_event_worker():
|
def _start_event_worker():
|
||||||
async def event_sleep_process():
|
async def event_sleep_process():
|
||||||
"""
|
"""
|
||||||
sleeping process for event emitter to schedule on
|
sleeping process for event emitter to schedule on
|
||||||
"""
|
"""
|
||||||
while True:
|
while True:
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
def start_loop(loop):
|
def start_loop(loop):
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
loop.run_until_complete(event_sleep_process())
|
loop.run_until_complete(event_sleep_process())
|
||||||
event_loop = asyncio.new_event_loop()
|
event_loop = asyncio.new_event_loop()
|
||||||
worker = Thread(target=start_loop, args=(event_loop,))
|
worker = Thread(target=start_loop, args=(event_loop,))
|
||||||
worker.start()
|
worker.start()
|
||||||
return event_loop
|
return event_loop
|
||||||
|
|
||||||
class GenericWebsocket:
|
class GenericWebsocket:
|
||||||
"""
|
"""
|
||||||
Websocket object used to contain the base functionality of a websocket.
|
Websocket object used to contain the base functionality of a websocket.
|
||||||
Inlcudes an event emitter and a standard websocket client.
|
Inlcudes an event emitter and a standard websocket client.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, host, logLevel='INFO', loop=None, max_retries=5,
|
def __init__(self, host, logLevel='INFO', loop=None, max_retries=5,
|
||||||
create_event_emitter=_start_event_worker):
|
create_event_emitter=_start_event_worker):
|
||||||
self.host = host
|
self.host = host
|
||||||
self.logger = CustomLogger('BfxWebsocket', logLevel=logLevel)
|
self.logger = CustomLogger('BfxWebsocket', logLevel=logLevel)
|
||||||
self.loop = loop or asyncio.get_event_loop()
|
self.loop = loop or asyncio.get_event_loop()
|
||||||
# overide 'error' event to stop it raising an exception
|
# overide 'error' event to stop it raising an exception
|
||||||
# self.events.on('error', self.on_error)
|
# self.events.on('error', self.on_error)
|
||||||
self.ws = None
|
self.ws = None
|
||||||
self.max_retries = max_retries
|
self.max_retries = max_retries
|
||||||
self.attempt_retry = True
|
self.attempt_retry = True
|
||||||
self.sockets = {}
|
self.sockets = {}
|
||||||
# start seperate process for the even emitter
|
# start seperate process for the even emitter
|
||||||
eventLoop = create_event_emitter()
|
eventLoop = create_event_emitter()
|
||||||
self.events = EventEmitter(scheduler=asyncio.ensure_future, loop=eventLoop)
|
self.events = EventEmitter(scheduler=asyncio.ensure_future, loop=eventLoop)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""
|
"""
|
||||||
Starte the websocket connection. This functions spawns the initial socket
|
Starte the websocket connection. This functions spawns the initial socket
|
||||||
thread and connection.
|
thread and connection.
|
||||||
"""
|
"""
|
||||||
self._start_new_socket()
|
self._start_new_socket()
|
||||||
|
|
||||||
def get_task_executable(self):
|
def get_task_executable(self):
|
||||||
"""
|
"""
|
||||||
Get the run indefinitely asyncio task
|
Get the run indefinitely asyncio task
|
||||||
"""
|
"""
|
||||||
return self._run_socket()
|
return self._run_socket()
|
||||||
|
|
||||||
def _start_new_socket(self, socketId=None):
|
def _start_new_socket(self, socketId=None):
|
||||||
if not socketId:
|
if not socketId:
|
||||||
socketId = len(self.sockets)
|
socketId = len(self.sockets)
|
||||||
def start_loop(loop):
|
def start_loop(loop):
|
||||||
asyncio.set_event_loop(loop)
|
asyncio.set_event_loop(loop)
|
||||||
loop.run_until_complete(self._run_socket())
|
loop.run_until_complete(self._run_socket())
|
||||||
worker_loop = asyncio.new_event_loop()
|
worker_loop = asyncio.new_event_loop()
|
||||||
worker = Thread(target=start_loop, args=(worker_loop,))
|
worker = Thread(target=start_loop, args=(worker_loop,))
|
||||||
worker.start()
|
worker.start()
|
||||||
return socketId
|
return socketId
|
||||||
|
|
||||||
def _wait_for_socket(self, socket_id):
|
def _wait_for_socket(self, socket_id):
|
||||||
"""
|
"""
|
||||||
Block until the given socket connection is open
|
Block until the given socket connection is open
|
||||||
"""
|
"""
|
||||||
while True:
|
while True:
|
||||||
socket = self.sockets.get(socket_id, False)
|
socket = self.sockets.get(socket_id, False)
|
||||||
if socket:
|
if socket:
|
||||||
if socket.isConnected and socket.ws:
|
if socket.isConnected and socket.ws:
|
||||||
return
|
return
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
async def _connect(self, socket):
|
async def _connect(self, socket):
|
||||||
async with websockets.connect(self.host) as websocket:
|
async with websockets.connect(self.host) as websocket:
|
||||||
self.sockets[socket.id].set_websocket(websocket)
|
self.sockets[socket.id].set_websocket(websocket)
|
||||||
self.sockets[socket.id].set_connected()
|
self.sockets[socket.id].set_connected()
|
||||||
self.logger.info("Wesocket connected to {}".format(self.host))
|
self.logger.info("Wesocket connected to {}".format(self.host))
|
||||||
while True:
|
while True:
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
message = await websocket.recv()
|
message = await websocket.recv()
|
||||||
await self.on_message(socket.id, message)
|
await self.on_message(socket.id, message)
|
||||||
|
|
||||||
def get_socket(self, socketId):
|
def get_socket(self, socketId):
|
||||||
return self.sockets[socketId]
|
return self.sockets[socketId]
|
||||||
|
|
||||||
def get_authenticated_socket(self):
|
def get_authenticated_socket(self):
|
||||||
for socketId in self.sockets:
|
for socketId in self.sockets:
|
||||||
if self.sockets[socketId].isAuthenticated:
|
if self.sockets[socketId].isAuthenticated:
|
||||||
return self.sockets[socketId]
|
return self.sockets[socketId]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def _run_socket(self):
|
async def _run_socket(self):
|
||||||
retries = 0
|
retries = 0
|
||||||
sId = len(self.sockets)
|
sId = len(self.sockets)
|
||||||
s = Socket(sId)
|
s = Socket(sId)
|
||||||
self.sockets[sId] = s
|
self.sockets[sId] = s
|
||||||
while retries < self.max_retries and self.attempt_retry:
|
while retries < self.max_retries and self.attempt_retry:
|
||||||
try:
|
try:
|
||||||
await self._connect(s)
|
await self._connect(s)
|
||||||
retries = 0
|
retries = 0
|
||||||
except (ConnectionClosed, socket.error) as e:
|
except (ConnectionClosed, socket.error) as e:
|
||||||
self.sockets[sId].set_disconnected()
|
self.sockets[sId].set_disconnected()
|
||||||
self._emit('disconnected')
|
self._emit('disconnected')
|
||||||
if (not self.attempt_retry):
|
if (not self.attempt_retry):
|
||||||
return
|
return
|
||||||
self.logger.error(str(e))
|
self.logger.error(str(e))
|
||||||
retries += 1
|
retries += 1
|
||||||
# wait 5 seconds befor retrying
|
# wait 5 seconds befor retrying
|
||||||
self.logger.info("Waiting 5 seconds before retrying...")
|
self.logger.info("Waiting 5 seconds before retrying...")
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
self.logger.info("Reconnect attempt {}/{}".format(retries, self.max_retries))
|
self.logger.info("Reconnect attempt {}/{}".format(retries, self.max_retries))
|
||||||
self.logger.info("Unable to connect to websocket.")
|
self.logger.info("Unable to connect to websocket.")
|
||||||
self._emit('stopped')
|
self._emit('stopped')
|
||||||
|
|
||||||
def remove_all_listeners(self, event):
|
def remove_all_listeners(self, event):
|
||||||
"""
|
"""
|
||||||
Remove all listeners from event emitter
|
Remove all listeners from event emitter
|
||||||
"""
|
"""
|
||||||
self.events.remove_all_listeners(event)
|
self.events.remove_all_listeners(event)
|
||||||
|
|
||||||
def on(self, event, func=None):
|
def on(self, event, func=None):
|
||||||
"""
|
"""
|
||||||
Add a new event to the event emitter
|
Add a new event to the event emitter
|
||||||
"""
|
"""
|
||||||
if not func:
|
if not func:
|
||||||
return self.events.on(event)
|
return self.events.on(event)
|
||||||
self.events.on(event, func)
|
self.events.on(event, func)
|
||||||
|
|
||||||
def once(self, event, func=None):
|
def once(self, event, func=None):
|
||||||
"""
|
"""
|
||||||
Add a new event to only fire once to the event
|
Add a new event to only fire once to the event
|
||||||
emitter
|
emitter
|
||||||
"""
|
"""
|
||||||
if not func:
|
if not func:
|
||||||
return self.events.once(event)
|
return self.events.once(event)
|
||||||
self.events.once(event, func)
|
self.events.once(event, func)
|
||||||
|
|
||||||
def _emit(self, event, *args, **kwargs):
|
def _emit(self, event, *args, **kwargs):
|
||||||
self.events.emit(event, *args, **kwargs)
|
self.events.emit(event, *args, **kwargs)
|
||||||
|
|
||||||
async def on_error(self, error):
|
async def on_error(self, error):
|
||||||
"""
|
"""
|
||||||
On websocket error print and fire event
|
On websocket error print and fire event
|
||||||
"""
|
"""
|
||||||
self.logger.error(error)
|
self.logger.error(error)
|
||||||
|
|
||||||
async def on_close(self):
|
async def on_close(self):
|
||||||
"""
|
"""
|
||||||
On websocket close print and fire event. This is used by the data server.
|
On websocket close print and fire event. This is used by the data server.
|
||||||
"""
|
"""
|
||||||
self.logger.info("Websocket closed.")
|
self.logger.info("Websocket closed.")
|
||||||
self.attempt_retry = False
|
self.attempt_retry = False
|
||||||
await self.ws.close()
|
await self.ws.close()
|
||||||
self._emit('done')
|
self._emit('done')
|
||||||
|
|
||||||
async def on_open(self):
|
async def on_open(self):
|
||||||
"""
|
"""
|
||||||
On websocket open
|
On websocket open
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def on_message(self, message):
|
async def on_message(self, message):
|
||||||
"""
|
"""
|
||||||
On websocket message
|
On websocket message
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
@@ -5,7 +5,7 @@ Module used to house all of the functions/classes used to handle orders
|
|||||||
import time
|
import time
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from ..utils.CustomLogger import CustomLogger
|
from ..utils.custom_logger import CustomLogger
|
||||||
from ..models import Order
|
from ..models import Order
|
||||||
|
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ import json
|
|||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from ..utils.CustomLogger import CustomLogger
|
from ..utils.custom_logger import CustomLogger
|
||||||
from ..models import Subscription
|
from ..models import Subscription
|
||||||
|
|
||||||
MAX_CHANNEL_COUNT = 25
|
MAX_CHANNEL_COUNT = 25
|
||||||
Reference in New Issue
Block a user