Example #1
0
    def getExternalMarket(self):
        try:
            bitstamp_book = yield self.bitstamp.getOrderBook('BTC/USD')
            btcusd_bid = bitstamp_book['bids'][0]['price']
            btcusd_ask = bitstamp_book['asks'][0]['price']
        except Exception as e:
            # Unable to get markets, just exit
            print "unable to get external market data from bitstamp: %s" % e
            raise e

        for ticker, market in self.markets.iteritems():
            new_ask = None
            new_bid = None

            if ticker not in self.factory.ignore_contracts:
                if market['contract_type'] == "cash_pair":
                    if ticker == "BTC/USD":
                        new_bid = btcusd_bid
                        new_ask = btcusd_ask
                    else:
                        currency = market['denominated_contract_ticker']

                        try:
                        # Get Yahoo quote
                            yahoo_book = yield self.yahoo.getOrderBook('USD/%s' % currency)
                            bid = yahoo_book['bids'][0]['price']
                            ask = yahoo_book['asks'][0]['price']
                        except Exception as e:
                            # Unable to get markets, just exit
                            print "unable to get external market data from Yahoo: %s" % e
                            continue

                        new_bid = btcusd_bid * bid
                        new_ask = btcusd_ask * ask
                elif market['contract_type'] == "futures":
                    got_spot = False
                    if ticker.startswith("USDBTC"):
                        # Ignore BTC and USD interest rates
                        new_bid_spot = 10000/btcusd_ask
                        new_ask_spot = 10000/btcusd_bid
                        # Assume 10bps USD risk-free rate
                        base_rate = 0.0010
                        got_spot = True
                    elif ticker.startswith("LTCBTC"):
                        kraken_book = yield self.kraken.getOrderBook('BTC/LTC')
                        btcltc_bid = kraken_book['bids'][0]['price']
                        btcltc_ask = kraken_book['asks'][0]['price']
                        new_bid_spot = 10000/btcltc_ask
                        new_ask_spot = 10000/btcltc_bid
                        # Assume 10% LTC risk-free rate
                        base_rate = 0.10
                        got_spot = True

                    if got_spot:
                        from datetime import datetime
                        import util
                        timedelta_to_expiry = util.timestamp_to_dt(market['expiration']) - datetime.utcnow()
                        time_to_expiry = timedelta_to_expiry.total_seconds() / (365.25*24*60*60)
                        # Assume 5% BTC risk-free rate
                        btc_rate = 0.0500
                        import math
                        forward_factor = Decimal(math.exp((base_rate - btc_rate) * time_to_expiry))
                        new_bid = new_bid_spot * forward_factor
                        new_ask = new_ask_spot * forward_factor

                if new_ask is not None and new_bid is not None:
                    logging.info("%s: %f/%f" % (ticker, new_bid, new_ask))

                    # Make sure that the marketwe are making isn't crossed
                    if new_bid > new_ask:
                        tmp = new_bid
                        new_bid = new_ask
                        new_ask = tmp

                    # If it's matched or crossed make a spread just because
                    if self.price_to_wire(ticker, new_bid) >= self.price_to_wire(ticker, new_ask):
                        new_bid = min(new_bid, new_ask) - self.price_from_wire(ticker, self.markets[ticker]['tick_size'])
                        new_ask = max(new_bid, new_ask) + self.price_from_wire(ticker, self.markets[ticker]['tick_size'])

                    if ticker in self.external_markets:
                        if new_bid != self.external_markets[ticker]['bid']:
                            self.external_markets[ticker]['bid'] = new_bid
                            self.replaceBidAsk(ticker, new_bid, 'BUY')
                        if new_ask != self.external_markets[ticker]['ask']:
                            self.external_markets[ticker]['ask'] = new_ask
                            self.replaceBidAsk(ticker, new_ask, 'SELL')
                    else:
                        self.external_markets[ticker] = {'bid': new_bid, 'ask': new_ask}
                        self.replaceBidAsk(ticker, new_ask, 'SELL')
                        self.replaceBidAsk(ticker, new_bid, 'BUY')
