]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git/blobdiff - test.py
Separate store and add helper
[perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git] / test.py
diff --git a/test.py b/test.py
index b27eb74f985899d1ce0520715aeb376a21effaf7..aae1dc85af02245f88ddd28d32a4fac9ecfe1cef 100644 (file)
--- a/test.py
+++ b/test.py
@@ -5,6 +5,7 @@ from unittest import mock
 import requests
 import requests_mock
 from io import StringIO
+import helper
 
 class WebMockTestCase(unittest.TestCase):
     import time
@@ -15,16 +16,18 @@ class WebMockTestCase(unittest.TestCase):
         self.wm.start()
 
         self.patchers = [
-                mock.patch.multiple(portfolio.Balance, known_balances={}),
+                mock.patch.multiple(portfolio.BalanceStore,
+                    all={},),
+                mock.patch.multiple(portfolio.TradeStore,
+                    all=[],
+                    debug=False),
                 mock.patch.multiple(portfolio.Portfolio, data=None, liquidities={}),
-                mock.patch.multiple(portfolio.Trade,
-                    ticker_cache={},
-                    ticker_cache_timestamp=self.time.time(),
-                    fees_cache={},
-                    debug=False,
-                    trades=[]),
                 mock.patch.multiple(portfolio.Computation,
-                    computations=portfolio.Computation.computations)
+                    computations=portfolio.Computation.computations),
+                mock.patch.multiple(helper,
+                    fees_cache={},
+                    ticker_cache={},
+                    ticker_cache_timestamp=self.time.time()),
                 ]
         for patcher in self.patchers:
             patcher.start()
@@ -149,12 +152,12 @@ class AmountTest(WebMockTestCase):
         self.assertEqual(amount, amount.in_currency("ETC", None))
 
         ticker_mock = unittest.mock.Mock()
-        with mock.patch.object(portfolio.Trade, 'get_ticker', new=ticker_mock):
+        with mock.patch.object(helper, 'get_ticker', new=ticker_mock):
             ticker_mock.return_value = None
 
             self.assertRaises(Exception, amount.in_currency, "ETH", None)
 
-        with mock.patch.object(portfolio.Trade, 'get_ticker', new=ticker_mock):
+        with mock.patch.object(helper, 'get_ticker', new=ticker_mock):
             ticker_mock.return_value = {
                     "bid": D("0.2"),
                     "ask": D("0.4"),
@@ -361,37 +364,6 @@ class AmountTest(WebMockTestCase):
         self.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT -> Amount(0.10000000 BTC)))", repr(amount1))
 
 class BalanceTest(WebMockTestCase):
-    def setUp(self):
-        super(BalanceTest, self).setUp()
-
-        self.fetch_balance = {
-                "ETC": {
-                    "exchange_free": 0,
-                    "exchange_used": 0,
-                    "exchange_total": 0,
-                    "margin_total": 0,
-                    },
-                "USDT": {
-                    "exchange_free": D("6.0"),
-                    "exchange_used": D("1.2"),
-                    "exchange_total": D("7.2"),
-                    "margin_total": 0,
-                    },
-                "XVG": {
-                    "exchange_free": 16,
-                    "exchange_used": 0,
-                    "exchange_total": 16,
-                    "margin_total": 0,
-                    },
-                "XMR": {
-                    "exchange_free": 0,
-                    "exchange_used": 0,
-                    "exchange_total": 0,
-                    "margin_total": D("-1.0"),
-                    "margin_free": 0,
-                    },
-                }
-
     def test_values(self):
         balance = portfolio.Balance("BTC", {
             "exchange_total": "0.65",
@@ -426,91 +398,100 @@ class BalanceTest(WebMockTestCase):
         self.assertEqual(portfolio.D("0.4"), balance.margin_lending_fees.value)
         self.assertEqual("USDT", balance.margin_lending_fees.currency)
 
-    @mock.patch.object(portfolio.Trade, "get_ticker")
-    def test_in_currency(self, get_ticker):
-        portfolio.Balance.known_balances = {
-                "BTC": portfolio.Balance("BTC", {
-                    "total": "0.65",
-                    "exchange_total":"0.65",
-                    "exchange_free": "0.35",
-                    "exchange_used": "0.30"}),
-                "ETH": portfolio.Balance("ETH", {
-                    "total": 3,
-                    "exchange_total": 3,
-                    "exchange_free": 3,
-                    "exchange_used": 0}),
-                }
-        market = mock.Mock()
-        get_ticker.return_value = {
-                "bid": D("0.09"),
-                "ask": D("0.11"),
-                "average": D("0.1"),
-                }
+    def test__repr(self):
+        self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX])",
+                repr(portfolio.Balance("BTX", { "exchange_free": 2, "exchange_total": 2 })))
+        balance = portfolio.Balance("BTX", { "exchange_total": 3,
+            "exchange_used": 1, "exchange_free": 2 })
+        self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX + ❌1.00000000 BTX = 3.00000000 BTX])", repr(balance))
 
