mirror of
https://github.com/aljazceru/bitfinex-api-py.git
synced 2025-12-19 23:04:21 +01:00
fix all linting for pylint3
This commit is contained in:
@@ -9,4 +9,4 @@ python:
|
|||||||
install:
|
install:
|
||||||
- pip install -r requirements.txt
|
- pip install -r requirements.txt
|
||||||
# command to run tests
|
# command to run tests
|
||||||
script: pylint bfxapi
|
script: pylint --rcfile=pylint.rc bfxapi
|
||||||
|
|||||||
@@ -1,13 +1,29 @@
|
|||||||
|
"""
|
||||||
|
This module exposes the core bitfinex clients which includes both
|
||||||
|
a websocket client and a rest interface client
|
||||||
|
"""
|
||||||
|
|
||||||
|
# pylint: disable-all
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from .websockets.BfxWebsocket import BfxWebsocket
|
from .websockets.BfxWebsocket import BfxWebsocket
|
||||||
from .rest.BfxRest import BfxRest
|
from .rest.BfxRest import BfxRest
|
||||||
|
|
||||||
|
REST_HOST = 'https://api.bitfinex.com/v2'
|
||||||
|
WS_HOST = 'wss://api.bitfinex.com/ws/2'
|
||||||
|
|
||||||
class Client:
|
class Client:
|
||||||
def __init__(self, API_KEY=None, API_SECRET=None, rest_host='https://api.bitfinex.com/v2',
|
"""
|
||||||
ws_host='wss://api.bitfinex.com/ws/2', loop=None, logLevel='INFO', dead_man_switch=False, *args, **kwargs):
|
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,
|
||||||
|
*args, **kwargs):
|
||||||
self.loop = loop or asyncio.get_event_loop()
|
self.loop = loop or asyncio.get_event_loop()
|
||||||
self.ws = BfxWebsocket(API_KEY=API_KEY, API_SECRET=API_SECRET, host=ws_host,
|
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, *args, **kwargs)
|
loop=self.loop, logLevel=logLevel, dead_man_switch=dead_man_switch,
|
||||||
|
*args, **kwargs)
|
||||||
self.rest = BfxRest(API_KEY=API_KEY, API_SECRET=API_SECRET, host=rest_host,
|
self.rest = BfxRest(API_KEY=API_KEY, API_SECRET=API_SECRET, host=rest_host,
|
||||||
loop=self.loop, logLevel=logLevel, *args, **kwargs)
|
loop=self.loop, logLevel=logLevel, *args, **kwargs)
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
name = 'bfxapi'
|
"""
|
||||||
|
This module is used to interact with the bitfinex api
|
||||||
|
"""
|
||||||
|
|
||||||
from bfxapi.Client import Client
|
from .client import Client
|
||||||
from bfxapi.websockets.GenericWebsocket import GenericWebsocket
|
from .models import (Order, Trade, OrderBook, Subscription, Wallet,
|
||||||
from bfxapi.models import *
|
Position, FundingLoan, FundingOffer, FundingCredit)
|
||||||
|
from .websockets.GenericWebsocket import GenericWebsocket
|
||||||
|
|
||||||
|
NAME = 'bfxapi'
|
||||||
|
|||||||
29
bfxapi/client.py
Normal file
29
bfxapi/client.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
"""
|
||||||
|
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.bitfinex.com/v2'
|
||||||
|
WS_HOST = 'wss://api.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,
|
||||||
|
*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,
|
||||||
|
*args, **kwargs)
|
||||||
|
self.rest = BfxRest(API_KEY=API_KEY, API_SECRET=API_SECRET, host=rest_host,
|
||||||
|
loop=self.loop, logLevel=logLevel, *args, **kwargs)
|
||||||
@@ -27,7 +27,7 @@ async def on_subscribe(subscription):
|
|||||||
@bfx.ws.once('subscribed')
|
@bfx.ws.once('subscribed')
|
||||||
async def on_once_subscribe(subscription):
|
async def on_once_subscribe(subscription):
|
||||||
print ("Performig resubscribe")
|
print ("Performig resubscribe")
|
||||||
await bfx.ws.resubscribe(subscription.chanId)
|
await bfx.ws.resubscribe(subscription.chan_id)
|
||||||
|
|
||||||
|
|
||||||
async def start():
|
async def start():
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
|
|
||||||
class FundingCreditModel:
|
|
||||||
ID = 0
|
|
||||||
SYMBOL = 1
|
|
||||||
SIDE = 2
|
|
||||||
MTS_CREATE = 3
|
|
||||||
MTS_UPDATE = 4
|
|
||||||
AMOUNT = 5
|
|
||||||
FLAGS = 6
|
|
||||||
STATUS = 7
|
|
||||||
RATE = 11
|
|
||||||
PERIOD = 12
|
|
||||||
MTS_OPENING = 13
|
|
||||||
MTS_LAST_PAYOUT = 14
|
|
||||||
NOTIFY = 15
|
|
||||||
HIDDEN = 16
|
|
||||||
RENEW = 18
|
|
||||||
NO_CLOSE = 20
|
|
||||||
POSITION_PAIR = 21
|
|
||||||
|
|
||||||
class FundingLoan:
|
|
||||||
"""
|
|
||||||
ID integer Offer ID
|
|
||||||
SYMBOL string The currency of the offer (fUSD, etc)
|
|
||||||
SIDE string "Lend" or "Loan"
|
|
||||||
MTS_CREATE int Millisecond Time Stamp when the offer was created
|
|
||||||
MSG_UPDATE int Millisecond Time Stamp when the offer was updated
|
|
||||||
AMOUNT float Amount the offer is for
|
|
||||||
FLAGS object future params object (stay tuned)
|
|
||||||
STATUS string Offer Status: ACTIVE, EXECUTED, PARTIALLY FILLED, CANCELED
|
|
||||||
RATE float Rate of the offer
|
|
||||||
PERIOD int Period of the offer
|
|
||||||
MTS_OPENING int Millisecond Time Stamp when funding opened
|
|
||||||
MTS_LAST_PAYOUT int Millisecond Time Stamp when last payout received
|
|
||||||
NOTIFY int 0 if false, 1 if true
|
|
||||||
HIDDEN int 0 if false, 1 if true
|
|
||||||
RENEW int 0 if false, 1 if true
|
|
||||||
NO_CLOSE int 0 if false, 1 if true Whether the funding will be closed when the position is closed
|
|
||||||
POSITION_PAIR string Pair of the position that the funding was used for
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, id, symbol, side, mts_create, mts_update, amount, flags, status, rate,
|
|
||||||
period, mts_opening, mts_last_payout, notify, hidden, renew, no_close, position_pair):
|
|
||||||
self.id = id
|
|
||||||
self.symbol = symbol
|
|
||||||
self.side = side
|
|
||||||
self.mts_create = mts_create
|
|
||||||
self.mts_update = mts_update
|
|
||||||
self.amount = amount
|
|
||||||
self.flags = flags
|
|
||||||
self.status = status
|
|
||||||
self.rate = rate
|
|
||||||
self.period = period
|
|
||||||
self.mts_opening = mts_opening
|
|
||||||
self.mts_last_payout = mts_last_payout
|
|
||||||
self.notify = notify
|
|
||||||
self.hidden = hidden
|
|
||||||
self.renew = renew
|
|
||||||
self.no_close = no_close
|
|
||||||
self.position_pair = position_pair
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_raw_credit(raw_credit):
|
|
||||||
id = raw_credit[FundingCreditModel.ID]
|
|
||||||
symbol = raw_credit[FundingCreditModel.SYMBOL]
|
|
||||||
side = raw_credit[FundingCreditModel.SIDE]
|
|
||||||
mts_create = raw_credit[FundingCreditModel.MTS_CREATE]
|
|
||||||
mts_update = raw_credit[FundingCreditModel.MTS_UPDATE]
|
|
||||||
amount = raw_credit[FundingCreditModel.AMOUNT]
|
|
||||||
flags = raw_credit[FundingCreditModel.FLAGS]
|
|
||||||
status = raw_credit[FundingCreditModel.STATUS]
|
|
||||||
rate = raw_credit[FundingCreditModel.RATE]
|
|
||||||
period = raw_credit[FundingCreditModel.PERIOD]
|
|
||||||
mts_opening = raw_credit[FundingCreditModel.MTS_OPENING]
|
|
||||||
mts_last_payout = raw_credit[FundingCreditModel.MTS_LAST_PAYOUT]
|
|
||||||
notify = raw_credit[FundingCreditModel.NOTIFY]
|
|
||||||
hidden = raw_credit[FundingCreditModel.HIDDEN]
|
|
||||||
renew = raw_credit[FundingCreditModel.RENEW]
|
|
||||||
no_close = raw_credit[FundingCreditModel.NO_CLOSE]
|
|
||||||
position_pair = raw_credit[FundingCreditModel.POSITION_PAIR]
|
|
||||||
return FundingLoan(id, symbol, side, mts_create, mts_update, amount, flags, status, rate,
|
|
||||||
period, mts_opening, mts_last_payout, notify, hidden, renew, no_close, position_pair)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "FundingCredit '{}' <id={} rate={} amount={} period={} status='{}'>".format(
|
|
||||||
self.symbol, self.id, self.rate, self.amount, self.period, self.status)
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
|
|
||||||
class FundingLoanModel:
|
|
||||||
ID = 0
|
|
||||||
SYMBOL = 1
|
|
||||||
SIDE = 2
|
|
||||||
MTS_CREATE = 3
|
|
||||||
MTS_UPDATE = 4
|
|
||||||
AMOUNT = 5
|
|
||||||
FLAGS = 6
|
|
||||||
STATUS = 7
|
|
||||||
RATE = 11
|
|
||||||
PERIOD = 12
|
|
||||||
MTS_OPENING = 13
|
|
||||||
MTS_LAST_PAYOUT = 14
|
|
||||||
NOTIFY = 15
|
|
||||||
HIDDEN = 16
|
|
||||||
RENEW = 18
|
|
||||||
NO_CLOSE = 20
|
|
||||||
|
|
||||||
class FundingLoan:
|
|
||||||
"""
|
|
||||||
ID integer Offer ID
|
|
||||||
SYMBOL string The currency of the offer (fUSD, etc)
|
|
||||||
SIDE string "Lend" or "Loan"
|
|
||||||
MTS_CREATE int Millisecond Time Stamp when the offer was created
|
|
||||||
MTS_UPDATE int Millisecond Time Stamp when the offer was created
|
|
||||||
AMOUNT float Amount the offer is for
|
|
||||||
FLAGS object future params object (stay tuned)
|
|
||||||
STATUS string Offer Status: ACTIVE, EXECUTED, PARTIALLY FILLED, CANCELED
|
|
||||||
RATE float Rate of the offer
|
|
||||||
PERIOD int Period of the offer
|
|
||||||
MTS_OPENING int Millisecond Time Stamp for when the loan was opened
|
|
||||||
MTS_LAST_PAYOUT int Millisecond Time Stamp for when the last payout was made
|
|
||||||
NOTIFY int 0 if false, 1 if true
|
|
||||||
HIDDEN int 0 if false, 1 if true
|
|
||||||
RENEW int 0 if false, 1 if true
|
|
||||||
NO_CLOSE int If funding will be returned when position is closed. 0 if false, 1 if true
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, id, symbol, side, mts_create, mts_update, amount, flags, status, rate,
|
|
||||||
period, mts_opening, mts_last_payout, notify, hidden, renew, no_close):
|
|
||||||
self.id = id
|
|
||||||
self.symbol = symbol
|
|
||||||
self.side = side
|
|
||||||
self.mts_create = mts_create
|
|
||||||
self.mts_update = mts_update
|
|
||||||
self.amount = amount
|
|
||||||
self.flags = flags
|
|
||||||
self.status = status
|
|
||||||
self.rate = rate
|
|
||||||
self.period = period
|
|
||||||
self.mts_opening = mts_opening
|
|
||||||
self.mts_last_payout = mts_last_payout
|
|
||||||
self.notify = notify
|
|
||||||
self.hidden = hidden
|
|
||||||
self.renew = renew
|
|
||||||
self.no_close = no_close
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_raw_loan(raw_loan):
|
|
||||||
id = raw_loan[FundingLoanModel.ID]
|
|
||||||
symbol = raw_loan[FundingLoanModel.SYMBOL]
|
|
||||||
side = raw_loan[FundingLoanModel.SIDE]
|
|
||||||
mts_create = raw_loan[FundingLoanModel.MTS_CREATE]
|
|
||||||
mts_update = raw_loan[FundingLoanModel.MTS_UPDATE]
|
|
||||||
amount = raw_loan[FundingLoanModel.AMOUNT]
|
|
||||||
flags = raw_loan[FundingLoanModel.FLAGS]
|
|
||||||
status = raw_loan[FundingLoanModel.STATUS]
|
|
||||||
rate = raw_loan[FundingLoanModel.RATE]
|
|
||||||
period = raw_loan[FundingLoanModel.PERIOD]
|
|
||||||
mts_opening = raw_loan[FundingLoanModel.MTS_OPENING]
|
|
||||||
mts_last_payout = raw_loan[FundingLoanModel.MTS_LAST_PAYOUT]
|
|
||||||
notify = raw_loan[FundingLoanModel.NOTIFY]
|
|
||||||
hidden = raw_loan[FundingLoanModel.HIDDEN]
|
|
||||||
renew = raw_loan[FundingLoanModel.RENEW]
|
|
||||||
no_close = raw_loan[FundingLoanModel.NO_CLOSE]
|
|
||||||
return FundingLoan(id, symbol, side, mts_create, mts_update, amount, flags, status, rate,
|
|
||||||
period, mts_opening, mts_last_payout, notify, hidden, renew, no_close)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "FundingLoan '{}' <id={} rate={} amount={} period={} status='{}'>".format(
|
|
||||||
self.symbol, self.id, self.rate, self.amount, self.period, self.status)
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
|
|
||||||
class FundingOfferModel:
|
|
||||||
ID = 0
|
|
||||||
SYMBOL = 1
|
|
||||||
MTS_CREATE = 2
|
|
||||||
MTS_UPDATED = 3
|
|
||||||
AMOUNT = 4
|
|
||||||
AMOUNT_ORIG = 5
|
|
||||||
TYPE = 6
|
|
||||||
FLAGS = 9
|
|
||||||
STATUS = 10
|
|
||||||
RATE = 14
|
|
||||||
PERIOD = 15
|
|
||||||
NOTFIY = 16
|
|
||||||
HIDDEN = 17
|
|
||||||
RENEW = 19
|
|
||||||
|
|
||||||
class FundingOffer:
|
|
||||||
"""
|
|
||||||
ID integer Offer ID
|
|
||||||
SYMBOL string The currency of the offer (fUSD, etc)
|
|
||||||
MTS_CREATED int Millisecond Time Stamp when the offer was created
|
|
||||||
MSG_UPDATED int Millisecond Time Stamp when the offer was created
|
|
||||||
AMOUNT float Amount the offer is for
|
|
||||||
AMOUNT_ORIG float Amount the offer was entered with originally
|
|
||||||
TYPE string "lend" or "loan"
|
|
||||||
FLAGS object future params object (stay tuned)
|
|
||||||
STATUS string Offer Status: ACTIVE, EXECUTED, PARTIALLY FILLED, CANCELED
|
|
||||||
RATE float Rate of the offer
|
|
||||||
PERIOD int Period of the offer
|
|
||||||
NOTIFY int 0 if false, 1 if true
|
|
||||||
HIDDEN int 0 if false, 1 if true
|
|
||||||
RENEW int 0 if false, 1 if true
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, id, symbol, mts_create, mts_updated, amount, amount_orig, f_type,
|
|
||||||
flags, status, rate, period, notify, hidden, renew):
|
|
||||||
self.id = id
|
|
||||||
self.symbol = symbol
|
|
||||||
self.mts_create = mts_create
|
|
||||||
self.mts_updated = mts_updated
|
|
||||||
self.amount = amount
|
|
||||||
self.amount_orig = amount_orig
|
|
||||||
self.f_type = f_type
|
|
||||||
self.flags = flags
|
|
||||||
self.status = status
|
|
||||||
self.rate = rate
|
|
||||||
self.period = period
|
|
||||||
self.notify = notify
|
|
||||||
self.hidden = hidden
|
|
||||||
self.renew = renew
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_raw_offer(raw_offer):
|
|
||||||
id = raw_offer[FundingOfferModel.ID]
|
|
||||||
symbol = raw_offer[FundingOfferModel.SYMBOL]
|
|
||||||
mts_create = raw_offer[FundingOfferModel.MTS_CREATE]
|
|
||||||
mts_updated = raw_offer[FundingOfferModel.MTS_UPDATED]
|
|
||||||
amount = raw_offer[FundingOfferModel.AMOUNT]
|
|
||||||
amount_orig = raw_offer[FundingOfferModel.AMOUNT_ORIG]
|
|
||||||
f_type = raw_offer[FundingOfferModel.TYPE]
|
|
||||||
flags = raw_offer[FundingOfferModel.FLAGS]
|
|
||||||
status = raw_offer[FundingOfferModel.STATUS]
|
|
||||||
rate = raw_offer[FundingOfferModel.RATE]
|
|
||||||
period = raw_offer[FundingOfferModel.PERIOD]
|
|
||||||
notify = raw_offer[FundingOfferModel.NOTFIY]
|
|
||||||
hidden = raw_offer[FundingOfferModel.HIDDEN]
|
|
||||||
renew = raw_offer[FundingOfferModel.RENEW]
|
|
||||||
return FundingOffer(id, symbol, mts_create, mts_updated, amount,
|
|
||||||
amount_orig, f_type,flags, status, rate, period, notify, hidden, renew)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "FundingOffer '{}' <id={} rate={} period={} status='{}'>".format(
|
|
||||||
self.symbol, self.id, self.rate, self.period, self.status)
|
|
||||||
@@ -1,7 +1,15 @@
|
|||||||
|
"""
|
||||||
|
Module used to describe all of the different data types
|
||||||
|
"""
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
class OrderType:
|
class OrderType:
|
||||||
|
"""
|
||||||
|
Enum used to describe all of the different order types available for use
|
||||||
|
"""
|
||||||
MARKET = 'MARKET'
|
MARKET = 'MARKET'
|
||||||
LIMIT = 'LIMIT'
|
LIMIT = 'LIMIT'
|
||||||
STOP = 'STOP'
|
STOP = 'STOP'
|
||||||
@@ -15,14 +23,25 @@ class OrderType:
|
|||||||
EXCHANGE_TRAILING_STOP = 'EXCHANGE TRAILING STOP'
|
EXCHANGE_TRAILING_STOP = 'EXCHANGE TRAILING STOP'
|
||||||
EXCHANGE_FILL_OR_KILL = 'EXCHANGE FOK'
|
EXCHANGE_FILL_OR_KILL = 'EXCHANGE FOK'
|
||||||
|
|
||||||
|
|
||||||
LIMIT_ORDERS = [OrderType.LIMIT, OrderType.STOP_LIMIT, OrderType.EXCHANGE_LIMIT,
|
LIMIT_ORDERS = [OrderType.LIMIT, OrderType.STOP_LIMIT, OrderType.EXCHANGE_LIMIT,
|
||||||
OrderType.EXCHANGE_STOP_LIMIT, OrderType.FILL_OR_KILL, OrderType.EXCHANGE_FILL_OR_KILL]
|
OrderType.EXCHANGE_STOP_LIMIT, OrderType.FILL_OR_KILL,
|
||||||
|
OrderType.EXCHANGE_FILL_OR_KILL]
|
||||||
|
|
||||||
|
|
||||||
class OrderSide:
|
class OrderSide:
|
||||||
|
"""
|
||||||
|
Enum used to describe the different directions of an order
|
||||||
|
"""
|
||||||
BUY = 'buy'
|
BUY = 'buy'
|
||||||
SELL = 'sell'
|
SELL = 'sell'
|
||||||
|
|
||||||
|
|
||||||
class OrderClosedModel:
|
class OrderClosedModel:
|
||||||
|
"""
|
||||||
|
Enum used ad an index match to locate the different values in a
|
||||||
|
raw order array
|
||||||
|
"""
|
||||||
ID = 0
|
ID = 0
|
||||||
GID = 1
|
GID = 1
|
||||||
CID = 2
|
CID = 2
|
||||||
@@ -42,27 +61,38 @@ class OrderClosedModel:
|
|||||||
NOTIFY = 23
|
NOTIFY = 23
|
||||||
PLACE_ID = 25
|
PLACE_ID = 25
|
||||||
|
|
||||||
|
|
||||||
class OrderFlags:
|
class OrderFlags:
|
||||||
|
"""
|
||||||
|
Enum used to explain the different values that can be passed in
|
||||||
|
as flags
|
||||||
|
"""
|
||||||
HIDDEN = 64
|
HIDDEN = 64
|
||||||
CLOSE = 12
|
CLOSE = 12
|
||||||
REDUCE_ONLY = 1024
|
REDUCE_ONLY = 1024
|
||||||
POST_ONLY = 4096
|
POST_ONLY = 4096
|
||||||
OCO = 16384
|
OCO = 16384
|
||||||
|
|
||||||
|
|
||||||
def now_in_mills():
|
def now_in_mills():
|
||||||
|
"""
|
||||||
|
Gets the current time in milliseconds
|
||||||
|
"""
|
||||||
return int(round(time.time() * 1000))
|
return int(round(time.time() * 1000))
|
||||||
|
|
||||||
|
|
||||||
class Order:
|
class Order:
|
||||||
"""
|
"""
|
||||||
ID int64 Order ID
|
ID int64 Order ID
|
||||||
GID int Group ID
|
GID int Group ID
|
||||||
CID int Client Order ID
|
CID int Client Order ID
|
||||||
SYMBOL string Pair (tBTCUSD, …)
|
SYMBOL string Pair (tBTCUSD, ...)
|
||||||
MTS_CREATE int Millisecond timestamp of creation
|
MTS_CREATE int Millisecond timestamp of creation
|
||||||
MTS_UPDATE int Millisecond timestamp of update
|
MTS_UPDATE int Millisecond timestamp of update
|
||||||
AMOUNT float Positive means buy, negative means sell.
|
AMOUNT float Positive means buy, negative means sell.
|
||||||
AMOUNT_ORIG float Original amount
|
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 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
|
TYPE_PREV string Previous order type
|
||||||
FLAGS int Upcoming Params Object (stay tuned)
|
FLAGS int Upcoming Params Object (stay tuned)
|
||||||
ORDER_STATUS string Order Status: ACTIVE, EXECUTED, PARTIALLY FILLED, CANCELED
|
ORDER_STATUS string Order Status: ACTIVE, EXECUTED, PARTIALLY FILLED, CANCELED
|
||||||
@@ -71,34 +101,37 @@ class Order:
|
|||||||
PRICE_TRAILING float The trailing price
|
PRICE_TRAILING float The trailing price
|
||||||
PRICE_AUX_LIMIT float Auxiliary Limit price (for STOP LIMIT)
|
PRICE_AUX_LIMIT float Auxiliary Limit price (for STOP LIMIT)
|
||||||
HIDDEN int 1 if Hidden, 0 if not hidden
|
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
|
PLACED_ID int If another order caused this order to be placed (OCO) this will be that other
|
||||||
|
order's ID
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Type = OrderType()
|
Type = OrderType()
|
||||||
Side = OrderSide()
|
Side = OrderSide()
|
||||||
Flags = OrderFlags()
|
Flags = OrderFlags()
|
||||||
|
|
||||||
def __init__(self, id, gId, cId, symbol, mtsCreate, mtsUpdate, amount, amountOrig, oType,
|
def __init__(self, oid, gid, cid, symbol, mts_create, mts_update, amount,
|
||||||
typePrev, flags, status, price, priceAvg, priceTrailing, priceAuxLimit, notfiy, placeId):
|
amount_orig, o_type, typePrev, flags, status, price, price_avg,
|
||||||
self.id = id
|
price_trailing, price_aux_limit, notfiy, place_id):
|
||||||
self.gId = gId
|
# pylint: disable=invalid-name
|
||||||
self.cId = cId
|
self.id = oid
|
||||||
|
self.gid = gid
|
||||||
|
self.cid = cid
|
||||||
self.symbol = symbol
|
self.symbol = symbol
|
||||||
self.mtsCreate = mtsCreate
|
self.mts_create = mts_create
|
||||||
self.mtsUpdate = mtsUpdate
|
self.mts_update = mts_update
|
||||||
# self.amount = amount
|
# self.amount = amount
|
||||||
self.amount = amount
|
self.amount = amount
|
||||||
self.amountOrig = amountOrig
|
self.amount_orig = amount_orig
|
||||||
self.type = oType
|
self.type = o_type
|
||||||
self.typePrev = typePrev
|
self.type_prev = typePrev
|
||||||
self.flags = flags
|
self.flags = flags
|
||||||
self.status = status
|
self.status = status
|
||||||
self.price = price
|
self.price = price
|
||||||
self.priceAvg = priceAvg
|
self.price_avg = price_avg
|
||||||
self.priceTrailing = priceTrailing
|
self.price_trailing = price_trailing
|
||||||
self.priceAuxLimit = priceAuxLimit
|
self.price_aux_limit = price_aux_limit
|
||||||
self.notfiy = notfiy
|
self.notfiy = notfiy
|
||||||
self.placeId = placeId
|
self.place_id = place_id
|
||||||
self.tag = ""
|
self.tag = ""
|
||||||
self.fee = 0
|
self.fee = 0
|
||||||
|
|
||||||
@@ -106,56 +139,83 @@ class Order:
|
|||||||
self.is_confirmed_bool = False
|
self.is_confirmed_bool = False
|
||||||
self.is_open_bool = False
|
self.is_open_bool = False
|
||||||
|
|
||||||
self.date = datetime.datetime.fromtimestamp(mtsCreate/1000.0)
|
self.date = datetime.datetime.fromtimestamp(mts_create/1000.0)
|
||||||
## if cancelled then priceAvg wont exist
|
# if cancelled then priceAvg wont exist
|
||||||
if priceAvg:
|
if price_avg:
|
||||||
## check if order is taker or maker
|
# check if order is taker or maker
|
||||||
if self.type in LIMIT_ORDERS:
|
if self.type in LIMIT_ORDERS:
|
||||||
self.fee = (priceAvg * abs(amount)) * 0.001
|
self.fee = (price_avg * abs(amount)) * 0.001
|
||||||
else:
|
else:
|
||||||
self.fee = (priceAvg * abs(amount)) * 0.002
|
self.fee = (price_avg * abs(amount)) * 0.002
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_raw_order(raw_order):
|
def from_raw_order(raw_order):
|
||||||
|
"""
|
||||||
|
Parse a raw order object into an Order oject
|
||||||
|
|
||||||
|
@return Order
|
||||||
|
"""
|
||||||
oid = raw_order[OrderClosedModel.ID]
|
oid = raw_order[OrderClosedModel.ID]
|
||||||
gId = raw_order[OrderClosedModel.GID]
|
gid = raw_order[OrderClosedModel.GID]
|
||||||
cId = raw_order[OrderClosedModel.CID]
|
cid = raw_order[OrderClosedModel.CID]
|
||||||
symbol = raw_order[OrderClosedModel.SYMBOL]
|
symbol = raw_order[OrderClosedModel.SYMBOL]
|
||||||
mtsCreate = raw_order[OrderClosedModel.MTS_CREATE]
|
mts_create = raw_order[OrderClosedModel.MTS_CREATE]
|
||||||
mtsUpdate = raw_order[OrderClosedModel.MTS_UPDATE]
|
mts_update = raw_order[OrderClosedModel.MTS_UPDATE]
|
||||||
amount = raw_order[OrderClosedModel.AMOUNT]
|
amount = raw_order[OrderClosedModel.AMOUNT]
|
||||||
amountOrig = raw_order[OrderClosedModel.AMOUNT_ORIG]
|
amount_orig = raw_order[OrderClosedModel.AMOUNT_ORIG]
|
||||||
oType = raw_order[OrderClosedModel.TYPE]
|
o_type = raw_order[OrderClosedModel.TYPE]
|
||||||
typePrev = raw_order[OrderClosedModel.TYPE_PREV]
|
type_prev = raw_order[OrderClosedModel.TYPE_PREV]
|
||||||
flags = raw_order[OrderClosedModel.FLAGS]
|
flags = raw_order[OrderClosedModel.FLAGS]
|
||||||
status = raw_order[OrderClosedModel.STATUS]
|
status = raw_order[OrderClosedModel.STATUS]
|
||||||
price = raw_order[OrderClosedModel.PRICE]
|
price = raw_order[OrderClosedModel.PRICE]
|
||||||
priceAvg = raw_order[OrderClosedModel.PRIVE_AVG]
|
price_avg = raw_order[OrderClosedModel.PRIVE_AVG]
|
||||||
priceTrailing = raw_order[OrderClosedModel.PRICE_TRAILING]
|
price_trailing = raw_order[OrderClosedModel.PRICE_TRAILING]
|
||||||
priceAuxLimit = raw_order[OrderClosedModel.PRICE_AUX_LIMIT]
|
price_aux_limit = raw_order[OrderClosedModel.PRICE_AUX_LIMIT]
|
||||||
notfiy = raw_order[OrderClosedModel.NOTIFY]
|
notfiy = raw_order[OrderClosedModel.NOTIFY]
|
||||||
placeId = raw_order[OrderClosedModel.PLACE_ID]
|
place_id = raw_order[OrderClosedModel.PLACE_ID]
|
||||||
|
|
||||||
return Order(oid, gId, cId, symbol, mtsCreate, mtsUpdate, amount, amountOrig, oType,
|
return Order(oid, gid, cid, symbol, mts_create, mts_update, amount,
|
||||||
typePrev, flags, status, price, priceAvg, priceTrailing, priceAuxLimit, notfiy, placeId)
|
amount_orig, o_type, type_prev, flags, status, price, price_avg,
|
||||||
|
price_trailing, price_aux_limit, notfiy, place_id)
|
||||||
|
|
||||||
def set_confirmed(self):
|
def set_confirmed(self):
|
||||||
|
"""
|
||||||
|
Set the state of the order to be confirmed
|
||||||
|
"""
|
||||||
self.is_pending_bool = False
|
self.is_pending_bool = False
|
||||||
self.is_confirmed_bool = True
|
self.is_confirmed_bool = True
|
||||||
|
|
||||||
def set_open_state(self, isOpen):
|
def set_open_state(self, is_open):
|
||||||
self.is_open_bool = isOpen
|
"""
|
||||||
|
Set the is_open state of the order
|
||||||
|
"""
|
||||||
|
self.is_open_bool = is_open
|
||||||
|
|
||||||
def isOpen(self):
|
def is_open(self):
|
||||||
|
"""
|
||||||
|
Check if the order is still open
|
||||||
|
|
||||||
|
@return bool: Ture if order open else False
|
||||||
|
"""
|
||||||
return self.is_open_bool
|
return self.is_open_bool
|
||||||
|
|
||||||
def isPending(self):
|
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
|
return self.is_pending_bool
|
||||||
|
|
||||||
def isConfirmed(self):
|
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
|
return self.is_confirmed_bool
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
''' Allow us to print the Order object in a pretty format '''
|
''' Allow us to print the Order object in a pretty format '''
|
||||||
return "Order <'{}' mtsCreate={} status='{}' id={}>".format(self.symbol, self.mtsCreate,
|
text = "Order <'{}' mts_create={} status='{}' id={}>"
|
||||||
self.status, self.id)
|
return text.format(self.symbol, self.mts_create, self.status, self.id)
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
import zlib
|
|
||||||
|
|
||||||
def preparePrice(price):
|
|
||||||
# convert to 4 significant figures
|
|
||||||
prepPrice = '{0:.4f}'.format(price)
|
|
||||||
# remove decimal place if zero float
|
|
||||||
return '{0:g}'.format(float(prepPrice))
|
|
||||||
|
|
||||||
class OrderBook:
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.asks = []
|
|
||||||
self.bids = []
|
|
||||||
|
|
||||||
def get_bids(self):
|
|
||||||
return self.bids
|
|
||||||
|
|
||||||
def get_asks(self):
|
|
||||||
return self.asks
|
|
||||||
|
|
||||||
def updateFromSnapshot(self, data):
|
|
||||||
# [[4642.3, 1, 4.192], [4641.5, 1, 1]]
|
|
||||||
for order in data:
|
|
||||||
if len(order) is 4:
|
|
||||||
if order[3] < 0:
|
|
||||||
self.bids += [order]
|
|
||||||
else:
|
|
||||||
self.asks += [order]
|
|
||||||
else:
|
|
||||||
if order[2] < 0:
|
|
||||||
self.asks += [order]
|
|
||||||
else:
|
|
||||||
self.bids += [order]
|
|
||||||
|
|
||||||
def updateWith(self, order):
|
|
||||||
if len(order) is 4:
|
|
||||||
amount = order[3]
|
|
||||||
count = order[2]
|
|
||||||
side = self.bids if amount < 0 else self.asks
|
|
||||||
else:
|
|
||||||
amount = order[2]
|
|
||||||
side = self.asks if amount < 0 else self.bids
|
|
||||||
count = order[1]
|
|
||||||
price = order[0]
|
|
||||||
|
|
||||||
# if first item in ordebook
|
|
||||||
if len(side) is 0:
|
|
||||||
side += [order]
|
|
||||||
return
|
|
||||||
|
|
||||||
# match price level
|
|
||||||
for index, sOrder in enumerate(side):
|
|
||||||
sPrice = sOrder[0]
|
|
||||||
if sPrice == price:
|
|
||||||
if count is 0:
|
|
||||||
del side[index]
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
# remove but add as new below
|
|
||||||
del side[index]
|
|
||||||
|
|
||||||
# if ob is initialised w/o all price levels
|
|
||||||
if count is 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
# add to book and sort lowest to highest
|
|
||||||
side += [order]
|
|
||||||
side.sort(key=lambda x: x[0], reverse=not amount < 0)
|
|
||||||
return
|
|
||||||
|
|
||||||
def checksum(self):
|
|
||||||
data = []
|
|
||||||
# take set of top 25 bids/asks
|
|
||||||
for index in range(0, 25):
|
|
||||||
if index < len(self.bids):
|
|
||||||
bid = self.bids[index]
|
|
||||||
price = bid[0]
|
|
||||||
amount = bid[3] if len(bid) is 4 else bid[2]
|
|
||||||
data += [preparePrice(price)]
|
|
||||||
data += [str(amount)]
|
|
||||||
if index < len(self.asks):
|
|
||||||
ask = self.asks[index]
|
|
||||||
price = ask[0]
|
|
||||||
amount = ask[3] if len(ask) is 4 else ask[2]
|
|
||||||
data += [preparePrice(price)]
|
|
||||||
data += [str(amount)]
|
|
||||||
checksumStr = ':'.join(data)
|
|
||||||
# calculate checksum and force signed integer
|
|
||||||
checksum = zlib.crc32(checksumStr.encode('utf8')) & 0xffffffff
|
|
||||||
return checksum
|
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
|
"""
|
||||||
|
Module used to describe all of the different data types
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Position:
|
class Position:
|
||||||
"""
|
"""
|
||||||
SYMBOL string Pair (tBTCUSD, …).
|
SYMBOL string Pair (tBTCUSD, ...).
|
||||||
STATUS string Status (ACTIVE, CLOSED).
|
STATUS string Status (ACTIVE, CLOSED).
|
||||||
±AMOUNT float Size of the position. Positive values means a long position, negative values means a short position.
|
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.
|
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 float The amount of funding being used for this position.
|
||||||
MARGIN_FUNDING_TYPE int 0 for daily, 1 for term.
|
MARGIN_FUNDING_TYPE int 0 for daily, 1 for term.
|
||||||
@@ -13,24 +18,30 @@ class Position:
|
|||||||
LEVERAGE float Beta value
|
LEVERAGE float Beta value
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, symbol, status, amount, bPrice, mFunding, mFundingType,
|
def __init__(self, symbol, status, amount, b_price, m_funding, m_funding_type,
|
||||||
profit_loss, profit_loss_perc, lPrice, lev):
|
profit_loss, profit_loss_perc, l_price, lev):
|
||||||
self.symbol = symbol
|
self.symbol = symbol
|
||||||
self.status = status
|
self.status = status
|
||||||
self.amount = amount
|
self.amount = amount
|
||||||
self.base_price = bPrice
|
self.base_price = b_price
|
||||||
self.margin_funding = mFunding
|
self.margin_funding = m_funding
|
||||||
self.margin_funding_type = mFundingType
|
self.margin_funding_type = m_funding_type
|
||||||
self.profit_loss = profit_loss
|
self.profit_loss = profit_loss
|
||||||
self.profit_loss_percentage = profit_loss_perc
|
self.profit_loss_percentage = profit_loss_perc
|
||||||
self.liquidation_price = lPrice
|
self.liquidation_price = l_price
|
||||||
self.leverage = lev
|
self.leverage = lev
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_raw_rest_position(raw_position):
|
def from_raw_rest_position(raw_position):
|
||||||
|
"""
|
||||||
|
Generate a Position object from a raw position array
|
||||||
|
|
||||||
|
@return Position
|
||||||
|
"""
|
||||||
return Position(*raw_position)
|
return Position(*raw_position)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
''' Allow us to print the Trade object in a pretty format '''
|
''' Allow us to print the Trade object in a pretty format '''
|
||||||
return "Position '{}' {} x {} <Sstatus='{}' p&l={}>".format(
|
text = "Position '{}' {} x {} <status='{}' pl={}>"
|
||||||
self.symbol, self.base_price, self.amount, self.status, self.profit_loss)
|
return text.format(self.symbol, self.base_price, self.amount,
|
||||||
|
self.status, self.profit_loss)
|
||||||
|
|||||||
@@ -1,45 +1,74 @@
|
|||||||
|
"""
|
||||||
|
Module used to describe all of the different data types
|
||||||
|
"""
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
class Subscription:
|
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, ws, channel_name, symbol, timeframe=None, **kwargs):
|
def __init__(self, ws, channel_name, symbol, timeframe=None, **kwargs):
|
||||||
self.ws = ws
|
self._ws = ws
|
||||||
self.channel_name = channel_name
|
self.channel_name = channel_name
|
||||||
self.symbol = symbol
|
self.symbol = symbol
|
||||||
self.timeframe = timeframe
|
self.timeframe = timeframe
|
||||||
self.is_subscribed_bool = False
|
self.is_subscribed_bool = False
|
||||||
self.key = None
|
self.key = None
|
||||||
|
self.chan_id = None
|
||||||
if timeframe:
|
if timeframe:
|
||||||
self.key = 'trade:{}:{}'.format(self.timeframe, self.symbol)
|
self.key = 'trade:{}:{}'.format(self.timeframe, self.symbol)
|
||||||
self.sub_id = int(round(time.time() * 1000))
|
self.sub_id = int(round(time.time() * 1000))
|
||||||
self.send_payload = self._generate_payload(**kwargs)
|
self.send_payload = self._generate_payload(**kwargs)
|
||||||
|
|
||||||
async def subscribe(self):
|
def confirm_subscription(self, chan_id):
|
||||||
await self.ws.send(json.dumps(self.get_send_payload()))
|
"""
|
||||||
|
Update the subscription to confirmed state
|
||||||
|
"""
|
||||||
|
self.is_subscribed_bool = True
|
||||||
|
self.chan_id = chan_id
|
||||||
|
|
||||||
async def unsubscribe(self):
|
async def unsubscribe(self):
|
||||||
|
"""
|
||||||
|
Send an unsubscription request to the bitfinex socket
|
||||||
|
"""
|
||||||
if not self.is_subscribed():
|
if not self.is_subscribed():
|
||||||
raise Exception("Subscription is not subscribed to websocket")
|
raise Exception("Subscription is not subscribed to websocket")
|
||||||
payload = { 'event': 'unsubscribe', 'chanId': self.chanId }
|
payload = {'event': 'unsubscribe', 'chan_id': self.chan_id}
|
||||||
await self.ws.send(json.dumps(payload))
|
await self._ws.send(json.dumps(payload))
|
||||||
|
|
||||||
def confirm_subscription(self, chanId):
|
async def subscribe(self):
|
||||||
self.is_subscribed_bool = True
|
"""
|
||||||
self.chanId = chanId
|
Send a subscription request to the bitfinex socket
|
||||||
|
"""
|
||||||
|
await self._ws.send(json.dumps(self._get_send_payload()))
|
||||||
|
|
||||||
def confirm_unsubscribe(self):
|
def confirm_unsubscribe(self):
|
||||||
|
"""
|
||||||
|
Update the subscription to unsubscribed state
|
||||||
|
"""
|
||||||
self.is_subscribed_bool = False
|
self.is_subscribed_bool = False
|
||||||
|
|
||||||
def is_subscribed(self):
|
def is_subscribed(self):
|
||||||
|
"""
|
||||||
|
Check if the subscription is currently subscribed
|
||||||
|
|
||||||
|
@return bool: True if subscribed else False
|
||||||
|
"""
|
||||||
return self.is_subscribed_bool
|
return self.is_subscribed_bool
|
||||||
|
|
||||||
def _generate_payload(self, **kwargs):
|
def _generate_payload(self, **kwargs):
|
||||||
payload = { 'event': 'subscribe', 'channel': self.channel_name, 'symbol': self.symbol }
|
payload = {'event': 'subscribe',
|
||||||
|
'channel': self.channel_name, 'symbol': self.symbol}
|
||||||
if self.timeframe:
|
if self.timeframe:
|
||||||
payload['key'] = self.key
|
payload['key'] = self.key
|
||||||
payload.update(**kwargs)
|
payload.update(**kwargs)
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
def get_send_payload(self):
|
def _get_send_payload(self):
|
||||||
return self.send_payload
|
return self.send_payload
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
|
"""
|
||||||
|
Module used to describe all of the different data types
|
||||||
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
class Trade:
|
class Trade:
|
||||||
"""
|
"""
|
||||||
ID integer Trade database id
|
ID integer Trade database id
|
||||||
PAIR string Pair (BTCUSD, …)
|
PAIR string Pair (BTCUSD, ...)
|
||||||
MTS_CREATE integer Execution timestamp
|
MTS_CREATE integer Execution timestamp
|
||||||
ORDER_ID integer Order id
|
ORDER_ID integer Order id
|
||||||
EXEC_AMOUNT float Positive means buy, negative means sell
|
EXEC_AMOUNT float Positive means buy, negative means sell
|
||||||
@@ -18,9 +23,10 @@ class Trade:
|
|||||||
SHORT = 'SHORT'
|
SHORT = 'SHORT'
|
||||||
LONG = 'LONG'
|
LONG = 'LONG'
|
||||||
|
|
||||||
def __init__(self, id, pair, mts_create, order_id, amount, price, order_type,
|
def __init__(self, tid, pair, mts_create, order_id, amount, price, order_type,
|
||||||
order_price, maker, fee, fee_currency):
|
order_price, maker, fee, fee_currency):
|
||||||
self.id = id
|
# pylint: disable=invalid-name
|
||||||
|
self.id = tid
|
||||||
self.pair = pair
|
self.pair = pair
|
||||||
self.mts_create = mts_create
|
self.mts_create = mts_create
|
||||||
self.date = datetime.datetime.fromtimestamp(mts_create/1000.0)
|
self.date = datetime.datetime.fromtimestamp(mts_create/1000.0)
|
||||||
@@ -36,7 +42,11 @@ class Trade:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_raw_rest_trade(raw_trade):
|
def from_raw_rest_trade(raw_trade):
|
||||||
# [24224048, 'tBTCUSD', 1542800024000, 1151353484, 0.09399997, 19963, None, None, -1, -0.000188, 'BTC']
|
"""
|
||||||
|
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)
|
return Trade(*raw_trade)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
|
"""
|
||||||
|
Module used to describe all of the different data types
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Wallet:
|
class Wallet:
|
||||||
|
"""
|
||||||
|
Stores data relevant to a users wallet such as balance and
|
||||||
|
currency
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, wType, currency, balance, unsettled_interest):
|
def __init__(self, wType, currency, balance, unsettled_interest):
|
||||||
self.type = wType
|
self.type = wType
|
||||||
@@ -9,9 +17,15 @@ class Wallet:
|
|||||||
self.key = "{}_{}".format(wType, currency)
|
self.key = "{}_{}".format(wType, currency)
|
||||||
|
|
||||||
def set_balance(self, data):
|
def set_balance(self, data):
|
||||||
|
"""
|
||||||
|
Set the balance of the wallet
|
||||||
|
"""
|
||||||
self.balance = data
|
self.balance = data
|
||||||
|
|
||||||
def set_unsettled_interest(self, data):
|
def set_unsettled_interest(self, data):
|
||||||
|
"""
|
||||||
|
Set the unsettled interest of the wallet
|
||||||
|
"""
|
||||||
self.unsettled_interest = data
|
self.unsettled_interest = data
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
name = 'models'
|
"""
|
||||||
|
This module contains a group of different models which
|
||||||
|
are used to define data types
|
||||||
|
"""
|
||||||
|
|
||||||
from .Order import *
|
from .order import Order
|
||||||
from .Trade import *
|
from .trade import Trade
|
||||||
from .OrderBook import *
|
from .order_book import OrderBook
|
||||||
from .Subscription import *
|
from .subscription import Subscription
|
||||||
from .Wallet import *
|
from .wallet import Wallet
|
||||||
from .Position import *
|
from .position import Position
|
||||||
from .FundingLoan import *
|
from .funding_loan import FundingLoan
|
||||||
from .FundingOffer import *
|
from .funding_offer import FundingOffer
|
||||||
from .FundingCredit import *
|
from .funding_credit import FundingCredit
|
||||||
|
|
||||||
|
NAME = 'models'
|
||||||
|
|||||||
104
bfxapi/models/funding_credit.py
Normal file
104
bfxapi/models/funding_credit.py
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
"""
|
||||||
|
Module used to describe all of the different data types
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class FundingCreditModel:
|
||||||
|
"""
|
||||||
|
Enum used to index the location of each value in a raw array
|
||||||
|
"""
|
||||||
|
ID = 0
|
||||||
|
SYMBOL = 1
|
||||||
|
SIDE = 2
|
||||||
|
MTS_CREATE = 3
|
||||||
|
MTS_UPDATE = 4
|
||||||
|
AMOUNT = 5
|
||||||
|
FLAGS = 6
|
||||||
|
STATUS = 7
|
||||||
|
RATE = 11
|
||||||
|
PERIOD = 12
|
||||||
|
MTS_OPENING = 13
|
||||||
|
MTS_LAST_PAYOUT = 14
|
||||||
|
NOTIFY = 15
|
||||||
|
HIDDEN = 16
|
||||||
|
RENEW = 18
|
||||||
|
NO_CLOSE = 20
|
||||||
|
POSITION_PAIR = 21
|
||||||
|
|
||||||
|
|
||||||
|
class FundingCredit:
|
||||||
|
"""
|
||||||
|
ID integer Offer ID
|
||||||
|
SYMBOL string The currency of the offer (fUSD, etc)
|
||||||
|
SIDE string "Lend" or "Loan"
|
||||||
|
MTS_CREATE int Millisecond Time Stamp when the offer was created
|
||||||
|
MSG_UPDATE int Millisecond Time Stamp when the offer was updated
|
||||||
|
AMOUNT float Amount the offer is for
|
||||||
|
FLAGS object future params object (stay tuned)
|
||||||
|
STATUS string Offer Status: ACTIVE, EXECUTED, PARTIALLY FILLED, CANCELED
|
||||||
|
RATE float Rate of the offer
|
||||||
|
PERIOD int Period of the offer
|
||||||
|
MTS_OPENING int Millisecond Time Stamp when funding opened
|
||||||
|
MTS_LAST_PAYOUT int Millisecond Time Stamp when last payout received
|
||||||
|
NOTIFY int 0 if false, 1 if true
|
||||||
|
HIDDEN int 0 if false, 1 if true
|
||||||
|
RENEW int 0 if false, 1 if true
|
||||||
|
NO_CLOSE int 0 if false, 1 if true Whether the funding will be closed when the
|
||||||
|
position is closed
|
||||||
|
POSITION_PAIR string Pair of the position that the funding was used for
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, fid, symbol, side, mts_create, mts_update, amount, flags, status, rate,
|
||||||
|
period, mts_opening, mts_last_payout, notify, hidden, renew, no_close,
|
||||||
|
position_pair):
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
self.id = fid
|
||||||
|
self.symbol = symbol
|
||||||
|
self.side = side
|
||||||
|
self.mts_create = mts_create
|
||||||
|
self.mts_update = mts_update
|
||||||
|
self.amount = amount
|
||||||
|
self.flags = flags
|
||||||
|
self.status = status
|
||||||
|
self.rate = rate
|
||||||
|
self.period = period
|
||||||
|
self.mts_opening = mts_opening
|
||||||
|
self.mts_last_payout = mts_last_payout
|
||||||
|
self.notify = notify
|
||||||
|
self.hidden = hidden
|
||||||
|
self.renew = renew
|
||||||
|
self.no_close = no_close
|
||||||
|
self.position_pair = position_pair
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_raw_credit(raw_credit):
|
||||||
|
"""
|
||||||
|
Parse a raw credit object into a FundingCredit object
|
||||||
|
|
||||||
|
@return FundingCredit
|
||||||
|
"""
|
||||||
|
fid = raw_credit[FundingCreditModel.ID]
|
||||||
|
symbol = raw_credit[FundingCreditModel.SYMBOL]
|
||||||
|
side = raw_credit[FundingCreditModel.SIDE]
|
||||||
|
mts_create = raw_credit[FundingCreditModel.MTS_CREATE]
|
||||||
|
mts_update = raw_credit[FundingCreditModel.MTS_UPDATE]
|
||||||
|
amount = raw_credit[FundingCreditModel.AMOUNT]
|
||||||
|
flags = raw_credit[FundingCreditModel.FLAGS]
|
||||||
|
status = raw_credit[FundingCreditModel.STATUS]
|
||||||
|
rate = raw_credit[FundingCreditModel.RATE]
|
||||||
|
period = raw_credit[FundingCreditModel.PERIOD]
|
||||||
|
mts_opening = raw_credit[FundingCreditModel.MTS_OPENING]
|
||||||
|
mts_last_payout = raw_credit[FundingCreditModel.MTS_LAST_PAYOUT]
|
||||||
|
notify = raw_credit[FundingCreditModel.NOTIFY]
|
||||||
|
hidden = raw_credit[FundingCreditModel.HIDDEN]
|
||||||
|
renew = raw_credit[FundingCreditModel.RENEW]
|
||||||
|
no_close = raw_credit[FundingCreditModel.NO_CLOSE]
|
||||||
|
position_pair = raw_credit[FundingCreditModel.POSITION_PAIR]
|
||||||
|
return FundingCredit(fid, symbol, side, mts_create, mts_update, amount,
|
||||||
|
flags, status, rate, period, mts_opening, mts_last_payout,
|
||||||
|
notify, hidden, renew, no_close, position_pair)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
string = "FundingCredit '{}' <id={} rate={} amount={} period={} status='{}'>"
|
||||||
|
return string.format(self.symbol, self.id, self.rate, self.amount,
|
||||||
|
self.period, self.status)
|
||||||
96
bfxapi/models/funding_loan.py
Normal file
96
bfxapi/models/funding_loan.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
"""
|
||||||
|
Module used to describe all of the different data types
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class FundingLoanModel:
|
||||||
|
"""
|
||||||
|
Enum used to index the location of each value in a raw array
|
||||||
|
"""
|
||||||
|
ID = 0
|
||||||
|
SYMBOL = 1
|
||||||
|
SIDE = 2
|
||||||
|
MTS_CREATE = 3
|
||||||
|
MTS_UPDATE = 4
|
||||||
|
AMOUNT = 5
|
||||||
|
FLAGS = 6
|
||||||
|
STATUS = 7
|
||||||
|
RATE = 11
|
||||||
|
PERIOD = 12
|
||||||
|
MTS_OPENING = 13
|
||||||
|
MTS_LAST_PAYOUT = 14
|
||||||
|
NOTIFY = 15
|
||||||
|
HIDDEN = 16
|
||||||
|
RENEW = 18
|
||||||
|
NO_CLOSE = 20
|
||||||
|
|
||||||
|
|
||||||
|
class FundingLoan:
|
||||||
|
"""
|
||||||
|
ID integer Offer ID
|
||||||
|
SYMBOL string The currency of the offer (fUSD, etc)
|
||||||
|
SIDE string "Lend" or "Loan"
|
||||||
|
MTS_CREATE int Millisecond Time Stamp when the offer was created
|
||||||
|
MTS_UPDATE int Millisecond Time Stamp when the offer was created
|
||||||
|
AMOUNT float Amount the offer is for
|
||||||
|
FLAGS object future params object (stay tuned)
|
||||||
|
STATUS string Offer Status: ACTIVE, EXECUTED, PARTIALLY FILLED, CANCELED
|
||||||
|
RATE float Rate of the offer
|
||||||
|
PERIOD int Period of the offer
|
||||||
|
MTS_OPENING int Millisecond Time Stamp for when the loan was opened
|
||||||
|
MTS_LAST_PAYOUT int Millisecond Time Stamp for when the last payout was made
|
||||||
|
NOTIFY int 0 if false, 1 if true
|
||||||
|
HIDDEN int 0 if false, 1 if true
|
||||||
|
RENEW int 0 if false, 1 if true
|
||||||
|
NO_CLOSE int If funding will be returned when position is closed. 0 if false, 1 if true
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, fid, symbol, side, mts_create, mts_update, amount, flags, status, rate,
|
||||||
|
period, mts_opening, mts_last_payout, notify, hidden, renew, no_close):
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
self.id = fid
|
||||||
|
self.symbol = symbol
|
||||||
|
self.side = side
|
||||||
|
self.mts_create = mts_create
|
||||||
|
self.mts_update = mts_update
|
||||||
|
self.amount = amount
|
||||||
|
self.flags = flags
|
||||||
|
self.status = status
|
||||||
|
self.rate = rate
|
||||||
|
self.period = period
|
||||||
|
self.mts_opening = mts_opening
|
||||||
|
self.mts_last_payout = mts_last_payout
|
||||||
|
self.notify = notify
|
||||||
|
self.hidden = hidden
|
||||||
|
self.renew = renew
|
||||||
|
self.no_close = no_close
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_raw_loan(raw_loan):
|
||||||
|
"""
|
||||||
|
Parse a raw funding load into a FundingLoan object
|
||||||
|
|
||||||
|
@return FundingLoan
|
||||||
|
"""
|
||||||
|
fid = raw_loan[FundingLoanModel.ID]
|
||||||
|
symbol = raw_loan[FundingLoanModel.SYMBOL]
|
||||||
|
side = raw_loan[FundingLoanModel.SIDE]
|
||||||
|
mts_create = raw_loan[FundingLoanModel.MTS_CREATE]
|
||||||
|
mts_update = raw_loan[FundingLoanModel.MTS_UPDATE]
|
||||||
|
amount = raw_loan[FundingLoanModel.AMOUNT]
|
||||||
|
flags = raw_loan[FundingLoanModel.FLAGS]
|
||||||
|
status = raw_loan[FundingLoanModel.STATUS]
|
||||||
|
rate = raw_loan[FundingLoanModel.RATE]
|
||||||
|
period = raw_loan[FundingLoanModel.PERIOD]
|
||||||
|
mts_opening = raw_loan[FundingLoanModel.MTS_OPENING]
|
||||||
|
mts_last_payout = raw_loan[FundingLoanModel.MTS_LAST_PAYOUT]
|
||||||
|
notify = raw_loan[FundingLoanModel.NOTIFY]
|
||||||
|
hidden = raw_loan[FundingLoanModel.HIDDEN]
|
||||||
|
renew = raw_loan[FundingLoanModel.RENEW]
|
||||||
|
no_close = raw_loan[FundingLoanModel.NO_CLOSE]
|
||||||
|
return FundingLoan(fid, symbol, side, mts_create, mts_update, amount, flags, status, rate,
|
||||||
|
period, mts_opening, mts_last_payout, notify, hidden, renew, no_close)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "FundingLoan '{}' <id={} rate={} amount={} period={} status='{}'>".format(
|
||||||
|
self.symbol, self.id, self.rate, self.amount, self.period, self.status)
|
||||||
88
bfxapi/models/funding_offer.py
Normal file
88
bfxapi/models/funding_offer.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
"""
|
||||||
|
Module used to describe all of the different data types
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class FundingOfferModel:
|
||||||
|
"""
|
||||||
|
Enum used to index the location of each value in a raw array
|
||||||
|
"""
|
||||||
|
ID = 0
|
||||||
|
SYMBOL = 1
|
||||||
|
MTS_CREATE = 2
|
||||||
|
MTS_UPDATED = 3
|
||||||
|
AMOUNT = 4
|
||||||
|
AMOUNT_ORIG = 5
|
||||||
|
TYPE = 6
|
||||||
|
FLAGS = 9
|
||||||
|
STATUS = 10
|
||||||
|
RATE = 14
|
||||||
|
PERIOD = 15
|
||||||
|
NOTFIY = 16
|
||||||
|
HIDDEN = 17
|
||||||
|
RENEW = 19
|
||||||
|
|
||||||
|
|
||||||
|
class FundingOffer:
|
||||||
|
"""
|
||||||
|
ID integer Offer ID
|
||||||
|
SYMBOL string The currency of the offer (fUSD, etc)
|
||||||
|
MTS_CREATED int Millisecond Time Stamp when the offer was created
|
||||||
|
MSG_UPDATED int Millisecond Time Stamp when the offer was created
|
||||||
|
AMOUNT float Amount the offer is for
|
||||||
|
AMOUNT_ORIG float Amount the offer was entered with originally
|
||||||
|
TYPE string "lend" or "loan"
|
||||||
|
FLAGS object future params object (stay tuned)
|
||||||
|
STATUS string Offer Status: ACTIVE, EXECUTED, PARTIALLY FILLED, CANCELED
|
||||||
|
RATE float Rate of the offer
|
||||||
|
PERIOD int Period of the offer
|
||||||
|
NOTIFY int 0 if false, 1 if true
|
||||||
|
HIDDEN int 0 if false, 1 if true
|
||||||
|
RENEW int 0 if false, 1 if true
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, fid, symbol, mts_create, mts_updated, amount, amount_orig, f_type,
|
||||||
|
flags, status, rate, period, notify, hidden, renew):
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
self.id = fid
|
||||||
|
self.symbol = symbol
|
||||||
|
self.mts_create = mts_create
|
||||||
|
self.mts_updated = mts_updated
|
||||||
|
self.amount = amount
|
||||||
|
self.amount_orig = amount_orig
|
||||||
|
self.f_type = f_type
|
||||||
|
self.flags = flags
|
||||||
|
self.status = status
|
||||||
|
self.rate = rate
|
||||||
|
self.period = period
|
||||||
|
self.notify = notify
|
||||||
|
self.hidden = hidden
|
||||||
|
self.renew = renew
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_raw_offer(raw_offer):
|
||||||
|
"""
|
||||||
|
Parse a raw funding offer into a RawFunding object
|
||||||
|
|
||||||
|
@return FundingOffer
|
||||||
|
"""
|
||||||
|
oid = raw_offer[FundingOfferModel.ID]
|
||||||
|
symbol = raw_offer[FundingOfferModel.SYMBOL]
|
||||||
|
mts_create = raw_offer[FundingOfferModel.MTS_CREATE]
|
||||||
|
mts_updated = raw_offer[FundingOfferModel.MTS_UPDATED]
|
||||||
|
amount = raw_offer[FundingOfferModel.AMOUNT]
|
||||||
|
amount_orig = raw_offer[FundingOfferModel.AMOUNT_ORIG]
|
||||||
|
f_type = raw_offer[FundingOfferModel.TYPE]
|
||||||
|
flags = raw_offer[FundingOfferModel.FLAGS]
|
||||||
|
status = raw_offer[FundingOfferModel.STATUS]
|
||||||
|
rate = raw_offer[FundingOfferModel.RATE]
|
||||||
|
period = raw_offer[FundingOfferModel.PERIOD]
|
||||||
|
notify = raw_offer[FundingOfferModel.NOTFIY]
|
||||||
|
hidden = raw_offer[FundingOfferModel.HIDDEN]
|
||||||
|
renew = raw_offer[FundingOfferModel.RENEW]
|
||||||
|
return FundingOffer(oid, symbol, mts_create, mts_updated, amount,
|
||||||
|
amount_orig, f_type, flags, status, rate, period, notify, hidden, renew)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "FundingOffer '{}' <id={} rate={} period={} status='{}'>".format(
|
||||||
|
self.symbol, self.id, self.rate, self.period, self.status)
|
||||||
221
bfxapi/models/order.py
Normal file
221
bfxapi/models/order.py
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
"""
|
||||||
|
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
|
||||||
|
PRIVE_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):
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
self.id = oid
|
||||||
|
self.gid = gid
|
||||||
|
self.cid = cid
|
||||||
|
self.symbol = symbol
|
||||||
|
self.mts_create = mts_create
|
||||||
|
self.mts_update = mts_update
|
||||||
|
# self.amount = amount
|
||||||
|
self.amount = amount
|
||||||
|
self.amount_orig = amount_orig
|
||||||
|
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(amount)) * 0.001
|
||||||
|
else:
|
||||||
|
self.fee = (price_avg * abs(amount)) * 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.PRIVE_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 <'{}' mts_create={} status='{}' id={}>"
|
||||||
|
return text.format(self.symbol, self.mts_create, self.status, self.id)
|
||||||
121
bfxapi/models/order_book.py
Normal file
121
bfxapi/models/order_book.py
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
"""
|
||||||
|
Module used to describe all of the different data types
|
||||||
|
"""
|
||||||
|
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_price(price):
|
||||||
|
"""
|
||||||
|
Convert the price to an acceptable format
|
||||||
|
"""
|
||||||
|
# convert to 4 significant figures
|
||||||
|
prep_price = '{0:.4f}'.format(price)
|
||||||
|
# remove decimal place if zero float
|
||||||
|
return '{0:g}'.format(float(prep_price))
|
||||||
|
|
||||||
|
|
||||||
|
class OrderBook:
|
||||||
|
"""
|
||||||
|
Object used to store the state of the orderbook. This can then be used
|
||||||
|
in one of two ways. To get the checksum of the book or so get the bids/asks
|
||||||
|
of the book
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.asks = []
|
||||||
|
self.bids = []
|
||||||
|
|
||||||
|
def get_bids(self):
|
||||||
|
"""
|
||||||
|
Get all of the bids from the orderbook
|
||||||
|
|
||||||
|
@return bids Array
|
||||||
|
"""
|
||||||
|
return self.bids
|
||||||
|
|
||||||
|
def get_asks(self):
|
||||||
|
"""
|
||||||
|
Get all of the asks from the orderbook
|
||||||
|
|
||||||
|
@return asks Array
|
||||||
|
"""
|
||||||
|
return self.asks
|
||||||
|
|
||||||
|
def update_from_snapshot(self, data):
|
||||||
|
"""
|
||||||
|
Update the orderbook with a raw orderbook snapshot
|
||||||
|
"""
|
||||||
|
for order in data:
|
||||||
|
if len(order) == 4:
|
||||||
|
if order[3] < 0:
|
||||||
|
self.bids += [order]
|
||||||
|
else:
|
||||||
|
self.asks += [order]
|
||||||
|
else:
|
||||||
|
if order[2] < 0:
|
||||||
|
self.asks += [order]
|
||||||
|
else:
|
||||||
|
self.bids += [order]
|
||||||
|
|
||||||
|
def update_with(self, order):
|
||||||
|
"""
|
||||||
|
Update the orderbook with a single update
|
||||||
|
"""
|
||||||
|
if len(order) == 4:
|
||||||
|
amount = order[3]
|
||||||
|
count = order[2]
|
||||||
|
side = self.bids if amount < 0 else self.asks
|
||||||
|
else:
|
||||||
|
amount = order[2]
|
||||||
|
side = self.asks if amount < 0 else self.bids
|
||||||
|
count = order[1]
|
||||||
|
price = order[0]
|
||||||
|
|
||||||
|
# if first item in ordebook
|
||||||
|
if len(side) == 0:
|
||||||
|
side += [order]
|
||||||
|
return
|
||||||
|
|
||||||
|
# match price level
|
||||||
|
for index, s_order in enumerate(side):
|
||||||
|
s_price = s_order[0]
|
||||||
|
if s_price == price:
|
||||||
|
if count == 0:
|
||||||
|
del side[index]
|
||||||
|
return
|
||||||
|
# remove but add as new below
|
||||||
|
del side[index]
|
||||||
|
|
||||||
|
# if ob is initialised w/o all price levels
|
||||||
|
if count == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
# add to book and sort lowest to highest
|
||||||
|
side += [order]
|
||||||
|
side.sort(key=lambda x: x[0], reverse=not amount < 0)
|
||||||
|
return
|
||||||
|
|
||||||
|
def checksum(self):
|
||||||
|
"""
|
||||||
|
Generate a CRC32 checksum of the orderbook
|
||||||
|
"""
|
||||||
|
data = []
|
||||||
|
# take set of top 25 bids/asks
|
||||||
|
for index in range(0, 25):
|
||||||
|
if index < len(self.bids):
|
||||||
|
bid = self.bids[index]
|
||||||
|
price = bid[0]
|
||||||
|
amount = bid[3] if len(bid) == 4 else bid[2]
|
||||||
|
data += [prepare_price(price)]
|
||||||
|
data += [str(amount)]
|
||||||
|
if index < len(self.asks):
|
||||||
|
ask = self.asks[index]
|
||||||
|
price = ask[0]
|
||||||
|
amount = ask[3] if len(ask) == 4 else ask[2]
|
||||||
|
data += [prepare_price(price)]
|
||||||
|
data += [str(amount)]
|
||||||
|
checksum_str = ':'.join(data)
|
||||||
|
# calculate checksum and force signed integer
|
||||||
|
checksum = zlib.crc32(checksum_str.encode('utf8')) & 0xffffffff
|
||||||
|
return checksum
|
||||||
47
bfxapi/models/position.py
Normal file
47
bfxapi/models/position.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
"""
|
||||||
|
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)
|
||||||
74
bfxapi/models/subscription.py
Normal file
74
bfxapi/models/subscription.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
"""
|
||||||
|
Module used to describe all of the different data types
|
||||||
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
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, ws, channel_name, symbol, timeframe=None, **kwargs):
|
||||||
|
self._ws = ws
|
||||||
|
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 = int(round(time.time() * 1000))
|
||||||
|
self.send_payload = self._generate_payload(**kwargs)
|
||||||
|
|
||||||
|
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', 'chan_id': self.chan_id}
|
||||||
|
await self._ws.send(json.dumps(payload))
|
||||||
|
|
||||||
|
async def subscribe(self):
|
||||||
|
"""
|
||||||
|
Send a subscription request to the bitfinex socket
|
||||||
|
"""
|
||||||
|
await self._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
|
||||||
54
bfxapi/models/trade.py
Normal file
54
bfxapi/models/trade.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
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)
|
||||||
33
bfxapi/models/wallet.py
Normal file
33
bfxapi/models/wallet.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
"""
|
||||||
|
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)
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
"""
|
||||||
|
This module contains the BFX rest client data types
|
||||||
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import time
|
import time
|
||||||
@@ -8,7 +12,14 @@ 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
|
||||||
|
|
||||||
|
|
||||||
class BfxRest:
|
class BfxRest:
|
||||||
|
"""
|
||||||
|
BFX rest interface contains functions which are used to interact with both the public
|
||||||
|
and private Bitfinex http api's.
|
||||||
|
To use the private api you have to set the API_KEY and API_SECRET variables to your
|
||||||
|
api key.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, API_KEY, API_SECRET, host='https://api.bitfinex.com/v2', loop=None,
|
def __init__(self, API_KEY, API_SECRET, host='https://api.bitfinex.com/v2', loop=None,
|
||||||
logLevel='INFO', *args, **kwargs):
|
logLevel='INFO', *args, **kwargs):
|
||||||
@@ -19,6 +30,11 @@ class BfxRest:
|
|||||||
self.logger = CustomLogger('BfxRest', logLevel=logLevel)
|
self.logger = CustomLogger('BfxRest', logLevel=logLevel)
|
||||||
|
|
||||||
async def fetch(self, endpoint, params=""):
|
async def fetch(self, endpoint, params=""):
|
||||||
|
"""
|
||||||
|
Fetch a GET request from the bitfinex host
|
||||||
|
|
||||||
|
@return reponse
|
||||||
|
"""
|
||||||
url = '{}/{}{}'.format(self.host, endpoint, params)
|
url = '{}/{}{}'.format(self.host, endpoint, params)
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with session.get(url) as resp:
|
async with session.get(url) as resp:
|
||||||
@@ -29,6 +45,11 @@ class BfxRest:
|
|||||||
return await resp.json()
|
return await resp.json()
|
||||||
|
|
||||||
async def post(self, endpoint, data={}, params=""):
|
async def post(self, endpoint, data={}, params=""):
|
||||||
|
"""
|
||||||
|
Request a POST to the bitfinex host
|
||||||
|
|
||||||
|
@return response
|
||||||
|
"""
|
||||||
url = '{}/{}'.format(self.host, endpoint)
|
url = '{}/{}'.format(self.host, endpoint)
|
||||||
sData = json.dumps(data)
|
sData = json.dumps(data)
|
||||||
headers = generate_auth_headers(
|
headers = generate_auth_headers(
|
||||||
@@ -47,6 +68,9 @@ class BfxRest:
|
|||||||
##################################################
|
##################################################
|
||||||
|
|
||||||
async def get_seed_candles(self, symbol):
|
async def get_seed_candles(self, symbol):
|
||||||
|
"""
|
||||||
|
Used by the honey framework, this function gets the last 4k candles.
|
||||||
|
"""
|
||||||
endpoint = 'candles/trade:1m:{}/hist?limit=5000&_bfx=1'.format(symbol)
|
endpoint = 'candles/trade:1m:{}/hist?limit=5000&_bfx=1'.format(symbol)
|
||||||
time_difference = (1000 * 60) * 5000
|
time_difference = (1000 * 60) * 5000
|
||||||
# get now to the nearest min
|
# get now to the nearest min
|
||||||
@@ -77,12 +101,14 @@ class BfxRest:
|
|||||||
@param start int: millisecond start time
|
@param start int: millisecond start time
|
||||||
@param end int: millisecond end time
|
@param end int: millisecond end time
|
||||||
@param limit int: max number of items in response
|
@param limit int: max number of items in response
|
||||||
@param tf int: timeframe inbetween candles i.e 1m (min), ..., 1D (day), ... 1M (month)
|
@param tf int: timeframe inbetween candles i.e 1m (min), ..., 1D (day),
|
||||||
|
... 1M (month)
|
||||||
@param sort int: if = 1 it sorts results returned with old > new
|
@param sort int: if = 1 it sorts results returned with old > new
|
||||||
@return Array [ MTS, OPEN, CLOSE, HIGH, LOW, VOLUME ]
|
@return Array [ MTS, OPEN, CLOSE, HIGH, LOW, VOLUME ]
|
||||||
"""
|
"""
|
||||||
endpoint = "candles/trade:{}:{}/{}".format(tf, symbol, section)
|
endpoint = "candles/trade:{}:{}/{}".format(tf, symbol, section)
|
||||||
params = "?start={}&end={}&limit={}&sort={}".format(start, end, limit, sort)
|
params = "?start={}&end={}&limit={}&sort={}".format(
|
||||||
|
start, end, limit, sort)
|
||||||
candles = await self.fetch(endpoint, params=params)
|
candles = await self.fetch(endpoint, params=params)
|
||||||
return candles
|
return candles
|
||||||
|
|
||||||
@@ -97,7 +123,8 @@ class BfxRest:
|
|||||||
@return Array [ ID, MTS, AMOUNT, RATE, PERIOD? ]
|
@return Array [ ID, MTS, AMOUNT, RATE, PERIOD? ]
|
||||||
"""
|
"""
|
||||||
endpoint = "trades/{}/hist".format(symbol)
|
endpoint = "trades/{}/hist".format(symbol)
|
||||||
params = "?start={}&end={}&limit={}&sort={}".format(start, end, limit, sort)
|
params = "?start={}&end={}&limit={}&sort={}".format(
|
||||||
|
start, end, limit, sort)
|
||||||
trades = await self.fetch(endpoint, params=params)
|
trades = await self.fetch(endpoint, params=params)
|
||||||
return trades
|
return trades
|
||||||
|
|
||||||
@@ -121,8 +148,8 @@ class BfxRest:
|
|||||||
as well as the last trade price.
|
as well as the last trade price.
|
||||||
|
|
||||||
@parms symbols symbol string: pair symbol i.e tBTCUSD
|
@parms symbols symbol string: pair symbol i.e tBTCUSD
|
||||||
@return Array [ SYMBOL, BID, BID_SIZE, ASK, ASK_SIZE, DAILY_CHANGE, DAILY_CHANGE_PERC,
|
@return Array [ SYMBOL, BID, BID_SIZE, ASK, ASK_SIZE, DAILY_CHANGE,
|
||||||
LAST_PRICE, VOLUME, HIGH, LOW ]
|
DAILY_CHANGE_PERC, LAST_PRICE, VOLUME, HIGH, LOW ]
|
||||||
"""
|
"""
|
||||||
endpoint = "ticker/{}".format(symbol)
|
endpoint = "ticker/{}".format(symbol)
|
||||||
ticker = await self.fetch(endpoint)
|
ticker = await self.fetch(endpoint)
|
||||||
@@ -178,7 +205,8 @@ class BfxRest:
|
|||||||
@return Array <models.Order>
|
@return Array <models.Order>
|
||||||
"""
|
"""
|
||||||
endpoint = "auth/r/orders/{}/hist".format(symbol)
|
endpoint = "auth/r/orders/{}/hist".format(symbol)
|
||||||
params = "?start={}&end={}&limit={}&sort={}".format(start, end, limit, sort)
|
params = "?start={}&end={}&limit={}&sort={}".format(
|
||||||
|
start, end, limit, sort)
|
||||||
raw_orders = await self.post(endpoint, params=params)
|
raw_orders = await self.post(endpoint, params=params)
|
||||||
return [Order.from_raw_order(ro) for ro in raw_orders]
|
return [Order.from_raw_order(ro) for ro in raw_orders]
|
||||||
|
|
||||||
@@ -299,8 +327,8 @@ class BfxRest:
|
|||||||
##################################################
|
##################################################
|
||||||
|
|
||||||
async def __submit_order(self, symbol, amount, price, oType=Order.Type.LIMIT,
|
async def __submit_order(self, symbol, amount, price, oType=Order.Type.LIMIT,
|
||||||
is_hidden=False, is_postonly=False, use_all_available=False, stop_order=False,
|
is_hidden=False, is_postonly=False, use_all_available=False,
|
||||||
stop_buy_price=0, stop_sell_price=0):
|
stop_order=False, stop_buy_price=0, stop_sell_price=0):
|
||||||
"""
|
"""
|
||||||
Submit a new order
|
Submit a new order
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Module used to describe all of the different data types
|
||||||
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
RESET_SEQ = "\033[0m"
|
RESET_SEQ = "\033[0m"
|
||||||
@@ -22,6 +26,9 @@ KEYWORD_COLORS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def formatter_message(message, use_color = True):
|
def formatter_message(message, use_color = True):
|
||||||
|
"""
|
||||||
|
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:
|
||||||
@@ -29,6 +36,9 @@ def formatter_message(message, use_color = True):
|
|||||||
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
|
||||||
|
"""
|
||||||
replacer = color_seq + word + RESET_SEQ
|
replacer = color_seq + word + RESET_SEQ
|
||||||
if underline:
|
if underline:
|
||||||
replacer = UNDERLINE_SEQ + replacer
|
replacer = UNDERLINE_SEQ + replacer
|
||||||
@@ -45,6 +55,9 @@ class Formatter(logging.Formatter):
|
|||||||
self.use_color = use_color
|
self.use_color = use_color
|
||||||
|
|
||||||
def format(self, record):
|
def format(self, record):
|
||||||
|
"""
|
||||||
|
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
|
||||||
@@ -71,6 +84,9 @@ class CustomLogger(logging.Logger):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def trade(self, message, *args, **kws):
|
def trade(self, message, *args, **kws):
|
||||||
|
"""
|
||||||
|
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)
|
||||||
|
|||||||
@@ -1,8 +1,18 @@
|
|||||||
|
"""
|
||||||
|
This module is used to house all of the functions which are used
|
||||||
|
to handle the http authentication of the client
|
||||||
|
"""
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
import time
|
import time
|
||||||
|
|
||||||
def generate_auth_payload(API_KEY, API_SECRET):
|
def generate_auth_payload(API_KEY, API_SECRET):
|
||||||
|
"""
|
||||||
|
Generate a signed payload
|
||||||
|
|
||||||
|
@return json Oject headers
|
||||||
|
"""
|
||||||
nonce = _gen_nonce()
|
nonce = _gen_nonce()
|
||||||
authMsg, sig = _gen_signature(API_KEY, API_SECRET, nonce)
|
authMsg, sig = _gen_signature(API_KEY, API_SECRET, nonce)
|
||||||
|
|
||||||
@@ -15,6 +25,9 @@ def generate_auth_payload(API_KEY, API_SECRET):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def generate_auth_headers(API_KEY, API_SECRET, path, body):
|
def generate_auth_headers(API_KEY, API_SECRET, path, body):
|
||||||
|
"""
|
||||||
|
Generate headers for a signed payload
|
||||||
|
"""
|
||||||
nonce = str(_gen_nonce())
|
nonce = str(_gen_nonce())
|
||||||
signature = "/api/v2/{}{}{}".format(path, nonce, body)
|
signature = "/api/v2/{}{}{}".format(path, nonce, body)
|
||||||
h = hmac.new(API_SECRET.encode('utf8'), signature.encode('utf8'), hashlib.sha384)
|
h = hmac.new(API_SECRET.encode('utf8'), signature.encode('utf8'), hashlib.sha384)
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Module used to house the bitfine websocket client
|
||||||
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
@@ -10,7 +14,12 @@ from .OrderManager 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
|
||||||
|
|
||||||
|
|
||||||
class Flags:
|
class Flags:
|
||||||
|
"""
|
||||||
|
Enum used to index the available flags used in the authentication
|
||||||
|
websocket packet
|
||||||
|
"""
|
||||||
DEC_S = 9
|
DEC_S = 9
|
||||||
TIME_S = 32
|
TIME_S = 32
|
||||||
TIMESTAMP = 32768
|
TIMESTAMP = 32768
|
||||||
@@ -25,6 +34,7 @@ class Flags:
|
|||||||
131072: 'CHECKSUM'
|
131072: 'CHECKSUM'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _parse_candle(cData, symbol, tf):
|
def _parse_candle(cData, symbol, tf):
|
||||||
return {
|
return {
|
||||||
'mts': cData[0],
|
'mts': cData[0],
|
||||||
@@ -37,6 +47,7 @@ def _parse_candle(cData, symbol, tf):
|
|||||||
'tf': tf
|
'tf': tf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _parse_trade_snapshot_item(tData, symbol):
|
def _parse_trade_snapshot_item(tData, symbol):
|
||||||
return {
|
return {
|
||||||
'mts': tData[3],
|
'mts': tData[3],
|
||||||
@@ -54,10 +65,11 @@ def _parse_trade(tData, symbol):
|
|||||||
'symbol': symbol
|
'symbol': symbol
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class BfxWebsocket(GenericWebsocket):
|
class BfxWebsocket(GenericWebsocket):
|
||||||
"""
|
"""
|
||||||
More complex websocket that heavily relies on the btfxwss module. This websocket requires
|
More complex websocket that heavily relies on the btfxwss module.
|
||||||
authentication and is capable of handling orders.
|
This websocket requires authentication and is capable of handling orders.
|
||||||
https://github.com/Crypto-toolbox/btfxwss
|
https://github.com/Crypto-toolbox/btfxwss
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -85,8 +97,8 @@ class BfxWebsocket(GenericWebsocket):
|
|||||||
20061: 'Websocket server resync complete'
|
20061: 'Websocket server resync complete'
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, API_KEY=None, API_SECRET=None, host='wss://api.bitfinex.com/ws/2', manageOrderBooks=False,
|
def __init__(self, API_KEY=None, API_SECRET=None, host='wss://api.bitfinex.com/ws/2',
|
||||||
dead_man_switch=False, logLevel='INFO', *args, **kwargs):
|
manageOrderBooks=False, dead_man_switch=False, logLevel='INFO', *args, **kwargs):
|
||||||
self.API_KEY = API_KEY
|
self.API_KEY = API_KEY
|
||||||
self.API_SECRET = API_SECRET
|
self.API_SECRET = API_SECRET
|
||||||
self.manageOrderBooks = manageOrderBooks
|
self.manageOrderBooks = manageOrderBooks
|
||||||
@@ -94,7 +106,8 @@ class BfxWebsocket(GenericWebsocket):
|
|||||||
self.pendingOrders = {}
|
self.pendingOrders = {}
|
||||||
self.orderBooks = {}
|
self.orderBooks = {}
|
||||||
|
|
||||||
super(BfxWebsocket, self).__init__(host, logLevel=logLevel, *args, **kwargs)
|
super(BfxWebsocket, self).__init__(
|
||||||
|
host, logLevel=logLevel, *args, **kwargs)
|
||||||
self.subscriptionManager = SubscriptionManager(self, logLevel=logLevel)
|
self.subscriptionManager = SubscriptionManager(self, logLevel=logLevel)
|
||||||
self.orderManager = OrderManager(self, logLevel=logLevel)
|
self.orderManager = OrderManager(self, logLevel=logLevel)
|
||||||
self.wallets = WalletManager()
|
self.wallets = WalletManager()
|
||||||
@@ -133,16 +146,17 @@ class BfxWebsocket(GenericWebsocket):
|
|||||||
if eType in self._WS_SYSTEM_HANDLERS:
|
if eType in self._WS_SYSTEM_HANDLERS:
|
||||||
await self._WS_SYSTEM_HANDLERS[eType](msg)
|
await self._WS_SYSTEM_HANDLERS[eType](msg)
|
||||||
else:
|
else:
|
||||||
self.logger.warn("Unknown websocket event: '{}' {}".format(eType, msg))
|
self.logger.warn(
|
||||||
|
"Unknown websocket event: '{}' {}".format(eType, msg))
|
||||||
|
|
||||||
async def _ws_data_handler(self, data):
|
async def _ws_data_handler(self, data):
|
||||||
dataEvent = data[1]
|
dataEvent = data[1]
|
||||||
chanId = data[0]
|
chan_id = data[0]
|
||||||
|
|
||||||
if type(dataEvent) is str and dataEvent in self._WS_DATA_HANDLERS:
|
if type(dataEvent) is str and dataEvent in self._WS_DATA_HANDLERS:
|
||||||
return await self._WS_DATA_HANDLERS[dataEvent](data)
|
return await self._WS_DATA_HANDLERS[dataEvent](data)
|
||||||
elif self.subscriptionManager.is_subscribed(chanId):
|
elif self.subscriptionManager.is_subscribed(chan_id):
|
||||||
subscription = self.subscriptionManager.get(chanId)
|
subscription = self.subscriptionManager.get(chan_id)
|
||||||
# candles do not have an event
|
# candles do not have an event
|
||||||
if subscription.channel_name == 'candles':
|
if subscription.channel_name == 'candles':
|
||||||
await self._candle_handler(data)
|
await self._candle_handler(data)
|
||||||
@@ -151,12 +165,13 @@ class BfxWebsocket(GenericWebsocket):
|
|||||||
if subscription.channel_name == 'trades':
|
if subscription.channel_name == 'trades':
|
||||||
await self._trade_handler(data)
|
await self._trade_handler(data)
|
||||||
else:
|
else:
|
||||||
self.logger.warn("Unknown data event: '{}' {}".format(dataEvent, data))
|
self.logger.warn(
|
||||||
|
"Unknown data event: '{}' {}".format(dataEvent, data))
|
||||||
|
|
||||||
async def _system_info_handler(self, data):
|
async def _system_info_handler(self, data):
|
||||||
self.logger.info(data)
|
self.logger.info(data)
|
||||||
if data.get('serverId', None):
|
if data.get('serverId', None):
|
||||||
## connection has been established
|
# connection has been established
|
||||||
await self.on_open()
|
await self.on_open()
|
||||||
|
|
||||||
async def _system_conf_handler(self, data):
|
async def _system_conf_handler(self, data):
|
||||||
@@ -169,7 +184,8 @@ class BfxWebsocket(GenericWebsocket):
|
|||||||
if status == "OK":
|
if status == "OK":
|
||||||
self.logger.info("Enabled config flag {}".format(flagString))
|
self.logger.info("Enabled config flag {}".format(flagString))
|
||||||
else:
|
else:
|
||||||
self.logger.error("Unable to enable config flag {}".format(flagString))
|
self.logger.error(
|
||||||
|
"Unable to enable config flag {}".format(flagString))
|
||||||
|
|
||||||
async def _system_subscribed_handler(self, data):
|
async def _system_subscribed_handler(self, data):
|
||||||
await self.subscriptionManager.confirm_subscription(data)
|
await self.subscriptionManager.confirm_subscription(data)
|
||||||
@@ -221,19 +237,17 @@ class BfxWebsocket(GenericWebsocket):
|
|||||||
self.logger.info("Funding info update: {}".format(data))
|
self.logger.info("Funding info update: {}".format(data))
|
||||||
|
|
||||||
async def _notification_handler(self, 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]
|
nInfo = data[2]
|
||||||
self._emit('notification', nInfo)
|
self._emit('notification', nInfo)
|
||||||
notificationType = nInfo[6]
|
notificationType = nInfo[6]
|
||||||
notificationText = nInfo[7]
|
notificationText = nInfo[7]
|
||||||
if notificationType == 'ERROR':
|
if notificationType == 'ERROR':
|
||||||
# self._emit('error', notificationText)
|
# self._emit('error', notificationText)
|
||||||
self.logger.error("Notification ERROR: {}".format(notificationText))
|
self.logger.error(
|
||||||
|
"Notification ERROR: {}".format(notificationText))
|
||||||
else:
|
else:
|
||||||
self.logger.info("Notification SUCCESS: {}".format(notificationText))
|
self.logger.info(
|
||||||
|
"Notification SUCCESS: {}".format(notificationText))
|
||||||
|
|
||||||
async def _balance_update_handler(self, data):
|
async def _balance_update_handler(self, data):
|
||||||
self.logger.info('Balance update: {}'.format(data[2]))
|
self.logger.info('Balance update: {}'.format(data[2]))
|
||||||
@@ -295,15 +309,17 @@ class BfxWebsocket(GenericWebsocket):
|
|||||||
candlesSnapshot = data[1]
|
candlesSnapshot = data[1]
|
||||||
candlesSnapshot.reverse()
|
candlesSnapshot.reverse()
|
||||||
for c in candlesSnapshot:
|
for c in candlesSnapshot:
|
||||||
candle = _parse_candle(c, subscription.symbol, subscription.timeframe)
|
candle = _parse_candle(
|
||||||
|
c, subscription.symbol, subscription.timeframe)
|
||||||
self._emit('seed_candle', candle)
|
self._emit('seed_candle', candle)
|
||||||
else:
|
else:
|
||||||
candle = _parse_candle(data[1], subscription.symbol, subscription.timeframe)
|
candle = _parse_candle(
|
||||||
|
data[1], subscription.symbol, subscription.timeframe)
|
||||||
self._emit('new_candle', candle)
|
self._emit('new_candle', candle)
|
||||||
|
|
||||||
async def _order_book_handler(self, data):
|
async def _order_book_handler(self, data):
|
||||||
obInfo = data[1]
|
obInfo = data[1]
|
||||||
chanId = data[0]
|
chan_id = data[0]
|
||||||
subscription = self.subscriptionManager.get(data[0])
|
subscription = self.subscriptionManager.get(data[0])
|
||||||
symbol = subscription.symbol
|
symbol = subscription.symbol
|
||||||
if data[1] == "cs":
|
if data[1] == "cs":
|
||||||
@@ -312,13 +328,13 @@ class BfxWebsocket(GenericWebsocket):
|
|||||||
# force checksums to signed integers
|
# force checksums to signed integers
|
||||||
isValid = (dChecksum) == (checksum)
|
isValid = (dChecksum) == (checksum)
|
||||||
if isValid:
|
if isValid:
|
||||||
self.logger.debug("Checksum orderbook validation for '{}' successful."
|
msg = "Checksum orderbook validation for '{}' successful."
|
||||||
.format(symbol))
|
self.logger.debug(msg.format(symbol))
|
||||||
else:
|
else:
|
||||||
self.logger.warn("Checksum orderbook invalid for '{}'. Resetting subscription."
|
msg = "Checksum orderbook invalid for '{}'. Resetting subscription."
|
||||||
.format(symbol))
|
self.logger.warn(msg.format(symbol))
|
||||||
# re-build orderbook with snapshot
|
# re-build orderbook with snapshot
|
||||||
await self.subscriptionManager.resubscribe(chanId)
|
await self.subscriptionManager.resubscribe(chan_id)
|
||||||
return
|
return
|
||||||
if obInfo == []:
|
if obInfo == []:
|
||||||
self.orderBooks[symbol] = OrderBook()
|
self.orderBooks[symbol] = OrderBook()
|
||||||
@@ -326,10 +342,11 @@ class BfxWebsocket(GenericWebsocket):
|
|||||||
isSnapshot = type(obInfo[0]) is list
|
isSnapshot = type(obInfo[0]) is list
|
||||||
if isSnapshot:
|
if isSnapshot:
|
||||||
self.orderBooks[symbol] = OrderBook()
|
self.orderBooks[symbol] = OrderBook()
|
||||||
self.orderBooks[symbol].updateFromSnapshot(obInfo)
|
self.orderBooks[symbol].update_from_snapshot(obInfo)
|
||||||
self._emit('order_book_snapshot', { 'symbol': symbol, 'data': obInfo })
|
self._emit('order_book_snapshot', {
|
||||||
|
'symbol': symbol, 'data': obInfo})
|
||||||
else:
|
else:
|
||||||
self.orderBooks[symbol].updateWith(obInfo)
|
self.orderBooks[symbol].update_with(obInfo)
|
||||||
self._emit('order_book_update', {'symbol': symbol, 'data': obInfo})
|
self._emit('order_book_update', {'symbol': symbol, 'data': obInfo})
|
||||||
|
|
||||||
async def on_message(self, message):
|
async def on_message(self, message):
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
"""
|
||||||
|
Module used as a interfeace to describe a generick websocket client
|
||||||
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import websockets
|
import websockets
|
||||||
import json
|
import json
|
||||||
@@ -5,7 +9,13 @@ import json
|
|||||||
from pyee import EventEmitter
|
from pyee import EventEmitter
|
||||||
from ..utils.CustomLogger import CustomLogger
|
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):
|
def is_json(myjson):
|
||||||
try:
|
try:
|
||||||
@@ -14,19 +24,31 @@ def is_json(myjson):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
class GenericWebsocket(object):
|
|
||||||
|
class GenericWebsocket:
|
||||||
|
"""
|
||||||
|
Websocket object used to contain the base functionality of a websocket.
|
||||||
|
Inlcudes an event emitter and a standard websocket client.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, host, logLevel='INFO', loop=None):
|
def __init__(self, host, logLevel='INFO', loop=None):
|
||||||
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()
|
||||||
self.events = EventEmitter(scheduler=asyncio.ensure_future, loop=self.loop)
|
self.events = EventEmitter(
|
||||||
|
scheduler=asyncio.ensure_future, loop=self.loop)
|
||||||
self.ws = None
|
self.ws = None
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
"""
|
||||||
|
Run the websocket connection indefinitely
|
||||||
|
"""
|
||||||
self.loop.run_until_complete(self._main(self.host))
|
self.loop.run_until_complete(self._main(self.host))
|
||||||
|
|
||||||
def get_task_executable(self):
|
def get_task_executable(self):
|
||||||
|
"""
|
||||||
|
Get the run indefinitely asyncio task
|
||||||
|
"""
|
||||||
return self._main(self.host)
|
return self._main(self.host)
|
||||||
|
|
||||||
async def _main(self, host):
|
async def _main(self, host):
|
||||||
@@ -39,14 +61,24 @@ class GenericWebsocket(object):
|
|||||||
await self.on_message(message)
|
await self.on_message(message)
|
||||||
|
|
||||||
def remove_all_listeners(self, event):
|
def remove_all_listeners(self, event):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
"""
|
||||||
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
|
||||||
|
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)
|
||||||
@@ -55,16 +87,28 @@ class GenericWebsocket(object):
|
|||||||
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
|
||||||
|
"""
|
||||||
self.logger.error(error)
|
self.logger.error(error)
|
||||||
self.events.emit('error', error)
|
self.events.emit('error', error)
|
||||||
|
|
||||||
async def on_close(self):
|
async def on_close(self):
|
||||||
|
"""
|
||||||
|
On websocket close print and fire event
|
||||||
|
"""
|
||||||
self.logger.info("Websocket closed.")
|
self.logger.info("Websocket closed.")
|
||||||
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
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def on_message(self, message):
|
async def on_message(self, message):
|
||||||
|
"""
|
||||||
|
On websocket message
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
|
"""
|
||||||
|
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.CustomLogger import CustomLogger
|
||||||
from ..models import Order
|
from ..models import Order
|
||||||
|
|
||||||
|
|
||||||
class OrderManager:
|
class OrderManager:
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, bfxapi, logLevel='INFO'):
|
def __init__(self, bfxapi, logLevel='INFO'):
|
||||||
self.bfxapi = bfxapi
|
self.bfxapi = bfxapi
|
||||||
@@ -42,24 +52,16 @@ class OrderManager:
|
|||||||
await self._execute_close_callback(order.id, order)
|
await self._execute_close_callback(order.id, order)
|
||||||
|
|
||||||
async def confirm_order_closed(self, raw_ws_data):
|
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 = Order.from_raw_order(raw_ws_data[2])
|
||||||
order.set_open_state(False)
|
order.set_open_state(False)
|
||||||
if order.id in self.open_orders:
|
if order.id in self.open_orders:
|
||||||
del self.open_orders[order.id]
|
del self.open_orders[order.id]
|
||||||
await self._confirm_order(order, isClosed=True)
|
await self._confirm_order(order, isClosed=True)
|
||||||
self.logger.info("Order closed: {} {}".format(order.symbol, order.status))
|
self.logger.info("Order closed: {} {}".format(
|
||||||
|
order.symbol, order.status))
|
||||||
self.bfxapi._emit('order_closed', order)
|
self.bfxapi._emit('order_closed', order)
|
||||||
|
|
||||||
async def build_from_order_snapshot(self, raw_ws_data):
|
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
|
Rebuild the user orderbook based on an incoming snapshot
|
||||||
'''
|
'''
|
||||||
@@ -72,11 +74,6 @@ class OrderManager:
|
|||||||
self.bfxapi._emit('order_snapshot', self.get_open_orders())
|
self.bfxapi._emit('order_snapshot', self.get_open_orders())
|
||||||
|
|
||||||
async def confirm_order_update(self, raw_ws_data):
|
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 = Order.from_raw_order(raw_ws_data[2])
|
||||||
order.set_open_state(True)
|
order.set_open_state(True)
|
||||||
self.open_orders[order.id] = order
|
self.open_orders[order.id] = order
|
||||||
@@ -85,11 +82,6 @@ class OrderManager:
|
|||||||
self.bfxapi._emit('order_update', order)
|
self.bfxapi._emit('order_update', order)
|
||||||
|
|
||||||
async def confirm_order_new(self, raw_ws_data):
|
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 = Order.from_raw_order(raw_ws_data[2])
|
||||||
order.set_open_state(True)
|
order.set_open_state(True)
|
||||||
self.open_orders[order.id] = order
|
self.open_orders[order.id] = order
|
||||||
@@ -101,8 +93,9 @@ class OrderManager:
|
|||||||
return int(round(time.time() * 1000))
|
return int(round(time.time() * 1000))
|
||||||
|
|
||||||
async def submit_order(self, symbol, price, amount, market_type=Order.Type.LIMIT,
|
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,
|
hidden=False, price_trailing=None, price_aux_limit=None,
|
||||||
close=False, reduce_only=False, post_only=False, oco=False, time_in_force=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):
|
onConfirm=None, onClose=None, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Submit a new order
|
Submit a new order
|
||||||
@@ -140,7 +133,8 @@ class OrderManager:
|
|||||||
"price": str(price),
|
"price": str(price),
|
||||||
}
|
}
|
||||||
# caclulate and add flags
|
# caclulate and add flags
|
||||||
flags = self._calculate_flags(hidden, close, reduce_only, post_only, oco)
|
flags = self._calculate_flags(
|
||||||
|
hidden, close, reduce_only, post_only, oco)
|
||||||
payload['flags'] = flags
|
payload['flags'] = flags
|
||||||
# add extra parameters
|
# add extra parameters
|
||||||
if (price_trailing):
|
if (price_trailing):
|
||||||
@@ -159,8 +153,8 @@ class OrderManager:
|
|||||||
cId, symbol, amount, price))
|
cId, symbol, amount, price))
|
||||||
|
|
||||||
async def update_order(self, orderId, price=None, amount=None, delta=None, price_aux_limit=None,
|
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,
|
price_trailing=None, hidden=False, close=False, reduce_only=False,
|
||||||
time_in_force=None, onConfirm=None, onClose=None):
|
post_only=False, time_in_force=None, onConfirm=None, onClose=None):
|
||||||
"""
|
"""
|
||||||
Update an existing order
|
Update an existing order
|
||||||
|
|
||||||
@@ -197,7 +191,8 @@ class OrderManager:
|
|||||||
payload['price_trailing'] = str(price_trailing)
|
payload['price_trailing'] = str(price_trailing)
|
||||||
if time_in_force is not None:
|
if time_in_force is not None:
|
||||||
payload['time_in_force'] = str(time_in_force)
|
payload['time_in_force'] = str(time_in_force)
|
||||||
flags = self._calculate_flags(hidden, close, reduce_only, post_only, False)
|
flags = self._calculate_flags(
|
||||||
|
hidden, close, reduce_only, post_only, False)
|
||||||
payload['flags'] = flags
|
payload['flags'] = flags
|
||||||
await self.bfxapi._send_auth_command('ou', payload)
|
await self.bfxapi._send_auth_command('ou', payload)
|
||||||
self.logger.info("Update Order order_id={} dispatched".format(orderId))
|
self.logger.info("Update Order order_id={} dispatched".format(orderId))
|
||||||
@@ -207,7 +202,8 @@ class OrderManager:
|
|||||||
Cancel an existing open order
|
Cancel an existing open order
|
||||||
|
|
||||||
@param orderId: the id of the order that you want to update
|
@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
|
@param onConfirm: function called when the bitfinex websocket receives signal that the
|
||||||
|
order
|
||||||
was confirmed
|
was confirmed
|
||||||
@param onClose: function called when the bitfinex websocket receives signal that the order
|
@param onClose: function called when the bitfinex websocket receives signal that the order
|
||||||
was closed due to being filled or cancelled
|
was closed due to being filled or cancelled
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
"""
|
||||||
|
Module used to house all of the functions/classes used to handle
|
||||||
|
subscriptions
|
||||||
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
@@ -5,6 +10,7 @@ import time
|
|||||||
from ..utils.CustomLogger import CustomLogger
|
from ..utils.CustomLogger import CustomLogger
|
||||||
from ..models import Subscription
|
from ..models import Subscription
|
||||||
|
|
||||||
|
|
||||||
class SubscriptionManager:
|
class SubscriptionManager:
|
||||||
|
|
||||||
def __init__(self, bfxapi, logLevel='INFO'):
|
def __init__(self, bfxapi, logLevel='INFO'):
|
||||||
@@ -25,39 +31,37 @@ class SubscriptionManager:
|
|||||||
for the candles channel)
|
for the candles channel)
|
||||||
"""
|
"""
|
||||||
# create a new subscription
|
# create a new subscription
|
||||||
subscription = Subscription(self.bfxapi.ws, channel_name, symbol, timeframe, **kwargs)
|
subscription = Subscription(
|
||||||
|
self.bfxapi.ws, channel_name, symbol, timeframe, **kwargs)
|
||||||
self.logger.info("Subscribing to channel {}".format(channel_name))
|
self.logger.info("Subscribing to channel {}".format(channel_name))
|
||||||
key = "{}_{}".format(channel_name, subscription.key or symbol)
|
key = "{}_{}".format(channel_name, subscription.key or symbol)
|
||||||
self.pending_subscriptions[key] = subscription
|
self.pending_subscriptions[key] = subscription
|
||||||
await subscription.subscribe()
|
await subscription.subscribe()
|
||||||
|
|
||||||
async def confirm_subscription(self, raw_ws_data):
|
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)
|
symbol = raw_ws_data.get("symbol", None)
|
||||||
channel = raw_ws_data.get("channel")
|
channel = raw_ws_data.get("channel")
|
||||||
chanId = raw_ws_data.get("chanId")
|
chan_id = raw_ws_data.get("chanId")
|
||||||
key = raw_ws_data.get("key", None)
|
key = raw_ws_data.get("key", None)
|
||||||
get_key = "{}_{}".format(channel, key or symbol)
|
get_key = "{}_{}".format(channel, key or symbol)
|
||||||
|
|
||||||
if chanId in self.subscriptions_chanid:
|
if chan_id in self.subscriptions_chanid:
|
||||||
# subscription has already existed in the past
|
# subscription has already existed in the past
|
||||||
p_sub = self.subscriptions_chanid[chanId]
|
p_sub = self.subscriptions_chanid[chan_id]
|
||||||
else:
|
else:
|
||||||
# has just been created and is pending
|
# has just been created and is pending
|
||||||
p_sub = self.pending_subscriptions[get_key]
|
p_sub = self.pending_subscriptions[get_key]
|
||||||
# remove from pending list
|
# remove from pending list
|
||||||
del self.pending_subscriptions[get_key]
|
del self.pending_subscriptions[get_key]
|
||||||
p_sub.confirm_subscription(chanId)
|
p_sub.confirm_subscription(chan_id)
|
||||||
# add to confirmed list
|
# add to confirmed list
|
||||||
self.subscriptions_chanid[chanId] = p_sub
|
self.subscriptions_chanid[chan_id] = p_sub
|
||||||
self.subscriptions_subid[p_sub.sub_id] = p_sub
|
self.subscriptions_subid[p_sub.sub_id] = p_sub
|
||||||
self.bfxapi._emit('subscribed', p_sub)
|
self.bfxapi._emit('subscribed', p_sub)
|
||||||
|
|
||||||
async def confirm_unsubscribe(self, raw_ws_data):
|
async def confirm_unsubscribe(self, raw_ws_data):
|
||||||
chanId = raw_ws_data.get("chanId")
|
chan_id = raw_ws_data.get("chanId")
|
||||||
sub = self.subscriptions_chanid[chanId]
|
sub = self.subscriptions_chanid[chan_id]
|
||||||
sub.confirm_unsubscribe()
|
sub.confirm_unsubscribe()
|
||||||
self.bfxapi._emit('unsubscribed', sub)
|
self.bfxapi._emit('unsubscribed', sub)
|
||||||
# call onComplete callback if exists
|
# call onComplete callback if exists
|
||||||
@@ -65,56 +69,57 @@ class SubscriptionManager:
|
|||||||
await self.unsubscribe_callbacks[sub.sub_id]()
|
await self.unsubscribe_callbacks[sub.sub_id]()
|
||||||
del self.unsubscribe_callbacks[sub.sub_id]
|
del self.unsubscribe_callbacks[sub.sub_id]
|
||||||
|
|
||||||
def get(self, chanId):
|
def get(self, chan_id):
|
||||||
return self.subscriptions_chanid[chanId]
|
return self.subscriptions_chanid[chan_id]
|
||||||
|
|
||||||
async def unsubscribe(self, chanId, onComplete=None):
|
async def unsubscribe(self, chan_id, onComplete=None):
|
||||||
"""
|
"""
|
||||||
Unsubscribe from the channel with the given chanId
|
Unsubscribe from the channel with the given chanId
|
||||||
|
|
||||||
@param onComplete: function called when the bitfinex websocket resoponds with
|
@param onComplete: function called when the bitfinex websocket resoponds with
|
||||||
a signal that confirms the subscription has been unsubscribed to
|
a signal that confirms the subscription has been unsubscribed to
|
||||||
"""
|
"""
|
||||||
sub = self.subscriptions_chanid[chanId]
|
sub = self.subscriptions_chanid[chan_id]
|
||||||
if onComplete:
|
if onComplete:
|
||||||
self.unsubscribe_callbacks[sub.sub_id] = onComplete
|
self.unsubscribe_callbacks[sub.sub_id] = onComplete
|
||||||
if sub.is_subscribed():
|
if sub.is_subscribed():
|
||||||
await self.subscriptions_chanid[chanId].unsubscribe()
|
await self.subscriptions_chanid[chan_id].unsubscribe()
|
||||||
|
|
||||||
async def resubscribe(self, chanId):
|
async def resubscribe(self, chan_id):
|
||||||
"""
|
"""
|
||||||
Unsubscribes and then subscribes to the channel with the given 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.
|
This function is mostly used to force the channel to produce a fresh snapshot.
|
||||||
"""
|
"""
|
||||||
sub = self.subscriptions_chanid[chanId]
|
sub = self.subscriptions_chanid[chan_d]
|
||||||
|
|
||||||
async def re_sub():
|
async def re_sub():
|
||||||
await sub.subscribe()
|
await sub.subscribe()
|
||||||
if sub.is_subscribed():
|
if sub.is_subscribed():
|
||||||
# unsubscribe first and call callback to subscribe
|
# unsubscribe first and call callback to subscribe
|
||||||
await self.unsubscribe(chanId, re_sub)
|
await self.unsubscribe(chan_id, re_sub)
|
||||||
else:
|
else:
|
||||||
# already unsibscribed, so just subscribe
|
# already unsibscribed, so just subscribe
|
||||||
await sub.subscribe()
|
await sub.subscribe()
|
||||||
|
|
||||||
def is_subscribed(self, chanId):
|
def is_subscribed(self, chan_id):
|
||||||
"""
|
"""
|
||||||
Returns True if the channel with the given chanId is currenly subscribed to
|
Returns True if the channel with the given chanId is currenly subscribed to
|
||||||
"""
|
"""
|
||||||
if chanId not in self.subscriptions_chanid:
|
if chan_id not in self.subscriptions_chanid:
|
||||||
return False
|
return False
|
||||||
return self.subscriptions_chanid[chanId].is_subscribed()
|
return self.subscriptions_chanid[chan_id].is_subscribed()
|
||||||
|
|
||||||
async def unsubscribe_all(self):
|
async def unsubscribe_all(self):
|
||||||
"""
|
"""
|
||||||
Unsubscribe from all channels.
|
Unsubscribe from all channels.
|
||||||
"""
|
"""
|
||||||
task_batch = []
|
task_batch = []
|
||||||
for chanId in self.subscriptions_chanid:
|
for chan_id in self.subscriptions_chanid:
|
||||||
sub = self.get(chanId)
|
sub = self.get(chan_id)
|
||||||
if sub.is_subscribed():
|
if sub.is_subscribed():
|
||||||
task_batch += [
|
task_batch += [
|
||||||
asyncio.ensure_future(self.unsubscribe(chanId))
|
asyncio.ensure_future(self.unsubscribe(chan_id))
|
||||||
]
|
]
|
||||||
await asyncio.wait(*[task_batch])
|
await asyncio.wait(*[task_batch])
|
||||||
|
|
||||||
@@ -123,8 +128,8 @@ class SubscriptionManager:
|
|||||||
Unsubscribe and then subscribe to all channels
|
Unsubscribe and then subscribe to all channels
|
||||||
"""
|
"""
|
||||||
task_batch = []
|
task_batch = []
|
||||||
for chanId in self.subscriptions_chanid:
|
for chan_id in self.subscriptions_chanid:
|
||||||
task_batch += [
|
task_batch += [
|
||||||
asyncio.ensure_future(self.resubscribe(chanId))
|
asyncio.ensure_future(self.resubscribe(chan_id))
|
||||||
]
|
]
|
||||||
await asyncio.wait(*[task_batch])
|
await asyncio.wait(*[task_batch])
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
|
"""
|
||||||
|
Module used to handle wallet updates and data types
|
||||||
|
"""
|
||||||
|
|
||||||
from ..models import Wallet
|
from ..models import Wallet
|
||||||
|
|
||||||
|
|
||||||
class WalletManager:
|
class WalletManager:
|
||||||
|
"""
|
||||||
|
This class is used to interact with all of the different wallets
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.wallets = {}
|
self.wallets = {}
|
||||||
|
|
||||||
def _update_from_snapshot(self, raw_ws_data):
|
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]
|
wData = raw_ws_data[2]
|
||||||
self.wallets = {}
|
self.wallets = {}
|
||||||
for wallet in wData:
|
for wallet in wData:
|
||||||
@@ -16,7 +22,6 @@ class WalletManager:
|
|||||||
return self.get_wallets()
|
return self.get_wallets()
|
||||||
|
|
||||||
def _update_from_event(self, raw_ws_data):
|
def _update_from_event(self, raw_ws_data):
|
||||||
# [0,"wu",["exchange","USD",62761.86070104,0,61618.66070104]]
|
|
||||||
wallet = raw_ws_data[2]
|
wallet = raw_ws_data[2]
|
||||||
new_wallet = Wallet(wallet[0], wallet[1], wallet[2], wallet[3])
|
new_wallet = Wallet(wallet[0], wallet[1], wallet[2], wallet[3])
|
||||||
self.wallets[new_wallet.key] = new_wallet
|
self.wallets[new_wallet.key] = new_wallet
|
||||||
@@ -24,4 +29,3 @@ class WalletManager:
|
|||||||
|
|
||||||
def get_wallets(self):
|
def get_wallets(self):
|
||||||
return list(self.wallets.values())
|
return list(self.wallets.values())
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user