Пример #1
0
    def __init__(self):
        self.binanceClient = BinanceClient(BinanceSettings.api_key,
                                           BinanceSettings.api_secret)
        self.gdax_public_client = GdaxPublicClient()
        self.gdax_authenticated_client = GdaxAuthenticatedClient(
            key=GdaxSettings.api_key,
            b64secret=GdaxSettings.api_secret,
            passphrase=GdaxSettings.api_passphrase)
        self.sandbox_gdax_authenticated_client = GdaxAuthenticatedClient(
            key=GdaxSettings.sandbox_key,
            b64secret=GdaxSettings.sandbox_secret,
            passphrase=GdaxSettings.sandbox_passphrase,
            api_url='https://api-public.sandbox.gdax.com')
        self.kraken_public_client = KrakenAPI()

        self.MARKET_BINANCE = 'binance'
        self.MARKET_GDAX = 'gdax'
        self.MARKET_KRAKEN = 'kraken'
Пример #2
0
    def __init__(self, product_id, delta=0.01,
                 auth_client=None, api_key='', secret_key='', pass_phrase='', api_url='', ws_url=''):
        if delta > 0.05:
            raise AlgoStateException('Delta very high @ {}, please check your config'.format(delta))
        self.last_heartbeat = datetime.now()
        self.heartbeat_log_interval = timedelta(minutes=5)
        self.delta = delta
        self.product_id = product_id
        self.api_key = api_key
        self.secret_key = secret_key
        self.pass_phrase = pass_phrase
        if auth_client is None:
            self.client = AuthenticatedClient(api_key, secret_key, pass_phrase, api_url=api_url)
        else:
            self.client = auth_client  # Easier to test via mock

        # Query for product and find status/increment
        products = self.client.get_products()
        product = [x for x in products if x['status'] == 'online' and x['id'] == product_id]
        if len(product) != 1:
            module_logger.error('Product {} invalid from products {}'.format(
                product_id, json.dumps(products, indent=4, sort_keys=True)))
            raise ProductDefinitionFailure(product_id + ' not active for trading')
        self.base_currency = product[0]['base_currency']
        self.quote_currency = product[0]['quote_currency']
        self.quote_increment = float(product[0]['quote_increment'])
        self.base_min_size = float(product[0]['base_min_size'])
        self.base_max_size = float(product[0]['base_max_size'])
        # Account information including ID and available balance
        self.accounts = {}
        # Query for account balances
        self.reset_account_balances()
        # Queue of last (1000ish) filled orders for checking missed fills
        self.opened_orders = [x['id'] for x in self.get_orders()]
        module_logger.info('{}|Startup with orders {}'.format(self.product_id, ', '.join(self.opened_orders)))
        # Flag for when we're waiting for an order to settle, ignore HB/state reconciliation requests
        self.is_filling_order = False
        # Bind to websocket
        if ws_url != '':
            WebSocketClient.__init__(self, ws_url)
Пример #3
0
 def __init__(self, key, secret, passphrase, product):
     self.key = key
     self.secret = secret
     self.passphrase = passphrase
     self.product = product
     self.websocket_client = WebsocketClient()
     self.order_book = OrderBook(product_id=product)
     self.auth_client = AuthenticatedClient(key, secret, passphrase)
     self.public_client = PublicClient()
     self.sells = []
     self.buys = []
     self.sell_tracker = 0
     self.buy_tracker = 0
     self.sell_fees = 0
     self.buy_fees = 0
     self.num_buys = 0
     self.num_sells = 0
     self.av_sell = 0
     self.av_buy = 0
     self.last_buy = 0
     self.last_sell = 0
     self.current_balance = 0
     self.ticker_tracker = []
