示例#1
0
class Scope(object):
    # Static constants uses for indicating the result of the inserting
    # a symbol into the Scope.
    INSERT_SUCCESS = 0
    INSERT_SHADOWED = 1
    INSERT_REDECL = 2

    # Initializes the Scope with a balanced Red-Black tree.
    def __init__(self):
        self.map = RBTree()

    # Inserts a symbol into the Scope.
    #
    # @return INSERT_SUCCESS or INSERT_REDECL.
    def insert(self, symbol):
        if not isinstance(symbol, Symbol):
            raise TypeError("'symbol' is not an instance of Symbol.")

        if symbol.identifier not in self.map:
            self.map[symbol.identifier] = symbol
            return Scope.INSERT_SUCCESS
        else:
            return Scope.INSERT_REDECL

    # Finds a symbol in the Scope.
    #
    # @param name String identifier for the Symbol to find.
    #
    # @return Symbol if found or None if not found.
    def find(self, name):
        return self.map.get(name, None)

    # Size of the scope.
    #
    # @return Number of Symbols in the Scope.
    def size(self):
        return len(self.map)

    # Clones the current Scope and returns a deep copy.
    #
    # @return Copy of the Scope.
    def clone(self):
        result = Scope()
        for identifier, value in self.map.items():
            result.map[identifier] = value.clone()
        return result

    # @return String representation of the current Scope.
    def __repr__(self):
        symbols = []
        for key, value in self.map.items():
            symbols.append('  ' + key + ' : ' + repr(value))
        return '\n'.join(symbols)
