diff options
Diffstat (limited to 'test.py')
-rw-r--r-- | test.py | 263 |
1 files changed, 261 insertions, 2 deletions
@@ -256,6 +256,11 @@ class BalanceTest(unittest.TestCase): | |||
256 | "info": "bar", | 256 | "info": "bar", |
257 | "used": "baz", | 257 | "used": "baz", |
258 | "total": "bazz", | 258 | "total": "bazz", |
259 | "ETC": { | ||
260 | "free": 0.0, | ||
261 | "used": 0.0, | ||
262 | "total": 0.0 | ||
263 | }, | ||
259 | "USDT": { | 264 | "USDT": { |
260 | "free": 6.0, | 265 | "free": 6.0, |
261 | "used": 1.2, | 266 | "used": 1.2, |
@@ -327,7 +332,12 @@ class BalanceTest(unittest.TestCase): | |||
327 | 332 | ||
328 | portfolio.Balance.fetch_balances(portfolio.market) | 333 | portfolio.Balance.fetch_balances(portfolio.market) |
329 | self.assertNotIn("XMR", portfolio.Balance.currencies()) | 334 | self.assertNotIn("XMR", portfolio.Balance.currencies()) |
330 | self.assertEqual(["USDT", "XVG"], list(portfolio.Balance.currencies())) | 335 | self.assertListEqual(["USDT", "XVG"], list(portfolio.Balance.currencies())) |
336 | |||
337 | portfolio.Balance.known_balances["ETC"] = portfolio.Balance("ETC", "1", "0", "1") | ||
338 | portfolio.Balance.fetch_balances(portfolio.market) | ||
339 | self.assertEqual(0, portfolio.Balance.known_balances["ETC"].total) | ||
340 | self.assertListEqual(["USDT", "XVG", "ETC"], list(portfolio.Balance.currencies())) | ||
331 | 341 | ||
332 | @mock.patch.object(portfolio.Portfolio, "repartition_pertenthousand") | 342 | @mock.patch.object(portfolio.Portfolio, "repartition_pertenthousand") |
333 | @mock.patch.object(portfolio.market, "fetch_balance") | 343 | @mock.patch.object(portfolio.market, "fetch_balance") |
@@ -362,7 +372,7 @@ class BalanceTest(unittest.TestCase): | |||
362 | return { "average": D("0.000001") } | 372 | return { "average": D("0.000001") } |
363 | if c1 == "XEM" and c2 == "BTC": | 373 | if c1 == "XEM" and c2 == "BTC": |
364 | return { "average": D("0.001") } | 374 | return { "average": D("0.001") } |
365 | raise Exception("Should be called with {}, {}".format(c1, c2)) | 375 | self.fail("Should be called with {}, {}".format(c1, c2)) |
366 | get_ticker.side_effect = _get_ticker | 376 | get_ticker.side_effect = _get_ticker |
367 | 377 | ||
368 | market = mock.Mock() | 378 | market = mock.Mock() |
@@ -388,6 +398,10 @@ class BalanceTest(unittest.TestCase): | |||
388 | self.assertEqual(D("0.2525"), call[0][1]["BTC"].value) | 398 | self.assertEqual(D("0.2525"), call[0][1]["BTC"].value) |
389 | self.assertEqual(D("0.7575"), call[0][1]["XEM"].value) | 399 | self.assertEqual(D("0.7575"), call[0][1]["XEM"].value) |
390 | 400 | ||
401 | @unittest.skip("TODO") | ||
402 | def test_update_trades(self): | ||
403 | pass | ||
404 | |||
391 | def test__repr(self): | 405 | def test__repr(self): |
392 | balance = portfolio.Balance("BTX", 3, 1, 2) | 406 | balance = portfolio.Balance("BTX", 3, 1, 2) |
393 | self.assertEqual("Balance(BTX [1.00000000 BTX/2.00000000 BTX/3.00000000 BTX])", repr(balance)) | 407 | self.assertEqual("Balance(BTX [1.00000000 BTX/2.00000000 BTX/3.00000000 BTX])", repr(balance)) |
@@ -520,5 +534,250 @@ class TradeTest(unittest.TestCase): | |||
520 | def tearDown(self): | 534 | def tearDown(self): |
521 | self.patcher.stop() | 535 | self.patcher.stop() |
522 | 536 | ||
537 | class AcceptanceTest(unittest.TestCase): | ||
538 | import time | ||
539 | |||
540 | def setUp(self): | ||
541 | super(AcceptanceTest, self).setUp() | ||
542 | |||
543 | self.patchers = [ | ||
544 | mock.patch.multiple(portfolio.Balance, known_balances={}), | ||
545 | mock.patch.multiple(portfolio.Portfolio, data=None, liquidities={}), | ||
546 | mock.patch.multiple(portfolio.Trade, | ||
547 | ticker_cache={}, | ||
548 | ticker_cache_timestamp=self.time.time(), | ||
549 | fees_cache={}, | ||
550 | trades={}), | ||
551 | mock.patch.multiple(portfolio.Computation, | ||
552 | computations=portfolio.Computation.computations) | ||
553 | ] | ||
554 | for patcher in self.patchers: | ||
555 | patcher.start() | ||
556 | |||
557 | def test_success_sell_only_necessary(self): | ||
558 | fetch_balance = { | ||
559 | "ETH": { | ||
560 | "free": D("1.0"), | ||
561 | "used": D("0.0"), | ||
562 | "total": D("1.0"), | ||
563 | }, | ||
564 | "ETC": { | ||
565 | "free": D("4.0"), | ||
566 | "used": D("0.0"), | ||
567 | "total": D("4.0"), | ||
568 | }, | ||
569 | "XVG": { | ||
570 | "free": D("1000.0"), | ||
571 | "used": D("0.0"), | ||
572 | "total": D("1000.0"), | ||
573 | }, | ||
574 | } | ||
575 | repartition = { | ||
576 | "ETH": 2500, | ||
577 | "ETC": 2500, | ||
578 | "BTC": 4000, | ||
579 | "BTD": 500, | ||
580 | "USDT": 500, | ||
581 | } | ||
582 | |||
583 | def fetch_ticker(symbol): | ||
584 | if symbol == "ETH/BTC": | ||
585 | return { | ||
586 | "symbol": "ETH/BTC", | ||
587 | "bid": D("0.14"), | ||
588 | "ask": D("0.16") | ||
589 | } | ||
590 | if symbol == "ETC/BTC": | ||
591 | return { | ||
592 | "symbol": "ETC/BTC", | ||
593 | "bid": D("0.002"), | ||
594 | "ask": D("0.003") | ||
595 | } | ||
596 | if symbol == "XVG/BTC": | ||
597 | return { | ||
598 | "symbol": "XVG/BTC", | ||
599 | "bid": D("0.00003"), | ||
600 | "ask": D("0.00005") | ||
601 | } | ||
602 | if symbol == "BTD/BTC": | ||
603 | return { | ||
604 | "symbol": "BTD/BTC", | ||
605 | "bid": D("0.0008"), | ||
606 | "ask": D("0.0012") | ||
607 | } | ||
608 | if symbol == "USDT/BTC": | ||
609 | raise portfolio.ccxt.ExchangeError | ||
610 | if symbol == "BTC/USDT": | ||
611 | return { | ||
612 | "symbol": "BTC/USDT", | ||
613 | "bid": D("14000"), | ||
614 | "ask": D("16000") | ||
615 | } | ||
616 | self.fail("Shouldn't have been called with {}".format(symbol)) | ||
617 | |||
618 | market = mock.Mock() | ||
619 | market.fetch_balance.return_value = fetch_balance | ||
620 | market.fetch_ticker.side_effect = fetch_ticker | ||
621 | with mock.patch.object(portfolio.Portfolio, "repartition_pertenthousand", return_value=repartition): | ||
622 | # Action 1 | ||
623 | portfolio.Balance.prepare_trades(market) | ||
624 | |||
625 | balances = portfolio.Balance.known_balances | ||
626 | self.assertEqual(portfolio.Amount("ETH", 1), balances["ETH"].total) | ||
627 | self.assertEqual(portfolio.Amount("ETC", 4), balances["ETC"].total) | ||
628 | self.assertEqual(portfolio.Amount("XVG", 1000), balances["XVG"].total) | ||
629 | |||
630 | |||
631 | trades = portfolio.Trade.trades | ||
632 | self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades["ETH"].value_from) | ||
633 | self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades["ETH"].value_to) | ||
634 | self.assertEqual("sell", trades["ETH"].action) | ||
635 | |||
636 | self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades["ETC"].value_from) | ||
637 | self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades["ETC"].value_to) | ||
638 | self.assertEqual("buy", trades["ETC"].action) | ||
639 | |||
640 | self.assertNotIn("BTC", trades) | ||
641 | |||
642 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["BTD"].value_from) | ||
643 | self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades["BTD"].value_to) | ||
644 | self.assertEqual("buy", trades["BTD"].action) | ||
645 | |||
646 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["USDT"].value_from) | ||
647 | self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades["USDT"].value_to) | ||
648 | self.assertEqual("buy", trades["USDT"].action) | ||
649 | |||
650 | self.assertEqual(portfolio.Amount("BTC", D("0.04")), trades["XVG"].value_from) | ||
651 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["XVG"].value_to) | ||
652 | self.assertEqual("sell", trades["XVG"].action) | ||
653 | |||
654 | # Action 2 | ||
655 | portfolio.Trade.prepare_orders(only="sell", compute_value=lambda x, y: x["bid"] * D("1.001")) | ||
656 | |||
657 | all_orders = portfolio.Trade.all_orders() | ||
658 | self.assertEqual(2, len(all_orders)) | ||
659 | self.assertEqual(2, 3*all_orders[0].amount.value) | ||
660 | self.assertEqual(D("0.14014"), all_orders[0].rate) | ||
661 | self.assertEqual(1000, all_orders[1].amount.value) | ||
662 | self.assertEqual(D("0.00003003"), all_orders[1].rate) | ||
663 | |||
664 | |||
665 | def create_order(symbol, type, action, amount, price=None): | ||
666 | self.assertEqual("limit", type) | ||
667 | if symbol == "ETH/BTC": | ||
668 | self.assertEqual("bid", action) | ||
669 | self.assertEqual(2, 3*amount) | ||
670 | self.assertEqual(D("0.14014"), price) | ||
671 | elif symbol == "XVG/BTC": | ||
672 | self.assertEqual("bid", action) | ||
673 | self.assertEqual(1000, amount) | ||
674 | self.assertEqual(D("0.00003003"), price) | ||
675 | else: | ||
676 | self.fail("I shouldn't have been called") | ||
677 | |||
678 | return { | ||
679 | "id": symbol, | ||
680 | } | ||
681 | market.create_order.side_effect = create_order | ||
682 | |||
683 | # Action 3 | ||
684 | portfolio.Trade.run_orders() | ||
685 | |||
686 | self.assertEqual("open", all_orders[0].status) | ||
687 | self.assertEqual("open", all_orders[1].status) | ||
688 | |||
689 | market.fetch_order.return_value = { "status": "closed" } | ||
690 | with mock.patch.object(portfolio.time, "sleep") as sleep: | ||
691 | # Action 4 | ||
692 | portfolio.Trade.follow_orders(verbose=False) | ||
693 | |||
694 | sleep.assert_called_with(30) | ||
695 | |||
696 | for order in all_orders: | ||
697 | self.assertEqual("closed", order.status) | ||
698 | |||
699 | fetch_balance = { | ||
700 | "ETH": { | ||
701 | "free": D("1.0") / 3, | ||
702 | "used": D("0.0"), | ||
703 | "total": D("1.0") / 3, | ||
704 | }, | ||
705 | "BTC": { | ||
706 | "free": D("0.134"), | ||
707 | "used": D("0.0"), | ||
708 | "total": D("0.134"), | ||
709 | }, | ||
710 | "ETC": { | ||
711 | "free": D("4.0"), | ||
712 | "used": D("0.0"), | ||
713 | "total": D("4.0"), | ||
714 | }, | ||
715 | "XVG": { | ||
716 | "free": D("0.0"), | ||
717 | "used": D("0.0"), | ||
718 | "total": D("0.0"), | ||
719 | }, | ||
720 | } | ||
721 | market.fetch_balance.return_value = fetch_balance | ||
722 | |||
723 | with mock.patch.object(portfolio.Portfolio, "repartition_pertenthousand", return_value=repartition): | ||
724 | # Action 5 | ||
725 | portfolio.Balance.update_trades(market, only="buy", compute_value="average") | ||
726 | |||
727 | balances = portfolio.Balance.known_balances | ||
728 | self.assertEqual(portfolio.Amount("ETH", 1 / D("3")), balances["ETH"].total) | ||
729 | self.assertEqual(portfolio.Amount("ETC", 4), balances["ETC"].total) | ||
730 | self.assertEqual(portfolio.Amount("BTC", D("0.134")), balances["BTC"].total) | ||
731 | self.assertEqual(portfolio.Amount("XVG", 0), balances["XVG"].total) | ||
732 | |||
733 | |||
734 | trades = portfolio.Trade.trades | ||
735 | self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades["ETH"].value_from) | ||
736 | self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades["ETH"].value_to) | ||
737 | self.assertEqual("sell", trades["ETH"].action) | ||
738 | |||
739 | self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades["ETC"].value_from) | ||
740 | self.assertEqual(portfolio.Amount("BTC", D("0.0485")), trades["ETC"].value_to) | ||
741 | self.assertEqual("buy", trades["ETC"].action) | ||
742 | |||
743 | self.assertNotIn("BTC", trades) | ||
744 | |||
745 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["BTD"].value_from) | ||
746 | self.assertEqual(portfolio.Amount("BTC", D("0.0097")), trades["BTD"].value_to) | ||
747 | self.assertEqual("buy", trades["BTD"].action) | ||
748 | |||
749 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["USDT"].value_from) | ||
750 | self.assertEqual(portfolio.Amount("BTC", D("0.0097")), trades["USDT"].value_to) | ||
751 | self.assertEqual("buy", trades["USDT"].action) | ||
752 | |||
753 | self.assertEqual(portfolio.Amount("BTC", D("0.04")), trades["XVG"].value_from) | ||
754 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades["XVG"].value_to) | ||
755 | self.assertEqual("sell", trades["XVG"].action) | ||
756 | |||
757 | # Action 6 | ||
758 | portfolio.Trade.prepare_orders(only="buy", compute_value=lambda x, y: x["ask"] * D("0.999")) | ||
759 | |||
760 | all_orders = portfolio.Trade.all_orders(state="pending") | ||
761 | self.assertEqual(3, len(all_orders)) | ||
762 | self.assertEqual(portfolio.Amount("ETC", D("15.4")), all_orders[0].amount) | ||
763 | self.assertEqual(D("0.002997"), all_orders[0].rate) | ||
764 | self.assertEqual("ask", all_orders[0].action) | ||
765 | self.assertEqual(portfolio.Amount("BTD", D("9.7")), all_orders[1].amount) | ||
766 | self.assertEqual(D("0.0011988"), all_orders[1].rate) | ||
767 | self.assertEqual("ask", all_orders[1].action) | ||
768 | self.assertEqual(portfolio.Amount("BTC", D("0.0097")), all_orders[2].amount) | ||
769 | self.assertEqual(D("15984"), all_orders[2].rate) | ||
770 | self.assertEqual("bid", all_orders[2].action) | ||
771 | |||
772 | with mock.patch.object(portfolio.time, "sleep") as sleep: | ||
773 | # Action 7 | ||
774 | portfolio.Trade.follow_orders(verbose=False) | ||
775 | |||
776 | sleep.assert_called_with(30) | ||
777 | |||
778 | def tearDown(self): | ||
779 | for patcher in self.patchers: | ||
780 | patcher.stop() | ||
781 | |||
523 | if __name__ == '__main__': | 782 | if __name__ == '__main__': |
524 | unittest.main() | 783 | unittest.main() |