Ejemplo n.º 1
0
class OrderBook(object):
    """
    Uses RBTrees to handle all types of orders and store them in their corresponding bucket
    """
    def __init__(self, product_id: str):
        self._asks = RBTree()
        self._bids = RBTree()
        self._product_id = product_id

    @property
    def product_id(self):
        return self._product_id

    def process_snapshot(self, message: Dict):
        """
        Process a snapshot message
        :param message: json
        """

        # If a snapshot is sent reset trees
        self._asks = RBTree()
        self._bids = RBTree()

        # Parse all asks and add them to tree
        for ask in message['asks']:
            price, size = ask
            price = Decimal(price)
            size = Decimal(size)

            self._asks.insert(price, size)

        # Parse all bids and add them to tree
        for bid in message['bids']:
            price, size = bid
            price = Decimal(price)
            size = Decimal(size)

            self._bids.insert(price, size)

    def process_update(self, message: Dict):
        """
        Process a update message
        :param message: json
        """

        # Retrieve changes
        changes = message['changes']

        for change in changes:
            side, price, size = change

            # parse numbers and keep precision
            price = Decimal(price)
            size = Decimal(size)

            if side == 'buy':
                # If it is equal to 0 (or less than) the order no longer exists
                if size <= 0:
                    self._bids.remove(price)
                else:
                    self._bids.insert(price, size)
            elif side == 'sell':
                # If it is equal to 0 (or less than) the order no longer exists
                if size <= 0:
                    self._asks.remove(price)
                else:
                    self._asks.insert(price, size)

    def process_message(self, message: Dict):
        """
        Process all messages to identify next parser location
        :param message: json
        """
        # Read type
        msg_type = message['type']

        # dropped - not same product id
        if message.get('product_id', None) != self._product_id:
            return

        if msg_type == 'snapshot':
            self.process_snapshot(message)
        elif msg_type == 'l2update':
            self.process_update(message)

    def get_asks(self) -> List[Tuple[float, float]]:
        """
        Provides a list of asks and sizes in order of best price for the buyer

        :return: a list of Tuple's corresponding to ask (price rate), and ask size
        """
        asks = []
        for ask in self._asks:
            try:
                size = self._asks[ask]
            except KeyError:
                continue
            asks.append([float(ask), float(size)])
        return asks

    def get_bids(self) -> List[Tuple[float, float]]:
        """
       Provides a list of bids and sizes in order of best price for the seller

       :return: a list of Tuple's corresponding to ask (price rate), and ask size
       """
        bids = []
        for bid in self._bids:
            try:
                size = self._bids[bid]
            except KeyError:
                continue
            # For bids the best value (for selling) is reversed so inserting at the beginning flips the order
            bids.insert(0, [float(bid), float(size)])
        return bids

    def get_orders(self) -> Dict[str, List[Tuple[float, float]]]:
        """
        Uses get_bids and get_asks to compile all orders

        :return: both bids and asks
        """
        return {'asks': self.get_asks(), 'bids': self.get_bids()}

    def get_ask(self) -> Tuple[Decimal, Decimal]:
        """
        Get the best asking price. If it does not exist it returns a size of 0

        :return: the rate, and the size
        """
        price = self._asks.min_key()

        try:
            size = self._asks[price]
        except KeyError:
            return price, Decimal(0)

        return price, size

    def get_bid(self) -> Tuple[Decimal, Decimal]:
        """
        Get the best bid price. If it does not exist it returns a size of 0

        :return: the rate, and the size
        """
        price = self._bids.max_key()

        try:
            size = self._bids[price]
        except KeyError:
            return price, Decimal(0)

        return price, size