示例#2
0
class OrderBook(WebsocketClient):
    def __init__(self, product_id='BTC-USD'):
        WebsocketClient.__init__(self, products=product_id)
        self._asks = RBTree()
        self._bids = RBTree()
        self._client = PublicClient(product_id=product_id)
        self._sequence = -1

    def onMessage(self, message):
        sequence = message['sequence']
        if self._sequence == -1:
            self._asks = RBTree()
            self._bids = RBTree()
            res = self._client.getProductOrderBook(level=3)
            for bid in res['bids']:
                self.add({
                    'id': bid[2],
                    'side': 'buy',
                    'price': float(bid[0]),
                    'size': float(bid[1])
                })
            for ask in res['asks']:
                self.add({
                    'id': ask[2],
                    'side': 'sell',
                    'price': float(ask[0]),
                    'size': float(ask[1])
                })
            self._sequence = res['sequence']

        if sequence <= self._sequence:
            return
        elif sequence > self._sequence + 1:
            self.close()
            self.start()
            return

        # print(message)
        msg_type = message['type']
        if msg_type == 'open':
            self.add(message)
        elif msg_type == 'done' and 'price' in message:
            self.remove(message)
        elif msg_type == 'match':
            self.match(message)
        elif msg_type == 'change':
            self.change(message)
        self._sequence = sequence

        # bid = self.get_bid()
        # bids = self.get_bids(bid)
        # bid_depth = sum([b['size'] for b in bids])
        # ask = self.get_ask()
        # asks = self.get_asks(ask)
        # ask_depth = sum([a['size'] for a in asks])
        # print('bid: %f @ %f - ask: %f @ %f' % (bid_depth, bid, ask_depth, ask))

    def add(self, order):
        order = {
            'id':
            order['order_id'] if 'order_id' in order else order['id'],
            'side':
            order['side'],
            'price':
            float(order['price']),
            'size':
            float(order['size'])
            if 'size' in order else float(order['remaining_size'])
        }
        if order['side'] == 'buy':
            bids = self.get_bids(order['price'])
            if bids is None:
                bids = [order]
            else:
                bids.append(order)
            self.set_bids(order['price'], bids)
        else:
            asks = self.get_asks(order['price'])
            if asks is None:
                asks = [order]
            else:
                asks.append(order)
            self.set_asks(order['price'], asks)

    def remove(self, order):
        price = float(order['price'])
        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is not None:
                bids = [o for o in bids if o['id'] != order['order_id']]
                if len(bids) > 0:
                    self.set_bids(price, bids)
                else:
                    self.remove_bids(price)
        else:
            asks = self.get_asks(price)
            if asks is not None:
                asks = [o for o in asks if o['id'] != order['order_id']]
                if len(asks) > 0:
                    self.set_asks(price, asks)
                else:
                    self.remove_asks(price)

    def match(self, order):
        size = float(order['size'])
        price = float(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            assert bids[0]['id'] == order['maker_order_id']
            if bids[0]['size'] == size:
                self.set_bids(price, bids[1:])
            else:
                bids[0]['size'] -= size
                self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            assert asks[0]['id'] == order['maker_order_id']
            if asks[0]['size'] == size:
                self.set_asks(price, asks[1:])
            else:
                asks[0]['size'] -= size
                self.set_asks(price, asks)

    def change(self, order):
        new_size = float(order['new_size'])
        price = float(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is None or not any(o['id'] == order['order_id']
                                       for o in bids):
                return
            index = map(itemgetter('id'), bids).index(order['order_id'])
            bids[index]['size'] = new_size
            self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if asks is None or not any(o['id'] == order['order_id']
                                       for o in asks):
                return
            index = map(itemgetter('id'), asks).index(order['order_id'])
            asks[index]['size'] = new_size
            self.set_asks(price, asks)

        tree = self._asks if order['side'] == 'sell' else self._bids
        node = tree.get(price)

        if node is None or not any(o['id'] == order['order_id'] for o in node):
            return

    def get_ask(self):
        return self._asks.min_key()

    def get_asks(self, price):
        return self._asks.get(price)

    def remove_asks(self, price):
        self._asks.remove(price)

    def set_asks(self, price, asks):
        self._asks.insert(price, asks)

    def get_bid(self):
        return self._bids.max_key()

    def get_bids(self, price):
        return self._bids.get(price)

    def remove_bids(self, price):
        self._bids.remove(price)

    def set_bids(self, price, bids):
        self._bids.insert(price, bids)
示例#3
0
class OrderBook(WebsocketClient):
    def __init__(self,
                 product_id='BTC-USD',
                 log_to=None,
                 autoreconnect=True,
                 max_retries=3):
        super(OrderBook, self).__init__(products=[product_id])

        self.channels = ['full']

        self._asks = RBTree()
        self._bids = RBTree()
        self._client = PublicClient()
        self._sequence = -1
        self._log_to = log_to
        self._retries = 0
        self._max_retries = max_retries
        self._autoreconnect = autoreconnect
        self._is_ready = False
        if self._log_to:
            assert hasattr(self._log_to, 'write')
        self._current_ticker = None

    @property
    def product_id(self):
        ''' Currently OrderBook only supports a single product even though it is stored as a list of products. '''
        return self.products[0]

    def is_ready(self):
        return self._is_ready

    def on_open(self):
        self._sequence = -1
        self._retries = 0
        print("-- Subscribed to OrderBook! --\n")

    def on_close(self):
        print("\n-- OrderBook Socket Closed! --")
        self._is_ready = False
        if not self.ws.sock or not self.ws.sock.connected and self._autoreconnect:
            self._sequence = -1

            if self._retries < self._max_retries:
                print('-- OrderBook Reconnecting... --')
                self._retries += 1
                self.start()
            else:
                print('-- Could not reconnect, stopping... --')

    def reset_book(self):
        self._is_ready = False
        self._asks = RBTree()
        self._bids = RBTree()
        res = self._client.get_product_order_book(product_id=self.product_id,
                                                  level=3)
        for bid in res['bids']:
            self.add({
                'id': bid[2],
                'side': 'buy',
                'price': Decimal(bid[0]),
                'size': Decimal(bid[1])
            })
        for ask in res['asks']:
            self.add({
                'id': ask[2],
                'side': 'sell',
                'price': Decimal(ask[0]),
                'size': Decimal(ask[1])
            })
        print("\n-- OrderBook Data Initialized --")
        self._is_ready = True
        self._sequence = res['sequence']

    def on_message(self, message):
        if self._log_to:
            pickle.dump(message, self._log_to)

        if 'sequence' not in message:
            return

        sequence = message['sequence']
        if self._sequence == -1:
            print("\n-- Initializing OrderBook --")
            self.reset_book()
            return
        if sequence <= self._sequence:
            # ignore older messages (e.g. before order book initialization from getProductOrderBook)
            return
        elif sequence > self._sequence + 1:
            self.on_sequence_gap(self._sequence, sequence)
            return

        msg_type = message['type']

        if msg_type == 'open':
            self.add(message)
        elif msg_type == 'done' and 'price' in message:
            self.remove(message)
        elif msg_type == 'match':
            self.match(message)
            self._current_ticker = message
        elif msg_type == 'change':
            self.change(message)

        self._sequence = sequence

    def on_error(self, ws, e):
        super(OrderBook, self).on_error(ws, e)

        if not ws.sock or not ws.sock.connected:
            self._sequence = -1
            self._is_ready = False

            if self._retries < self._max_retries:
                print('-- OrderBook Disconnected, reconnecting... --')
                self._retries += 1
                self.start()
            else:
                print('-- Could not reconnect, stopping... --')

    def on_sequence_gap(self, gap_start, gap_end):
        self._sequence = -1
        self.reset_book()

        print('-- Error: messages missing, re-initializing book. --'.format(
            gap_start, gap_end, self._sequence))

    def add(self, order):
        order = {
            'id': order.get('order_id') or order['id'],
            'side': order['side'],
            'price': Decimal(order['price']),
            'size': Decimal(order.get('size') or order['remaining_size'])
        }
        if order['side'] == 'buy':
            bids = self.get_bids(order['price'])
            if bids is None:
                bids = [order]
            else:
                bids.append(order)
            self.set_bids(order['price'], bids)
        else:
            asks = self.get_asks(order['price'])
            if asks is None:
                asks = [order]
            else:
                asks.append(order)
            self.set_asks(order['price'], asks)

    def remove(self, order):
        price = Decimal(order['price'])
        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is not None:
                bids = [o for o in bids if o['id'] != order['order_id']]
                if len(bids) > 0:
                    self.set_bids(price, bids)
                else:
                    self.remove_bids(price)
        else:
            asks = self.get_asks(price)
            if asks is not None:
                asks = [o for o in asks if o['id'] != order['order_id']]
                if len(asks) > 0:
                    self.set_asks(price, asks)
                else:
                    self.remove_asks(price)

    def match(self, order):
        size = Decimal(order['size'])
        price = Decimal(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if not bids:
                return
            assert bids[0]['id'] == order['maker_order_id']
            if bids[0]['size'] == size:
                self.set_bids(price, bids[1:])
            else:
                bids[0]['size'] -= size
                self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if not asks:
                return
            assert asks[0]['id'] == order['maker_order_id']
            if asks[0]['size'] == size:
                self.set_asks(price, asks[1:])
            else:
                asks[0]['size'] -= size
                self.set_asks(price, asks)

    def change(self, order):
        try:
            new_size = Decimal(order['new_size'])
        except KeyError:
            return

        try:
            price = Decimal(order['price'])
        except KeyError:
            return

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is None or not any(o['id'] == order['order_id']
                                       for o in bids):
                return
            index = [b['id'] for b in bids].index(order['order_id'])
            bids[index]['size'] = new_size
            self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if asks is None or not any(o['id'] == order['order_id']
                                       for o in asks):
                return
            index = [a['id'] for a in asks].index(order['order_id'])
            asks[index]['size'] = new_size
            self.set_asks(price, asks)

        tree = self._asks if order['side'] == 'sell' else self._bids
        node = tree.get(price)

        if node is None or not any(o['id'] == order['order_id'] for o in node):
            return

    def get_current_ticker(self):
        return self._current_ticker

    def get_current_book(self):
        result = {
            'sequence': self._sequence,
            'asks': [],
            'bids': [],
        }
        for ask in self._asks:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_ask = self._asks[ask]
            except KeyError:
                continue
            for order in this_ask:
                result['asks'].append(
                    [order['price'], order['size'], order['id']])
        for bid in self._bids:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_bid = self._bids[bid]
            except KeyError:
                continue

            for order in this_bid:
                result['bids'].append(
                    [order['price'], order['size'], order['id']])
        return result

    def get_ask(self):
        return self._asks.min_key()

    def get_asks(self, price):
        return self._asks.get(price)

    def remove_asks(self, price):
        self._asks.remove(price)

    def set_asks(self, price, asks):
        self._asks.insert(price, asks)

    def get_bid(self):
        return self._bids.max_key()

    def get_bids(self, price):
        return self._bids.get(price)

    def remove_bids(self, price):
        self._bids.remove(price)

    def set_bids(self, price, bids):
        self._bids.insert(price, bids)
示例#4
0
class BitMEXBook:
    def __init__(self,
                 endpoint="https://www.bitmex.com/api/v1",
                 symbol='XBTUSD'):
        '''Connect to the websocket and initialize data stores.'''
        self.logger = logging.getLogger(__name__)
        self.logger.debug("Initializing WebSocket.")

        self.endpoint = endpoint
        self.symbol = symbol

        self.data = {}
        self.keys = {}
        self.exited = False
        self._asks = RBTree()
        self._bids = RBTree()

        # We can subscribe right in the connection querystring, so let's build that.
        # Subscribe to all pertinent endpoints
        wsURL = self.__get_url()
        self.logger.debug("Connecting to %s" % wsURL)
        self.__connect(wsURL, symbol)
        self.logger.info('Connected to WS, waiting for partials.')

        # Connected. Wait for partials
        self.__wait_for_symbol(symbol)
        self.logger.info('Got all market data. Starting.')

    def init(self):
        self.logger.debug("Initializing WebSocket...")

        self.data = {}
        self.keys = {}
        self.exited = False

        wsURL = self.__get_url()
        self.logger.debug("Connecting to URL -- %s" % wsURL)
        self.__connect(wsURL, self.symbol)
        self.logger.info('Connected to WS, waiting for partials.')

        # Connected. Wait for partials
        self.__wait_for_symbol(self.symbol)
        self.logger.info('Got all market data. Starting.')

    def error(self, err):
        self._error = err
        self.logger.error(err)
        #self.exit()

    def __del__(self):
        self.exit()

    def reset(self):
        self.logger.warning('Websocket resetting...')
        self.ws.close()
        self.logger.info('Weboscket closed.')
        self.logger.info('Restarting...')
        self.init()

    def exit(self):
        '''Call this to exit - will close websocket.'''
        self.exited = True
        self.ws.close()

    ### Main orderbook function
    def get_current_book(self):
        result = {'asks': [], 'bids': []}
        for ask in self._asks:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_ask = self._asks[ask]
            except KeyError:
                continue
            for order in this_ask:
                result['asks'].append(
                    [order['price'], order['size'],
                     order['id']])  #(order['size']/Decimal(order['price']))
        # Same procedure for bids
        for bid in self._bids:
            try:
                this_bid = self._bids[bid]
            except KeyError:
                continue

            for order in this_bid:
                result['bids'].append(
                    [order['price'], order['size'],
                     order['id']])  #(order['size']/Decimal(order['price']))
        return result

    # -----------------------------------------------------------------------------------------
    # ----------------------RBTrees Handling---------------------------------------------------
    # -----------------------------------------------------------------------------------------
    # -----------------------------------------------------------------------------------------

    # Get current minimum ask price from tree
    def get_ask(self):
        return self._asks.min_key()

    # Get ask given id
    def get_asks(self, id):
        return self._asks.get(id)

    # Remove ask form tree
    def remove_asks(self, id):
        self._asks.remove(id)

    # Insert ask into tree
    def set_asks(self, id, asks):
        self._asks.insert(id, asks)

    # Get current maximum bid price from tree
    def get_bid(self):
        return self._bids.max_key()

    # Get bid given id
    def get_bids(self, id):
        return self._bids.get(id)

    # Remove bid form tree
    def remove_bids(self, id):
        self._bids.remove(id)

    # Insert bid into tree
    def set_bids(self, id, bids):
        self._bids.insert(id, bids)

    # Add order to out watched orders
    def add(self, order):
        order = {
            'id': order['id'],  # Order id data
            'side': order['side'],  # Order side data
            'size': Decimal(order['size']),  # Order size data
            'price': order['price']  # Order price data
        }
        if order['side'] == 'Buy':
            bids = self.get_bids(order['id'])
            if bids is None:
                bids = [order]
            else:
                bids.append(order)
            self.set_bids(order['id'], bids)
        else:
            asks = self.get_asks(order['id'])
            if asks is None:
                asks = [order]
            else:
                asks.append(order)
            self.set_asks(order['id'], asks)

    # Order is done, remove it from watched orders
    def remove(self, order):
        oid = order['id']
        if order['side'] == 'Buy':
            bids = self.get_bids(oid)
            if bids is not None:
                bids = [o for o in bids if o['id'] != order['id']]
                if len(bids) > 0:
                    self.set_bids(oid, bids)
                else:
                    self.remove_bids(oid)
        else:
            asks = self.get_asks(oid)
            if asks is not None:
                asks = [o for o in asks if o['id'] != order['id']]
                if len(asks) > 0:
                    self.set_asks(oid, asks)
                else:
                    self.remove_asks(oid)

    # Updating order price and size
    def change(self, order):
        new_size = Decimal(order['size'])
        # Bitmex updates don't come with price, so we use the id to match it instead
        oid = order['id']

        if order['side'] == 'Buy':
            bids = self.get_bids(oid)
            if bids is None or not any(o['id'] == order['id'] for o in bids):
                return
            index = list(map(itemgetter('id'), bids)).index(order['id'])
            bids[index]['size'] = new_size
            self.set_bids(oid, bids)
        else:
            asks = self.get_asks(oid)
            if asks is None or not any(o['id'] == order['id'] for o in asks):
                return
            index = list(map(itemgetter('id'), asks)).index(order['id'])
            asks[index]['size'] = new_size
            self.set_asks(oid, asks)

        tree = self._asks if order['side'] == 'Sell' else self._bids
        node = tree.get(oid)

        if node is None or not any(o['id'] == order['id'] for o in node):
            return

    # -----------------------------------------------------------------------------------------
    # ----------------------WS Private Methods-------------------------------------------------
    # -----------------------------------------------------------------------------------------
    # -----------------------------------------------------------------------------------------

    def __connect(self, wsURL, symbol):
        '''Connect to the websocket in a thread.'''
        self.logger.debug("Starting thread")

        self.ws = websocket.WebSocketApp(wsURL,
                                         on_message=self.__on_message,
                                         on_close=self.__on_close,
                                         on_open=self.__on_open,
                                         on_error=self.__on_error)

        self.wst = threading.Thread(target=lambda: self.ws.run_forever())
        self.wst.daemon = True
        self.wst.start()
        self.logger.debug("Started thread")

        # Wait for connect before continuing
        conn_timeout = CONN_TIMEOUT
        while (not self.ws.sock
               or not self.ws.sock.connected) and conn_timeout:
            sleep(1)
            conn_timeout -= 1
        if not conn_timeout:
            self.logger.error("Couldn't connect to WS! Exiting.")
            # self.exit()
            # raise websocket.WebSocketTimeoutException('Couldn\'t connect to WS! Exiting.')
            self.reset()

    def __get_url(self):
        '''
        Generate a connection URL. We can define subscriptions right in the querystring.
        Most subscription topics are scoped by the symbol we're listening to.
        '''
        symbolSubs = ["orderBookL2"]
        subscriptions = [sub + ':' + self.symbol for sub in symbolSubs]

        urlParts = list(urllib.parse.urlparse(self.endpoint))
        urlParts[0] = urlParts[0].replace('http', 'ws')
        urlParts[2] = "/realtime?subscribe={}".format(','.join(subscriptions))
        return urllib.parse.urlunparse(urlParts)

    def __wait_for_symbol(self, symbol):
        '''On subscribe, this data will come down. Wait for it.'''
        pbar = tqdm(total=160)

        # Wait until data reaches our RBTrees
        while self._asks.is_empty() and self._bids.is_empty():
            sleep(0.1)
            pbar.update(3)
        pbar.close()

    def __send_command(self, command, args=None):
        '''Send a raw command.'''
        if args is None:
            args = []
        self.ws.send(json.dumps({"op": command, "args": args}))

    def __on_message(self, message):
        '''Handler for parsing WS messages.'''
        message = json.loads(message)
        # self.logger.debug(json.dumps(message))

        table = message['table'] if 'table' in message else None
        action = message['action'] if 'action' in message else None
        # table = message.get("table")
        # action = message.get("action")

        try:
            # RBTrees for orderBook
            if table == 'orderBookL2':
                # For every order received
                try:
                    for order in message['data']:
                        if action == 'partial':
                            self.logger.debug('%s: adding partial %s' %
                                              (table, order))
                            self.add(order)
                        elif action == 'insert':
                            self.logger.debug('%s: inserting %s' %
                                              (table, order))
                            self.add(order)
                        elif action == 'update':
                            self.logger.debug('%s: updating %s' %
                                              (table, order))
                            self.change(order)
                        elif action == 'delete':
                            self.logger.debug('%s: deleting %s' %
                                              (table, order))
                            self.remove(order)
                        else:
                            raise Exception("Unknown action: %s" % action)
                except:
                    self.logger.error('Error handling RBTrees: %s' %
                                      traceback.format_exc())

            # Uncomment this to watch RBTrees evolution in real time
            # self.logger.info('==============================================================')
            # self.logger.info('=============================ASKS=============================')
            # self.logger.info('==============================================================')
            # self.logger.info(self._asks)
            # self.logger.info('==============================================================')
            # self.logger.info('=============================BIDS=============================')
            # self.logger.info('==============================================================')
            # self.logger.info(self._bids)

        except:
            self.logger.error(traceback.format_exc())

    def __on_error(self, error):
        '''Called on fatal websocket errors. We exit on these.'''
        if not self.exited:
            self.logger.error("Error : %s" % error)
            raise websocket.WebSocketException(error)

    def __on_open(self):
        '''Called when the WS opens.'''
        self.logger.debug("Websocket Opened.")

    def __on_close(self):
        '''Called on websocket close.'''
        self.logger.info('Websocket Closed')
            print(seen)
 
PrintUnique1(input) #1,5


#Code: Solution 2 (using using design #1 and red black tree)
'''
Why red and black trees are better than hashtables?
Source: http://www.quora.com/Why-would-anyone-like-to-use-a-red-black-tree-when-a-hash-table-can-do-the-job-perfectly 
1. Simple memory management - simplifies error handling in concurrent code, less I/O hits, 
2. Consistent performance because rehashing (expanding the hash table's array) happens on some insertions.
   This is important in real-time systems where you want to provide bounds on how long each operation takes
3. Keys are sorted
'''
#https://bitbucket.org/mozman/bintrees
from bintrees import RBTree
def PrintUnique2(k,v):
    if (v == 1): #unique
        print(k) #print unique
#Initialize
tree = RBTree()        
#Insert
for x in input:
    if (tree.get(x)): #duplicate
         tree.insert(x,tree.get(x)+1) #increment occurrence
    else: #new
        tree.insert(x,1)
#Iterate        
tree.foreach(PrintUnique2,0)  #0=inorder

示例#6
0
class GdaxBookFeed(GdaxWebsocketClient):
    def __init__(self,
                 product_id='LTC-USD',
                 log_to=None,
                 gdax=None,
                 auth=True):

        if gdax is None:
            from stocklook.crypto.gdax.api import Gdax
            gdax = Gdax()

        super(GdaxBookFeed, self).__init__(products=product_id,
                                           auth=auth,
                                           api_key=gdax.api_key,
                                           api_secret=gdax.api_secret,
                                           api_passphrase=gdax.api_passphrase)
        self._asks = None
        self._bids = None
        self._client = gdax
        self._sequence = -1
        self._log_to = log_to
        if self._log_to:
            assert hasattr(self._log_to, 'write')
        self._current_ticker = None
        self._key_errs = 0
        self._errs = 0
        self.message_count = 0

    @property
    def product_id(self):
        '''
        Currently OrderBook only supports a single product even
        though it is stored as a list of products.
        '''
        return self.products[0]

    def on_message(self, message):
        self.message_count += 1
        if self._log_to:
            pickle.dump(message, self._log_to)

        try:
            sequence = message['sequence']
        except KeyError:
            print("Error: {}".format(message))
            self._key_errs += 1
            if self._key_errs >= 3:
                print("3 errors retrieving sequence. Restarting....")
                self.close()
                self._key_errs = 0
                self.start()
            return

        if self._sequence == -1:
            self._asks = RBTree()
            self._bids = RBTree()
            res = self._client.get_book(self.product_id, level=3)
            for bid in res['bids']:
                self.add({
                    'id': bid[2],
                    'side': 'buy',
                    'price': float(bid[0]),
                    'size': float(bid[1])
                })
            for ask in res['asks']:
                self.add({
                    'id': ask[2],
                    'side': 'sell',
                    'price': float(ask[0]),
                    'size': float(ask[1])
                })
            self._sequence = res['sequence']

        if sequence <= self._sequence:
            # ignore older messages (e.g. before order book
            # initialization from getProductOrderBook)
            return
        elif sequence > self._sequence + 1:
            print('Error: messages missing ({} - {}). '
                  'Re-initializing websocket.'.format(sequence,
                                                      self._sequence))
            self.close()
            self.start()
            return

        msg_type = message['type']
        if msg_type == 'open':
            self.add(message)
        elif msg_type == 'done' and 'price' in message:
            self.remove(message)
        elif msg_type == 'match':
            self.match(message)
            self._current_ticker = message
        elif msg_type == 'change':
            self.change(message)

        self._sequence = sequence

        # bid = self.get_bid()
        # bids = self.get_bids(bid)
        # bid_depth = sum([b['size'] for b in bids])
        # ask = self.get_ask()
        # asks = self.get_asks(ask)
        # ask_depth = sum([a['size'] for a in asks])
        # print('bid: %f @ %f - ask: %f @ %f' % (bid_depth, bid, ask_depth, ask))

    def on_error(self, e):
        self._sequence = -1
        self._errs += 1
        self.close()
        if self._errs >= 3:
            raise Exception(e)
        self.start()

    def add(self, order):
        order = {
            'id': order.get('order_id') or order['id'],
            'side': order['side'],
            'price': float(order['price']),
            'size': float(order.get('size') or order['remaining_size'])
        }
        if order['side'] == 'buy':
            bids = self.get_bids(order['price'])
            if bids is None:
                bids = [order]
            else:
                bids.append(order)
            self.set_bids(order['price'], bids)
        else:
            asks = self.get_asks(order['price'])
            if asks is None:
                asks = [order]
            else:
                asks.append(order)
            self.set_asks(order['price'], asks)

    def remove(self, order):
        price = float(order['price'])
        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is not None:
                bids = [o for o in bids if o['id'] != order['order_id']]
                if len(bids) > 0:
                    self.set_bids(price, bids)
                else:
                    self.remove_bids(price)
        else:
            asks = self.get_asks(price)
            if asks is not None:
                asks = [o for o in asks if o['id'] != order['order_id']]
                if len(asks) > 0:
                    self.set_asks(price, asks)
                else:
                    self.remove_asks(price)

    def match(self, order):
        size = float(order['size'])
        price = float(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if not bids:
                return
            assert bids[0]['id'] == order['maker_order_id']
            if bids[0]['size'] == size:
                self.set_bids(price, bids[1:])
            else:
                bids[0]['size'] -= size
                self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if not asks:
                return
            assert asks[0]['id'] == order['maker_order_id']
            if asks[0]['size'] == size:
                self.set_asks(price, asks[1:])
            else:
                asks[0]['size'] -= size
                self.set_asks(price, asks)

    def change(self, order):
        try:
            new_size = float(order['new_size'])
        except KeyError:
            return

        price = float(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is None or not any(o['id'] == order['order_id']
                                       for o in bids):
                return
            index = [b['id'] for b in bids].index(order['order_id'])
            bids[index]['size'] = new_size
            self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if asks is None or not any(o['id'] == order['order_id']
                                       for o in asks):
                return
            index = [a['id'] for a in asks].index(order['order_id'])
            asks[index]['size'] = new_size
            self.set_asks(price, asks)

        tree = self._asks if order['side'] == 'sell' else self._bids
        node = tree.get(price)

        if node is None or not any(o['id'] == order['order_id'] for o in node):
            return

    def get_current_ticker(self):
        return self._current_ticker

    def get_current_book(self):
        result = {
            'sequence': self._sequence,
            'asks': [],
            'bids': [],
        }
        for ask in self._asks:
            try:
                # There can be a race condition here,
                # where a price point is removed
                # between these two ops.
                this_ask = self._asks[ask]
            except KeyError:
                continue
            for order in this_ask:
                bit = [order['price'], order['size'], order['id']]
                result['asks'].append(bit)
        for bid in self._bids:
            try:
                # There can be a race condition here,
                # where a price point is removed
                # between these two ops.
                this_bid = self._bids[bid]
            except KeyError:
                continue

            for order in this_bid:
                result['bids'].append(
                    [order['price'], order['size'], order['id']])
        return result

    def get_orders_matching_ids(self, order_ids):
        return [a for a in self._asks
                if a['id'] in order_ids] +\
               [b for b in self._bids
                if b['id'] in order_ids
        ]

    def get_ask(self):
        return self._asks.min_key()

    def get_asks(self, price):
        return self._asks.get(price)

    def remove_asks(self, price):
        self._asks.remove(price)

    def set_asks(self, price, asks):
        self._asks.insert(price, asks)

    def get_bid(self):
        return self._bids.max_key()

    def get_bids(self, price):
        return self._bids.get(price)

    def remove_bids(self, price):
        self._bids.remove(price)

    def set_bids(self, price, bids):
        self._bids.insert(price, bids)
示例#7
0
Source: http://www.quora.com/Why-would-anyone-like-to-use-a-red-black-tree-when-a-hash-table-can-do-the-job-perfectly 
1. Simple memory management - simplifies error handling in concurrent code, less I/O hits, 
2. Consistent performance because rehashing (expanding the hash table's array) happens on some insertions.
   This is important in real-time systems where you want to provide bounds on how long each operation takes
3. Keys are sorted
'''
#https://bitbucket.org/mozman/bintrees
from bintrees import RBTree
def PrintUnique2(k,v):
    if (v == 1): #unique
        print(k) #print unique
#Initialize
tree = RBTree()        
#Insert
for x in input:
    if (tree.get(x)): #duplicate
         tree.insert(x,tree.get(x)+1) #increment occurrence
    else: #new
        tree.insert(x,1)
#Iterate        
tree.foreach(PrintUnique2,0)  #0=inorder



#Solution 3
'''
REFERENCE:
http://www.geeksforgeeks.org/find-duplicates-in-on-time-and-constant-extra-space/

LIMITATIONS:
1. n elements with value > 0 and < n
示例#8
0
class MarinerOrderBook(GDAX.OrderBook):
    def __init__(self, ticker, threshold):
        GDAX.OrderBook.__init__(self, product_id = ticker)
        self._threshold = threshold
        self._buyWhales = RBTree()
        self._sellWhales = RBTree()
        self._topBuyWhale = None
        self._topSellWhale = None

    def registerHandlers(self, topWhaleChangedHandler):
        print("    registering callbacks...")
        self.topWhaleChangedHandler = topWhaleChangedHandler

    def onMessage(self, message):
        sequence = message['sequence']
        if self._sequence == -1:
            self._asks = RBTree()
            self._bids = RBTree()
            res = self._client.getProductOrderBook(level=3)
            for bid in res['bids']:
                self.add({
                    'id': bid[2],
                    'side': 'buy',
                    'price': Decimal(bid[0]),
                    'size': Decimal(bid[1])
                })
            for ask in res['asks']:
                self.add({
                    'id': ask[2],
                    'side': 'sell',
                    'price': Decimal(ask[0]),
                    'size': Decimal(ask[1])
                })
            self._sequence = res['sequence']

        if sequence <= self._sequence:
            # ignore older messages (e.g. before order book initialization from getProductOrderBook)
            return
        elif sequence > self._sequence + 1:
            print('Error: messages missing ({} - {}). Re-initializing websocket.'.format(sequence, self._sequence))
            self.close()
            self.start()
            return

        msg_type = message['type']
        if msg_type == 'open':
            self.add(message)
        elif msg_type == 'done' and 'price' in message:
            self.remove(message)
        elif msg_type == 'match':
            self.match(message)
        elif msg_type == 'change':
            self.change(message)

        self._sequence = sequence


    def add(self, order):
        order = {
            'id': order.get('order_id') or order['id'],
            'side': order['side'],
            'price': Decimal(order['price']),
            'size': Decimal(order.get('size') or order['remaining_size'])
        }

        if order['side'] == 'buy':
            self.checkWhaleAddBuy(order)
            bids = self.get_bids(order['price'])
            if bids is None:
                bids = [order]
            else:
                bids.append(order)
            self.set_bids(order['price'], bids)
        else:
            self.checkWhaleAddSell(order)
            asks = self.get_asks(order['price'])
            if asks is None:
                asks = [order]
            else:
                asks.append(order)
            self.set_asks(order['price'], asks)


    def checkWhaleAddBuy(self, order):
        price = Decimal(order['price'])
        volume = Decimal(order['size'])
        if self.isWhale(volume): #too low in practice
            print("WHALE ENTERED (BUY): price=" + str(price) + " volume=" + str(volume))
            whale_bid_level = self.get_whale_bids(price)
            if whale_bid_level is None:
                whale_bid_level = WhaleLevel(price, volume)
            else:
                whale_bid_level.add_volume(volume)
            self.set_whale_bids(price, whale_bid_level)
            top_whale_price = self.get_whale_bid()
            if  self._topBuyWhale is None or top_whale_price != self._topBuyWhale.get_price():
                self._topBuyWhale = self._buyWhales.get(top_whale_price)
                self.topWhaleChangedHandler(self._topBuyWhale, "BUY")
            else:
                print("    top BUY whale is still " + str(self._topBuyWhale))

    def checkWhaleAddSell(self, order):
        price = Decimal(order['price'])
        volume = Decimal(order['size'])
        if self.isWhale(volume): #too low in practice
            print("WHALE ENTERED (SELL): price=" + str(price) + " volume=" + str(volume))
            whale_ask_level = self.get_whale_asks(price)
            if whale_ask_level is None:
                whale_ask_level = WhaleLevel(price, volume)
            else:
                whale_ask_level.add_volume(volume)
            self.set_whale_asks(price, whale_ask_level)
            top_whale_price = self.get_whale_ask()
            if self._topSellWhale is None or top_whale_price != self._topSellWhale.get_price():
                self._topSellWhale = self._sellWhales.get(top_whale_price)
                self.topWhaleChangedHandler(self._topSellWhale, "SELL")
            else:
                print("    top SELL whale is still " + str(self._topSellWhale))


    def remove(self, order):
        order = {
            'id': order.get('order_id') or order['id'],
            'side': order['side'],
            'price': Decimal(order['price']),
            'size': Decimal(order.get('size') or order['remaining_size'])
        }
        price = Decimal(order['price'])
        if order['side'] == 'buy':
            self.checkWhaleRemoveBuy(order)
            bids = self.get_bids(price)
            if bids is not None:
                bids = [o for o in bids if o['id'] != order['id']]
                if len(bids) > 0:
                    self.set_bids(price, bids)
                else:
                    self.remove_bids(price)
        else:
            self.checkWhaleRemoveSell(order)
            asks = self.get_asks(price)
            if asks is not None:
                asks = [o for o in asks if o['id'] != order['id']]
                if len(asks) > 0:
                    self.set_asks(price, asks)
                else:
                    self.remove_asks(price)


    def checkWhaleRemoveBuy(self, order):
        price = Decimal(order['price'])
        volume = Decimal(order['size'])
        whale_bid_level = self.get_whale_bids(price)
        if whale_bid_level is not None:
            whale_bid_level.remove_volume(volume)
            print("WHALE LEFT (BUY): price=" + str(price) + " volume=" + str(volume))
            if not self.isWhale(whale_bid_level.get_volume()):
                self.remove_whale_bids(price)
                top_whale_price = self.get_whale_bid()
                self._topBuyWhale = self._buyWhales.get(top_whale_price)
                self.topWhaleChangedHandler(self._topBuyWhale, "BUY")
            else:
                print("    top BUY whale is still " + str(self._topBuyWhale))


    def checkWhaleRemoveSell(self, order):
        price = Decimal(order['price'])
        volume = Decimal(order['size'])
        whale_ask_level = self.get_whale_asks(price)
        if whale_ask_level is not None:
            whale_ask_level.remove_volume(volume)
            print("WHALE ENTERED (SELL): price=" + str(price) + " volume=" + str(volume))
            if not self.isWhale(whale_ask_level.get_volume()):
                self.remove_whale_asks(price)
                top_whale_price = self.get_whale_ask()
                self._topSellWhale = self._sellWhales.get(top_whale_price)
                self.topWhaleChangedHandler(self._topSellWhale, "SELL")
            else:
                print("    top SELL whale is still " + str(self._topSellWhale))


    def match(self, order):
        size = Decimal(order['size'])
        price = Decimal(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if not bids:
                return
            assert bids[0]['id'] == order['maker_order_id']
            if bids[0]['size'] == size:
                self.set_bids(price, bids[1:])
            else:
                bids[0]['size'] -= size
                self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if not asks:
                return
            assert asks[0]['id'] == order['maker_order_id']
            if asks[0]['size'] == size:
                self.set_asks(price, asks[1:])
            else:
                asks[0]['size'] -= size
                self.set_asks(price, asks)


    def change(self, order):
        price = Decimal(order['price'])
        new_volume = Decimal(order['new_size'])
        if order['side'] == 'buy':
            self.checkWhaleChangeBuy(order)
            bids = self.get_bids(price)
            if bids is None or not any(o['id'] == order['order_id'] for o in bids):
                return
            index = map(itemgetter('id'), bids).index(order['order_id'])
            bids[index]['size'] = new_size
            self.set_bids(price, bids)
        else:
            self.checkWhaleChangeSell(order)
            asks = self.get_asks(price)
            if asks is None or not any(o['id'] == order['order_id'] for o in asks):
                return
            index = map(itemgetter('id'), asks).index(order['order_id'])
            asks[index]['size'] = new_size
            self.set_asks(price, asks)

        tree = self._asks if order['side'] == 'sell' else self._bids
        node = tree.get(price)

        if node is None or not any(o['id'] == order['order_id'] for o in node):
            return


    def checkWhaleChangeBuy(self, order):
        price = Decimal(order['price'])
        new_volume = order['new_size']
        whale_bid_level = self.get_whale_bids(price)
        if not whale_bid_level == None:
            print("BUY WHALE CHANGED: price=" + price + " new_volume=" + new_volume)
            whale_bid_level.setVolume(new_volume)
            self.set_whale_bids(price, whale_bid_level)


    def checkWhaleChangeSell(self, order):
        price = Decimal(order['price'])
        new_volume = order['new_size']
        whale_ask_level = self.get_whale_asks(price)
        if not whale_ask_level == None:
            print("SELL WHALE CHANGED: price=" + price + " new_volume=" + new_volume)
            whale_ask_level.setVolume(new_volume)
            self.set_whale_asks(price, whale_ask_level)


    def get_current_book(self):
        result = {
            'sequence': self._sequence,
            'asks': [],
            'bids': [],
        }
        for ask in self._asks:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                thisAsk = self._asks[ask]
            except KeyError:
                continue
            for order in thisAsk:
                result['asks'].append([
                    order['price'],
                    order['size'],
                    order['id'],
                ])
        for bid in self._bids:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                thisBid = self._bids[bid]
            except KeyError:
                continue

            for order in thisBid:
                result['bids'].append([
                    order['price'],
                    order['size'],
                    order['id'],
                ])
        return result


    def isWhale(self, aVolume):
        return aVolume >= self._threshold

    #For general book management purposes
    def get_ask(self):
        return self._asks.min_key()


    def get_asks(self, price):
        return self._asks.get(price)


    def remove_asks(self, price):
        self._asks.remove(price)


    def set_asks(self, price, asks):
        self._asks.insert(price, asks)


    def get_bid(self):
        return self._bids.max_key()


    def get_bids(self, price):
        return self._bids.get(price)


    def remove_bids(self, price):
        self._bids.remove(price)


    def set_bids(self, price, bids):
        self._bids.insert(price, bids)


    #For whale book management purposes

    def get_whale_ask(self):
        return self._sellWhales.min_key()


    def get_whale_asks(self, price):
        return self._sellWhales.get(price)


    def remove_whale_asks(self, price):
        self._sellWhales.remove(price)


    def set_whale_asks(self, price, asks):
        self._sellWhales.insert(price, asks)


    def get_whale_bid(self):
        return self._buyWhales.max_key()

    def get_whale_bids(self, price):
        return self._buyWhales.get(price)


    def remove_whale_bids(self, price):
        self._buyWhales.remove(price)


    def set_whale_bids(self, price, bids):
        self._buyWhales.insert(price, bids)
示例#9
0
class OrderBook():
    def __init__(self, market=None, bids=None, asks=None, log_to=None):
        self._asks = RBTree()
        self._bids = RBTree()
        self.book_valid = False
        self.new_book(bids, asks)
        self._sequence = -1
        self.market = market
        self._log_to = log_to
        if self._log_to:
            assert hasattr(self._log_to, 'write')
        # My order Details
        self.total_order_count = 0
        self.total_open_order_count = 0
        self.pending_buy_orders_db = {}
        self.pending_sell_orders_db = {}
        self.traded_buy_orders_db = {}
        self.traded_sell_orders_db = {}

        # positions
        self.all_positions = []
        self.open_positions = []
        self.close_pending_positions = {}
        self.closed_positions = []

        self.orderDb = db.OrderDb(Order, market.exchange_name,
                                  market.product_id)
        self.positionsDb = db.PositionDb(Position, market.exchange_name,
                                         market.product_id)
        #stop loss handling
        self.sl_dict = sorteddict.SortedDict()

        #take profit handling
        self.tp_dict = sorteddict.SortedDict()

        #trade Reqs
        self.pending_trade_req = [
        ]  # TODO: FIXME: jork: this better be a nice AVL tree of sort

    def __str__(self):
        return """
{"position_all": %d, "open": %d, "close_pending": %d, "closed": %d}""" % (
            len(self.all_positions), len(self.open_positions),
            len(self.close_pending_positions), len(self.closed_positions))

    def add_pending_trade_req(self, trade_req):
        self.pending_trade_req.append(trade_req)

    def remove_pending_trade_req(self, trade_req):
        # primitive
        self.pending_trade_req.remove(trade_req)

    def open_position(self, buy_order):
        #open positions with buy orders only(we don't support 'short' now)
        #         log.debug("open_position order: %s"%(buy_order))
        position = Position(id=buy_order.id)
        position.add_buy(buy_order)
        if self.market.tradeConfig["stop_loss_enabled"]:
            self.add_stop_loss_position(position, buy_order.get_price(),
                                        self.market, buy_order.stop)
        if self.market.tradeConfig["take_profit_enabled"]:
            self.add_take_profit_position(position, buy_order.get_price(),
                                          self.market, buy_order.profit)

        self.all_positions.append(position)
        self.open_positions.append(position)
        #         log.debug("\n\n\n***open_position: open(%d) closed(%d) close_pend(%d)"%(len(self.open_positions), len(self.closed_positions), len(self.close_pending_positions)))
        # save pos to db
        self.positionsDb.db_save_position(position)

    def get_closable_position(self):
        log.info("get_closable_position ")

        #get last open position for now
        # TODO: FIXME: This may not be the best way. might cause race with below api with multi thread/multi exch
        pos = None
        if len(self.open_positions):
            if self.market.tradeConfig["stop_loss_enabled"]:
                log.info("Finding closable position from _stop_loss_ pool.")
                pos = self.pop_stop_loss_position()
            if pos == None:
                try:
                    pos = self.open_positions.pop()
                except IndexError:
                    log.error("unable to find open position to close")
                    return None
                log.info(
                    "Found closable position from _regular_ pool. pos: %s" %
                    (str(pos)))
            else:
                log.info(
                    "Found closable position from _stop_loss_ pool. pos: %s" %
                    (str(pos)))
                self.open_positions.remove(pos)
            if (self.close_pending_positions.get(pos.id)):
                log.critical("""Position already close pending \npos:%s
                     close_pending_positions: %s
                     open_positions: %s""" %
                             (str(pos), str(self.close_pending_positions),
                              str(self.open_positions)))
                raise Exception("Duplicate close pending position")

            if self.market.tradeConfig["take_profit_enabled"]:
                self.pop_take_profit_position(pos)

            self.close_pending_positions[pos.id] = pos
#         log.debug("\n\n\n***get_closable_position: open(%d) closed(%d) close_pend(%d) \n pos: %s"%(
#             len(self.open_positions), len(self.closed_positions), len(self.close_pending_positions), pos))
        return pos

    def close_position_pending(self, sell_order):
        # Intentionally _not_ updating the pos_db here. Yes, There is a case of wrong pos state if we go down come back during this state
        # TODO: FIXME: This may not be the best way. might cause race with below api with multi thread/multi exch
        log.info(" order:%s" % (sell_order.id))
        #         log.debug("\n\n\n***: open(%d) closed(%d) close_pend(%d)\n"%(
        #             len(self.open_positions), len(self.closed_positions), len(self.close_pending_positions)))
        pos = self.close_pending_positions.get(sell_order.id)
        if pos:
            log.info(": sell order(%s) already in pending_list. do nothing" %
                     (sell_order.id))
            return pos
        #find a close_pending pos without sell attached.
        k = sell_order._pos_id
        if not k:
            log.critical(
                "*****Invalid pos_id attached to order:%s**** \n may be an outside order"
                % (sell_order.id))
            raise Exception("Invalid pos_id attached to order")
        pos = self.close_pending_positions.get(k)
        if pos:
            #find the pos without sell attached. and reinsert after attach
            #             log.debug("pos:\n%s"%(pos))
            if pos.sell == None:
                pos.add_sell(sell_order)
                pos.update_state("close_pending")
                del (self.close_pending_positions[k])
                self.close_pending_positions[sell_order.id] = pos

                #                 log.debug("\n\n\n***: open(%d) closed(%d) close_pend(%d)"%(len(self.open_positions), len(self.closed_positions), len(self.close_pending_positions)))
                return pos
            else:
                log.critical("Wrong sell attached to pos:%s" % (pos))
                raise Exception("Wrong sell attached to pos")
        else:
            #something is very wrong
            log.critical("Unable to find pending position for close id: %s" %
                         (sell_order.id))
            raise Exception("Unable to find pending position for close")

    def close_position_failed(self, pos_id):
        log.info("close_position_failed order: %s" % (pos_id))
        #         log.debug("\n\n\n***close_position_failed: open(%d) closed(%d) close_pend(%d)"%(len(self.open_positions), len(self.closed_positions), len(self.close_pending_positions)))

        #         id = sell_order.id
        position = self.close_pending_positions.get(pos_id)
        if position:
            position.sell = None
            self.close_pending_positions.pop(pos_id, None)
            self.open_positions.append(position)
            if self.market.tradeConfig["stop_loss_enabled"]:
                self.add_stop_loss_position(position, position.buy.get_price(),
                                            self.market,
                                            position.get_stop_loss())
            if self.market.tradeConfig["take_profit_enabled"]:
                self.add_take_profit_position(position,
                                              position.buy.get_price(),
                                              self.market,
                                              position.get_take_profit())
        else:
            log.critical("Unable to get close_pending position. order_id: %s" %
                         (pos_id))

    def close_position(self, sell_order):
        log.info("close_position order: %s" % (sell_order.id))
        id = sell_order.id
        position = self.close_pending_positions.pop(id, None)
        if position:
            position.add_sell(sell_order)
            position.update_state("closed")
            profit = position.get_profit()
            self.market.fund.current_realized_profit += profit
            if profit > 0:
                self.market.num_success_trade += 1
            else:
                self.market.num_failed_trade += 1
            self.closed_positions.append(position)

            #update position
            self.positionsDb.db_save_position(position)
            log.info("position closed pos: %s" % (str(position)))
        else:
            log.critical("Unable to get close_pending position. order_id: %s" %
                         (sell_order.id))
#         log.debug("\n\n\n***close_position: open(%d) closed(%d) close_pend(%d)\n pos:%s"%(
#             len(self.open_positions), len(self.closed_positions), len(self.close_pending_positions), position))

    def add_stop_loss_position(self,
                               position,
                               market_rate,
                               market,
                               stop_price=0):

        sl_kind = market.tradeConfig['stop_loss_kind']
        if sl_kind in ['trailing', 'simple']:
            sl_rate = market.tradeConfig["stop_loss_rate"]
            stop_price = float(
                round(market_rate * (1 - sl_rate * float(.01)), 4))
        elif 'ATR' in sl_kind:
            atr = market.get_cur_indicators()[
                market.tradeConfig['stop_loss_kind']]
            stop_price = float(round(market_rate - 2 * atr, 4))
        elif sl_kind == "strategy":
            if stop_price == 0:
                log.critical("strategy provided invalid stop loss value")
                return
        else:
            raise Exception("Unknown  stop_loss kind - " +
                            market.tradeConfig['stop_loss_kind'])

        position.set_stop_loss(stop_price)

        pos_list = self.sl_dict.get(stop_price, None)
        if pos_list == None:
            pos_list = []
            self.sl_dict[stop_price] = pos_list
        pos_list.append(position)

    def add_stop_loss_position_list(self, stop_price, position_l):
        [pos.set_stop_loss(stop_price) for pos in position_l]
        pos_list = self.sl_dict.get(stop_price, None)
        if pos_list == None:
            pos_list = []
            self.sl_dict[stop_price] = pos_list
        pos_list += position_l

    def remove_all_positions_at_stop(self, stop_price):
        return self.sl_dict.pop(stop_price, None)

    def smart_stop_loss_update_positions(self, cur_indicators, market_rate,
                                         tcfg):

        if tcfg['stop_loss_kind'] == 'trailing':
            sl_rate = tcfg["stop_loss_rate"]
            new_sl = float(round(market_rate * (1 - sl_rate * float(.01)), 4))
        elif 'ATR' in tcfg['stop_loss_kind']:
            atr = cur_indicators[tcfg['stop_loss_kind']]
            new_sl = float(round(market_rate - 2 * atr, 4))
        else:
            raise Exception("Unknown  smart stop_loss kind %s" %
                            (tcfg['stop_loss_kind']))

        key_list = list(
            self.sl_dict.irange(maximum=new_sl, inclusive=(False, False)))

        for key in key_list:
            #             log.critical("new_sl: %d key: %d"%(new_sl, key))
            pos_list = self.sl_dict.pop(key)
            self.add_stop_loss_position_list(new_sl, pos_list)

    def db_commit_dirty_positions(self):
        dirty_pos_list = []
        for pos in self.all_positions:
            if pos._dirty == True:
                pos._dirty = False
                dirty_pos_list.append(pos)

        if len(dirty_pos_list):
            log.debug("commit positions to db")
            # save pos to db
            self.positionsDb.db_save_positions(dirty_pos_list)

    def pop_stop_loss_position(self, pos=None):
        try:
            sl_price, pos_list = 0, None
            if pos:
                sl_price = pos.get_stop_loss()
                pos_list = self.sl_dict.get(sl_price)
            else:
                #get the lowest in the sorted SL list
                sl_price, pos_list = self.sl_dict.peekitem(index=0)
#             log.debug("pop position at sl_price:%d"%(sl_price))
            if pos_list and len(pos_list):
                if pos:
                    pos_list.remove(pos)
                else:
                    pos = pos_list.pop()
                if len(pos_list) == 0:
                    del (self.sl_dict[sl_price])
            return pos
        except (IndexError, ValueError):
            return None

    def get_stop_loss_positions(self, market_rate):
        sl_pos_list = []

        key_list = list(
            self.sl_dict.irange(minimum=market_rate, inclusive=(True, True)))
        #         log.critical("slPrice: %d"%market_rate)
        #         log.critical("key_list :%s"%(key_list))

        for key in key_list:
            pos_list = self.sl_dict.pop(key)
            sl_pos_list += pos_list
            for pos in pos_list:
                self.open_positions.remove(pos)
                if (self.close_pending_positions.get(pos.id)):
                    log.critical("""Position already close pending \npos:%s
                     close_pending_positions: %s
                     open_positions: %s""" %
                                 (str(pos), str(self.close_pending_positions),
                                  str(self.open_positions)))
                    raise Exception("Duplicate close pending position")
                self.close_pending_positions[pos.id] = pos
                # remove pos from take profit points
                self.pop_take_profit_position(pos)
        self.market.num_stop_loss_hit += len(sl_pos_list)

        #         if len(sl_pos_list):
        #             log.critical("num_stop_loss_hit: %d slPrice: %d"%(len(sl_pos_list), market_rate))

        return sl_pos_list

    def get_take_profit_positions(self, market_rate):
        tp_pos_list = []

        key_list = list(
            self.tp_dict.irange(maximum=market_rate, inclusive=(True, True)))

        for key in key_list:
            pos_list = self.tp_dict.pop(key)
            tp_pos_list += pos_list
            for pos in pos_list:
                self.open_positions.remove(pos)
                if (self.close_pending_positions.get(pos.id)):
                    log.critical("""Position already close pending \npos:%s
                     close_pending_positions: %s
                     open_positions: %s""" %
                                 (str(pos), str(self.close_pending_positions),
                                  str(self.open_positions)))
                    raise Exception("Duplicate close pending position")
                self.close_pending_positions[pos.id] = pos
                # remove pos from take profit points
                self.pop_stop_loss_position(pos)

        self.market.num_take_profit_hit += len(tp_pos_list)
        return tp_pos_list

    def add_take_profit_position(self,
                                 position,
                                 market_rate,
                                 market,
                                 new_tp=0):
        tp_kind = market.tradeConfig['take_profit_kind']
        if tp_kind == 'simple':
            tp_rate = market.tradeConfig["take_profit_rate"]
            new_tp = float(round(market_rate * (1 + tp_rate * float(.01)), 4))
        elif tp_kind == 'strategy':
            if new_tp == 0:
                log.critical("strategy provided invalid take profit value")
                return
        else:
            raise Exception("Unknown  take profit kind - " +
                            market.tradeConfig['take_profit_kind'])

        position.set_take_profit(new_tp)
        pos_list = self.tp_dict.get(new_tp, None)
        if pos_list == None:
            pos_list = []
            self.tp_dict[new_tp] = pos_list
        pos_list.append(position)
        log.debug("add take profit(%d) market_rate:(%d)" %
                  (new_tp, market_rate))

    def pop_take_profit_position(self, pos=None):
        try:
            tp_price, pos_list = 0, None
            if pos:
                tp_price = pos.get_take_profit()
                pos_list = self.tp_dict.get(tp_price)
            else:
                tp_price, pos_list = self.tp_dict.peekitem(0)
#             log.debug("pop position at sl_price:%d"%(sl_price))
            if pos_list and len(pos_list):
                if pos:
                    pos_list.remove(pos)
                else:
                    pos = pos_list.pop()
                if len(pos_list) == 0:
                    del (self.tp_dict[tp_price])
            return pos
        except (IndexError, ValueError):
            return None

    def get_all_pending_orders(self):
        pending_order_list = []
        pending_order_list += list(self.pending_buy_orders_db.values())
        pending_order_list += list(self.pending_sell_orders_db.values())
        return pending_order_list

    def add_or_update_pending_buy_order(self, order):
        id = order.id
        cur_order = self.pending_buy_orders_db.get(id)
        if not cur_order:
            self.total_open_order_count += 1
            self.total_order_count += 1
        else:
            #copy required fields
            order.stop = cur_order.stop
            order.profit = cur_order.profit
        self.pending_buy_orders_db[id] = order

    def get_pending_buy_order(self, order_id):
        return self.pending_buy_orders_db.get(order_id)

    def add_traded_buy_order(self, order):
        cur_order = self.pending_buy_orders_db.get(order.id)
        if cur_order:
            #copy required fields
            order.stop = cur_order.stop
            order.profit = cur_order.profit
        self.total_open_order_count -= 1
        del (self.pending_buy_orders_db[order.id])
        self.traded_buy_orders_db[order.id] = order
        #if this is a successful order, we have a new position open
        if order.status == "filled":
            self.open_position(order)

    def get_traded_buy_order(self, order_id):
        return self.traded_buy_orders_db.get(order_id)

    def add_or_update_pending_sell_order(self, order):
        id = order.id
        if not self.pending_sell_orders_db.get(id):
            self.total_open_order_count += 1
            self.total_order_count += 1
        self.pending_sell_orders_db[id] = order

    def get_pending_sell_order(self, order_id):
        self.pending_sell_orders_db.get(order_id)

    def add_traded_sell_order(self, order):
        del (self.pending_sell_orders_db[order.id])
        self.total_open_order_count -= 1
        self.traded_sell_orders_db[order.id] = order
        #close/reopen position
        #TODO: TBD: more checks required??
        if order.status == "filled":
            log.debug("closed position order: %s" % (order.id))
            self.close_position(order)
        else:
            log.critical("closed position failed order: %s" % (order))
            self.close_position_failed(order.id)

    def get_traded_sell_order(self, order_id):
        return self.traded_sell_orders_db.get(order_id)

    def add_order_list(self, bids, asks):
        if (asks):
            self.add_asks(asks)
        if (bids):
            self.add_bids(bids)

    def clear_order_book(self):
        log.info("clearing older states")
        self.orderDb.clear_order_db()
        self.positionsDb.clear_position_db()

    def restore_order_book(self):
        # TODO: FIXME:  Not considering pending state orders

        if (sims.simulator_on):
            #don't do order db init for sim
            return None

        log.info("Restoring positions and orders")

        #1. Retrieve states back from Db
        order_list = self.orderDb.get_all_orders()

        if not order_list:
            log.info("no orders to restore")
        else:
            # restore orders
            log.info("Restoring %d orders" % (len(order_list)))
            for order in order_list:
                #             order_status = order.status_type
                order_side = order.side

                log.info("restoring order: %s side: %s" %
                         (order.id, order_side))
                self.total_order_count += 1
                if order_side == 'buy':
                    self.traded_buy_orders_db[order.id] = order
                else:
                    self.traded_sell_orders_db[order.id] = order

        # restore positions
        pos_list = self.positionsDb.db_get_all_positions(self.orderDb)
        #         log.critical("mapping: %s"%(str(self.positionsDb.mapping)))
        if not pos_list:
            log.info("no positions to restore")
            return

        log.info("Restoring %d positions" % (len(pos_list)))
        for pos in pos_list:
            log.debug("restoring position(%s)" % (pos.id))
            self.all_positions.append(pos)
            if pos.status == "open":
                self.open_positions.append(pos)
                if self.market.tradeConfig["stop_loss_enabled"]:
                    self.add_stop_loss_position(pos, pos.buy.get_price(),
                                                self.market,
                                                pos.get_stop_loss())
                if self.market.tradeConfig["take_profit_enabled"]:
                    self.add_take_profit_position(pos, pos.buy.get_price(),
                                                  self.market,
                                                  pos.get_take_profit())
            else:
                self.closed_positions.append(pos)

        log.info("all positions and orders are restored")
#         sys.exit()

    def get_positions(self, from_time=0, to_time=0):
        log.info("get positions ", from_time, to_time)
        return self.all_positions[:]

    def dump_traded_orders(self, fd=sys.stdout):
        traded = str(
            list(self.traded_buy_orders_db.values()) +
            list(self.traded_sell_orders_db.values()))
        fd.write(traded)

    def dump_positions(self, fd=sys.stdout):
        fd.write(str(self.all_positions))
#
#     def on_sequence_gap(self, gap_start, gap_end):
#         self.reset_book()
#         print('Error: messages missing({} - {}). Re-initializing  book at sequence.'.format(
#             gap_start, gap_end, self._sequence))

####### Public API #######

    def add_or_update_my_order(self, order):
        # simplified order state machine :[open, filled, canceled]
        # this rework is assumed an abstraction and handles only simplified order status
        # if there are more order states, it should be handled/translated in the exch impl.
        # linked to market.order_status_update()
        #         '''
        #         Handle a new order update msg
        #         return : order
        #         '''
        if (not order):
            return None
        order_id = order.id
        order_status = order.status
        order_side = order.side
        if (not order_id):
            log.critical("Invalid order_id: status:%s side: %s" %
                         (order_status, order_side))
            raise Exception("Invalid order_id: status:%s side: %s" %
                            (order_status, order_side))

        if (order_side == 'buy'):
            # see if this is a late/mixed up state msg for an already done order. What we do here, may not be correct
            if self.get_traded_buy_order(order_id):
                log.critical(
                    "********(%s) order done already, but(%s) state msg recvd, ignore for now, FIXME: FIXME:"
                    % (order_side, order_status))
                return None
            # insert/replace the order
            if (order_status == 'filled' or order_status == 'canceled'):
                # a previously placed order is completed, remove from open order, add to completed orderlist
                self.add_traded_buy_order(order)
                log.debug("Buy order Done: total_order_count: %d "
                          "total_open_order_count: %d "
                          "traded_buy_orders_count: %d" %
                          (self.total_order_count, self.total_open_order_count,
                           len(self.traded_buy_orders_db)))
            elif (order_status == 'open'):
                # Nothing much to do for us here
                log.info("Buy order_id(%s) Status: %s" %
                         (str(order_id), order_status))
                self.add_or_update_pending_buy_order(order)
            else:
                log.critical("UNKNOWN buy order status: %s" % (order_status))
                raise Exception("UNKNOWN buy order status: %s" %
                                (order_status))
        elif (order_side == 'sell'):
            # see if this is a late/mixed up state msg for an already done order. What we do here, may not be correct
            if self.get_traded_sell_order(order_id):
                log.critical(
                    "********(%s) order done already, but(%s) state msg recvd, ignore for now, FIXME: FIXME:"
                    % (order_side, order_status))
                return None
            # insert/replace the order
            if (order_status == 'filled' or order_status == 'canceled'):
                # a previously placed order is completed, remove from open order, add to completed orderlist
                self.add_traded_sell_order(order)
                log.debug("Sell order Done: total_order_count: %d "
                          "total_open_order_count: %d "
                          "traded_sell_orders_count: %d" %
                          (self.total_order_count, self.total_open_order_count,
                           len(self.traded_sell_orders_db)))
            elif (order_status == 'open'):
                # Nothing much to do for us here
                log.info("Sell order_id(%s) Status: %s" %
                         (str(order_id), order_status))
                self.add_or_update_pending_sell_order(order)
                self.close_position_pending(order)
            else:
                log.critical("UNKNOWN sell order status: %s" % (order_status))
                raise Exception("UNKNOWN buy order status: %s" %
                                (order_status))
        else:
            log.critical("Invalid order :%s" % (order))
            raise Exception("Invalid order :%s" % (order))
#         log.debug("Order: %s\n"%(str(order)))

#Add the successful order to the db
        self.orderDb.db_add_or_update_order(order)
        stats.stats_update_order(self.market, order)
        return order

    def new_book(self, bids, asks):
        log.info("Building new order book")
        if (bids and len(bids)) or (asks and len(asks)):
            self.add_order_list(bids, asks)
            self.book_valid = True
        else:
            self.book_valid = False

    def reset_book(self):
        self._asks = RBTree()
        self._bids = RBTree()
        res = self.market.exchange.get_product_order_book(
            self.market.product_id, level=3)
        # log.debug("%s"%(str(res)))
        if res == None:
            log.error("Unable to get orderbook for exchange(%s) product: %s" %
                      (self.market.exchange.name, self.market.product_id))
            return
        for bid in res['bids']:
            new_size = float(bid[1])
            price = float(bid[0])
            new_size += float((self.get_bids(price) or 0))
            self.set_bids(price, new_size)
        for ask in res['asks']:
            new_size = float(ask[1])
            price = float(ask[0])
            new_size += float((self.get_asks(price) or 0))
            self.set_asks(price, new_size)
        self._sequence = float(res['sequence'])
        self.book_valid = True
#         print("asks: %s"%(str(self._asks)))
#         print("bids: %s"%(str(self._bids)))

    def add_asks(self, asks):
        '''
        asks =[[price, size]]
        '''
        for ask in asks:
            price = float(ask[0])
            size = float(ask[1])
            if size > 0:  # size > 0 add, size = 0 remove
                self.set_asks(price, size)
            else:
                if (self.get_asks(price)):
                    self.remove_asks(price)

    def get_ask(self):
        return self._asks.min_key()

    def get_asks(self, price):
        return self._asks.get(price)

    def remove_asks(self, price):
        self._asks.remove(price)

    def set_asks(self, price, asks):
        price = round(price, 8)
        asks = round(asks, 8)
        log.debug("set_asks: price: %g size: %g" % (price, asks))
        self._asks.insert(price, asks)

    def add_bids(self, bids):
        '''
        bids =[[price, size]]
        '''
        for bid in bids:
            price = float(bid[0])
            size = float(bid[1])
            if size > 0:  # size > 0 add, size = 0 remove
                self.set_bids(price, size)
            else:
                if (self.get_bids(price)):
                    self.remove_bids(price)

    def get_bid(self):
        return self._bids.max_key()

    def get_bids(self, price):
        return self._bids.get(price)

    def remove_bids(self, price):
        self._bids.remove(price)

    def set_bids(self, price, bids):
        price = round(price, 8)
        bids = round(bids, 8)
        log.debug("set_bid: price: %g size: %g" % (price, bids))
        self._bids.insert(
            price, bids)  # insert on RBtree is a replace for existing keys
示例#10
0
class OrderBook:
    def __init__(self,
                 API_KEY,
                 API_SECRET,
                 API_PASS,
                 API_URL,
                 WSS_URL,
                 products,
                 M_bidask,
                 auth_user_msg_q,
                 M_get_depth,
                 authclient,
                 message_type="subscribe",
                 auth=True,
                 level=True):
        self.authclient = authclient
        self.WSS_URL = WSS_URL
        self.API_URL = API_URL
        self.products = products
        self.type = message_type
        self.stop = False
        self.ws = None
        self.thread = None
        self.lock = Lock()
        self.auth = auth
        self.API_KEY = API_KEY
        self.API_SECRET = API_SECRET
        self.API_PASS = API_PASS
        self._sequence = -1
        self._bid = None
        self._ask = None
        self._bid_depth = None
        self._ask_depth = None
        self.price = None
        self.level = level
        self.pd_data_set = None
        self.buyop = 0
        self.sellop = 0
        self._bid = None
        self._ask = None
        self.M_bidask = M_bidask
        self.auth_user_msg_q = auth_user_msg_q
        self.M_get_depth = M_get_depth
        self.count01 = 0
        self.manager = Manager()

    def start(self):
        logging.info('start called')
        self.on_open()
        self.process = Process(target=self._go,
                               args=(self.M_bidask, self.M_get_depth))
        self.process.start()

    def _go(self, M_bidask, M_get_depth):
        self.stop = False
        self._sequence = -1
        self.M_get_depth = M_get_depth
        self._connect()
        self._listen(M_bidask)

    def _connect(self):
        logging.info('_connect called')
        if self.WSS_URL[-1] == "/":
            self.WSS_URL = self.WSS_URL[:-1]  # If url ends in a "/" strip it
        sub_params = {'type': 'subscribe', 'product_ids': [self.products]}
        if self.auth:
            timestamp = str(time.time())
            message = timestamp + 'GET' + '/users/self'
            message = message.encode('ascii')
            hmac_key = base64.b64decode(self.API_SECRET)
            signature = hmac.new(hmac_key, message, hashlib.sha256)
            digest = signature.digest()
            signature_b64 = base64.b64encode(digest).decode('utf-8').rstrip(
                '\n')
            sub_params['signature'] = signature_b64
            sub_params['key'] = self.API_KEY
            sub_params['passphrase'] = self.API_PASS
            sub_params['timestamp'] = timestamp
        self.ws = create_connection(self.WSS_URL)
        self.ws.send(json.dumps(sub_params))

        if self.level:
            sub_params = {
                "type": "subscribe",
                "product_ids": [self.products],
                "channels": ["matches"]
            }
            self.ws.send(json.dumps(sub_params))

    def _listen(self, M_bidask):
        self.M_bidask2 = M_bidask  # setting self.p1_w2 within this process so as to not confuse with self.p1_w. they are the came Pipe object
        logging.info('_listen called')
        while not self.stop:
            try:
                msg = self.ws.recv()
            except Exception as e:

                print('exception 1')
                gv.logger.exception(e)
                gv.logger.info('sleeping for 5 seconds and running _connect()')
                self.close()
                time.sleep(5)
                self._go(self.M_bidask, self.M_get_depth)
            else:
                if msg:
                    msg = json.loads(msg)
                    self.on_message(msg)

    def on_message(self, message):
        try:
            if 'user_id' in message:
                self.auth_user_msg_q.put(message)
            if 'sequence' not in message:
                return
            sequence = message['sequence']
            if self._sequence == -1:
                self._asks = RBTree()
                self._bids = RBTree()
                res = self.get_product_order_book(level=3)
                for bid in res['bids']:
                    self.add({
                        'id': bid[2],
                        'side': 'buy',
                        'price': Decimal(bid[0]),
                        'size': Decimal(bid[1])
                    })
                for ask in res['asks']:
                    self.add({
                        'id': ask[2],
                        'side': 'sell',
                        'price': Decimal(ask[0]),
                        'size': Decimal(ask[1])
                    })
                self._sequence = res['sequence']

            if sequence <= self._sequence:
                # ignore older messages (e.g. before order book initialization from getProductOrderBook)
                return
            elif sequence > self._sequence + 1:
                print(
                    'Error: messages missing ({} - {}). Re-initializing websocket.'
                    .format(sequence, self._sequence))
                self.close()
                time.sleep(5)
                self._go(self.M_bidask, self.M_get_depth)
                return

            msg_type = message['type']
            if msg_type == 'open':
                self.add(message)
            elif msg_type == 'done' and 'price' in message:
                self.remove(message)
            elif msg_type == 'match':
                self.match(message)
            elif msg_type == 'change':
                self.change(message)
            self._sequence = sequence

            bid = self.get_bid(
            )  # Update Multiprocessing.Value variables with the new bid and ask
            ask = self.get_ask()
            if self.M_get_depth.value == 'bid':
                bids = self.get_bids(bid)
                bid_depth = sum([b['size'] for b in bids])
                self.M_get_depth.value = bid_depth
            if self.M_get_depth.value == 'ask':
                asks = self.get_asks(ask)
                ask_depth = sum([a['size'] for a in asks])
                self.M_get_depth.value = ask_depth

            if not self._bid == bid or not self._ask == ask:
                # lock these vars
                self.M_bidask2['bid'] = float(bid)
                self.M_bidask2['ask'] = float(ask)
                self._bid = bid
                self._ask = ask

        except Exception as e:
            print(e)
            print('exception 2')
            gv.logger.exception(e)
            self.close()
            time.sleep(5)
            self._go(self.M_bidask, self.M_get_depth)

        # bids = self.get_bids(bid)
        # bid_depth = sum([b['size'] for b in bids])
        # asks = self.get_asks(ask)
        # ask_depth = sum([a['size'] for a in asks])
        # print('bid: %f @ %f - ask: %f @ %f' % (bid_depth, bid, ask_depth, ask))

    def on_error(self, e):
        self._sequence.value = -1
        print('on_error called')
        self.close()
        self._go(self.M_bidask, self.M_get_depth)

    def close(self):
        logging.info('close called')
        if not self.stop:

            print('in not self.stop')
            self.on_close()
            self.stop = True
            #self.process.join()
            try:
                if self.ws:
                    print('if self.ws')
                    self.ws.close()
            except WebSocketConnectionClosedException as e:
                gv.logger.debug(e)

    def add(self, order):
        order = {
            'id': order.get('order_id') or order['id'],
            'side': order['side'],
            'price': Decimal(order['price']),
            'size': Decimal(order.get('size') or order['remaining_size'])
        }
        if order['side'] == 'buy':
            bids = self.get_bids(order['price'])
            if bids is None:
                bids = [order]
            else:
                bids.append(order)
            self.set_bids(order['price'], bids)
        else:
            asks = self.get_asks(order['price'])
            if asks is None:
                asks = [order]
            else:
                asks.append(order)
            self.set_asks(order['price'], asks)

    def remove(
        self, order
    ):  # order is the instance of one message receded from the websockit
        price = Decimal(
            order['price']
        )  # get the price from the order and saves it as a decimal in variable price
        if order['side'] == 'buy':  # if its a buy order continue
            bids = self.get_bids(
                price
            )  # returns all the bids at specified (price) from orderbook, geting ready to compare with current order.
            if bids is not None:  # bids being None does not happen often
                bidslist = [
                    o for o in bids if o['id'] != order['order_id']
                ]  # creates a list minus the current order to be removed
                if len(
                        bidslist
                ) > 0:  # when bidslist still contains items in its list send list and price to set_bids
                    self.set_bids(price, bidslist)
                else:
                    self.remove_bids(
                        price
                    )  # when bidslist returns nothing in its list send price to remove_bids(price) to be removed from orderbook.
        else:
            asks = self.get_asks(
                price
            )  # same as the first part of this function except on the ask side
            if asks is not None:
                asks = [o for o in asks if o['id'] != order['order_id']]
                if len(asks) > 0:
                    self.set_asks(price, asks)
                else:
                    self.remove_asks(price)

                    # An order was matched so it needs to be removed from our orderbook \
                    # and keep the remaining
    def match(
        self, order
    ):  # order is the instance of one message receded from the websockit
        size = Decimal(order['size'])  # order size how many ETH or BTC etc..
        price = Decimal(order['price'])
        if order['side'] == 'buy':  # if the order is a buy order \
            bids = self.get_bids(
                price
            )  # returns all the bids at specified (price) from orderbook \
            if not bids:  # if there are no orders at this price on the bid/buy side than just return
                return
            assert bids[0]['id'] == order[
                'maker_order_id']  # if condition returns False trigger an error
            # this is taking the trade id of the order on our orderbook and comparing \
            # it to the market makers order id to varify the data.
            # this should always return True if it returns false are market data \
            # we have saved is incorrect
            if bids[0][
                    'size'] == size:  # The first order on our books at this pirce is bids[0] and if the size \
                # of that order matches the incoming match order then we need to remove \
                # the order from our books at this price
                # we use bids[0] because the market works on a first come first serve \
                # and bids[0] has been here the longest
                self.set_bids(
                    price, bids[1:]
                )  # excludes the first order in the list and sets the remaining \
                # back in the orderbook list
            else:
                bids[0][
                    'size'] -= size  # subtract size of current trade from the position held on the orderbook "bids[0]"
                self.set_bids(price, bids)
        else:  # same as above for the match function but on the ask/sell side
            asks = self.get_asks(price)
            if not asks:
                return
            assert asks[0]['id'] == order['maker_order_id']
            if asks[0]['size'] == size:
                self.set_asks(price, asks[1:])
            else:
                asks[0]['size'] -= size
                self.set_asks(price, asks)

    def change(self, order):
        try:
            new_size = Decimal(order['new_size'])
        except KeyError as e:
            gv.logger.debug(e)
            return
        price = Decimal(order['price'])
        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is None or not any(o['id'] == order['order_id']
                                       for o in bids):
                return
            index = [b['id'] for b in bids].index(order['order_id'])
            bids[index]['size'] = new_size
            self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if asks is None or not any(o['id'] == order['order_id']
                                       for o in asks):
                return
            index = [a['id'] for a in asks].index(order['order_id'])
            asks[index]['size'] = new_size
            self.set_asks(price, asks)
        tree = self._asks if order['side'] == 'sell' else self._bids
        node = tree.get(price)
        if node is None or not any(o['id'] == order['order_id'] for o in node):
            return

    def get_ask(self):
        return self._asks.min_key()

    def get_asks(self, price):
        return self._asks.get(price)

    def remove_asks(self, price):
        self._asks.remove(price)

    def set_asks(self, price, asks):
        self._asks.insert(price, asks)

    def get_bid(self):
        return self._bids.max_key()

    def get_bids(self, price):
        return self._bids.get(price)

    def remove_bids(self, price):
        self._bids.remove(price)

    def set_bids(self, price, bids):
        self._bids.insert(price, bids)

    # _bids is the is the binary data structure of the entire buy side of the overbook
    # this function is returning every order from the buy side with a specific price
    # get is a funcntion of an RBTree data structure

    def get_product_order_book(self, level):
        params = {'level': level}
        r = requests.get(self.API_URL +
                         '/products/{}/book'.format(self.products),
                         params=params,
                         timeout=30)
        return r.json()

    def on_open(self):
        print("-- Subscribed! --\n")

    def on_close(self):
        print("\n-- Socket Closed --")

    def bid_ask_spread(self):
        lock.acquire()
        try:
            bid = self.get_bid()
            ask = self.get_ask()
            print((bid - ask) * -1)
        except Exception as e:
            gv.logger.debug(e)
        finally:
            lock.release()

    #def bid_ask(self):
    #    try:
    #        bid = self.get_bid()
    #        bids = self.get_bids(bid)
    #        ask = self.get_ask()
    #        asks = self.get_asks(ask)
#
#        bid_depth = sum([b['size'] for b in bids])              # If there are no changes to the bid-ask spread \
#        ask_depth = sum([a['size'] for a in asks])              # since the last update, no need to print
#
#                                                                # if we end up logging a type error we may need to \
#                                                                # lock the rbtree(_bid and _ask)
#        if gv._bid == bid and gv._ask == ask and self._bid_depth == bid_depth and self._ask_depth == ask_depth:
#            pass
#        else:                                                   # If there are differences, update the cache
#            gv._bid = bid
#            gv._ask = ask
#            self._bid_depth = bid_depth
#            self._ask_depth = ask_depth
#            print('{}\tbid: {:.3f} @ {:.2f}\task: {:.3f} @ {:.2f}'.format(dt.datetime.now(), bid_depth, bid, ask_depth, ask))
#    except Exception as e:
#        gv.logger.debug(e)

    def user_orders_on_book(self):
        self.lock.acquire()
        try:
            print('user orders on book {}'.format(gv.transactions_onbook))
        finally:
            self.lock.release()

    def test2(self):
        return 'test'

    def market_price(self):
        self.lock.acquire()
        try:
            if gv.market_price is not None:
                print('match {}'.format(gv.market_price))
                pass

        finally:
            self.lock.release()
示例#11
0
class OrderBook(object):
    def __init__(self, product_id='BTC-USD', feed=None, log_to=None):
        self._product_id = product_id
        self._asks = RBTree()
        self._bids = RBTree()
        self._sequence = -1
        self._current_ticker = None
        self._feed = feed

    @property
    def product_id(self):
        """ Currently OrderBook only supports a single product even though it is stored as a list of products. """
        return self.product_id

    def reset_book(self, snapshot):
        self._asks = RBTree()
        self._bids = RBTree()
        for bid in snapshot['bids']:
            self._add({
                'id': bid[2],
                'side': 'buy',
                'price': Decimal(bid[0]),
                'size': Decimal(bid[1])
            })
        for ask in snapshot['asks']:
            self._add({
                'id': ask[2],
                'side': 'sell',
                'price': Decimal(ask[0]),
                'size': Decimal(ask[1])
            })
        self._sequence = snapshot['sequence']

    def on_message(self, message):
        sequence = message['sequence']
        if self._sequence == -1:
            logger.error("Expected snapshot before any message")
            sys.exit()
            # self.reset_book()
            return
        if sequence <= self._sequence:
            # ignore older messages (e.g. before order book initialization from getProductOrderBook)
            return
        elif sequence > self._sequence + 1:
            self.on_sequence_gap(self._sequence, sequence)
            # return

        msg_type = message['type']
        if msg_type == 'open':
            self._add(message)
        elif msg_type == 'done' and 'price' in message:
            self._remove(message)
        elif msg_type == 'match':
            self._match(message)
            self._current_ticker = message
        elif msg_type == 'change':
            self._change(message)

        self._sequence = sequence

    def on_sequence_gap(self, gap_start, gap_end):
        # self.reset_book()
        logger.error(
            'Error: messages missing ({} - {}). ignoring the gap.'.format(
                gap_start, gap_end, self._sequence))

    def get_current_ticker(self):
        return self._current_ticker

    def get_current_book(self):
        result = {
            'sequence': self._sequence,
            'asks': [],
            'bids': [],
        }
        for ask in self._asks:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_ask = self._asks[ask]
            except KeyError:
                continue
            for order in this_ask:
                result['asks'].append(
                    [order['price'], order['size'], order['id']])
        for bid in self._bids:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_bid = self._bids[bid]
            except KeyError:
                continue

            for order in this_bid:
                result['bids'].append(
                    [order['price'], order['size'], order['id']])
        return result

    def get_ask(self):
        return self._asks.min_key()

    def get_asks(self, price):
        return self._asks.get(price)

    def remove_asks(self, price):
        self._asks.remove(price)

    def set_asks(self, price, asks):
        self._asks.insert(price, asks)

    def get_bid(self):
        return self._bids.max_key()

    def get_bids(self, price):
        return self._bids.get(price)

    def remove_bids(self, price):
        self._bids.remove(price)

    def set_bids(self, price, bids):
        self._bids.insert(price, bids)

    @staticmethod
    def meet_min_diff_price(low_price, high_price, min_diff_price):
        if low_price is None or high_price is None or min_diff_price is None:
            return False
        return high_price - low_price > min_diff_price

    @staticmethod
    def meet_max_size(total_size, max_size):
        if total_size is None or max_size is None:
            return False
        return total_size > max_size

    def get_aggr_bids(self, min_diff_price=None, max_size=None):
        max_bid = self.get_bid()
        total_size = 0
        aggr_bids = list()
        for price, bid_orders in reversed(list(self._bids.items())):
            if (OrderBook.meet_min_diff_price(price, max_bid, min_diff_price)
                    and OrderBook.meet_max_size(total_size, max_size)):
                break
            size = sum(bid_order['size'] for bid_order in bid_orders)
            num_orders = len(bid_orders)
            aggr_bids.append({
                'price': price,
                'size': size,
                'num_orders': num_orders
            })
            total_size += size
        return aggr_bids

    def get_aggr_asks(self, min_diff_price=None, max_size=None):
        min_ask = self.get_ask()
        total_size = 0
        aggr_asks = list()
        for price, ask_orders in self._asks.items():
            if (OrderBook.meet_min_diff_price(min_ask, price, min_diff_price)
                    and OrderBook.meet_max_size(total_size, max_size)):
                break
            size = sum(ask_order['size'] for ask_order in ask_orders)
            num_orders = len(ask_orders)
            aggr_asks.append({
                'price': price,
                'size': size,
                'num_orders': num_orders
            })
            total_size += size
        return aggr_asks

    # Internal operations
    def _add(self, order):
        order = {
            'id': order.get('order_id') or order['id'],
            'side': order['side'],
            'price': Decimal(order['price']),
            'size': Decimal(order.get('size') or order['remaining_size'])
        }
        if order['side'] == 'buy':
            bids = self.get_bids(order['price'])
            if bids is None:
                bids = [order]
            else:
                bids.append(order)
            self.set_bids(order['price'], bids)
        else:
            asks = self.get_asks(order['price'])
            if asks is None:
                asks = [order]
            else:
                asks.append(order)
            self.set_asks(order['price'], asks)

    def _remove(self, order):
        price = Decimal(order['price'])
        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is not None:
                bids = [o for o in bids if o['id'] != order['order_id']]
                if len(bids) > 0:
                    self.set_bids(price, bids)
                else:
                    self.remove_bids(price)
        else:
            asks = self.get_asks(price)
            if asks is not None:
                asks = [o for o in asks if o['id'] != order['order_id']]
                if len(asks) > 0:
                    self.set_asks(price, asks)
                else:
                    self.remove_asks(price)

    def _match(self, order):
        if self._feed is not None:
            self._feed._match(order)

        size = Decimal(order['size'])
        price = Decimal(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if not bids:
                return
            assert bids[0]['id'] == order['maker_order_id']
            if bids[0]['size'] == size:
                self.set_bids(price, bids[1:])
            else:
                bids[0]['size'] -= size
                self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if not asks:
                return
            assert asks[0]['id'] == order['maker_order_id']
            if asks[0]['size'] == size:
                self.set_asks(price, asks[1:])
            else:
                asks[0]['size'] -= size
                self.set_asks(price, asks)

    def _change(self, order):
        try:
            new_size = Decimal(order['new_size'])
        except KeyError:
            return

        try:
            price = Decimal(order['price'])
        except KeyError:
            return

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is None or not any(o['id'] == order['order_id']
                                       for o in bids):
                return
            index = [b['id'] for b in bids].index(order['order_id'])
            bids[index]['size'] = new_size
            self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if asks is None or not any(o['id'] == order['order_id']
                                       for o in asks):
                return
            index = [a['id'] for a in asks].index(order['order_id'])
            asks[index]['size'] = new_size
            self.set_asks(price, asks)

        tree = self._asks if order['side'] == 'sell' else self._bids
        node = tree.get(price)

        if node is None or not any(o['id'] == order['order_id'] for o in node):
            return
class MarinerOrderBook(gdax.OrderBook):

    current_milli_time = lambda self: int(time.time() * 1000)

    def __init__(self, ticker, threshold):
        gdax.OrderBook.__init__(self, product_id=ticker)
        self._threshold = threshold
        self.bookChanged = None
        self.whaleEnteredMarket = None
        self.whaleExitedMarket = None

    def registerHandlers(self, bookChangedHandler, whaleEnteredMarketHandler,
                         whaleExitedMarketHandler, whaleChangedHandler):
        Logging.logger.info("registering callbacks...")
        self.bookChanged = bookChangedHandler
        self.whaleEnteredMarket = whaleEnteredMarketHandler
        self.whaleExitedMarket = whaleExitedMarketHandler
        self.whaleChanged = whaleChangedHandler

    def onMessage(self, message):
        #Logging.logger.info(message)
        sequence = message['sequence']
        if self._sequence == -1:
            self._asks = RBTree()
            self._bids = RBTree()
            res = self._client.getProductOrderBook(level=3)
            for bid in res['bids']:
                self.add({
                    'id': bid[2],
                    'side': 'buy',
                    'price': Decimal(bid[0]),
                    'size': Decimal(bid[1])
                })
            for ask in res['asks']:
                self.add({
                    'id': ask[2],
                    'side': 'sell',
                    'price': Decimal(ask[0]),
                    'size': Decimal(ask[1])
                })
            self._sequence = res['sequence']

        if sequence <= self._sequence:
            # ignore older messages (e.g. before order book initialization from getProductOrderBook)
            return
        elif sequence > self._sequence + 1:
            Logging.logger.error(
                'Error: messages missing ({} - {}). Re-initializing websocket.'
                .format(sequence, self._sequence))
            self.close()
            self.start()
            return

        msg_type = message['type']
        if msg_type == 'open':
            self.add(message)
        elif msg_type == 'done' and 'price' in message:
            self.remove(message)
        elif msg_type == 'match':
            self.match(message)
        elif msg_type == 'change':
            self.change(message)

        self._sequence = sequence

        if not self.bookChanged is None:
            self.bookChanged()

    def add(self, order):
        order = {
            'id': order.get('order_id') or order['id'],
            'side': order['side'],
            'price': Decimal(order['price']),
            'size': Decimal(order.get('size') or order['remaining_size'])
        }

        if order['side'] == 'buy':
            bids = self.get_bids(order['price'])
            if bids is None:
                bids = [order]
            else:
                bids.append(order)
            self.set_bids(order['price'], bids)
        else:
            asks = self.get_asks(order['price'])
            if asks is None:
                asks = [order]
            else:
                asks.append(order)
            self.set_asks(order['price'], asks)

        if not self.whaleEnteredMarket == None and self.isWhale(order['size']):
            self.whaleEnteredMarket(order)

    def remove(self, order):
        order = {
            'id': order.get('order_id') or order['id'],
            'side': order['side'],
            'price': Decimal(order['price']),
            'size': Decimal(order.get('size') or order['remaining_size'])
        }
        price = Decimal(order['price'])
        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is not None:
                bids = [o for o in bids if o['id'] != order['id']]
                if len(bids) > 0:
                    self.set_bids(price, bids)
                else:
                    self.remove_bids(price)
        else:
            asks = self.get_asks(price)
            if asks is not None:
                asks = [o for o in asks if o['id'] != order['id']]
                if len(asks) > 0:
                    self.set_asks(price, asks)
                else:
                    self.remove_asks(price)

        if not self.whaleExitedMarket == None and self.isWhale(order['size']):
            self.whaleExitedMarket(order)

    def match(self, order):
        size = Decimal(order['size'])
        price = Decimal(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if not bids:
                return
            assert bids[0]['id'] == order['maker_order_id']
            if bids[0]['size'] == size:
                self.set_bids(price, bids[1:])
            else:
                bids[0]['size'] -= size
                self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if not asks:
                return
            assert asks[0]['id'] == order['maker_order_id']
            if asks[0]['size'] == size:
                self.set_asks(price, asks[1:])
            else:
                asks[0]['size'] -= size
                self.set_asks(price, asks)

    def change(self, order):
        price = Decimal(order['price'])
        new_volume = Decimal(order['new_size'])
        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is None or not any(o['id'] == order['order_id']
                                       for o in bids):
                return
            index = map(itemgetter('id'), bids).index(order['order_id'])
            bids[index]['size'] = new_size
            self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if asks is None or not any(o['id'] == order['order_id']
                                       for o in asks):
                return
            index = map(itemgetter('id'), asks).index(order['order_id'])
            asks[index]['size'] = new_size
            self.set_asks(price, asks)

        tree = self._asks if order['side'] == 'sell' else self._bids
        node = tree.get(price)

        if node is None or not any(o['id'] == order['order_id'] for o in node):
            return

        if not self.whaleChanged == None and self.isWhale(order['size']):
            self.whaleChanged(order)

    def get_current_book(self,
                         num_levels=10
                         ):  #fetch only 100 levels off book either way
        book = []

        for index, bid_entry in enumerate(self._bids.items([True])):
            if index == num_levels:
                break
            else:
                try:
                    # There can be a race condition here, where a price point is removed
                    # between these two ops
                    thisBid = bid_entry[1]
                except KeyError:
                    continue

                for order in thisBid:
                    order['price'] = Decimal128(order['price'])
                    order['size'] = Decimal128(order['size'])
                    book.append(order)

        for index, ask_entry in enumerate(self._asks.items()):
            if index == num_levels:
                break
            else:
                try:
                    # There can be a race condition here, where a price point is removed
                    # between these two ops
                    thisAsk = ask_entry[1]
                except KeyError:
                    continue

                for order in thisAsk:
                    order['price'] = Decimal128(order['price'])
                    order['size'] = Decimal128(order['size'])
                    book.append(order)

        book_frame = pd.DataFrame(book)
        return book_frame

    def isWhale(self, aVolume):
        return aVolume >= self._threshold

    #For general book management purposes
    def get_bid(self):
        if not self._bids.is_empty():
            return self._bids.max_key()
        else:
            return None

    def get_bids(self, price):
        return self._bids.get(price)

    def remove_bids(self, price):
        self._bids.remove(price)

    def set_bids(self, price, bids):
        self._bids.insert(price, bids)

    def get_ask(self):
        if not self._asks.is_empty():
            return self._asks.min_key()
        else:
            return None

    def get_asks(self, price):
        return self._asks.get(price)

    def remove_asks(self, price):
        self._asks.remove(price)

    def set_asks(self, price, asks):
        self._asks.insert(price, asks)
示例#13
0
class OrderBook(WebsocketClient):
    def __init__(self, product_id='BTC-USD', log_to=None):
        super(OrderBook, self).__init__(products=product_id)
        self._asks = RBTree()
        self._bids = RBTree()
        self._client = PublicClient()
        self._sequence = -1
        self._log_to = log_to
        if self._log_to:
            assert hasattr(self._log_to, 'write')
        self._current_ticker = None

    @property
    def product_id(self):
        ''' Currently OrderBook only supports a single product even though it is stored as a list of products. '''
        return self.products[0]

    def on_open(self):
        self._sequence = -1
        print("-- Subscribed to OrderBook! --\n")

    def on_close(self):
        print("\n-- OrderBook Socket Closed! --")

    def reset_book(self):
        self._asks = RBTree()
        self._bids = RBTree()
        res = self._client.get_product_order_book(
            product_id=self.product_id, level=3)
        for bid in res['bids']:
            self.add({
                'id': bid[2],
                'side': 'buy',
                'price': Decimal(bid[0]),
                'size': Decimal(bid[1])
            })
        for ask in res['asks']:
            self.add({
                'id': ask[2],
                'side': 'sell',
                'price': Decimal(ask[0]),
                'size': Decimal(ask[1])
            })
        self._sequence = res['sequence']

    def on_message(self, message):
        if self._log_to:
            pickle.dump(message, self._log_to)

        sequence = message['sequence']
        if self._sequence == -1:
            self.reset_book()
            return
        if sequence <= self._sequence:
            # ignore older messages (e.g. before order book initialization from getProductOrderBook)
            return
        elif sequence > self._sequence + 1:
            self.on_sequence_gap(self._sequence, sequence)
            return

        msg_type = message['type']
        if msg_type == 'open':
            self.add(message)
        elif msg_type == 'done' and 'price' in message:
            self.remove(message)
        elif msg_type == 'match':
            self.match(message)
            self._current_ticker = message
        elif msg_type == 'change':
            self.change(message)

        self._sequence = sequence

    def on_sequence_gap(self, gap_start, gap_end):
        self.reset_book()
        print('Error: messages missing ({} - {}). Re-initializing  book at sequence.'.format(
            gap_start, gap_end, self._sequence))

    def add(self, order):
        order = {
            'id': order.get('order_id') or order['id'],
            'side': order['side'],
            'price': Decimal(order['price']),
            'size': Decimal(order.get('size') or order['remaining_size'])
        }
        if order['side'] == 'buy':
            bids = self.get_bids(order['price'])
            if bids is None:
                bids = collections.OrderedDict()
            bids[order['id']] = order
            self.set_bids(order['price'], bids)
        else:
            asks = self.get_asks(order['price'])
            if asks is None:
                asks = collections.OrderedDict()
            asks[order['id']] = order
            self.set_asks(order['price'], asks)

    def remove(self, order):
        price = Decimal(order['price'])
        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is not None:
                if order['order_id'] in bids:
                    del bids[order['order_id']]
                if len(bids) > 0:
                    self.set_bids(price, bids)
                else:
                    self.remove_bids(price)
        else:
            asks = self.get_asks(price)
            if asks is not None:
                if order['order_id'] in asks:
                    del asks[order['order_id']]
                if len(asks) > 0:
                    self.set_asks(price, asks)
                else:
                    self.remove_asks(price)

    def match(self, order):
        size = Decimal(order['size'])
        price = Decimal(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if not bids:
                return
            top_order = bids.items()[0]
            assert top_order[1]['id'] == order['maker_order_id']
            if top_order[1]['size'] == size:
                del bids[order['maker_order_id']]
                self.set_bids(price, bids)
            else:
                top_order[1]['size'] -= size
                self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if not asks:
                return
            top_order = asks.items()[0]
            assert top_order[1]['id'] == order['maker_order_id']
            if top_order[1]['size'] == size:
                del asks[order['maker_order_id']]
                self.set_asks(price, asks)
            else:
                top_order[1]['size'] -= size
                self.set_asks(price, asks)

    def change(self, order):
        try:
            new_size = Decimal(order['new_size'])
        except KeyError:
            return

        try:
            price = Decimal(order['price'])
        except KeyError:
            return

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is None or order['order_id'] not in bids:
                return
            bids[order['order_id']]['size'] = new_size
            self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if asks is None or order['order_id'] not in asks:
                return
            asks[order['order_id']]['size'] = new_size
            self.set_asks(price, asks)

    def get_current_ticker(self):
        return self._current_ticker

    def get_current_book(self):
        result = {
            'sequence': self._sequence,
            'asks': [],
            'bids': [],
        }
        for ask in self._asks:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_ask = self._asks[ask]
            except KeyError:
                continue
            for order in this_ask:
                result['asks'].append(
                    [order['price'], order['size'], order['id']])
        for bid in self._bids:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_bid = self._bids[bid]
            except KeyError:
                continue

            for order in this_bid:
                result['bids'].append(
                    [order['price'], order['size'], order['id']])
        return result

    def get_ask(self):
        return self._asks.min_key()

    def get_asks(self, price):
        return self._asks.get(price)

    def remove_asks(self, price):
        self._asks.remove(price)

    def set_asks(self, price, asks):
        self._asks.insert(price, asks)

    def get_bid(self):
        return self._bids.max_key()

    def get_bids(self, price):
        return self._bids.get(price)

    def remove_bids(self, price):
        self._bids.remove(price)

    def set_bids(self, price, bids):
        self._bids.insert(price, bids)
class WhaleTracker():

    current_milli_time = lambda self: int(time.time() * 1000)

    def __init__(self, ticker):
        self._ticker = ticker
        self._bid_whales = RBTree()
        self._ask_whales = RBTree()

    def whaleEnteredMarketHandler(self, order):
        if order['side'] == 'buy':
            self.addBidWhale(order)
        else:
            self.addAskWhale(order)

    def whaleExitedMarketHandler(self, order):
        if order['side'] == 'buy':
            self.removeBidWhale(order)
        else:
            self.removeAskWhale(order)

    def whaleChangedHandler(self, order):
        if order['side'] == 'buy':
            self.changeBidWhale(order)
        else:
            self.changeAskWhale(order)

    def addBidWhale(self, order):
        ID = order['id']
        price = Decimal(order['price'])
        volume = Decimal(order['size'])
        bid_whale_order = self.get_bid_whale(price)
        if bid_whale_order is None or volume > bid_whale_order.get_volume():
            #Logging.logger.info("NEW WHALE ENTERED (BID): price=" + str(price) + " volume=" + str(volume))
            bid_whale_order = WhaleOrder(ID, price, volume)
            self.set_bid_whale(price, bid_whale_order)

    def addAskWhale(self, order):
        ID = order['id']
        price = Decimal(order['price'])
        volume = Decimal(order['size'])
        ask_whale_order = self.get_ask_whale(price)
        if ask_whale_order is None or volume > ask_whale_order.get_volume():
            #Logging.logger.info("NEW WHALE ENTERED (ASK): price=" + str(price) + " volume=" + str(volume))
            ask_whale_order = WhaleOrder(ID, price, volume)
            self.set_ask_whale(price, ask_whale_order)

    def removeBidWhale(self, order):
        ID = order['id']
        price = Decimal(order['price'])
        volume = Decimal(order['size'])
        bid_whale_order = self.get_bid_whale(price)
        if bid_whale_order is not None and bid_whale_order.get_id() == ID:
            #Logging.logger.info("WHALE LEFT (BID): price=" + str(price) + " volume=" + str(volume))
            self.remove_bid_whale(price)

    def removeAskWhale(self, order):
        ID = order['id']
        price = Decimal(order['price'])
        volume = Decimal(order['size'])
        ask_whale_order = self.get_ask_whale(price)
        if ask_whale_order is not None and ask_whale_order.get_id() == ID:
            #Logging.logger.info("WHALE LEFT (ASK): price=" + str(price) + " volume=" + str(volume))
            self.remove_ask_whale(price)

    def changeBidWhale(self, order):
        ID = order['id']
        price = Decimal(order['price'])
        new_volume = order['new_size']
        bid_whale_order = self.get_bid_whale(price)
        if not bid_whale_order == None:
            #Logger.logging.info("WHALE CHANGED (BID): price=" + price + " new_volume=" + new_volume)
            if self.isWhale(new_volume):
                bid_whale_order.setVolume(new_volume)
                self.set_bid_whale(price, bid_whale_order)
            else:
                self.removeBidWhale(order)

    def changeAskWhale(self, order):
        ID = order['id']
        price = Decimal(order['price'])
        new_volume = order['new_size']
        ask_whale_order = self.get_ask_whale(price)
        if not ask_whale_order == None:
            #Logger.logging.info("WHALE CHANGED (ASK): price=" + price + " new_volume=" + new_volume)
            if self.isWhale(new_volume):
                ask_whale_order.setVolume(new_volume)
                self.set_ask_whale(price, ask_whale_order)
            else:
                self.removeAskWhale(order)

    def get_current_whales(
        self,
        num_whales=30
    ):  #by default, fetch only 30 whales off the book either way
        whales = []

        for index, bid_whale in self._bid_whales.items(True):
            if index == num_whales:
                break
            else:
                whales.append({
                    'price': Decimal128(bid_whale.get_price()),
                    'volume': Decimal128(bid_whale.get_volume()),
                    'id': bid_whale.get_id(),
                })

        for index, ask_whale in self._ask_whales.items():
            if index == num_whales:
                break
            else:
                whales.append({
                    'price': Decimal128(ask_whale.get_price()),
                    'volume': Decimal128(ask_whale.get_volume()),
                    'id': ask_whale.get_id(),
                })

        whales_frame = pd.DataFrame(whales)
        return whales_frame

    def get_top_bid_whale(self):
        if not self._bid_whales.is_empty():
            top_bid = self._bid_whales.max_key()
            return self.get_bid_whale(top_bid)
        else:
            return None

    def get_bid_whale(self, price):
        return self._bid_whales.get(price)

    def remove_bid_whale(self, price):
        self._bid_whales.remove(price)

    def set_bid_whale(self, price, whale):
        self._bid_whales.insert(price, whale)

    def get_top_ask_whale(self):
        if not self._ask_whales.is_empty():
            top_ask = self._ask_whales.min_key()
            return self.get_ask_whale(top_ask)
        else:
            return None

    def get_ask_whale(self, price):
        return self._ask_whales.get(price)

    def remove_ask_whale(self, price):
        self._ask_whales.remove(price)

    def set_ask_whale(self, price, whale):
        self._ask_whales.insert(price, whale)
示例#15
0
class OrderBook(object):
    def __init__(self, product_id, log_to=None):
        self._asks = RBTree()
        self._bids = RBTree()
        self._client = PublicClient()
        self._sequence = -1
        self.sync = -1
        self.product_id = product_id
        self._log_to = log_to
        if self._log_to:
            assert hasattr(self._log_to, 'write')
        self._current_ticker = None

    def on_open(self):
        self._sequence = -1
        #print("-- Subscribed to " + self.product_id + " OrderBook! --\n")

    def on_close(self):
        print("\n-- OrderBook " + self.product_id + " Socket Closed! --")

    def reset_book(self):
        self._asks = RBTree()
        self._bids = RBTree()
        res = self._client.get_product_order_book(product_id=self.product_id,
                                                  level=3)
        for bid in res['bids']:
            self.add({
                'id': bid[2],
                'side': 'buy',
                'price': Decimal(bid[0]),
                'size': Decimal(bid[1])
            })
        for ask in res['asks']:
            self.add({
                'id': ask[2],
                'side': 'sell',
                'price': Decimal(ask[0]),
                'size': Decimal(ask[1])
            })
        self._sequence = res['sequence']

    def on_message(self, message):
        if self._log_to:
            pickle.dump(message, self._log_to)

        sequence = message['sequence']
        if self._sequence == -1:
            self.sync = -1
            self.reset_book()
            return
        if sequence <= self._sequence:
            # ignore older messages (e.g. before order book initialization from getProductOrderBook)
            return
        elif sequence > self._sequence + 1:
            self.on_sequence_gap(self._sequence, sequence)
            self.sync = -1
            return

        self.sync = 1
        msg_type = message['type']
        if msg_type == 'open':
            self.add(message)
        elif msg_type == 'done' and 'price' in message:
            self.remove(message)
        elif msg_type == 'match':
            self.match(message)
            self._current_ticker = message
        elif msg_type == 'change':
            self.change(message)

        self._sequence = sequence

    def on_sequence_gap(self, gap_start, gap_end):
        self.reset_book()
        print(
            'Error: messages missing ({} - {}). Re-initializing  book at sequence.'
            .format(gap_start, gap_end, self._sequence))

    def add(self, order):
        order = {
            'id': order.get('order_id') or order['id'],
            'side': order['side'],
            'price': Decimal(order['price']),
            'size': Decimal(order.get('size') or order['remaining_size'])
        }
        if order['side'] == 'buy':
            bids = self.get_bids(order['price'])
            if bids is None:
                bids = [order]
            else:
                bids.append(order)
            self.set_bids(order['price'], bids)
        else:
            asks = self.get_asks(order['price'])
            if asks is None:
                asks = [order]
            else:
                asks.append(order)
            self.set_asks(order['price'], asks)

    def remove(self, order):
        price = Decimal(order['price'])
        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is not None:
                bids = [o for o in bids if o['id'] != order['order_id']]
                if len(bids) > 0:
                    self.set_bids(price, bids)
                else:
                    self.remove_bids(price)
        else:
            asks = self.get_asks(price)
            if asks is not None:
                asks = [o for o in asks if o['id'] != order['order_id']]
                if len(asks) > 0:
                    self.set_asks(price, asks)
                else:
                    self.remove_asks(price)

    def match(self, order):
        size = Decimal(order['size'])
        price = Decimal(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if not bids:
                return
            assert bids[0]['id'] == order['maker_order_id']
            if bids[0]['size'] == size:
                self.set_bids(price, bids[1:])
            else:
                bids[0]['size'] -= size
                self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if not asks:
                return
            assert asks[0]['id'] == order['maker_order_id']
            if asks[0]['size'] == size:
                self.set_asks(price, asks[1:])
            else:
                asks[0]['size'] -= size
                self.set_asks(price, asks)

    def change(self, order):
        try:
            new_size = Decimal(order['new_size'])
        except KeyError:
            return

        try:
            price = Decimal(order['price'])
        except KeyError:
            return

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is None or not any(o['id'] == order['order_id']
                                       for o in bids):
                return
            index = [b['id'] for b in bids].index(order['order_id'])
            bids[index]['size'] = new_size
            self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if asks is None or not any(o['id'] == order['order_id']
                                       for o in asks):
                return
            index = [a['id'] for a in asks].index(order['order_id'])
            asks[index]['size'] = new_size
            self.set_asks(price, asks)

        tree = self._asks if order['side'] == 'sell' else self._bids
        node = tree.get(price)

        if node is None or not any(o['id'] == order['order_id'] for o in node):
            return

    def get_current_ticker(self):
        return self._current_ticker

    def get_current_book(self):
        result = {
            'sequence': self._sequence,
            'asks': [],
            'bids': [],
        }
        for ask in self._asks:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_ask = self._asks[ask]
            except KeyError:
                continue
            for order in this_ask:
                result['asks'].append(
                    [order['price'], order['size'], order['id']])
        for bid in self._bids:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_bid = self._bids[bid]
            except KeyError:
                continue

            for order in this_bid:
                result['bids'].append(
                    [order['price'], order['size'], order['id']])
        return result

    def get_ask(self):
        return self._asks.min_key()

    def get_ask_size(self):
        return sum(order['size'] for order in self._asks.min_item()[1])

    def get_asks(self, price):
        return self._asks.get(price)

    def remove_asks(self, price):
        self._asks.remove(price)

    def set_asks(self, price, asks):
        self._asks.insert(price, asks)

    def get_bid(self):
        return self._bids.max_key()

    def get_bid_size(self):
        return sum(order['size'] for order in self._bids.min_item()[1])

    def get_bids(self, price):
        return self._bids.get(price)

    def remove_bids(self, price):
        self._bids.remove(price)

    def set_bids(self, price, bids):
        self._bids.insert(price, bids)
示例#16
0
class OrderBook(WebsocketClient):
    def __init__(self, product_id='BTC-USD'):
        super(OrderBook, self).__init__(products=product_id)
        self._asks = RBTree()
        self._bids = RBTree()
        self._client = PublicClient(product_id=product_id)
        self._sequence = -1

    def on_message(self, message):
        sequence = message['sequence']
        if self._sequence == -1:
            self._asks = RBTree()
            self._bids = RBTree()
            res = self._client.get_product_order_book(level=3)
            for bid in res['bids']:
                self.add({
                    'id': bid[2],
                    'side': 'buy',
                    'price': Decimal(bid[0]),
                    'size': Decimal(bid[1])
                })
            for ask in res['asks']:
                self.add({
                    'id': ask[2],
                    'side': 'sell',
                    'price': Decimal(ask[0]),
                    'size': Decimal(ask[1])
                })
            self._sequence = res['sequence']

        if sequence <= self._sequence:
            # ignore older messages (e.g. before order book initialization from getProductOrderBook)
            return
        elif sequence > self._sequence + 1:
            print('Error: messages missing ({} - {}). Re-initializing websocket.'.format(sequence, self._sequence))
            self.close()
            self.start()
            return

        # print(message)
        msg_type = message['type']
        if msg_type == 'open':
            self.add(message)
        elif msg_type == 'done' and 'price' in message:
            self.remove(message)
        elif msg_type == 'match':
            self.match(message)
        elif msg_type == 'change':
            self.change(message)

        self._sequence = sequence

        # bid = self.get_bid()
        # bids = self.get_bids(bid)
        # bid_depth = sum([b['size'] for b in bids])
        # ask = self.get_ask()
        # asks = self.get_asks(ask)
        # ask_depth = sum([a['size'] for a in asks])
        # print('bid: %f @ %f - ask: %f @ %f' % (bid_depth, bid, ask_depth, ask))

    def add(self, order):
        order = {
            'id': order.get('order_id') or order['id'],
            'side': order['side'],
            'price': Decimal(order['price']),
            'size': Decimal(order.get('size') or order['remaining_size'])
        }
        if order['side'] == 'buy':
            bids = self.get_bids(order['price'])
            if bids is None:
                bids = [order]
            else:
                bids.append(order)
            self.set_bids(order['price'], bids)
        else:
            asks = self.get_asks(order['price'])
            if asks is None:
                asks = [order]
            else:
                asks.append(order)
            self.set_asks(order['price'], asks)

    def remove(self, order):
        price = Decimal(order['price'])
        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is not None:
                bids = [o for o in bids if o['id'] != order['order_id']]
                if len(bids) > 0:
                    self.set_bids(price, bids)
                else:
                    self.remove_bids(price)
        else:
            asks = self.get_asks(price)
            if asks is not None:
                asks = [o for o in asks if o['id'] != order['order_id']]
                if len(asks) > 0:
                    self.set_asks(price, asks)
                else:
                    self.remove_asks(price)

    def match(self, order):
        size = Decimal(order['size'])
        price = Decimal(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if not bids:
                return
            assert bids[0]['id'] == order['maker_order_id']
            if bids[0]['size'] == size:
                self.set_bids(price, bids[1:])
            else:
                bids[0]['size'] -= size
                self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if not asks:
                return
            assert asks[0]['id'] == order['maker_order_id']
            if asks[0]['size'] == size:
                self.set_asks(price, asks[1:])
            else:
                asks[0]['size'] -= size
                self.set_asks(price, asks)

    def change(self, order):
        new_size = Decimal(order['new_size'])
        price = Decimal(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is None or not any(o['id'] == order['order_id'] for o in bids):
                return
            index = map(itemgetter('id'), bids).index(order['order_id'])
            bids[index]['size'] = new_size
            self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if asks is None or not any(o['id'] == order['order_id'] for o in asks):
                return
            index = map(itemgetter('id'), asks).index(order['order_id'])
            asks[index]['size'] = new_size
            self.set_asks(price, asks)

        tree = self._asks if order['side'] == 'sell' else self._bids
        node = tree.get(price)

        if node is None or not any(o['id'] == order['order_id'] for o in node):
            return

    def get_current_book(self):
        result = {
            'sequence': self._sequence,
            'asks': [],
            'bids': [],
        }
        for ask in self._asks:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_ask = self._asks[ask]
            except KeyError:
                continue
            for order in this_ask:
                result['asks'].append([order['price'], order['size'], order['id']])
        for bid in self._bids:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_bid = self._bids[bid]
            except KeyError:
                continue

            for order in this_bid:
                result['bids'].append([order['price'], order['size'], order['id']])
        return result

    def get_ask(self):
        return self._asks.min_key()

    def get_asks(self, price):
        return self._asks.get(price)

    def remove_asks(self, price):
        self._asks.remove(price)

    def set_asks(self, price, asks):
        self._asks.insert(price, asks)

    def get_bid(self):
        return self._bids.max_key()

    def get_bids(self, price):
        return self._bids.get(price)

    def remove_bids(self, price):
        self._bids.remove(price)

    def set_bids(self, price, bids):
        self._bids.insert(price, bids)
示例#17
0
class OrderBook(WebsocketClient):
    def __init__(self, product_id='BTC-USD', log_to=None):
        super(OrderBook, self).__init__(products=product_id)
        self._asks = RBTree()
        self._bids = RBTree()
        self._client = PublicClient()
        self._sequence = -1
        self._log_to = log_to
        if self._log_to:
            assert hasattr(self._log_to, 'write')
        self._current_ticker = None

    @property
    def product_id(self):
        ''' Currently OrderBook only supports a single product even though it is stored as a list of products. '''
        return self.products[0]

    def on_message(self, message):
        if self._log_to:
            pickle.dump(message, self._log_to)

        sequence = message['sequence']
        if self._sequence == -1:
            self._asks = RBTree()
            self._bids = RBTree()
            res = self._client.get_product_order_book(product_id=self.product_id, level=3)
            for bid in res['bids']:
                self.add({
                    'id': bid[2],
                    'side': 'buy',
                    'price': Decimal(bid[0]),
                    'size': Decimal(bid[1])
                })
            for ask in res['asks']:
                self.add({
                    'id': ask[2],
                    'side': 'sell',
                    'price': Decimal(ask[0]),
                    'size': Decimal(ask[1])
                })
            self._sequence = res['sequence']

        if sequence <= self._sequence:
            # ignore older messages (e.g. before order book initialization from getProductOrderBook)
            return
        elif sequence > self._sequence + 1:
            print('Error: messages missing ({} - {}). Re-initializing websocket.'.format(sequence, self._sequence))
            self.close()
            self.start()
            return

        msg_type = message['type']
        if msg_type == 'open':
            self.add(message)
        elif msg_type == 'done' and 'price' in message:
            self.remove(message)
        elif msg_type == 'match':
            self.match(message)
            self._current_ticker = message
        elif msg_type == 'change':
            self.change(message)

        self._sequence = sequence

        # bid = self.get_bid()
        # bids = self.get_bids(bid)
        # bid_depth = sum([b['size'] for b in bids])
        # ask = self.get_ask()
        # asks = self.get_asks(ask)
        # ask_depth = sum([a['size'] for a in asks])
        # print('bid: %f @ %f - ask: %f @ %f' % (bid_depth, bid, ask_depth, ask))

    def on_error(self, e):
        self._sequence = -1
        self.close()
        self.start()

    def add(self, order):
        order = {
            'id': order.get('order_id') or order['id'],
            'side': order['side'],
            'price': Decimal(order['price']),
            'size': Decimal(order.get('size') or order['remaining_size'])
        }
        if order['side'] == 'buy':
            bids = self.get_bids(order['price'])
            if bids is None:
                bids = [order]
            else:
                bids.append(order)
            self.set_bids(order['price'], bids)
        else:
            asks = self.get_asks(order['price'])
            if asks is None:
                asks = [order]
            else:
                asks.append(order)
            self.set_asks(order['price'], asks)

    def remove(self, order):
        price = Decimal(order['price'])
        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is not None:
                bids = [o for o in bids if o['id'] != order['order_id']]
                if len(bids) > 0:
                    self.set_bids(price, bids)
                else:
                    self.remove_bids(price)
        else:
            asks = self.get_asks(price)
            if asks is not None:
                asks = [o for o in asks if o['id'] != order['order_id']]
                if len(asks) > 0:
                    self.set_asks(price, asks)
                else:
                    self.remove_asks(price)

    def match(self, order):
        size = Decimal(order['size'])
        price = Decimal(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if not bids:
                return
            assert bids[0]['id'] == order['maker_order_id']
            if bids[0]['size'] == size:
                self.set_bids(price, bids[1:])
            else:
                bids[0]['size'] -= size
                self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if not asks:
                return
            assert asks[0]['id'] == order['maker_order_id']
            if asks[0]['size'] == size:
                self.set_asks(price, asks[1:])
            else:
                asks[0]['size'] -= size
                self.set_asks(price, asks)

    def change(self, order):
        try:
            new_size = Decimal(order['new_size'])
        except KeyError:
            return
            
        price = Decimal(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is None or not any(o['id'] == order['order_id'] for o in bids):
                return
            index = [b['id'] for b in bids].index(order['order_id'])
            bids[index]['size'] = new_size
            self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if asks is None or not any(o['id'] == order['order_id'] for o in asks):
                return
            index = [a['id'] for a in asks].index(order['order_id'])
            asks[index]['size'] = new_size
            self.set_asks(price, asks)

        tree = self._asks if order['side'] == 'sell' else self._bids
        node = tree.get(price)

        if node is None or not any(o['id'] == order['order_id'] for o in node):
            return

    def get_current_ticker(self):
        return self._current_ticker

    def get_current_book(self):
        result = {
            'sequence': self._sequence,
            'asks': [],
            'bids': [],
        }
        for ask in self._asks:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_ask = self._asks[ask]
            except KeyError:
                continue
            for order in this_ask:
                result['asks'].append([order['price'], order['size'], order['id']])
        for bid in self._bids:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_bid = self._bids[bid]
            except KeyError:
                continue

            for order in this_bid:
                result['bids'].append([order['price'], order['size'], order['id']])
        return result

    def get_ask(self):
        return self._asks.min_key()

    def get_asks(self, price):
        return self._asks.get(price)

    def remove_asks(self, price):
        self._asks.remove(price)

    def set_asks(self, price, asks):
        self._asks.insert(price, asks)

    def get_bid(self):
        return self._bids.max_key()

    def get_bids(self, price):
        return self._bids.get(price)

    def remove_bids(self, price):
        self._bids.remove(price)

    def set_bids(self, price, bids):
        self._bids.insert(price, bids)
示例#18
0
class obook(object):
    def __init__(self, saveMessage=0, product_id='BTC-USD'):

        self.url = "wss://ws-feed.gdax.com/"
        self.ws = create_connection(self.url)
        self.product_id = product_id
        self.sequence = -1
        self.stop = False
        self._sequence = -1
        self._client = PublicClient()
        self._asks = RBTree()
        self._bids = RBTree()
        self._current_ticker = None
        self.saveMessage = saveMessage
        self.t1 = None
        self.t2 = None
        self.t3 = None

    def saveMessage(self, msg):
        ### To be done - save live message in file
        path = 'xxx'
        with open(pathOUT, 'a') as fileIO:
            pathOUT.write(json.dumps(msg))
        a = 4

    def demo(self):
        self.connect()
        self.listen(demo=1)
        self.disconnect()

    def connect(self):
        print('connessione')
        sub_params = {'type': 'subscribe', 'product_ids': [product_id]}
        self.ws.send(json.dumps(sub_params))
        sub_params = {"type": "heartbeat", "on": False}
        self.ws.send(json.dumps(sub_params))
        c = 0

    def listen(self, demo=0):
        print('in ascolto')
        count = 0
        while not self.stop:
            data = self.ws.recv()
            msg = json.loads(data)
            self.onmessage(msg)
            print msg['sequence']
            count = count + 1
            if count > 10 and demo:
                self.stop = True

    def disconnect(self):
        print('Fine Connessione')
        #self.reset_book()

    def get_snapshot(self):
        self.reset_book()

    def reset_book(self):
        self._asks = RBTree()
        self._bids = RBTree()
        self.tref = datetime.datetime.now()
        # res = self._client.get_product_order_book(product_id=self.product_id, level=3)
        ##################
        params = {'level': 3}
        r = requests.get('https://api.gdax.com/products/{}/book'.format(
            self.product_id),
                         params=params,
                         timeout=30)
        try:
            res = r.json()
        except:
            res['bids'] = {}
            res['asks'] = {}
            res['sequence'] = 0
        # r.raise_for_status()

        ##################
        for bid in res['bids']:
            self.add1({
                'id': bid[2],
                'side': 'buy',
                'price': Decimal(bid[0]),
                'size': Decimal(bid[1])
            })
        for ask in res['asks']:
            self.add1({
                'id': ask[2],
                'side': 'sell',
                'price': Decimal(ask[0]),
                'size': Decimal(ask[1])
            })
        self._sequence = res['sequence']

    def onmessage(self, msg):
        if self.saveMessage:
            saveMessage(msg)
        message = msg
        sequence = message['sequence']
        if self._sequence == -1:
            self.reset_book()
            return
        if sequence <= self._sequence:
            # ignore older messages (e.g. before order book initialization from getProductOrderBook)
            return
        elif sequence > self._sequence + 1:
            self.on_sequence_gap(self._sequence, sequence)
            return

        msg_type = message['type']
        if msg_type == 'open':
            self.add(message)
        elif msg_type == 'done' and 'price' in message:
            self.remove(message)
        elif msg_type == 'match':
            self.match(message)
            self._current_ticker = message
        elif msg_type == 'change':
            self.change(message)

    def get_current_book_serializable(self):
        result = {
            'sequence': self._sequence,
            'asks': [],
            'bids': [],
        }
        for ask in self._asks:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_ask = self._asks[ask]
            except KeyError:
                continue
            for order in this_ask:
                result['asks'].append(
                    [float(order['price']),
                     float(order['size']), order['id']])
        for bid in self._bids:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_bid = self._bids[bid]
            except KeyError:
                continue

            for order in this_bid:
                result['bids'].append(
                    [float(order['price']),
                     float(order['size']), order['id']])
        return result

    def get_current_book(self):
        result = {
            'sequence': self._sequence,
            'asks': [],
            'bids': [],
        }
        for ask in self._asks:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_ask = self._asks[ask]
            except KeyError:
                continue
            for order in this_ask:
                result['asks'].append(
                    [order['price'], order['size'], order['id']])
        for bid in self._bids:
            try:
                # There can be a race condition here, where a price point is removed
                # between these two ops
                this_bid = self._bids[bid]
            except KeyError:
                continue

            for order in this_bid:
                result['bids'].append(
                    [order['price'], order['size'], order['id']])
        return result

    def on_sequence_gap(self, gap_start, gap_end):
        self.reset_book()
        print(
            'Error: messages missing ({} - {}). Re-initializing  book at sequence.'
            .format(gap_start, gap_end, self._sequence))

    #### UTILS

    def remove(self, order):
        price = Decimal(order['price'])
        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is not None:
                bids = [o for o in bids if o['id'] != order['order_id']]
                if len(bids) > 0:
                    self.set_bids(price, bids)
                else:
                    self.remove_bids(price)
        else:
            asks = self.get_asks(price)
            if asks is not None:
                asks = [o for o in asks if o['id'] != order['order_id']]
                if len(asks) > 0:
                    self.set_asks(price, asks)
                else:
                    self.remove_asks(price)

    def match(self, order):
        size = Decimal(order['size'])
        price = Decimal(order['price'])

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if not bids:
                return
            assert bids[0]['id'] == order['maker_order_id']
            if bids[0]['size'] == size:
                self.set_bids(price, bids[1:])
            else:
                bids[0]['size'] -= size
                self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if not asks:
                return
            assert asks[0]['id'] == order['maker_order_id']
            if asks[0]['size'] == size:
                self.set_asks(price, asks[1:])
            else:
                asks[0]['size'] -= size
                self.set_asks(price, asks)

    def change(self, order):
        try:
            new_size = Decimal(order['new_size'])
        except KeyError:
            return

        try:
            price = Decimal(order['price'])
        except KeyError:
            return

        if order['side'] == 'buy':
            bids = self.get_bids(price)
            if bids is None or not any(o['id'] == order['order_id']
                                       for o in bids):
                return
            index = [b['id'] for b in bids].index(order['order_id'])
            bids[index]['size'] = new_size
            self.set_bids(price, bids)
        else:
            asks = self.get_asks(price)
            if asks is None or not any(o['id'] == order['order_id']
                                       for o in asks):
                return
            index = [a['id'] for a in asks].index(order['order_id'])
            asks[index]['size'] = new_size
            self.set_asks(price, asks)

        tree = self._asks if order['side'] == 'sell' else self._bids
        node = tree.get(price)

        if node is None or not any(o['id'] == order['order_id'] for o in node):
            return

    def get_current_ticker(self):
        return self._current_ticker

########################################

    def add1(self, order):
        order = {
            'id': order.get('order_id') or order['id'],
            'side': order['side'],
            'price': float(order['price']),
            'size': float(order.get('size') or order['remaining_size'])
        }
        if order['side'] == 'buy':
            bids = self.get_bids(order['price'])
            if bids is None:
                bids = [order]
            else:
                bids.append(order)
            self.set_bids(order['price'], bids)
        else:
            asks = self.get_asks(order['price'])
            if asks is None:
                asks = [order]
            else:
                asks.append(order)
            self.set_asks(order['price'], asks)


########################################

    def add(self, order):
        order = {
            'id': order.get('order_id') or order['id'],
            'side': order['side'],
            'price': Decimal(order['price']),
            'size': Decimal(order.get('size') or order['remaining_size'])
        }
        if order['side'] == 'buy':
            bids = self.get_bids(order['price'])
            if bids is None:
                bids = [order]
            else:
                bids.append(order)
            self.set_bids(order['price'], bids)
        else:
            asks = self.get_asks(order['price'])
            if asks is None:
                asks = [order]
            else:
                asks.append(order)
            self.set_asks(order['price'], asks)

    def get_bids(self, price):
        return self._bids.get(price)

    def set_bids(self, price, bids):
        self._bids.insert(price, bids)

    def get_asks(self, price):
        return self._asks.get(price)

    def set_asks(self, price, asks):
        self._asks.insert(price, asks)