-        amounts = portfolio.Balance.in_currency("BTC", market)
-        self.assertEqual("BTC", amounts["ETH"].currency)
-        self.assertEqual(D("0.65"), amounts["BTC"].value)
-        self.assertEqual(D("0.30"), amounts["ETH"].value)
+        balance = portfolio.Balance("BTX", { "margin_total": 3,
+            "margin_borrowed": 1, "margin_free": 2 })
+        self.assertEqual("Balance(BTX Margin: [✔2.00000000 BTX + borrowed 1.00000000 BTX = 3.00000000 BTX])", repr(balance))
 
-        amounts = portfolio.Balance.in_currency("BTC", market, compute_value="bid")
-        self.assertEqual(D("0.65"), amounts["BTC"].value)
-        self.assertEqual(D("0.27"), amounts["ETH"].value)
+        balance = portfolio.Balance("BTX", { "margin_total": -3,
+            "margin_borrowed_base_price": D("0.1"),
+            "margin_borrowed_base_currency": "BTC",
+            "margin_lending_fees": D("0.002") })
+        self.assertEqual("Balance(BTX Margin: [-3.00000000 BTX @@ 0.10000000 BTC/0.00200000 BTC])", repr(balance))
 
-        amounts = portfolio.Balance.in_currency("BTC", market, compute_value="bid", type="exchange_used")
-        self.assertEqual(D("0.30"), amounts["BTC"].value)
-        self.assertEqual(0, amounts["ETH"].value)
+class HelperTest(WebMockTestCase):
+    def test_get_ticker(self):
+        market = mock.Mock()
+        market.fetch_ticker.side_effect = [
+                { "bid": 1, "ask": 3 },
+                helper.ExchangeError("foo"),
+                { "bid": 10, "ask": 40 },
+                helper.ExchangeError("foo"),
+                helper.ExchangeError("foo"),
+                ]
 
-    def test_currencies(self):
-        portfolio.Balance.known_balances = {
-                "BTC": portfolio.Balance("BTC", {
-                    "total": "0.65",
-                    "exchange_total":"0.65",
-                    "exchange_free": "0.35",
-                    "exchange_used": "0.30"}),
-                "ETH": portfolio.Balance("ETH", {
-                    "total": 3,
-                    "exchange_total": 3,
-                    "exchange_free": 3,
-                    "exchange_used": 0}),
-                }
-        self.assertListEqual(["BTC", "ETH"], list(portfolio.Balance.currencies()))
+        ticker = helper.get_ticker("ETH", "ETC", market)
+        market.fetch_ticker.assert_called_with("ETH/ETC")
+        self.assertEqual(1, ticker["bid"])
+        self.assertEqual(3, ticker["ask"])
+        self.assertEqual(2, ticker["average"])
+        self.assertFalse(ticker["inverted"])
 
-    @mock.patch.object(portfolio.market, "fetch_all_balances")
-    def test_fetch_balances(self, fetch_all_balances):
-        fetch_all_balances.return_value = self.fetch_balance
+        ticker = helper.get_ticker("ETH", "XVG", market)
+        self.assertEqual(0.0625, ticker["average"])
+        self.assertTrue(ticker["inverted"])
+        self.assertIn("original", ticker)
+        self.assertEqual(10, ticker["original"]["bid"])
 
-        portfolio.Balance.fetch_balances(portfolio.market)
-        self.assertNotIn("ETC", portfolio.Balance.currencies())
-        self.assertListEqual(["USDT", "XVG", "XMR"], list(portfolio.Balance.currencies()))
+        ticker = helper.get_ticker("XVG", "XMR", market)
+        self.assertIsNone(ticker)
 
-        portfolio.Balance.known_balances["ETC"] = portfolio.Balance("ETC", {
-            "exchange_total": "1", "exchange_free": "0",
-            "exchange_used": "1" })
-        portfolio.Balance.fetch_balances(portfolio.market)
-        self.assertEqual(0, portfolio.Balance.known_balances["ETC"].total)
-        self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(portfolio.Balance.currencies()))
+        market.fetch_ticker.assert_has_calls([
+            mock.call("ETH/ETC"),
+            mock.call("ETH/XVG"),
+            mock.call("XVG/ETH"),
+            mock.call("XVG/XMR"),
+            mock.call("XMR/XVG"),
+            ])
 