Ejemplo n.º 2
0
class LOBTree:
    def __init__(self):
        '''
        Limit order book tree implementation using Red-Black tree for self-balancing 
        Each limit price level is a OrderLinkedlist, and each order contains information 
        including id, price, timestamp, volume
        self.limit_level: dict
            key: price level; value: OrderLinkedlist object
        self.order_ids: dict  
            key: order id; value: Order object
            helps to locate order by id
        '''
        # tree that store price as keys and number of orders on that level as values
        self.price_tree = FastRBTree()
        self.max_price = None
        self.min_price = None
        self.limit_levels = {}
        self.order_ids = {}

    @property
    def max(self):
        return self.max_price

    @property
    def min(self):
        return self.min_price

    def _get_price(self, price):
        '''
        price: int 
        :return: OrderLinkedlist instance 

        '''
        return self.limit_levels[price]

    def insert_order(self, order: Order):
        '''
        order: Order Instance
            If order price doesn't exist in the self.limit_levels, insert it into the price level,
            else update accordingly;
            Will be used as limit order submission
        :return: None
        '''
        if order.id in self.order_ids:
            raise ValueError('order already exists in the book')
            return

        if order.price not in self.limit_levels:
            new_price_level = OrderLinkedlist()
            self.price_tree[order.price] = 1
            self.limit_levels[order.price] = new_price_level
            self.limit_levels[order.price].set_head(order)
            self.limit_levels[order.price].size += order.size
            self.order_ids[order.id] = order
            if self.max_price is None or order.price > self.max_price:
                self.max_price = order.price
            if self.min_price is None or order.price < self.min_price:
                self.min_price = order.price
        else:
            self.limit_levels[order.price].set_head(order)
            self.limit_levels[order.price].size += order.size
            self.order_ids[order.id] = order
            self.price_tree[order.price] += 1

    def update_existing_order_size(self, order_id: int, updated_size: int):
        '''
        order_id: int
        size: int
            Update an existing order's size in a price level and its price level's overall size
        :return: None
        '''
        delta = self.order_ids[order_id].size - updated_size
        try:
            self.order_ids[order_id].size = updated_size
            order_price = self.order_ids[order_id].price
            # updated order will be put at the front of the list
            self.limit_levels[order_price].set_head(self.order_ids[order_id])
            self.limit_levels[order_price].size -= delta
        except Exception as e:
            LOG.info('Order is not in the book')

    def remove_order(self, order_id: int):
        '''
        order: Order Instance
            Remove the order from the self.order_ids first, 
            then remove it from the self.limit_levels;
            if the limit_levels is empty after removal, 
            adjust the max_price and min_price accordingly from the self.price_tree
        :return: Order Instance | order removed from the book
        '''
        popped = self.order_ids.pop(order_id)
        self.limit_levels[popped.price].remove(popped, decrement=True)
        self.price_tree[popped.price] -= 1
        if self.limit_levels[popped.price].size == 0:
            self._remove_price_level(popped.price)
        return popped

    def _remove_price_level(self, price: int):
        '''
        order: Order Instance
            Given a price level, remove the price level in the price_tree and limit_levels
            reset the max and min prices
        '''
        del self.limit_levels[price]
        self.price_tree.remove(price)
        if self.max_price == price:
            try:
                self.max_price = self.price_tree.max_key()
            except KeyError or ValueError:
                self.max_price = None
        if self.min_price == price:
            try:
                self.min_price = self.price_tree.min_key()
            except KeyError or ValueError:
                self.min_price = None

    def market_order(self, order: Order):
        '''
        order: Order Instance
        '''
        if len(self.limit_levels) == 0:
            raise ValueError('No orders in the book')
            return

        if order.is_bid:
            best_price = self.min_price
            while order.size > 0 and best_price != None:
                price_level = self._get_price(best_price)
                order.size, number_of_orders_deleted = price_level._consume_orders(
                    order, self.order_ids)
                self.price_tree[best_price] -= number_of_orders_deleted
                if price_level._head == None:
                    self._remove_price_level(best_price)
                best_price = self.min_price
            if order.size != 0:
                LOG.warning('no more limit orders in the bid book')
        else:
            best_price = self.max_price
            while order.size > 0 and best_price != None:
                price_level = self._get_price(best_price)
                order.size, number_of_orders_deleted = price_level._consume_orders(
                    order, self.order_ids)
                self.price_tree[best_price] -= number_of_orders_deleted
                if price_level._head == None:
                    self._remove_price_level(best_price)
                best_price = self.max_price
            if order.size != 0:
                LOG.warning('no more orders in the ask book')

    def level_with_most_orders(self, range: int):
        '''
        range: int
            Gives the price level with the most orders on the top levels
        '''
        pass

    def iceberg(self):
        '''
        Iceberg order type
        '''
        pass