Пример #4
0
class Interface:
    def __init__(self):
        self.binanceClient = BinanceClient(BinanceSettings.api_key,
                                           BinanceSettings.api_secret)
        self.gdax_public_client = GdaxPublicClient()
        self.gdax_authenticated_client = GdaxAuthenticatedClient(
            key=GdaxSettings.api_key,
            b64secret=GdaxSettings.api_secret,
            passphrase=GdaxSettings.api_passphrase)
        self.sandbox_gdax_authenticated_client = GdaxAuthenticatedClient(
            key=GdaxSettings.sandbox_key,
            b64secret=GdaxSettings.sandbox_secret,
            passphrase=GdaxSettings.sandbox_passphrase,
            api_url='https://api-public.sandbox.gdax.com')
        self.kraken_public_client = KrakenAPI()

        self.MARKET_BINANCE = 'binance'
        self.MARKET_GDAX = 'gdax'
        self.MARKET_KRAKEN = 'kraken'

    def get_prices(self, exchange):
        if exchange == self.MARKET_BINANCE:
            market_books = self.binanceClient.get_products_and_prices()
        elif exchange == self.MARKET_GDAX:
            market_books = self.gdax_public_client.get_products_with_prices()
        elif exchange == self.MARKET_KRAKEN:
            market_books = self.kraken_public_client.get_market_tickers()
        else:
            print("No exchange found with name: " + exchange)
            # TODO throw exception
        return market_books

    def create_test_order(self,
                          exchange,
                          symbol,
                          side,
                          limit_market,
                          quantity,
                          price=0.0):
        if exchange == self.MARKET_BINANCE:
            response = self.binanceClient.create_test_order(symbol=symbol,
                                                            side=side,
                                                            type=limit_market,
                                                            quantity=quantity,
                                                            price=price,
                                                            timeInForce="GTC")
            print("order is filled")
            # The binance test order does not actually get passed to the matching engine, so we're unable to test this.

        if exchange == self.MARKET_GDAX:
            symbol_reformatted = symbol[:3] + "-" + symbol[
                3:]  # TODO is this correct?
            response = self.sandbox_gdax_authenticated_client.create_order(
                symbol=symbol_reformatted,
                side=side,
                limit_market=limit_market,
                quantity=quantity,
                price=price)
            order_id = response["id"]
            order_filled = False
            while not order_filled:
                order = self.sandbox_gdax_authenticated_client.get_order(
                    order_id)
                if order["status"] == "done":
                    print("order is filled!")
                    order_filled = True
                else:
                    print("order not yet filled")
                    time.sleep(5)

    def create_order(self,
                     exchange,
                     symbol,
                     side,
                     limit_market,
                     quantity,
                     price=0.0):
        if exchange == self.MARKET_BINANCE:
            order = self.binanceClient.create_order(symbol,
                                                    side,
                                                    quantity,
                                                    price,
                                                    type=limit_market)
        elif exchange == self.MARKET_GDAX:
            order = self.gdax_authenticated_client.create_order(
                symbol, side, limit_market, quantity, price)
        else:
            print("No exchange found with name: " + exchange)
            # TODO throw exception
        print(order)

    def order_filled(self, exchange, order_id):
        if exchange == self.MARKET_BINANCE:
            return self.binanceClient.get_order(order_id)['status'] == 'done'
        elif exchange == self.MARKET_GDAX:
            return self.gdax_authenticated_client.get_order(
                order_id)['status'] == 'done'
        else:
            return ''
Пример #5
0
    },
})
module_logger = logging.getLogger(__name__)

if __name__ == '__main__':

    if len(sys.argv) == 1:
        file = 'config/prod.json'
    else:
        file = sys.argv[1]

    with open(file) as config:
        data = json.load(config)

    auth_client = AuthenticatedClient(data['auth']['key'],
                                      data['auth']['secret'],
                                      data['auth']['phrase'],
                                      api_url=data['endpoints']['rest'])
    running = True
    while running:
        try:
            total = 0.0
            accounts = auth_client.get_accounts()
            message_parts = []
            for account in accounts:
                balance = float(account['balance'])
                if balance > 0:
                    currency = account['currency']
                    if currency != 'USD':
                        product = '{}-USD'.format(currency)
                        ask = float(
                            auth_client.get_product_ticker(product)['ask'])