-    @mock.patch.object(portfolio.Portfolio, "repartition")
-    @mock.patch.object(portfolio.market, "fetch_all_balances")
-    def test_dispatch_assets(self, fetch_all_balances, repartition):
-        fetch_all_balances.return_value = self.fetch_balance
-        portfolio.Balance.fetch_balances(portfolio.market)
+        market2 = mock.Mock()
+        market2.fetch_ticker.side_effect = [
+                { "bid": 1, "ask": 3 },
+                { "bid": 1.2, "ask": 3.5 },
+                ]
+        ticker1 = helper.get_ticker("ETH", "ETC", market2)
+        ticker2 = helper.get_ticker("ETH", "ETC", market2)
+        ticker3 = helper.get_ticker("ETC", "ETH", market2)
+        market2.fetch_ticker.assert_called_once_with("ETH/ETC")
+        self.assertEqual(1, ticker1["bid"])
+        self.assertDictEqual(ticker1, ticker2)
+        self.assertDictEqual(ticker1, ticker3["original"])
 
-        self.assertNotIn("XEM", portfolio.Balance.currencies())
+        ticker4 = helper.get_ticker("ETH", "ETC", market2, refresh=True)
+        ticker5 = helper.get_ticker("ETH", "ETC", market2)
+        self.assertEqual(1.2, ticker4["bid"])
+        self.assertDictEqual(ticker4, ticker5)
 
-        repartition.return_value = {
-                "XEM": (D("0.75"), "long"),
-                "BTC": (D("0.26"), "long"),
-                }
+        market3 = mock.Mock()
+        market3.fetch_ticker.side_effect = [
+                { "bid": 1, "ask": 3 },
+                { "bid": 1.2, "ask": 3.5 },
+                ]
+        ticker6 = helper.get_ticker("ETH", "ETC", market3)
+        helper.ticker_cache_timestamp -= 4
+        ticker7 = helper.get_ticker("ETH", "ETC", market3)
+        helper.ticker_cache_timestamp -= 2
+        ticker8 = helper.get_ticker("ETH", "ETC", market3)
+        self.assertDictEqual(ticker6, ticker7)
+        self.assertEqual(1.2, ticker8["bid"])
 
-        amounts = portfolio.Balance.dispatch_assets(portfolio.Amount("BTC", "10.1"))
-        self.assertIn("XEM", portfolio.Balance.currencies())
-        self.assertEqual(D("2.6"), amounts["BTC"].value)
-        self.assertEqual(D("7.5"), amounts["XEM"].value)
+    def test_fetch_fees(self):
+        market = mock.Mock()
+        market.fetch_fees.return_value = "Foo"
+        self.assertEqual("Foo", helper.fetch_fees(market))
+        market.fetch_fees.assert_called_once()
+        self.assertEqual("Foo", helper.fetch_fees(market))
+        market.fetch_fees.assert_called_once()
 
     @mock.patch.object(portfolio.Portfolio, "repartition")
-    @mock.patch.object(portfolio.Trade, "get_ticker")
-    @mock.patch.object(portfolio.Trade, "compute_trades")
+    @mock.patch.object(helper, "get_ticker")
+    @mock.patch.object(portfolio.TradeStore, "compute_trades")
     def test_prepare_trades(self, compute_trades, get_ticker, repartition):
         repartition.return_value = {
                 "XEM": (D("0.75"), "long"),
@@ -541,7 +522,7 @@ class BalanceTest(WebMockTestCase):
                     "total": D("10000.0")
                     },
                 }
-        portfolio.Balance.prepare_trades(market)
+        helper.prepare_trades(market)
         compute_trades.assert_called()
 
         call = compute_trades.call_args
@@ -552,8 +533,8 @@ class BalanceTest(WebMockTestCase):
         self.assertEqual(D("0.7575"), call[0][1]["XEM"].value)
 
     @mock.patch.object(portfolio.Portfolio, "repartition")
-    @mock.patch.object(portfolio.Trade, "get_ticker")
-    @mock.patch.object(portfolio.Trade, "compute_trades")
+    @mock.patch.object(helper, "get_ticker")
+    @mock.patch.object(portfolio.TradeStore, "compute_trades")
     def test_update_trades(self, compute_trades, get_ticker, repartition):
         repartition.return_value = {
                 "XEM": (D("0.75"), "long"),
@@ -584,7 +565,7 @@ class BalanceTest(WebMockTestCase):
                     "total": D("10000.0")
                     },
                 }
-        portfolio.Balance.update_trades(market)
+        helper.update_trades(market)
         compute_trades.assert_called()
 
         call = compute_trades.call_args
@@ -595,8 +576,8 @@ class BalanceTest(WebMockTestCase):
         self.assertEqual(D("0.7575"), call[0][1]["XEM"].value)
 
     @mock.patch.object(portfolio.Portfolio, "repartition")
-    @mock.patch.object(portfolio.Trade, "get_ticker")
-    @mock.patch.object(portfolio.Trade, "compute_trades")
+    @mock.patch.object(helper, "get_ticker")
+    @mock.patch.object(portfolio.TradeStore, "compute_trades")
     def test_prepare_trades_to_sell_all(self, compute_trades, get_ticker, repartition):
         def _get_ticker(c1, c2, market):
             if c1 == "USDT" and c2 == "BTC":