Ejemplo n.º 3
0
class TradeTree(object):
    '''A red-black tree used to store TradeLists in price trade
    The exchange will be using the TradeTree to hold bid and ask data (one TradeTree for each side).
    Keeping the information in a red black tree makes it easier/faster to detect a match.
    '''
    def __init__(self):
        self.price_tree = FastRBTree()
        self.trade_map = {}
        self.num_trades = 0  # Contains count of Orders in tree
        self.depth = 0  # Number of different prices in tree (http://en.wikipedia.org/wiki/trade_book_(trading)#Book_depth)

    def __len__(self):
        return len(self.trade_map)

    def get_price_list(self, price):
        return self.price_tree.get(price, [])

    def get_trade(self, trade_id):
        return self.trade_map[trade_id] if trade_id in self.trade_map else None

    def create_price(self, price):
        self.depth += 1  # Add a price depth level to the tree
        new_list = LinkedList()
        self.price_tree.insert(price,
                               new_list)  # Insert a new price into the tree

    def remove_price(self, price):
        self.depth -= 1  # Remove a price depth level
        self.price_tree.remove(price)

    def price_exists(self, price):
        return self.price_tree.__contains__(price)

    def trade_exists(self, trade_id):
        return trade_id in self.trade_map

    def insert_trade(self, xtrade):
        if self.trade_exists(xtrade.id):
            return
        self.num_trades += 1
        if not self.price_exists(xtrade.limit_price):
            self.create_price(
                xtrade.limit_price
            )  # If price not in Price Map, create a node in RBtree
        self.trade_map[
            trade.id] = self.price_tree[xtrade.limit_price].append_item(
                xtrade
            )  # Add the trade to the TradeList in Price Map return the reference

    def remove_trade(self, xtrade):
        self.num_trades -= 1
        trade_node = self.trade_map[trade.id]
        self.price_tree[trade.limit_price].remove_item(trade_node)
        if len(self.price_tree[trade.limit_price]) == 0:
            self.remove_price(trade.limit_price)
        self.trade_map.pop(trade.id, None)

    def max_price(self):
        if self.depth > 0:
            return self.price_tree.max_key()
        else:
            return None

    def min_price(self):
        if self.depth > 0:
            return self.price_tree.min_key()
        else:
            return None

    def max_price_list(self):
        if self.depth > 0:
            return self.get_price_list(self.max_price())
        else:
            return None

    def min_price_list(self):
        if self.depth > 0:
            return self.get_price_list(self.min_price())
        else:
            return None
Ejemplo n.º 4
0
class BidBook(object):
    """
	A BidBook is used to store the order book's rates and amounts on the bid side with a defined depth.
	To maintain a sorted order of rates, the BidBook uses a red-black tree to store rates and corresponding amounts.
	For O(1) query of volume at a predetermined rate, the BidBook also uses a dictionary to store rate and amount.
	"""
    def __init__(self, max_depth, data):
        # RBTree: maintains sorted order of rates
        # every value inserted to RBTree must be a tuple, so we hard code the second value to be 0
        self.rate_tree = FastRBTree()

        # dict: Uses rate and amount for key value pairs
        self.rate_dict = {}

        # float: amounts summed across all rate levels in tree
        self.volume = 0

        # int: total number of rate levels in tree
        self.depth = len(data)

        # int: maximum number of rate levels in tree
        self.max_depth = max_depth

        # populate rate_tree and rate_dict from public API call data
        # set volume
        for level in data:
            rate = float(level[0])
            amount = float(level[1])
            self.rate_tree.insert(rate, 0)
            self.rate_dict[rate] = amount
            self.volume += amount

    def __len__(self):
        return len(self.rate_dict)

    def rate_exists(self, rate):
        return rate in self.rate_dict

    def get_amount_at_rate(self, rate):
        return self.rate_dict.get(rate)

    def max_rate_level(self):
        if self.depth > 0:
            rate = self.rate_tree.max_key()
            amount = self.get_amount_at_rate(rate)
            return rate, amount
        else:
            return None

    def min_rate_level(self):
        if self.depth > 0:
            rate = self.rate_tree.min_key()
            amount = self.get_amount_at_rate(rate)
            return rate, amount
        else:
            return None

    def modify(self, event):
        # if the event's rate is already in the book, just modify the amount at the event's rate
        rate = float(event[u'data'][u'rate'])
        amount = float(event[u'data'][u'amount'])
        if self.rate_exists(rate):
            # print '~~~~~~~~~~~~~~~~~~~~~~  BID MODIFY  ~~~~~~~~~~~~~~~~~~~~~~'
            self.rate_dict[rate] = amount

        # only rates not already in the book reach this logic
        # if the max depth hasn't been reached, just insert the event's rate and amount
        elif self.depth < self.max_depth:
            # print '~~~~~~~~~~~~~~~~~~~~~~  BID MODIFY  ~~~~~~~~~~~~~~~~~~~~~~'
            self.rate_tree.insert(rate, 0)
            self.rate_dict[rate] = amount
            self.depth += 1

        # only events being handled by a full order tree reach this logic
        # if the event is a bid and the rate is greater than min rate, effectively replace min rate level with event
        else:
            min_rate = self.min_rate_level()[0]
            if rate > min_rate:
                # print '~~~~~~~~~~~~~~~~~~~~~~  BID MODIFY  ~~~~~~~~~~~~~~~~~~~~~~'
                self.rate_tree.remove(min_rate)
                del self.rate_dict[min_rate]
                self.rate_tree.insert(rate, 0)
                self.rate_dict[rate] = amount

    def remove(self, event):
        # if the event's rate is in the book, delete it
        rate = float(event[u'data'][u'rate'])
        if self.rate_exists(rate):
            # print '~~~~~~~~~~~~~~~~~~~~~~  BID REMOVE  ~~~~~~~~~~~~~~~~~~~~~~'
            self.rate_tree.remove(rate)
            del self.rate_dict[rate]
            self.depth -= 1

    def __str__(self):
        rate_tree_str = '[' + ','.join(rate[0]
                                       for rate in self.rate_tree) + ']'
        return 'BIDS: ' + rate_tree_str