Example #2
0
    def getExternalMarket(self):
        try:
            bitstamp_book = yield self.bitstamp.getOrderBook('BTC/USD')
            btcusd_bid = bitstamp_book['bids'][0]['price']
            btcusd_ask = bitstamp_book['asks'][0]['price']
        except Exception as e:
            # Unable to get markets, just exit
            print "unable to get external market data from bitstamp: %s" % e
            raise e

        for ticker, market in self.markets.iteritems():
            new_ask = None
            new_bid = None

            if ticker not in self.factory.ignore_contracts:
                if market['contract_type'] == "cash_pair":
                    if ticker == "BTC/USD":
                        new_bid = btcusd_bid
                        new_ask = btcusd_ask
                    else:
                        currency = market['denominated_contract_ticker']

                        try:
                            # Get Yahoo quote
                            yahoo_book = yield self.yahoo.getOrderBook(
                                'USD/%s' % currency)
                            bid = yahoo_book['bids'][0]['price']
                            ask = yahoo_book['asks'][0]['price']
                        except Exception as e:
                            # Unable to get markets, just exit
                            print "unable to get external market data from Yahoo: %s" % e
                            continue

                        new_bid = btcusd_bid * bid
                        new_ask = btcusd_ask * ask
                elif market['contract_type'] == "futures":
                    got_spot = False
                    if ticker.startswith("USDBTC"):
                        # Ignore BTC and USD interest rates
                        new_bid_spot = 10000 / btcusd_ask
                        new_ask_spot = 10000 / btcusd_bid
                        # Assume 10bps USD risk-free rate
                        base_rate = 0.0010
                        got_spot = True
                    elif ticker.startswith("LTCBTC"):
                        kraken_book = yield self.kraken.getOrderBook('BTC/LTC')
                        btcltc_bid = kraken_book['bids'][0]['price']
                        btcltc_ask = kraken_book['asks'][0]['price']
                        new_bid_spot = 10000 / btcltc_ask
                        new_ask_spot = 10000 / btcltc_bid
                        # Assume 10% LTC risk-free rate
                        base_rate = 0.10
                        got_spot = True

                    if got_spot:
                        from datetime import datetime
                        import util
                        timedelta_to_expiry = util.timestamp_to_dt(
                            market['expiration']) - datetime.utcnow()
                        time_to_expiry = timedelta_to_expiry.total_seconds(
                        ) / (365.25 * 24 * 60 * 60)
                        # Assume 5% BTC risk-free rate
                        btc_rate = 0.0500
                        import math
                        forward_factor = Decimal(
                            math.exp((base_rate - btc_rate) * time_to_expiry))
                        new_bid = new_bid_spot * forward_factor
                        new_ask = new_ask_spot * forward_factor

                if new_ask is not None and new_bid is not None:
                    logging.info("%s: %f/%f" % (ticker, new_bid, new_ask))

                    # Make sure that the marketwe are making isn't crossed
                    if new_bid > new_ask:
                        tmp = new_bid
                        new_bid = new_ask
                        new_ask = tmp

                    # If it's matched or crossed make a spread just because
                    if self.price_to_wire(ticker,
                                          new_bid) >= self.price_to_wire(
                                              ticker, new_ask):
                        new_bid = min(new_bid, new_ask) - self.price_from_wire(
                            ticker, self.markets[ticker]['tick_size'])
                        new_ask = max(new_bid, new_ask) + self.price_from_wire(
                            ticker, self.markets[ticker]['tick_size'])

                    if ticker in self.external_markets:
                        if new_bid != self.external_markets[ticker]['bid']:
                            self.external_markets[ticker]['bid'] = new_bid
                            self.replaceBidAsk(ticker, new_bid, 'BUY')
                        if new_ask != self.external_markets[ticker]['ask']:
                            self.external_markets[ticker]['ask'] = new_ask
                            self.replaceBidAsk(ticker, new_ask, 'SELL')
                    else:
                        self.external_markets[ticker] = {
                            'bid': new_bid,
                            'ask': new_ask
                        }
                        self.replaceBidAsk(ticker, new_ask, 'SELL')
                        self.replaceBidAsk(ticker, new_bid, 'BUY')
