Пример #1
0
    def test_basic_crypto_crypto_minimally_profitable(self):
        """
        OB1 has a bid at 600 and OB2 has an ask at 599, both at 1btc. These should cross
        for a volume of 2btc and a profit of $1 USD.
        """
        self.itbit.market_order_fee = Decimal('0.0008')
        self.bitstamp.market_order_fee = Decimal('0.0008')

        result = arbitrage.detect_cross(
            self.basic_ob_2(price_currency='BTC', vol_currency='ETH'),
            self.basic_ob_1(price_currency='BTC', vol_currency='ETH'),
        )

        result.volume.should.equal(Money('1', 'ETH'))
        result.revenue.should.equal(Money('1', 'BTC'))
        result.fees.should.equal(Money('0.9592', 'BTC'))
        result.profit.should.equal(Money('0.0408', 'BTC'))
Пример #2
0
    def __init__(self, session=None, configuration=None):
        # 2018-4-4: This is a hack for an outstanding issue in our environment that
        # causes bitstamp to reject all but the first requests made with a
        # requests.Session() object. See trello for more information.
        if not session:
            session = FuturesSession(max_workers=10)
            session.cookies = ForgetfulCookieJar()

        super(BitstampBTCUSDExchange, self).__init__(session)

        # Immutable properties.
        # TODO: Check on status of the withdrawal_requests_url (might need a dash).
        # TODO: Check if the withdraw_url is still being used or why it isn't in the
        #   v2 API.
        self.name = u'BITSTAMP_BTC_USD'
        self.friendly_name = u'Bitstamp BTC-USD'
        self.base_url = 'https://www.bitstamp.net/api/v2/'
        self.currency = u'USD'
        self.volume_currency = 'BTC'
        self.price_decimal_precision = 2
        self.volume_decimal_precision = 8

        # Configurables defaults.
        self.market_order_fee = self.fee
        self.limit_order_fee = self.fee
        self.fee = Decimal('0.0005')  # TODO: update these.
        self.fiat_balance_tolerance = Money('0.0001', 'USD')
        self.volume_balance_tolerance = Money('0.00000001', 'BTC')
        self.max_tick_speed = 1
        self.min_order_size = Money('0.001', 'BTC')
        self.use_cached_orderbook = False

        if configuration:
            self.configure(configuration)

        # Endpoints.
        self.ticker_url = 'ticker/btcusd/'
        self.orderbook_url = 'order_book/btcusd/'
        self.buy_url = 'buy/btcusd/'
        self.sell_url = 'sell/btcusd/'
        self.open_orders_url = 'open_orders/btcusd/'
        self.trade_status_url = 'user_transactions/btcusd/'
        self.balance_url = 'balance/'
        self.trade_cancel_url = 'cancel_order/'
        self.withdrawl_requests_url = 'withdrawal_requests/'
        self.withdraw_url = 'https://priv-api.bitstamp.net/api/bitcoin_withdrawal/'
Пример #3
0
    def parse_trades(self, resp_obj):
        trades = []

        for trade in resp_obj:
            # No unique id comes back with the trade so we use the milliseconds
            # timestamp.
            trade = {
                'exchange': self.exchange_name,
                'price': Money(trade['rate'], 'CAD'),
                'volume': Money(trade['amount'], 'BTC'),
                'timestamp': int(trade['datetime']) / 1000.0,
                'trade_id': int(trade['datetime']),
            }

            trades.append(trade)

        return trades
Пример #4
0
 def get_open_orders_resp(self, req):
     response = self.resp(req)
     return [{
         'mode':
         self._order_mode_to_const(order['side']),
         'id':
         str(order['orderId']),
         'price':
         Money(order['price'], self.currency),
         'type':
         self.binance_type_to_order_type[order['type']],
         'volume':
         Money(order['origQty'], self.volume_currency),
         'volume_remaining':
         Money(order['origQty'], self.volume_currency) -
         Money(order['executedQty'], self.volume_currency),
     } for order in response]
Пример #5
0
    def test_real_data_unprofitable_flag_off(self):
        """
        Test based on real data taken from itbit and bitstamp at about 4:30 on
        September 26 2016 against math done by hand.
        """
        self.itbit.market_order_fee = Decimal('0.10')
        self.bitstamp.market_order_fee = Decimal('0.10')

        result = arbitrage.detect_cross(
            self.real_orderbook_1(),
            self.real_orderbook_2(),
            ignore_unprofitable=False,
        )

        result.volume.should.equal(Money('12.9087178', 'BTC'))
        result.revenue.should.equal(Money('5.2679425048', 'USD'))
        assert result.profit < Money('0', 'USD')
