def test_run_trade(self):
        targets = {
            'XRP': 45,
            'XLM': 45,
            'USDT': 10,
        }
        current = {'XRP': 400, 'XLM': 400, 'USDT': 200}
        rates = {
            'XRP/USDT': 1.0,
            'XLM/USDT': 1.0,
            'XLM/XRP': 1.0,
        }

        exchange = DummyExchange(targets.keys(), current, rates, 0.001)
        portfolio = Portfolio.make_portfolio(targets, exchange)
        balancer = SimpleBalancer()
        executor = Executor(portfolio, exchange, balancer)

        res = executor.run(force=True, trade=True)

        # Test the orders we get are correct
        expected = [
            Order('XLM/USDT', 'BUY', 50, 1.0),
            Order('XRP/USDT', 'BUY', 50, 1.0),
        ]
        self.assertEqual(res['orders'], expected)

        self.assertEqual(exchange.balances['XRP'], 449.95)
        self.assertEqual(exchange.balances['XLM'], 449.95)
        self.assertEqual(exchange.balances['USDT'], 100)
    def test_real2a_max_orders(self):

        targets = {
            'XRP': 40,
            'XLM': 20,
            'BTC': 20,
            'ETH': 10,
            'USDT': 10,
        }
        current = {
            'XRP': 3352,
            'XLM': 0,
            'BTC': 0,
            'ETH': 0,
            'USDT': 243,
        }
        rates = {
            'XRP/USDT': 0.32076,
            'XLM/USDT': 0.09084,
            'XLM/XRP': 0.283366,
            'XRP/BTC': 0.00008102,
            'XRP/ETH': 0.00217366,
            'BTC/USDT': 3968.13,
            'ETH/USDT': 147.81,
        }

        res = self.execute(targets, current, rates, max_orders=3)
        # Test the orders we get are correct
        expected = [
            Order('ETH/USDT', 'BUY', 0.7521902983559976, 147.81),
            Order('XLM/XRP', 'BUY', 2902.218229854689, 0.283366),
            Order('XRP/BTC', 'SELL', 821.9151515151515, 8.102e-05)
        ]
        self.assertEqual(res['orders'], expected)
    def test_real2_nondirect(self):

        targets = {
            'XRP': 40,
            'XLM': 20,
            'BTC': 20,
            'ETH': 10,
            'USDT': 10,
        }
        current = {
            'XRP': 3352,
            'XLM': 0,
            'BTC': 0,
            'ETH': 0,
            'USDT': 243,
        }
        base = "USDT"
        rates = {
            'XRP/USDT': 0.32076,
            'XLM/USDT': 0.09084,
            'XRP/BTC': 0.00008102,
            'XRP/ETH': 0.00217366,
            'BTC/USDT': 3968.13,
            'ETH/USDT': 147.81,
            'USDT/USDT': 1.0,
        }

        res = self.execute(targets, current, rates)
        # Test the orders we get are correct
        expected = [
            Order('XLM/USDT', 'BUY', 1223.9239101717305, 0.09084),
            Order('XRP/BTC', 'SELL', 821.9151515151515, 8.102e-05),
            Order('XRP/ETH', 'SELL', 410.95757575757574, 0.00217366),
        ]

        self.assertEqual(res['orders'], expected)

        # Test that the final amounts are in proportion to the targets
        base_amounts = {}
        for cur in res['proposed_portfolio'].currencies:
            pair = "{}/{}".format(cur, base)
            base_amounts[cur] = \
                res['proposed_portfolio'].balances[cur] * rates[pair]
        total_base = sum(base_amounts.values())

        expected_targets = {
            'XRP': 51.56,
            'XLM': 8.43,
            'BTC': 20.00,
            'ETH': 10.00,
            'USDT': 10.00
        }

        for cur in targets:
            self.assertAlmostEqual(expected_targets[cur],
                                   (base_amounts[cur] / total_base) * 100, 1)
    def test_compareOrders(self):
        a = Order('XRP/USDT', 'BUY', 10, 0.32)
        b = Order('XRP/USDT', 'BUY', 10, 0.32)
        self.assertEqual(a, b)

        c = Order('XRP/BTC', 'SELL', 20, 0.0001)
        self.assertNotEqual(a, c)

        self.assertLess(c, a)
        self.assertGreater(a, c)
    def test_createOrder(self):
        order = Order('XRP/USDT', 'BUY', 10, 0.32)
        self.assertEqual(order.pair, 'XRP/USDT')
        self.assertEqual(order.direction, 'BUY')
        self.assertEqual(order.amount, 10)
        self.assertEqual(order.price, 0.32)

        order = Order('XRP/BTC', 'SELL', 20, 0.0001)
        self.assertEqual(order.pair, 'XRP/BTC')
        self.assertEqual(order.direction, 'SELL')
        self.assertEqual(order.amount, 20)
        self.assertEqual(order.price, 0.0001)
    def test_real2a(self):

        targets = {
            'XRP': 40,
            'XLM': 20,
            'BTC': 20,
            'ETH': 10,
            'USDT': 10,
        }
        current = {
            'XRP': 3402.24,
            'XLM': 6009.74,
            'BTC': 0.14,
            'ETH': 1.82,
            'USDT': 270.82
        }
        base = "USDT"
        rates = {
            'XRP/USDT': 0.32076,
            'XLM/USDT': 0.09084,
            'XLM/XRP': 0.283366,
            'XRP/BTC': 0.00008102,
            'XRP/ETH': 0.00217366,
            'BTC/USDT': 3968.13,
            'ETH/USDT': 147.81,
            'USDT/USDT': 1.0,
        }

        res = self.execute(targets, current, rates)
        # Test the orders we get are correct
        expected = [
            Order('BTC/USDT', 'SELL', 0.0037801180908891593, 3968.13),
            Order('XLM/XRP', 'BUY', 6.551686481727605, 0.283366),
            Order('XRP/BTC', 'SELL', 18.648636987155573, 8.102e-05),
            Order('XRP/ETH', 'SELL', 13.236589350291911, 0.00217366),
            Order('XRP/USDT', 'BUY', 39.15710063598936, 0.32076),
        ]
        self.assertEqual(res['orders'], expected)

        # Test that the final amounts are in proportion to the targets
        base_amounts = {}
        for cur in res['proposed_portfolio'].currencies:
            pair = "{}/{}".format(cur, base)
            base_amounts[cur] = \
                res['proposed_portfolio'].balances[cur] * rates[pair]
        total_base = sum(base_amounts.values())
        for cur in targets:
            self.assertAlmostEqual(targets[cur],
                                   (base_amounts[cur] / total_base) * 100)
    def test_mixed2b(self):

        targets = {
            'XRP': 45,
            'XLM': 45,
            'USDT': 10,
        }
        current = {
            'XRP': 800,
            'XLM': 800,
            'USDT': 200,
        }
        rates = {
            'XRP/USDT': 0.5,
            'XLM/USDT': 0.5,
        }
        res = self.execute(targets, current, rates)
        expected = [
            Order('XLM/USDT', 'BUY', 100, 0.5),
            Order('XRP/USDT', 'BUY', 100, 0.5),
        ]
        self.assertEqual(res['orders'], expected)
    def test_real1a(self):

        targets = {
            'XRP': 45,
            'XLM': 45,
            'USDT': 10,
        }
        current = {
            'XRP': 6703.45,
            'XLM': 0,
            'USDT': 243.31,
        }
        base = "USDT"
        rates = {
            'XRP/USDT': 0.32076,
            'XLM/USDT': 0.09084,
            'XLM/XRP': 0.283366,
            'USDT/USDT': 1.0,
        }

        res = self.execute(targets, current, rates)
        # Test the orders we get are correct
        expected = [
            Order('XLM/USDT', 'BUY', 165.12549537648613, 0.09084),
            Order('XLM/XRP', 'BUY', 11691.753411492733, 0.283366),
            Order('XRP/USDT', 'SELL', 34.42094463150016, 0.32076)
        ]
        self.assertEqual(res['orders'], expected)

        # Test that the final amounts are in proportion to the targets
        base_amounts = {}
        for cur in res['proposed_portfolio'].currencies:
            pair = "{}/{}".format(cur, base)
            base_amounts[cur] = \
                res['proposed_portfolio'].balances[cur] * rates[pair]
        total_base = sum(base_amounts.values())
        for cur in targets:
            self.assertAlmostEqual(targets[cur],
                                   (base_amounts[cur] / total_base) * 100)
    def test_start_all_xrp(self):
        targets = {
            'XRP': 50,
            'XLM': 40,
            'USDT': 10,
        }
        current = {
            'XRP': 1000,
            'XLM': 0,
            'USDT': 0,
        }
        rates = {
            'XRP/USDT': 1.0,
            'XLM/USDT': 1.0,
            'XLM/XRP': 1.0,
        }
        res = self.execute(targets, current, rates)

        expected = [
            Order('XLM/XRP', 'BUY', 400, 1.0),
            Order('XRP/USDT', 'SELL', 100, 1.0),
        ]
        self.assertEqual(res['orders'], expected)
    def test_threshold_over(self):

        targets = {
            'XRP': 45,
            'XLM': 45,
            'USDT': 10,
        }
        current = {'XRP': 400, 'XLM': 400, 'USDT': 200}
        rates = {
            'XRP/USDT': 1.0,
            'XLM/USDT': 1.0,
            'XLM/XRP': 1.0,
        }

        executor = self.create_executor(targets, current, rates)
        res = executor.run(force=False)

        # TEST the orders we get are correct
        expected = [
            Order('XLM/USDT', 'BUY', 50, 1.0),
            Order('XRP/USDT', 'BUY', 50, 1.0),
        ]
        self.assertEqual(res['orders'], expected)
    def test_mixed3a(self):

        targets = {
            'XRP': 45,
            'XLM': 45,
            'USDT': 10,
        }
        current = {
            'XRP': 500,
            'XLM': 500,
            'USDT': 0,
        }
        rates = {
            'XRP/USDT': 1.0,
            'XLM/USDT': 1.0,
        }

        res = self.execute(targets, current, rates)
        expected = [
            Order('XLM/USDT', 'SELL', 50, 1.0),
            Order('XRP/USDT', 'SELL', 50, 1.0),
        ]
        self.assertEqual(res['orders'], expected)
    def test_real2a_cheap(self):

        targets = {
            'XRP': 40,
            'XLM': 20,
            'BTC': 20,
            'ETH': 10,
            'USDT': 10,
        }
        current = {
            'XRP': 3402.24,
            'XLM': 6009.74,
            'BTC': 0.14,
            'ETH': 1.82,
            'USDT': 270.82
        }
        base = "USDT"
        rates = {
            'XRP/USDT': 0.32076,
            'XLM/USDT': 0.09084,
            'XLM/XRP': 0.283366,
            'XRP/BTC': 0.00008102,
            'XRP/ETH': 0.00217366,
            'BTC/USDT': 3968.13,
            'ETH/USDT': 147.81,
            'USDT/USDT': 1.0,
        }

        res = self.execute(targets, current, rates, mode='cheap')
        # Test the orders we get are correct
        expected = [
            Order('XLM/XRP', 'BUY', 6.551686481727605, 0.283366),
            Order('XRP/BTC', 'BUY', 28.11529866566897, 8.102e-05),
            Order('XRP/ETH', 'SELL', 13.236589350292975, 0.00217366)
        ]
        self.assertEqual(res['orders'], expected)
    def test_start_all_usdt_rates(self):

        targets = {
            'XRP': 50,
            'XLM': 40,
            'USDT': 10,
        }
        current = {
            'XRP': 0,
            'XLM': 0,
            'USDT': 1000,
        }
        rates = {
            'XRP/USDT': 0.5,
            'XLM/USDT': 0.5,
        }

        res = self.execute(targets, current, rates)

        expected = [
            Order('XLM/USDT', 'BUY', 800, 0.5),
            Order('XRP/USDT', 'BUY', 1000, 0.5),
        ]
        self.assertEqual(res['orders'], expected)
 def test_HashOrder(self):
     a = Order('XRP/USDT', 'BUY', 10, 0.32)
     b = Order('XRP/USDT', 'BUY', 10, 0.32)
     c = Order('XLM/USDT', 'BUY', 10, 0.32)
     d = Order('XLM/USDT', 'SELL', 10, 0.32)
     e = Order('XLM/USDT', 'SELL', 100, 0.32)
     f = Order('XLM/USDT', 'SELL', 100, 0.1)
     self.assertEqual(hash(a), hash(b))
     self.assertNotEqual(hash(a), hash(f))
     self.assertNotEqual(hash(b), hash(c))
     self.assertNotEqual(hash(c), hash(d))
     self.assertNotEqual(hash(d), hash(e))
     self.assertNotEqual(hash(e), hash(f))
    def test_threshold_under_force(self):

        targets = {
            'XRP': 45,
            'XLM': 45,
            'USDT': 10,
        }
        current = {'XRP': 460, 'XLM': 450, 'USDT': 90}
        rates = {
            'XRP/USDT': 1.0,
            'XLM/USDT': 1.0,
            'XLM/XRP': 1.0,
        }

        executor = self.create_executor(targets, current, rates)
        res = executor.run(force=True)

        # Test the orders we get are correct
        expected = [Order('XRP/USDT', 'SELL', 10, 1.0)]
        self.assertEqual(res['orders'], expected)
    def test_mixed1(self):

        targets = {
            'XRP': 50,
            'XLM': 40,
            'USDT': 10,
        }
        current = {
            'XRP': 100,
            'XLM': 100,
            'USDT': 50,
        }
        rates = {
            'XRP/USDT': 0.1,
            'XLM/USDT': 0.4,
        }
        res = self.execute(targets, current, rates)
        expected = [
            Order('XRP/USDT', 'BUY', 400, 0.1),
        ]
        self.assertEqual(res['orders'], expected)
