diff --git a/bfxapi/models/Order.py b/bfxapi/models/Order.py index 1da0a7c..e2a1532 100644 --- a/bfxapi/models/Order.py +++ b/bfxapi/models/Order.py @@ -1,4 +1,5 @@ import time +import datetime class OrderClosedModel: ID = 0 @@ -24,51 +25,59 @@ def now_in_mills(): return int(round(time.time() * 1000)) class Order: - def __init__(self, bfxapi, closingOrderArray): - self.bfxapi = bfxapi - self.id = closingOrderArray[OrderClosedModel.ID] - self.gId = closingOrderArray[OrderClosedModel.GID] - self.cId = closingOrderArray[OrderClosedModel.CID] - self.symbol = closingOrderArray[OrderClosedModel.SYMBOL] - self.mtsCreate = closingOrderArray[OrderClosedModel.MTS_CREATE] - self.mtsUpdate = closingOrderArray[OrderClosedModel.MTS_UPDATE] - self.amount = closingOrderArray[OrderClosedModel.AMOUNT] - self.amountOrig = closingOrderArray[OrderClosedModel.AMOUNT_ORIG] - self.type = closingOrderArray[OrderClosedModel.TYPE] - self.typePrev = closingOrderArray[OrderClosedModel.TYPE_PREV] - self.flags = closingOrderArray[OrderClosedModel.FLAGS] - self.status = closingOrderArray[OrderClosedModel.STATUS] - self.price = closingOrderArray[OrderClosedModel.PRICE] - self.priceAvg = closingOrderArray[OrderClosedModel.PRIVE_AVG] - self.priceTrailing = closingOrderArray[OrderClosedModel.PRICE_TRAILING] - self.priceAuxLimit = closingOrderArray[OrderClosedModel.PRICE_AUX_LIMIT] - self.notfiy = closingOrderArray[OrderClosedModel.NOTIFY] - self.placeId = closingOrderArray[OrderClosedModel.PLACE_ID] + def __init__(self, id, gId, cId, symbol, mtsCreate, mtsUpdate, amount, amountOrig, oType, + typePrev, flags, status, price, priceAvg, priceTrailing, priceAuxLimit, notfiy, placeId): + self.id = id + self.gId = gId + self.cId = cId + self.symbol = symbol + self.mtsCreate = mtsCreate + self.mtsUpdate = mtsUpdate + self.amount = amount + self.amountOrig = amountOrig + self.type = oType + self.typePrev = typePrev + self.flags = flags + self.status = status + self.price = price + self.priceAvg = priceAvg + self.priceTrailing = priceTrailing + self.priceAuxLimit = priceAuxLimit + self.notfiy = notfiy + self.placeId = placeId + self.is_pending_bool = True self.is_confirmed_bool = False self.is_open_bool = False - async def update(self, price=None, amount=None, delta=None, price_aux_limit=None, - price_trailing=None, flags=None, time_in_force=None): - payload = { "id": self.id } - if price is not None: - payload['price'] = str(price) - if amount is not None: - payload['amount'] = str(amount) - if delta is not None: - payload['delta'] = str(delta) - if price_aux_limit is not None: - payload['price_aux_limit'] = str(price_aux_limit) - if price_trailing is not None: - payload['price_trailing'] = str(price_trailing) - if flags is not None: - payload['flags'] = str(flags) - if time_in_force is not None: - payload['time_in_force'] = str(time_in_force) - await self.bfxapi._send_auth_command('ou', payload) + self.date = datetime.datetime.fromtimestamp(mtsCreate/1000.0) + if priceAvg: + ## if cancelled then priceAvg wont exist + self.fee = (priceAvg * abs(amount)) * 0.002 - async def close(self): - await self.bfxapi._send_auth_command('oc', { 'id': self.id }) + @staticmethod + def from_raw_order(raw_order): + id = raw_order[OrderClosedModel.ID] + gId = raw_order[OrderClosedModel.GID] + cId = raw_order[OrderClosedModel.CID] + symbol = raw_order[OrderClosedModel.SYMBOL] + mtsCreate = raw_order[OrderClosedModel.MTS_CREATE] + mtsUpdate = raw_order[OrderClosedModel.MTS_UPDATE] + amount = raw_order[OrderClosedModel.AMOUNT] + amountOrig = raw_order[OrderClosedModel.AMOUNT_ORIG] + oType = raw_order[OrderClosedModel.TYPE] + typePrev = raw_order[OrderClosedModel.TYPE_PREV] + flags = raw_order[OrderClosedModel.FLAGS] + status = raw_order[OrderClosedModel.STATUS] + price = raw_order[OrderClosedModel.PRICE] + priceAvg = raw_order[OrderClosedModel.PRIVE_AVG] + priceTrailing = raw_order[OrderClosedModel.PRICE_TRAILING] + priceAuxLimit = raw_order[OrderClosedModel.PRICE_AUX_LIMIT] + notfiy = raw_order[OrderClosedModel.NOTIFY] + placeId = raw_order[OrderClosedModel.PLACE_ID] + + return Order(id, gId, cId, symbol, mtsCreate, mtsUpdate, amount, amountOrig, oType, + typePrev, flags, status, price, priceAvg, priceTrailing, priceAuxLimit, notfiy, placeId) def set_confirmed(self): self.is_pending_bool = False diff --git a/bfxapi/models/Trade.py b/bfxapi/models/Trade.py index 8fa60b7..bcb0e83 100644 --- a/bfxapi/models/Trade.py +++ b/bfxapi/models/Trade.py @@ -4,17 +4,27 @@ class Trade: SHORT = 'SHORT' LONG = 'LONG' - def __init__(self, order, tag=''): - self.order = order - self.amount = order.amount - self.price = order.priceAvg - self.fee = (order.priceAvg * abs(order.amount)) * 0.002 - self.mts = order.mtsCreate - self.date = datetime.datetime.fromtimestamp(order.mtsCreate/1000.0) - self.direction = self.SHORT if order.amount < 0 else self.LONG - self.tag = tag + def __init__(self, id, pair, mts_create, order_id, amount, price, order_type, + order_price, maker, fee, fee_currency): + self.id = id + 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): + # [24224048, 'tBTCUSD', 1542800024000, 1151353484, 0.09399997, 19963, None, None, -1, -0.000188, 'BTC'] + return Trade(*raw_trade) def __str__(self): - ''' Allow us to print the Trade object in a pretty format ''' - return "Trade {} @ {} fee={} ".format( - self.amount, self.price, self.fee, self.order) + return "Trade '{}' x {} @ {} ".format( + self.pair, self.amount, self.price, self.direction, self.fee) diff --git a/bfxapi/websockets/OrderManager.py b/bfxapi/websockets/OrderManager.py index c6976d1..c7cecf0 100644 --- a/bfxapi/websockets/OrderManager.py +++ b/bfxapi/websockets/OrderManager.py @@ -46,7 +46,7 @@ class OrderManager: # "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(self.bfxapi, raw_ws_data[2]) + order = Order.from_raw_order(raw_ws_data[2]) order.set_open_state(False) if order.id in self.open_orders: del self.open_orders[order.id] @@ -61,7 +61,7 @@ class OrderManager: osData = raw_ws_data[2] self.open_orders = {} for raw_order in osData: - order = Order(self.bfxapi, raw_order) + order = Order.from_raw_order(raw_ws_data[2]) order.set_open_state(True) self.open_orders[order.id] = order self.bfxapi._emit('order_snapshot', self.get_open_orders()) @@ -72,7 +72,7 @@ class OrderManager: # 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(self.bfxapi, raw_ws_data[2]) + order = Order.from_raw_order(raw_ws_data[2]) order.set_open_state(True) self.open_orders[order.id] = order await self._confirm_order(order) @@ -85,7 +85,7 @@ class OrderManager: # 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(self.bfxapi, raw_ws_data[2]) + order = Order.from_raw_order(raw_ws_data[2]) order.set_open_state(True) self.open_orders[order.id] = order await self._confirm_order(order) @@ -117,7 +117,7 @@ class OrderManager: raise Exception("Order id={} is not open".format(orderId)) order = self.open_orders[orderId] self.pending_callbacks[order.cId] = (onConfirm, onClose) - await order.update(*args, **kwargs) + await order._update(orderId, *args, **kwargs) self.logger.info("Update Order order_id={} dispatched".format(orderId)) async def close_order(self, orderId, onConfirm=None, onClose=None): @@ -125,7 +125,7 @@ class OrderManager: raise Exception("Order id={} is not open".format(orderId)) order = self.open_orders[orderId] self.pending_callbacks[order.cId] = (onConfirm, onClose) - await order.cancel() + await self._close_order(orderId) self.logger.info("Order cancel order_id={} dispatched".format(orderId)) async def close_all_orders(self): @@ -139,3 +139,25 @@ class OrderManager: asyncio.ensure_future(self.open_orders[oid].close()) ] await asyncio.wait(*[ task_batch ]) + + async def _close_order(self, orderId): + await self.bfxapi._send_auth_command('oc', { 'id': orderId }) + + async def _update(self, orderId, price=None, amount=None, delta=None, price_aux_limit=None, + price_trailing=None, flags=None, time_in_force=None): + payload = { "id": orderId } + if price is not None: + payload['price'] = str(price) + if amount is not None: + payload['amount'] = str(amount) + if delta is not None: + payload['delta'] = str(delta) + if price_aux_limit is not None: + payload['price_aux_limit'] = str(price_aux_limit) + if price_trailing is not None: + payload['price_trailing'] = str(price_trailing) + if flags is not None: + payload['flags'] = str(flags) + if time_in_force is not None: + payload['time_in_force'] = str(time_in_force) + await self.bfxapi._send_auth_command('ou', payload)