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 /test.py | |
parent | 1aa7d4fa2ec3c2b3268bef31a666ca6e1aaa6563 (diff) | |
download | Trader-5a72ded790f8b5e7c9b38a3cc91c12fbfb6cb97a.tar.gz Trader-5a72ded790f8b5e7c9b38a3cc91c12fbfb6cb97a.tar.zst Trader-5a72ded790f8b5e7c9b38a3cc91c12fbfb6cb97a.zip |
Add missing tests
Diffstat (limited to 'test.py')
-rw-r--r-- | test.py | 511 |
1 files changed, 464 insertions, 47 deletions
@@ -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) |