Пример #6
0
    def get_balance_resp(self, req):
        response, headers = self.resp(req)

        balance = Balance()

        try:
            for account in response:
                if account['currency'] == 'BTC':
                    balance['BTC'] = Money(account['available'], 'BTC')
                elif account['currency'] == self.currency:
                    balance[self.currency] = Money(account['available'],
                                                   self.currency)
        except KeyError:
            raise exceptions.ExchangeAPIErrorException(self,
                                                       'malformed response')

        return balance
Пример #7
0
def check_btc_net_assets(db):
    btc_net_assets_error = assets.calculate_btc_net_assets_error(db)

    if btc_net_assets_error == Money(0, 'BTC'):
        succeed('BTC_NET_ASSETS')
    else:
        detail = 'Error: %s' % btc_net_assets_error
        fail('BTC_NET_ASSETS', detail)
Пример #8
0
    def setUp(self):
        self.order = mock.MagicMock()
        self.order.exchange_rate = Decimal('0.80')
        self.order.fundamental_value = Money('250', 'USD')

        self.trades = []

        self.bid = Trade(
            Consts.BID,
            Money('100', 'USD'),
            Money('0', 'USD'),
            Money('1', 'BTC'),
            '1',
            self.order,
        )

        self.trades.append(self.bid)

        self.ask = Trade(
            Consts.ASK,
            Money('100', 'USD'),
            Money('0', 'USD'),
            Money('1', 'BTC'),
            '2',
            self.order,
        )

        self.trades.append(self.ask)
Пример #9
0
def calculate_outstanding_btc_fees(db):
    raw_trade_btc_fees = db.query(func.sum(Trade._fee))\
        .filter(Trade.has_outstanding_btc_fee)\
        .scalar()

    raw_transaction_btc_fees = db.query(func.sum(Transaction._fee))\
        .filter(Transaction.has_outstanding_btc_fee)\
        .scalar()

    if raw_trade_btc_fees == None:
        raw_trade_btc_fees = 0
    if raw_transaction_btc_fees == None:
        raw_transaction_btc_fees = 0

    trade_btc_fees = Money(raw_trade_btc_fees, 'BTC')
    transaction_btc_fees = Money(raw_transaction_btc_fees, 'BTC')

    return trade_btc_fees + transaction_btc_fees
Пример #10
0
    def _get_midpoint(self, orderbook):
        bid_quote = Exchange.price_quote_from_orderbook(
            orderbook,
            'BID',
            Money(20, 'BTC'),
        )

        bid_price = float(bid_quote['price_for_order'])

        ask_quote = Exchange.price_quote_from_orderbook(
            orderbook,
            'ASK',
            Money(20, 'BTC'),
        )

        ask_price = float(ask_quote['price_for_order'])

        return (bid_price + ask_price) / 2
Пример #11
0
    def get_open_orders_resp(self, req):
        raw_open_orders = self.resp(req)
        raw_open_orders = raw_open_orders if raw_open_orders else []
        open_orders = []

        for raw_order in raw_open_orders:
            mode = self._order_mode_to_const(str(raw_order['type']))

            order = {
                'mode': mode,
                'id': str(raw_order['id']),
                'price': Money(raw_order['price'], 'CAD'),
                'volume_remaining': Money(raw_order['amount'], 'BTC'),
            }

            open_orders.append(order)

        return open_orders
Пример #12
0
 def period(self, step='1d', sid=''):
     from twisted.internet import defer
     points = yield self.req(self.create_url(step))
     if not points:
         defer.returnValue(OrderedDict())
     point_hash = OrderedDict()
     for p in points:
         point_hash[epoch(p[0]).naive] = Money(str(p[7]), 'BTC')
     defer.returnValue(point_hash)
Пример #13
0
def get_all_fees_in_period_by_exchange_in_usd(db, start, end):
    fees_by_exchange = db\
        .query(
            func.sum(Trade.fee_in_usd),
            Order._exchange_name,
        )\
        .join(Order)\
        .filter(Trade.time_created >= start)\
        .filter(Trade.time_created < end)\
        .group_by(Order._exchange_name)\
        .all()

    exchange_fees = defaultdict(lambda: Money(0, 'USD'))

    for fees, exchange_name in fees_by_exchange:
        exchange_fees[exchange_name] = Money(fees, 'USD')

    return exchange_fees
Пример #14
0
    def test_trivial_ask(self):
        mode = Consts.ASK
        initial_price = Money('1002', 'USD')
        orderbook = self.basic_a
        ignore_volume = Money('0.001', 'BTC')
        jump = Money('0.01', 'USD')
        max_slide = None

        new_price = order_sliding.slide_order(
            mode,
            initial_price,
            orderbook,
            ignore_volume,
            jump,
            max_slide,
        )

        new_price.should.equal(Money('1500.99', 'USD'))