Пример #6
0
class Trader(WebSocketClient):
    def __init__(self, product_id, delta=0.01,
                 auth_client=None, api_key='', secret_key='', pass_phrase='', api_url='', ws_url=''):
        if delta > 0.05:
            raise AlgoStateException('Delta very high @ {}, please check your config'.format(delta))
        self.last_heartbeat = datetime.now()
        self.heartbeat_log_interval = timedelta(minutes=5)
        self.delta = delta
        self.product_id = product_id
        self.api_key = api_key
        self.secret_key = secret_key
        self.pass_phrase = pass_phrase
        if auth_client is None:
            self.client = AuthenticatedClient(api_key, secret_key, pass_phrase, api_url=api_url)
        else:
            self.client = auth_client  # Easier to test via mock

        # Query for product and find status/increment
        products = self.client.get_products()
        product = [x for x in products if x['status'] == 'online' and x['id'] == product_id]
        if len(product) != 1:
            module_logger.error('Product {} invalid from products {}'.format(
                product_id, json.dumps(products, indent=4, sort_keys=True)))
            raise ProductDefinitionFailure(product_id + ' not active for trading')
        self.base_currency = product[0]['base_currency']
        self.quote_currency = product[0]['quote_currency']
        self.quote_increment = float(product[0]['quote_increment'])
        self.base_min_size = float(product[0]['base_min_size'])
        self.base_max_size = float(product[0]['base_max_size'])
        # Account information including ID and available balance
        self.accounts = {}
        # Query for account balances
        self.reset_account_balances()
        # Queue of last (1000ish) filled orders for checking missed fills
        self.opened_orders = [x['id'] for x in self.get_orders()]
        module_logger.info('{}|Startup with orders {}'.format(self.product_id, ', '.join(self.opened_orders)))
        # Flag for when we're waiting for an order to settle, ignore HB/state reconciliation requests
        self.is_filling_order = False
        # Bind to websocket
        if ws_url != '':
            WebSocketClient.__init__(self, ws_url)

    def opened(self):
        """Called when the websocket handshake has been established, sends
        the initial subscribe message to gdax.
        """
        timestamp = str(time.time())
        message = timestamp + 'GET' + '/users/self/verify'
        message = message.encode('ascii')
        hmac_key = base64.b64decode(self.secret_key)
        signature = hmac.new(hmac_key, message, hashlib.sha256)
        signature_b64 = base64.b64encode(signature.digest())
        params = {
            'type': 'subscribe',
            'product_ids': [
                self.product_id,
            ],
            'signature': signature_b64.decode('utf-8'),
            'key': self.api_key,
            'passphrase': self.pass_phrase,
            'timestamp': timestamp,
            'channels': [
                'heartbeat',
                'user'
            ],
        }
        self.send(json.dumps(params))

    def closed(self, code, reason=None):
        module_logger.info('{}|Closed down. Code: {} Reason: {}'.format(self.product_id, code, reason))

    def cache_orders(self, order_id):
        """Sliding window of IDs
        """
        self.opened_orders.append(order_id)
        if len(self.opened_orders) > 1200:
            self.opened_orders.pop(0)

    def remove_order(self, order_id):
        self.opened_orders = [x for x in self.opened_orders if x != order_id]

    def received_message(self, message):
        message = json.loads(str(message))
        # Ignore messages for different products since we're subscribing to user channel
        if message.get('product_id', '') != self.product_id:
            return
        message_type = message.get('type', '')
        log_message = '{}|Message from websocket:{}'.format(self.product_id,
                                                            json.dumps(message, indent=4, sort_keys=True))
        if message_type == 'heartbeat':
            if self.last_heartbeat + self.heartbeat_log_interval <= datetime.now():
                orders = self.get_orders()
                module_logger.info(
                    '{}|Heartbeat:{}:{} orders'.format(self.product_id, message.get('sequence', 0), len(orders)))
                for order in orders:
                    module_logger.info(
                        '{}|{} {} @ {}:{}'.format(self.product_id, order['side'], order['size'],
                                                  self.get_order_price(order),
                                                  order['id']))
                self.last_heartbeat = datetime.now()
                # Also take opportunity to check for missed messages
                self.check_missed_fills()
                if not self.is_filling_order:
                    # Refresh orders first in case we filled
                    orders = self.get_orders()
                    self.check_orders(orders)

            else:
                module_logger.debug(log_message)
        # Order fill message
        elif message_type == 'done':
            module_logger.info(log_message)
            self.is_filling_order = True
            self.on_order_done(message)
            self.is_filling_order = False

    @staticmethod
    def get_order_price(order):
        if 'stop_price' in order:
            return order['stop_price']
        else:
            return order['price']

    def check_orders(self, orders):
        """Validate that we have the proper orders set for our current status
        """
        pass

    def check_missed_fills(self):
        """Queries for transactions by going against account history api
        https://docs.gdax.com/#get-account-history
        [
            {
                "id": "100",
                "created_at": "2014-11-07T08:19:27.028459Z",
                "amount": "0.001",
                "balance": "239.669",
                "type": "fee",
                "details": {
                    "order_id": "d50ec984-77a8-460a-b958-66f114b0de9b",
                    "trade_id": "74",
                    "product_id": "BTC-USD"
                }
            }
        ]
        https://docs.gdax.com/#get-an-order
        {
            "id": "68e6a28f-ae28-4788-8d4f-5ab4e5e5ae08",
            "size": "1.00000000",
            "product_id": "BTC-USD",
            "side": "buy",
            "stp": "dc",
            "funds": "9.9750623400000000",
            "specified_funds": "10.0000000000000000",
            "type": "market",
            "post_only": false,
            "created_at": "2016-12-08T20:09:05.508883Z",
            "done_at": "2016-12-08T20:09:05.527Z",
            "done_reason": "filled",
            "fill_fees": "0.0249376391550000",
            "filled_size": "0.01291771",
            "executed_value": "9.9750556620000000",
            "status": "done",
            "settled": true
        }
        """
        accounts = [v['id'] for v in self.accounts.values()]
        for account in accounts:
            history = self.client.get_account_history(account)[0]
            matches = [x for x in history if x['type'] == 'match']
            matches = [x for x in matches if x['details']['product_id'] == self.product_id]
            order_ids = [x['details']['order_id'] for x in matches]
            for order_id in order_ids:
                if order_id in self.opened_orders:
                    order = self.client.get_order(order_id)
                    # Done? Or just partially filled
                    if order['status'] == 'done' and order['done_reason'] == 'filled':
                        # We've missed a fill, rectify that
                        module_logger.info('{}|Missed fill for {}'.format(self.product_id, order_id))
                        self.on_order_done({
                            'order_id': order_id,
                            'reason': order['done_reason'],
                            'product_id': self.product_id,
                        })

    def reset_account_balances(self):
        """Query rest endpoint for available account balance
        https://docs.gdax.com/#accounts
        [
            {
                "id": "71452118-efc7-4cc4-8780-a5e22d4baa53",
                "currency": "BTC",
                "balance": "0.0000000000000000",
                "available": "0.0000000000000000",
                "hold": "0.0000000000000000",
                "profile_id": "75da88c5-05bf-4f54-bc85-5c775bd68254"
            },
            {
                "id": "e316cb9a-0808-4fd7-8914-97829c1925de",
                "currency": "USD",
                "balance": "80.2301373066930000",
                "available": "79.2266348066930000",
                "hold": "1.0035025000000000",
                "profile_id": "75da88c5-05bf-4f54-bc85-5c775bd68254"
            }
        ]
        """
        accounts = self.client.get_accounts()
        for currency in [self.base_currency, self.quote_currency]:
            account = [x for x in accounts if x['currency'] == currency]
            if len(account) != 1 or 'available' not in account[0] or 'balance' not in account[0]:
                module_logger.error('Account lookup failure for {} from {}'.format(
                    currency, json.dumps(accounts, indent=4, sort_keys=True)))
                raise AccountBalanceFailure(currency + ' not found in active accounts')
            self.accounts[currency] = {
                'available': float(account[0]['available']),
                'balance': float(account[0]['balance']),
                'id': account[0]['id'],
            }
            module_logger.debug(
                'Set available account balances: {}'.format(json.dumps(accounts, indent=4, sort_keys=True)))

    def seed_wallet(self, quote_ccy_size):
        """At the start of the day or when the wallet is empty, need something to trade
        Place a stop buy at 1% above current market and a limit post buy at 1% below to minimize fees
        """
        ticker = self.client.get_product_ticker(self.product_id)
        if 'price' not in ticker:
            module_logger.exception('Unable to get current market quote')
            raise OrderPlacementFailure(
                'Unable to get market quote, api message: {}'.format(ticker.get('message', 'unknown error')))
        current_price = float(ticker['price'])
        size = self.to_size_increment(quote_ccy_size / current_price)
        delta = current_price * self.delta
        module_logger.info(
            '{}|Seeding wallet: {} {} @ {}/{}'.format(self.product_id, size, self.product_id, current_price + delta,
                                                      current_price - delta))
        self.buy_stop(size, current_price + delta)
        self.buy_limit_ptc(size, current_price - delta)

    def wait_for_settle(self, order_id):
        """Funds aren't available until order is in settled state. Return the full order message
        """
        settled = False
        order = None
        while not settled:
            order = self.client.get_order(order_id)
            settled = order.get('settled', False)
            if not settled:
                module_logger.info('{}|Waiting for {} to settled'.format(self.product_id, order_id))
                time.sleep(1)  # Takes a few seconds
        # Once we know order is settled, re-query account balances
        self.reset_account_balances()
        module_logger.info('{}|{} settled'.format(self.product_id, order_id))
        return order

    def place_decaying_order(self, side, order_type, size, price, retries=3, spread=0.006):
        """Makes a call to the rest order endpoint. On failure, tries again n times widening the bid/ask
        by 0.6% each time in case the order would result in taking liquidity (and thus accruing fees)
        """
        size = self.to_size_increment(size)
        if size < self.base_min_size or size > self.base_max_size:
            raise OrderPlacementFailure('Size of {} outside of exchange limits'.format(size))
        if side is 'buy':
            direction = -1
        elif side is 'sell':
            direction = 1
        else:
            raise OrderPlacementFailure('Side {} not expected, what are you doing?'.format(side))

        for i in range(retries):
            price = self.to_price_increment(price + (price * direction * i * spread))
            if side is 'buy':
                if order_type is 'limit':
                    result = self.client.buy(
                        type='limit',
                        product_id=self.product_id,
                        price=price,
                        size=size,
                        post_only=True,
                    )
                elif order_type is 'stop':
                    # Stop order will accrue fees, but can't really avoid that
                    result = self.client.buy(
                        type='stop',
                        product_id=self.product_id,
                        price=price,
                        size=size,
                        # If not specified, will hold entire account balance!
                        funds=self.to_size_increment(size * price, self.quote_currency)
                    )
                else:
                    raise OrderPlacementFailure('{} of type {} not supported'.format(side, order_type))
            else:
                if order_type is 'limit':
                    result = self.client.sell(
                        type='limit',
                        product_id=self.product_id,
                        price=price,
                        size=size,
                        # Post Only to avoid fees, order fails if it would result in taking liquidity
                        post_only=True,
                    )
                else:
                    raise OrderPlacementFailure('{} of type {} not supported'.format(side, order_type))
            if 'message' in result:
                module_logger.warning(
                    'Error placing {} {} order for {} {} @ {}, retrying. Message from api: {}'.format(
                        side, order_type, size, self.product_id, price, result['message']))
                time.sleep(1)
            else:
                self.cache_orders(result['id'])
                module_logger.info(
                    '{}|Placed {} {} order {} @ {}'.format(self.product_id, side, order_type, size, price))
                return
        # Failed on decaying price, raise an exception
        message = '{}|Error placing {} order of type {}. Retried {} times, giving up'.format(self.product_id, side,
                                                                                             order_type, retries)
        module_logger.exception(message)
        raise OrderPlacementFailure(message)

    def buy_stop(self, size, price, retries=3, spread=0.003):
        self.place_decaying_order('buy', 'stop', size, price, retries=retries, spread=spread)

    def buy_limit_ptc(self, size, price, retries=3, spread=0.003):
        self.place_decaying_order('buy', 'limit', size, price, retries=retries, spread=spread)

    def sell_limit_ptc(self, size, price, retries=3, spread=0.003):
        self.place_decaying_order('sell', 'limit', size, price, retries=retries, spread=spread)

    def get_orders(self):
        return [x for x in self.client.get_orders()[0] if x['product_id'] == self.product_id]

    def cancel_all(self):
        """BAIL!!!
        """
        orders = [x['id'] for x in self.get_orders()]
        for order_id in orders:
            result = self.client.cancel_order(order_id)
            module_logger.info('{}|Canceling {}, result: {}'.format(self.product_id, order_id,
                                                                    json.dumps(result, indent=4, sort_keys=True)))

    def on_order_done(self, message):
        """Action to take on order complete. Orders may be considered done even if they have a small amount
        of remaining size. Base implementation blocks until order settles then resets account balances
        {
            "order_id": "6decaa0f-1a71-40c1-a665-4613e6312e9f",
            "product_id": "ETH-USD",
            "profile_id": "bleh",
            "reason": "filled",
            "remaining_size": "0.00007585",
            "sequence": 2066310881,
            "side": "buy",
            "time": "2018-01-17T10:38:00.203000Z",
            "type": "done",
            "user_id": "bah"
        }
        """
        order_id = message['order_id']
        reason = message['reason']
        if reason == 'filled' and message['product_id'] == self.product_id:
            module_logger.info('{}|Order {} {}'.format(self.product_id, order_id, reason))
            if order_id not in self.opened_orders:
                module_logger.info('{}|Order not in cached orders, ignoring'.format(self.product_id))
            else:
                self.remove_order(order_id)
                settled_order = self.wait_for_settle(order_id)
                self.reset_account_balances()
                self.place_next_orders(settled_order)

    def on_start(self):
        """Intended to be overriden.
        """
        pass

    def place_next_orders(self, message):
        """Intended to be overriden. Main algo logic goes here
        """
        pass

    def get_available_balance(self, currency):
        if currency in self.accounts:
            return self.accounts[currency]['available']
        else:
            return 0.0

    def get_balance(self, currency):
        if currency in self.accounts:
            return self.accounts[currency].get('balance', self.get_available_balance(currency))
        else:
            return 0.0

    def to_price_increment(self, price):
        """Round to nearest price increment.
        https://docs.gdax.com/#get-products
        Otherwise order will be rejected.
        """
        diff = price - round(price)
        increments = round(diff / self.quote_increment)
        return round(price) + (increments * self.quote_increment)

    def to_size_increment(self, size_base_ccy, base_currency=''):
        if base_currency == '':
            base_currency = self.base_currency
        if base_currency in ['BTC', 'ETH', 'LTC', 'BCH']:
            return max(round(size_base_ccy, 8), self.base_min_size)
        else:
            return round(size_base_ccy, 2)