@@ -621,7 +602,7 @@ class BalanceTest(WebMockTestCase):
                     "total": D("10000.0")
                     },
                 }
-        portfolio.Balance.prepare_trades_to_sell_all(market)
+        helper.prepare_trades_to_sell_all(market)
         repartition.assert_not_called()
         compute_trades.assert_called()
 
@@ -631,89 +612,243 @@ class BalanceTest(WebMockTestCase):
         self.assertEqual(D("0.01"), call[0][0]["XVG"].value)
         self.assertEqual(D("1.01"), call[0][1]["BTC"].value)
 
-    def test__repr(self):
-        self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX])",
-                repr(portfolio.Balance("BTX", { "exchange_free": 2, "exchange_total": 2 })))
-        balance = portfolio.Balance("BTX", { "exchange_total": 3,
-            "exchange_used": 1, "exchange_free": 2 })
-        self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX + ❌1.00000000 BTX = 3.00000000 BTX])", repr(balance))
+    @unittest.skip("TODO")
+    def test_follow_orders(self):
+        pass
 
-        balance = portfolio.Balance("BTX", { "margin_total": 3,
-            "margin_borrowed": 1, "margin_free": 2 })
-        self.assertEqual("Balance(BTX Margin: [✔2.00000000 BTX + borrowed 1.00000000 BTX = 3.00000000 BTX])", repr(balance))
 
-        balance = portfolio.Balance("BTX", { "margin_total": -3,
-            "margin_borrowed_base_price": D("0.1"),
-            "margin_borrowed_base_currency": "BTC",
-            "margin_lending_fees": D("0.002") })
-        self.assertEqual("Balance(BTX Margin: [-3.00000000 BTX @@ 0.10000000 BTC/0.00200000 BTC])", repr(balance))
+class TradeStoreTest(WebMockTestCase):
+    @unittest.skip("TODO")
+    def test_compute_trades(self):
+        pass
 
-class TradeTest(WebMockTestCase):
+    def test_prepare_orders(self):
+        trade_mock1 = mock.Mock()
+        trade_mock2 = mock.Mock()
 
-    def test_get_ticker(self):
+        portfolio.TradeStore.all.append(trade_mock1)
+        portfolio.TradeStore.all.append(trade_mock2)
+
+        portfolio.TradeStore.prepare_orders()
+        trade_mock1.prepare_order.assert_called_with(compute_value="default")
+        trade_mock2.prepare_order.assert_called_with(compute_value="default")
+
+        portfolio.TradeStore.prepare_orders(compute_value="bla")
+        trade_mock1.prepare_order.assert_called_with(compute_value="bla")
+        trade_mock2.prepare_order.assert_called_with(compute_value="bla")
+
+        trade_mock1.prepare_order.reset_mock()
+        trade_mock2.prepare_order.reset_mock()
+
+        trade_mock1.action = "foo"
+        trade_mock2.action = "bar"
+        portfolio.TradeStore.prepare_orders(only="bar")
+        trade_mock1.prepare_order.assert_not_called()
+        trade_mock2.prepare_order.assert_called_with(compute_value="default")
+
+    def test_print_all_with_order(self):
+        trade_mock1 = mock.Mock()
+        trade_mock2 = mock.Mock()
+        trade_mock3 = mock.Mock()
+        portfolio.TradeStore.all = [trade_mock1, trade_mock2, trade_mock3]
+
+        portfolio.TradeStore.print_all_with_order()
+
+        trade_mock1.print_with_order.assert_called()
+        trade_mock2.print_with_order.assert_called()
+        trade_mock3.print_with_order.assert_called()
+
+    @mock.patch.object(portfolio.TradeStore, "all_orders")
+    def test_run_orders(self, all_orders):
+        order_mock1 = mock.Mock()
+        order_mock2 = mock.Mock()
+        order_mock3 = mock.Mock()
+        all_orders.return_value = [order_mock1, order_mock2, order_mock3]
+        portfolio.TradeStore.run_orders()
+        all_orders.assert_called_with(state="pending")
+
+        order_mock1.run.assert_called()
+        order_mock2.run.assert_called()
+        order_mock3.run.assert_called()
+
+    def test_all_orders(self):
+        trade_mock1 = mock.Mock()
+        trade_mock2 = mock.Mock()
+
+        order_mock1 = mock.Mock()
+        order_mock2 = mock.Mock()
+        order_mock3 = mock.Mock()
+
+        trade_mock1.orders = [order_mock1, order_mock2]
+        trade_mock2.orders = [order_mock3]
+
+        order_mock1.status = "pending"
+        order_mock2.status = "open"
+        order_mock3.status = "open"
+
+        portfolio.TradeStore.all.append(trade_mock1)
+        portfolio.TradeStore.all.append(trade_mock2)
+
+        orders = portfolio.TradeStore.all_orders()
+        self.assertEqual(3, len(orders))
+
+        open_orders = portfolio.TradeStore.all_orders(state="open")
+        self.assertEqual(2, len(open_orders))
+        self.assertEqual([order_mock2, order_mock3], open_orders)
+
+    @mock.patch.object(portfolio.TradeStore, "all_orders")
+    def test_update_all_orders_status(self, all_orders):
+        order_mock1 = mock.Mock()
+        order_mock2 = mock.Mock()
+        order_mock3 = mock.Mock()
+        all_orders.return_value = [order_mock1, order_mock2, order_mock3]
+        portfolio.TradeStore.update_all_orders_status()
+        all_orders.assert_called_with(state="open")
+
+        order_mock1.get_status.assert_called()
+        order_mock2.get_status.assert_called()
+        order_mock3.get_status.assert_called()
+
+
+class BalanceStoreTest(WebMockTestCase):
+    def setUp(self):
+        super(BalanceStoreTest, self).setUp()
+
+        self.fetch_balance = {
+                "ETC": {
+                    "exchange_free": 0,
+                    "exchange_used": 0,
+                    "exchange_total": 0,
+                    "margin_total": 0,
+                    },
+                "USDT": {
+                    "exchange_free": D("6.0"),
+                    "exchange_used": D("1.2"),
+                    "exchange_total": D("7.2"),
+                    "margin_total": 0,
+                    },
+                "XVG": {
+                    "exchange_free": 16,
+                    "exchange_used": 0,
+                    "exchange_total": 16,
+                    "margin_total": 0,
+                    },
+                "XMR": {
+                    "exchange_free": 0,
+                    "exchange_used": 0,
+                    "exchange_total": 0,
+                    "margin_total": D("-1.0"),
+                    "margin_free": 0,
+                    },
+                }
+
+    @mock.patch.object(helper, "get_ticker")
+    def test_in_currency(self, get_ticker):
+        portfolio.BalanceStore.all = {
+                "BTC": portfolio.Balance("BTC", {
+                    "total": "0.65",
+                    "exchange_total":"0.65",
+                    "exchange_free": "0.35",
+                    "exchange_used": "0.30"}),
+                "ETH": portfolio.Balance("ETH", {
+                    "total": 3,
+                    "exchange_total": 3,
+                    "exchange_free": 3,
+                    "exchange_used": 0}),
+                }
         market = mock.Mock()
