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)
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)