Пример #7
0
class Bot(object):
    def __init__(self, key, secret, passphrase, product):
        self.key = key
        self.secret = secret
        self.passphrase = passphrase
        self.product = product
        self.websocket_client = WebsocketClient()
        self.order_book = OrderBook(product_id=product)
        self.auth_client = AuthenticatedClient(key, secret, passphrase)
        self.public_client = PublicClient()
        self.sells = []
        self.buys = []
        self.sell_tracker = 0
        self.buy_tracker = 0
        self.sell_fees = 0
        self.buy_fees = 0
        self.num_buys = 0
        self.num_sells = 0
        self.av_sell = 0
        self.av_buy = 0
        self.last_buy = 0
        self.last_sell = 0
        self.current_balance = 0
        self.ticker_tracker = []

    def get_order_info(self, ticker, number_measures, length_measures,
                       order_book_skim):

        bid_intervals = []
        bid_depths_intervals = []
        ask_intervals = []
        ask_depths_intervals = []
        count = 1
        while count <= number_measures:
            print('Volume check %s' % count)
            time.sleep(length_measures)
            current_order_book = self.order_book.get_current_book()
            bids, bid_depths, asks, ask_depths = sort_messages(
                ticker, current_order_book, order_book_skim)
            bid_intervals.append(bids)
            ask_intervals.append(asks)
            bid_depths_intervals.append(bid_depths)
            ask_depths_intervals.append(ask_depths)
            count += 1

        return bid_intervals, bid_depths_intervals, ask_intervals, ask_depths_intervals

    def check_volatility(self, volatility_interval, num_volatility_measures):
        if len(self.ticker_tracker) > num_volatility_measures + 1:
            delta_price = np.diff(self.ticker_tracker)
            vol_measure = np.average(delta_price[-num_volatility_measures:])

        else:
            vol_measure = 0

        return abs(vol_measure)

    def start_order_book(self):
        self.order_book.start()

    def order_book_repeat_sample(self, duration, order_book_skim):

        with open('crypto_data.csv', 'w') as csv_file:
            writer = csv.writer(
                csv_file,
                delimiter='\t',
                lineterminator='\n',
            )
            header = [
                'datetime', 'ticker price', 'average bid', 'average ask',
                'bid volume', 'ask volume'
            ]
            writer.writerow(header)
            while True:
                ticker = float(
                    self.public_client.get_product_ticker(
                        self.product)['price'])
                current_order_book = self.order_book.get_current_book()
                bids, bid_depths, asks, ask_depths = sort_messages(
                    ticker, current_order_book, order_book_skim)

                if len(bids) != 0 and len(asks) != 0 and len(
                        bid_depths) != 0 and len(ask_depths) != 0:
                    print(bids[1])
                    print(bids[2])
                    av_bid = np.average(bids)
                    av_ask = np.average(asks)
                    total_bids = np.sum(bid_depths)
                    total_asks = np.sum(ask_depths)
                else:
                    av_bid = 'N/A'
                    av_ask = 'N/A'
                    total_bids = 'N/A'
                    total_asks = 'N/A'
                print('Average bid', av_bid)
                print('Average ask', av_ask)
                print('total bid volume', total_bids)
                print('total ask volume', total_asks)
                data = [
                    datetime.datetime.now(), ticker, av_bid, av_ask,
                    total_bids, total_asks
                ]
                writer.writerow(data)
                time.sleep(duration)

    def track_performance(self, string_id, transaction):

        if 'side' in transaction.keys():
            if transaction['side'] == 'sell':
                self.sells.append(transaction['id'])
            if transaction['side'] == 'buy':
                self.buys.append(transaction['id'])

    def evaluate_performance(self):

        print('Current sell orders:')
        for sell in self.sells:
            order = self.auth_client.get_order(sell)

            if 'message' in order.keys():
                self.sells.remove(sell)

            else:
                print('price:', order['price'], 'size:', order['size'])

                if order['settled'] == True:
                    self.sell_tracker += float(order['size']) * float(
                        order['price'])
                    self.sell_fees += float(order['fill_fees'])
                    self.num_sells += float(order['size'])
                    self.sells.remove(sell)

        print('Current buy orders:')
        for buy in self.buys:
            order = self.auth_client.get_order(buy)

            if 'message' in order.keys():
                self.buys.remove(buy)

            else:

                print('price:', order['price'], 'size:', order['size'])
                if order['settled'] == True:
                    self.buy_tracker += float(order['size']) * float(
                        order['price'])
                    self.buy_fees += float(order['fill_fees'])
                    self.num_buys += float(order['size'])
                    self.buys.remove(buy)

        if self.num_buys > 0:
            self.av_buy = self.buy_tracker / self.num_buys

        if self.num_sells > 0:
            self.av_sell = self.sell_tracker / self.num_sells

        print('Number of buys:', self.num_buys)
        print('Buy USD volume:', self.buy_tracker)
        print('Number of sells:', self.num_sells)
        print('Sell USD volume:', self.sell_tracker)
        print('Net fees:', self.sell_fees + self.buy_fees)
        print(
            'Net USD profit:', self.sell_tracker - self.buy_tracker -
            self.sell_fees - self.buy_fees)
        print('Av sell:', self.av_sell)
        print('Av buy:', self.av_buy)

    def check_existing_sells(self, current_order_price, current_order_size,
                             vol_measure):

        new_average = (self.sell_tracker +
                       current_order_price * current_order_size) / (
                           self.num_sells + current_order_size)

        print('new av', new_average)

        if len(self.sells) > 0:

            if new_average > self.av_buy:

                if self.num_buys > 0:

                    for sell in self.sells:

                        order = self.auth_client.get_order(sell)
                        order_price = float(order['price'])

                        if 'message' in order.keys():
                            self.buys.remove(sell)
                            return 'keep'

                        else:
                            if self.av_buy < current_order_price:

                                if order_price < current_order_price - vol_measure:
                                    self.buys.remove(sell)
                                    self.auth_client.cancel_order(order['id'])
                                    return 'keep'

                                else:
                                    return 'abandon'

                            else:
                                return 'abandon'

                else:

                    if current_order_price > self.last_buy:

                        return 'keep'

                    else:
                        return 'abandon'

            else:
                return 'abandon'

        else:
            return 'keep'

    def check_existing_buys(self, current_order_price, current_order_size,
                            vol_measure):

        fees = 0.0025 * (current_order_price * current_order_size)

        current_plus_fees = current_order_price + fees

        new_average = (self.buy_tracker +
                       current_order_price * current_order_size +
                       fees) / (self.num_buys + current_order_size)

        if len(self.buys) > 0:

            if new_average < self.av_sell:

                if self.num_sells > 0:

                    for buy in self.buys:

                        order = self.auth_client.get_order(buy)

                        order_plus_fees = float(
                            order['price']) + 0.0025 * float(
                                order['price']) * float(order['size'])

                        if 'message' in order.keys():
                            self.buys.remove(buy)
                            return 'keep'

                        else:
                            if self.av_sell > order_plus_fees:

                                if order_plus_fees > current_plus_fees + vol_measure:
                                    self.buys.remove(buy)
                                    self.auth_client.cancel_order(order['id'])
                                    return 'keep'

                                else:
                                    return 'abandon'

                            else:
                                return 'abandon'

                else:

                    if current_order_price < self.last_sell:

                        return 'keep'

                    else:

                        return 'abandon'

            else:
                return 'abandon'

        else:
            return 'keep'

    def execute_volume_strategy(self, number_measures, length_measures,
                                order_book_skim, base_size,
                                volatility_interval, num_volatility_measures,
                                vol_confidence, run_minutes):

        t_end = time.time() + 60 * run_minutes
        while time.time() < t_end:
            print('time remaining', t_end - time.time())
            #
            #        while True:
            vol_measure = self.check_volatility(volatility_interval,
                                                num_volatility_measures)

            #        accounts = self.auth_client.get_accounts()

            #        balance = 0

            #     for account in accounts:
            #         if account['currency'] == self.product:
            #             balance = account['balance']

            #    print('balance', balance)

            ticker = float(
                self.public_client.get_product_ticker(self.product)['price'])

            bid_intervals, bid_depths_intervals, ask_intervals, ask_depths_intervals = self.get_order_info(
                ticker, number_measures, length_measures, order_book_skim)

            while check_order_info(bid_intervals, bid_depths_intervals,
                                   ask_intervals,
                                   ask_depths_intervals) == False:
                time.sleep(10)
                bid_intervals, bid_depths_intervals, ask_intervals, ask_depths_intervals = self.get_order_info(
                    ticker, number_measures, length_measures, order_book_skim)

            ticker = float(
                self.public_client.get_product_ticker(self.product)['price'])
            self.ticker_tracker.append(ticker)
            self.check_volatility(volatility_interval, num_volatility_measures)

            decision = make_trade_decision(ticker, bid_intervals,
                                           bid_depths_intervals, ask_intervals,
                                           ask_depths_intervals, vol_measure,
                                           vol_confidence, base_size,
                                           self.num_buys, self.num_sells,
                                           self.av_buy, self.av_sell)

            if decision[0] == 'buy':
                place_decision = self.check_existing_buys(
                    float(decision[1]), float(decision[2]), vol_measure)
                if place_decision == 'keep':

                    print('Placing buy order...')
                    print('price', decision[1], 'size', decision[2])
                    buy = self.auth_client.buy(price=decision[1],
                                               size=decision[2],
                                               product_id=self.product)
                    self.last_buy = float(decision[1])
                    self.track_performance('buy', buy)
                    time.sleep(10)

                if place_decision == 'abandon':
                    'Waiting on existing trades'

            elif decision[0] == 'sell':
                place_decision = self.check_existing_sells(
                    float(decision[1]), float(decision[2]), vol_measure)
                if place_decision == 'keep':

                    print('Placing sell order...')
                    print('price', decision[1], 'size', decision[2])
                    sell = self.auth_client.sell(price=decision[1],
                                                 size=decision[2],
                                                 product_id=self.product)
                    self.last_sell = float(decision[1])
                    self.track_performance('sell', sell)
                    time.sleep(10)

                if place_decision == 'abandon':
                    'Waiting on existing trades'

            elif decision[0] == 'hold':
                print('Holding...')

            else:
                print('Problem...')

            self.evaluate_performance()

        print('test complete')
        print()