Пример #15
0
    def test_do_not_ignore_bid(self):
        mode = Consts.BID
        initial_price = Money('999', 'USD')
        orderbook = self.basic_b
        ignore_volume = Money('0.001', 'BTC')
        jump = Money('0.01', 'USD')
        max_slide = None

        new_price = order_sliding.slide_order(
            mode,
            initial_price,
            orderbook,
            ignore_volume,
            jump,
            max_slide,
        )

        new_price.should.equal(Money('900.01', 'USD'))
Пример #16
0
    def generate_null_data(self):
        open_orders_for_exchange = {}
        orderbooks = defaultdict(lambda x: [])
        levels_for_exchange = {e: [] for e in self.trading_pair_names}

        args = {
            'gds_active': False,
            'levels_for_exchange': levels_for_exchange,
            'selected_exchanges': [''],
            'open_orders_for_exchange': open_orders_for_exchange,
            'high': Money('1', 'USD'),
            'low': Money('0', 'USD'),
            'pair_name': self.pair_name,
            'show_orders': False,
            'at_time': None,
        }

        return args
Пример #17
0
    def test_simple(self):
        configuration = {
            'platform': {
                'emerald': True
            },
            'strategy': {},
            'exchanges': {
                self.exchange_name: {
                    'fiat_balance_tolerance': Money('1', self.price_currency),
                },
            }
        }

        exchange = self.exchange_class(configuration=configuration)

        assert exchange.fiat_balance_tolerance == Money(
            '1', self.price_currency)
        assert exchange.use_cached_orderbook == True
Пример #18
0
    def test_basic_crypto_crypto_unprofitable_with_ignore_flag_off(self):
        """
        OB1 has a bid at 600 and OB2 has an ask at 599, both at 1btc. These should cross
        for a volume of 2btc and a profit of $1 USD.
        """

        self.itbit.market_order_fee = Decimal('0.1')
        self.bitstamp.market_order_fee = Decimal('0.1')

        result = arbitrage.detect_cross(
            self.basic_ob_2(price_currency='BTC', vol_currency='ETH'),
            self.basic_ob_1(price_currency='BTC', vol_currency='ETH'),
            ignore_unprofitable=False,
        )

        result.volume.should.equal(Money('1', 'ETH'))
        result.revenue.should.equal(Money('1', 'BTC'))
        result.fees.should.equal(Money('119.9', 'BTC'))
Пример #19
0
    def more_ob_1(self, price_currency='USD', vol_currency='BTC'):
        ex = self.bitstamp
        volume = Money('1', vol_currency)

        ex.currency = price_currency
        ex.volume_currency = vol_currency

        bids3 = [
            Order(Money('600', price_currency), volume, ex, Consts.BID),
            Order(Money('550', price_currency), volume, ex, Consts.BID),
        ]

        asks3 = [
            Order(Money('601', price_currency), volume, ex, Consts.ASK),
            Order(Money('650', price_currency), volume, ex, Consts.ASK),
        ]

        return {'bids': bids3, 'asks': asks3}
Пример #20
0
    def order_details_resp(self, reqs):
        details_response = self.resp(reqs['details'])
        fee = self.order_fee_resp(reqs['fee'])

        if details_response['orders']:
            raw_order = details_response['orders'][0]
            order = self.parse_raw_order(raw_order, fee)

            return order
        else:
            empty_order = {
                'time_created': None,
                'type': None,
                'btc_total': Money(0, 'BTC'),
                'fiat_total': Money(0, 'USD'),
                'trades': [],
            }

            return empty_order
Пример #21
0
    def balance_resp(self, req):
        response = self.resp(req)

        try:
            balances = response['info']['funds']

            btc_available = Money(balances['free']['btc'], 'BTC')
            usd_available = Money(balances['free']['usd'], 'USD')
        except KeyError:
            raise exceptions.ExchangeAPIErrorException(
                self,
                'Balance missing expected keys',
            )

        balance = Balance()
        balance['BTC'] = btc_available
        balance['USD'] = usd_available

        return balance
Пример #22
0
    def test_standardization_exchanges(self):
        base_config = {
            'platform': {
                'audit': False
            },
            'strategy': {
                'tick_sleep': Decimal('1')
            },
            'coinbase_btc_usd': {
                'fiat_balance_tolerance': Money('0.01', 'USD')
            }
        }

        new_config = config_helper.format_file_config_to_standard(base_config)

        len(new_config['exchanges']).should.equal(1)
        len(new_config['exchanges']['coinbase_btc_usd'].keys()).should.equal(1)
        new_config['exchanges']['coinbase_btc_usd']['fiat_balance_tolerance']\
            .should.equal(Money('0.01', 'USD'))