-        market.fetch_ticker.side_effect = [
-                { "bid": 1, "ask": 3 },
-                portfolio.ExchangeError("foo"),
-                { "bid": 10, "ask": 40 },
-                portfolio.ExchangeError("foo"),
-                portfolio.ExchangeError("foo"),
-                ]
+        get_ticker.return_value = {
+                "bid": D("0.09"),
+                "ask": D("0.11"),
+                "average": D("0.1"),
+                }
 
-        ticker = portfolio.Trade.get_ticker("ETH", "ETC", market)
-        market.fetch_ticker.assert_called_with("ETH/ETC")
-        self.assertEqual(1, ticker["bid"])
-        self.assertEqual(3, ticker["ask"])
-        self.assertEqual(2, ticker["average"])
-        self.assertFalse(ticker["inverted"])
+        amounts = portfolio.BalanceStore.in_currency("BTC", market)
+        self.assertEqual("BTC", amounts["ETH"].currency)
+        self.assertEqual(D("0.65"), amounts["BTC"].value)
+        self.assertEqual(D("0.30"), amounts["ETH"].value)
 
-        ticker = portfolio.Trade.get_ticker("ETH", "XVG", market)
-        self.assertEqual(0.0625, ticker["average"])
-        self.assertTrue(ticker["inverted"])
-        self.assertIn("original", ticker)
-        self.assertEqual(10, ticker["original"]["bid"])
+        amounts = portfolio.BalanceStore.in_currency("BTC", market, compute_value="bid")
+        self.assertEqual(D("0.65"), amounts["BTC"].value)
+        self.assertEqual(D("0.27"), amounts["ETH"].value)
 
-        ticker = portfolio.Trade.get_ticker("XVG", "XMR", market)
-        self.assertIsNone(ticker)
+        amounts = portfolio.BalanceStore.in_currency("BTC", market, compute_value="bid", type="exchange_used")
+        self.assertEqual(D("0.30"), amounts["BTC"].value)
+        self.assertEqual(0, amounts["ETH"].value)
 
-        market.fetch_ticker.assert_has_calls([
-            mock.call("ETH/ETC"),
-            mock.call("ETH/XVG"),
-            mock.call("XVG/ETH"),
-            mock.call("XVG/XMR"),
-            mock.call("XMR/XVG"),
-            ])
+    def test_fetch_balances(self):
+        market = mock.Mock()
+        market.fetch_all_balances.return_value = self.fetch_balance
 
