diff options
author | Ismaël Bouya <ismael.bouya@normalesup.org> | 2018-02-11 22:40:30 +0100 |
---|---|---|
committer | Ismaël Bouya <ismael.bouya@normalesup.org> | 2018-02-11 22:40:30 +0100 |
commit | 5a72ded790f8b5e7c9b38a3cc91c12fbfb6cb97a (patch) | |
tree | e12aa9ec9a5c543442aa512ee0d485ccf9f02906 | |
parent | 1aa7d4fa2ec3c2b3268bef31a666ca6e1aaa6563 (diff) | |
download | Trader-5a72ded790f8b5e7c9b38a3cc91c12fbfb6cb97a.tar.gz Trader-5a72ded790f8b5e7c9b38a3cc91c12fbfb6cb97a.tar.zst Trader-5a72ded790f8b5e7c9b38a3cc91c12fbfb6cb97a.zip |
Add missing tests
-rw-r--r-- | helper.py | 39 | ||||
-rw-r--r-- | portfolio.py | 55 | ||||
-rw-r--r-- | test.py | 511 |
3 files changed, 520 insertions, 85 deletions
@@ -115,16 +115,33 @@ def print_orders(market, base_currency="BTC"): | |||
115 | print(balance) | 115 | print(balance) |
116 | TradeStore.print_all_with_order() | 116 | TradeStore.print_all_with_order() |
117 | 117 | ||
118 | def make_orders(market, base_currency="BTC"): | 118 | def process_sell_needed__1_sell(market, base_currency="BTC", debug=False): |
119 | prepare_trades(market, base_currency=base_currency) | 119 | prepare_trades(market, base_currency=base_currency, debug=debug) |
120 | for trade in TradeStore.all: | 120 | TradeStore.prepare_orders(compute_value="average", only="dispose") |
121 | print(trade) | 121 | print("------------------") |
122 | for order in trade.orders: | 122 | for currency, balance in BalanceStore.all.items(): |
123 | print("\t", order, sep="") | 123 | print(balance) |
124 | order.run() | 124 | print("------------------") |
125 | TradeStore.print_all_with_order() | ||
126 | print("------------------") | ||
127 | TradeStore.run_orders() | ||
128 | follow_orders() | ||
129 | |||
130 | def process_sell_needed__2_sell(market, base_currency="BTC", debug=False): | ||
131 | update_trades(market, base_currency=base_currency, debug=debug, only="acquire") | ||
132 | TradeStore.prepare_orders(compute_value="average", only="acquire") | ||
133 | print("------------------") | ||
134 | for currency, balance in BalanceStore.all.items(): | ||
135 | print(balance) | ||
136 | print("------------------") | ||
137 | TradeStore.print_all_with_order() | ||
138 | print("------------------") | ||
139 | move_balances(market, debug=debug) | ||
140 | TradeStore.run_orders() | ||
141 | follow_orders() | ||
125 | 142 | ||
126 | def process_sell_all_sell(market, base_currency="BTC", debug=False): | 143 | def process_sell_all__1_all_sell(market, base_currency="BTC", debug=False): |
127 | prepare_trades_to_sell_all(market, debug=debug) | 144 | prepare_trades_to_sell_all(market, base_currency=base_currency, debug=debug) |
128 | TradeStore.prepare_orders(compute_value="average") | 145 | TradeStore.prepare_orders(compute_value="average") |
129 | print("------------------") | 146 | print("------------------") |
130 | for currency, balance in BalanceStore.all.items(): | 147 | for currency, balance in BalanceStore.all.items(): |
@@ -135,8 +152,8 @@ def process_sell_all_sell(market, base_currency="BTC", debug=False): | |||
135 | TradeStore.run_orders() | 152 | TradeStore.run_orders() |
136 | follow_orders() | 153 | follow_orders() |
137 | 154 | ||
138 | def process_sell_all_buy(market, base_currency="BTC", debug=False): | 155 | def process_sell_all__2_all_buy(market, base_currency="BTC", debug=False): |
139 | prepare_trades(market, debug=debug) | 156 | prepare_trades(market, base_currency=base_currency, debug=debug) |
140 | TradeStore.prepare_orders() | 157 | TradeStore.prepare_orders() |
141 | print("------------------") | 158 | print("------------------") |
142 | for currency, balance in BalanceStore.all.items(): | 159 | for currency, balance in BalanceStore.all.items(): |
diff --git a/portfolio.py b/portfolio.py index b629966..e98689e 100644 --- a/portfolio.py +++ b/portfolio.py | |||
@@ -1,4 +1,5 @@ | |||
1 | import time | 1 | import time |
2 | from datetime import datetime | ||
2 | from decimal import Decimal as D, ROUND_DOWN | 3 | from decimal import Decimal as D, ROUND_DOWN |
3 | # Put your poloniex api key in market.py | 4 | # Put your poloniex api key in market.py |
4 | from json import JSONDecodeError | 5 | from json import JSONDecodeError |
@@ -272,7 +273,7 @@ class Trade: | |||
272 | if self.base_currency == self.currency: | 273 | if self.base_currency == self.currency: |
273 | return None | 274 | return None |
274 | 275 | ||
275 | if self.value_from < self.value_to: | 276 | if abs(self.value_from) < abs(self.value_to): |
276 | return "acquire" | 277 | return "acquire" |
277 | else: | 278 | else: |
278 | return "dispose" | 279 | return "dispose" |
@@ -301,19 +302,16 @@ class Trade: | |||
301 | if tick in [0, 1, 3, 4, 6]: | 302 | if tick in [0, 1, 3, 4, 6]: |
302 | print("{}, tick {}, waiting".format(order, tick)) | 303 | print("{}, tick {}, waiting".format(order, tick)) |
303 | elif tick == 2: | 304 | elif tick == 2: |
304 | self.prepare_order(compute_value=lambda x, y: (x[y] + x["average"]) / 2) | 305 | new_order = self.prepare_order(compute_value=lambda x, y: (x[y] + x["average"]) / 2) |
305 | new_order = self.orders[-1] | ||
306 | print("{}, tick {}, cancelling and adjusting to {}".format(order, tick, new_order)) | 306 | print("{}, tick {}, cancelling and adjusting to {}".format(order, tick, new_order)) |
307 | elif tick ==5: | 307 | elif tick ==5: |
308 | self.prepare_order(compute_value=lambda x, y: (x[y]*2 + x["average"]) / 3) | 308 | new_order = self.prepare_order(compute_value=lambda x, y: (x[y]*2 + x["average"]) / 3) |
309 | new_order = self.orders[-1] | ||
310 | print("{}, tick {}, cancelling and adjusting to {}".format(order, tick, new_order)) | 309 | print("{}, tick {}, cancelling and adjusting to {}".format(order, tick, new_order)) |
311 | elif tick >= 7: | 310 | elif tick >= 7: |
312 | if tick == 7: | 311 | if tick == 7: |
313 | print("{}, tick {}, fallbacking to market value".format(order, tick)) | 312 | print("{}, tick {}, fallbacking to market value".format(order, tick)) |
314 | if (tick - 7) % 3 == 0: | 313 | if (tick - 7) % 3 == 0: |
315 | self.prepare_order(compute_value="default") | 314 | new_order = self.prepare_order(compute_value="default") |
316 | new_order = self.orders[-1] | ||
317 | print("{}, tick {}, market value, cancelling and adjusting to {}".format(order, tick, new_order)) | 315 | print("{}, tick {}, market value, cancelling and adjusting to {}".format(order, tick, new_order)) |
318 | 316 | ||
319 | if new_order is not None: | 317 | if new_order is not None: |
@@ -322,7 +320,7 @@ class Trade: | |||
322 | 320 | ||
323 | def prepare_order(self, compute_value="default"): | 321 | def prepare_order(self, compute_value="default"): |
324 | if self.action is None: | 322 | if self.action is None: |
325 | return | 323 | return None |
326 | ticker = h.get_ticker(self.currency, self.base_currency, self.market) | 324 | ticker = h.get_ticker(self.currency, self.base_currency, self.market) |
327 | inverted = ticker["inverted"] | 325 | inverted = ticker["inverted"] |
328 | if inverted: | 326 | if inverted: |
@@ -387,11 +385,13 @@ class Trade: | |||
387 | 385 | ||
388 | if delta <= 0: | 386 | if delta <= 0: |
389 | print("Less to do than already filled: {}".format(delta)) | 387 | print("Less to do than already filled: {}".format(delta)) |
390 | return | 388 | return None |
391 | 389 | ||
392 | self.orders.append(Order(self.order_action(inverted), | 390 | order = Order(self.order_action(inverted), |
393 | delta, rate, base_currency, self.trade_type, | 391 | delta, rate, base_currency, self.trade_type, |
394 | self.market, self, close_if_possible=close_if_possible)) | 392 | self.market, self, close_if_possible=close_if_possible) |
393 | self.orders.append(order) | ||
394 | return order | ||
395 | 395 | ||
396 | def __repr__(self): | 396 | def __repr__(self): |
397 | return "Trade({} -> {} in {}, {})".format( | 397 | return "Trade({} -> {} in {}, {})".format( |
@@ -419,6 +419,8 @@ class Order: | |||
419 | self.status = "pending" | 419 | self.status = "pending" |
420 | self.trade = trade | 420 | self.trade = trade |
421 | self.close_if_possible = close_if_possible | 421 | self.close_if_possible = close_if_possible |
422 | self.id = None | ||
423 | self.fetch_cache_timestamp = None | ||
422 | 424 | ||
423 | def __repr__(self): | 425 | def __repr__(self): |
424 | return "Order({} {} {} at {} {} [{}]{})".format( | 426 | return "Order({} {} {} at {} {} [{}]{})".format( |
@@ -439,6 +441,10 @@ class Order: | |||
439 | return "margin" | 441 | return "margin" |
440 | 442 | ||
441 | @property | 443 | @property |
444 | def open(self): | ||
445 | return self.status == "open" | ||
446 | |||
447 | @property | ||
442 | def pending(self): | 448 | def pending(self): |
443 | return self.status == "pending" | 449 | return self.status == "pending" |
444 | 450 | ||
@@ -446,10 +452,6 @@ class Order: | |||
446 | def finished(self): | 452 | def finished(self): |
447 | return self.status == "closed" or self.status == "canceled" or self.status == "error" | 453 | return self.status == "closed" or self.status == "canceled" or self.status == "error" |
448 | 454 | ||
449 | @property | ||
450 | def id(self): | ||
451 | return self.results[0]["id"] | ||
452 | |||
453 | def run(self): | 455 | def run(self): |
454 | symbol = "{}/{}".format(self.amount.currency, self.base_currency) | 456 | symbol = "{}/{}".format(self.amount.currency, self.base_currency) |
455 | amount = round(self.amount, self.market.order_precision(symbol)).value | 457 | amount = round(self.amount, self.market.order_precision(symbol)).value |
@@ -457,26 +459,27 @@ class Order: | |||
457 | if TradeStore.debug: | 459 | if TradeStore.debug: |
458 | print("market.create_order('{}', 'limit', '{}', {}, price={}, account={})".format( | 460 | print("market.create_order('{}', 'limit', '{}', {}, price={}, account={})".format( |
459 | symbol, self.action, amount, self.rate, self.account)) | 461 | symbol, self.action, amount, self.rate, self.account)) |
460 | self.status = "open" | ||
461 | self.results.append({"debug": True, "id": -1}) | 462 | self.results.append({"debug": True, "id": -1}) |
462 | else: | 463 | else: |
463 | try: | 464 | try: |
464 | self.results.append(self.market.create_order(symbol, 'limit', self.action, amount, price=self.rate, account=self.account)) | 465 | self.results.append(self.market.create_order(symbol, 'limit', self.action, amount, price=self.rate, account=self.account)) |
465 | self.status = "open" | ||
466 | except Exception as e: | 466 | except Exception as e: |
467 | self.status = "error" | 467 | self.status = "error" |
468 | print("error when running market.create_order('{}', 'limit', '{}', {}, price={}, account={})".format( | 468 | print("error when running market.create_order('{}', 'limit', '{}', {}, price={}, account={})".format( |
469 | symbol, self.action, amount, self.rate, self.account)) | 469 | symbol, self.action, amount, self.rate, self.account)) |
470 | self.error_message = str("{}: {}".format(e.__class__.__name__, e)) | 470 | self.error_message = str("{}: {}".format(e.__class__.__name__, e)) |
471 | print(self.error_message) | 471 | print(self.error_message) |
472 | return | ||
473 | self.id = self.results[0]["id"] | ||
474 | self.status = "open" | ||
472 | 475 | ||
473 | def get_status(self): | 476 | def get_status(self): |
474 | if TradeStore.debug: | 477 | if TradeStore.debug: |
475 | return self.status | 478 | return self.status |
476 | # other states are "closed" and "canceled" | 479 | # other states are "closed" and "canceled" |
477 | if self.status == "open": | 480 | if not self.finished: |
478 | self.fetch() | 481 | self.fetch() |
479 | if self.status != "open": | 482 | if self.finished: |
480 | self.mark_finished_order() | 483 | self.mark_finished_order() |
481 | return self.status | 484 | return self.status |
482 | 485 | ||
@@ -487,15 +490,15 @@ class Order: | |||
487 | if self.trade_type == "short" and self.action == "buy" and self.close_if_possible: | 490 | if self.trade_type == "short" and self.action == "buy" and self.close_if_possible: |
488 | self.market.close_margin_position(self.amount.currency, self.base_currency) | 491 | self.market.close_margin_position(self.amount.currency, self.base_currency) |
489 | 492 | ||
490 | fetch_cache_timestamp = None | ||
491 | def fetch(self, force=False): | 493 | def fetch(self, force=False): |
492 | if TradeStore.debug or (not force and self.fetch_cache_timestamp is not None | 494 | if TradeStore.debug or (not force and self.fetch_cache_timestamp is not None |
493 | and time.time() - self.fetch_cache_timestamp < 10): | 495 | and time.time() - self.fetch_cache_timestamp < 10): |
494 | return | 496 | return |
495 | self.fetch_cache_timestamp = time.time() | 497 | self.fetch_cache_timestamp = time.time() |
496 | 498 | ||
497 | self.results.append(self.market.fetch_order(self.id)) | 499 | result = self.market.fetch_order(self.id) |
498 | result = self.results[-1] | 500 | self.results.append(result) |
501 | |||
499 | self.status = result["status"] | 502 | self.status = result["status"] |
500 | # Time at which the order started | 503 | # Time at which the order started |
501 | self.timestamp = result["datetime"] | 504 | self.timestamp = result["datetime"] |
@@ -503,11 +506,9 @@ class Order: | |||
503 | 506 | ||
504 | # FIXME: consider open order with dust remaining as closed | 507 | # FIXME: consider open order with dust remaining as closed |
505 | 508 | ||
506 | @property | ||
507 | def dust_amount_remaining(self): | 509 | def dust_amount_remaining(self): |
508 | return self.remaining_amount < 0.001 | 510 | return self.remaining_amount() < Amount(self.amount.currency, D("0.001")) |
509 | 511 | ||
510 | @property | ||
511 | def remaining_amount(self): | 512 | def remaining_amount(self): |
512 | if self.status == "open": | 513 | if self.status == "open": |
513 | self.fetch() | 514 | self.fetch() |
@@ -536,7 +537,7 @@ class Order: | |||
536 | if TradeStore.debug: | 537 | if TradeStore.debug: |
537 | self.status = "canceled" | 538 | self.status = "canceled" |
538 | return | 539 | return |
539 | self.market.cancel_order(self.result['id']) | 540 | self.market.cancel_order(self.id) |
540 | self.fetch() | 541 | self.fetch() |
541 | 542 | ||
542 | class Mouvement: | 543 | class Mouvement: |
@@ -552,6 +553,6 @@ class Mouvement: | |||
552 | # rate * total = total_in_base | 553 | # rate * total = total_in_base |
553 | self.total_in_base = Amount(base_currency, hash_["total"]) | 554 | self.total_in_base = Amount(base_currency, hash_["total"]) |
554 | 555 | ||
555 | if __name__ == '__main__': | 556 | if __name__ == '__main__': # pragma: no cover |
556 | from market import market | 557 | from market import market |
557 | h.print_orders(market) | 558 | h.print_orders(market) |
@@ -43,7 +43,6 @@ class WebMockTestCase(unittest.TestCase): | |||
43 | for patcher in self.patchers: | 43 | for patcher in self.patchers: |
44 | patcher.start() | 44 | patcher.start() |
45 | 45 | ||
46 | |||
47 | def tearDown(self): | 46 | def tearDown(self): |
48 | for patcher in self.patchers: | 47 | for patcher in self.patchers: |
49 | patcher.stop() | 48 | patcher.stop() |
@@ -702,6 +701,68 @@ class HelperTest(WebMockTestCase): | |||
702 | else: | 701 | else: |
703 | self.assertEqual("", stdout_mock.getvalue()) | 702 | self.assertEqual("", stdout_mock.getvalue()) |
704 | 703 | ||
704 | @mock.patch.object(portfolio.BalanceStore, "fetch_balances") | ||
705 | def test_move_balance(self, fetch_balances): | ||
706 | for debug in [True, False]: | ||
707 | with self.subTest(debug=debug),\ | ||
708 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | ||
709 | value_from = portfolio.Amount("BTC", "1.0") | ||
710 | value_from.linked_to = portfolio.Amount("ETH", "10.0") | ||
711 | value_to = portfolio.Amount("BTC", "10.0") | ||
712 | trade1 = portfolio.Trade(value_from, value_to, "ETH") | ||
713 | |||
714 | value_from = portfolio.Amount("BTC", "0.0") | ||
715 | value_from.linked_to = portfolio.Amount("ETH", "0.0") | ||
716 | value_to = portfolio.Amount("BTC", "-3.0") | ||
717 | trade2 = portfolio.Trade(value_from, value_to, "ETH") | ||
718 | |||
719 | value_from = portfolio.Amount("USDT", "0.0") | ||
720 | value_from.linked_to = portfolio.Amount("XVG", "0.0") | ||
721 | value_to = portfolio.Amount("USDT", "-50.0") | ||
722 | trade3 = portfolio.Trade(value_from, value_to, "XVG") | ||
723 | |||
724 | portfolio.TradeStore.all = [trade1, trade2, trade3] | ||
725 | balance1 = portfolio.Balance("BTC", { "margin_free": "0" }) | ||
726 | balance2 = portfolio.Balance("USDT", { "margin_free": "100" }) | ||
727 | portfolio.BalanceStore.all = {"BTC": balance1, "USDT": balance2} | ||
728 | |||
729 | market = mock.Mock() | ||
730 | |||
731 | helper.move_balances(market, debug=debug) | ||
732 | |||
733 | fetch_balances.assert_called_with(market) | ||
734 | if debug: | ||
735 | self.assertRegex(stdout_mock.getvalue(), "market.transfer_balance") | ||
736 | else: | ||
737 | market.transfer_balance.assert_any_call("BTC", 3, "exchange", "margin") | ||
738 | market.transfer_balance.assert_any_call("USDT", 50, "margin", "exchange") | ||
739 | |||
740 | @mock.patch.object(helper, "prepare_trades") | ||
741 | @mock.patch.object(portfolio.TradeStore, "prepare_orders") | ||
742 | @mock.patch.object(portfolio.TradeStore, "print_all_with_order") | ||
743 | @mock.patch('sys.stdout', new_callable=StringIO) | ||
744 | def test_print_orders(self, stdout_mock, print_all_with_order, prepare_orders, prepare_trades): | ||
745 | market = mock.Mock() | ||
746 | portfolio.BalanceStore.all = { | ||
747 | "BTC": portfolio.Balance("BTC", { | ||
748 | "total": "0.65", | ||
749 | "exchange_total":"0.65", | ||
750 | "exchange_free": "0.35", | ||
751 | "exchange_used": "0.30"}), | ||
752 | "ETH": portfolio.Balance("ETH", { | ||
753 | "total": 3, | ||
754 | "exchange_total": 3, | ||
755 | "exchange_free": 3, | ||
756 | "exchange_used": 0}), | ||
757 | } | ||
758 | helper.print_orders(market) | ||
759 | prepare_trades.assert_called_with(market, base_currency="BTC", | ||
760 | compute_value="average") | ||
761 | prepare_orders.assert_called_with(compute_value="average") | ||
762 | print_all_with_order.assert_called() | ||
763 | self.assertRegex(stdout_mock.getvalue(), "Balance") | ||
764 | |||
765 | |||
705 | @unittest.skipUnless("unit" in limits, "Unit skipped") | 766 | @unittest.skipUnless("unit" in limits, "Unit skipped") |
706 | class TradeStoreTest(WebMockTestCase): | 767 | class TradeStoreTest(WebMockTestCase): |
707 | @mock.patch.object(portfolio.BalanceStore, "currencies") | 768 | @mock.patch.object(portfolio.BalanceStore, "currencies") |
@@ -1068,7 +1129,7 @@ class TradeTest(WebMockTestCase): | |||
1068 | value_to = portfolio.Amount("BTC", "-1.0") | 1129 | value_to = portfolio.Amount("BTC", "-1.0") |
1069 | trade = portfolio.Trade(value_from, value_to, "ETH") | 1130 | trade = portfolio.Trade(value_from, value_to, "ETH") |
1070 | 1131 | ||
1071 | self.assertEqual("dispose", trade.action) | 1132 | self.assertEqual("acquire", trade.action) |
1072 | 1133 | ||
1073 | def test_order_action(self): | 1134 | def test_order_action(self): |
1074 | value_from = portfolio.Amount("BTC", "0.5") | 1135 | value_from = portfolio.Amount("BTC", "0.5") |
@@ -1275,9 +1336,7 @@ class TradeTest(WebMockTestCase): | |||
1275 | value_from.linked_to = portfolio.Amount("ETH", "10.0") | 1336 | value_from.linked_to = portfolio.Amount("ETH", "10.0") |
1276 | value_to = portfolio.Amount("BTC", "1.0") | 1337 | value_to = portfolio.Amount("BTC", "1.0") |
1277 | trade = portfolio.Trade(value_from, value_to, "ETH") | 1338 | trade = portfolio.Trade(value_from, value_to, "ETH") |
1278 | def _prepare_order(compute_value=None): | 1339 | prepare_order.return_value = new_order_mock |
1279 | trade.orders.append(new_order_mock) | ||
1280 | prepare_order.side_effect = _prepare_order | ||
1281 | 1340 | ||
1282 | for i in [0, 1, 3, 4, 6]: | 1341 | for i in [0, 1, 3, 4, 6]: |
1283 | with self.subTest(tick=i), mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | 1342 | with self.subTest(tick=i), mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: |
@@ -1285,7 +1344,6 @@ class TradeTest(WebMockTestCase): | |||
1285 | order_mock.cancel.assert_not_called() | 1344 | order_mock.cancel.assert_not_called() |
1286 | new_order_mock.run.assert_not_called() | 1345 | new_order_mock.run.assert_not_called() |
1287 | self.assertRegex(stdout_mock.getvalue(), "tick {}, waiting".format(i)) | 1346 | self.assertRegex(stdout_mock.getvalue(), "tick {}, waiting".format(i)) |
1288 | self.assertEqual(0, len(trade.orders)) | ||
1289 | 1347 | ||
1290 | order_mock.reset_mock() | 1348 | order_mock.reset_mock() |
1291 | new_order_mock.reset_mock() | 1349 | new_order_mock.reset_mock() |
@@ -1297,7 +1355,6 @@ class TradeTest(WebMockTestCase): | |||
1297 | new_order_mock.run.assert_called() | 1355 | new_order_mock.run.assert_called() |
1298 | prepare_order.assert_called() | 1356 | prepare_order.assert_called() |
1299 | self.assertRegex(stdout_mock.getvalue(), "tick 2, cancelling and adjusting") | 1357 | self.assertRegex(stdout_mock.getvalue(), "tick 2, cancelling and adjusting") |
1300 | self.assertEqual(1, len(trade.orders)) | ||
1301 | 1358 | ||
1302 | order_mock.reset_mock() | 1359 | order_mock.reset_mock() |
1303 | new_order_mock.reset_mock() | 1360 | new_order_mock.reset_mock() |
@@ -1309,7 +1366,6 @@ class TradeTest(WebMockTestCase): | |||
1309 | new_order_mock.run.assert_called() | 1366 | new_order_mock.run.assert_called() |
1310 | prepare_order.assert_called() | 1367 | prepare_order.assert_called() |
1311 | self.assertRegex(stdout_mock.getvalue(), "tick 5, cancelling and adjusting") | 1368 | self.assertRegex(stdout_mock.getvalue(), "tick 5, cancelling and adjusting") |
1312 | self.assertEqual(1, len(trade.orders)) | ||
1313 | 1369 | ||
1314 | order_mock.reset_mock() | 1370 | order_mock.reset_mock() |
1315 | new_order_mock.reset_mock() | 1371 | new_order_mock.reset_mock() |
@@ -1322,7 +1378,6 @@ class TradeTest(WebMockTestCase): | |||
1322 | prepare_order.assert_called_with(compute_value="default") | 1378 | prepare_order.assert_called_with(compute_value="default") |
1323 | self.assertRegex(stdout_mock.getvalue(), "tick 7, fallbacking to market value") | 1379 | 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") | 1380 | self.assertRegex(stdout_mock.getvalue(), "tick 7, market value, cancelling and adjusting to") |
1325 | self.assertEqual(1, len(trade.orders)) | ||
1326 | 1381 | ||
1327 | order_mock.reset_mock() | 1382 | order_mock.reset_mock() |
1328 | new_order_mock.reset_mock() | 1383 | new_order_mock.reset_mock() |
@@ -1336,7 +1391,6 @@ class TradeTest(WebMockTestCase): | |||
1336 | prepare_order.assert_called_with(compute_value="default") | 1391 | prepare_order.assert_called_with(compute_value="default") |
1337 | self.assertNotRegex(stdout_mock.getvalue(), "tick {}, fallbacking to market value".format(i)) | 1392 | 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)) | 1393 | self.assertRegex(stdout_mock.getvalue(), "tick {}, market value, cancelling and adjusting to".format(i)) |
1339 | self.assertEqual(1, len(trade.orders)) | ||
1340 | 1394 | ||
1341 | order_mock.reset_mock() | 1395 | order_mock.reset_mock() |
1342 | new_order_mock.reset_mock() | 1396 | new_order_mock.reset_mock() |
@@ -1348,7 +1402,6 @@ class TradeTest(WebMockTestCase): | |||
1348 | order_mock.cancel.assert_not_called() | 1402 | order_mock.cancel.assert_not_called() |
1349 | new_order_mock.run.assert_not_called() | 1403 | new_order_mock.run.assert_not_called() |
1350 | self.assertEqual("", stdout_mock.getvalue()) | 1404 | self.assertEqual("", stdout_mock.getvalue()) |
1351 | self.assertEqual(0, len(trade.orders)) | ||
1352 | 1405 | ||
1353 | order_mock.reset_mock() | 1406 | order_mock.reset_mock() |
1354 | new_order_mock.reset_mock() | 1407 | new_order_mock.reset_mock() |
@@ -1386,6 +1439,355 @@ class TradeTest(WebMockTestCase): | |||
1386 | 1439 | ||
1387 | self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(trade)) | 1440 | self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(trade)) |
1388 | 1441 | ||
1442 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
1443 | class OrderTest(WebMockTestCase): | ||
1444 | def test_values(self): | ||
1445 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1446 | D("0.1"), "BTC", "long", "market", "trade") | ||
1447 | self.assertEqual("buy", order.action) | ||
1448 | self.assertEqual(10, order.amount.value) | ||
1449 | self.assertEqual("ETH", order.amount.currency) | ||
1450 | self.assertEqual(D("0.1"), order.rate) | ||
1451 | self.assertEqual("BTC", order.base_currency) | ||
1452 | self.assertEqual("market", order.market) | ||
1453 | self.assertEqual("long", order.trade_type) | ||
1454 | self.assertEqual("pending", order.status) | ||
1455 | self.assertEqual("trade", order.trade) | ||
1456 | self.assertIsNone(order.id) | ||
1457 | self.assertFalse(order.close_if_possible) | ||
1458 | |||
1459 | def test__repr(self): | ||
1460 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1461 | D("0.1"), "BTC", "long", "market", "trade") | ||
1462 | self.assertEqual("Order(buy long 10.00000000 ETH at 0.1 BTC [pending])", repr(order)) | ||
1463 | |||
1464 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1465 | D("0.1"), "BTC", "long", "market", "trade", | ||
1466 | close_if_possible=True) | ||
1467 | self.assertEqual("Order(buy long 10.00000000 ETH at 0.1 BTC [pending] ✂)", repr(order)) | ||
1468 | |||
1469 | def test_account(self): | ||
1470 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1471 | D("0.1"), "BTC", "long", "market", "trade") | ||
1472 | self.assertEqual("exchange", order.account) | ||
1473 | |||
1474 | order = portfolio.Order("sell", portfolio.Amount("ETH", 10), | ||
1475 | D("0.1"), "BTC", "short", "market", "trade") | ||
1476 | self.assertEqual("margin", order.account) | ||
1477 | |||
1478 | def test_pending(self): | ||
1479 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1480 | D("0.1"), "BTC", "long", "market", "trade") | ||
1481 | self.assertTrue(order.pending) | ||
1482 | order.status = "open" | ||
1483 | self.assertFalse(order.pending) | ||
1484 | |||
1485 | def test_open(self): | ||
1486 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1487 | D("0.1"), "BTC", "long", "market", "trade") | ||
1488 | self.assertFalse(order.open) | ||
1489 | order.status = "open" | ||
1490 | self.assertTrue(order.open) | ||
1491 | |||
1492 | def test_finished(self): | ||
1493 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1494 | D("0.1"), "BTC", "long", "market", "trade") | ||
1495 | self.assertFalse(order.finished) | ||
1496 | order.status = "closed" | ||
1497 | self.assertTrue(order.finished) | ||
1498 | order.status = "canceled" | ||
1499 | self.assertTrue(order.finished) | ||
1500 | order.status = "error" | ||
1501 | self.assertTrue(order.finished) | ||
1502 | |||
1503 | @mock.patch.object(portfolio.Order, "fetch") | ||
1504 | def test_cancel(self, fetch): | ||
1505 | market = mock.Mock() | ||
1506 | portfolio.TradeStore.debug = True | ||
1507 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1508 | D("0.1"), "BTC", "long", market, "trade") | ||
1509 | order.status = "open" | ||
1510 | |||
1511 | order.cancel() | ||
1512 | market.cancel_order.assert_not_called() | ||
1513 | self.assertEqual("canceled", order.status) | ||
1514 | |||
1515 | portfolio.TradeStore.debug = False | ||
1516 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1517 | D("0.1"), "BTC", "long", market, "trade") | ||
1518 | order.status = "open" | ||
1519 | order.id = 42 | ||
1520 | |||
1521 | order.cancel() | ||
1522 | market.cancel_order.assert_called_with(42) | ||
1523 | fetch.assert_called_once() | ||
1524 | |||
1525 | def test_dust_amount_remaining(self): | ||
1526 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1527 | D("0.1"), "BTC", "long", "market", "trade") | ||
1528 | order.remaining_amount = mock.Mock(return_value=portfolio.Amount("ETH", 1)) | ||
1529 | self.assertFalse(order.dust_amount_remaining()) | ||
1530 | |||
1531 | order.remaining_amount = mock.Mock(return_value=portfolio.Amount("ETH", D("0.0001"))) | ||
1532 | self.assertTrue(order.dust_amount_remaining()) | ||
1533 | |||
1534 | @mock.patch.object(portfolio.Order, "fetch") | ||
1535 | @mock.patch.object(portfolio.Order, "filled_amount", return_value=portfolio.Amount("ETH", 1)) | ||
1536 | def test_remaining_amount(self, filled_amount, fetch): | ||
1537 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1538 | D("0.1"), "BTC", "long", "market", "trade") | ||
1539 | |||
1540 | self.assertEqual(9, order.remaining_amount().value) | ||
1541 | order.fetch.assert_not_called() | ||
1542 | |||
1543 | order.status = "open" | ||
1544 | self.assertEqual(9, order.remaining_amount().value) | ||
1545 | fetch.assert_called_once() | ||
1546 | |||
1547 | @mock.patch.object(portfolio.Order, "fetch") | ||
1548 | def test_filled_amount(self, fetch): | ||
1549 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1550 | D("0.1"), "BTC", "long", "market", "trade") | ||
1551 | order.mouvements.append(portfolio.Mouvement("ETH", "BTC", { | ||
1552 | "id": 42, "type": "buy", "fee": "0.0015", | ||
1553 | "date": "2017-12-30 12:00:12", "rate": "0.1", | ||
1554 | "amount": "3", "total": "0.3" | ||
1555 | })) | ||
1556 | order.mouvements.append(portfolio.Mouvement("ETH", "BTC", { | ||
1557 | "id": 43, "type": "buy", "fee": "0.0015", | ||
1558 | "date": "2017-12-30 13:00:12", "rate": "0.2", | ||
1559 | "amount": "2", "total": "0.4" | ||
1560 | })) | ||
1561 | self.assertEqual(portfolio.Amount("ETH", 5), order.filled_amount()) | ||
1562 | fetch.assert_not_called() | ||
1563 | order.status = "open" | ||
1564 | self.assertEqual(portfolio.Amount("ETH", 5), order.filled_amount(in_base_currency=False)) | ||
1565 | fetch.assert_called_once() | ||
1566 | self.assertEqual(portfolio.Amount("BTC", "0.7"), order.filled_amount(in_base_currency=True)) | ||
1567 | |||
1568 | def test_fetch_mouvements(self): | ||
1569 | market = mock.Mock() | ||
1570 | market.privatePostReturnOrderTrades.return_value = [ | ||
1571 | { | ||
1572 | "id": 42, "type": "buy", "fee": "0.0015", | ||
1573 | "date": "2017-12-30 12:00:12", "rate": "0.1", | ||
1574 | "amount": "3", "total": "0.3" | ||
1575 | }, | ||
1576 | { | ||
1577 | "id": 43, "type": "buy", "fee": "0.0015", | ||
1578 | "date": "2017-12-30 13:00:12", "rate": "0.2", | ||
1579 | "amount": "2", "total": "0.4" | ||
1580 | } | ||
1581 | ] | ||
1582 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1583 | D("0.1"), "BTC", "long", market, "trade") | ||
1584 | order.id = 12 | ||
1585 | order.mouvements = ["Foo", "Bar", "Baz"] | ||
1586 | |||
1587 | order.fetch_mouvements() | ||
1588 | |||
1589 | market.privatePostReturnOrderTrades.assert_called_with({"orderNumber": 12}) | ||
1590 | self.assertEqual(2, len(order.mouvements)) | ||
1591 | self.assertEqual(42, order.mouvements[0].id) | ||
1592 | self.assertEqual(43, order.mouvements[1].id) | ||
1593 | |||
1594 | def test_mark_finished_order(self): | ||
1595 | market = mock.Mock() | ||
1596 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1597 | D("0.1"), "BTC", "short", market, "trade", | ||
1598 | close_if_possible=True) | ||
1599 | order.status = "closed" | ||
1600 | portfolio.TradeStore.debug = False | ||
1601 | |||
1602 | order.mark_finished_order() | ||
1603 | market.close_margin_position.assert_called_with("ETH", "BTC") | ||
1604 | market.close_margin_position.reset_mock() | ||
1605 | |||
1606 | order.status = "open" | ||
1607 | order.mark_finished_order() | ||
1608 | market.close_margin_position.assert_not_called() | ||
1609 | |||
1610 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1611 | D("0.1"), "BTC", "short", market, "trade", | ||
1612 | close_if_possible=False) | ||
1613 | order.status = "closed" | ||
1614 | order.mark_finished_order() | ||
1615 | market.close_margin_position.assert_not_called() | ||
1616 | |||
1617 | order = portfolio.Order("sell", portfolio.Amount("ETH", 10), | ||
1618 | D("0.1"), "BTC", "short", market, "trade", | ||
1619 | close_if_possible=True) | ||
1620 | order.status = "closed" | ||
1621 | order.mark_finished_order() | ||
1622 | market.close_margin_position.assert_not_called() | ||
1623 | |||
1624 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1625 | D("0.1"), "BTC", "long", market, "trade", | ||
1626 | close_if_possible=True) | ||
1627 | order.status = "closed" | ||
1628 | order.mark_finished_order() | ||
1629 | market.close_margin_position.assert_not_called() | ||
1630 | |||
1631 | portfolio.TradeStore.debug = True | ||
1632 | |||
1633 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1634 | D("0.1"), "BTC", "short", market, "trade", | ||
1635 | close_if_possible=True) | ||
1636 | order.status = "closed" | ||
1637 | |||
1638 | order.mark_finished_order() | ||
1639 | market.close_margin_position.assert_not_called() | ||
1640 | |||
1641 | @mock.patch.object(portfolio.Order, "fetch_mouvements") | ||
1642 | def test_fetch(self, fetch_mouvements): | ||
1643 | time = self.time.time() | ||
1644 | with mock.patch.object(portfolio.time, "time") as time_mock: | ||
1645 | market = mock.Mock() | ||
1646 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1647 | D("0.1"), "BTC", "long", market, "trade") | ||
1648 | order.id = 45 | ||
1649 | with self.subTest(debug=True): | ||
1650 | portfolio.TradeStore.debug = True | ||
1651 | order.fetch() | ||
1652 | time_mock.assert_not_called() | ||
1653 | order.fetch(force=True) | ||
1654 | time_mock.assert_not_called() | ||
1655 | market.fetch_order.assert_not_called() | ||
1656 | fetch_mouvements.assert_not_called() | ||
1657 | self.assertIsNone(order.fetch_cache_timestamp) | ||
1658 | |||
1659 | with self.subTest(debug=False): | ||
1660 | portfolio.TradeStore.debug = False | ||
1661 | time_mock.return_value = time | ||
1662 | market.fetch_order.return_value = { | ||
1663 | "status": "foo", | ||
1664 | "datetime": "timestamp" | ||
1665 | } | ||
1666 | order.fetch() | ||
1667 | |||
1668 | market.fetch_order.assert_called_once() | ||
1669 | fetch_mouvements.assert_called_once() | ||
1670 | self.assertEqual("foo", order.status) | ||
1671 | self.assertEqual("timestamp", order.timestamp) | ||
1672 | self.assertEqual(time, order.fetch_cache_timestamp) | ||
1673 | self.assertEqual(1, len(order.results)) | ||
1674 | |||
1675 | market.fetch_order.reset_mock() | ||
1676 | fetch_mouvements.reset_mock() | ||
1677 | |||
1678 | time_mock.return_value = time + 8 | ||
1679 | order.fetch() | ||
1680 | market.fetch_order.assert_not_called() | ||
1681 | fetch_mouvements.assert_not_called() | ||
1682 | |||
1683 | order.fetch(force=True) | ||
1684 | market.fetch_order.assert_called_once() | ||
1685 | fetch_mouvements.assert_called_once() | ||
1686 | |||
1687 | market.fetch_order.reset_mock() | ||
1688 | fetch_mouvements.reset_mock() | ||
1689 | |||
1690 | time_mock.return_value = time + 19 | ||
1691 | order.fetch() | ||
1692 | market.fetch_order.assert_called_once() | ||
1693 | fetch_mouvements.assert_called_once() | ||
1694 | |||
1695 | @mock.patch.object(portfolio.Order, "fetch") | ||
1696 | @mock.patch.object(portfolio.Order, "mark_finished_order") | ||
1697 | def test_get_status(self, mark_finished_order, fetch): | ||
1698 | with self.subTest(debug=True): | ||
1699 | portfolio.TradeStore.debug = True | ||
1700 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1701 | D("0.1"), "BTC", "long", "market", "trade") | ||
1702 | self.assertEqual("pending", order.get_status()) | ||
1703 | fetch.assert_not_called() | ||
1704 | |||
1705 | with self.subTest(debug=False, finished=False): | ||
1706 | portfolio.TradeStore.debug = False | ||
1707 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1708 | D("0.1"), "BTC", "long", "market", "trade") | ||
1709 | def _fetch(order): | ||
1710 | def update_status(): | ||
1711 | order.status = "open" | ||
1712 | return update_status | ||
1713 | fetch.side_effect = _fetch(order) | ||
1714 | self.assertEqual("open", order.get_status()) | ||
1715 | mark_finished_order.assert_not_called() | ||
1716 | fetch.assert_called_once() | ||
1717 | |||
1718 | mark_finished_order.reset_mock() | ||
1719 | fetch.reset_mock() | ||
1720 | with self.subTest(debug=False, finished=True): | ||
1721 | portfolio.TradeStore.debug = False | ||
1722 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1723 | D("0.1"), "BTC", "long", "market", "trade") | ||
1724 | def _fetch(order): | ||
1725 | def update_status(): | ||
1726 | order.status = "closed" | ||
1727 | return update_status | ||
1728 | fetch.side_effect = _fetch(order) | ||
1729 | self.assertEqual("closed", order.get_status()) | ||
1730 | mark_finished_order.assert_called_once() | ||
1731 | fetch.assert_called_once() | ||
1732 | |||
1733 | def test_run(self): | ||
1734 | market = mock.Mock() | ||
1735 | |||
1736 | market.order_precision.return_value = 4 | ||
1737 | with self.subTest(debug=True),\ | ||
1738 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | ||
1739 | portfolio.TradeStore.debug = True | ||
1740 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1741 | D("0.1"), "BTC", "long", market, "trade") | ||
1742 | order.run() | ||
1743 | market.create_order.assert_not_called() | ||
1744 | self.assertEqual("market.create_order('ETH/BTC', 'limit', 'buy', 10.0000, price=0.1, account=exchange)\n", stdout_mock.getvalue()) | ||
1745 | self.assertEqual("open", order.status) | ||
1746 | self.assertEqual(1, len(order.results)) | ||
1747 | self.assertEqual(-1, order.id) | ||
1748 | |||
1749 | market.create_order.reset_mock() | ||
1750 | with self.subTest(debug=False): | ||
1751 | portfolio.TradeStore.debug = False | ||
1752 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1753 | D("0.1"), "BTC", "long", market, "trade") | ||
1754 | market.create_order.return_value = { "id": 123 } | ||
1755 | order.run() | ||
1756 | market.create_order.assert_called_once() | ||
1757 | self.assertEqual(1, len(order.results)) | ||
1758 | self.assertEqual("open", order.status) | ||
1759 | |||
1760 | market.create_order.reset_mock() | ||
1761 | with self.subTest(exception=True),\ | ||
1762 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | ||
1763 | order = portfolio.Order("buy", portfolio.Amount("ETH", 10), | ||
1764 | D("0.1"), "BTC", "long", market, "trade") | ||
1765 | market.create_order.side_effect = Exception("bouh") | ||
1766 | order.run() | ||
1767 | market.create_order.assert_called_once() | ||
1768 | self.assertEqual(0, len(order.results)) | ||
1769 | self.assertEqual("error", order.status) | ||
1770 | self.assertRegex(stdout_mock.getvalue(), "error when running market.create_order") | ||
1771 | self.assertRegex(stdout_mock.getvalue(), "Exception: bouh") | ||
1772 | |||
1773 | @unittest.skipUnless("unit" in limits, "Unit skipped") | ||
1774 | class MouvementTest(WebMockTestCase): | ||
1775 | def test_values(self): | ||
1776 | mouvement = portfolio.Mouvement("ETH", "BTC", { | ||
1777 | "id": 42, "type": "buy", "fee": "0.0015", | ||
1778 | "date": "2017-12-30 12:00:12", "rate": "0.1", | ||
1779 | "amount": "10", "total": "1" | ||
1780 | }) | ||
1781 | self.assertEqual("ETH", mouvement.currency) | ||
1782 | self.assertEqual("BTC", mouvement.base_currency) | ||
1783 | self.assertEqual(42, mouvement.id) | ||
1784 | self.assertEqual("buy", mouvement.action) | ||
1785 | self.assertEqual(D("0.0015"), mouvement.fee_rate) | ||
1786 | self.assertEqual(portfolio.datetime(2017, 12, 30, 12, 0, 12), mouvement.date) | ||
1787 | self.assertEqual(D("0.1"), mouvement.rate) | ||
1788 | self.assertEqual(portfolio.Amount("ETH", "10"), mouvement.total) | ||
1789 | self.assertEqual(portfolio.Amount("BTC", "1"), mouvement.total_in_base) | ||
1790 | |||
1389 | @unittest.skipUnless("acceptance" in limits, "Acceptance skipped") | 1791 | @unittest.skipUnless("acceptance" in limits, "Acceptance skipped") |
1390 | class AcceptanceTest(WebMockTestCase): | 1792 | class AcceptanceTest(WebMockTestCase): |
1391 | @unittest.expectedFailure | 1793 | @unittest.expectedFailure |
@@ -1473,7 +1875,7 @@ class AcceptanceTest(WebMockTestCase): | |||
1473 | self.assertEqual(portfolio.Amount("XVG", 1000), balances["XVG"].total) | 1875 | self.assertEqual(portfolio.Amount("XVG", 1000), balances["XVG"].total) |
1474 | 1876 | ||
1475 | 1877 | ||
1476 | trades = TradeStore.all | 1878 | trades = portfolio.TradeStore.all |
1477 | self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades[0].value_from) | 1879 | self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades[0].value_from) |
1478 | self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[0].value_to) | 1880 | self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[0].value_to) |
1479 | self.assertEqual("dispose", trades[0].action) | 1881 | self.assertEqual("dispose", trades[0].action) |
@@ -1488,7 +1890,7 @@ class AcceptanceTest(WebMockTestCase): | |||
1488 | 1890 | ||
1489 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[3].value_from) | 1891 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[3].value_from) |
1490 | self.assertEqual(portfolio.Amount("BTC", D("-0.002")), trades[3].value_to) | 1892 | self.assertEqual(portfolio.Amount("BTC", D("-0.002")), trades[3].value_to) |
1491 | self.assertEqual("dispose", trades[3].action) | 1893 | self.assertEqual("acquire", trades[3].action) |
1492 | 1894 | ||
1493 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[4].value_from) | 1895 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[4].value_from) |
1494 | self.assertEqual(portfolio.Amount("BTC", D("0.008")), trades[4].value_to) | 1896 | self.assertEqual(portfolio.Amount("BTC", D("0.008")), trades[4].value_to) |
@@ -1499,9 +1901,9 @@ class AcceptanceTest(WebMockTestCase): | |||
1499 | self.assertEqual("acquire", trades[5].action) | 1901 | self.assertEqual("acquire", trades[5].action) |
1500 | 1902 | ||
1501 | # Action 2 | 1903 | # Action 2 |
1502 | portfolio.Trade.prepare_orders(only="dispose", compute_value=lambda x, y: x["bid"] * D("1.001")) | 1904 | portfolio.TradeStore.prepare_orders(only="dispose", compute_value=lambda x, y: x["bid"] * D("1.001")) |
1503 | 1905 | ||
1504 | all_orders = portfolio.Trade.all_orders() | 1906 | all_orders = portfolio.TradeStore.all_orders(state="pending") |
1505 | self.assertEqual(2, len(all_orders)) | 1907 | self.assertEqual(2, len(all_orders)) |
1506 | self.assertEqual(2, 3*all_orders[0].amount.value) | 1908 | self.assertEqual(2, 3*all_orders[0].amount.value) |
1507 | self.assertEqual(D("0.14014"), all_orders[0].rate) | 1909 | self.assertEqual(D("0.14014"), all_orders[0].rate) |
@@ -1534,7 +1936,14 @@ class AcceptanceTest(WebMockTestCase): | |||
1534 | self.assertEqual("open", all_orders[0].status) | 1936 | self.assertEqual("open", all_orders[0].status) |
1535 | self.assertEqual("open", all_orders[1].status) | 1937 | self.assertEqual("open", all_orders[1].status) |
1536 | 1938 | ||
1537 | market.fetch_order.return_value = { "status": "closed" } | 1939 | market.fetch_order.return_value = { "status": "closed", "datetime": "2018-01-20 13:40:00" } |
1940 | market.privatePostReturnOrderTrades.return_value = [ | ||
1941 | { | ||
1942 | "id": 42, "type": "buy", "fee": "0.0015", | ||
1943 | "date": "2017-12-30 12:00:12", "rate": "0.1", | ||
1944 | "amount": "10", "total": "1" | ||
1945 | } | ||
1946 | ] | ||
1538 | with mock.patch.object(portfolio.time, "sleep") as sleep: | 1947 | with mock.patch.object(portfolio.time, "sleep") as sleep: |
1539 | # Action 4 | 1948 | # Action 4 |
1540 | helper.follow_orders(verbose=False) | 1949 | helper.follow_orders(verbose=False) |
@@ -1546,31 +1955,39 @@ class AcceptanceTest(WebMockTestCase): | |||
1546 | 1955 | ||
1547 | fetch_balance = { | 1956 | fetch_balance = { |
1548 | "ETH": { | 1957 | "ETH": { |
1549 | "free": D("1.0") / 3, | 1958 | "exchange_free": D("1.0") / 3, |
1550 | "used": D("0.0"), | 1959 | "exchange_used": D("0.0"), |
1960 | "exchange_total": D("1.0") / 3, | ||
1961 | "margin_total": 0, | ||
1551 | "total": D("1.0") / 3, | 1962 | "total": D("1.0") / 3, |
1552 | }, | 1963 | }, |
1553 | "BTC": { | 1964 | "BTC": { |
1554 | "free": D("0.134"), | 1965 | "exchange_free": D("0.134"), |
1555 | "used": D("0.0"), | 1966 | "exchange_used": D("0.0"), |
1967 | "exchange_total": D("0.134"), | ||
1968 | "margin_total": 0, | ||
1556 | "total": D("0.134"), | 1969 | "total": D("0.134"), |
1557 | }, | 1970 | }, |
1558 | "ETC": { | 1971 | "ETC": { |
1559 | "free": D("4.0"), | 1972 | "exchange_free": D("4.0"), |
1560 | "used": D("0.0"), | 1973 | "exchange_used": D("0.0"), |
1974 | "exchange_total": D("4.0"), | ||
1975 | "margin_total": 0, | ||
1561 | "total": D("4.0"), | 1976 | "total": D("4.0"), |
1562 | }, | 1977 | }, |
1563 | "XVG": { | 1978 | "XVG": { |
1564 | "free": D("0.0"), | 1979 | "exchange_free": D("0.0"), |
1565 | "used": D("0.0"), | 1980 | "exchange_used": D("0.0"), |
1981 | "exchange_total": D("0.0"), | ||
1982 | "margin_total": 0, | ||
1566 | "total": D("0.0"), | 1983 | "total": D("0.0"), |
1567 | }, | 1984 | }, |
1568 | } | 1985 | } |
1569 | market.fetch_balance.return_value = fetch_balance | 1986 | market.fetch_all_balances.return_value = fetch_balance |
1570 | 1987 | ||
1571 | with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition): | 1988 | with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition): |
1572 | # Action 5 | 1989 | # Action 5 |
1573 | helper.update_trades(market, only="buy", compute_value="average") | 1990 | helper.update_trades(market, only="acquire", compute_value="average") |
1574 | 1991 | ||
1575 | balances = portfolio.BalanceStore.all | 1992 | balances = portfolio.BalanceStore.all |
1576 | self.assertEqual(portfolio.Amount("ETH", 1 / D("3")), balances["ETH"].total) | 1993 | self.assertEqual(portfolio.Amount("ETH", 1 / D("3")), balances["ETH"].total) |
@@ -1579,37 +1996,37 @@ class AcceptanceTest(WebMockTestCase): | |||
1579 | self.assertEqual(portfolio.Amount("XVG", 0), balances["XVG"].total) | 1996 | self.assertEqual(portfolio.Amount("XVG", 0), balances["XVG"].total) |
1580 | 1997 | ||
1581 | 1998 | ||
1582 | trades = TradeStore.all | 1999 | trades = portfolio.TradeStore.all |
1583 | self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades["ETH"].value_from) | 2000 | self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades[0].value_from) |
1584 | self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades["ETH"].value_to) | 2001 | self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[0].value_to) |
1585 | self.assertEqual("sell", trades["ETH"].action) | 2002 | self.assertEqual("dispose", trades[0].action) |
1586 | 2003 | ||
1587 | self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades["ETC"].value_from) | 2004 | self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades[1].value_from) |
1588 | self.assertEqual(portfolio.Amount("BTC", D("0.0485")), trades["ETC"].value_to) | 2005 | self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[1].value_to) |
1589 | self.assertEqual("buy", trades["ETC"].action) | 2006 | self.assertEqual("acquire", trades[1].action) |
1590 | 2007 | ||
1591 | self.assertNotIn("BTC", trades) | 2008 | self.assertNotIn("BTC", trades) |
1592 | 2009 | ||
1593 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["BTD"].value_from) | 2010 | self.assertEqual(portfolio.Amount("BTC", D("0.04")), trades[2].value_from) |
1594 | self.assertEqual(portfolio.Amount("BTC", D("0.00194")), trades["BTD"].value_to) | 2011 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[2].value_to) |
1595 | self.assertEqual("buy", trades["BTD"].action) | 2012 | self.assertEqual("dispose", trades[2].action) |
1596 | 2013 | ||
1597 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["B2X"].value_from) | 2014 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[3].value_from) |
1598 | self.assertEqual(portfolio.Amount("BTC", D("0.00776")), trades["B2X"].value_to) | 2015 | self.assertEqual(portfolio.Amount("BTC", D("-0.002")), trades[3].value_to) |
1599 | self.assertEqual("buy", trades["B2X"].action) | 2016 | self.assertEqual("acquire", trades[3].action) |
1600 | 2017 | ||
1601 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["USDT"].value_from) | 2018 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[4].value_from) |
1602 | self.assertEqual(portfolio.Amount("BTC", D("0.0097")), trades["USDT"].value_to) | 2019 | self.assertEqual(portfolio.Amount("BTC", D("0.008")), trades[4].value_to) |
1603 | self.assertEqual("buy", trades["USDT"].action) | 2020 | self.assertEqual("acquire", trades[4].action) |
1604 | 2021 | ||
1605 | self.assertEqual(portfolio.Amount("BTC", D("0.04")), trades["XVG"].value_from) | 2022 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[5].value_from) |
1606 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["XVG"].value_to) | 2023 | self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades[5].value_to) |
1607 | self.assertEqual("sell", trades["XVG"].action) | 2024 | self.assertEqual("acquire", trades[5].action) |
1608 | 2025 | ||
1609 | # Action 6 | 2026 | # Action 6 |
1610 | portfolio.Trade.prepare_orders(only="buy", compute_value=lambda x, y: x["ask"]) | 2027 | portfolio.TradeStore.prepare_orders(only="acquire", compute_value=lambda x, y: x["ask"]) |
1611 | 2028 | ||
1612 | all_orders = portfolio.Trade.all_orders(state="pending") | 2029 | all_orders = portfolio.TradeStore.all_orders(state="pending") |
1613 | self.assertEqual(4, len(all_orders)) | 2030 | self.assertEqual(4, len(all_orders)) |
1614 | self.assertEqual(portfolio.Amount("ETC", D("12.83333333")), round(all_orders[0].amount)) | 2031 | self.assertEqual(portfolio.Amount("ETC", D("12.83333333")), round(all_orders[0].amount)) |
1615 | self.assertEqual(D("0.003"), all_orders[0].rate) | 2032 | self.assertEqual(D("0.003"), all_orders[0].rate) |