]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git/blobdiff - test.py
Add Makefile and test coverage
[perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git] / test.py
diff --git a/test.py b/test.py
index aae1dc85af02245f88ddd28d32a4fac9ecfe1cef..be3ad4a6d481f61e8e5e40056dc49dae20c7759e 100644 (file)
--- 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
 
@@ -39,6 +50,7 @@ class WebMockTestCase(unittest.TestCase):
         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 +152,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 +263,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 +379,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 +422,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 +640,153 @@ 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())
+
+@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 +875,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 +968,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 +992,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 +1017,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 +1051,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)
@@ -939,22 +1109,251 @@ 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")
+        def _prepare_order(compute_value=None):
+            trade.orders.append(new_order_mock)
+        prepare_order.side_effect = _prepare_order
+
+        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))
+                self.assertEqual(0, len(trade.orders))
+
+            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")
+            self.assertEqual(1, len(trade.orders))
+
+        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")
+            self.assertEqual(1, len(trade.orders))
+
+        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")
+            self.assertEqual(1, len(trade.orders))
+
+        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))
+                self.assertEqual(1, len(trade.orders))
+
+            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())
+                self.assertEqual(0, len(trade.orders))
+
+            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 +1386,7 @@ class TradeTest(WebMockTestCase):
 
         self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(trade))
 
+@unittest.skipUnless("acceptance" in limits, "Acceptance skipped")
 class AcceptanceTest(WebMockTestCase):
     @unittest.expectedFailure
     def test_success_sell_only_necessary(self):