Exemple #17
0
    def balance(self, initial_portfolio, exchange, max_orders=5, mode='mid'):
        rates = exchange.rates
        quote_currency = initial_portfolio.quote_currency

        # Add in the identify rate just so we don't have to special
        # case it later
        rates["{}/{}".format(quote_currency, quote_currency)] = {
            'mid': 1.0,
            'high': 1.0,
            'low': 1.0,
        }

        todo = [Attempt(initial_portfolio)]
        attempts = []

        while todo:
            attempt = todo.pop()
            diffs = self.permute_differences(
                attempt.portfolio.differences_quote)

            if not diffs or attempt.depth >= max_orders:
                continue

            for pos, neg in diffs:
                p_cur, p_amount = pos
                n_cur, n_amount = neg

                trade_direction = None

                # try and see if we have the pair we need
                # and if so, buy it
                pair = "{}/{}".format(p_cur, n_cur)
                if pair in rates:
                    trade_direction = "BUY"
                    trade_pair = pair

                # if previous failed then reverse paid and try
                # sell instead
                pair = "{}/{}".format(n_cur, p_cur)
                if pair in rates:
                    trade_direction = "SELL"
                    trade_pair = pair

                if not trade_direction:
                    continue

                if trade_pair in attempt.pairs_processed:
                    continue

                # Calculate the min order for this pair
                min_trade_amount_quote = \
                    exchange.limits[trade_pair]['cost']['min']

                # Work out the pair to get to the quote currency
                to_sell_pair_quote = "{}/{}".format(n_cur, quote_currency)
                to_buy_pair_quote = "{}/{}".format(p_cur, quote_currency)

                amounts = [
                    p_amount,
                    -n_amount,
                ]
                if mode == 'mid':
                    amounts.extend(
                        [min_trade_amount_quote, min_trade_amount_quote * 1.5])

                for trade_amount_quote in amounts:

                    # Work out how much of the currency to buy/sell
                    to_sell_amount_cur = \
                        trade_amount_quote / rates[to_sell_pair_quote]['mid']
                    to_buy_amount_cur = \
                        trade_amount_quote / rates[to_buy_pair_quote]['mid']

                    if trade_direction == "BUY":
                        trade_amount = to_buy_amount_cur

                    if trade_direction == "SELL":
                        trade_amount = to_sell_amount_cur

                    # We got a direction, so we know we can either
                    # buy or sell this pair
                    if mode == 'passive':
                        if trade_direction == 'BUY':
                            trade_rate = rates[trade_pair]['low']
                        if trade_direction == 'SELL':
                            trade_rate = rates[trade_pair]['high']
                    else:
                        trade_rate = rates[trade_pair]['mid']

                    order = Order(trade_pair, trade_direction, trade_amount,
                                  trade_rate)

                    order = exchange.preprocess_order(order)
                    if not order:
                        continue

                    # Adjust the amounts of each currency we hold
                    new_portfolio = attempt.portfolio.copy()

                    new_portfolio.balances[p_cur] \
                        += to_buy_amount_cur
                    new_portfolio.balances[n_cur] \
                        -= to_sell_amount_cur

                    if new_portfolio.balances[n_cur] < 0:
                        # gone negative so not valid result
                        break

                    fee = trade_amount_quote * exchange.fee
                    new_attempt = Attempt(
                        new_portfolio, sorted(attempt.orders + [order]),
                        attempt.total_fee + fee, attempt.depth + 1,
                        attempt.pairs_processed | set([trade_pair]))
                    todo.append(new_attempt)

                    if new_attempt.portfolio.balance_rms_error < \
                       initial_portfolio.balance_rms_error:
                        attempts.append(new_attempt)

        if not attempts:
            return {
                'orders': [],
                'total_fee': 0.0,
                'initial_portfolio': initial_portfolio,
                'proposed_portfolio': None
            }

        sort_key = lambda x: (x.portfolio.balance_rms_error, x.total_fee,
                              len(x.orders), x.orders)
        decorated_attempts = [(sort_key(x), x) for x in attempts]
        decorated_attempts.sort(key=lambda x: x[0])
        best_attempt = decorated_attempts[0][1]

        return {
            'orders': sorted(best_attempt.orders),
            'total_fee': best_attempt.total_fee,
            'initial_portfolio': initial_portfolio,
            'proposed_portfolio': best_attempt.portfolio
        }
 def test_execute_buy(self):
     order = Order('XRP/USDT', 'BUY', 10, 0.32)
     self.exchange.execute_order(order)
     self.assertEqual(self.exchange.balances['XRP'], 109.99)
     self.assertEqual(self.exchange.balances['BTC'], 200.0)
     self.assertEqual(self.exchange.balances['USDT'], 296.8)
 def test_execute_sell(self):
     order = Order('BTC/USDT', 'SELL', 0.01, 3500)
     self.exchange.execute_order(order)
     self.assertEqual(self.exchange.balances['XRP'], 100.00)
     self.assertEqual(self.exchange.balances['BTC'], 199.99)
     self.assertEqual(self.exchange.balances['USDT'], 334.965)
 def test_execute_buy_toomuch1(self):
     order = Order('XRP/USDT', 'BUY', 1000, 0.32)
     with self.assertRaises(ValueError):
         self.exchange.execute_order(order)
 def test_execute_sell_toomuch2(self):
     order = Order('BTC/USDT', 'SELL', 1000, 3500)
     with self.assertRaises(ValueError):
         self.exchange.execute_order(order)
 def test_preprocess_order_true(self):
     order = Order('XRP/USDT', 'BUY', 50, 0.32)
     self.assertEqual(self.exchange.preprocess_order(order), order)
 def test_ReprStrOrder(self):
     a = Order('XRP/USDT', 'BUY', 10, 0.32)
     self.assertEqual(str(a), 'BUY 10.0 XRP/USDT @ 0.32')
     self.assertEqual(repr(a), "Order('XRP/USDT', 'BUY', 10.0, 0.32)")
 def test_preprocess_order_false(self):
     order = Order('XRP/USDT', 'BUY', 0.1, 0.32)
     self.assertIsNone(self.exchange.preprocess_order(order))
 def test_preprocess_order_bad(self):
     order = Order('ZEC/USDT', 'BUY', 10, 0.32)
     self.assertIsNone(self.exchange.preprocess_order(order))
 def test_createOrderBadDirection(self):
     with self.assertRaises(ValueError):
         Order('XRP/USDT', 'FOO', 10, 0.001)