-        market2 = mock.Mock()
-        market2.fetch_ticker.side_effect = [
-                { "bid": 1, "ask": 3 },
-                { "bid": 1.2, "ask": 3.5 },
-                ]
-        ticker1 = portfolio.Trade.get_ticker("ETH", "ETC", market2)
-        ticker2 = portfolio.Trade.get_ticker("ETH", "ETC", market2)
-        ticker3 = portfolio.Trade.get_ticker("ETC", "ETH", market2)
-        market2.fetch_ticker.assert_called_once_with("ETH/ETC")
-        self.assertEqual(1, ticker1["bid"])
-        self.assertDictEqual(ticker1, ticker2)
-        self.assertDictEqual(ticker1, ticker3["original"])
+        portfolio.BalanceStore.fetch_balances(market)
+        self.assertNotIn("ETC", portfolio.BalanceStore.currencies())
+        self.assertListEqual(["USDT", "XVG", "XMR"], list(portfolio.BalanceStore.currencies()))
 
-        ticker4 = portfolio.Trade.get_ticker("ETH", "ETC", market2, refresh=True)
-        ticker5 = portfolio.Trade.get_ticker("ETH", "ETC", market2)
-        self.assertEqual(1.2, ticker4["bid"])
-        self.assertDictEqual(ticker4, ticker5)
+        portfolio.BalanceStore.all["ETC"] = portfolio.Balance("ETC", {
+            "exchange_total": "1", "exchange_free": "0",
+            "exchange_used": "1" })
+        portfolio.BalanceStore.fetch_balances(market)
+        self.assertEqual(0, portfolio.BalanceStore.all["ETC"].total)
+        self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(portfolio.BalanceStore.currencies()))
 
-        market3 = mock.Mock()
-        market3.fetch_ticker.side_effect = [
-                { "bid": 1, "ask": 3 },
-                { "bid": 1.2, "ask": 3.5 },
-                ]
-        ticker6 = portfolio.Trade.get_ticker("ETH", "ETC", market3)
-        portfolio.Trade.ticker_cache_timestamp -= 4
-        ticker7 = portfolio.Trade.get_ticker("ETH", "ETC", market3)
-        portfolio.Trade.ticker_cache_timestamp -= 2
-        ticker8 = portfolio.Trade.get_ticker("ETH", "ETC", market3)
-        self.assertDictEqual(ticker6, ticker7)
-        self.assertEqual(1.2, ticker8["bid"])
+    @mock.patch.object(portfolio.Portfolio, "repartition")
+    def test_dispatch_assets(self, repartition):
+        market = mock.Mock()
+        market.fetch_all_balances.return_value = self.fetch_balance
+        portfolio.BalanceStore.fetch_balances(market)
+
+        self.assertNotIn("XEM", portfolio.BalanceStore.currencies())
+
+        repartition.return_value = {
+                "XEM": (D("0.75"), "long"),
+                "BTC": (D("0.26"), "long"),
+                }
+
+        amounts = portfolio.BalanceStore.dispatch_assets(portfolio.Amount("BTC", "10.1"))
+        self.assertIn("XEM", portfolio.BalanceStore.currencies())
+        self.assertEqual(D("2.6"), amounts["BTC"].value)
+        self.assertEqual(D("7.5"), amounts["XEM"].value)
+
+    def test_currencies(self):
+        portfolio.BalanceStore.all = {
+                "BTC": portfolio.Balance("BTC", {
+                    "total": "0.65",
+                    "exchange_total":"0.65",
+                    "exchange_free": "0.35",
+                    "exchange_used": "0.30"}),
+                "ETH": portfolio.Balance("ETH", {
+                    "total": 3,
+                    "exchange_total": 3,
+                    "exchange_free": 3,
+                    "exchange_used": 0}),
+                }
+        self.assertListEqual(["BTC", "ETH"], list(portfolio.BalanceStore.currencies()))
+
+class ComputationTest(WebMockTestCase):
+    def test_compute_value(self):
+        compute = mock.Mock()
+        portfolio.Computation.compute_value("foo", "buy", compute_value=compute)
+        compute.assert_called_with("foo", "ask")
+
+        compute.reset_mock()
+        portfolio.Computation.compute_value("foo", "sell", compute_value=compute)
+        compute.assert_called_with("foo", "bid")
+
+        compute.reset_mock()
+        portfolio.Computation.compute_value("foo", "ask", compute_value=compute)
+        compute.assert_called_with("foo", "ask")
+
+        compute.reset_mock()
+        portfolio.Computation.compute_value("foo", "bid", compute_value=compute)
+        compute.assert_called_with("foo", "bid")
+
+        compute.reset_mock()
+        portfolio.Computation.computations["test"] = compute
+        portfolio.Computation.compute_value("foo", "bid", compute_value="test")
+        compute.assert_called_with("foo", "bid")
+
+
+class TradeTest(WebMockTestCase):
 
     def test_values_assertion(self):
         value_from = portfolio.Amount("BTC", "1.0")