Пример #23
0
def all_fees(trades, price_currency=None, volume_currency='BTC'):
    """
    Return the fiat equivalent fees for a list of trades and the exact BTC amount of any
    BTC fees (their USD value is still included in the total).
    """

    if not price_currency:
        price_currency = price_currency_for_trades(trades)

    all_fees = Money('0', price_currency)
    all_volume_currency_fees = Money('0', volume_currency)

    for t in trades:
        if t.fee.currency == volume_currency:
            all_volume_currency_fees += t.fee

        all_fees += t.fee_in_currency(price_currency)

    return all_fees, all_volume_currency_fees
Пример #24
0
    def test_real_data_directional(self):
        """
        Test that the directional calculations work as expected against real data.
        """

        result1 = arbitrage.detect_directional_cross(
            self.real_orderbook_1(),
            self.real_orderbook_2(),
        )

        result1.volume.should.equal(Money('12.9087178', 'BTC'))
        result1.revenue.should.equal(Money('5.2679425048', 'USD'))

        result2 = arbitrage.detect_directional_cross(
            self.real_orderbook_2(),
            self.real_orderbook_1(),
        )

        result2.should.equal(None)
Пример #25
0
    def get_order_details_resp(self, req):
        response = self.resp(req)

        volume_amount = Money(response['origQty'], self.volume_currency)
        executed_volume_amount = Money(response['executedQty'],
                                       self.volume_currency)
        price = Money(response['price'], self.currency)
        # Cannot multiple Money * Money, we need to know the unit, so we use the currency unit and a Decimal
        price_currency_amount = price * Decimal(response['executedQty'])
        result = {
            'id': str(response['orderId']),
            'time_created': int(response['time']),
            'mode': self._order_mode_to_const(response['side']),
            'type': self.binance_type_to_order_type[response['type']],
            self.volume_currency.lower() + '_total': volume_amount,
            self.currency.lower() + '_total': price_currency_amount,
            # 'trades': [], # Binance does not split orders into trades, just partial fill quantities
        }
        return result
Пример #26
0
    def basic_ob_1(self, price_currency='USD', vol_currency='BTC'):
        ex = self.itbit

        ex.currency = price_currency
        ex.volume_currency = vol_currency

        volume = Money('1', vol_currency)

        bids2 = [
            Order(Money('598', price_currency), volume, ex, Consts.BID),
            Order(Money('550', price_currency), volume, ex, Consts.BID),
        ]

        asks2 = [
            Order(Money('599', price_currency), volume, ex, Consts.ASK),
            Order(Money('650', price_currency), volume, ex, Consts.ASK),
        ]

        return {'bids': bids2, 'asks': asks2}
Пример #27
0
    def test_creation_with_details(self):
        l = Liability(
                Money('100', 'ETH'),
                Liability.FIXED_INTEREST,
                'John',
                time_started=parse('2016-10-1').datetime,
                details={'interest_rate': 0.05},
        )

        l.details['interest_rate'].should.equal(0.05)
Пример #28
0
    def fiat_withdrawal_fee(self, withdrawal_amount):
        """
        Itbit fee is from their documentation, and an extra $15 is being charged to us
        before it shows up in our bank account (as of the September 2016), so I assume
        that's an intermediary fee.

        The fee should be a flat $50 on withdrawals > $10k, but we'll see.
        """
        fee = Money('0', 'USD')

        if withdrawal_amount < Money('10,000', 'USD'):
            itbit_fee = Money('15', 'USD')
            intermediary_fee = Money('15', 'USD')

            fee = itbit_fee + intermediary_fee
        else:
            fee = Money('50', 'USD')

        return fee
Пример #29
0
 def __init__(self, session=None, currency=u"CAD", use_cached_orderbook=False):
     super(VaultOfSatoshiExchange, self).__init__(session)
     self.name = u'VAULTOFSATOSHI'
     self.friendly_name = u'Vault of Satoshi'
     self.base_url = 'https://api.vaultofsatoshi.com'
     self.currency = currency
     self.fee = Decimal("0") # assuming $99 unlimited account
     self.withdrawal_fee = Money("0.0005", "BTC")
     self.bid_string = "bid"
     self.ask_string = "ask"
     self.use_cached_orderbook = use_cached_orderbook
Пример #30
0
    def order_fee_resp(self, req):
        try:
            response = self.resp(req)
        except CancelOrderNotFoundError:
            # OkCoin has started returning this error on non-executed orders or orders
            # with no fees.
            return Money('0', 'USD')

        # Okcoin returns fees as negative values.
        fee_amount = abs(Decimal(response['data']['fee']))
        fee_currency = 'USD'

        # If the order has not been filled, response['data']['type] is not
        # in the response.
        if 'type' in response['data']:
            fee_currency = response['data']['type'].upper()

        fee = Money(fee_amount, fee_currency)

        return fee