Ejemplo n.º 5
0
cur[ 0 ] = 0
i = 0
for elem in newInput:
   v = elem[ 2 ]
   w = elem[ 1 ]
   #for line in fin:
   #info = line.split()
   #v = int( info[ 0 ] )
   #w = int( info[ 1 ] )
   #print newItems - i, (v,w), len( prev )#, len( testSet )
   i += 1
   for stepWeight in prev:
      step = [ stepWeight, prev[ stepWeight ] ]
      curv = cur.floor_item( step[ 0 ] )[ 1 ]
      maxv = max( step[ 1 ], curv )
      # compare prev and cur on same weight
      if maxv == step[ 1 ]:
         cur[ step[ 0 ] ] = maxv

      nextw = step[ 0 ] + w
      # using step weight as base, compare value of 
      # prev val( step weight ) + item val --> with current item
      # and prev val( step weight + item weight ) --> without current item
      if nextw < size and prev.floor_item( nextw )[ 1 ] < step[ 1 ] + v:
         cur[ nextw ] = step[ 1 ] + v
   prev = cur
   cur = FastRBTree()
   cur[ 0 ] = 0

print prev[ prev.max_key() ]
Ejemplo n.º 6
0
class PriceTree(object):
    def __init__(self, name):
        self.tree = FastRBTree()
        self.name = name
        self.price_map = {}  # Map price -> OrderList
        self.order_map = {}  # Map order_id -> Order
        self.min_price = None
        self.max_price = None

    def insert_price(self, price):
        """
        Add a new price TreedNode and associate it with an orderList
        :param price:
        :return:
        """
        new_list = OrderList()
        self.tree.insert(price, new_list)
        self.price_map[price] = new_list
        if self.max_price is None or price > self.max_price:
            self.max_price = price
        if self.min_price is None or price < self.min_price:
            self.min_price = price

    def remove_price(self, price):
        """
        Remove price from the tree structure and the associated orderList
        Update min and max prices if needed
        :param price:
        :return:
        """
        self.tree.remove(price)
        # Order-map will still contain all Orders emptied (with size 0)
        # as we delete them on the List match_order which is fine for now
        for to_del_order in self.price_map[price]:
            del self.order_map[to_del_order.id]
        # Delete the price from the price-map
        del self.price_map[price]
        if self.max_price == price:
            try:
                self.max_price = self.tree.max_key()
            except ValueError:
                self.max_price = None
        if self.min_price == price:
            try:
                self.min_price = self.tree.min_key()
            except ValueError:
                self.min_price = None

    def insert_price_order(self, order):
        if order.price not in self.price_map:
            self.insert_price(order.price)
        # Add order to orderList
        self.price_map[order.price].add(order)
        # Also keep it in the order mapping
        self.order_map[order.id] = order

    def match_price_order(self, curr_order):
        if len(self.price_map) == 0:
            return []
        # if bid -> sell_tree min
        # if ask -> buy_tree max
        best_price = self.min if curr_order.is_bid else self.max
        complete_trades = []
        while ((curr_order.is_bid and curr_order.price >= best_price)
                or (not curr_order.is_bid and curr_order.price <= best_price)) \
                and curr_order.peak_size > 0:
            # Get price OrderList
            matching_orders_list = self.get_price(best_price)
            complete_trades.extend(
                matching_orders_list.match_order(curr_order, self.order_map))
            # Remove exhausted price
            if matching_orders_list.size == 0:
                self.remove_price(best_price)
                if len(self.price_map) == 0:
                    break
                # Try to find more price matches using the next price
                best_price = self.min if curr_order.is_bid else self.max

        return complete_trades

    def price_exists(self, price):
        return price in self.price_map

    def order_exists(self, id_num):
        return id_num in self.order_map

    def get_price(self, price):
        return self.price_map[price]

    def get_order(self, id_num):
        return self.order_map[id_num]

    @property
    def max(self):
        return self.max_price

    @property
    def min(self):
        return self.min_price