def from_huobi(cls, json_document, pair_name, timest): """ "tick": { "id": 1489464585407, "ts": 1489464585407, "bids": [ [7964, 0.0678], // [price, amount] [7963, 0.9162], [7961, 0.1], ], "asks": [ [7979, 0.0736], [7980, 1.0292], ] :param pair_name: :param timest: :return: """ sell_bids = [ Deal(price=entry[0], volume=entry[1]) for entry in json_document.get("asks", []) ] buy_bids = [ Deal(price=entry[0], volume=entry[1]) for entry in json_document.get("bids", []) ] pair_id = get_currency_pair_from_huobi(pair_name) sequence_id = long(json_document["version"]) return OrderBook(pair_id, timest, sell_bids, buy_bids, EXCHANGE.HUOBI, sequence_id)
def __init__(self, trade_type, exchange_id, pair_id, price, volume, order_book_time, create_time, execute_time=None, order_id=None, trade_id=None, executed_volume=None, arbitrage_id=-13): Deal.__init__(self, price, volume) self.trade_type = int(trade_type) self.exchange_id = int(exchange_id) self.pair_id = int(pair_id) self.order_book_time = long(order_book_time) self.create_time = long(create_time) self.execute_time = long( execute_time) if execute_time is not None else execute_time self.order_id = order_id self.trade_id = trade_id self.executed_volume = Decimal( executed_volume ) if executed_volume is not None else executed_volume self.arbitrage_id = long(arbitrage_id)
def from_row(cls, db_row, asks_rows, sell_rows): # id, pair_id, exchange_id, timest, date_time # id, order_book_id, price, volume currency_pair_id = db_row[1] exchange_id = db_row[2] timest = db_row[3] ask_bids = [Deal(entry[2], entry[3]) for entry in asks_rows] sell_bids = [Deal(entry[2], entry[3]) for entry in sell_rows] return OrderBook(currency_pair_id, timest, ask_bids, sell_bids, exchange_id)
def parse_socket_update_huobi(order_book_delta, pair_id): if "tick" not in order_book_delta: return None order_book_delta = order_book_delta["tick"] sequence_id = long(order_book_delta["version"]) asks = [Deal(price=b[0], volume=b[1]) for b in order_book_delta.get("asks", [])] bids = [Deal(price=b[0], volume=b[1]) for b in order_book_delta.get("bids", [])] timest_ms = get_now_seconds_utc_ms() return OrderBook(pair_id, timest_ms, asks, bids, EXCHANGE.HUOBI, sequence_id)
def from_kraken(cls, json_document, currency, timest): """ {"error":[],"result":{"XETHXXBT":{"asks":[["0.081451","0.200",1501690777],["0.081496","163.150",1501691124] "bids":[["0.080928","0.100",1501691107],["0.080926","0.255",1501691110] """ sell_bids = [ Deal(entry[0], entry[1]) for entry in json_document["asks"] ] buy_bids = [ Deal(entry[0], entry[1]) for entry in json_document["bids"] ] pair_id = get_currency_pair_from_kraken(currency) return OrderBook(pair_id, timest, sell_bids, buy_bids, EXCHANGE.KRAKEN)
def from_binance(cls, json_document, currency, timest): """ "lastUpdateId":1668114,"bids":[["0.40303000","22.00000000",[]],],"asks":[["0.41287000","1.00000000",[]] """ sell_bids = [ Deal(price=entry[0], volume=entry[1]) for entry in json_document.get("asks", []) ] buy_bids = [ Deal(price=entry[0], volume=entry[1]) for entry in json_document.get("bids", []) ] pair_id = get_currency_pair_from_binance(currency) sequence_id = long(json_document["lastUpdateId"]) return OrderBook(pair_id, timest, sell_bids, buy_bids, EXCHANGE.BINANCE, sequence_id)
def from_poloniex(cls, json_document, currency, timest): """ {"asks":[["0.00006604",11590.35669799],["0.00006606",25756.70896058]], "bids":[["0.00006600",46771.47390146],["0.00006591",25268.665],], "isFrozen":"0","seq":41049600} """ sell_bids = [ Deal(entry[0], entry[1]) for entry in json_document["asks"] ] buy_bids = [ Deal(entry[0], entry[1]) for entry in json_document["bids"] ] pair_id = get_currency_pair_from_poloniex(currency) sequence_id = long(json_document["seq"]) return OrderBook(pair_id, timest, sell_bids, buy_bids, EXCHANGE.POLONIEX, sequence_id)
def parse_socket_update_binance(order_book_delta): """ How to manage a local order book correctly Open a stream to wss://stream.binance.com:9443/ws/bnbbtc@depth Buffer the events you receive from the stream Get a depth snapshot from **https://www.binance.com/api/v1/depth?symbol=BNBBTC&limit=1000" Drop any event where u is <= lastUpdateId in the snapshot The first processed should have U <= lastUpdateId+1 AND u >= lastUpdateId+1 While listening to the stream, each new event's U should be equal to the previous event's u+1 The data in each event is the absolute quantity for a price level If the quantity is 0, remove the price level Receiving an event that removes a price level that is not in your local order book can happen and is normal. 4 "U": 157, // First update ID in event "u": 160, // Final update ID in event {"e": "depthUpdate", "E": 1527861613915, "s": "DASHBTC", "U": 45790140, "u": 45790142, "b": [["0.04073500", "2.02000000", []], ["0.04073200", "0.00000000", []]], "a": [["0.04085300", "0.00000000", []]]} :param order_book_delta: :return: """ timest_ms = get_now_seconds_utc_ms() sequence_id = long(order_book_delta["U"]) sequence_id_end = long(order_book_delta["u"]) asks = [Deal(a[0], a[1]) for a in order_book_delta["a"]] bids = [Deal(b[0], b[1]) for b in order_book_delta["b"]] trades_sell = [] trades_buy = [] return OrderBookUpdate(sequence_id, bids, asks, timest_ms, trades_sell, trades_buy, sequence_id_end)
def from_bittrex(cls, json_document, currency, timest): """ {"success":true,"message":"","result":{"buy":[{"Quantity":12.76073322,"Rate":0.01557999},{"Quantity":12.01802925,"Rate":0.01557998} "sell":[{"Quantity":0.38767680,"Rate":0.01560999},{"Quantity":2.24182363,"Rate":0.01561999} """ sell_bids = [] if "sell" in json_document and json_document["sell"] is not None: for b in json_document["sell"]: sell_bids.append(Deal(b["Rate"], b["Quantity"])) buy_bids = [] if "buy" in json_document and json_document["buy"] is not None: for b in json_document["buy"]: buy_bids.append(Deal(b["Rate"], b["Quantity"])) pair_id = get_currency_pair_from_bittrex(currency) # DK FIXME! not exactly but Rest API doesnt offer any viable options :/ sequence_id = get_now_seconds_utc_ms() return OrderBook(pair_id, timest, sell_bids, buy_bids, EXCHANGE.BITTREX, sequence_id)
def setUp(self): set_logging_level(LOG_ALL_ERRORS) self.first = Deal(0.03172795, 0.1) self.non_present = Deal(10.1, 0.1) self.deal_update = Deal(0.03172801, 0.4) unsorted = [ self.first, Deal(0.03172796, 0.2), Deal(0.03172798, 0.3), self.deal_update, Deal(0.03173, 0.5) ] self.asks = sorted(unsorted, key=lambda x: x.price, reverse=False) self.bids = sorted(unsorted, key=lambda x: x.price, reverse=True)
def parse_socket_order_book_poloniex(order_book_snapshot, pair_id): """ :param order_book_snapshot: [ <channel id>, <sequence number>, [ [ "i", { "currencyPair": "<currency pair name>", "orderBook": [ { "<lowest ask price>": "<lowest ask size>", "<next ask price>": "<next ask size>", … }, { "<highest bid price>": "<highest bid size>", "<next bid price>": "<next bid size>", … } ] } ] ] ] order_book_snapshot[2][0][1]["orderBook"][0] Example: [ 148, 573963482, [ [ "i", { "currencyPair": "BTC_ETH", "orderBook": [ { "0.08964203": "0.00225904", "0.04069708": "15.37598559", ... }, { "0.03496358": "0.32591524", "0.02020000": "0.50000000", ... } ] } ] ] ] :param pair_id: :return: """ timest_ms = get_now_seconds_utc_ms() sequence_id = long(order_book_snapshot[1]) asks = [] for k, v in order_book_snapshot[2][0][1]["orderBook"][0].iteritems(): asks.append(Deal(k, v)) bids = [] for k, v in order_book_snapshot[2][0][1]["orderBook"][1].iteritems(): bids.append(Deal(k, v)) return OrderBook(pair_id, timest_ms, asks, bids, EXCHANGE.POLONIEX, sequence_id)
def parse_socket_update_poloniex(order_book_delta): """ Message format for ticker [ 1002, Channel null, Unknown [ 121, CurrencyPairID "10777.56054438", Last "10800.00000000", lowestAsk "10789.20000001", highestBid "-0.00860373", percentChange "72542984.79776118", baseVolume "6792.60163706", quoteVolume 0, isForzen "11400.00000000", high24hr "9880.00000009" low24hr ] ] [1002,null,[158,"0.00052808","0.00053854","0.00052926","0.05571659","4.07923480","7302.01523251",0,"0.00061600","0.00049471"]] So the columns for orders are messageType -> t/trade, o/order tradeID -> only for trades, just a number orderType -> 1/bid,0/ask rate amount time sequence 148 is code for BTCETH, yeah there is no documentation.. but when trades occur You can figure out. Bid is always 1, cause You add something new.. PairId, Nonce, orders\trades deltas: [24,219199090,[["o",1,"0.04122908","0.01636493"],["t","10026908",0,"0.04122908","0.00105314",1527880700]]] [24,219201009,[["o",0,"0.04111587","0.00000000"],["o",0,"0.04111174","1.52701255"]]] [24,219164304,[["o",1,"0.04064791","0.01435233"],["o",1,"0.04068034","0.16858384"]]] :param order_book_delta: :return: """ asks = [] bids = [] trades_sell = [] trades_buy = [] # We suppose that bid and ask are sorted in particular order: # for bids - highest - first # for asks - lowest - first if len(order_book_delta) < 3: return None timest_ms = get_now_seconds_utc_ms() sequence_id = long(order_book_delta[1]) delta = order_book_delta[2] for entry in delta: if entry[0] == POLONIEX_WEBSOCKET_ORDER: new_deal = Deal(entry[2], entry[3]) # If it is just orders - we insert in a way to keep sorted order if entry[1] == POLONIEX_WEBSOCKET_ORDER_ASK: asks.append(new_deal) elif entry[1] == POLONIEX_WEBSOCKET_ORDER_BID: bids.append(new_deal) else: msg = "Poloniex socket update parsing - {update} total: {ttt}".format( update=entry, ttt=order_book_delta) log_to_file(msg, SOCKET_ERRORS_LOG_FILE_NAME) elif entry[0] == POLONIEX_WEBSOCKET_TRADE: # FIXME NOTE: this is ugly hack to avoid creation of custom objects # and at the same Deal object contains lt method that # used by bisect for efficient binary search in sorted list new_deal = Deal(entry[3], entry[4]) # For trade - vice-versa we should update opposite arrays: # in case we have trade with type bid -> we will update orders at ask # in case we have trade with type ask -> we will update orders at bid if entry[2] == POLONIEX_WEBSOCKET_ORDER_BID: trades_sell.append(new_deal) elif entry[2] == POLONIEX_WEBSOCKET_ORDER_ASK: trades_buy.append(new_deal) else: msg = "Poloniex socket update parsing - {wtf}".format( wtf=entry) log_to_file(msg, SOCKET_ERRORS_LOG_FILE_NAME) else: msg = "Poloniex socket update parsing - UNKNOWN TYPE - {wtf}".format( wtf=entry) log_to_file(msg, SOCKET_ERRORS_LOG_FILE_NAME) # FIXME NOTE: during update we are not using trades_sell\trades_buy at all - so probably # better not to use them at all return OrderBookUpdate(sequence_id, bids, asks, timest_ms, trades_sell, trades_buy)
def parse_socket_order_book_bittrex(order_book_snapshot, pair_id): """ :param: order_book_snapshot stringified json of following format: Bittrex order book format: "S" = "Sells" "Z" = "Buys" "M" = "MarketName" "f" = "Fills" "N" = "Nonce" For fills: "F" = "FillType": FILL | PARTIAL_FILL "I" = "Id" "Q" = "Quantity" "P" = "Price" "t" = "Total" "OT" = "OrderType": BUY | SELL "T" = "TimeStamp" { "S": [ { "Q": 4.29981987, "R": 0.04083123 }, { "Q": 0.59844883, "R": 0.04083824 }], "Z": [ { "Q": 10.8408461, "R": 0.04069406 }, { "Q": 0.9, "R": 0.04069405 }], "M": null, "f": [ { "F": "FILL", "I": 274260522, "Q": 0.37714445, "P": 0.04083123, "t": 0.01539927, "OT": "BUY", "T": 1535772645920 }, { "F": "PARTIAL_FILL", "I": 274260519, "Q": 1.75676, "P": 0.04069406, "t": 0.07148969, "OT": "SELL", "T": 1535772645157 }], "N": 28964 } :return: newly assembled OrderBook object """ timest_ms = get_now_seconds_utc_ms() sequence_id = long(order_book_snapshot["N"]) sells = order_book_snapshot["S"] asks = [] for new_sell in sells: asks.append(Deal(new_sell["R"], new_sell["Q"])) buys = order_book_snapshot["Z"] bids = [] for new_buy in buys: bids.append(Deal(new_buy["R"], new_buy["Q"])) # DK WTF NOTE: ignore for now # fills = order_book_snapshot["f"] return OrderBook(pair_id, timest_ms, asks, bids, EXCHANGE.BITTREX, sequence_id)
def parse_socket_update_bittrex(order_book_delta): """ https://bittrex.github.io/#callback-for-1 "S" = "Sells" "Z" = "Buys" "Q" = "Quantity" "R" = "Rate" "TY" = "Type" The Type key can be one of the following values: 0 = ADD, 1 = REMOVE, 2 = UPDATE "M" = "MarketName" "N" = "Nonce" "f" = "Fills" 3 {u'S': [], u'Z': [{u'Q': 0.0, u'R': 0.04040231, u'TY': 1}, {u'Q': 0.78946119, u'R': 0.00126352, u'TY': 0}], u'M': u'BTC-DASH', u'f': [], u'N': 15692} 3 {u'S': [], u'Z': [{u'Q': 1.59914865, u'R': 0.040436, u'TY': 0}, {u'Q': 0.0, u'R': 0.04040232, u'TY': 1}], u'M': u'BTC-DASH', u'f': [], u'N': 15691} u'f': [ {u'Q': 0.11299437, u'R': 0.042135, u'OT': u'BUY', u'T': 1527961548500}, {u'Q': 0.39487459, u'R': 0.04213499, u'OT': u'BUY', u'T': 1527961548500}], :param order_book_delta :return: """ timest_ms = get_now_seconds_utc_ms() sequence_id = long(order_book_delta["N"]) asks = [] bids = [] trades_sell = [] trades_buy = [] sells = order_book_delta["S"] buys = order_book_delta["Z"] fills = order_book_delta["f"] for new_sell in sells: new_deal = Deal(new_sell["R"], new_sell["Q"]) if "TY" not in new_sell: msg = "Bittrex socket update - within SELL array some weird format - no TY - {wtf}".format( wtf=new_sell) log_to_file(msg, SOCKET_ERRORS_LOG_FILE_NAME) continue type_update = int(new_sell["TY"]) if type_update in [ BittrexParameters.BITTREX_ORDER_ADD, BittrexParameters.BITTREX_ORDER_UPDATE ]: asks.append(new_deal) elif type_update == BittrexParameters.BITTREX_ORDER_REMOVE: asks.append(Deal(new_sell["R"], 0)) else: msg = "Bittrex socket un-supported sells format? {wtf}".format( wtf=new_sell) log_to_file(msg, SOCKET_ERRORS_LOG_FILE_NAME) for new_buy in buys: new_deal = Deal(new_buy["R"], new_buy["Q"]) if "TY" not in new_buy: msg = "Bittrex socket update - within BUYS array some weird format - no TY - {wtf}".format( wtf=new_buy) log_to_file(msg, SOCKET_ERRORS_LOG_FILE_NAME) continue type_update = int(new_buy["TY"]) if type_update in [ BittrexParameters.BITTREX_ORDER_ADD, BittrexParameters.BITTREX_ORDER_UPDATE ]: bids.append(new_deal) elif type_update == BittrexParameters.BITTREX_ORDER_REMOVE: bids.append(Deal(new_buy["R"], 0)) else: msg = "Bittrex socket un-supported buys format? {wtf}".format( wtf=new_buy) log_to_file(msg, SOCKET_ERRORS_LOG_FILE_NAME) return OrderBookUpdate(sequence_id, bids, asks, timest_ms, trades_sell, trades_buy)