Example #3
0
    def atomic_commit(self, postings):

        start = time.time()
        log.msg("atomic commit called for %s at %f" % (postings, start))
        try:
            # sanity check
            if len(postings) == 0:
                raise INTERNAL_ERROR

            types = [posting["type"] for posting in postings]
            counts = [posting["count"] for posting in postings]

            if not all(type == types[0] for type in types):
                raise TYPE_MISMATCH
            if not all(count == counts[0] for count in counts):
                raise COUNT_MISMATCH

            # balance check
            debitsum = defaultdict(int)
            creditsum = defaultdict(int)

            log.msg("auditing postings at %f" % (time.time() - start))
            for posting in postings:
                if posting["direction"] == "debit":
                    debitsum[posting["contract"]] += posting["quantity"]
                if posting["direction"] == "credit":
                    creditsum[posting["contract"]] += posting["quantity"]

            for ticker in debitsum:
                if debitsum[ticker] - creditsum[ticker] is not 0:
                    raise QUANTITY_MISMATCH

            # create the journal and postings
            # The journal is created separately from the postings but this is ok because
            # all the postings are created at once. If the posting commit fails then we'll
            # just end up with an empty journal which won't break anything
            # TODO: Create the journal and postings together
            log.msg("creating the journal at %f" % (time.time() - start))
            ins = Journal.__table__.insert()

            result = self.execute(ins,
                                  type=types[0],
                                  timestamp=datetime.datetime.utcnow())

            journal_id = result.inserted_primary_key[0]

            log.msg("creating the db postings at %f" % (time.time() - start))
            db_postings = []
            for posting in postings:
                contract_table = Contract.__table__
                s = select([contract_table.c.id],
                           contract_table.c.ticker == posting["contract"])
                result = self.execute(s)
                contract_id = result.first()[0]

                user_table = User.__table__
                s = select([user_table.c.type],
                           user_table.c.username == posting["username"])
                result = self.execute(s)
                user_type = result.first()[0]

                username = posting["username"]
                quantity = posting["quantity"]
                direction = posting["direction"]
                note = posting["note"]
                if posting["timestamp"] is not None:
                    timestamp = util.timestamp_to_dt(posting["timestamp"])
                else:
                    timestamp = None

                if direction == 'debit':
                    if user_type == 'Asset':
                        sign = 1
                    else:
                        sign = -1
                else:
                    if user_type == 'Asset':
                        sign = -1
                    else:
                        sign = 1

                posting = {
                    'username': username,
                    'contract_id': contract_id,
                    'quantity': sign * quantity,
                    'note': note,
                    'timestamp': timestamp,
                    'journal_id': journal_id
                }
                db_postings.append(posting)
                log.msg("done making posting at %f: %s" %
                        (time.time() - start, posting))

            ins = Posting.__table__.insert()
            result = self.execute(ins, db_postings)
            log.msg("Inserted %d rows of %d postings" %
                    (result.rowcount, len(db_postings)))
            log.msg("Done committing postings at %f" % (time.time() - start))

            return True

        except Exception, e:
            log.err("Caught exception trying to commit. Postings were:")
            for posting in postings:
                log.err(str(posting))
            log.err("Stack trace follows:")
            log.err()
            if isinstance(e, SQLAlchemyError):
                raise DATABASE_ERROR
            raise e
Example #4
0
    def atomic_commit(self, postings):

        start = time.time()
        log.msg("atomic commit called for %s at %f" % (postings, start))
        try:
            # sanity check
            if len(postings) == 0:
                raise INTERNAL_ERROR


            types = [posting["type"] for posting in postings]
            counts = [posting["count"] for posting in postings]

            if not all(type == types[0] for type in types):
                raise TYPE_MISMATCH
            if not all(count == counts[0] for count in counts):
                raise COUNT_MISMATCH

            # balance check
            debitsum = defaultdict(int)
            creditsum = defaultdict(int)

            log.msg("auditing postings at %f" % (time.time() - start))
            for posting in postings:
                if posting["direction"] == "debit":
                    debitsum[posting["contract"]] += posting["quantity"]
                if posting["direction"] == "credit":
                    creditsum[posting["contract"]] += posting["quantity"]

            for ticker in debitsum:
                if debitsum[ticker] - creditsum[ticker] is not 0:
                    raise QUANTITY_MISMATCH

            # create the journal and postings
            # The journal is created separately from the postings but this is ok because
            # all the postings are created at once. If the posting commit fails then we'll
            # just end up with an empty journal which won't break anything
            # TODO: Create the journal and postings together
            log.msg("creating the journal at %f" % (time.time() - start))
            ins = Journal.__table__.insert()

            result = self.execute(ins, type=types[0], timestamp=datetime.datetime.utcnow())

            journal_id = result.inserted_primary_key[0]

            log.msg("creating the db postings at %f" % (time.time() - start))
            db_postings = []
            for posting in postings:
                contract_table = Contract.__table__
                s = select([contract_table.c.id], contract_table.c.ticker==posting["contract"])
                result = self.execute(s)
                contract_id = result.first()[0]

                user_table = User.__table__
                s = select([user_table.c.type], user_table.c.username==posting["username"])
                result = self.execute(s)
                user_type = result.first()[0]

                username = posting["username"]
                quantity = posting["quantity"]
                direction = posting["direction"]
                note = posting["note"]
                if posting["timestamp"] is not None:
                    timestamp = util.timestamp_to_dt(posting["timestamp"])
                else:
                    timestamp = None

                if direction == 'debit':
                    if user_type == 'Asset':
                        sign = 1
                    else:
                        sign = -1
                else:
                    if user_type == 'Asset':
                        sign = -1
                    else:
                        sign = 1

                posting = {'username': username,
                           'contract_id': contract_id,
                           'quantity': sign * quantity,
                           'note': note,
                           'timestamp': timestamp,
                           'journal_id': journal_id
                }
                db_postings.append(posting)
                log.msg("done making posting at %f: %s" % (time.time() - start, posting))


            ins = Posting.__table__.insert()
            result = self.execute(ins, db_postings)
            log.msg("Inserted %d rows of %d postings" % (result.rowcount, len(db_postings)))
            log.msg("Done committing postings at %f" % (time.time() - start))


            return True

        except Exception, e:
            log.err("Caught exception trying to commit. Postings were:")
            for posting in postings:
                log.err(str(posting))
            log.err("Stack trace follows:")
            log.err()
            if isinstance(e, SQLAlchemyError):
                raise DATABASE_ERROR
            raise e