X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=test.py;h=26646be24d8b87a75401e00fc3b5c150946aa41a;hb=2308a1c4c0d2514dc9fc9acf235f9e5a33299f63;hp=aae1dc85af02245f88ddd28d32a4fac9ecfe1cef;hpb=6ca5a1ec669593fa915a2824efca068c975f9caa;p=perso%2FImmae%2FProjets%2FCryptomonnaies%2FCryptoportfolio%2FTrader.git diff --git a/test.py b/test.py index aae1dc8..26646be 100644 --- a/test.py +++ b/test.py @@ -1,3 +1,4 @@ +import sys import portfolio import unittest from decimal import Decimal as D @@ -7,6 +8,16 @@ import requests_mock from io import StringIO import helper +limits = ["acceptance", "unit"] +for test_type in limits: + if "--no{}".format(test_type) in sys.argv: + sys.argv.remove("--no{}".format(test_type)) + limits.remove(test_type) + if "--only{}".format(test_type) in sys.argv: + sys.argv.remove("--only{}".format(test_type)) + limits = [test_type] + break + class WebMockTestCase(unittest.TestCase): import time @@ -32,13 +43,13 @@ class WebMockTestCase(unittest.TestCase): for patcher in self.patchers: patcher.start() - def tearDown(self): for patcher in self.patchers: patcher.stop() self.wm.stop() super(WebMockTestCase, self).tearDown() +@unittest.skipUnless("unit" in limits, "Unit skipped") class PortfolioTest(WebMockTestCase): def fill_data(self): if self.json_response is not None: @@ -140,6 +151,7 @@ class PortfolioTest(WebMockTestCase): self.assertEqual(expected_medium, portfolio.Portfolio.repartition(liquidity="medium")) self.assertEqual(expected_high, portfolio.Portfolio.repartition(liquidity="high")) +@unittest.skipUnless("unit" in limits, "Unit skipped") class AmountTest(WebMockTestCase): def test_values(self): amount = portfolio.Amount("BTC", "0.65") @@ -250,6 +262,9 @@ class AmountTest(WebMockTestCase): self.assertEqual(D("5.5"), (amount / 2).value) self.assertEqual(D("4.4"), (amount / D("2.5")).value) + with self.assertRaises(Exception): + amount / amount + def test__truediv(self): amount = portfolio.Amount("XEM", 11) @@ -363,6 +378,7 @@ class AmountTest(WebMockTestCase): amount2.linked_to = amount3 self.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT -> Amount(0.10000000 BTC)))", repr(amount1)) +@unittest.skipUnless("unit" in limits, "Unit skipped") class BalanceTest(WebMockTestCase): def test_values(self): balance = portfolio.Balance("BTC", { @@ -405,16 +421,27 @@ class BalanceTest(WebMockTestCase): "exchange_used": 1, "exchange_free": 2 }) self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX + ❌1.00000000 BTX = 3.00000000 BTX])", repr(balance)) + balance = portfolio.Balance("BTX", { "exchange_total": 1, "exchange_used": 1}) + self.assertEqual("Balance(BTX Exch: [❌1.00000000 BTX])", repr(balance)) + balance = portfolio.Balance("BTX", { "margin_total": 3, "margin_borrowed": 1, "margin_free": 2 }) self.assertEqual("Balance(BTX Margin: [✔2.00000000 BTX + borrowed 1.00000000 BTX = 3.00000000 BTX])", repr(balance)) + balance = portfolio.Balance("BTX", { "margin_total": 2, "margin_free": 2 }) + self.assertEqual("Balance(BTX Margin: [✔2.00000000 BTX])", repr(balance)) + balance = portfolio.Balance("BTX", { "margin_total": -3, "margin_borrowed_base_price": D("0.1"), "margin_borrowed_base_currency": "BTC", "margin_lending_fees": D("0.002") }) self.assertEqual("Balance(BTX Margin: [-3.00000000 BTX @@ 0.10000000 BTC/0.00200000 BTC])", repr(balance)) + balance = portfolio.Balance("BTX", { "margin_total": 1, + "margin_borrowed": 1, "exchange_free": 2, "exchange_total": 2}) + self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX] Margin: [borrowed 1.00000000 BTX] Total: [0.00000000 BTX])", repr(balance)) + +@unittest.skipUnless("unit" in limits, "Unit skipped") class HelperTest(WebMockTestCase): def test_get_ticker(self): market = mock.Mock() @@ -612,15 +639,345 @@ class HelperTest(WebMockTestCase): self.assertEqual(D("0.01"), call[0][0]["XVG"].value) self.assertEqual(D("1.01"), call[0][1]["BTC"].value) - @unittest.skip("TODO") - def test_follow_orders(self): - pass - - + @mock.patch.object(portfolio.time, "sleep") + @mock.patch.object(portfolio.TradeStore, "all_orders") + def test_follow_orders(self, all_orders, time_mock): + for verbose, debug, sleep in [ + (True, False, None), (False, False, None), + (True, True, None), (True, False, 12), + (True, True, 12)]: + with self.subTest(sleep=sleep, debug=debug, verbose=verbose), \ + mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: + portfolio.TradeStore.debug = debug + order_mock1 = mock.Mock() + order_mock2 = mock.Mock() + order_mock3 = mock.Mock() + all_orders.side_effect = [ + [order_mock1, order_mock2], + [order_mock1, order_mock2], + + [order_mock1, order_mock3], + [order_mock1, order_mock3], + + [order_mock1, order_mock3], + [order_mock1, order_mock3], + + [] + ] + + order_mock1.get_status.side_effect = ["open", "open", "closed"] + order_mock2.get_status.side_effect = ["open"] + order_mock3.get_status.side_effect = ["open", "closed"] + + order_mock1.trade = mock.Mock() + order_mock2.trade = mock.Mock() + order_mock3.trade = mock.Mock() + + helper.follow_orders(verbose=verbose, sleep=sleep) + + order_mock1.trade.update_order.assert_any_call(order_mock1, 1) + order_mock1.trade.update_order.assert_any_call(order_mock1, 2) + self.assertEqual(2, order_mock1.trade.update_order.call_count) + self.assertEqual(3, order_mock1.get_status.call_count) + + order_mock2.trade.update_order.assert_any_call(order_mock2, 1) + self.assertEqual(1, order_mock2.trade.update_order.call_count) + self.assertEqual(1, order_mock2.get_status.call_count) + + order_mock3.trade.update_order.assert_any_call(order_mock3, 2) + self.assertEqual(1, order_mock3.trade.update_order.call_count) + self.assertEqual(2, order_mock3.get_status.call_count) + + if sleep is None: + if debug: + time_mock.assert_called_with(7) + else: + time_mock.assert_called_with(30) + else: + time_mock.assert_called_with(sleep) + + if verbose: + self.assertNotEqual("", stdout_mock.getvalue()) + else: + self.assertEqual("", stdout_mock.getvalue()) + + @mock.patch.object(portfolio.BalanceStore, "fetch_balances") + def test_move_balance(self, fetch_balances): + for debug in [True, False]: + with self.subTest(debug=debug),\ + mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: + value_from = portfolio.Amount("BTC", "1.0") + value_from.linked_to = portfolio.Amount("ETH", "10.0") + value_to = portfolio.Amount("BTC", "10.0") + trade1 = portfolio.Trade(value_from, value_to, "ETH") + + value_from = portfolio.Amount("BTC", "0.0") + value_from.linked_to = portfolio.Amount("ETH", "0.0") + value_to = portfolio.Amount("BTC", "-3.0") + trade2 = portfolio.Trade(value_from, value_to, "ETH") + + value_from = portfolio.Amount("USDT", "0.0") + value_from.linked_to = portfolio.Amount("XVG", "0.0") + value_to = portfolio.Amount("USDT", "-50.0") + trade3 = portfolio.Trade(value_from, value_to, "XVG") + + portfolio.TradeStore.all = [trade1, trade2, trade3] + balance1 = portfolio.Balance("BTC", { "margin_free": "0" }) + balance2 = portfolio.Balance("USDT", { "margin_free": "100" }) + portfolio.BalanceStore.all = {"BTC": balance1, "USDT": balance2} + + market = mock.Mock() + + helper.move_balances(market, debug=debug) + + fetch_balances.assert_called_with(market) + if debug: + self.assertRegex(stdout_mock.getvalue(), "market.transfer_balance") + else: + market.transfer_balance.assert_any_call("BTC", 3, "exchange", "margin") + market.transfer_balance.assert_any_call("USDT", 50, "margin", "exchange") + + @mock.patch.object(helper, "prepare_trades") + @mock.patch.object(portfolio.TradeStore, "prepare_orders") + @mock.patch.object(portfolio.TradeStore, "print_all_with_order") + @mock.patch('sys.stdout', new_callable=StringIO) + def test_print_orders(self, stdout_mock, print_all_with_order, prepare_orders, prepare_trades): + market = mock.Mock() + portfolio.BalanceStore.all = { + "BTC": portfolio.Balance("BTC", { + "total": "0.65", + "exchange_total":"0.65", + "exchange_free": "0.35", + "exchange_used": "0.30"}), + "ETH": portfolio.Balance("ETH", { + "total": 3, + "exchange_total": 3, + "exchange_free": 3, + "exchange_used": 0}), + } + helper.print_orders(market) + prepare_trades.assert_called_with(market, base_currency="BTC", + compute_value="average", debug=True) + prepare_orders.assert_called_with(compute_value="average") + print_all_with_order.assert_called() + self.assertRegex(stdout_mock.getvalue(), "Balance") + + @mock.patch.object(helper, "prepare_trades") + @mock.patch.object(helper, "follow_orders") + @mock.patch.object(portfolio.TradeStore, "prepare_orders") + @mock.patch.object(portfolio.TradeStore, "print_all_with_order") + @mock.patch.object(portfolio.TradeStore, "run_orders") + @mock.patch('sys.stdout', new_callable=StringIO) + def test_process_sell_needed__1_sell(self, stdout_mock, run_orders, + print_all_with_order, prepare_orders, follow_orders, + prepare_trades): + market = mock.Mock() + portfolio.BalanceStore.all = { + "BTC": portfolio.Balance("BTC", { + "total": "0.65", + "exchange_total":"0.65", + "exchange_free": "0.35", + "exchange_used": "0.30"}), + "ETH": portfolio.Balance("ETH", { + "total": 3, + "exchange_total": 3, + "exchange_free": 3, + "exchange_used": 0}), + } + helper.process_sell_needed__1_sell(market) + prepare_trades.assert_called_with(market, base_currency="BTC", + debug=False) + prepare_orders.assert_called_with(compute_value="average", + only="dispose") + print_all_with_order.assert_called() + run_orders.assert_called() + follow_orders.assert_called() + self.assertRegex(stdout_mock.getvalue(), "Balance") + + @mock.patch.object(helper, "update_trades") + @mock.patch.object(helper, "follow_orders") + @mock.patch.object(helper, "move_balances") + @mock.patch.object(portfolio.TradeStore, "prepare_orders") + @mock.patch.object(portfolio.TradeStore, "print_all_with_order") + @mock.patch.object(portfolio.TradeStore, "run_orders") + @mock.patch('sys.stdout', new_callable=StringIO) + def test_process_sell_needed__2_buy(self, stdout_mock, run_orders, + print_all_with_order, prepare_orders, move_balances, + follow_orders, update_trades): + market = mock.Mock() + portfolio.BalanceStore.all = { + "BTC": portfolio.Balance("BTC", { + "total": "0.65", + "exchange_total":"0.65", + "exchange_free": "0.35", + "exchange_used": "0.30"}), + "ETH": portfolio.Balance("ETH", { + "total": 3, + "exchange_total": 3, + "exchange_free": 3, + "exchange_used": 0}), + } + helper.process_sell_needed__2_buy(market) + update_trades.assert_called_with(market, base_currency="BTC", + debug=False, only="acquire") + prepare_orders.assert_called_with(compute_value="average", + only="acquire") + print_all_with_order.assert_called() + move_balances.assert_called_with(market, debug=False) + run_orders.assert_called() + follow_orders.assert_called() + self.assertRegex(stdout_mock.getvalue(), "Balance") + + @mock.patch.object(helper, "prepare_trades_to_sell_all") + @mock.patch.object(helper, "follow_orders") + @mock.patch.object(portfolio.TradeStore, "prepare_orders") + @mock.patch.object(portfolio.TradeStore, "print_all_with_order") + @mock.patch.object(portfolio.TradeStore, "run_orders") + @mock.patch('sys.stdout', new_callable=StringIO) + def test_process_sell_all__1_sell(self, stdout_mock, run_orders, + print_all_with_order, prepare_orders, follow_orders, + prepare_trades_to_sell_all): + market = mock.Mock() + portfolio.BalanceStore.all = { + "BTC": portfolio.Balance("BTC", { + "total": "0.65", + "exchange_total":"0.65", + "exchange_free": "0.35", + "exchange_used": "0.30"}), + "ETH": portfolio.Balance("ETH", { + "total": 3, + "exchange_total": 3, + "exchange_free": 3, + "exchange_used": 0}), + } + helper.process_sell_all__1_all_sell(market) + prepare_trades_to_sell_all.assert_called_with(market, base_currency="BTC", + debug=False) + prepare_orders.assert_called_with(compute_value="average") + print_all_with_order.assert_called() + run_orders.assert_called() + follow_orders.assert_called() + self.assertRegex(stdout_mock.getvalue(), "Balance") + + @mock.patch.object(helper, "prepare_trades") + @mock.patch.object(helper, "follow_orders") + @mock.patch.object(helper, "move_balances") + @mock.patch.object(portfolio.TradeStore, "prepare_orders") + @mock.patch.object(portfolio.TradeStore, "print_all_with_order") + @mock.patch.object(portfolio.TradeStore, "run_orders") + @mock.patch('sys.stdout', new_callable=StringIO) + def test_process_sell_all__2_all_buy(self, stdout_mock, run_orders, + print_all_with_order, prepare_orders, move_balances, + follow_orders, prepare_trades): + market = mock.Mock() + portfolio.BalanceStore.all = { + "BTC": portfolio.Balance("BTC", { + "total": "0.65", + "exchange_total":"0.65", + "exchange_free": "0.35", + "exchange_used": "0.30"}), + "ETH": portfolio.Balance("ETH", { + "total": 3, + "exchange_total": 3, + "exchange_free": 3, + "exchange_used": 0}), + } + helper.process_sell_all__2_all_buy(market) + prepare_trades.assert_called_with(market, base_currency="BTC", + debug=False) + prepare_orders.assert_called_with() + print_all_with_order.assert_called() + move_balances.assert_called_with(market, debug=False) + run_orders.assert_called() + follow_orders.assert_called() + self.assertRegex(stdout_mock.getvalue(), "Balance") + + +@unittest.skipUnless("unit" in limits, "Unit skipped") class TradeStoreTest(WebMockTestCase): - @unittest.skip("TODO") - def test_compute_trades(self): - pass + @mock.patch.object(portfolio.BalanceStore, "currencies") + @mock.patch.object(portfolio.TradeStore, "add_trade_if_matching") + def test_compute_trades(self, add_trade_if_matching, currencies): + currencies.return_value = ["XMR", "DASH", "XVG", "BTC", "ETH"] + + values_in_base = { + "XMR": portfolio.Amount("BTC", D("0.9")), + "DASH": portfolio.Amount("BTC", D("0.4")), + "XVG": portfolio.Amount("BTC", D("-0.5")), + "BTC": portfolio.Amount("BTC", D("0.5")), + } + new_repartition = { + "DASH": portfolio.Amount("BTC", D("0.5")), + "XVG": portfolio.Amount("BTC", D("0.1")), + "BTC": portfolio.Amount("BTC", D("0.4")), + "ETH": portfolio.Amount("BTC", D("0.3")), + } + + portfolio.TradeStore.compute_trades(values_in_base, + new_repartition, only="only", market="market") + + self.assertEqual(5, add_trade_if_matching.call_count) + add_trade_if_matching.assert_any_call( + portfolio.Amount("BTC", D("0.9")), + portfolio.Amount("BTC", 0), + "XMR", only="only", market="market" + ) + add_trade_if_matching.assert_any_call( + portfolio.Amount("BTC", D("0.4")), + portfolio.Amount("BTC", D("0.5")), + "DASH", only="only", market="market" + ) + add_trade_if_matching.assert_any_call( + portfolio.Amount("BTC", D("-0.5")), + portfolio.Amount("BTC", D("0")), + "XVG", only="only", market="market" + ) + add_trade_if_matching.assert_any_call( + portfolio.Amount("BTC", D("0")), + portfolio.Amount("BTC", D("0.1")), + "XVG", only="only", market="market" + ) + add_trade_if_matching.assert_any_call( + portfolio.Amount("BTC", D("0")), + portfolio.Amount("BTC", D("0.3")), + "ETH", only="only", market="market" + ) + + def test_add_trade_if_matching(self): + result = portfolio.TradeStore.add_trade_if_matching( + portfolio.Amount("BTC", D("0")), + portfolio.Amount("BTC", D("0.3")), + "ETH", only="nope", market="market" + ) + self.assertEqual(0, len(portfolio.TradeStore.all)) + self.assertEqual(False, result) + + portfolio.TradeStore.all = [] + result = portfolio.TradeStore.add_trade_if_matching( + portfolio.Amount("BTC", D("0")), + portfolio.Amount("BTC", D("0.3")), + "ETH", only=None, market="market" + ) + self.assertEqual(1, len(portfolio.TradeStore.all)) + self.assertEqual(True, result) + + portfolio.TradeStore.all = [] + result = portfolio.TradeStore.add_trade_if_matching( + portfolio.Amount("BTC", D("0")), + portfolio.Amount("BTC", D("0.3")), + "ETH", only="acquire", market="market" + ) + self.assertEqual(1, len(portfolio.TradeStore.all)) + self.assertEqual(True, result) + + portfolio.TradeStore.all = [] + result = portfolio.TradeStore.add_trade_if_matching( + portfolio.Amount("BTC", D("0")), + portfolio.Amount("BTC", D("0.3")), + "ETH", only="dispose", market="market" + ) + self.assertEqual(0, len(portfolio.TradeStore.all)) + self.assertEqual(False, result) def test_prepare_orders(self): trade_mock1 = mock.Mock() @@ -709,7 +1066,7 @@ class TradeStoreTest(WebMockTestCase): order_mock2.get_status.assert_called() order_mock3.get_status.assert_called() - +@unittest.skipUnless("unit" in limits, "Unit skipped") class BalanceStoreTest(WebMockTestCase): def setUp(self): super(BalanceStoreTest, self).setUp() @@ -802,12 +1159,14 @@ class BalanceStoreTest(WebMockTestCase): repartition.return_value = { "XEM": (D("0.75"), "long"), "BTC": (D("0.26"), "long"), + "DASH": (D("0.10"), "short"), } - amounts = portfolio.BalanceStore.dispatch_assets(portfolio.Amount("BTC", "10.1")) + amounts = portfolio.BalanceStore.dispatch_assets(portfolio.Amount("BTC", "11.1")) self.assertIn("XEM", portfolio.BalanceStore.currencies()) self.assertEqual(D("2.6"), amounts["BTC"].value) self.assertEqual(D("7.5"), amounts["XEM"].value) + self.assertEqual(D("-1.0"), amounts["DASH"].value) def test_currencies(self): portfolio.BalanceStore.all = { @@ -824,6 +1183,7 @@ class BalanceStoreTest(WebMockTestCase): } self.assertListEqual(["BTC", "ETH"], list(portfolio.BalanceStore.currencies())) +@unittest.skipUnless("unit" in limits, "Unit skipped") class ComputationTest(WebMockTestCase): def test_compute_value(self): compute = mock.Mock() @@ -848,6 +1208,7 @@ class ComputationTest(WebMockTestCase): compute.assert_called_with("foo", "bid") +@unittest.skipUnless("unit" in limits, "Unit skipped") class TradeTest(WebMockTestCase): def test_values_assertion(self): @@ -881,7 +1242,7 @@ class TradeTest(WebMockTestCase): value_from = portfolio.Amount("BTC", "1.0") value_from.linked_to = portfolio.Amount("BTC", "1.0") - value_to = portfolio.Amount("BTC", "1.0") + value_to = portfolio.Amount("BTC", "2.0") trade = portfolio.Trade(value_from, value_to, "BTC") self.assertIsNone(trade.action) @@ -898,7 +1259,7 @@ class TradeTest(WebMockTestCase): value_to = portfolio.Amount("BTC", "-1.0") trade = portfolio.Trade(value_from, value_to, "ETH") - self.assertEqual("dispose", trade.action) + self.assertEqual("acquire", trade.action) def test_order_action(self): value_from = portfolio.Amount("BTC", "0.5") @@ -939,22 +1300,243 @@ class TradeTest(WebMockTestCase): trade = portfolio.Trade(value_from, value_to, "ETH") order1 = mock.Mock() - order1.filled_amount = portfolio.Amount("ETH", "0.3") + order1.filled_amount.return_value = portfolio.Amount("ETH", "0.3") order2 = mock.Mock() - order2.filled_amount = portfolio.Amount("ETH", "0.01") + order2.filled_amount.return_value = portfolio.Amount("ETH", "0.01") trade.orders.append(order1) trade.orders.append(order2) - self.assertEqual(portfolio.Amount("ETH", "0.31"), trade.filled_amount) + self.assertEqual(portfolio.Amount("ETH", "0.31"), trade.filled_amount()) + order1.filled_amount.assert_called_with(in_base_currency=False) + order2.filled_amount.assert_called_with(in_base_currency=False) - @unittest.skip("TODO") - def test_prepare_order(self): - pass + self.assertEqual(portfolio.Amount("ETH", "0.31"), trade.filled_amount(in_base_currency=False)) + order1.filled_amount.assert_called_with(in_base_currency=False) + order2.filled_amount.assert_called_with(in_base_currency=False) + + self.assertEqual(portfolio.Amount("ETH", "0.31"), trade.filled_amount(in_base_currency=True)) + order1.filled_amount.assert_called_with(in_base_currency=True) + order2.filled_amount.assert_called_with(in_base_currency=True) + + @mock.patch.object(helper, "get_ticker") + @mock.patch.object(portfolio.Computation, "compute_value") + @mock.patch.object(portfolio.Trade, "filled_amount") + @mock.patch.object(portfolio, "Order") + def test_prepare_order(self, Order, filled_amount, compute_value, get_ticker): + Order.return_value = "Order" + + with self.subTest(desc="Nothing to do"): + value_from = portfolio.Amount("BTC", "10") + value_from.rate = D("0.1") + value_from.linked_to = portfolio.Amount("FOO", "100") + value_to = portfolio.Amount("BTC", "10") + trade = portfolio.Trade(value_from, value_to, "FOO", market="market") + + trade.prepare_order() + + filled_amount.assert_not_called() + compute_value.assert_not_called() + self.assertEqual(0, len(trade.orders)) + Order.assert_not_called() + + get_ticker.return_value = { "inverted": False } + with self.subTest(desc="Already filled"), mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: + filled_amount.return_value = portfolio.Amount("FOO", "100") + compute_value.return_value = D("0.125") + + value_from = portfolio.Amount("BTC", "10") + value_from.rate = D("0.1") + value_from.linked_to = portfolio.Amount("FOO", "100") + value_to = portfolio.Amount("BTC", "0") + trade = portfolio.Trade(value_from, value_to, "FOO", market="market") + + trade.prepare_order() + + filled_amount.assert_called_with(in_base_currency=False) + compute_value.assert_called_with(get_ticker.return_value, "sell", compute_value="default") + self.assertEqual(0, len(trade.orders)) + self.assertRegex(stdout_mock.getvalue(), "Less to do than already filled: ") + Order.assert_not_called() + + with self.subTest(action="dispose", inverted=False): + filled_amount.return_value = portfolio.Amount("FOO", "60") + compute_value.return_value = D("0.125") + + value_from = portfolio.Amount("BTC", "10") + value_from.rate = D("0.1") + value_from.linked_to = portfolio.Amount("FOO", "100") + value_to = portfolio.Amount("BTC", "1") + trade = portfolio.Trade(value_from, value_to, "FOO", market="market") + + trade.prepare_order() + + filled_amount.assert_called_with(in_base_currency=False) + compute_value.assert_called_with(get_ticker.return_value, "sell", compute_value="default") + self.assertEqual(1, len(trade.orders)) + Order.assert_called_with("sell", portfolio.Amount("FOO", 30), + D("0.125"), "BTC", "long", "market", + trade, close_if_possible=False) + + with self.subTest(action="acquire", inverted=False): + filled_amount.return_value = portfolio.Amount("BTC", "3") + compute_value.return_value = D("0.125") + + value_from = portfolio.Amount("BTC", "1") + value_from.rate = D("0.1") + value_from.linked_to = portfolio.Amount("FOO", "10") + value_to = portfolio.Amount("BTC", "10") + trade = portfolio.Trade(value_from, value_to, "FOO", market="market") + + trade.prepare_order() + + filled_amount.assert_called_with(in_base_currency=True) + compute_value.assert_called_with(get_ticker.return_value, "buy", compute_value="default") + self.assertEqual(1, len(trade.orders)) + + Order.assert_called_with("buy", portfolio.Amount("FOO", 48), + D("0.125"), "BTC", "long", "market", + trade, close_if_possible=False) + + with self.subTest(close_if_possible=True): + filled_amount.return_value = portfolio.Amount("FOO", "0") + compute_value.return_value = D("0.125") + + value_from = portfolio.Amount("BTC", "10") + value_from.rate = D("0.1") + value_from.linked_to = portfolio.Amount("FOO", "100") + value_to = portfolio.Amount("BTC", "0") + trade = portfolio.Trade(value_from, value_to, "FOO", market="market") + + trade.prepare_order() + + filled_amount.assert_called_with(in_base_currency=False) + compute_value.assert_called_with(get_ticker.return_value, "sell", compute_value="default") + self.assertEqual(1, len(trade.orders)) + Order.assert_called_with("sell", portfolio.Amount("FOO", 100), + D("0.125"), "BTC", "long", "market", + trade, close_if_possible=True) + + get_ticker.return_value = { "inverted": True, "original": {} } + with self.subTest(action="dispose", inverted=True): + filled_amount.return_value = portfolio.Amount("FOO", "300") + compute_value.return_value = D("125") + + value_from = portfolio.Amount("BTC", "10") + value_from.rate = D("0.01") + value_from.linked_to = portfolio.Amount("FOO", "1000") + value_to = portfolio.Amount("BTC", "1") + trade = portfolio.Trade(value_from, value_to, "FOO", market="market") + + trade.prepare_order(compute_value="foo") + + filled_amount.assert_called_with(in_base_currency=True) + compute_value.assert_called_with(get_ticker.return_value["original"], "buy", compute_value="foo") + self.assertEqual(1, len(trade.orders)) + Order.assert_called_with("buy", portfolio.Amount("BTC", D("4.8")), + D("125"), "FOO", "long", "market", + trade, close_if_possible=False) + + with self.subTest(action="acquire", inverted=True): + filled_amount.return_value = portfolio.Amount("BTC", "4") + compute_value.return_value = D("125") + + value_from = portfolio.Amount("BTC", "1") + value_from.rate = D("0.01") + value_from.linked_to = portfolio.Amount("FOO", "100") + value_to = portfolio.Amount("BTC", "10") + trade = portfolio.Trade(value_from, value_to, "FOO", market="market") + + trade.prepare_order(compute_value="foo") + + filled_amount.assert_called_with(in_base_currency=False) + compute_value.assert_called_with(get_ticker.return_value["original"], "sell", compute_value="foo") + self.assertEqual(1, len(trade.orders)) + Order.assert_called_with("sell", portfolio.Amount("BTC", D("5")), + D("125"), "FOO", "long", "market", + trade, close_if_possible=False) + + + @mock.patch.object(portfolio.Trade, "prepare_order") + def test_update_order(self, prepare_order): + order_mock = mock.Mock() + new_order_mock = mock.Mock() + + value_from = portfolio.Amount("BTC", "0.5") + value_from.linked_to = portfolio.Amount("ETH", "10.0") + value_to = portfolio.Amount("BTC", "1.0") + trade = portfolio.Trade(value_from, value_to, "ETH") + prepare_order.return_value = new_order_mock + + for i in [0, 1, 3, 4, 6]: + with self.subTest(tick=i), mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: + trade.update_order(order_mock, i) + order_mock.cancel.assert_not_called() + new_order_mock.run.assert_not_called() + self.assertRegex(stdout_mock.getvalue(), "tick {}, waiting".format(i)) + + order_mock.reset_mock() + new_order_mock.reset_mock() + trade.orders = [] + + with mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: + trade.update_order(order_mock, 2) + order_mock.cancel.assert_called() + new_order_mock.run.assert_called() + prepare_order.assert_called() + self.assertRegex(stdout_mock.getvalue(), "tick 2, cancelling and adjusting") + + order_mock.reset_mock() + new_order_mock.reset_mock() + trade.orders = [] + + with mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: + trade.update_order(order_mock, 5) + order_mock.cancel.assert_called() + new_order_mock.run.assert_called() + prepare_order.assert_called() + self.assertRegex(stdout_mock.getvalue(), "tick 5, cancelling and adjusting") + + order_mock.reset_mock() + new_order_mock.reset_mock() + trade.orders = [] + + with mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: + trade.update_order(order_mock, 7) + order_mock.cancel.assert_called() + new_order_mock.run.assert_called() + prepare_order.assert_called_with(compute_value="default") + self.assertRegex(stdout_mock.getvalue(), "tick 7, fallbacking to market value") + self.assertRegex(stdout_mock.getvalue(), "tick 7, market value, cancelling and adjusting to") + + order_mock.reset_mock() + new_order_mock.reset_mock() + trade.orders = [] + + for i in [10, 13, 16]: + with self.subTest(tick=i), mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: + trade.update_order(order_mock, i) + order_mock.cancel.assert_called() + new_order_mock.run.assert_called() + prepare_order.assert_called_with(compute_value="default") + self.assertNotRegex(stdout_mock.getvalue(), "tick {}, fallbacking to market value".format(i)) + self.assertRegex(stdout_mock.getvalue(), "tick {}, market value, cancelling and adjusting to".format(i)) + + order_mock.reset_mock() + new_order_mock.reset_mock() + trade.orders = [] + + for i in [8, 9, 11, 12]: + with self.subTest(tick=i), mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: + trade.update_order(order_mock, i) + order_mock.cancel.assert_not_called() + new_order_mock.run.assert_not_called() + self.assertEqual("", stdout_mock.getvalue()) + + order_mock.reset_mock() + new_order_mock.reset_mock() + trade.orders = [] - @unittest.skip("TODO") - def test_update_order(self): - pass @mock.patch('sys.stdout', new_callable=StringIO) def test_print_with_order(self, mock_stdout): @@ -987,6 +1569,384 @@ class TradeTest(WebMockTestCase): self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(trade)) +@unittest.skipUnless("unit" in limits, "Unit skipped") +class OrderTest(WebMockTestCase): + def test_values(self): + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", "market", "trade") + self.assertEqual("buy", order.action) + self.assertEqual(10, order.amount.value) + self.assertEqual("ETH", order.amount.currency) + self.assertEqual(D("0.1"), order.rate) + self.assertEqual("BTC", order.base_currency) + self.assertEqual("market", order.market) + self.assertEqual("long", order.trade_type) + self.assertEqual("pending", order.status) + self.assertEqual("trade", order.trade) + self.assertIsNone(order.id) + self.assertFalse(order.close_if_possible) + + def test__repr(self): + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", "market", "trade") + self.assertEqual("Order(buy long 10.00000000 ETH at 0.1 BTC [pending])", repr(order)) + + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", "market", "trade", + close_if_possible=True) + self.assertEqual("Order(buy long 10.00000000 ETH at 0.1 BTC [pending] ✂)", repr(order)) + + def test_account(self): + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", "market", "trade") + self.assertEqual("exchange", order.account) + + order = portfolio.Order("sell", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "short", "market", "trade") + self.assertEqual("margin", order.account) + + def test_pending(self): + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", "market", "trade") + self.assertTrue(order.pending) + order.status = "open" + self.assertFalse(order.pending) + + def test_open(self): + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", "market", "trade") + self.assertFalse(order.open) + order.status = "open" + self.assertTrue(order.open) + + def test_finished(self): + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", "market", "trade") + self.assertFalse(order.finished) + order.status = "closed" + self.assertTrue(order.finished) + order.status = "canceled" + self.assertTrue(order.finished) + order.status = "error" + self.assertTrue(order.finished) + + @mock.patch.object(portfolio.Order, "fetch") + def test_cancel(self, fetch): + market = mock.Mock() + portfolio.TradeStore.debug = True + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", market, "trade") + order.status = "open" + + order.cancel() + market.cancel_order.assert_not_called() + self.assertEqual("canceled", order.status) + + portfolio.TradeStore.debug = False + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", market, "trade") + order.status = "open" + order.id = 42 + + order.cancel() + market.cancel_order.assert_called_with(42) + fetch.assert_called_once() + + def test_dust_amount_remaining(self): + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", "market", "trade") + order.remaining_amount = mock.Mock(return_value=portfolio.Amount("ETH", 1)) + self.assertFalse(order.dust_amount_remaining()) + + order.remaining_amount = mock.Mock(return_value=portfolio.Amount("ETH", D("0.0001"))) + self.assertTrue(order.dust_amount_remaining()) + + @mock.patch.object(portfolio.Order, "fetch") + @mock.patch.object(portfolio.Order, "filled_amount", return_value=portfolio.Amount("ETH", 1)) + def test_remaining_amount(self, filled_amount, fetch): + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", "market", "trade") + + self.assertEqual(9, order.remaining_amount().value) + order.fetch.assert_not_called() + + order.status = "open" + self.assertEqual(9, order.remaining_amount().value) + fetch.assert_called_once() + + @mock.patch.object(portfolio.Order, "fetch") + def test_filled_amount(self, fetch): + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", "market", "trade") + order.mouvements.append(portfolio.Mouvement("ETH", "BTC", { + "tradeID": 42, "type": "buy", "fee": "0.0015", + "date": "2017-12-30 12:00:12", "rate": "0.1", + "amount": "3", "total": "0.3" + })) + order.mouvements.append(portfolio.Mouvement("ETH", "BTC", { + "tradeID": 43, "type": "buy", "fee": "0.0015", + "date": "2017-12-30 13:00:12", "rate": "0.2", + "amount": "2", "total": "0.4" + })) + self.assertEqual(portfolio.Amount("ETH", 5), order.filled_amount()) + fetch.assert_not_called() + order.status = "open" + self.assertEqual(portfolio.Amount("ETH", 5), order.filled_amount(in_base_currency=False)) + fetch.assert_called_once() + self.assertEqual(portfolio.Amount("BTC", "0.7"), order.filled_amount(in_base_currency=True)) + + def test_fetch_mouvements(self): + market = mock.Mock() + market.privatePostReturnOrderTrades.return_value = [ + { + "tradeID": 42, "type": "buy", "fee": "0.0015", + "date": "2017-12-30 12:00:12", "rate": "0.1", + "amount": "3", "total": "0.3" + }, + { + "tradeID": 43, "type": "buy", "fee": "0.0015", + "date": "2017-12-30 13:00:12", "rate": "0.2", + "amount": "2", "total": "0.4" + } + ] + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", market, "trade") + order.id = 12 + order.mouvements = ["Foo", "Bar", "Baz"] + + order.fetch_mouvements() + + market.privatePostReturnOrderTrades.assert_called_with({"orderNumber": 12}) + self.assertEqual(2, len(order.mouvements)) + self.assertEqual(42, order.mouvements[0].id) + self.assertEqual(43, order.mouvements[1].id) + + market.privatePostReturnOrderTrades.side_effect = portfolio.ExchangeError + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", market, "trade") + order.fetch_mouvements() + self.assertEqual(0, len(order.mouvements)) + + def test_mark_finished_order(self): + market = mock.Mock() + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "short", market, "trade", + close_if_possible=True) + order.status = "closed" + portfolio.TradeStore.debug = False + + order.mark_finished_order() + market.close_margin_position.assert_called_with("ETH", "BTC") + market.close_margin_position.reset_mock() + + order.status = "open" + order.mark_finished_order() + market.close_margin_position.assert_not_called() + + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "short", market, "trade", + close_if_possible=False) + order.status = "closed" + order.mark_finished_order() + market.close_margin_position.assert_not_called() + + order = portfolio.Order("sell", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "short", market, "trade", + close_if_possible=True) + order.status = "closed" + order.mark_finished_order() + market.close_margin_position.assert_not_called() + + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", market, "trade", + close_if_possible=True) + order.status = "closed" + order.mark_finished_order() + market.close_margin_position.assert_not_called() + + portfolio.TradeStore.debug = True + + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "short", market, "trade", + close_if_possible=True) + order.status = "closed" + + order.mark_finished_order() + market.close_margin_position.assert_not_called() + + @mock.patch.object(portfolio.Order, "fetch_mouvements") + def test_fetch(self, fetch_mouvements): + time = self.time.time() + with mock.patch.object(portfolio.time, "time") as time_mock: + market = mock.Mock() + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", market, "trade") + order.id = 45 + with self.subTest(debug=True): + portfolio.TradeStore.debug = True + order.fetch() + time_mock.assert_not_called() + order.fetch(force=True) + time_mock.assert_not_called() + market.fetch_order.assert_not_called() + fetch_mouvements.assert_not_called() + self.assertIsNone(order.fetch_cache_timestamp) + + with self.subTest(debug=False): + portfolio.TradeStore.debug = False + time_mock.return_value = time + market.fetch_order.return_value = { + "status": "foo", + "datetime": "timestamp" + } + order.fetch() + + market.fetch_order.assert_called_once() + fetch_mouvements.assert_called_once() + self.assertEqual("foo", order.status) + self.assertEqual("timestamp", order.timestamp) + self.assertEqual(time, order.fetch_cache_timestamp) + self.assertEqual(1, len(order.results)) + + market.fetch_order.reset_mock() + fetch_mouvements.reset_mock() + + time_mock.return_value = time + 8 + order.fetch() + market.fetch_order.assert_not_called() + fetch_mouvements.assert_not_called() + + order.fetch(force=True) + market.fetch_order.assert_called_once() + fetch_mouvements.assert_called_once() + + market.fetch_order.reset_mock() + fetch_mouvements.reset_mock() + + time_mock.return_value = time + 19 + order.fetch() + market.fetch_order.assert_called_once() + fetch_mouvements.assert_called_once() + + @mock.patch.object(portfolio.Order, "fetch") + @mock.patch.object(portfolio.Order, "mark_finished_order") + def test_get_status(self, mark_finished_order, fetch): + with self.subTest(debug=True): + portfolio.TradeStore.debug = True + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", "market", "trade") + self.assertEqual("pending", order.get_status()) + fetch.assert_not_called() + + with self.subTest(debug=False, finished=False): + portfolio.TradeStore.debug = False + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", "market", "trade") + def _fetch(order): + def update_status(): + order.status = "open" + return update_status + fetch.side_effect = _fetch(order) + self.assertEqual("open", order.get_status()) + mark_finished_order.assert_not_called() + fetch.assert_called_once() + + mark_finished_order.reset_mock() + fetch.reset_mock() + with self.subTest(debug=False, finished=True): + portfolio.TradeStore.debug = False + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", "market", "trade") + def _fetch(order): + def update_status(): + order.status = "closed" + return update_status + fetch.side_effect = _fetch(order) + self.assertEqual("closed", order.get_status()) + mark_finished_order.assert_called_once() + fetch.assert_called_once() + + def test_run(self): + market = mock.Mock() + + market.order_precision.return_value = 4 + with self.subTest(debug=True),\ + mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: + portfolio.TradeStore.debug = True + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", market, "trade") + order.run() + market.create_order.assert_not_called() + self.assertEqual("market.create_order('ETH/BTC', 'limit', 'buy', 10.0000, price=0.1, account=exchange)\n", stdout_mock.getvalue()) + self.assertEqual("open", order.status) + self.assertEqual(1, len(order.results)) + self.assertEqual(-1, order.id) + + market.create_order.reset_mock() + with self.subTest(debug=False): + portfolio.TradeStore.debug = False + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", market, "trade") + market.create_order.return_value = { "id": 123 } + order.run() + market.create_order.assert_called_once() + self.assertEqual(1, len(order.results)) + self.assertEqual("open", order.status) + + market.create_order.reset_mock() + with self.subTest(exception=True),\ + mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: + order = portfolio.Order("buy", portfolio.Amount("ETH", 10), + D("0.1"), "BTC", "long", market, "trade") + market.create_order.side_effect = Exception("bouh") + order.run() + market.create_order.assert_called_once() + self.assertEqual(0, len(order.results)) + self.assertEqual("error", order.status) + self.assertRegex(stdout_mock.getvalue(), "error when running market.create_order") + self.assertRegex(stdout_mock.getvalue(), "Exception: bouh") + + market.create_order.reset_mock() + with self.subTest(dust_amount_exception=True),\ + mock.patch.object(portfolio.Order, "mark_finished_order") as mark_finished_order: + order = portfolio.Order("buy", portfolio.Amount("ETH", 0.001), + D("0.1"), "BTC", "long", market, "trade") + market.create_order.side_effect = portfolio.ExchangeNotAvailable + order.run() + market.create_order.assert_called_once() + self.assertEqual(0, len(order.results)) + self.assertEqual("closed", order.status) + mark_finished_order.assert_called_once() + + +@unittest.skipUnless("unit" in limits, "Unit skipped") +class MouvementTest(WebMockTestCase): + def test_values(self): + mouvement = portfolio.Mouvement("ETH", "BTC", { + "tradeID": 42, "type": "buy", "fee": "0.0015", + "date": "2017-12-30 12:00:12", "rate": "0.1", + "amount": "10", "total": "1" + }) + self.assertEqual("ETH", mouvement.currency) + self.assertEqual("BTC", mouvement.base_currency) + self.assertEqual(42, mouvement.id) + self.assertEqual("buy", mouvement.action) + self.assertEqual(D("0.0015"), mouvement.fee_rate) + self.assertEqual(portfolio.datetime(2017, 12, 30, 12, 0, 12), mouvement.date) + self.assertEqual(D("0.1"), mouvement.rate) + self.assertEqual(portfolio.Amount("ETH", "10"), mouvement.total) + self.assertEqual(portfolio.Amount("BTC", "1"), mouvement.total_in_base) + + mouvement = portfolio.Mouvement("ETH", "BTC", { "foo": "bar" }) + self.assertIsNone(mouvement.date) + self.assertIsNone(mouvement.id) + self.assertIsNone(mouvement.action) + self.assertEqual(-1, mouvement.fee_rate) + self.assertEqual(0, mouvement.rate) + self.assertEqual(portfolio.Amount("ETH", 0), mouvement.total) + self.assertEqual(portfolio.Amount("BTC", 0), mouvement.total_in_base) + +@unittest.skipUnless("acceptance" in limits, "Acceptance skipped") class AcceptanceTest(WebMockTestCase): @unittest.expectedFailure def test_success_sell_only_necessary(self): @@ -1073,7 +2033,7 @@ class AcceptanceTest(WebMockTestCase): self.assertEqual(portfolio.Amount("XVG", 1000), balances["XVG"].total) - trades = TradeStore.all + trades = portfolio.TradeStore.all self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades[0].value_from) self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[0].value_to) self.assertEqual("dispose", trades[0].action) @@ -1088,7 +2048,7 @@ class AcceptanceTest(WebMockTestCase): self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[3].value_from) self.assertEqual(portfolio.Amount("BTC", D("-0.002")), trades[3].value_to) - self.assertEqual("dispose", trades[3].action) + self.assertEqual("acquire", trades[3].action) self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[4].value_from) self.assertEqual(portfolio.Amount("BTC", D("0.008")), trades[4].value_to) @@ -1099,9 +2059,9 @@ class AcceptanceTest(WebMockTestCase): self.assertEqual("acquire", trades[5].action) # Action 2 - portfolio.Trade.prepare_orders(only="dispose", compute_value=lambda x, y: x["bid"] * D("1.001")) + portfolio.TradeStore.prepare_orders(only="dispose", compute_value=lambda x, y: x["bid"] * D("1.001")) - all_orders = portfolio.Trade.all_orders() + all_orders = portfolio.TradeStore.all_orders(state="pending") self.assertEqual(2, len(all_orders)) self.assertEqual(2, 3*all_orders[0].amount.value) self.assertEqual(D("0.14014"), all_orders[0].rate) @@ -1134,7 +2094,14 @@ class AcceptanceTest(WebMockTestCase): self.assertEqual("open", all_orders[0].status) self.assertEqual("open", all_orders[1].status) - market.fetch_order.return_value = { "status": "closed" } + market.fetch_order.return_value = { "status": "closed", "datetime": "2018-01-20 13:40:00" } + market.privatePostReturnOrderTrades.return_value = [ + { + "tradeID": 42, "type": "buy", "fee": "0.0015", + "date": "2017-12-30 12:00:12", "rate": "0.1", + "amount": "10", "total": "1" + } + ] with mock.patch.object(portfolio.time, "sleep") as sleep: # Action 4 helper.follow_orders(verbose=False) @@ -1146,31 +2113,39 @@ class AcceptanceTest(WebMockTestCase): fetch_balance = { "ETH": { - "free": D("1.0") / 3, - "used": D("0.0"), + "exchange_free": D("1.0") / 3, + "exchange_used": D("0.0"), + "exchange_total": D("1.0") / 3, + "margin_total": 0, "total": D("1.0") / 3, }, "BTC": { - "free": D("0.134"), - "used": D("0.0"), + "exchange_free": D("0.134"), + "exchange_used": D("0.0"), + "exchange_total": D("0.134"), + "margin_total": 0, "total": D("0.134"), }, "ETC": { - "free": D("4.0"), - "used": D("0.0"), + "exchange_free": D("4.0"), + "exchange_used": D("0.0"), + "exchange_total": D("4.0"), + "margin_total": 0, "total": D("4.0"), }, "XVG": { - "free": D("0.0"), - "used": D("0.0"), + "exchange_free": D("0.0"), + "exchange_used": D("0.0"), + "exchange_total": D("0.0"), + "margin_total": 0, "total": D("0.0"), }, } - market.fetch_balance.return_value = fetch_balance + market.fetch_all_balances.return_value = fetch_balance with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition): # Action 5 - helper.update_trades(market, only="buy", compute_value="average") + helper.update_trades(market, only="acquire", compute_value="average") balances = portfolio.BalanceStore.all self.assertEqual(portfolio.Amount("ETH", 1 / D("3")), balances["ETH"].total) @@ -1179,37 +2154,37 @@ class AcceptanceTest(WebMockTestCase): self.assertEqual(portfolio.Amount("XVG", 0), balances["XVG"].total) - trades = TradeStore.all - self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades["ETH"].value_from) - self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades["ETH"].value_to) - self.assertEqual("sell", trades["ETH"].action) + trades = portfolio.TradeStore.all + self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades[0].value_from) + self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[0].value_to) + self.assertEqual("dispose", trades[0].action) - self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades["ETC"].value_from) - self.assertEqual(portfolio.Amount("BTC", D("0.0485")), trades["ETC"].value_to) - self.assertEqual("buy", trades["ETC"].action) + self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades[1].value_from) + self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[1].value_to) + self.assertEqual("acquire", trades[1].action) self.assertNotIn("BTC", trades) - self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["BTD"].value_from) - self.assertEqual(portfolio.Amount("BTC", D("0.00194")), trades["BTD"].value_to) - self.assertEqual("buy", trades["BTD"].action) + self.assertEqual(portfolio.Amount("BTC", D("0.04")), trades[2].value_from) + self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[2].value_to) + self.assertEqual("dispose", trades[2].action) - self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["B2X"].value_from) - self.assertEqual(portfolio.Amount("BTC", D("0.00776")), trades["B2X"].value_to) - self.assertEqual("buy", trades["B2X"].action) + self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[3].value_from) + self.assertEqual(portfolio.Amount("BTC", D("-0.002")), trades[3].value_to) + self.assertEqual("acquire", trades[3].action) - self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["USDT"].value_from) - self.assertEqual(portfolio.Amount("BTC", D("0.0097")), trades["USDT"].value_to) - self.assertEqual("buy", trades["USDT"].action) + self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[4].value_from) + self.assertEqual(portfolio.Amount("BTC", D("0.008")), trades[4].value_to) + self.assertEqual("acquire", trades[4].action) - self.assertEqual(portfolio.Amount("BTC", D("0.04")), trades["XVG"].value_from) - self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["XVG"].value_to) - self.assertEqual("sell", trades["XVG"].action) + self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[5].value_from) + self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades[5].value_to) + self.assertEqual("acquire", trades[5].action) # Action 6 - portfolio.Trade.prepare_orders(only="buy", compute_value=lambda x, y: x["ask"]) + portfolio.TradeStore.prepare_orders(only="acquire", compute_value=lambda x, y: x["ask"]) - all_orders = portfolio.Trade.all_orders(state="pending") + all_orders = portfolio.TradeStore.all_orders(state="pending") self.assertEqual(4, len(all_orders)) self.assertEqual(portfolio.Amount("ETC", D("12.83333333")), round(all_orders[0].amount)) self.assertEqual(D("0.003"), all_orders[0].rate)