@@ -736,14 +871,6 @@ class TradeTest(WebMockTestCase):
         trade = portfolio.Trade(value_from, value_to, "ETH")
         self.assertEqual(0, trade.value_from.linked_to)
 
-    def test_fetch_fees(self):
-        market = mock.Mock()
-        market.fetch_fees.return_value = "Foo"
-        self.assertEqual("Foo", portfolio.Trade.fetch_fees(market))
-        market.fetch_fees.assert_called_once()
-        self.assertEqual("Foo", portfolio.Trade.fetch_fees(market))
-        market.fetch_fees.assert_called_once()
-
     def test_action(self):
         value_from = portfolio.Amount("BTC", "1.0")
         value_from.linked_to = portfolio.Amount("ETH", "10.0")
@@ -821,34 +948,6 @@ class TradeTest(WebMockTestCase):
 
         self.assertEqual(portfolio.Amount("ETH", "0.31"), trade.filled_amount)
 
-    def test_prepare_orders(self):
-        trade_mock1 = mock.Mock()
-        trade_mock2 = mock.Mock()
-
-        portfolio.Trade.trades.append(trade_mock1)
-        portfolio.Trade.trades.append(trade_mock2)
-
-        portfolio.Trade.prepare_orders()
-        trade_mock1.prepare_order.assert_called_with(compute_value="default")
-        trade_mock2.prepare_order.assert_called_with(compute_value="default")
-
-        portfolio.Trade.prepare_orders(compute_value="bla")
-        trade_mock1.prepare_order.assert_called_with(compute_value="bla")
-        trade_mock2.prepare_order.assert_called_with(compute_value="bla")
-
-        trade_mock1.prepare_order.reset_mock()
-        trade_mock2.prepare_order.reset_mock()
-
-        trade_mock1.action = "foo"
-        trade_mock2.action = "bar"
-        portfolio.Trade.prepare_orders(only="bar")
-        trade_mock1.prepare_order.assert_not_called()
-        trade_mock2.prepare_order.assert_called_with(compute_value="default")
-
-    @unittest.skip("TODO")
-    def test_compute_trades(self):
-        pass
-
     @unittest.skip("TODO")
     def test_prepare_order(self):
         pass
@@ -857,77 +956,6 @@ class TradeTest(WebMockTestCase):
     def test_update_order(self):
         pass
 
-    @unittest.skip("TODO")
-    def test_follow_orders(self):
-        pass
-
-    @unittest.skip("TODO")
-    def test_move_balances(self):
-        pass
-
-    def test_all_orders(self):
-        trade_mock1 = mock.Mock()
-        trade_mock2 = mock.Mock()
-
-        order_mock1 = mock.Mock()
-        order_mock2 = mock.Mock()
-        order_mock3 = mock.Mock()
-
-        trade_mock1.orders = [order_mock1, order_mock2]
-        trade_mock2.orders = [order_mock3]
-
-        order_mock1.status = "pending"
-        order_mock2.status = "open"
-        order_mock3.status = "open"
-
-        portfolio.Trade.trades.append(trade_mock1)
-        portfolio.Trade.trades.append(trade_mock2)
-
-        orders = portfolio.Trade.all_orders()
-        self.assertEqual(3, len(orders))
-
-        open_orders = portfolio.Trade.all_orders(state="open")
-        self.assertEqual(2, len(open_orders))
-        self.assertEqual([order_mock2, order_mock3], open_orders)
-
-    @mock.patch.object(portfolio.Trade, "all_orders")
-    def test_run_orders(self, all_orders):
-        order_mock1 = mock.Mock()
-        order_mock2 = mock.Mock()
-        order_mock3 = mock.Mock()
-        all_orders.return_value = [order_mock1, order_mock2, order_mock3]
-        portfolio.Trade.run_orders()
-        all_orders.assert_called_with(state="pending")
-
-        order_mock1.run.assert_called()
-        order_mock2.run.assert_called()
-        order_mock3.run.assert_called()
-
-    @mock.patch.object(portfolio.Trade, "all_orders")
-    def test_update_all_orders_status(self, all_orders):
-        order_mock1 = mock.Mock()
-        order_mock2 = mock.Mock()
-        order_mock3 = mock.Mock()
-        all_orders.return_value = [order_mock1, order_mock2, order_mock3]
-        portfolio.Trade.update_all_orders_status()
-        all_orders.assert_called_with(state="open")
-
-        order_mock1.get_status.assert_called()
-        order_mock2.get_status.assert_called()
-        order_mock3.get_status.assert_called()
-
-    def test_print_all_with_order(self):
-        trade_mock1 = mock.Mock()
-        trade_mock2 = mock.Mock()
-        trade_mock3 = mock.Mock()
-        portfolio.Trade.trades = [trade_mock1, trade_mock2, trade_mock3]
-
-        portfolio.Trade.print_all_with_order()
-
-        trade_mock1.print_with_order.assert_called()
-        trade_mock2.print_with_order.assert_called()
-        trade_mock3.print_with_order.assert_called()
-
     @mock.patch('sys.stdout', new_callable=StringIO)
     def test_print_with_order(self, mock_stdout):
         value_from = portfolio.Amount("BTC", "0.5")
