aboutsummaryrefslogtreecommitdiff
path: root/test.py
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2018-02-11 13:36:54 +0100
committerIsmaël Bouya <ismael.bouya@normalesup.org>2018-02-11 13:36:54 +0100
commit1aa7d4fa2ec3c2b3268bef31a666ca6e1aaa6563 (patch)
treef47f804070e935bf8fdf621cc59f4320606cf46f /test.py
parent6ca5a1ec669593fa915a2824efca068c975f9caa (diff)
downloadTrader-1aa7d4fa2ec3c2b3268bef31a666ca6e1aaa6563.tar.gz
Trader-1aa7d4fa2ec3c2b3268bef31a666ca6e1aaa6563.tar.zst
Trader-1aa7d4fa2ec3c2b3268bef31a666ca6e1aaa6563.zip
Add Makefile and test coverage
Fix order preparation and add tests for the step Separate tests between acceptance and unit Add more tests
Diffstat (limited to 'test.py')
-rw-r--r--test.py440
1 files changed, 420 insertions, 20 deletions
diff --git a/test.py b/test.py
index aae1dc8..be3ad4a 100644
--- a/test.py
+++ b/test.py
@@ -1,3 +1,4 @@
1import sys
1import portfolio 2import portfolio
2import unittest 3import unittest
3from decimal import Decimal as D 4from decimal import Decimal as D
@@ -7,6 +8,16 @@ import requests_mock
7from io import StringIO 8from io import StringIO
8import helper 9import helper
9 10
11limits = ["acceptance", "unit"]
12for test_type in limits:
13 if "--no{}".format(test_type) in sys.argv:
14 sys.argv.remove("--no{}".format(test_type))
15 limits.remove(test_type)
16 if "--only{}".format(test_type) in sys.argv:
17 sys.argv.remove("--only{}".format(test_type))
18 limits = [test_type]
19 break
20
10class WebMockTestCase(unittest.TestCase): 21class WebMockTestCase(unittest.TestCase):
11 import time 22 import time
12 23
@@ -39,6 +50,7 @@ class WebMockTestCase(unittest.TestCase):
39 self.wm.stop() 50 self.wm.stop()
40 super(WebMockTestCase, self).tearDown() 51 super(WebMockTestCase, self).tearDown()
41 52
53@unittest.skipUnless("unit" in limits, "Unit skipped")
42class PortfolioTest(WebMockTestCase): 54class PortfolioTest(WebMockTestCase):
43 def fill_data(self): 55 def fill_data(self):
44 if self.json_response is not None: 56 if self.json_response is not None:
@@ -140,6 +152,7 @@ class PortfolioTest(WebMockTestCase):
140 self.assertEqual(expected_medium, portfolio.Portfolio.repartition(liquidity="medium")) 152 self.assertEqual(expected_medium, portfolio.Portfolio.repartition(liquidity="medium"))
141 self.assertEqual(expected_high, portfolio.Portfolio.repartition(liquidity="high")) 153 self.assertEqual(expected_high, portfolio.Portfolio.repartition(liquidity="high"))
142 154
155@unittest.skipUnless("unit" in limits, "Unit skipped")
143class AmountTest(WebMockTestCase): 156class AmountTest(WebMockTestCase):
144 def test_values(self): 157 def test_values(self):
145 amount = portfolio.Amount("BTC", "0.65") 158 amount = portfolio.Amount("BTC", "0.65")
@@ -250,6 +263,9 @@ class AmountTest(WebMockTestCase):
250 self.assertEqual(D("5.5"), (amount / 2).value) 263 self.assertEqual(D("5.5"), (amount / 2).value)
251 self.assertEqual(D("4.4"), (amount / D("2.5")).value) 264 self.assertEqual(D("4.4"), (amount / D("2.5")).value)
252 265
266 with self.assertRaises(Exception):
267 amount / amount
268
253 def test__truediv(self): 269 def test__truediv(self):
254 amount = portfolio.Amount("XEM", 11) 270 amount = portfolio.Amount("XEM", 11)
255 271
@@ -363,6 +379,7 @@ class AmountTest(WebMockTestCase):
363 amount2.linked_to = amount3 379 amount2.linked_to = amount3
364 self.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT -> Amount(0.10000000 BTC)))", repr(amount1)) 380 self.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT -> Amount(0.10000000 BTC)))", repr(amount1))
365 381
382@unittest.skipUnless("unit" in limits, "Unit skipped")
366class BalanceTest(WebMockTestCase): 383class BalanceTest(WebMockTestCase):
367 def test_values(self): 384 def test_values(self):
368 balance = portfolio.Balance("BTC", { 385 balance = portfolio.Balance("BTC", {
@@ -405,16 +422,27 @@ class BalanceTest(WebMockTestCase):
405 "exchange_used": 1, "exchange_free": 2 }) 422 "exchange_used": 1, "exchange_free": 2 })
406 self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX + ❌1.00000000 BTX = 3.00000000 BTX])", repr(balance)) 423 self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX + ❌1.00000000 BTX = 3.00000000 BTX])", repr(balance))
407 424
425 balance = portfolio.Balance("BTX", { "exchange_total": 1, "exchange_used": 1})
426 self.assertEqual("Balance(BTX Exch: [❌1.00000000 BTX])", repr(balance))
427
408 balance = portfolio.Balance("BTX", { "margin_total": 3, 428 balance = portfolio.Balance("BTX", { "margin_total": 3,
409 "margin_borrowed": 1, "margin_free": 2 }) 429 "margin_borrowed": 1, "margin_free": 2 })
410 self.assertEqual("Balance(BTX Margin: [✔2.00000000 BTX + borrowed 1.00000000 BTX = 3.00000000 BTX])", repr(balance)) 430 self.assertEqual("Balance(BTX Margin: [✔2.00000000 BTX + borrowed 1.00000000 BTX = 3.00000000 BTX])", repr(balance))
411 431
432 balance = portfolio.Balance("BTX", { "margin_total": 2, "margin_free": 2 })
433 self.assertEqual("Balance(BTX Margin: [✔2.00000000 BTX])", repr(balance))
434
412 balance = portfolio.Balance("BTX", { "margin_total": -3, 435 balance = portfolio.Balance("BTX", { "margin_total": -3,
413 "margin_borrowed_base_price": D("0.1"), 436 "margin_borrowed_base_price": D("0.1"),
414 "margin_borrowed_base_currency": "BTC", 437 "margin_borrowed_base_currency": "BTC",
415 "margin_lending_fees": D("0.002") }) 438 "margin_lending_fees": D("0.002") })
416 self.assertEqual("Balance(BTX Margin: [-3.00000000 BTX @@ 0.10000000 BTC/0.00200000 BTC])", repr(balance)) 439 self.assertEqual("Balance(BTX Margin: [-3.00000000 BTX @@ 0.10000000 BTC/0.00200000 BTC])", repr(balance))
417 440
441 balance = portfolio.Balance("BTX", { "margin_total": 1,
442 "margin_borrowed": 1, "exchange_free": 2, "exchange_total": 2})
443 self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX] Margin: [borrowed 1.00000000 BTX] Total: [0.00000000 BTX])", repr(balance))
444
445@unittest.skipUnless("unit" in limits, "Unit skipped")
418class HelperTest(WebMockTestCase): 446class HelperTest(WebMockTestCase):
419 def test_get_ticker(self): 447 def test_get_ticker(self):
420 market = mock.Mock() 448 market = mock.Mock()
@@ -612,15 +640,153 @@ class HelperTest(WebMockTestCase):
612 self.assertEqual(D("0.01"), call[0][0]["XVG"].value) 640 self.assertEqual(D("0.01"), call[0][0]["XVG"].value)
613 self.assertEqual(D("1.01"), call[0][1]["BTC"].value) 641 self.assertEqual(D("1.01"), call[0][1]["BTC"].value)
614 642
615 @unittest.skip("TODO") 643 @mock.patch.object(portfolio.time, "sleep")
616 def test_follow_orders(self): 644 @mock.patch.object(portfolio.TradeStore, "all_orders")
617 pass 645 def test_follow_orders(self, all_orders, time_mock):
618 646 for verbose, debug, sleep in [
619 647 (True, False, None), (False, False, None),
648 (True, True, None), (True, False, 12),
649 (True, True, 12)]:
650 with self.subTest(sleep=sleep, debug=debug, verbose=verbose), \
651 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
652 portfolio.TradeStore.debug = debug
653 order_mock1 = mock.Mock()
654 order_mock2 = mock.Mock()
655 order_mock3 = mock.Mock()
656 all_orders.side_effect = [
657 [order_mock1, order_mock2],
658 [order_mock1, order_mock2],
659
660 [order_mock1, order_mock3],
661 [order_mock1, order_mock3],
662
663 [order_mock1, order_mock3],
664 [order_mock1, order_mock3],
665
666 []
667 ]
668
669 order_mock1.get_status.side_effect = ["open", "open", "closed"]
670 order_mock2.get_status.side_effect = ["open"]
671 order_mock3.get_status.side_effect = ["open", "closed"]
672
673 order_mock1.trade = mock.Mock()
674 order_mock2.trade = mock.Mock()
675 order_mock3.trade = mock.Mock()
676
677 helper.follow_orders(verbose=verbose, sleep=sleep)
678
679 order_mock1.trade.update_order.assert_any_call(order_mock1, 1)
680 order_mock1.trade.update_order.assert_any_call(order_mock1, 2)
681 self.assertEqual(2, order_mock1.trade.update_order.call_count)
682 self.assertEqual(3, order_mock1.get_status.call_count)
683
684 order_mock2.trade.update_order.assert_any_call(order_mock2, 1)
685 self.assertEqual(1, order_mock2.trade.update_order.call_count)
686 self.assertEqual(1, order_mock2.get_status.call_count)
687
688 order_mock3.trade.update_order.assert_any_call(order_mock3, 2)
689 self.assertEqual(1, order_mock3.trade.update_order.call_count)
690 self.assertEqual(2, order_mock3.get_status.call_count)
691
692 if sleep is None:
693 if debug:
694 time_mock.assert_called_with(7)
695 else:
696 time_mock.assert_called_with(30)
697 else:
698 time_mock.assert_called_with(sleep)
699
700 if verbose:
701 self.assertNotEqual("", stdout_mock.getvalue())
702 else:
703 self.assertEqual("", stdout_mock.getvalue())
704
705@unittest.skipUnless("unit" in limits, "Unit skipped")
620class TradeStoreTest(WebMockTestCase): 706class TradeStoreTest(WebMockTestCase):
621 @unittest.skip("TODO") 707 @mock.patch.object(portfolio.BalanceStore, "currencies")
622 def test_compute_trades(self): 708 @mock.patch.object(portfolio.TradeStore, "add_trade_if_matching")
623 pass 709 def test_compute_trades(self, add_trade_if_matching, currencies):
710 currencies.return_value = ["XMR", "DASH", "XVG", "BTC", "ETH"]
711
712 values_in_base = {
713 "XMR": portfolio.Amount("BTC", D("0.9")),
714 "DASH": portfolio.Amount("BTC", D("0.4")),
715 "XVG": portfolio.Amount("BTC", D("-0.5")),
716 "BTC": portfolio.Amount("BTC", D("0.5")),
717 }
718 new_repartition = {
719 "DASH": portfolio.Amount("BTC", D("0.5")),
720 "XVG": portfolio.Amount("BTC", D("0.1")),
721 "BTC": portfolio.Amount("BTC", D("0.4")),
722 "ETH": portfolio.Amount("BTC", D("0.3")),
723 }
724
725 portfolio.TradeStore.compute_trades(values_in_base,
726 new_repartition, only="only", market="market")
727
728 self.assertEqual(5, add_trade_if_matching.call_count)
729 add_trade_if_matching.assert_any_call(
730 portfolio.Amount("BTC", D("0.9")),
731 portfolio.Amount("BTC", 0),
732 "XMR", only="only", market="market"
733 )
734 add_trade_if_matching.assert_any_call(
735 portfolio.Amount("BTC", D("0.4")),
736 portfolio.Amount("BTC", D("0.5")),
737 "DASH", only="only", market="market"
738 )
739 add_trade_if_matching.assert_any_call(
740 portfolio.Amount("BTC", D("-0.5")),
741 portfolio.Amount("BTC", D("0")),
742 "XVG", only="only", market="market"
743 )
744 add_trade_if_matching.assert_any_call(
745 portfolio.Amount("BTC", D("0")),
746 portfolio.Amount("BTC", D("0.1")),
747 "XVG", only="only", market="market"
748 )
749 add_trade_if_matching.assert_any_call(
750 portfolio.Amount("BTC", D("0")),
751 portfolio.Amount("BTC", D("0.3")),
752 "ETH", only="only", market="market"
753 )
754
755 def test_add_trade_if_matching(self):
756 result = portfolio.TradeStore.add_trade_if_matching(
757 portfolio.Amount("BTC", D("0")),
758 portfolio.Amount("BTC", D("0.3")),
759 "ETH", only="nope", market="market"
760 )
761 self.assertEqual(0, len(portfolio.TradeStore.all))
762 self.assertEqual(False, result)
763
764 portfolio.TradeStore.all = []
765 result = portfolio.TradeStore.add_trade_if_matching(
766 portfolio.Amount("BTC", D("0")),
767 portfolio.Amount("BTC", D("0.3")),
768 "ETH", only=None, market="market"
769 )
770 self.assertEqual(1, len(portfolio.TradeStore.all))
771 self.assertEqual(True, result)
772
773 portfolio.TradeStore.all = []
774 result = portfolio.TradeStore.add_trade_if_matching(
775 portfolio.Amount("BTC", D("0")),
776 portfolio.Amount("BTC", D("0.3")),
777 "ETH", only="acquire", market="market"
778 )
779 self.assertEqual(1, len(portfolio.TradeStore.all))
780 self.assertEqual(True, result)
781
782 portfolio.TradeStore.all = []
783 result = portfolio.TradeStore.add_trade_if_matching(
784 portfolio.Amount("BTC", D("0")),
785 portfolio.Amount("BTC", D("0.3")),
786 "ETH", only="dispose", market="market"
787 )
788 self.assertEqual(0, len(portfolio.TradeStore.all))
789 self.assertEqual(False, result)
624 790
625 def test_prepare_orders(self): 791 def test_prepare_orders(self):
626 trade_mock1 = mock.Mock() 792 trade_mock1 = mock.Mock()
@@ -709,7 +875,7 @@ class TradeStoreTest(WebMockTestCase):
709 order_mock2.get_status.assert_called() 875 order_mock2.get_status.assert_called()
710 order_mock3.get_status.assert_called() 876 order_mock3.get_status.assert_called()
711 877
712 878@unittest.skipUnless("unit" in limits, "Unit skipped")
713class BalanceStoreTest(WebMockTestCase): 879class BalanceStoreTest(WebMockTestCase):
714 def setUp(self): 880 def setUp(self):
715 super(BalanceStoreTest, self).setUp() 881 super(BalanceStoreTest, self).setUp()
@@ -802,12 +968,14 @@ class BalanceStoreTest(WebMockTestCase):
802 repartition.return_value = { 968 repartition.return_value = {
803 "XEM": (D("0.75"), "long"), 969 "XEM": (D("0.75"), "long"),
804 "BTC": (D("0.26"), "long"), 970 "BTC": (D("0.26"), "long"),
971 "DASH": (D("0.10"), "short"),
805 } 972 }
806 973
807 amounts = portfolio.BalanceStore.dispatch_assets(portfolio.Amount("BTC", "10.1")) 974 amounts = portfolio.BalanceStore.dispatch_assets(portfolio.Amount("BTC", "11.1"))
808 self.assertIn("XEM", portfolio.BalanceStore.currencies()) 975 self.assertIn("XEM", portfolio.BalanceStore.currencies())
809 self.assertEqual(D("2.6"), amounts["BTC"].value) 976 self.assertEqual(D("2.6"), amounts["BTC"].value)
810 self.assertEqual(D("7.5"), amounts["XEM"].value) 977 self.assertEqual(D("7.5"), amounts["XEM"].value)
978 self.assertEqual(D("-1.0"), amounts["DASH"].value)
811 979
812 def test_currencies(self): 980 def test_currencies(self):
813 portfolio.BalanceStore.all = { 981 portfolio.BalanceStore.all = {
@@ -824,6 +992,7 @@ class BalanceStoreTest(WebMockTestCase):
824 } 992 }
825 self.assertListEqual(["BTC", "ETH"], list(portfolio.BalanceStore.currencies())) 993 self.assertListEqual(["BTC", "ETH"], list(portfolio.BalanceStore.currencies()))
826 994
995@unittest.skipUnless("unit" in limits, "Unit skipped")
827class ComputationTest(WebMockTestCase): 996class ComputationTest(WebMockTestCase):
828 def test_compute_value(self): 997 def test_compute_value(self):
829 compute = mock.Mock() 998 compute = mock.Mock()
@@ -848,6 +1017,7 @@ class ComputationTest(WebMockTestCase):
848 compute.assert_called_with("foo", "bid") 1017 compute.assert_called_with("foo", "bid")
849 1018
850 1019
1020@unittest.skipUnless("unit" in limits, "Unit skipped")
851class TradeTest(WebMockTestCase): 1021class TradeTest(WebMockTestCase):
852 1022
853 def test_values_assertion(self): 1023 def test_values_assertion(self):
@@ -881,7 +1051,7 @@ class TradeTest(WebMockTestCase):
881 1051
882 value_from = portfolio.Amount("BTC", "1.0") 1052 value_from = portfolio.Amount("BTC", "1.0")
883 value_from.linked_to = portfolio.Amount("BTC", "1.0") 1053 value_from.linked_to = portfolio.Amount("BTC", "1.0")
884 value_to = portfolio.Amount("BTC", "1.0") 1054 value_to = portfolio.Amount("BTC", "2.0")
885 trade = portfolio.Trade(value_from, value_to, "BTC") 1055 trade = portfolio.Trade(value_from, value_to, "BTC")
886 1056
887 self.assertIsNone(trade.action) 1057 self.assertIsNone(trade.action)
@@ -939,22 +1109,251 @@ class TradeTest(WebMockTestCase):
939 trade = portfolio.Trade(value_from, value_to, "ETH") 1109 trade = portfolio.Trade(value_from, value_to, "ETH")
940 1110
941 order1 = mock.Mock() 1111 order1 = mock.Mock()
942 order1.filled_amount = portfolio.Amount("ETH", "0.3") 1112 order1.filled_amount.return_value = portfolio.Amount("ETH", "0.3")
943 1113
944 order2 = mock.Mock() 1114 order2 = mock.Mock()
945 order2.filled_amount = portfolio.Amount("ETH", "0.01") 1115 order2.filled_amount.return_value = portfolio.Amount("ETH", "0.01")
946 trade.orders.append(order1) 1116 trade.orders.append(order1)
947 trade.orders.append(order2) 1117 trade.orders.append(order2)
948 1118
949 self.assertEqual(portfolio.Amount("ETH", "0.31"), trade.filled_amount) 1119 self.assertEqual(portfolio.Amount("ETH", "0.31"), trade.filled_amount())
1120 order1.filled_amount.assert_called_with(in_base_currency=False)
1121 order2.filled_amount.assert_called_with(in_base_currency=False)
950 1122
951 @unittest.skip("TODO") 1123 self.assertEqual(portfolio.Amount("ETH", "0.31"), trade.filled_amount(in_base_currency=False))
952 def test_prepare_order(self): 1124 order1.filled_amount.assert_called_with(in_base_currency=False)
953 pass 1125 order2.filled_amount.assert_called_with(in_base_currency=False)
1126
1127 self.assertEqual(portfolio.Amount("ETH", "0.31"), trade.filled_amount(in_base_currency=True))
1128 order1.filled_amount.assert_called_with(in_base_currency=True)
1129 order2.filled_amount.assert_called_with(in_base_currency=True)
1130
1131 @mock.patch.object(helper, "get_ticker")
1132 @mock.patch.object(portfolio.Computation, "compute_value")
1133 @mock.patch.object(portfolio.Trade, "filled_amount")
1134 @mock.patch.object(portfolio, "Order")
1135 def test_prepare_order(self, Order, filled_amount, compute_value, get_ticker):
1136 Order.return_value = "Order"
1137
1138 with self.subTest(desc="Nothing to do"):
1139 value_from = portfolio.Amount("BTC", "10")
1140 value_from.rate = D("0.1")
1141 value_from.linked_to = portfolio.Amount("FOO", "100")
1142 value_to = portfolio.Amount("BTC", "10")
1143 trade = portfolio.Trade(value_from, value_to, "FOO", market="market")
1144
1145 trade.prepare_order()
1146
1147 filled_amount.assert_not_called()
1148 compute_value.assert_not_called()
1149 self.assertEqual(0, len(trade.orders))
1150 Order.assert_not_called()
1151
1152 get_ticker.return_value = { "inverted": False }
1153 with self.subTest(desc="Already filled"), mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
1154 filled_amount.return_value = portfolio.Amount("FOO", "100")
1155 compute_value.return_value = D("0.125")
1156
1157 value_from = portfolio.Amount("BTC", "10")
1158 value_from.rate = D("0.1")
1159 value_from.linked_to = portfolio.Amount("FOO", "100")
1160 value_to = portfolio.Amount("BTC", "0")
1161 trade = portfolio.Trade(value_from, value_to, "FOO", market="market")
1162
1163 trade.prepare_order()
1164
1165 filled_amount.assert_called_with(in_base_currency=False)
1166 compute_value.assert_called_with(get_ticker.return_value, "sell", compute_value="default")
1167 self.assertEqual(0, len(trade.orders))
1168 self.assertRegex(stdout_mock.getvalue(), "Less to do than already filled: ")
1169 Order.assert_not_called()
1170
1171 with self.subTest(action="dispose", inverted=False):
1172 filled_amount.return_value = portfolio.Amount("FOO", "60")
1173 compute_value.return_value = D("0.125")
1174
1175 value_from = portfolio.Amount("BTC", "10")
1176 value_from.rate = D("0.1")
1177 value_from.linked_to = portfolio.Amount("FOO", "100")
1178 value_to = portfolio.Amount("BTC", "1")
1179 trade = portfolio.Trade(value_from, value_to, "FOO", market="market")
1180
1181 trade.prepare_order()
1182
1183 filled_amount.assert_called_with(in_base_currency=False)
1184 compute_value.assert_called_with(get_ticker.return_value, "sell", compute_value="default")
1185 self.assertEqual(1, len(trade.orders))
1186 Order.assert_called_with("sell", portfolio.Amount("FOO", 30),
1187 D("0.125"), "BTC", "long", "market",
1188 trade, close_if_possible=False)
1189
1190 with self.subTest(action="acquire", inverted=False):
1191 filled_amount.return_value = portfolio.Amount("BTC", "3")
1192 compute_value.return_value = D("0.125")
1193
1194 value_from = portfolio.Amount("BTC", "1")
1195 value_from.rate = D("0.1")
1196 value_from.linked_to = portfolio.Amount("FOO", "10")
1197 value_to = portfolio.Amount("BTC", "10")
1198 trade = portfolio.Trade(value_from, value_to, "FOO", market="market")
1199
1200 trade.prepare_order()
1201
1202 filled_amount.assert_called_with(in_base_currency=True)
1203 compute_value.assert_called_with(get_ticker.return_value, "buy", compute_value="default")
1204 self.assertEqual(1, len(trade.orders))
1205
1206 Order.assert_called_with("buy", portfolio.Amount("FOO", 48),
1207 D("0.125"), "BTC", "long", "market",
1208 trade, close_if_possible=False)
1209
1210 with self.subTest(close_if_possible=True):
1211 filled_amount.return_value = portfolio.Amount("FOO", "0")
1212 compute_value.return_value = D("0.125")
1213
1214 value_from = portfolio.Amount("BTC", "10")
1215 value_from.rate = D("0.1")
1216 value_from.linked_to = portfolio.Amount("FOO", "100")
1217 value_to = portfolio.Amount("BTC", "0")
1218 trade = portfolio.Trade(value_from, value_to, "FOO", market="market")
1219
1220 trade.prepare_order()
1221
1222 filled_amount.assert_called_with(in_base_currency=False)
1223 compute_value.assert_called_with(get_ticker.return_value, "sell", compute_value="default")
1224 self.assertEqual(1, len(trade.orders))
1225 Order.assert_called_with("sell", portfolio.Amount("FOO", 100),
1226 D("0.125"), "BTC", "long", "market",
1227 trade, close_if_possible=True)
1228
1229 get_ticker.return_value = { "inverted": True, "original": {} }
1230 with self.subTest(action="dispose", inverted=True):
1231 filled_amount.return_value = portfolio.Amount("FOO", "300")
1232 compute_value.return_value = D("125")
1233
1234 value_from = portfolio.Amount("BTC", "10")
1235 value_from.rate = D("0.01")
1236 value_from.linked_to = portfolio.Amount("FOO", "1000")
1237 value_to = portfolio.Amount("BTC", "1")
1238 trade = portfolio.Trade(value_from, value_to, "FOO", market="market")
1239
1240 trade.prepare_order(compute_value="foo")
1241
1242 filled_amount.assert_called_with(in_base_currency=True)
1243 compute_value.assert_called_with(get_ticker.return_value["original"], "buy", compute_value="foo")
1244 self.assertEqual(1, len(trade.orders))
1245 Order.assert_called_with("buy", portfolio.Amount("BTC", D("4.8")),
1246 D("125"), "FOO", "long", "market",
1247 trade, close_if_possible=False)
1248
1249 with self.subTest(action="acquire", inverted=True):
1250 filled_amount.return_value = portfolio.Amount("BTC", "4")
1251 compute_value.return_value = D("125")
1252
1253 value_from = portfolio.Amount("BTC", "1")
1254 value_from.rate = D("0.01")
1255 value_from.linked_to = portfolio.Amount("FOO", "100")
1256 value_to = portfolio.Amount("BTC", "10")
1257 trade = portfolio.Trade(value_from, value_to, "FOO", market="market")
1258
1259 trade.prepare_order(compute_value="foo")
1260
1261 filled_amount.assert_called_with(in_base_currency=False)
1262 compute_value.assert_called_with(get_ticker.return_value["original"], "sell", compute_value="foo")
1263 self.assertEqual(1, len(trade.orders))
1264 Order.assert_called_with("sell", portfolio.Amount("BTC", D("5")),
1265 D("125"), "FOO", "long", "market",
1266 trade, close_if_possible=False)
1267
1268
1269 @mock.patch.object(portfolio.Trade, "prepare_order")
1270 def test_update_order(self, prepare_order):
1271 order_mock = mock.Mock()
1272 new_order_mock = mock.Mock()
1273
1274 value_from = portfolio.Amount("BTC", "0.5")
1275 value_from.linked_to = portfolio.Amount("ETH", "10.0")
1276 value_to = portfolio.Amount("BTC", "1.0")
1277 trade = portfolio.Trade(value_from, value_to, "ETH")
1278 def _prepare_order(compute_value=None):
1279 trade.orders.append(new_order_mock)
1280 prepare_order.side_effect = _prepare_order
1281
1282 for i in [0, 1, 3, 4, 6]:
1283 with self.subTest(tick=i), mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
1284 trade.update_order(order_mock, i)
1285 order_mock.cancel.assert_not_called()
1286 new_order_mock.run.assert_not_called()
1287 self.assertRegex(stdout_mock.getvalue(), "tick {}, waiting".format(i))
1288 self.assertEqual(0, len(trade.orders))
1289
1290 order_mock.reset_mock()
1291 new_order_mock.reset_mock()
1292 trade.orders = []
1293
1294 with mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
1295 trade.update_order(order_mock, 2)
1296 order_mock.cancel.assert_called()
1297 new_order_mock.run.assert_called()
1298 prepare_order.assert_called()
1299 self.assertRegex(stdout_mock.getvalue(), "tick 2, cancelling and adjusting")
1300 self.assertEqual(1, len(trade.orders))
1301
1302 order_mock.reset_mock()
1303 new_order_mock.reset_mock()
1304 trade.orders = []
1305
1306 with mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
1307 trade.update_order(order_mock, 5)
1308 order_mock.cancel.assert_called()
1309 new_order_mock.run.assert_called()
1310 prepare_order.assert_called()
1311 self.assertRegex(stdout_mock.getvalue(), "tick 5, cancelling and adjusting")
1312 self.assertEqual(1, len(trade.orders))
1313
1314 order_mock.reset_mock()
1315 new_order_mock.reset_mock()
1316 trade.orders = []
1317
1318 with mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
1319 trade.update_order(order_mock, 7)
1320 order_mock.cancel.assert_called()
1321 new_order_mock.run.assert_called()
1322 prepare_order.assert_called_with(compute_value="default")
1323 self.assertRegex(stdout_mock.getvalue(), "tick 7, fallbacking to market value")
1324 self.assertRegex(stdout_mock.getvalue(), "tick 7, market value, cancelling and adjusting to")
1325 self.assertEqual(1, len(trade.orders))
1326
1327 order_mock.reset_mock()
1328 new_order_mock.reset_mock()
1329 trade.orders = []
1330
1331 for i in [10, 13, 16]:
1332 with self.subTest(tick=i), mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
1333 trade.update_order(order_mock, i)
1334 order_mock.cancel.assert_called()
1335 new_order_mock.run.assert_called()
1336 prepare_order.assert_called_with(compute_value="default")
1337 self.assertNotRegex(stdout_mock.getvalue(), "tick {}, fallbacking to market value".format(i))
1338 self.assertRegex(stdout_mock.getvalue(), "tick {}, market value, cancelling and adjusting to".format(i))
1339 self.assertEqual(1, len(trade.orders))
1340
1341 order_mock.reset_mock()
1342 new_order_mock.reset_mock()
1343 trade.orders = []
1344
1345 for i in [8, 9, 11, 12]:
1346 with self.subTest(tick=i), mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
1347 trade.update_order(order_mock, i)
1348 order_mock.cancel.assert_not_called()
1349 new_order_mock.run.assert_not_called()
1350 self.assertEqual("", stdout_mock.getvalue())
1351 self.assertEqual(0, len(trade.orders))
1352
1353 order_mock.reset_mock()
1354 new_order_mock.reset_mock()
1355 trade.orders = []
954 1356
955 @unittest.skip("TODO")
956 def test_update_order(self):
957 pass
958 1357
959 @mock.patch('sys.stdout', new_callable=StringIO) 1358 @mock.patch('sys.stdout', new_callable=StringIO)
960 def test_print_with_order(self, mock_stdout): 1359 def test_print_with_order(self, mock_stdout):
@@ -987,6 +1386,7 @@ class TradeTest(WebMockTestCase):
987 1386
988 self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(trade)) 1387 self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(trade))
989 1388
1389@unittest.skipUnless("acceptance" in limits, "Acceptance skipped")
990class AcceptanceTest(WebMockTestCase): 1390class AcceptanceTest(WebMockTestCase):
991 @unittest.expectedFailure 1391 @unittest.expectedFailure
992 def test_success_sell_only_necessary(self): 1392 def test_success_sell_only_necessary(self):