@@ -951,28 +979,6 @@ class TradeTest(WebMockTestCase):
         self.assertEqual("\tMock 1", out[1])
         self.assertEqual("\tMock 2", out[2])
 
-    def test_compute_value(self):
-        compute = mock.Mock()
-        portfolio.Trade.compute_value("foo", "buy", compute_value=compute)
-        compute.assert_called_with("foo", "ask")
-
-        compute.reset_mock()
-        portfolio.Trade.compute_value("foo", "sell", compute_value=compute)
-        compute.assert_called_with("foo", "bid")
-
-        compute.reset_mock()
-        portfolio.Trade.compute_value("foo", "ask", compute_value=compute)
-        compute.assert_called_with("foo", "ask")
-
-        compute.reset_mock()
-        portfolio.Trade.compute_value("foo", "bid", compute_value=compute)
-        compute.assert_called_with("foo", "bid")
-
-        compute.reset_mock()
-        portfolio.Computation.computations["test"] = compute
-        portfolio.Trade.compute_value("foo", "bid", compute_value="test")
-        compute.assert_called_with("foo", "bid")
-
     def test__repr(self):
         value_from = portfolio.Amount("BTC", "0.5")
         value_from.linked_to = portfolio.Amount("ETH", "10.0")
@@ -1045,7 +1051,7 @@ class AcceptanceTest(WebMockTestCase):
                         "ask": D("0.0012")
                         }
             if symbol == "USDT/BTC":
-                raise portfolio.ExchangeError
+                raise helper.ExchangeError
             if symbol == "BTC/USDT":
                 return {
                         "symbol": "BTC/USDT",
@@ -1059,15 +1065,15 @@ class AcceptanceTest(WebMockTestCase):
         market.fetch_ticker.side_effect = fetch_ticker
         with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition):
             # Action 1
-            portfolio.Balance.prepare_trades(market)
+            helper.prepare_trades(market)
 
-        balances = portfolio.Balance.known_balances
+        balances = portfolio.BalanceStore.all
         self.assertEqual(portfolio.Amount("ETH", 1), balances["ETH"].total)
         self.assertEqual(portfolio.Amount("ETC", 4), balances["ETC"].total)
         self.assertEqual(portfolio.Amount("XVG", 1000), balances["XVG"].total)
 
 
-        trades = portfolio.Trade.trades
+        trades = TradeStore.all
         self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades[0].value_from)
         self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[0].value_to)
         self.assertEqual("dispose", trades[0].action)
@@ -1123,7 +1129,7 @@ class AcceptanceTest(WebMockTestCase):
         market.order_precision.return_value = 8
 
         # Action 3
-        portfolio.Trade.run_orders()
+        portfolio.TradeStore.run_orders()
 
         self.assertEqual("open", all_orders[0].status)
         self.assertEqual("open", all_orders[1].status)
@@ -1131,7 +1137,7 @@ class AcceptanceTest(WebMockTestCase):
         market.fetch_order.return_value = { "status": "closed" }
         with mock.patch.object(portfolio.time, "sleep") as sleep:
             # Action 4
-            portfolio.Trade.follow_orders(verbose=False)
+            helper.follow_orders(verbose=False)
 
             sleep.assert_called_with(30)
 
@@ -1164,16 +1170,16 @@ class AcceptanceTest(WebMockTestCase):
 
         with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition):
             # Action 5
-            portfolio.Balance.update_trades(market, only="buy", compute_value="average")
+            helper.update_trades(market, only="buy", compute_value="average")
 
-        balances = portfolio.Balance.known_balances
+        balances = portfolio.BalanceStore.all
         self.assertEqual(portfolio.Amount("ETH", 1 / D("3")), balances["ETH"].total)
         self.assertEqual(portfolio.Amount("ETC", 4), balances["ETC"].total)
         self.assertEqual(portfolio.Amount("BTC", D("0.134")), balances["BTC"].total)
         self.assertEqual(portfolio.Amount("XVG", 0), balances["XVG"].total)
 
 
-        trades = portfolio.Trade.trades
+        trades = TradeStore.all
         self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades["ETH"].value_from)
         self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades["ETH"].value_to)
         self.assertEqual("sell", trades["ETH"].action)
@@ -1232,11 +1238,11 @@ class AcceptanceTest(WebMockTestCase):
 
         # Action 7
         # TODO
-        # portfolio.Trade.run_orders()
+        # portfolio.TradeStore.run_orders()
 
         with mock.patch.object(portfolio.time, "sleep") as sleep:
             # Action 8
-            portfolio.Trade.follow_orders(verbose=False)
+            helper.follow_orders(verbose=False)
 
             sleep.assert_called_with(30)