aboutsummaryrefslogtreecommitdiff
path: root/test.py
diff options
context:
space:
mode:
Diffstat (limited to 'test.py')
-rw-r--r--test.py1730
1 files changed, 916 insertions, 814 deletions
diff --git a/test.py b/test.py
index fc331c3..a9cae94 100644
--- a/test.py
+++ b/test.py
@@ -1,12 +1,13 @@
1import sys 1import sys
2import portfolio 2import portfolio
3import unittest 3import unittest
4import datetime
4from decimal import Decimal as D 5from decimal import Decimal as D
5from unittest import mock 6from unittest import mock
6import requests 7import requests
7import requests_mock 8import requests_mock
8from io import StringIO 9from io import StringIO
9import helper 10import portfolio, helper, market
10 11
11limits = ["acceptance", "unit"] 12limits = ["acceptance", "unit"]
12for test_type in limits: 13for test_type in limits:
@@ -26,18 +27,15 @@ class WebMockTestCase(unittest.TestCase):
26 self.wm = requests_mock.Mocker() 27 self.wm = requests_mock.Mocker()
27 self.wm.start() 28 self.wm.start()
28 29
30 # market
31 self.m = mock.Mock(name="Market", spec=market.Market)
32 self.m.debug = False
33
29 self.patchers = [ 34 self.patchers = [
30 mock.patch.multiple(portfolio.ReportStore,
31 logs=[], verbose_print=True),
32 mock.patch.multiple(portfolio.BalanceStore,
33 all={},),
34 mock.patch.multiple(portfolio.TradeStore,
35 all=[],
36 debug=False),
37 mock.patch.multiple(portfolio.Portfolio, last_date=None, data=None, liquidities={}), 35 mock.patch.multiple(portfolio.Portfolio, last_date=None, data=None, liquidities={}),
38 mock.patch.multiple(portfolio.Computation, 36 mock.patch.multiple(portfolio.Computation,
39 computations=portfolio.Computation.computations), 37 computations=portfolio.Computation.computations),
40 mock.patch.multiple(helper, 38 mock.patch.multiple(market.Market,
41 fees_cache={}, 39 fees_cache={},
42 ticker_cache={}, 40 ticker_cache={},
43 ticker_cache_timestamp=self.time.time()), 41 ticker_cache_timestamp=self.time.time()),
@@ -65,41 +63,39 @@ class PortfolioTest(WebMockTestCase):
65 63
66 self.wm.get(portfolio.Portfolio.URL, text=self.json_response) 64 self.wm.get(portfolio.Portfolio.URL, text=self.json_response)
67 65
68 @mock.patch("portfolio.ReportStore") 66 def test_get_cryptoportfolio(self):
69 def test_get_cryptoportfolio(self, report_store):
70 self.wm.get(portfolio.Portfolio.URL, [ 67 self.wm.get(portfolio.Portfolio.URL, [
71 {"text":'{ "foo": "bar" }', "status_code": 200}, 68 {"text":'{ "foo": "bar" }', "status_code": 200},
72 {"text": "System Error", "status_code": 500}, 69 {"text": "System Error", "status_code": 500},
73 {"exc": requests.exceptions.ConnectTimeout}, 70 {"exc": requests.exceptions.ConnectTimeout},
74 ]) 71 ])
75 portfolio.Portfolio.get_cryptoportfolio() 72 portfolio.Portfolio.get_cryptoportfolio(self.m)
76 self.assertIn("foo", portfolio.Portfolio.data) 73 self.assertIn("foo", portfolio.Portfolio.data)
77 self.assertEqual("bar", portfolio.Portfolio.data["foo"]) 74 self.assertEqual("bar", portfolio.Portfolio.data["foo"])
78 self.assertTrue(self.wm.called) 75 self.assertTrue(self.wm.called)
79 self.assertEqual(1, self.wm.call_count) 76 self.assertEqual(1, self.wm.call_count)
80 report_store.log_error.assert_not_called() 77 self.m.report.log_error.assert_not_called()
81 report_store.log_http_request.assert_called_once() 78 self.m.report.log_http_request.assert_called_once()
82 report_store.log_http_request.reset_mock() 79 self.m.report.log_http_request.reset_mock()
83 80
84 portfolio.Portfolio.get_cryptoportfolio() 81 portfolio.Portfolio.get_cryptoportfolio(self.m)
85 self.assertIsNone(portfolio.Portfolio.data) 82 self.assertIsNone(portfolio.Portfolio.data)
86 self.assertEqual(2, self.wm.call_count) 83 self.assertEqual(2, self.wm.call_count)
87 report_store.log_error.assert_not_called() 84 self.m.report.log_error.assert_not_called()
88 report_store.log_http_request.assert_called_once() 85 self.m.report.log_http_request.assert_called_once()
89 report_store.log_http_request.reset_mock() 86 self.m.report.log_http_request.reset_mock()
90 87
91 88
92 portfolio.Portfolio.data = "Foo" 89 portfolio.Portfolio.data = "Foo"
93 portfolio.Portfolio.get_cryptoportfolio() 90 portfolio.Portfolio.get_cryptoportfolio(self.m)
94 self.assertEqual("Foo", portfolio.Portfolio.data) 91 self.assertEqual("Foo", portfolio.Portfolio.data)
95 self.assertEqual(3, self.wm.call_count) 92 self.assertEqual(3, self.wm.call_count)
96 report_store.log_error.assert_called_once_with("get_cryptoportfolio", 93 self.m.report.log_error.assert_called_once_with("get_cryptoportfolio",
97 exception=mock.ANY) 94 exception=mock.ANY)
98 report_store.log_http_request.assert_not_called() 95 self.m.report.log_http_request.assert_not_called()
99 96
100 @mock.patch("portfolio.ReportStore") 97 def test_parse_cryptoportfolio(self):
101 def test_parse_cryptoportfolio(self, report_store): 98 portfolio.Portfolio.parse_cryptoportfolio(self.m)
102 portfolio.Portfolio.parse_cryptoportfolio()
103 99
104 self.assertListEqual( 100 self.assertListEqual(
105 ["medium", "high"], 101 ["medium", "high"],
@@ -135,22 +131,21 @@ class PortfolioTest(WebMockTestCase):
135 self.assertDictEqual(expected, liquidities["medium"][date]) 131 self.assertDictEqual(expected, liquidities["medium"][date])
136 self.assertEqual(portfolio.datetime(2018, 1, 15), portfolio.Portfolio.last_date) 132 self.assertEqual(portfolio.datetime(2018, 1, 15), portfolio.Portfolio.last_date)
137 133
138 report_store.log_http_request.assert_called_once_with("GET", 134 self.m.report.log_http_request.assert_called_once_with("GET",
139 portfolio.Portfolio.URL, None, mock.ANY, mock.ANY) 135 portfolio.Portfolio.URL, None, mock.ANY, mock.ANY)
140 report_store.log_http_request.reset_mock() 136 self.m.report.log_http_request.reset_mock()
141 137
142 # It doesn't refetch the data when available 138 # It doesn't refetch the data when available
143 portfolio.Portfolio.parse_cryptoportfolio() 139 portfolio.Portfolio.parse_cryptoportfolio(self.m)
144 report_store.log_http_request.assert_not_called() 140 self.m.report.log_http_request.assert_not_called()
145 141
146 self.assertEqual(1, self.wm.call_count) 142 self.assertEqual(1, self.wm.call_count)
147 143
148 portfolio.Portfolio.parse_cryptoportfolio(refetch=True) 144 portfolio.Portfolio.parse_cryptoportfolio(self.m, refetch=True)
149 self.assertEqual(2, self.wm.call_count) 145 self.assertEqual(2, self.wm.call_count)
150 report_store.log_http_request.assert_called_once() 146 self.m.report.log_http_request.assert_called_once()
151 147
152 @mock.patch("portfolio.ReportStore") 148 def test_repartition(self):
153 def test_repartition(self, report_store):
154 expected_medium = { 149 expected_medium = {
155 'BTC': (D("1.1102e-16"), "long"), 150 'BTC': (D("1.1102e-16"), "long"),
156 'USDT': (D("0.1"), "long"), 151 'USDT': (D("0.1"), "long"),
@@ -173,25 +168,26 @@ class PortfolioTest(WebMockTestCase):
173 'GAS': (D("0.1308"), "long"), 168 'GAS': (D("0.1308"), "long"),
174 } 169 }
175 170
176 self.assertEqual(expected_medium, portfolio.Portfolio.repartition()) 171 self.assertEqual(expected_medium, portfolio.Portfolio.repartition(self.m))
177 self.assertEqual(expected_medium, portfolio.Portfolio.repartition(liquidity="medium")) 172 self.assertEqual(expected_medium, portfolio.Portfolio.repartition(self.m, liquidity="medium"))
178 self.assertEqual(expected_high, portfolio.Portfolio.repartition(liquidity="high")) 173 self.assertEqual(expected_high, portfolio.Portfolio.repartition(self.m, liquidity="high"))
179 174
180 self.assertEqual(1, self.wm.call_count) 175 self.assertEqual(1, self.wm.call_count)
181 176
182 portfolio.Portfolio.repartition() 177 portfolio.Portfolio.repartition(self.m)
183 self.assertEqual(1, self.wm.call_count) 178 self.assertEqual(1, self.wm.call_count)
184 179
185 portfolio.Portfolio.repartition(refetch=True) 180 portfolio.Portfolio.repartition(self.m, refetch=True)
186 self.assertEqual(2, self.wm.call_count) 181 self.assertEqual(2, self.wm.call_count)
187 report_store.log_http_request.assert_called() 182 self.m.report.log_http_request.assert_called()
188 self.assertEqual(2, report_store.log_http_request.call_count) 183 self.assertEqual(2, self.m.report.log_http_request.call_count)
189 184
190 @mock.patch.object(portfolio.time, "sleep") 185 @mock.patch.object(portfolio.time, "sleep")
191 @mock.patch.object(portfolio.Portfolio, "repartition") 186 @mock.patch.object(portfolio.Portfolio, "repartition")
192 def test_wait_for_recent(self, repartition, sleep): 187 def test_wait_for_recent(self, repartition, sleep):
193 self.call_count = 0 188 self.call_count = 0
194 def _repartition(refetch): 189 def _repartition(market, refetch):
190 self.assertEqual(self.m, market)
195 self.assertTrue(refetch) 191 self.assertTrue(refetch)
196 self.call_count += 1 192 self.call_count += 1
197 portfolio.Portfolio.last_date = portfolio.datetime.now()\ 193 portfolio.Portfolio.last_date = portfolio.datetime.now()\
@@ -199,16 +195,17 @@ class PortfolioTest(WebMockTestCase):
199 + portfolio.timedelta(self.call_count) 195 + portfolio.timedelta(self.call_count)
200 repartition.side_effect = _repartition 196 repartition.side_effect = _repartition
201 197
202 portfolio.Portfolio.wait_for_recent() 198 portfolio.Portfolio.wait_for_recent(self.m)
203 sleep.assert_called_with(30) 199 sleep.assert_called_with(30)
204 self.assertEqual(6, sleep.call_count) 200 self.assertEqual(6, sleep.call_count)
205 self.assertEqual(7, repartition.call_count) 201 self.assertEqual(7, repartition.call_count)
202 self.m.report.print_log.assert_called_with("Attempt to fetch up-to-date cryptoportfolio")
206 203
207 sleep.reset_mock() 204 sleep.reset_mock()
208 repartition.reset_mock() 205 repartition.reset_mock()
209 portfolio.Portfolio.last_date = None 206 portfolio.Portfolio.last_date = None
210 self.call_count = 0 207 self.call_count = 0
211 portfolio.Portfolio.wait_for_recent(delta=15) 208 portfolio.Portfolio.wait_for_recent(self.m, delta=15)
212 sleep.assert_not_called() 209 sleep.assert_not_called()
213 self.assertEqual(1, repartition.call_count) 210 self.assertEqual(1, repartition.call_count)
214 211
@@ -216,7 +213,7 @@ class PortfolioTest(WebMockTestCase):
216 repartition.reset_mock() 213 repartition.reset_mock()
217 portfolio.Portfolio.last_date = None 214 portfolio.Portfolio.last_date = None
218 self.call_count = 0 215 self.call_count = 0
219 portfolio.Portfolio.wait_for_recent(delta=1) 216 portfolio.Portfolio.wait_for_recent(self.m, delta=1)
220 sleep.assert_called_with(30) 217 sleep.assert_called_with(30)
221 self.assertEqual(9, sleep.call_count) 218 self.assertEqual(9, sleep.call_count)
222 self.assertEqual(10, repartition.call_count) 219 self.assertEqual(10, repartition.call_count)
@@ -231,35 +228,34 @@ class AmountTest(WebMockTestCase):
231 def test_in_currency(self): 228 def test_in_currency(self):
232 amount = portfolio.Amount("ETC", 10) 229 amount = portfolio.Amount("ETC", 10)
233 230
234 self.assertEqual(amount, amount.in_currency("ETC", None)) 231 self.assertEqual(amount, amount.in_currency("ETC", self.m))
235 232
236 ticker_mock = unittest.mock.Mock() 233 with self.subTest(desc="no ticker for currency"):
237 with mock.patch.object(helper, 'get_ticker', new=ticker_mock): 234 self.m.get_ticker.return_value = None
238 ticker_mock.return_value = None
239 235
240 self.assertRaises(Exception, amount.in_currency, "ETH", None) 236 self.assertRaises(Exception, amount.in_currency, "ETH", self.m)
241 237
242 with mock.patch.object(helper, 'get_ticker', new=ticker_mock): 238 with self.subTest(desc="nominal case"):
243 ticker_mock.return_value = { 239 self.m.get_ticker.return_value = {
244 "bid": D("0.2"), 240 "bid": D("0.2"),
245 "ask": D("0.4"), 241 "ask": D("0.4"),
246 "average": D("0.3"), 242 "average": D("0.3"),
247 "foo": "bar", 243 "foo": "bar",
248 } 244 }
249 converted_amount = amount.in_currency("ETH", None) 245 converted_amount = amount.in_currency("ETH", self.m)
250 246
251 self.assertEqual(D("3.0"), converted_amount.value) 247 self.assertEqual(D("3.0"), converted_amount.value)
252 self.assertEqual("ETH", converted_amount.currency) 248 self.assertEqual("ETH", converted_amount.currency)
253 self.assertEqual(amount, converted_amount.linked_to) 249 self.assertEqual(amount, converted_amount.linked_to)
254 self.assertEqual("bar", converted_amount.ticker["foo"]) 250 self.assertEqual("bar", converted_amount.ticker["foo"])
255 251
256 converted_amount = amount.in_currency("ETH", None, action="bid", compute_value="default") 252 converted_amount = amount.in_currency("ETH", self.m, action="bid", compute_value="default")
257 self.assertEqual(D("2"), converted_amount.value) 253 self.assertEqual(D("2"), converted_amount.value)
258 254
259 converted_amount = amount.in_currency("ETH", None, compute_value="ask") 255 converted_amount = amount.in_currency("ETH", self.m, compute_value="ask")
260 self.assertEqual(D("4"), converted_amount.value) 256 self.assertEqual(D("4"), converted_amount.value)
261 257
262 converted_amount = amount.in_currency("ETH", None, rate=D("0.02")) 258 converted_amount = amount.in_currency("ETH", self.m, rate=D("0.02"))
263 self.assertEqual(D("0.2"), converted_amount.value) 259 self.assertEqual(D("0.2"), converted_amount.value)
264 260
265 def test__round(self): 261 def test__round(self):
@@ -318,7 +314,7 @@ class AmountTest(WebMockTestCase):
318 with self.assertRaises(Exception): 314 with self.assertRaises(Exception):
319 3 - amount 315 3 - amount
320 316
321 self.assertEqual(portfolio.Amount("ETH", "-1.6"), -amount) 317 self.assertEqual(portfolio.Amount("ETH", "-1.6"), 0-amount)
322 318
323 def test__mul(self): 319 def test__mul(self):
324 amount = portfolio.Amount("XEM", 11) 320 amount = portfolio.Amount("XEM", 11)
@@ -544,34 +540,76 @@ class BalanceTest(WebMockTestCase):
544 self.assertEqual(D(0), as_json["margin_borrowed"]) 540 self.assertEqual(D(0), as_json["margin_borrowed"])
545 541
546@unittest.skipUnless("unit" in limits, "Unit skipped") 542@unittest.skipUnless("unit" in limits, "Unit skipped")
547class HelperTest(WebMockTestCase): 543class MarketTest(WebMockTestCase):
544 def setUp(self):
545 super(MarketTest, self).setUp()
546
547 self.ccxt = mock.Mock(spec=market.ccxt.poloniexE)
548
549 def test_values(self):
550 m = market.Market(self.ccxt)
551
552 self.assertEqual(self.ccxt, m.ccxt)
553 self.assertFalse(m.debug)
554 self.assertIsInstance(m.report, market.ReportStore)
555 self.assertIsInstance(m.trades, market.TradeStore)
556 self.assertIsInstance(m.balances, market.BalanceStore)
557 self.assertEqual(m, m.report.market)
558 self.assertEqual(m, m.trades.market)
559 self.assertEqual(m, m.balances.market)
560 self.assertEqual(m, m.ccxt._market)
561
562 m = market.Market(self.ccxt, debug=True)
563 self.assertTrue(m.debug)
564
565 m = market.Market(self.ccxt, debug=False)
566 self.assertFalse(m.debug)
567
568 @mock.patch("market.ccxt")
569 def test_from_config(self, ccxt):
570 with mock.patch("market.ReportStore"):
571 ccxt.poloniexE.return_value = self.ccxt
572 self.ccxt.session.request.return_value = "response"
573
574 m = market.Market.from_config("config")
575
576 self.assertEqual(self.ccxt, m.ccxt)
577
578 self.ccxt.session.request("GET", "URL", data="data",
579 headers="headers")
580 m.report.log_http_request.assert_called_with('GET', 'URL', 'data',
581 'headers', 'response')
582
583 m = market.Market.from_config("config", debug=True)
584 self.assertEqual(True, m.debug)
585
548 def test_get_ticker(self): 586 def test_get_ticker(self):
549 market = mock.Mock() 587 m = market.Market(self.ccxt)
550 market.fetch_ticker.side_effect = [ 588 self.ccxt.fetch_ticker.side_effect = [
551 { "bid": 1, "ask": 3 }, 589 { "bid": 1, "ask": 3 },
552 helper.ExchangeError("foo"), 590 market.ExchangeError("foo"),
553 { "bid": 10, "ask": 40 }, 591 { "bid": 10, "ask": 40 },
554 helper.ExchangeError("foo"), 592 market.ExchangeError("foo"),
555 helper.ExchangeError("foo"), 593 market.ExchangeError("foo"),
556 ] 594 ]
557 595
558 ticker = helper.get_ticker("ETH", "ETC", market) 596 ticker = m.get_ticker("ETH", "ETC")
559 market.fetch_ticker.assert_called_with("ETH/ETC") 597 self.ccxt.fetch_ticker.assert_called_with("ETH/ETC")
560 self.assertEqual(1, ticker["bid"]) 598 self.assertEqual(1, ticker["bid"])
561 self.assertEqual(3, ticker["ask"]) 599 self.assertEqual(3, ticker["ask"])
562 self.assertEqual(2, ticker["average"]) 600 self.assertEqual(2, ticker["average"])
563 self.assertFalse(ticker["inverted"]) 601 self.assertFalse(ticker["inverted"])
564 602
565 ticker = helper.get_ticker("ETH", "XVG", market) 603 ticker = m.get_ticker("ETH", "XVG")
566 self.assertEqual(0.0625, ticker["average"]) 604 self.assertEqual(0.0625, ticker["average"])
567 self.assertTrue(ticker["inverted"]) 605 self.assertTrue(ticker["inverted"])
568 self.assertIn("original", ticker) 606 self.assertIn("original", ticker)
569 self.assertEqual(10, ticker["original"]["bid"]) 607 self.assertEqual(10, ticker["original"]["bid"])
570 608
571 ticker = helper.get_ticker("XVG", "XMR", market) 609 ticker = m.get_ticker("XVG", "XMR")
572 self.assertIsNone(ticker) 610 self.assertIsNone(ticker)
573 611
574 market.fetch_ticker.assert_has_calls([ 612 self.ccxt.fetch_ticker.assert_has_calls([
575 mock.call("ETH/ETC"), 613 mock.call("ETH/ETC"),
576 mock.call("ETH/XVG"), 614 mock.call("ETH/XVG"),
577 mock.call("XVG/ETH"), 615 mock.call("XVG/ETH"),
@@ -579,56 +617,62 @@ class HelperTest(WebMockTestCase):
579 mock.call("XMR/XVG"), 617 mock.call("XMR/XVG"),
580 ]) 618 ])
581 619
582 market2 = mock.Mock() 620 self.ccxt = mock.Mock(spec=market.ccxt.poloniexE)
583 market2.fetch_ticker.side_effect = [ 621 m1b = market.Market(self.ccxt)
622 m1b.get_ticker("ETH", "ETC")
623 self.ccxt.fetch_ticker.assert_not_called()
624
625 self.ccxt = mock.Mock(spec=market.ccxt.poloniex)
626 m2 = market.Market(self.ccxt)
627 self.ccxt.fetch_ticker.side_effect = [
584 { "bid": 1, "ask": 3 }, 628 { "bid": 1, "ask": 3 },
585 { "bid": 1.2, "ask": 3.5 }, 629 { "bid": 1.2, "ask": 3.5 },
586 ] 630 ]
587 ticker1 = helper.get_ticker("ETH", "ETC", market2) 631 ticker1 = m2.get_ticker("ETH", "ETC")
588 ticker2 = helper.get_ticker("ETH", "ETC", market2) 632 ticker2 = m2.get_ticker("ETH", "ETC")
589 ticker3 = helper.get_ticker("ETC", "ETH", market2) 633 ticker3 = m2.get_ticker("ETC", "ETH")
590 market2.fetch_ticker.assert_called_once_with("ETH/ETC") 634 self.ccxt.fetch_ticker.assert_called_once_with("ETH/ETC")
591 self.assertEqual(1, ticker1["bid"]) 635 self.assertEqual(1, ticker1["bid"])
592 self.assertDictEqual(ticker1, ticker2) 636 self.assertDictEqual(ticker1, ticker2)
593 self.assertDictEqual(ticker1, ticker3["original"]) 637 self.assertDictEqual(ticker1, ticker3["original"])
594 638
595 ticker4 = helper.get_ticker("ETH", "ETC", market2, refresh=True) 639 ticker4 = m2.get_ticker("ETH", "ETC", refresh=True)
596 ticker5 = helper.get_ticker("ETH", "ETC", market2) 640 ticker5 = m2.get_ticker("ETH", "ETC")
597 self.assertEqual(1.2, ticker4["bid"]) 641 self.assertEqual(1.2, ticker4["bid"])
598 self.assertDictEqual(ticker4, ticker5) 642 self.assertDictEqual(ticker4, ticker5)
599 643
600 market3 = mock.Mock() 644 self.ccxt = mock.Mock(spec=market.ccxt.binance)
601 market3.fetch_ticker.side_effect = [ 645 m3 = market.Market(self.ccxt)
646 self.ccxt.fetch_ticker.side_effect = [
602 { "bid": 1, "ask": 3 }, 647 { "bid": 1, "ask": 3 },
603 { "bid": 1.2, "ask": 3.5 }, 648 { "bid": 1.2, "ask": 3.5 },
604 ] 649 ]
605 ticker6 = helper.get_ticker("ETH", "ETC", market3) 650 ticker6 = m3.get_ticker("ETH", "ETC")
606 helper.ticker_cache_timestamp -= 4 651 m3.ticker_cache_timestamp -= 4
607 ticker7 = helper.get_ticker("ETH", "ETC", market3) 652 ticker7 = m3.get_ticker("ETH", "ETC")
608 helper.ticker_cache_timestamp -= 2 653 m3.ticker_cache_timestamp -= 2
609 ticker8 = helper.get_ticker("ETH", "ETC", market3) 654 ticker8 = m3.get_ticker("ETH", "ETC")
610 self.assertDictEqual(ticker6, ticker7) 655 self.assertDictEqual(ticker6, ticker7)
611 self.assertEqual(1.2, ticker8["bid"]) 656 self.assertEqual(1.2, ticker8["bid"])
612 657
613 def test_fetch_fees(self): 658 def test_fetch_fees(self):
614 market = mock.Mock() 659 m = market.Market(self.ccxt)
615 market.fetch_fees.return_value = "Foo" 660 self.ccxt.fetch_fees.return_value = "Foo"
616 self.assertEqual("Foo", helper.fetch_fees(market)) 661 self.assertEqual("Foo", m.fetch_fees())
617 market.fetch_fees.assert_called_once() 662 self.ccxt.fetch_fees.assert_called_once()
618 self.assertEqual("Foo", helper.fetch_fees(market)) 663 self.ccxt.reset_mock()
619 market.fetch_fees.assert_called_once() 664 self.assertEqual("Foo", m.fetch_fees())
665 self.ccxt.fetch_fees.assert_not_called()
620 666
621 @mock.patch.object(portfolio.Portfolio, "repartition") 667 @mock.patch.object(portfolio.Portfolio, "repartition")
622 @mock.patch.object(helper, "get_ticker") 668 @mock.patch.object(market.Market, "get_ticker")
623 @mock.patch.object(portfolio.TradeStore, "compute_trades") 669 @mock.patch.object(market.TradeStore, "compute_trades")
624 @mock.patch("store.ReportStore") 670 def test_prepare_trades(self, compute_trades, get_ticker, repartition):
625 @mock.patch("helper.ReportStore")
626 def test_prepare_trades(self, report_store_h, report_store, compute_trades, get_ticker, repartition):
627 repartition.return_value = { 671 repartition.return_value = {
628 "XEM": (D("0.75"), "long"), 672 "XEM": (D("0.75"), "long"),
629 "BTC": (D("0.25"), "long"), 673 "BTC": (D("0.25"), "long"),
630 } 674 }
631 def _get_ticker(c1, c2, market): 675 def _get_ticker(c1, c2):
632 if c1 == "USDT" and c2 == "BTC": 676 if c1 == "USDT" and c2 == "BTC":
633 return { "average": D("0.0001") } 677 return { "average": D("0.0001") }
634 if c1 == "XVG" and c2 == "BTC": 678 if c1 == "XVG" and c2 == "BTC":
@@ -638,46 +682,45 @@ class HelperTest(WebMockTestCase):
638 self.fail("Should be called with {}, {}".format(c1, c2)) 682 self.fail("Should be called with {}, {}".format(c1, c2))
639 get_ticker.side_effect = _get_ticker 683 get_ticker.side_effect = _get_ticker
640 684
641 market = mock.Mock() 685 with mock.patch("market.ReportStore"):
642 market.fetch_all_balances.return_value = { 686 m = market.Market(self.ccxt)
643 "USDT": { 687 self.ccxt.fetch_all_balances.return_value = {
644 "exchange_free": D("10000.0"), 688 "USDT": {
645 "exchange_used": D("0.0"), 689 "exchange_free": D("10000.0"),
646 "exchange_total": D("10000.0"), 690 "exchange_used": D("0.0"),
647 "total": D("10000.0") 691 "exchange_total": D("10000.0"),
648 }, 692 "total": D("10000.0")
649 "XVG": { 693 },
650 "exchange_free": D("10000.0"), 694 "XVG": {
651 "exchange_used": D("0.0"), 695 "exchange_free": D("10000.0"),
652 "exchange_total": D("10000.0"), 696 "exchange_used": D("0.0"),
653 "total": D("10000.0") 697 "exchange_total": D("10000.0"),
654 }, 698 "total": D("10000.0")
655 } 699 },
656 portfolio.BalanceStore.fetch_balances(market, tag="tag") 700 }
657 701
658 helper.prepare_trades(market) 702 m.balances.fetch_balances(tag="tag")
659 compute_trades.assert_called()
660 703
661 call = compute_trades.call_args 704 m.prepare_trades()
662 self.assertEqual(market, call[1]["market"]) 705 compute_trades.assert_called()
663 self.assertEqual(1, call[0][0]["USDT"].value) 706
664 self.assertEqual(D("0.01"), call[0][0]["XVG"].value) 707 call = compute_trades.call_args
665 self.assertEqual(D("0.2525"), call[0][1]["BTC"].value) 708 self.assertEqual(1, call[0][0]["USDT"].value)
666 self.assertEqual(D("0.7575"), call[0][1]["XEM"].value) 709 self.assertEqual(D("0.01"), call[0][0]["XVG"].value)
667 report_store_h.log_stage.assert_called_once_with("prepare_trades") 710 self.assertEqual(D("0.2525"), call[0][1]["BTC"].value)
668 report_store.log_balances.assert_called_once_with(market, tag="tag") 711 self.assertEqual(D("0.7575"), call[0][1]["XEM"].value)
712 m.report.log_stage.assert_called_once_with("prepare_trades")
713 m.report.log_balances.assert_called_once_with(tag="tag")
669 714
670 @mock.patch.object(portfolio.Portfolio, "repartition") 715 @mock.patch.object(portfolio.Portfolio, "repartition")
671 @mock.patch.object(helper, "get_ticker") 716 @mock.patch.object(market.Market, "get_ticker")
672 @mock.patch.object(portfolio.TradeStore, "compute_trades") 717 @mock.patch.object(market.TradeStore, "compute_trades")
673 @mock.patch("store.ReportStore") 718 def test_update_trades(self, compute_trades, get_ticker, repartition):
674 @mock.patch("helper.ReportStore")
675 def test_update_trades(self, report_store_h, report_store, compute_trades, get_ticker, repartition):
676 repartition.return_value = { 719 repartition.return_value = {
677 "XEM": (D("0.75"), "long"), 720 "XEM": (D("0.75"), "long"),
678 "BTC": (D("0.25"), "long"), 721 "BTC": (D("0.25"), "long"),
679 } 722 }
680 def _get_ticker(c1, c2, market): 723 def _get_ticker(c1, c2):
681 if c1 == "USDT" and c2 == "BTC": 724 if c1 == "USDT" and c2 == "BTC":
682 return { "average": D("0.0001") } 725 return { "average": D("0.0001") }
683 if c1 == "XVG" and c2 == "BTC": 726 if c1 == "XVG" and c2 == "BTC":
@@ -687,42 +730,41 @@ class HelperTest(WebMockTestCase):
687 self.fail("Should be called with {}, {}".format(c1, c2)) 730 self.fail("Should be called with {}, {}".format(c1, c2))
688 get_ticker.side_effect = _get_ticker 731 get_ticker.side_effect = _get_ticker
689 732
690 market = mock.Mock() 733 with mock.patch("market.ReportStore"):
691 market.fetch_all_balances.return_value = { 734 m = market.Market(self.ccxt)
692 "USDT": { 735 self.ccxt.fetch_all_balances.return_value = {
693 "exchange_free": D("10000.0"), 736 "USDT": {
694 "exchange_used": D("0.0"), 737 "exchange_free": D("10000.0"),
695 "exchange_total": D("10000.0"), 738 "exchange_used": D("0.0"),
696 "total": D("10000.0") 739 "exchange_total": D("10000.0"),
697 }, 740 "total": D("10000.0")
698 "XVG": { 741 },
699 "exchange_free": D("10000.0"), 742 "XVG": {
700 "exchange_used": D("0.0"), 743 "exchange_free": D("10000.0"),
701 "exchange_total": D("10000.0"), 744 "exchange_used": D("0.0"),
702 "total": D("10000.0") 745 "exchange_total": D("10000.0"),
703 }, 746 "total": D("10000.0")
704 } 747 },
705 portfolio.BalanceStore.fetch_balances(market, tag="tag") 748 }
749
750 m.balances.fetch_balances(tag="tag")
706 751
707 helper.update_trades(market) 752 m.update_trades()
708 compute_trades.assert_called() 753 compute_trades.assert_called()
709 754
710 call = compute_trades.call_args 755 call = compute_trades.call_args
711 self.assertEqual(market, call[1]["market"]) 756 self.assertEqual(1, call[0][0]["USDT"].value)
712 self.assertEqual(1, call[0][0]["USDT"].value) 757 self.assertEqual(D("0.01"), call[0][0]["XVG"].value)
713 self.assertEqual(D("0.01"), call[0][0]["XVG"].value) 758 self.assertEqual(D("0.2525"), call[0][1]["BTC"].value)
714 self.assertEqual(D("0.2525"), call[0][1]["BTC"].value) 759 self.assertEqual(D("0.7575"), call[0][1]["XEM"].value)
715 self.assertEqual(D("0.7575"), call[0][1]["XEM"].value) 760 m.report.log_stage.assert_called_once_with("update_trades")
716 report_store_h.log_stage.assert_called_once_with("update_trades") 761 m.report.log_balances.assert_called_once_with(tag="tag")
717 report_store.log_balances.assert_called_once_with(market, tag="tag")
718 762
719 @mock.patch.object(portfolio.Portfolio, "repartition") 763 @mock.patch.object(portfolio.Portfolio, "repartition")
720 @mock.patch.object(helper, "get_ticker") 764 @mock.patch.object(market.Market, "get_ticker")
721 @mock.patch.object(portfolio.TradeStore, "compute_trades") 765 @mock.patch.object(market.TradeStore, "compute_trades")
722 @mock.patch("store.ReportStore") 766 def test_prepare_trades_to_sell_all(self, compute_trades, get_ticker, repartition):
723 @mock.patch("helper.ReportStore") 767 def _get_ticker(c1, c2):
724 def test_prepare_trades_to_sell_all(self, report_store_h, report_store, compute_trades, get_ticker, repartition):
725 def _get_ticker(c1, c2, market):
726 if c1 == "USDT" and c2 == "BTC": 768 if c1 == "USDT" and c2 == "BTC":
727 return { "average": D("0.0001") } 769 return { "average": D("0.0001") }
728 if c1 == "XVG" and c2 == "BTC": 770 if c1 == "XVG" and c2 == "BTC":
@@ -730,44 +772,47 @@ class HelperTest(WebMockTestCase):
730 self.fail("Should be called with {}, {}".format(c1, c2)) 772 self.fail("Should be called with {}, {}".format(c1, c2))
731 get_ticker.side_effect = _get_ticker 773 get_ticker.side_effect = _get_ticker
732 774
733 market = mock.Mock() 775 with mock.patch("market.ReportStore"):
734 market.fetch_all_balances.return_value = { 776 m = market.Market(self.ccxt)
735 "USDT": { 777 self.ccxt.fetch_all_balances.return_value = {
736 "exchange_free": D("10000.0"), 778 "USDT": {
737 "exchange_used": D("0.0"), 779 "exchange_free": D("10000.0"),
738 "exchange_total": D("10000.0"), 780 "exchange_used": D("0.0"),
739 "total": D("10000.0") 781 "exchange_total": D("10000.0"),
740 }, 782 "total": D("10000.0")
741 "XVG": { 783 },
742 "exchange_free": D("10000.0"), 784 "XVG": {
743 "exchange_used": D("0.0"), 785 "exchange_free": D("10000.0"),
744 "exchange_total": D("10000.0"), 786 "exchange_used": D("0.0"),
745 "total": D("10000.0") 787 "exchange_total": D("10000.0"),
746 }, 788 "total": D("10000.0")
747 } 789 },
748 portfolio.BalanceStore.fetch_balances(market, tag="tag") 790 }
791
792 m.balances.fetch_balances(tag="tag")
793
794 m.prepare_trades_to_sell_all()
749 795
750 helper.prepare_trades_to_sell_all(market) 796 repartition.assert_not_called()
751 repartition.assert_not_called() 797 compute_trades.assert_called()
752 compute_trades.assert_called()
753 798
754 call = compute_trades.call_args 799 call = compute_trades.call_args
755 self.assertEqual(market, call[1]["market"]) 800 self.assertEqual(1, call[0][0]["USDT"].value)
756 self.assertEqual(1, call[0][0]["USDT"].value) 801 self.assertEqual(D("0.01"), call[0][0]["XVG"].value)
757 self.assertEqual(D("0.01"), call[0][0]["XVG"].value) 802 self.assertEqual(D("1.01"), call[0][1]["BTC"].value)
758 self.assertEqual(D("1.01"), call[0][1]["BTC"].value) 803 m.report.log_stage.assert_called_once_with("prepare_trades_to_sell_all")
759 report_store_h.log_stage.assert_called_once_with("prepare_trades_to_sell_all") 804 m.report.log_balances.assert_called_once_with(tag="tag")
760 report_store.log_balances.assert_called_once_with(market, tag="tag")
761 805
762 @mock.patch.object(portfolio.time, "sleep") 806 @mock.patch.object(portfolio.time, "sleep")
763 @mock.patch.object(portfolio.TradeStore, "all_orders") 807 @mock.patch.object(market.TradeStore, "all_orders")
764 def test_follow_orders(self, all_orders, time_mock): 808 def test_follow_orders(self, all_orders, time_mock):
765 for debug, sleep in [ 809 for debug, sleep in [
766 (False, None), (True, None), 810 (False, None), (True, None),
767 (False, 12), (True, 12)]: 811 (False, 12), (True, 12)]:
768 with self.subTest(sleep=sleep, debug=debug), \ 812 with self.subTest(sleep=sleep, debug=debug), \
769 mock.patch("helper.ReportStore") as report_store: 813 mock.patch("market.ReportStore"):
770 portfolio.TradeStore.debug = debug 814 m = market.Market(self.ccxt, debug=debug)
815
771 order_mock1 = mock.Mock() 816 order_mock1 = mock.Mock()
772 order_mock2 = mock.Mock() 817 order_mock2 = mock.Mock()
773 order_mock3 = mock.Mock() 818 order_mock3 = mock.Mock()
@@ -792,7 +837,7 @@ class HelperTest(WebMockTestCase):
792 order_mock2.trade = mock.Mock() 837 order_mock2.trade = mock.Mock()
793 order_mock3.trade = mock.Mock() 838 order_mock3.trade = mock.Mock()
794 839
795 helper.follow_orders(sleep=sleep) 840 m.follow_orders(sleep=sleep)
796 841
797 order_mock1.trade.update_order.assert_any_call(order_mock1, 1) 842 order_mock1.trade.update_order.assert_any_call(order_mock1, 1)
798 order_mock1.trade.update_order.assert_any_call(order_mock1, 2) 843 order_mock1.trade.update_order.assert_any_call(order_mock1, 2)
@@ -806,7 +851,7 @@ class HelperTest(WebMockTestCase):
806 order_mock3.trade.update_order.assert_any_call(order_mock3, 2) 851 order_mock3.trade.update_order.assert_any_call(order_mock3, 2)
807 self.assertEqual(1, order_mock3.trade.update_order.call_count) 852 self.assertEqual(1, order_mock3.trade.update_order.call_count)
808 self.assertEqual(2, order_mock3.get_status.call_count) 853 self.assertEqual(2, order_mock3.get_status.call_count)
809 report_store.log_stage.assert_called() 854 m.report.log_stage.assert_called()
810 calls = [ 855 calls = [
811 mock.call("follow_orders_begin"), 856 mock.call("follow_orders_begin"),
812 mock.call("follow_orders_tick_1"), 857 mock.call("follow_orders_tick_1"),
@@ -814,285 +859,75 @@ class HelperTest(WebMockTestCase):
814 mock.call("follow_orders_tick_3"), 859 mock.call("follow_orders_tick_3"),
815 mock.call("follow_orders_end"), 860 mock.call("follow_orders_end"),
816 ] 861 ]
817 report_store.log_stage.assert_has_calls(calls) 862 m.report.log_stage.assert_has_calls(calls)
818 report_store.log_orders.assert_called() 863 m.report.log_orders.assert_called()
819 self.assertEqual(3, report_store.log_orders.call_count) 864 self.assertEqual(3, m.report.log_orders.call_count)
820 calls = [ 865 calls = [
821 mock.call([order_mock1, order_mock2], tick=1), 866 mock.call([order_mock1, order_mock2], tick=1),
822 mock.call([order_mock1, order_mock3], tick=2), 867 mock.call([order_mock1, order_mock3], tick=2),
823 mock.call([order_mock1, order_mock3], tick=3), 868 mock.call([order_mock1, order_mock3], tick=3),
824 ] 869 ]
825 report_store.log_orders.assert_has_calls(calls) 870 m.report.log_orders.assert_has_calls(calls)
826 calls = [ 871 calls = [
827 mock.call(order_mock1, 3, finished=True), 872 mock.call(order_mock1, 3, finished=True),
828 mock.call(order_mock3, 3, finished=True), 873 mock.call(order_mock3, 3, finished=True),
829 ] 874 ]
830 report_store.log_order.assert_has_calls(calls) 875 m.report.log_order.assert_has_calls(calls)
831 876
832 if sleep is None: 877 if sleep is None:
833 if debug: 878 if debug:
834 report_store.log_debug_action.assert_called_with("Set follow_orders tick to 7s") 879 m.report.log_debug_action.assert_called_with("Set follow_orders tick to 7s")
835 time_mock.assert_called_with(7) 880 time_mock.assert_called_with(7)
836 else: 881 else:
837 time_mock.assert_called_with(30) 882 time_mock.assert_called_with(30)
838 else: 883 else:
839 time_mock.assert_called_with(sleep) 884 time_mock.assert_called_with(sleep)
840 885
841 @mock.patch.object(portfolio.BalanceStore, "fetch_balances") 886 @mock.patch.object(market.BalanceStore, "fetch_balances")
842 def test_move_balance(self, fetch_balances): 887 def test_move_balance(self, fetch_balances):
843 for debug in [True, False]: 888 for debug in [True, False]:
844 with self.subTest(debug=debug),\ 889 with self.subTest(debug=debug),\
845 mock.patch("helper.ReportStore") as report_store: 890 mock.patch("market.ReportStore"):
891 m = market.Market(self.ccxt, debug=debug)
892
846 value_from = portfolio.Amount("BTC", "1.0") 893 value_from = portfolio.Amount("BTC", "1.0")
847 value_from.linked_to = portfolio.Amount("ETH", "10.0") 894 value_from.linked_to = portfolio.Amount("ETH", "10.0")
848 value_to = portfolio.Amount("BTC", "10.0") 895 value_to = portfolio.Amount("BTC", "10.0")
849 trade1 = portfolio.Trade(value_from, value_to, "ETH") 896 trade1 = portfolio.Trade(value_from, value_to, "ETH", m)
850 897
851 value_from = portfolio.Amount("BTC", "0.0") 898 value_from = portfolio.Amount("BTC", "0.0")
852 value_from.linked_to = portfolio.Amount("ETH", "0.0") 899 value_from.linked_to = portfolio.Amount("ETH", "0.0")
853 value_to = portfolio.Amount("BTC", "-3.0") 900 value_to = portfolio.Amount("BTC", "-3.0")
854 trade2 = portfolio.Trade(value_from, value_to, "ETH") 901 trade2 = portfolio.Trade(value_from, value_to, "ETH", m)
855 902
856 value_from = portfolio.Amount("USDT", "0.0") 903 value_from = portfolio.Amount("USDT", "0.0")
857 value_from.linked_to = portfolio.Amount("XVG", "0.0") 904 value_from.linked_to = portfolio.Amount("XVG", "0.0")
858 value_to = portfolio.Amount("USDT", "-50.0") 905 value_to = portfolio.Amount("USDT", "-50.0")
859 trade3 = portfolio.Trade(value_from, value_to, "XVG") 906 trade3 = portfolio.Trade(value_from, value_to, "XVG", m)
860 907
861 portfolio.TradeStore.all = [trade1, trade2, trade3] 908 m.trades.all = [trade1, trade2, trade3]
862 balance1 = portfolio.Balance("BTC", { "margin_free": "0" }) 909 balance1 = portfolio.Balance("BTC", { "margin_free": "0" })
863 balance2 = portfolio.Balance("USDT", { "margin_free": "100" }) 910 balance2 = portfolio.Balance("USDT", { "margin_free": "100" })
864 balance3 = portfolio.Balance("ETC", { "margin_free": "10" }) 911 balance3 = portfolio.Balance("ETC", { "margin_free": "10" })
865 portfolio.BalanceStore.all = {"BTC": balance1, "USDT": balance2, "ETC": balance3} 912 m.balances.all = {"BTC": balance1, "USDT": balance2, "ETC": balance3}
866
867 market = mock.Mock()
868 913
869 helper.move_balances(market, debug=debug) 914 m.move_balances()
870 915
871 fetch_balances.assert_called_with(market) 916 fetch_balances.assert_called_with()
872 report_store.log_move_balances.assert_called_once() 917 m.report.log_move_balances.assert_called_once()
873 918
874 if debug: 919 if debug:
875 report_store.log_debug_action.assert_called() 920 m.report.log_debug_action.assert_called()
876 self.assertEqual(3, report_store.log_debug_action.call_count) 921 self.assertEqual(3, m.report.log_debug_action.call_count)
877 else: 922 else:
878 market.transfer_balance.assert_any_call("BTC", 3, "exchange", "margin") 923 self.ccxt.transfer_balance.assert_any_call("BTC", 3, "exchange", "margin")
879 market.transfer_balance.assert_any_call("USDT", 50, "margin", "exchange") 924 self.ccxt.transfer_balance.assert_any_call("USDT", 50, "margin", "exchange")
880 market.transfer_balance.assert_any_call("ETC", 10, "margin", "exchange") 925 self.ccxt.transfer_balance.assert_any_call("ETC", 10, "margin", "exchange")
881 926
882 @mock.patch.object(helper, "prepare_trades")
883 @mock.patch.object(portfolio.TradeStore, "prepare_orders")
884 @mock.patch.object(portfolio.BalanceStore, "fetch_balances")
885 @mock.patch.object(portfolio.ReportStore, "log_stage")
886 def test_print_orders(self, log_stage, fetch_balances, prepare_orders, prepare_trades):
887 market = mock.Mock()
888 portfolio.BalanceStore.all = {
889 "BTC": portfolio.Balance("BTC", {
890 "total": "0.65",
891 "exchange_total":"0.65",
892 "exchange_free": "0.35",
893 "exchange_used": "0.30"}),
894 "ETH": portfolio.Balance("ETH", {
895 "total": 3,
896 "exchange_total": 3,
897 "exchange_free": 3,
898 "exchange_used": 0}),
899 }
900
901 helper.print_orders(market)
902 fetch_balances.assert_called_with(market, tag="print_orders")
903 prepare_trades.assert_called_with(market, base_currency="BTC",
904 compute_value="average", debug=True)
905 prepare_orders.assert_called_with(compute_value="average")
906 log_stage.assert_called_with("print_orders")
907
908 @mock.patch.object(portfolio.BalanceStore, "fetch_balances")
909 @mock.patch.object(portfolio.BalanceStore, "in_currency")
910 @mock.patch.object(helper.ReportStore, "print_log")
911 def test_print_balances(self, print_log, in_currency, fetch_balances):
912 market = mock.Mock()
913 portfolio.BalanceStore.all = {
914 "BTC": portfolio.Balance("BTC", {
915 "total": "0.65",
916 "exchange_total":"0.65",
917 "exchange_free": "0.35",
918 "exchange_used": "0.30"}),
919 "ETH": portfolio.Balance("ETH", {
920 "total": 3,
921 "exchange_total": 3,
922 "exchange_free": 3,
923 "exchange_used": 0}),
924 }
925 in_currency.return_value = {
926 "BTC": portfolio.Amount("BTC", "0.65"),
927 "ETH": portfolio.Amount("BTC", "0.3"),
928 }
929 helper.print_balances(market)
930 fetch_balances.assert_called_with(market)
931 print_log.assert_has_calls([
932 mock.call("total:"),
933 mock.call(portfolio.Amount("BTC", "0.95")),
934 ])
935
936 @mock.patch.object(helper, "prepare_trades")
937 @mock.patch.object(helper, "follow_orders")
938 @mock.patch.object(portfolio.TradeStore, "prepare_orders")
939 @mock.patch.object(portfolio.TradeStore, "run_orders")
940 @mock.patch.object(portfolio.BalanceStore, "fetch_balances")
941 @mock.patch.object(portfolio.ReportStore, "log_stage")
942 def test_process_sell_needed__1_sell(self, log_stage,
943 fetch_balances, run_orders, prepare_orders, follow_orders,
944 prepare_trades):
945 market = mock.Mock()
946 portfolio.BalanceStore.all = {
947 "BTC": portfolio.Balance("BTC", {
948 "total": "0.65",
949 "exchange_total":"0.65",
950 "exchange_free": "0.35",
951 "exchange_used": "0.30"}),
952 "ETH": portfolio.Balance("ETH", {
953 "total": 3,
954 "exchange_total": 3,
955 "exchange_free": 3,
956 "exchange_used": 0}),
957 }
958 helper.process_sell_needed__1_sell(market)
959 fetch_balances.assert_has_calls([
960 mock.call(market, tag="process_sell_needed__1_sell_begin"),
961 mock.call(market, tag="process_sell_needed__1_sell_end"),
962 ])
963 prepare_trades.assert_called_with(market, base_currency="BTC",
964 liquidity="medium", debug=False)
965 prepare_orders.assert_called_with(compute_value="average",
966 only="dispose")
967 run_orders.assert_called()
968 follow_orders.assert_called()
969 log_stage.assert_called_with("process_sell_needed__1_sell_end")
970
971 @mock.patch.object(helper, "update_trades")
972 @mock.patch.object(helper, "follow_orders")
973 @mock.patch.object(helper, "move_balances")
974 @mock.patch.object(portfolio.TradeStore, "prepare_orders")
975 @mock.patch.object(portfolio.TradeStore, "run_orders")
976 @mock.patch.object(portfolio.BalanceStore, "fetch_balances")
977 @mock.patch.object(portfolio.ReportStore, "log_stage")
978 def test_process_sell_needed__2_buy(self, log_stage, fetch_balances,
979 run_orders, prepare_orders, move_balances, follow_orders,
980 update_trades):
981 market = mock.Mock()
982 portfolio.BalanceStore.all = {
983 "BTC": portfolio.Balance("BTC", {
984 "total": "0.65",
985 "exchange_total":"0.65",
986 "exchange_free": "0.35",
987 "exchange_used": "0.30"}),
988 "ETH": portfolio.Balance("ETH", {
989 "total": 3,
990 "exchange_total": 3,
991 "exchange_free": 3,
992 "exchange_used": 0}),
993 }
994 helper.process_sell_needed__2_buy(market)
995 fetch_balances.assert_has_calls([
996 mock.call(market, tag="process_sell_needed__2_buy_begin"),
997 mock.call(market, tag="process_sell_needed__2_buy_end"),
998 ])
999 update_trades.assert_called_with(market, base_currency="BTC",
1000 debug=False, liquidity="medium", only="acquire")
1001 prepare_orders.assert_called_with(compute_value="average",
1002 only="acquire")
1003 move_balances.assert_called_with(market, debug=False)
1004 run_orders.assert_called()
1005 follow_orders.assert_called()
1006 log_stage.assert_called_with("process_sell_needed__2_buy_end")
1007
1008 @mock.patch.object(helper, "prepare_trades_to_sell_all")
1009 @mock.patch.object(helper, "follow_orders")
1010 @mock.patch.object(portfolio.TradeStore, "prepare_orders")
1011 @mock.patch.object(portfolio.TradeStore, "run_orders")
1012 @mock.patch.object(portfolio.BalanceStore, "fetch_balances")
1013 @mock.patch.object(portfolio.ReportStore, "log_stage")
1014 def test_process_sell_all__1_sell(self, log_stage, fetch_balances,
1015 run_orders, prepare_orders, follow_orders,
1016 prepare_trades_to_sell_all):
1017 market = mock.Mock()
1018 portfolio.BalanceStore.all = {
1019 "BTC": portfolio.Balance("BTC", {
1020 "total": "0.65",
1021 "exchange_total":"0.65",
1022 "exchange_free": "0.35",
1023 "exchange_used": "0.30"}),
1024 "ETH": portfolio.Balance("ETH", {
1025 "total": 3,
1026 "exchange_total": 3,
1027 "exchange_free": 3,
1028 "exchange_used": 0}),
1029 }
1030 helper.process_sell_all__1_all_sell(market)
1031 fetch_balances.assert_has_calls([
1032 mock.call(market, tag="process_sell_all__1_all_sell_begin"),
1033 mock.call(market, tag="process_sell_all__1_all_sell_end"),
1034 ])
1035 prepare_trades_to_sell_all.assert_called_with(market, base_currency="BTC",
1036 debug=False)
1037 prepare_orders.assert_called_with(compute_value="average")
1038 run_orders.assert_called()
1039 follow_orders.assert_called()
1040 log_stage.assert_called_with("process_sell_all__1_all_sell_end")
1041
1042 @mock.patch.object(helper, "prepare_trades")
1043 @mock.patch.object(helper, "follow_orders")
1044 @mock.patch.object(helper, "move_balances")
1045 @mock.patch.object(portfolio.TradeStore, "prepare_orders")
1046 @mock.patch.object(portfolio.TradeStore, "run_orders")
1047 @mock.patch.object(portfolio.BalanceStore, "fetch_balances")
1048 @mock.patch.object(portfolio.ReportStore, "log_stage")
1049 def test_process_sell_all__2_all_buy(self, log_stage,
1050 fetch_balances, run_orders, prepare_orders, move_balances,
1051 follow_orders, prepare_trades):
1052 market = mock.Mock()
1053 portfolio.BalanceStore.all = {
1054 "BTC": portfolio.Balance("BTC", {
1055 "total": "0.65",
1056 "exchange_total":"0.65",
1057 "exchange_free": "0.35",
1058 "exchange_used": "0.30"}),
1059 "ETH": portfolio.Balance("ETH", {
1060 "total": 3,
1061 "exchange_total": 3,
1062 "exchange_free": 3,
1063 "exchange_used": 0}),
1064 }
1065 helper.process_sell_all__2_all_buy(market)
1066 fetch_balances.assert_has_calls([
1067 mock.call(market, tag="process_sell_all__2_all_buy_begin"),
1068 mock.call(market, tag="process_sell_all__2_all_buy_end"),
1069 ])
1070 prepare_trades.assert_called_with(market, base_currency="BTC",
1071 liquidity="medium", debug=False)
1072 prepare_orders.assert_called_with(compute_value="average")
1073 move_balances.assert_called_with(market, debug=False)
1074 run_orders.assert_called()
1075 follow_orders.assert_called()
1076 log_stage.assert_called_with("process_sell_all__2_all_buy_end")
1077
1078 def test_reset_all(self):
1079 portfolio.BalanceStore.all = { "foo": "bar" }
1080 portfolio.ReportStore.logs.append("hey")
1081 portfolio.TradeStore.all.append("bouh")
1082
1083 helper.reset_all()
1084
1085 self.assertEqual(0, len(portfolio.BalanceStore.all))
1086 self.assertEqual(0, len(portfolio.ReportStore.logs))
1087 self.assertEqual(0, len(portfolio.TradeStore.all))
1088
1089@unittest.skipUnless("unit" in limits, "Unit skipped") 927@unittest.skipUnless("unit" in limits, "Unit skipped")
1090class TradeStoreTest(WebMockTestCase): 928class TradeStoreTest(WebMockTestCase):
1091 @mock.patch.object(portfolio.BalanceStore, "currencies") 929 def test_compute_trades(self):
1092 @mock.patch.object(portfolio.TradeStore, "trade_if_matching") 930 self.m.balances.currencies.return_value = ["XMR", "DASH", "XVG", "BTC", "ETH"]
1093 @mock.patch.object(portfolio.ReportStore, "log_trades")
1094 def test_compute_trades(self, log_trades, trade_if_matching, currencies):
1095 currencies.return_value = ["XMR", "DASH", "XVG", "BTC", "ETH"]
1096 931
1097 values_in_base = { 932 values_in_base = {
1098 "XMR": portfolio.Amount("BTC", D("0.9")), 933 "XMR": portfolio.Amount("BTC", D("0.9")),
@@ -1113,112 +948,121 @@ class TradeStoreTest(WebMockTestCase):
1113 (True, 4), 948 (True, 4),
1114 (True, 5) 949 (True, 5)
1115 ] 950 ]
1116 trade_if_matching.side_effect = side_effect
1117 951
1118 portfolio.TradeStore.compute_trades(values_in_base, 952 with mock.patch.object(market.TradeStore, "trade_if_matching") as trade_if_matching:
1119 new_repartition, only="only", market="market") 953 trade_store = market.TradeStore(self.m)
954 trade_if_matching.side_effect = side_effect
1120 955
1121 self.assertEqual(5, trade_if_matching.call_count) 956 trade_store.compute_trades(values_in_base,
1122 self.assertEqual(3, len(portfolio.TradeStore.all)) 957 new_repartition, only="only")
1123 self.assertEqual([1, 4, 5], portfolio.TradeStore.all) 958
1124 log_trades.assert_called_with(side_effect, "only", False) 959 self.assertEqual(5, trade_if_matching.call_count)
960 self.assertEqual(3, len(trade_store.all))
961 self.assertEqual([1, 4, 5], trade_store.all)
962 self.m.report.log_trades.assert_called_with(side_effect, "only")
1125 963
1126 def test_trade_if_matching(self): 964 def test_trade_if_matching(self):
1127 result = portfolio.TradeStore.trade_if_matching( 965
1128 portfolio.Amount("BTC", D("0")), 966 with self.subTest(only="nope"):
1129 portfolio.Amount("BTC", D("0.3")), 967 trade_store = market.TradeStore(self.m)
1130 "ETH", only="nope", market="market" 968 result = trade_store.trade_if_matching(
1131 ) 969 portfolio.Amount("BTC", D("0")),
1132 self.assertEqual(False, result[0]) 970 portfolio.Amount("BTC", D("0.3")),
1133 self.assertIsInstance(result[1], portfolio.Trade) 971 "ETH", only="nope")
1134 972 self.assertEqual(False, result[0])
1135 portfolio.TradeStore.all = [] 973 self.assertIsInstance(result[1], portfolio.Trade)
1136 result = portfolio.TradeStore.trade_if_matching( 974
1137 portfolio.Amount("BTC", D("0")), 975 with self.subTest(only=None):
1138 portfolio.Amount("BTC", D("0.3")), 976 trade_store = market.TradeStore(self.m)
1139 "ETH", only=None, market="market" 977 result = trade_store.trade_if_matching(
1140 ) 978 portfolio.Amount("BTC", D("0")),
1141 self.assertEqual(True, result[0]) 979 portfolio.Amount("BTC", D("0.3")),
1142 980 "ETH", only=None)
1143 portfolio.TradeStore.all = [] 981 self.assertEqual(True, result[0])
1144 result = portfolio.TradeStore.trade_if_matching( 982
1145 portfolio.Amount("BTC", D("0")), 983 with self.subTest(only="acquire"):
1146 portfolio.Amount("BTC", D("0.3")), 984 trade_store = market.TradeStore(self.m)
1147 "ETH", only="acquire", market="market" 985 result = trade_store.trade_if_matching(
1148 ) 986 portfolio.Amount("BTC", D("0")),
1149 self.assertEqual(True, result[0]) 987 portfolio.Amount("BTC", D("0.3")),
1150 988 "ETH", only="acquire")
1151 portfolio.TradeStore.all = [] 989 self.assertEqual(True, result[0])
1152 result = portfolio.TradeStore.trade_if_matching( 990
1153 portfolio.Amount("BTC", D("0")), 991 with self.subTest(only="dispose"):
1154 portfolio.Amount("BTC", D("0.3")), 992 trade_store = market.TradeStore(self.m)
1155 "ETH", only="dispose", market="market" 993 result = trade_store.trade_if_matching(
1156 ) 994 portfolio.Amount("BTC", D("0")),
1157 self.assertEqual(False, result[0]) 995 portfolio.Amount("BTC", D("0.3")),
1158 996 "ETH", only="dispose")
1159 @mock.patch.object(portfolio.ReportStore, "log_orders") 997 self.assertEqual(False, result[0])
1160 def test_prepare_orders(self, log_orders): 998
999 def test_prepare_orders(self):
1000 trade_store = market.TradeStore(self.m)
1001
1161 trade_mock1 = mock.Mock() 1002 trade_mock1 = mock.Mock()
1162 trade_mock2 = mock.Mock() 1003 trade_mock2 = mock.Mock()
1163 1004
1164 trade_mock1.prepare_order.return_value = 1 1005 trade_mock1.prepare_order.return_value = 1
1165 trade_mock2.prepare_order.return_value = 2 1006 trade_mock2.prepare_order.return_value = 2
1166 1007
1167 portfolio.TradeStore.all.append(trade_mock1) 1008 trade_store.all.append(trade_mock1)
1168 portfolio.TradeStore.all.append(trade_mock2) 1009 trade_store.all.append(trade_mock2)
1169 1010
1170 portfolio.TradeStore.prepare_orders() 1011 trade_store.prepare_orders()
1171 trade_mock1.prepare_order.assert_called_with(compute_value="default") 1012 trade_mock1.prepare_order.assert_called_with(compute_value="default")
1172 trade_mock2.prepare_order.assert_called_with(compute_value="default") 1013 trade_mock2.prepare_order.assert_called_with(compute_value="default")
1173 log_orders.assert_called_once_with([1, 2], None, "default") 1014 self.m.report.log_orders.assert_called_once_with([1, 2], None, "default")
1174 1015
1175 log_orders.reset_mock() 1016 self.m.report.log_orders.reset_mock()
1176 1017
1177 portfolio.TradeStore.prepare_orders(compute_value="bla") 1018 trade_store.prepare_orders(compute_value="bla")
1178 trade_mock1.prepare_order.assert_called_with(compute_value="bla") 1019 trade_mock1.prepare_order.assert_called_with(compute_value="bla")
1179 trade_mock2.prepare_order.assert_called_with(compute_value="bla") 1020 trade_mock2.prepare_order.assert_called_with(compute_value="bla")
1180 log_orders.assert_called_once_with([1, 2], None, "bla") 1021 self.m.report.log_orders.assert_called_once_with([1, 2], None, "bla")
1181 1022
1182 trade_mock1.prepare_order.reset_mock() 1023 trade_mock1.prepare_order.reset_mock()
1183 trade_mock2.prepare_order.reset_mock() 1024 trade_mock2.prepare_order.reset_mock()
1184 log_orders.reset_mock() 1025 self.m.report.log_orders.reset_mock()
1185 1026
1186 trade_mock1.action = "foo" 1027 trade_mock1.action = "foo"
1187 trade_mock2.action = "bar" 1028 trade_mock2.action = "bar"
1188 portfolio.TradeStore.prepare_orders(only="bar") 1029 trade_store.prepare_orders(only="bar")
1189 trade_mock1.prepare_order.assert_not_called() 1030 trade_mock1.prepare_order.assert_not_called()
1190 trade_mock2.prepare_order.assert_called_with(compute_value="default") 1031 trade_mock2.prepare_order.assert_called_with(compute_value="default")
1191 log_orders.assert_called_once_with([2], "bar", "default") 1032 self.m.report.log_orders.assert_called_once_with([2], "bar", "default")
1192 1033
1193 def test_print_all_with_order(self): 1034 def test_print_all_with_order(self):
1194 trade_mock1 = mock.Mock() 1035 trade_mock1 = mock.Mock()
1195 trade_mock2 = mock.Mock() 1036 trade_mock2 = mock.Mock()
1196 trade_mock3 = mock.Mock() 1037 trade_mock3 = mock.Mock()
1197 portfolio.TradeStore.all = [trade_mock1, trade_mock2, trade_mock3] 1038 trade_store = market.TradeStore(self.m)
1039 trade_store.all = [trade_mock1, trade_mock2, trade_mock3]
1198 1040
1199 portfolio.TradeStore.print_all_with_order() 1041 trade_store.print_all_with_order()
1200 1042
1201 trade_mock1.print_with_order.assert_called() 1043 trade_mock1.print_with_order.assert_called()
1202 trade_mock2.print_with_order.assert_called() 1044 trade_mock2.print_with_order.assert_called()
1203 trade_mock3.print_with_order.assert_called() 1045 trade_mock3.print_with_order.assert_called()
1204 1046
1205 @mock.patch.object(portfolio.ReportStore, "log_stage") 1047 def test_run_orders(self):
1206 @mock.patch.object(portfolio.ReportStore, "log_orders") 1048 with mock.patch.object(market.TradeStore, "all_orders") as all_orders:
1207 @mock.patch.object(portfolio.TradeStore, "all_orders") 1049 order_mock1 = mock.Mock()
1208 def test_run_orders(self, all_orders, log_orders, log_stage): 1050 order_mock2 = mock.Mock()
1209 order_mock1 = mock.Mock() 1051 order_mock3 = mock.Mock()
1210 order_mock2 = mock.Mock() 1052 trade_store = market.TradeStore(self.m)
1211 order_mock3 = mock.Mock() 1053
1212 all_orders.return_value = [order_mock1, order_mock2, order_mock3] 1054 all_orders.return_value = [order_mock1, order_mock2, order_mock3]
1213 portfolio.TradeStore.run_orders() 1055
1214 all_orders.assert_called_with(state="pending") 1056 trade_store.run_orders()
1057
1058 all_orders.assert_called_with(state="pending")
1215 1059
1216 order_mock1.run.assert_called() 1060 order_mock1.run.assert_called()
1217 order_mock2.run.assert_called() 1061 order_mock2.run.assert_called()
1218 order_mock3.run.assert_called() 1062 order_mock3.run.assert_called()
1219 1063
1220 log_stage.assert_called_with("run_orders") 1064 self.m.report.log_stage.assert_called_with("run_orders")
1221 log_orders.assert_called_with([order_mock1, order_mock2, 1065 self.m.report.log_orders.assert_called_with([order_mock1, order_mock2,
1222 order_mock3]) 1066 order_mock3])
1223 1067
1224 def test_all_orders(self): 1068 def test_all_orders(self):
@@ -1236,28 +1080,33 @@ class TradeStoreTest(WebMockTestCase):
1236 order_mock2.status = "open" 1080 order_mock2.status = "open"
1237 order_mock3.status = "open" 1081 order_mock3.status = "open"
1238 1082
1239 portfolio.TradeStore.all.append(trade_mock1) 1083 trade_store = market.TradeStore(self.m)
1240 portfolio.TradeStore.all.append(trade_mock2) 1084 trade_store.all.append(trade_mock1)
1085 trade_store.all.append(trade_mock2)
1241 1086
1242 orders = portfolio.TradeStore.all_orders() 1087 orders = trade_store.all_orders()
1243 self.assertEqual(3, len(orders)) 1088 self.assertEqual(3, len(orders))
1244 1089
1245 open_orders = portfolio.TradeStore.all_orders(state="open") 1090 open_orders = trade_store.all_orders(state="open")
1246 self.assertEqual(2, len(open_orders)) 1091 self.assertEqual(2, len(open_orders))
1247 self.assertEqual([order_mock2, order_mock3], open_orders) 1092 self.assertEqual([order_mock2, order_mock3], open_orders)
1248 1093
1249 @mock.patch.object(portfolio.TradeStore, "all_orders") 1094 def test_update_all_orders_status(self):
1250 def test_update_all_orders_status(self, all_orders): 1095 with mock.patch.object(market.TradeStore, "all_orders") as all_orders:
1251 order_mock1 = mock.Mock() 1096 order_mock1 = mock.Mock()
1252 order_mock2 = mock.Mock() 1097 order_mock2 = mock.Mock()
1253 order_mock3 = mock.Mock() 1098 order_mock3 = mock.Mock()
1254 all_orders.return_value = [order_mock1, order_mock2, order_mock3]
1255 portfolio.TradeStore.update_all_orders_status()
1256 all_orders.assert_called_with(state="open")
1257 1099
1258 order_mock1.get_status.assert_called() 1100 all_orders.return_value = [order_mock1, order_mock2, order_mock3]
1259 order_mock2.get_status.assert_called() 1101
1260 order_mock3.get_status.assert_called() 1102 trade_store = market.TradeStore(self.m)
1103
1104 trade_store.update_all_orders_status()
1105 all_orders.assert_called_with(state="open")
1106
1107 order_mock1.get_status.assert_called()
1108 order_mock2.get_status.assert_called()
1109 order_mock3.get_status.assert_called()
1261 1110
1262 1111
1263@unittest.skipUnless("unit" in limits, "Unit skipped") 1112@unittest.skipUnless("unit" in limits, "Unit skipped")
@@ -1293,10 +1142,15 @@ class BalanceStoreTest(WebMockTestCase):
1293 }, 1142 },
1294 } 1143 }
1295 1144
1296 @mock.patch.object(helper, "get_ticker") 1145 def test_in_currency(self):
1297 @mock.patch("portfolio.ReportStore.log_tickers") 1146 self.m.get_ticker.return_value = {
1298 def test_in_currency(self, log_tickers, get_ticker): 1147 "bid": D("0.09"),
1299 portfolio.BalanceStore.all = { 1148 "ask": D("0.11"),
1149 "average": D("0.1"),
1150 }
1151
1152 balance_store = market.BalanceStore(self.m)
1153 balance_store.all = {
1300 "BTC": portfolio.Balance("BTC", { 1154 "BTC": portfolio.Balance("BTC", {
1301 "total": "0.65", 1155 "total": "0.65",
1302 "exchange_total":"0.65", 1156 "exchange_total":"0.65",
@@ -1308,61 +1162,54 @@ class BalanceStoreTest(WebMockTestCase):
1308 "exchange_free": 3, 1162 "exchange_free": 3,
1309 "exchange_used": 0}), 1163 "exchange_used": 0}),
1310 } 1164 }
1311 market = mock.Mock()
1312 get_ticker.return_value = {
1313 "bid": D("0.09"),
1314 "ask": D("0.11"),
1315 "average": D("0.1"),
1316 }
1317 1165
1318 amounts = portfolio.BalanceStore.in_currency("BTC", market) 1166 amounts = balance_store.in_currency("BTC")
1319 self.assertEqual("BTC", amounts["ETH"].currency) 1167 self.assertEqual("BTC", amounts["ETH"].currency)
1320 self.assertEqual(D("0.65"), amounts["BTC"].value) 1168 self.assertEqual(D("0.65"), amounts["BTC"].value)
1321 self.assertEqual(D("0.30"), amounts["ETH"].value) 1169 self.assertEqual(D("0.30"), amounts["ETH"].value)
1322 log_tickers.assert_called_once_with(market, amounts, "BTC", 1170 self.m.report.log_tickers.assert_called_once_with(amounts, "BTC",
1323 "average", "total") 1171 "average", "total")
1324 log_tickers.reset_mock() 1172 self.m.report.log_tickers.reset_mock()
1325 1173
1326 amounts = portfolio.BalanceStore.in_currency("BTC", market, compute_value="bid") 1174 amounts = balance_store.in_currency("BTC", compute_value="bid")
1327 self.assertEqual(D("0.65"), amounts["BTC"].value) 1175 self.assertEqual(D("0.65"), amounts["BTC"].value)
1328 self.assertEqual(D("0.27"), amounts["ETH"].value) 1176 self.assertEqual(D("0.27"), amounts["ETH"].value)
1329 log_tickers.assert_called_once_with(market, amounts, "BTC", 1177 self.m.report.log_tickers.assert_called_once_with(amounts, "BTC",
1330 "bid", "total") 1178 "bid", "total")
1331 log_tickers.reset_mock() 1179 self.m.report.log_tickers.reset_mock()
1332 1180
1333 amounts = portfolio.BalanceStore.in_currency("BTC", market, compute_value="bid", type="exchange_used") 1181 amounts = balance_store.in_currency("BTC", compute_value="bid", type="exchange_used")
1334 self.assertEqual(D("0.30"), amounts["BTC"].value) 1182 self.assertEqual(D("0.30"), amounts["BTC"].value)
1335 self.assertEqual(0, amounts["ETH"].value) 1183 self.assertEqual(0, amounts["ETH"].value)
1336 log_tickers.assert_called_once_with(market, amounts, "BTC", 1184 self.m.report.log_tickers.assert_called_once_with(amounts, "BTC",
1337 "bid", "exchange_used") 1185 "bid", "exchange_used")
1338 log_tickers.reset_mock() 1186 self.m.report.log_tickers.reset_mock()
1339 1187
1340 @mock.patch.object(portfolio.ReportStore, "log_balances") 1188 def test_fetch_balances(self):
1341 def test_fetch_balances(self, log_balances): 1189 self.m.ccxt.fetch_all_balances.return_value = self.fetch_balance
1342 market = mock.Mock()
1343 market.fetch_all_balances.return_value = self.fetch_balance
1344 1190
1345 portfolio.BalanceStore.fetch_balances(market) 1191 balance_store = market.BalanceStore(self.m)
1346 self.assertNotIn("ETC", portfolio.BalanceStore.currencies())
1347 self.assertListEqual(["USDT", "XVG", "XMR"], list(portfolio.BalanceStore.currencies()))
1348 1192
1349 portfolio.BalanceStore.all["ETC"] = portfolio.Balance("ETC", { 1193 balance_store.fetch_balances()
1194 self.assertNotIn("ETC", balance_store.currencies())
1195 self.assertListEqual(["USDT", "XVG", "XMR"], list(balance_store.currencies()))
1196
1197 balance_store.all["ETC"] = portfolio.Balance("ETC", {
1350 "exchange_total": "1", "exchange_free": "0", 1198 "exchange_total": "1", "exchange_free": "0",
1351 "exchange_used": "1" }) 1199 "exchange_used": "1" })
1352 portfolio.BalanceStore.fetch_balances(market, tag="foo") 1200 balance_store.fetch_balances(tag="foo")
1353 self.assertEqual(0, portfolio.BalanceStore.all["ETC"].total) 1201 self.assertEqual(0, balance_store.all["ETC"].total)
1354 self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(portfolio.BalanceStore.currencies())) 1202 self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(balance_store.currencies()))
1355 log_balances.assert_called_with(market, tag="foo") 1203 self.m.report.log_balances.assert_called_with(tag="foo")
1356 1204
1357 @mock.patch.object(portfolio.Portfolio, "repartition") 1205 @mock.patch.object(portfolio.Portfolio, "repartition")
1358 @mock.patch.object(portfolio.ReportStore, "log_balances") 1206 def test_dispatch_assets(self, repartition):
1359 @mock.patch("store.ReportStore.log_dispatch") 1207 self.m.ccxt.fetch_all_balances.return_value = self.fetch_balance
1360 def test_dispatch_assets(self, log_dispatch, log_balances, repartition):
1361 market = mock.Mock()
1362 market.fetch_all_balances.return_value = self.fetch_balance
1363 portfolio.BalanceStore.fetch_balances(market)
1364 1208
1365 self.assertNotIn("XEM", portfolio.BalanceStore.currencies()) 1209 balance_store = market.BalanceStore(self.m)
1210 balance_store.fetch_balances()
1211
1212 self.assertNotIn("XEM", balance_store.currencies())
1366 1213
1367 repartition_hash = { 1214 repartition_hash = {
1368 "XEM": (D("0.75"), "long"), 1215 "XEM": (D("0.75"), "long"),
@@ -1371,18 +1218,20 @@ class BalanceStoreTest(WebMockTestCase):
1371 } 1218 }
1372 repartition.return_value = repartition_hash 1219 repartition.return_value = repartition_hash
1373 1220
1374 amounts = portfolio.BalanceStore.dispatch_assets(portfolio.Amount("BTC", "11.1")) 1221 amounts = balance_store.dispatch_assets(portfolio.Amount("BTC", "11.1"))
1375 repartition.assert_called_with(liquidity="medium") 1222 repartition.assert_called_with(self.m, liquidity="medium")
1376 self.assertIn("XEM", portfolio.BalanceStore.currencies()) 1223 self.assertIn("XEM", balance_store.currencies())
1377 self.assertEqual(D("2.6"), amounts["BTC"].value) 1224 self.assertEqual(D("2.6"), amounts["BTC"].value)
1378 self.assertEqual(D("7.5"), amounts["XEM"].value) 1225 self.assertEqual(D("7.5"), amounts["XEM"].value)
1379 self.assertEqual(D("-1.0"), amounts["DASH"].value) 1226 self.assertEqual(D("-1.0"), amounts["DASH"].value)
1380 log_balances.assert_called_with(market, tag=None) 1227 self.m.report.log_balances.assert_called_with(tag=None)
1381 log_dispatch.assert_called_once_with(portfolio.Amount("BTC", 1228 self.m.report.log_dispatch.assert_called_once_with(portfolio.Amount("BTC",
1382 "11.1"), amounts, "medium", repartition_hash) 1229 "11.1"), amounts, "medium", repartition_hash)
1383 1230
1384 def test_currencies(self): 1231 def test_currencies(self):
1385 portfolio.BalanceStore.all = { 1232 balance_store = market.BalanceStore(self.m)
1233
1234 balance_store.all = {
1386 "BTC": portfolio.Balance("BTC", { 1235 "BTC": portfolio.Balance("BTC", {
1387 "total": "0.65", 1236 "total": "0.65",
1388 "exchange_total":"0.65", 1237 "exchange_total":"0.65",
@@ -1394,7 +1243,7 @@ class BalanceStoreTest(WebMockTestCase):
1394 "exchange_free": 3, 1243 "exchange_free": 3,
1395 "exchange_used": 0}), 1244 "exchange_used": 0}),
1396 } 1245 }
1397 self.assertListEqual(["BTC", "ETH"], list(portfolio.BalanceStore.currencies())) 1246 self.assertListEqual(["BTC", "ETH"], list(balance_store.currencies()))
1398 1247
1399 def test_as_json(self): 1248 def test_as_json(self):
1400 balance_mock1 = mock.Mock() 1249 balance_mock1 = mock.Mock()
@@ -1403,12 +1252,13 @@ class BalanceStoreTest(WebMockTestCase):
1403 balance_mock2 = mock.Mock() 1252 balance_mock2 = mock.Mock()
1404 balance_mock2.as_json.return_value = 2 1253 balance_mock2.as_json.return_value = 2
1405 1254
1406 portfolio.BalanceStore.all = { 1255 balance_store = market.BalanceStore(self.m)
1256 balance_store.all = {
1407 "BTC": balance_mock1, 1257 "BTC": balance_mock1,
1408 "ETH": balance_mock2, 1258 "ETH": balance_mock2,
1409 } 1259 }
1410 1260
1411 as_json = portfolio.BalanceStore.as_json() 1261 as_json = balance_store.as_json()
1412 self.assertEqual(1, as_json["BTC"]) 1262 self.assertEqual(1, as_json["BTC"])
1413 self.assertEqual(2, as_json["ETH"]) 1263 self.assertEqual(2, as_json["ETH"])
1414 1264
@@ -1445,49 +1295,50 @@ class TradeTest(WebMockTestCase):
1445 value_from = portfolio.Amount("BTC", "1.0") 1295 value_from = portfolio.Amount("BTC", "1.0")
1446 value_from.linked_to = portfolio.Amount("ETH", "10.0") 1296 value_from.linked_to = portfolio.Amount("ETH", "10.0")
1447 value_to = portfolio.Amount("BTC", "1.0") 1297 value_to = portfolio.Amount("BTC", "1.0")
1448 trade = portfolio.Trade(value_from, value_to, "ETH") 1298 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1449 self.assertEqual("BTC", trade.base_currency) 1299 self.assertEqual("BTC", trade.base_currency)
1450 self.assertEqual("ETH", trade.currency) 1300 self.assertEqual("ETH", trade.currency)
1301 self.assertEqual(self.m, trade.market)
1451 1302
1452 with self.assertRaises(AssertionError): 1303 with self.assertRaises(AssertionError):
1453 portfolio.Trade(value_from, value_to, "ETC") 1304 portfolio.Trade(value_from, value_to, "ETC", self.m)
1454 with self.assertRaises(AssertionError): 1305 with self.assertRaises(AssertionError):
1455 value_from.linked_to = None 1306 value_from.linked_to = None
1456 portfolio.Trade(value_from, value_to, "ETH") 1307 portfolio.Trade(value_from, value_to, "ETH", self.m)
1457 with self.assertRaises(AssertionError): 1308 with self.assertRaises(AssertionError):
1458 value_from.currency = "ETH" 1309 value_from.currency = "ETH"
1459 portfolio.Trade(value_from, value_to, "ETH") 1310 portfolio.Trade(value_from, value_to, "ETH", self.m)
1460 1311
1461 value_from = portfolio.Amount("BTC", 0) 1312 value_from = portfolio.Amount("BTC", 0)
1462 trade = portfolio.Trade(value_from, value_to, "ETH") 1313 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1463 self.assertEqual(0, trade.value_from.linked_to) 1314 self.assertEqual(0, trade.value_from.linked_to)
1464 1315
1465 def test_action(self): 1316 def test_action(self):
1466 value_from = portfolio.Amount("BTC", "1.0") 1317 value_from = portfolio.Amount("BTC", "1.0")
1467 value_from.linked_to = portfolio.Amount("ETH", "10.0") 1318 value_from.linked_to = portfolio.Amount("ETH", "10.0")
1468 value_to = portfolio.Amount("BTC", "1.0") 1319 value_to = portfolio.Amount("BTC", "1.0")
1469 trade = portfolio.Trade(value_from, value_to, "ETH") 1320 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1470 1321
1471 self.assertIsNone(trade.action) 1322 self.assertIsNone(trade.action)
1472 1323
1473 value_from = portfolio.Amount("BTC", "1.0") 1324 value_from = portfolio.Amount("BTC", "1.0")
1474 value_from.linked_to = portfolio.Amount("BTC", "1.0") 1325 value_from.linked_to = portfolio.Amount("BTC", "1.0")
1475 value_to = portfolio.Amount("BTC", "2.0") 1326 value_to = portfolio.Amount("BTC", "2.0")
1476 trade = portfolio.Trade(value_from, value_to, "BTC") 1327 trade = portfolio.Trade(value_from, value_to, "BTC", self.m)
1477 1328
1478 self.assertIsNone(trade.action) 1329 self.assertIsNone(trade.action)
1479 1330
1480 value_from = portfolio.Amount("BTC", "0.5") 1331 value_from = portfolio.Amount("BTC", "0.5")
1481 value_from.linked_to = portfolio.Amount("ETH", "10.0") 1332 value_from.linked_to = portfolio.Amount("ETH", "10.0")
1482 value_to = portfolio.Amount("BTC", "1.0") 1333 value_to = portfolio.Amount("BTC", "1.0")
1483 trade = portfolio.Trade(value_from, value_to, "ETH") 1334 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1484 1335
1485 self.assertEqual("acquire", trade.action) 1336 self.assertEqual("acquire", trade.action)
1486 1337
1487 value_from = portfolio.Amount("BTC", "0") 1338 value_from = portfolio.Amount("BTC", "0")
1488 value_from.linked_to = portfolio.Amount("ETH", "0") 1339 value_from.linked_to = portfolio.Amount("ETH", "0")
1489 value_to = portfolio.Amount("BTC", "-1.0") 1340 value_to = portfolio.Amount("BTC", "-1.0")
1490 trade = portfolio.Trade(value_from, value_to, "ETH") 1341 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1491 1342
1492 self.assertEqual("acquire", trade.action) 1343 self.assertEqual("acquire", trade.action)
1493 1344
@@ -1495,7 +1346,7 @@ class TradeTest(WebMockTestCase):
1495 value_from = portfolio.Amount("BTC", "0.5") 1346 value_from = portfolio.Amount("BTC", "0.5")
1496 value_from.linked_to = portfolio.Amount("ETH", "10.0") 1347 value_from.linked_to = portfolio.Amount("ETH", "10.0")
1497 value_to = portfolio.Amount("BTC", "1.0") 1348 value_to = portfolio.Amount("BTC", "1.0")
1498 trade = portfolio.Trade(value_from, value_to, "ETH") 1349 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1499 1350
1500 self.assertEqual("buy", trade.order_action(False)) 1351 self.assertEqual("buy", trade.order_action(False))
1501 self.assertEqual("sell", trade.order_action(True)) 1352 self.assertEqual("sell", trade.order_action(True))
@@ -1503,7 +1354,7 @@ class TradeTest(WebMockTestCase):
1503 value_from = portfolio.Amount("BTC", "0") 1354 value_from = portfolio.Amount("BTC", "0")
1504 value_from.linked_to = portfolio.Amount("ETH", "0") 1355 value_from.linked_to = portfolio.Amount("ETH", "0")
1505 value_to = portfolio.Amount("BTC", "-1.0") 1356 value_to = portfolio.Amount("BTC", "-1.0")
1506 trade = portfolio.Trade(value_from, value_to, "ETH") 1357 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1507 1358
1508 self.assertEqual("sell", trade.order_action(False)) 1359 self.assertEqual("sell", trade.order_action(False))
1509 self.assertEqual("buy", trade.order_action(True)) 1360 self.assertEqual("buy", trade.order_action(True))
@@ -1512,14 +1363,14 @@ class TradeTest(WebMockTestCase):
1512 value_from = portfolio.Amount("BTC", "0.5") 1363 value_from = portfolio.Amount("BTC", "0.5")
1513 value_from.linked_to = portfolio.Amount("ETH", "10.0") 1364 value_from.linked_to = portfolio.Amount("ETH", "10.0")
1514 value_to = portfolio.Amount("BTC", "1.0") 1365 value_to = portfolio.Amount("BTC", "1.0")
1515 trade = portfolio.Trade(value_from, value_to, "ETH") 1366 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1516 1367
1517 self.assertEqual("long", trade.trade_type) 1368 self.assertEqual("long", trade.trade_type)
1518 1369
1519 value_from = portfolio.Amount("BTC", "0") 1370 value_from = portfolio.Amount("BTC", "0")
1520 value_from.linked_to = portfolio.Amount("ETH", "0") 1371 value_from.linked_to = portfolio.Amount("ETH", "0")
1521 value_to = portfolio.Amount("BTC", "-1.0") 1372 value_to = portfolio.Amount("BTC", "-1.0")
1522 trade = portfolio.Trade(value_from, value_to, "ETH") 1373 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1523 1374
1524 self.assertEqual("short", trade.trade_type) 1375 self.assertEqual("short", trade.trade_type)
1525 1376
@@ -1527,7 +1378,7 @@ class TradeTest(WebMockTestCase):
1527 value_from = portfolio.Amount("BTC", "0.5") 1378 value_from = portfolio.Amount("BTC", "0.5")
1528 value_from.linked_to = portfolio.Amount("ETH", "10.0") 1379 value_from.linked_to = portfolio.Amount("ETH", "10.0")
1529 value_to = portfolio.Amount("BTC", "1.0") 1380 value_to = portfolio.Amount("BTC", "1.0")
1530 trade = portfolio.Trade(value_from, value_to, "ETH") 1381 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1531 1382
1532 order1 = mock.Mock() 1383 order1 = mock.Mock()
1533 order1.filled_amount.return_value = portfolio.Amount("ETH", "0.3") 1384 order1.filled_amount.return_value = portfolio.Amount("ETH", "0.3")
@@ -1549,11 +1400,10 @@ class TradeTest(WebMockTestCase):
1549 order1.filled_amount.assert_called_with(in_base_currency=True) 1400 order1.filled_amount.assert_called_with(in_base_currency=True)
1550 order2.filled_amount.assert_called_with(in_base_currency=True) 1401 order2.filled_amount.assert_called_with(in_base_currency=True)
1551 1402
1552 @mock.patch.object(helper, "get_ticker")
1553 @mock.patch.object(portfolio.Computation, "compute_value") 1403 @mock.patch.object(portfolio.Computation, "compute_value")
1554 @mock.patch.object(portfolio.Trade, "filled_amount") 1404 @mock.patch.object(portfolio.Trade, "filled_amount")
1555 @mock.patch.object(portfolio, "Order") 1405 @mock.patch.object(portfolio, "Order")
1556 def test_prepare_order(self, Order, filled_amount, compute_value, get_ticker): 1406 def test_prepare_order(self, Order, filled_amount, compute_value):
1557 Order.return_value = "Order" 1407 Order.return_value = "Order"
1558 1408
1559 with self.subTest(desc="Nothing to do"): 1409 with self.subTest(desc="Nothing to do"):
@@ -1561,7 +1411,7 @@ class TradeTest(WebMockTestCase):
1561 value_from.rate = D("0.1") 1411 value_from.rate = D("0.1")
1562 value_from.linked_to = portfolio.Amount("FOO", "100") 1412 value_from.linked_to = portfolio.Amount("FOO", "100")
1563 value_to = portfolio.Amount("BTC", "10") 1413 value_to = portfolio.Amount("BTC", "10")
1564 trade = portfolio.Trade(value_from, value_to, "FOO", market="market") 1414 trade = portfolio.Trade(value_from, value_to, "FOO", self.m)
1565 1415
1566 trade.prepare_order() 1416 trade.prepare_order()
1567 1417
@@ -1570,9 +1420,8 @@ class TradeTest(WebMockTestCase):
1570 self.assertEqual(0, len(trade.orders)) 1420 self.assertEqual(0, len(trade.orders))
1571 Order.assert_not_called() 1421 Order.assert_not_called()
1572 1422
1573 get_ticker.return_value = { "inverted": False } 1423 self.m.get_ticker.return_value = { "inverted": False }
1574 with self.subTest(desc="Already filled"),\ 1424 with self.subTest(desc="Already filled"):
1575 mock.patch("portfolio.ReportStore") as report_store:
1576 filled_amount.return_value = portfolio.Amount("FOO", "100") 1425 filled_amount.return_value = portfolio.Amount("FOO", "100")
1577 compute_value.return_value = D("0.125") 1426 compute_value.return_value = D("0.125")
1578 1427
@@ -1580,14 +1429,14 @@ class TradeTest(WebMockTestCase):
1580 value_from.rate = D("0.1") 1429 value_from.rate = D("0.1")
1581 value_from.linked_to = portfolio.Amount("FOO", "100") 1430 value_from.linked_to = portfolio.Amount("FOO", "100")
1582 value_to = portfolio.Amount("BTC", "0") 1431 value_to = portfolio.Amount("BTC", "0")
1583 trade = portfolio.Trade(value_from, value_to, "FOO", market="market") 1432 trade = portfolio.Trade(value_from, value_to, "FOO", self.m)
1584 1433
1585 trade.prepare_order() 1434 trade.prepare_order()
1586 1435
1587 filled_amount.assert_called_with(in_base_currency=False) 1436 filled_amount.assert_called_with(in_base_currency=False)
1588 compute_value.assert_called_with(get_ticker.return_value, "sell", compute_value="default") 1437 compute_value.assert_called_with(self.m.get_ticker.return_value, "sell", compute_value="default")
1589 self.assertEqual(0, len(trade.orders)) 1438 self.assertEqual(0, len(trade.orders))
1590 report_store.log_error.assert_called_with("prepare_order", message=mock.ANY) 1439 self.m.report.log_error.assert_called_with("prepare_order", message=mock.ANY)
1591 Order.assert_not_called() 1440 Order.assert_not_called()
1592 1441
1593 with self.subTest(action="dispose", inverted=False): 1442 with self.subTest(action="dispose", inverted=False):
@@ -1598,15 +1447,15 @@ class TradeTest(WebMockTestCase):
1598 value_from.rate = D("0.1") 1447 value_from.rate = D("0.1")
1599 value_from.linked_to = portfolio.Amount("FOO", "100") 1448 value_from.linked_to = portfolio.Amount("FOO", "100")
1600 value_to = portfolio.Amount("BTC", "1") 1449 value_to = portfolio.Amount("BTC", "1")
1601 trade = portfolio.Trade(value_from, value_to, "FOO", market="market") 1450 trade = portfolio.Trade(value_from, value_to, "FOO", self.m)
1602 1451
1603 trade.prepare_order() 1452 trade.prepare_order()
1604 1453
1605 filled_amount.assert_called_with(in_base_currency=False) 1454 filled_amount.assert_called_with(in_base_currency=False)
1606 compute_value.assert_called_with(get_ticker.return_value, "sell", compute_value="default") 1455 compute_value.assert_called_with(self.m.get_ticker.return_value, "sell", compute_value="default")
1607 self.assertEqual(1, len(trade.orders)) 1456 self.assertEqual(1, len(trade.orders))
1608 Order.assert_called_with("sell", portfolio.Amount("FOO", 30), 1457 Order.assert_called_with("sell", portfolio.Amount("FOO", 30),
1609 D("0.125"), "BTC", "long", "market", 1458 D("0.125"), "BTC", "long", self.m,
1610 trade, close_if_possible=False) 1459 trade, close_if_possible=False)
1611 1460
1612 with self.subTest(action="acquire", inverted=False): 1461 with self.subTest(action="acquire", inverted=False):
@@ -1617,16 +1466,16 @@ class TradeTest(WebMockTestCase):
1617 value_from.rate = D("0.1") 1466 value_from.rate = D("0.1")
1618 value_from.linked_to = portfolio.Amount("FOO", "10") 1467 value_from.linked_to = portfolio.Amount("FOO", "10")
1619 value_to = portfolio.Amount("BTC", "10") 1468 value_to = portfolio.Amount("BTC", "10")
1620 trade = portfolio.Trade(value_from, value_to, "FOO", market="market") 1469 trade = portfolio.Trade(value_from, value_to, "FOO", self.m)
1621 1470
1622 trade.prepare_order() 1471 trade.prepare_order()
1623 1472
1624 filled_amount.assert_called_with(in_base_currency=True) 1473 filled_amount.assert_called_with(in_base_currency=True)
1625 compute_value.assert_called_with(get_ticker.return_value, "buy", compute_value="default") 1474 compute_value.assert_called_with(self.m.get_ticker.return_value, "buy", compute_value="default")
1626 self.assertEqual(1, len(trade.orders)) 1475 self.assertEqual(1, len(trade.orders))
1627 1476
1628 Order.assert_called_with("buy", portfolio.Amount("FOO", 48), 1477 Order.assert_called_with("buy", portfolio.Amount("FOO", 48),
1629 D("0.125"), "BTC", "long", "market", 1478 D("0.125"), "BTC", "long", self.m,
1630 trade, close_if_possible=False) 1479 trade, close_if_possible=False)
1631 1480
1632 with self.subTest(close_if_possible=True): 1481 with self.subTest(close_if_possible=True):
@@ -1637,18 +1486,18 @@ class TradeTest(WebMockTestCase):
1637 value_from.rate = D("0.1") 1486 value_from.rate = D("0.1")
1638 value_from.linked_to = portfolio.Amount("FOO", "100") 1487 value_from.linked_to = portfolio.Amount("FOO", "100")
1639 value_to = portfolio.Amount("BTC", "0") 1488 value_to = portfolio.Amount("BTC", "0")
1640 trade = portfolio.Trade(value_from, value_to, "FOO", market="market") 1489 trade = portfolio.Trade(value_from, value_to, "FOO", self.m)
1641 1490
1642 trade.prepare_order() 1491 trade.prepare_order()
1643 1492
1644 filled_amount.assert_called_with(in_base_currency=False) 1493 filled_amount.assert_called_with(in_base_currency=False)
1645 compute_value.assert_called_with(get_ticker.return_value, "sell", compute_value="default") 1494 compute_value.assert_called_with(self.m.get_ticker.return_value, "sell", compute_value="default")
1646 self.assertEqual(1, len(trade.orders)) 1495 self.assertEqual(1, len(trade.orders))
1647 Order.assert_called_with("sell", portfolio.Amount("FOO", 100), 1496 Order.assert_called_with("sell", portfolio.Amount("FOO", 100),
1648 D("0.125"), "BTC", "long", "market", 1497 D("0.125"), "BTC", "long", self.m,
1649 trade, close_if_possible=True) 1498 trade, close_if_possible=True)
1650 1499
1651 get_ticker.return_value = { "inverted": True, "original": {} } 1500 self.m.get_ticker.return_value = { "inverted": True, "original": {} }
1652 with self.subTest(action="dispose", inverted=True): 1501 with self.subTest(action="dispose", inverted=True):
1653 filled_amount.return_value = portfolio.Amount("FOO", "300") 1502 filled_amount.return_value = portfolio.Amount("FOO", "300")
1654 compute_value.return_value = D("125") 1503 compute_value.return_value = D("125")
@@ -1657,15 +1506,15 @@ class TradeTest(WebMockTestCase):
1657 value_from.rate = D("0.01") 1506 value_from.rate = D("0.01")
1658 value_from.linked_to = portfolio.Amount("FOO", "1000") 1507 value_from.linked_to = portfolio.Amount("FOO", "1000")
1659 value_to = portfolio.Amount("BTC", "1") 1508 value_to = portfolio.Amount("BTC", "1")
1660 trade = portfolio.Trade(value_from, value_to, "FOO", market="market") 1509 trade = portfolio.Trade(value_from, value_to, "FOO", self.m)
1661 1510
1662 trade.prepare_order(compute_value="foo") 1511 trade.prepare_order(compute_value="foo")
1663 1512
1664 filled_amount.assert_called_with(in_base_currency=True) 1513 filled_amount.assert_called_with(in_base_currency=True)
1665 compute_value.assert_called_with(get_ticker.return_value["original"], "buy", compute_value="foo") 1514 compute_value.assert_called_with(self.m.get_ticker.return_value["original"], "buy", compute_value="foo")
1666 self.assertEqual(1, len(trade.orders)) 1515 self.assertEqual(1, len(trade.orders))
1667 Order.assert_called_with("buy", portfolio.Amount("BTC", D("4.8")), 1516 Order.assert_called_with("buy", portfolio.Amount("BTC", D("4.8")),
1668 D("125"), "FOO", "long", "market", 1517 D("125"), "FOO", "long", self.m,
1669 trade, close_if_possible=False) 1518 trade, close_if_possible=False)
1670 1519
1671 with self.subTest(action="acquire", inverted=True): 1520 with self.subTest(action="acquire", inverted=True):
@@ -1676,15 +1525,15 @@ class TradeTest(WebMockTestCase):
1676 value_from.rate = D("0.01") 1525 value_from.rate = D("0.01")
1677 value_from.linked_to = portfolio.Amount("FOO", "100") 1526 value_from.linked_to = portfolio.Amount("FOO", "100")
1678 value_to = portfolio.Amount("BTC", "10") 1527 value_to = portfolio.Amount("BTC", "10")
1679 trade = portfolio.Trade(value_from, value_to, "FOO", market="market") 1528 trade = portfolio.Trade(value_from, value_to, "FOO", self.m)
1680 1529
1681 trade.prepare_order(compute_value="foo") 1530 trade.prepare_order(compute_value="foo")
1682 1531
1683 filled_amount.assert_called_with(in_base_currency=False) 1532 filled_amount.assert_called_with(in_base_currency=False)
1684 compute_value.assert_called_with(get_ticker.return_value["original"], "sell", compute_value="foo") 1533 compute_value.assert_called_with(self.m.get_ticker.return_value["original"], "sell", compute_value="foo")
1685 self.assertEqual(1, len(trade.orders)) 1534 self.assertEqual(1, len(trade.orders))
1686 Order.assert_called_with("sell", portfolio.Amount("BTC", D("5")), 1535 Order.assert_called_with("sell", portfolio.Amount("BTC", D("5")),
1687 D("125"), "FOO", "long", "market", 1536 D("125"), "FOO", "long", self.m,
1688 trade, close_if_possible=False) 1537 trade, close_if_possible=False)
1689 1538
1690 1539
@@ -1696,118 +1545,119 @@ class TradeTest(WebMockTestCase):
1696 value_from = portfolio.Amount("BTC", "0.5") 1545 value_from = portfolio.Amount("BTC", "0.5")
1697 value_from.linked_to = portfolio.Amount("ETH", "10.0") 1546 value_from.linked_to = portfolio.Amount("ETH", "10.0")
1698 value_to = portfolio.Amount("BTC", "1.0") 1547 value_to = portfolio.Amount("BTC", "1.0")
1699 trade = portfolio.Trade(value_from, value_to, "ETH") 1548 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1700 prepare_order.return_value = new_order_mock 1549 prepare_order.return_value = new_order_mock
1701 1550
1702 for i in [0, 1, 3, 4, 6]: 1551 for i in [0, 1, 3, 4, 6]:
1703 with self.subTest(tick=i),\ 1552 with self.subTest(tick=i):
1704 mock.patch.object(portfolio.ReportStore, "log_order") as log_order:
1705 trade.update_order(order_mock, i) 1553 trade.update_order(order_mock, i)
1706 order_mock.cancel.assert_not_called() 1554 order_mock.cancel.assert_not_called()
1707 new_order_mock.run.assert_not_called() 1555 new_order_mock.run.assert_not_called()
1708 log_order.assert_called_once_with(order_mock, i, 1556 self.m.report.log_order.assert_called_once_with(order_mock, i,
1709 update="waiting", compute_value=None, new_order=None) 1557 update="waiting", compute_value=None, new_order=None)
1710 1558
1711 order_mock.reset_mock() 1559 order_mock.reset_mock()
1712 new_order_mock.reset_mock() 1560 new_order_mock.reset_mock()
1713 trade.orders = [] 1561 trade.orders = []
1714 1562 self.m.report.log_order.reset_mock()
1715 with mock.patch.object(portfolio.ReportStore, "log_order") as log_order: 1563
1716 trade.update_order(order_mock, 2) 1564 trade.update_order(order_mock, 2)
1717 order_mock.cancel.assert_called() 1565 order_mock.cancel.assert_called()
1718 new_order_mock.run.assert_called() 1566 new_order_mock.run.assert_called()
1719 prepare_order.assert_called() 1567 prepare_order.assert_called()
1720 log_order.assert_called() 1568 self.m.report.log_order.assert_called()
1721 self.assertEqual(2, log_order.call_count) 1569 self.assertEqual(2, self.m.report.log_order.call_count)
1722 calls = [ 1570 calls = [
1723 mock.call(order_mock, 2, update="adjusting", 1571 mock.call(order_mock, 2, update="adjusting",
1724 compute_value='lambda x, y: (x[y] + x["average"]) / 2', 1572 compute_value='lambda x, y: (x[y] + x["average"]) / 2',
1725 new_order=new_order_mock), 1573 new_order=new_order_mock),
1726 mock.call(order_mock, 2, new_order=new_order_mock), 1574 mock.call(order_mock, 2, new_order=new_order_mock),
1727 ] 1575 ]
1728 log_order.assert_has_calls(calls) 1576 self.m.report.log_order.assert_has_calls(calls)
1729 1577
1730 order_mock.reset_mock() 1578 order_mock.reset_mock()
1731 new_order_mock.reset_mock() 1579 new_order_mock.reset_mock()
1732 trade.orders = [] 1580 trade.orders = []
1733 1581 self.m.report.log_order.reset_mock()
1734 with mock.patch.object(portfolio.ReportStore, "log_order") as log_order: 1582
1735 trade.update_order(order_mock, 5) 1583 trade.update_order(order_mock, 5)
1736 order_mock.cancel.assert_called() 1584 order_mock.cancel.assert_called()
1737 new_order_mock.run.assert_called() 1585 new_order_mock.run.assert_called()
1738 prepare_order.assert_called() 1586 prepare_order.assert_called()
1739 self.assertEqual(2, log_order.call_count) 1587 self.assertEqual(2, self.m.report.log_order.call_count)
1740 log_order.assert_called() 1588 self.m.report.log_order.assert_called()
1741 calls = [ 1589 calls = [
1742 mock.call(order_mock, 5, update="adjusting", 1590 mock.call(order_mock, 5, update="adjusting",
1743 compute_value='lambda x, y: (x[y]*2 + x["average"]) / 3', 1591 compute_value='lambda x, y: (x[y]*2 + x["average"]) / 3',
1744 new_order=new_order_mock), 1592 new_order=new_order_mock),
1745 mock.call(order_mock, 5, new_order=new_order_mock), 1593 mock.call(order_mock, 5, new_order=new_order_mock),
1746 ] 1594 ]
1747 log_order.assert_has_calls(calls) 1595 self.m.report.log_order.assert_has_calls(calls)
1748 1596
1749 order_mock.reset_mock() 1597 order_mock.reset_mock()
1750 new_order_mock.reset_mock() 1598 new_order_mock.reset_mock()
1751 trade.orders = [] 1599 trade.orders = []
1752 1600 self.m.report.log_order.reset_mock()
1753 with mock.patch.object(portfolio.ReportStore, "log_order") as log_order: 1601
1754 trade.update_order(order_mock, 7) 1602 trade.update_order(order_mock, 7)
1755 order_mock.cancel.assert_called() 1603 order_mock.cancel.assert_called()
1756 new_order_mock.run.assert_called() 1604 new_order_mock.run.assert_called()
1757 prepare_order.assert_called_with(compute_value="default") 1605 prepare_order.assert_called_with(compute_value="default")
1758 log_order.assert_called() 1606 self.m.report.log_order.assert_called()
1759 self.assertEqual(2, log_order.call_count) 1607 self.assertEqual(2, self.m.report.log_order.call_count)
1760 calls = [ 1608 calls = [
1761 mock.call(order_mock, 7, update="market_fallback", 1609 mock.call(order_mock, 7, update="market_fallback",
1762 compute_value='default', 1610 compute_value='default',
1763 new_order=new_order_mock), 1611 new_order=new_order_mock),
1764 mock.call(order_mock, 7, new_order=new_order_mock), 1612 mock.call(order_mock, 7, new_order=new_order_mock),
1765 ] 1613 ]
1766 log_order.assert_has_calls(calls) 1614 self.m.report.log_order.assert_has_calls(calls)
1767 1615
1768 order_mock.reset_mock() 1616 order_mock.reset_mock()
1769 new_order_mock.reset_mock() 1617 new_order_mock.reset_mock()
1770 trade.orders = [] 1618 trade.orders = []
1619 self.m.report.log_order.reset_mock()
1771 1620
1772 for i in [10, 13, 16]: 1621 for i in [10, 13, 16]:
1773 with self.subTest(tick=i), mock.patch.object(portfolio.ReportStore, "log_order") as log_order: 1622 with self.subTest(tick=i):
1774 trade.update_order(order_mock, i) 1623 trade.update_order(order_mock, i)
1775 order_mock.cancel.assert_called() 1624 order_mock.cancel.assert_called()
1776 new_order_mock.run.assert_called() 1625 new_order_mock.run.assert_called()
1777 prepare_order.assert_called_with(compute_value="default") 1626 prepare_order.assert_called_with(compute_value="default")
1778 log_order.assert_called() 1627 self.m.report.log_order.assert_called()
1779 self.assertEqual(2, log_order.call_count) 1628 self.assertEqual(2, self.m.report.log_order.call_count)
1780 calls = [ 1629 calls = [
1781 mock.call(order_mock, i, update="market_adjust", 1630 mock.call(order_mock, i, update="market_adjust",
1782 compute_value='default', 1631 compute_value='default',
1783 new_order=new_order_mock), 1632 new_order=new_order_mock),
1784 mock.call(order_mock, i, new_order=new_order_mock), 1633 mock.call(order_mock, i, new_order=new_order_mock),
1785 ] 1634 ]
1786 log_order.assert_has_calls(calls) 1635 self.m.report.log_order.assert_has_calls(calls)
1787 1636
1788 order_mock.reset_mock() 1637 order_mock.reset_mock()
1789 new_order_mock.reset_mock() 1638 new_order_mock.reset_mock()
1790 trade.orders = [] 1639 trade.orders = []
1640 self.m.report.log_order.reset_mock()
1791 1641
1792 for i in [8, 9, 11, 12]: 1642 for i in [8, 9, 11, 12]:
1793 with self.subTest(tick=i), mock.patch.object(portfolio.ReportStore, "log_order") as log_order: 1643 with self.subTest(tick=i):
1794 trade.update_order(order_mock, i) 1644 trade.update_order(order_mock, i)
1795 order_mock.cancel.assert_not_called() 1645 order_mock.cancel.assert_not_called()
1796 new_order_mock.run.assert_not_called() 1646 new_order_mock.run.assert_not_called()
1797 log_order.assert_called_once_with(order_mock, i, update="waiting", 1647 self.m.report.log_order.assert_called_once_with(order_mock, i, update="waiting",
1798 compute_value=None, new_order=None) 1648 compute_value=None, new_order=None)
1799 1649
1800 order_mock.reset_mock() 1650 order_mock.reset_mock()
1801 new_order_mock.reset_mock() 1651 new_order_mock.reset_mock()
1802 trade.orders = [] 1652 trade.orders = []
1653 self.m.report.log_order.reset_mock()
1803 1654
1804 1655
1805 @mock.patch.object(portfolio.ReportStore, "print_log") 1656 def test_print_with_order(self):
1806 def test_print_with_order(self, print_log):
1807 value_from = portfolio.Amount("BTC", "0.5") 1657 value_from = portfolio.Amount("BTC", "0.5")
1808 value_from.linked_to = portfolio.Amount("ETH", "10.0") 1658 value_from.linked_to = portfolio.Amount("ETH", "10.0")
1809 value_to = portfolio.Amount("BTC", "1.0") 1659 value_to = portfolio.Amount("BTC", "1.0")
1810 trade = portfolio.Trade(value_from, value_to, "ETH") 1660 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1811 1661
1812 order_mock1 = mock.Mock() 1662 order_mock1 = mock.Mock()
1813 order_mock1.__repr__ = mock.Mock() 1663 order_mock1.__repr__ = mock.Mock()
@@ -1830,8 +1680,8 @@ class TradeTest(WebMockTestCase):
1830 1680
1831 trade.print_with_order() 1681 trade.print_with_order()
1832 1682
1833 print_log.assert_called() 1683 self.m.report.print_log.assert_called()
1834 calls = print_log.mock_calls 1684 calls = self.m.report.print_log.mock_calls
1835 self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(calls[0][1][0])) 1685 self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(calls[0][1][0]))
1836 self.assertEqual("\tMock 1", str(calls[1][1][0])) 1686 self.assertEqual("\tMock 1", str(calls[1][1][0]))
1837 self.assertEqual("\tMock 2", str(calls[2][1][0])) 1687 self.assertEqual("\tMock 2", str(calls[2][1][0]))
@@ -1842,7 +1692,7 @@ class TradeTest(WebMockTestCase):
1842 value_from = portfolio.Amount("BTC", "0.5") 1692 value_from = portfolio.Amount("BTC", "0.5")
1843 value_from.linked_to = portfolio.Amount("ETH", "10.0") 1693 value_from.linked_to = portfolio.Amount("ETH", "10.0")
1844 value_to = portfolio.Amount("BTC", "1.0") 1694 value_to = portfolio.Amount("BTC", "1.0")
1845 trade = portfolio.Trade(value_from, value_to, "ETH") 1695 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1846 1696
1847 self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(trade)) 1697 self.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(trade))
1848 1698
@@ -1850,7 +1700,7 @@ class TradeTest(WebMockTestCase):
1850 value_from = portfolio.Amount("BTC", "0.5") 1700 value_from = portfolio.Amount("BTC", "0.5")
1851 value_from.linked_to = portfolio.Amount("ETH", "10.0") 1701 value_from.linked_to = portfolio.Amount("ETH", "10.0")
1852 value_to = portfolio.Amount("BTC", "1.0") 1702 value_to = portfolio.Amount("BTC", "1.0")
1853 trade = portfolio.Trade(value_from, value_to, "ETH") 1703 trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
1854 1704
1855 as_json = trade.as_json() 1705 as_json = trade.as_json()
1856 self.assertEqual("acquire", as_json["action"]) 1706 self.assertEqual("acquire", as_json["action"])
@@ -1942,34 +1792,32 @@ class OrderTest(WebMockTestCase):
1942 self.assertTrue(order.finished) 1792 self.assertTrue(order.finished)
1943 1793
1944 @mock.patch.object(portfolio.Order, "fetch") 1794 @mock.patch.object(portfolio.Order, "fetch")
1945 @mock.patch("portfolio.ReportStore") 1795 def test_cancel(self, fetch):
1946 def test_cancel(self, report_store, fetch): 1796 self.m.debug = True
1947 market = mock.Mock()
1948 portfolio.TradeStore.debug = True
1949 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1797 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
1950 D("0.1"), "BTC", "long", market, "trade") 1798 D("0.1"), "BTC", "long", self.m, "trade")
1951 order.status = "open" 1799 order.status = "open"
1952 1800
1953 order.cancel() 1801 order.cancel()
1954 market.cancel_order.assert_not_called() 1802 self.m.ccxt.cancel_order.assert_not_called()
1955 report_store.log_debug_action.assert_called_once() 1803 self.m.report.log_debug_action.assert_called_once()
1956 report_store.log_debug_action.reset_mock() 1804 self.m.report.log_debug_action.reset_mock()
1957 self.assertEqual("canceled", order.status) 1805 self.assertEqual("canceled", order.status)
1958 1806
1959 portfolio.TradeStore.debug = False 1807 self.m.debug = False
1960 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1808 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
1961 D("0.1"), "BTC", "long", market, "trade") 1809 D("0.1"), "BTC", "long", self.m, "trade")
1962 order.status = "open" 1810 order.status = "open"
1963 order.id = 42 1811 order.id = 42
1964 1812
1965 order.cancel() 1813 order.cancel()
1966 market.cancel_order.assert_called_with(42) 1814 self.m.ccxt.cancel_order.assert_called_with(42)
1967 fetch.assert_called_once() 1815 fetch.assert_called_once()
1968 report_store.log_debug_action.assert_not_called() 1816 self.m.report.log_debug_action.assert_not_called()
1969 1817
1970 def test_dust_amount_remaining(self): 1818 def test_dust_amount_remaining(self):
1971 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1819 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
1972 D("0.1"), "BTC", "long", "market", "trade") 1820 D("0.1"), "BTC", "long", self.m, "trade")
1973 order.remaining_amount = mock.Mock(return_value=portfolio.Amount("ETH", 1)) 1821 order.remaining_amount = mock.Mock(return_value=portfolio.Amount("ETH", 1))
1974 self.assertFalse(order.dust_amount_remaining()) 1822 self.assertFalse(order.dust_amount_remaining())
1975 1823
@@ -1980,7 +1828,7 @@ class OrderTest(WebMockTestCase):
1980 @mock.patch.object(portfolio.Order, "filled_amount", return_value=portfolio.Amount("ETH", 1)) 1828 @mock.patch.object(portfolio.Order, "filled_amount", return_value=portfolio.Amount("ETH", 1))
1981 def test_remaining_amount(self, filled_amount, fetch): 1829 def test_remaining_amount(self, filled_amount, fetch):
1982 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1830 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
1983 D("0.1"), "BTC", "long", "market", "trade") 1831 D("0.1"), "BTC", "long", self.m, "trade")
1984 1832
1985 self.assertEqual(9, order.remaining_amount().value) 1833 self.assertEqual(9, order.remaining_amount().value)
1986 order.fetch.assert_not_called() 1834 order.fetch.assert_not_called()
@@ -1992,7 +1840,7 @@ class OrderTest(WebMockTestCase):
1992 @mock.patch.object(portfolio.Order, "fetch") 1840 @mock.patch.object(portfolio.Order, "fetch")
1993 def test_filled_amount(self, fetch): 1841 def test_filled_amount(self, fetch):
1994 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1842 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
1995 D("0.1"), "BTC", "long", "market", "trade") 1843 D("0.1"), "BTC", "long", self.m, "trade")
1996 order.mouvements.append(portfolio.Mouvement("ETH", "BTC", { 1844 order.mouvements.append(portfolio.Mouvement("ETH", "BTC", {
1997 "tradeID": 42, "type": "buy", "fee": "0.0015", 1845 "tradeID": 42, "type": "buy", "fee": "0.0015",
1998 "date": "2017-12-30 12:00:12", "rate": "0.1", 1846 "date": "2017-12-30 12:00:12", "rate": "0.1",
@@ -2011,8 +1859,7 @@ class OrderTest(WebMockTestCase):
2011 self.assertEqual(portfolio.Amount("BTC", "0.7"), order.filled_amount(in_base_currency=True)) 1859 self.assertEqual(portfolio.Amount("BTC", "0.7"), order.filled_amount(in_base_currency=True))
2012 1860
2013 def test_fetch_mouvements(self): 1861 def test_fetch_mouvements(self):
2014 market = mock.Mock() 1862 self.m.ccxt.privatePostReturnOrderTrades.return_value = [
2015 market.privatePostReturnOrderTrades.return_value = [
2016 { 1863 {
2017 "tradeID": 42, "type": "buy", "fee": "0.0015", 1864 "tradeID": 42, "type": "buy", "fee": "0.0015",
2018 "date": "2017-12-30 12:00:12", "rate": "0.1", 1865 "date": "2017-12-30 12:00:12", "rate": "0.1",
@@ -2025,148 +1872,143 @@ class OrderTest(WebMockTestCase):
2025 } 1872 }
2026 ] 1873 ]
2027 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1874 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2028 D("0.1"), "BTC", "long", market, "trade") 1875 D("0.1"), "BTC", "long", self.m, "trade")
2029 order.id = 12 1876 order.id = 12
2030 order.mouvements = ["Foo", "Bar", "Baz"] 1877 order.mouvements = ["Foo", "Bar", "Baz"]
2031 1878
2032 order.fetch_mouvements() 1879 order.fetch_mouvements()
2033 1880
2034 market.privatePostReturnOrderTrades.assert_called_with({"orderNumber": 12}) 1881 self.m.ccxt.privatePostReturnOrderTrades.assert_called_with({"orderNumber": 12})
2035 self.assertEqual(2, len(order.mouvements)) 1882 self.assertEqual(2, len(order.mouvements))
2036 self.assertEqual(42, order.mouvements[0].id) 1883 self.assertEqual(42, order.mouvements[0].id)
2037 self.assertEqual(43, order.mouvements[1].id) 1884 self.assertEqual(43, order.mouvements[1].id)
2038 1885
2039 market.privatePostReturnOrderTrades.side_effect = portfolio.ExchangeError 1886 self.m.ccxt.privatePostReturnOrderTrades.side_effect = portfolio.ExchangeError
2040 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1887 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2041 D("0.1"), "BTC", "long", market, "trade") 1888 D("0.1"), "BTC", "long", self.m, "trade")
2042 order.fetch_mouvements() 1889 order.fetch_mouvements()
2043 self.assertEqual(0, len(order.mouvements)) 1890 self.assertEqual(0, len(order.mouvements))
2044 1891
2045 @mock.patch("portfolio.ReportStore") 1892 def test_mark_finished_order(self):
2046 def test_mark_finished_order(self, report_store):
2047 market = mock.Mock()
2048 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1893 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2049 D("0.1"), "BTC", "short", market, "trade", 1894 D("0.1"), "BTC", "short", self.m, "trade",
2050 close_if_possible=True) 1895 close_if_possible=True)
2051 order.status = "closed" 1896 order.status = "closed"
2052 portfolio.TradeStore.debug = False 1897 self.m.debug = False
2053 1898
2054 order.mark_finished_order() 1899 order.mark_finished_order()
2055 market.close_margin_position.assert_called_with("ETH", "BTC") 1900 self.m.ccxt.close_margin_position.assert_called_with("ETH", "BTC")
2056 market.close_margin_position.reset_mock() 1901 self.m.ccxt.close_margin_position.reset_mock()
2057 1902
2058 order.status = "open" 1903 order.status = "open"
2059 order.mark_finished_order() 1904 order.mark_finished_order()
2060 market.close_margin_position.assert_not_called() 1905 self.m.ccxt.close_margin_position.assert_not_called()
2061 1906
2062 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1907 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2063 D("0.1"), "BTC", "short", market, "trade", 1908 D("0.1"), "BTC", "short", self.m, "trade",
2064 close_if_possible=False) 1909 close_if_possible=False)
2065 order.status = "closed" 1910 order.status = "closed"
2066 order.mark_finished_order() 1911 order.mark_finished_order()
2067 market.close_margin_position.assert_not_called() 1912 self.m.ccxt.close_margin_position.assert_not_called()
2068 1913
2069 order = portfolio.Order("sell", portfolio.Amount("ETH", 10), 1914 order = portfolio.Order("sell", portfolio.Amount("ETH", 10),
2070 D("0.1"), "BTC", "short", market, "trade", 1915 D("0.1"), "BTC", "short", self.m, "trade",
2071 close_if_possible=True) 1916 close_if_possible=True)
2072 order.status = "closed" 1917 order.status = "closed"
2073 order.mark_finished_order() 1918 order.mark_finished_order()
2074 market.close_margin_position.assert_not_called() 1919 self.m.ccxt.close_margin_position.assert_not_called()
2075 1920
2076 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1921 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2077 D("0.1"), "BTC", "long", market, "trade", 1922 D("0.1"), "BTC", "long", self.m, "trade",
2078 close_if_possible=True) 1923 close_if_possible=True)
2079 order.status = "closed" 1924 order.status = "closed"
2080 order.mark_finished_order() 1925 order.mark_finished_order()
2081 market.close_margin_position.assert_not_called() 1926 self.m.ccxt.close_margin_position.assert_not_called()
2082 1927
2083 portfolio.TradeStore.debug = True 1928 self.m.debug = True
2084 1929
2085 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1930 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2086 D("0.1"), "BTC", "short", market, "trade", 1931 D("0.1"), "BTC", "short", self.m, "trade",
2087 close_if_possible=True) 1932 close_if_possible=True)
2088 order.status = "closed" 1933 order.status = "closed"
2089 1934
2090 order.mark_finished_order() 1935 order.mark_finished_order()
2091 market.close_margin_position.assert_not_called() 1936 self.m.ccxt.close_margin_position.assert_not_called()
2092 report_store.log_debug_action.assert_called_once() 1937 self.m.report.log_debug_action.assert_called_once()
2093 1938
2094 @mock.patch.object(portfolio.Order, "fetch_mouvements") 1939 @mock.patch.object(portfolio.Order, "fetch_mouvements")
2095 @mock.patch("portfolio.ReportStore") 1940 def test_fetch(self, fetch_mouvements):
2096 def test_fetch(self, report_store, fetch_mouvements):
2097 time = self.time.time() 1941 time = self.time.time()
2098 with mock.patch.object(portfolio.time, "time") as time_mock: 1942 with mock.patch.object(portfolio.time, "time") as time_mock:
2099 market = mock.Mock()
2100 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 1943 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2101 D("0.1"), "BTC", "long", market, "trade") 1944 D("0.1"), "BTC", "long", self.m, "trade")
2102 order.id = 45 1945 order.id = 45
2103 with self.subTest(debug=True): 1946 with self.subTest(debug=True):
2104 portfolio.TradeStore.debug = True 1947 self.m.debug = True
2105 order.fetch() 1948 order.fetch()
2106 time_mock.assert_not_called() 1949 time_mock.assert_not_called()
2107 report_store.log_debug_action.assert_called_once() 1950 self.m.report.log_debug_action.assert_called_once()
2108 report_store.log_debug_action.reset_mock() 1951 self.m.report.log_debug_action.reset_mock()
2109 order.fetch(force=True) 1952 order.fetch(force=True)
2110 time_mock.assert_not_called() 1953 time_mock.assert_not_called()
2111 market.fetch_order.assert_not_called() 1954 self.m.ccxt.fetch_order.assert_not_called()
2112 fetch_mouvements.assert_not_called() 1955 fetch_mouvements.assert_not_called()
2113 report_store.log_debug_action.assert_called_once() 1956 self.m.report.log_debug_action.assert_called_once()
2114 report_store.log_debug_action.reset_mock() 1957 self.m.report.log_debug_action.reset_mock()
2115 self.assertIsNone(order.fetch_cache_timestamp) 1958 self.assertIsNone(order.fetch_cache_timestamp)
2116 1959
2117 with self.subTest(debug=False): 1960 with self.subTest(debug=False):
2118 portfolio.TradeStore.debug = False 1961 self.m.debug = False
2119 time_mock.return_value = time 1962 time_mock.return_value = time
2120 market.fetch_order.return_value = { 1963 self.m.ccxt.fetch_order.return_value = {
2121 "status": "foo", 1964 "status": "foo",
2122 "datetime": "timestamp" 1965 "datetime": "timestamp"
2123 } 1966 }
2124 order.fetch() 1967 order.fetch()
2125 1968
2126 market.fetch_order.assert_called_once() 1969 self.m.ccxt.fetch_order.assert_called_once()
2127 fetch_mouvements.assert_called_once() 1970 fetch_mouvements.assert_called_once()
2128 self.assertEqual("foo", order.status) 1971 self.assertEqual("foo", order.status)
2129 self.assertEqual("timestamp", order.timestamp) 1972 self.assertEqual("timestamp", order.timestamp)
2130 self.assertEqual(time, order.fetch_cache_timestamp) 1973 self.assertEqual(time, order.fetch_cache_timestamp)
2131 self.assertEqual(1, len(order.results)) 1974 self.assertEqual(1, len(order.results))
2132 1975
2133 market.fetch_order.reset_mock() 1976 self.m.ccxt.fetch_order.reset_mock()
2134 fetch_mouvements.reset_mock() 1977 fetch_mouvements.reset_mock()
2135 1978
2136 time_mock.return_value = time + 8 1979 time_mock.return_value = time + 8
2137 order.fetch() 1980 order.fetch()
2138 market.fetch_order.assert_not_called() 1981 self.m.ccxt.fetch_order.assert_not_called()
2139 fetch_mouvements.assert_not_called() 1982 fetch_mouvements.assert_not_called()
2140 1983
2141 order.fetch(force=True) 1984 order.fetch(force=True)
2142 market.fetch_order.assert_called_once() 1985 self.m.ccxt.fetch_order.assert_called_once()
2143 fetch_mouvements.assert_called_once() 1986 fetch_mouvements.assert_called_once()
2144 1987
2145 market.fetch_order.reset_mock() 1988 self.m.ccxt.fetch_order.reset_mock()
2146 fetch_mouvements.reset_mock() 1989 fetch_mouvements.reset_mock()
2147 1990
2148 time_mock.return_value = time + 19 1991 time_mock.return_value = time + 19
2149 order.fetch() 1992 order.fetch()
2150 market.fetch_order.assert_called_once() 1993 self.m.ccxt.fetch_order.assert_called_once()
2151 fetch_mouvements.assert_called_once() 1994 fetch_mouvements.assert_called_once()
2152 report_store.log_debug_action.assert_not_called() 1995 self.m.report.log_debug_action.assert_not_called()
2153 1996
2154 @mock.patch.object(portfolio.Order, "fetch") 1997 @mock.patch.object(portfolio.Order, "fetch")
2155 @mock.patch.object(portfolio.Order, "mark_finished_order") 1998 @mock.patch.object(portfolio.Order, "mark_finished_order")
2156 @mock.patch("portfolio.ReportStore") 1999 def test_get_status(self, mark_finished_order, fetch):
2157 def test_get_status(self, report_store, mark_finished_order, fetch):
2158 with self.subTest(debug=True): 2000 with self.subTest(debug=True):
2159 portfolio.TradeStore.debug = True 2001 self.m.debug = True
2160 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 2002 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2161 D("0.1"), "BTC", "long", "market", "trade") 2003 D("0.1"), "BTC", "long", self.m, "trade")
2162 self.assertEqual("pending", order.get_status()) 2004 self.assertEqual("pending", order.get_status())
2163 fetch.assert_not_called() 2005 fetch.assert_not_called()
2164 report_store.log_debug_action.assert_called_once() 2006 self.m.report.log_debug_action.assert_called_once()
2165 2007
2166 with self.subTest(debug=False, finished=False): 2008 with self.subTest(debug=False, finished=False):
2167 portfolio.TradeStore.debug = False 2009 self.m.debug = False
2168 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 2010 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2169 D("0.1"), "BTC", "long", "market", "trade") 2011 D("0.1"), "BTC", "long", self.m, "trade")
2170 def _fetch(order): 2012 def _fetch(order):
2171 def update_status(): 2013 def update_status():
2172 order.status = "open" 2014 order.status = "open"
@@ -2179,9 +2021,9 @@ class OrderTest(WebMockTestCase):
2179 mark_finished_order.reset_mock() 2021 mark_finished_order.reset_mock()
2180 fetch.reset_mock() 2022 fetch.reset_mock()
2181 with self.subTest(debug=False, finished=True): 2023 with self.subTest(debug=False, finished=True):
2182 portfolio.TradeStore.debug = False 2024 self.m.debug = False
2183 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 2025 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2184 D("0.1"), "BTC", "long", "market", "trade") 2026 D("0.1"), "BTC", "long", self.m, "trade")
2185 def _fetch(order): 2027 def _fetch(order):
2186 def update_status(): 2028 def update_status():
2187 order.status = "closed" 2029 order.status = "closed"
@@ -2192,52 +2034,48 @@ class OrderTest(WebMockTestCase):
2192 fetch.assert_called_once() 2034 fetch.assert_called_once()
2193 2035
2194 def test_run(self): 2036 def test_run(self):
2195 market = mock.Mock() 2037 self.m.ccxt.order_precision.return_value = 4
2196 2038 with self.subTest(debug=True):
2197 market.order_precision.return_value = 4 2039 self.m.debug = True
2198 with self.subTest(debug=True),\
2199 mock.patch('portfolio.ReportStore') as report_store:
2200 portfolio.TradeStore.debug = True
2201 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 2040 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2202 D("0.1"), "BTC", "long", market, "trade") 2041 D("0.1"), "BTC", "long", self.m, "trade")
2203 order.run() 2042 order.run()
2204 market.create_order.assert_not_called() 2043 self.m.ccxt.create_order.assert_not_called()
2205 report_store.log_debug_action.assert_called_with("market.create_order('ETH/BTC', 'limit', 'buy', 10.0000, price=0.1, account=exchange)") 2044 self.m.report.log_debug_action.assert_called_with("market.ccxt.create_order('ETH/BTC', 'limit', 'buy', 10.0000, price=0.1, account=exchange)")
2206 self.assertEqual("open", order.status) 2045 self.assertEqual("open", order.status)
2207 self.assertEqual(1, len(order.results)) 2046 self.assertEqual(1, len(order.results))
2208 self.assertEqual(-1, order.id) 2047 self.assertEqual(-1, order.id)
2209 2048
2210 market.create_order.reset_mock() 2049 self.m.ccxt.create_order.reset_mock()
2211 with self.subTest(debug=False): 2050 with self.subTest(debug=False):
2212 portfolio.TradeStore.debug = False 2051 self.m.debug = False
2213 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 2052 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2214 D("0.1"), "BTC", "long", market, "trade") 2053 D("0.1"), "BTC", "long", self.m, "trade")
2215 market.create_order.return_value = { "id": 123 } 2054 self.m.ccxt.create_order.return_value = { "id": 123 }
2216 order.run() 2055 order.run()
2217 market.create_order.assert_called_once() 2056 self.m.ccxt.create_order.assert_called_once()
2218 self.assertEqual(1, len(order.results)) 2057 self.assertEqual(1, len(order.results))
2219 self.assertEqual("open", order.status) 2058 self.assertEqual("open", order.status)
2220 2059
2221 market.create_order.reset_mock() 2060 self.m.ccxt.create_order.reset_mock()
2222 with self.subTest(exception=True),\ 2061 with self.subTest(exception=True):
2223 mock.patch('portfolio.ReportStore') as report_store:
2224 order = portfolio.Order("buy", portfolio.Amount("ETH", 10), 2062 order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
2225 D("0.1"), "BTC", "long", market, "trade") 2063 D("0.1"), "BTC", "long", self.m, "trade")
2226 market.create_order.side_effect = Exception("bouh") 2064 self.m.ccxt.create_order.side_effect = Exception("bouh")
2227 order.run() 2065 order.run()
2228 market.create_order.assert_called_once() 2066 self.m.ccxt.create_order.assert_called_once()
2229 self.assertEqual(0, len(order.results)) 2067 self.assertEqual(0, len(order.results))
2230 self.assertEqual("error", order.status) 2068 self.assertEqual("error", order.status)
2231 report_store.log_error.assert_called_once() 2069 self.m.report.log_error.assert_called_once()
2232 2070
2233 market.create_order.reset_mock() 2071 self.m.ccxt.create_order.reset_mock()
2234 with self.subTest(dust_amount_exception=True),\ 2072 with self.subTest(dust_amount_exception=True),\
2235 mock.patch.object(portfolio.Order, "mark_finished_order") as mark_finished_order: 2073 mock.patch.object(portfolio.Order, "mark_finished_order") as mark_finished_order:
2236 order = portfolio.Order("buy", portfolio.Amount("ETH", 0.001), 2074 order = portfolio.Order("buy", portfolio.Amount("ETH", 0.001),
2237 D("0.1"), "BTC", "long", market, "trade") 2075 D("0.1"), "BTC", "long", self.m, "trade")
2238 market.create_order.side_effect = portfolio.ExchangeNotAvailable 2076 self.m.ccxt.create_order.side_effect = portfolio.ExchangeNotAvailable
2239 order.run() 2077 order.run()
2240 market.create_order.assert_called_once() 2078 self.m.ccxt.create_order.assert_called_once()
2241 self.assertEqual(0, len(order.results)) 2079 self.assertEqual(0, len(order.results))
2242 self.assertEqual("closed", order.status) 2080 self.assertEqual("closed", order.status)
2243 mark_finished_order.assert_called_once() 2081 mark_finished_order.assert_called_once()
@@ -2304,61 +2142,66 @@ class MouvementTest(WebMockTestCase):
2304@unittest.skipUnless("unit" in limits, "Unit skipped") 2142@unittest.skipUnless("unit" in limits, "Unit skipped")
2305class ReportStoreTest(WebMockTestCase): 2143class ReportStoreTest(WebMockTestCase):
2306 def test_add_log(self): 2144 def test_add_log(self):
2307 portfolio.ReportStore.add_log({"foo": "bar"}) 2145 report_store = market.ReportStore(self.m)
2146 report_store.add_log({"foo": "bar"})
2308 2147
2309 self.assertEqual({"foo": "bar", "date": mock.ANY}, portfolio.ReportStore.logs[0]) 2148 self.assertEqual({"foo": "bar", "date": mock.ANY}, report_store.logs[0])
2310 2149
2311 def test_set_verbose(self): 2150 def test_set_verbose(self):
2151 report_store = market.ReportStore(self.m)
2312 with self.subTest(verbose=True): 2152 with self.subTest(verbose=True):
2313 portfolio.ReportStore.set_verbose(True) 2153 report_store.set_verbose(True)
2314 self.assertTrue(portfolio.ReportStore.verbose_print) 2154 self.assertTrue(report_store.verbose_print)
2315 2155
2316 with self.subTest(verbose=False): 2156 with self.subTest(verbose=False):
2317 portfolio.ReportStore.set_verbose(False) 2157 report_store.set_verbose(False)
2318 self.assertFalse(portfolio.ReportStore.verbose_print) 2158 self.assertFalse(report_store.verbose_print)
2319 2159
2320 def test_print_log(self): 2160 def test_print_log(self):
2161 report_store = market.ReportStore(self.m)
2321 with self.subTest(verbose=True),\ 2162 with self.subTest(verbose=True),\
2322 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: 2163 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
2323 portfolio.ReportStore.set_verbose(True) 2164 report_store.set_verbose(True)
2324 portfolio.ReportStore.print_log("Coucou") 2165 report_store.print_log("Coucou")
2325 portfolio.ReportStore.print_log(portfolio.Amount("BTC", 1)) 2166 report_store.print_log(portfolio.Amount("BTC", 1))
2326 self.assertEqual(stdout_mock.getvalue(), "Coucou\n1.00000000 BTC\n") 2167 self.assertEqual(stdout_mock.getvalue(), "Coucou\n1.00000000 BTC\n")
2327 2168
2328 with self.subTest(verbose=False),\ 2169 with self.subTest(verbose=False),\
2329 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: 2170 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
2330 portfolio.ReportStore.set_verbose(False) 2171 report_store.set_verbose(False)
2331 portfolio.ReportStore.print_log("Coucou") 2172 report_store.print_log("Coucou")
2332 portfolio.ReportStore.print_log(portfolio.Amount("BTC", 1)) 2173 report_store.print_log(portfolio.Amount("BTC", 1))
2333 self.assertEqual(stdout_mock.getvalue(), "") 2174 self.assertEqual(stdout_mock.getvalue(), "")
2334 2175
2335 def test_to_json(self): 2176 def test_to_json(self):
2336 portfolio.ReportStore.logs.append({"foo": "bar"}) 2177 report_store = market.ReportStore(self.m)
2337 self.assertEqual('[{"foo": "bar"}]', portfolio.ReportStore.to_json()) 2178 report_store.logs.append({"foo": "bar"})
2338 portfolio.ReportStore.logs.append({"date": portfolio.datetime(2018, 2, 24)}) 2179 self.assertEqual('[{"foo": "bar"}]', report_store.to_json())
2339 self.assertEqual('[{"foo": "bar"}, {"date": "2018-02-24T00:00:00"}]', portfolio.ReportStore.to_json()) 2180 report_store.logs.append({"date": portfolio.datetime(2018, 2, 24)})
2340 portfolio.ReportStore.logs.append({"amount": portfolio.Amount("BTC", 1)}) 2181 self.assertEqual('[{"foo": "bar"}, {"date": "2018-02-24T00:00:00"}]', report_store.to_json())
2182 report_store.logs.append({"amount": portfolio.Amount("BTC", 1)})
2341 with self.assertRaises(TypeError): 2183 with self.assertRaises(TypeError):
2342 portfolio.ReportStore.to_json() 2184 report_store.to_json()
2343 2185
2344 @mock.patch.object(portfolio.ReportStore, "print_log") 2186 @mock.patch.object(market.ReportStore, "print_log")
2345 @mock.patch.object(portfolio.ReportStore, "add_log") 2187 @mock.patch.object(market.ReportStore, "add_log")
2346 def test_log_stage(self, add_log, print_log): 2188 def test_log_stage(self, add_log, print_log):
2347 portfolio.ReportStore.log_stage("foo") 2189 report_store = market.ReportStore(self.m)
2190 report_store.log_stage("foo")
2348 print_log.assert_has_calls([ 2191 print_log.assert_has_calls([
2349 mock.call("-----------"), 2192 mock.call("-----------"),
2350 mock.call("[Stage] foo"), 2193 mock.call("[Stage] foo"),
2351 ]) 2194 ])
2352 add_log.assert_called_once_with({'type': 'stage', 'stage': 'foo'}) 2195 add_log.assert_called_once_with({'type': 'stage', 'stage': 'foo'})
2353 2196
2354 @mock.patch.object(portfolio.ReportStore, "print_log") 2197 @mock.patch.object(market.ReportStore, "print_log")
2355 @mock.patch.object(portfolio.ReportStore, "add_log") 2198 @mock.patch.object(market.ReportStore, "add_log")
2356 @mock.patch("store.BalanceStore") 2199 def test_log_balances(self, add_log, print_log):
2357 def test_log_balances(self, balance_store, add_log, print_log): 2200 report_store = market.ReportStore(self.m)
2358 balance_store.as_json.return_value = "json" 2201 self.m.balances.as_json.return_value = "json"
2359 balance_store.all = { "FOO": "bar", "BAR": "baz" } 2202 self.m.balances.all = { "FOO": "bar", "BAR": "baz" }
2360 2203
2361 portfolio.ReportStore.log_balances("market", tag="tag") 2204 report_store.log_balances(tag="tag")
2362 print_log.assert_has_calls([ 2205 print_log.assert_has_calls([
2363 mock.call("[Balance]"), 2206 mock.call("[Balance]"),
2364 mock.call("\tbar"), 2207 mock.call("\tbar"),
@@ -2370,17 +2213,17 @@ class ReportStoreTest(WebMockTestCase):
2370 'tag': 'tag' 2213 'tag': 'tag'
2371 }) 2214 })
2372 2215
2373 @mock.patch.object(portfolio.ReportStore, "print_log") 2216 @mock.patch.object(market.ReportStore, "print_log")
2374 @mock.patch.object(portfolio.ReportStore, "add_log") 2217 @mock.patch.object(market.ReportStore, "add_log")
2375 def test_log_tickers(self, add_log, print_log): 2218 def test_log_tickers(self, add_log, print_log):
2376 market = mock.Mock() 2219 report_store = market.ReportStore(self.m)
2377 amounts = { 2220 amounts = {
2378 "BTC": portfolio.Amount("BTC", 10), 2221 "BTC": portfolio.Amount("BTC", 10),
2379 "ETH": portfolio.Amount("BTC", D("0.3")) 2222 "ETH": portfolio.Amount("BTC", D("0.3"))
2380 } 2223 }
2381 amounts["ETH"].rate = D("0.1") 2224 amounts["ETH"].rate = D("0.1")
2382 2225
2383 portfolio.ReportStore.log_tickers(market, amounts, "BTC", "default", "total") 2226 report_store.log_tickers(amounts, "BTC", "default", "total")
2384 print_log.assert_not_called() 2227 print_log.assert_not_called()
2385 add_log.assert_called_once_with({ 2228 add_log.assert_called_once_with({
2386 'type': 'tickers', 2229 'type': 'tickers',
@@ -2398,15 +2241,16 @@ class ReportStoreTest(WebMockTestCase):
2398 'total': D('10.3') 2241 'total': D('10.3')
2399 }) 2242 })
2400 2243
2401 @mock.patch.object(portfolio.ReportStore, "print_log") 2244 @mock.patch.object(market.ReportStore, "print_log")
2402 @mock.patch.object(portfolio.ReportStore, "add_log") 2245 @mock.patch.object(market.ReportStore, "add_log")
2403 def test_log_dispatch(self, add_log, print_log): 2246 def test_log_dispatch(self, add_log, print_log):
2247 report_store = market.ReportStore(self.m)
2404 amount = portfolio.Amount("BTC", "10.3") 2248 amount = portfolio.Amount("BTC", "10.3")
2405 amounts = { 2249 amounts = {
2406 "BTC": portfolio.Amount("BTC", 10), 2250 "BTC": portfolio.Amount("BTC", 10),
2407 "ETH": portfolio.Amount("BTC", D("0.3")) 2251 "ETH": portfolio.Amount("BTC", D("0.3"))
2408 } 2252 }
2409 portfolio.ReportStore.log_dispatch(amount, amounts, "medium", "repartition") 2253 report_store.log_dispatch(amount, amounts, "medium", "repartition")
2410 print_log.assert_not_called() 2254 print_log.assert_not_called()
2411 add_log.assert_called_once_with({ 2255 add_log.assert_called_once_with({
2412 'type': 'dispatch', 2256 'type': 'dispatch',
@@ -2422,9 +2266,10 @@ class ReportStoreTest(WebMockTestCase):
2422 } 2266 }
2423 }) 2267 })
2424 2268
2425 @mock.patch.object(portfolio.ReportStore, "print_log") 2269 @mock.patch.object(market.ReportStore, "print_log")
2426 @mock.patch.object(portfolio.ReportStore, "add_log") 2270 @mock.patch.object(market.ReportStore, "add_log")
2427 def test_log_trades(self, add_log, print_log): 2271 def test_log_trades(self, add_log, print_log):
2272 report_store = market.ReportStore(self.m)
2428 trade_mock1 = mock.Mock() 2273 trade_mock1 = mock.Mock()
2429 trade_mock2 = mock.Mock() 2274 trade_mock2 = mock.Mock()
2430 trade_mock1.as_json.return_value = { "trade": "1" } 2275 trade_mock1.as_json.return_value = { "trade": "1" }
@@ -2434,23 +2279,24 @@ class ReportStoreTest(WebMockTestCase):
2434 (True, trade_mock1), 2279 (True, trade_mock1),
2435 (False, trade_mock2), 2280 (False, trade_mock2),
2436 ] 2281 ]
2437 portfolio.ReportStore.log_trades(matching_and_trades, "only", "debug") 2282 report_store.log_trades(matching_and_trades, "only")
2438 2283
2439 print_log.assert_not_called() 2284 print_log.assert_not_called()
2440 add_log.assert_called_with({ 2285 add_log.assert_called_with({
2441 'type': 'trades', 2286 'type': 'trades',
2442 'only': 'only', 2287 'only': 'only',
2443 'debug': 'debug', 2288 'debug': False,
2444 'trades': [ 2289 'trades': [
2445 {'trade': '1', 'skipped': False}, 2290 {'trade': '1', 'skipped': False},
2446 {'trade': '2', 'skipped': True} 2291 {'trade': '2', 'skipped': True}
2447 ] 2292 ]
2448 }) 2293 })
2449 2294
2450 @mock.patch.object(portfolio.ReportStore, "print_log") 2295 @mock.patch.object(market.ReportStore, "print_log")
2451 @mock.patch.object(portfolio.ReportStore, "add_log") 2296 @mock.patch.object(market.ReportStore, "add_log")
2452 @mock.patch.object(portfolio.TradeStore, "print_all_with_order") 2297 def test_log_orders(self, add_log, print_log):
2453 def test_log_orders(self, print_all_with_order, add_log, print_log): 2298 report_store = market.ReportStore(self.m)
2299
2454 order_mock1 = mock.Mock() 2300 order_mock1 = mock.Mock()
2455 order_mock2 = mock.Mock() 2301 order_mock2 = mock.Mock()
2456 2302
@@ -2459,11 +2305,11 @@ class ReportStoreTest(WebMockTestCase):
2459 2305
2460 orders = [order_mock1, order_mock2] 2306 orders = [order_mock1, order_mock2]
2461 2307
2462 portfolio.ReportStore.log_orders(orders, tick="tick", 2308 report_store.log_orders(orders, tick="tick",
2463 only="only", compute_value="compute_value") 2309 only="only", compute_value="compute_value")
2464 2310
2465 print_log.assert_called_once_with("[Orders]") 2311 print_log.assert_called_once_with("[Orders]")
2466 print_all_with_order.assert_called_once_with(ind="\t") 2312 self.m.trades.print_all_with_order.assert_called_once_with(ind="\t")
2467 2313
2468 add_log.assert_called_with({ 2314 add_log.assert_called_with({
2469 'type': 'orders', 2315 'type': 'orders',
@@ -2473,9 +2319,10 @@ class ReportStoreTest(WebMockTestCase):
2473 'orders': ['order1', 'order2'] 2319 'orders': ['order1', 'order2']
2474 }) 2320 })
2475 2321
2476 @mock.patch.object(portfolio.ReportStore, "print_log") 2322 @mock.patch.object(market.ReportStore, "print_log")
2477 @mock.patch.object(portfolio.ReportStore, "add_log") 2323 @mock.patch.object(market.ReportStore, "add_log")
2478 def test_log_order(self, add_log, print_log): 2324 def test_log_order(self, add_log, print_log):
2325 report_store = market.ReportStore(self.m)
2479 order_mock = mock.Mock() 2326 order_mock = mock.Mock()
2480 order_mock.as_json.return_value = "order" 2327 order_mock.as_json.return_value = "order"
2481 new_order_mock = mock.Mock() 2328 new_order_mock = mock.Mock()
@@ -2486,7 +2333,7 @@ class ReportStoreTest(WebMockTestCase):
2486 new_order_mock.__repr__.return_value = "New order Mock" 2333 new_order_mock.__repr__.return_value = "New order Mock"
2487 2334
2488 with self.subTest(finished=True): 2335 with self.subTest(finished=True):
2489 portfolio.ReportStore.log_order(order_mock, 1, finished=True) 2336 report_store.log_order(order_mock, 1, finished=True)
2490 print_log.assert_called_once_with("[Order] Finished Order Mock") 2337 print_log.assert_called_once_with("[Order] Finished Order Mock")
2491 add_log.assert_called_once_with({ 2338 add_log.assert_called_once_with({
2492 'type': 'order', 2339 'type': 'order',
@@ -2501,7 +2348,7 @@ class ReportStoreTest(WebMockTestCase):
2501 print_log.reset_mock() 2348 print_log.reset_mock()
2502 2349
2503 with self.subTest(update="waiting"): 2350 with self.subTest(update="waiting"):
2504 portfolio.ReportStore.log_order(order_mock, 1, update="waiting") 2351 report_store.log_order(order_mock, 1, update="waiting")
2505 print_log.assert_called_once_with("[Order] Order Mock, tick 1, waiting") 2352 print_log.assert_called_once_with("[Order] Order Mock, tick 1, waiting")
2506 add_log.assert_called_once_with({ 2353 add_log.assert_called_once_with({
2507 'type': 'order', 2354 'type': 'order',
@@ -2515,7 +2362,7 @@ class ReportStoreTest(WebMockTestCase):
2515 add_log.reset_mock() 2362 add_log.reset_mock()
2516 print_log.reset_mock() 2363 print_log.reset_mock()
2517 with self.subTest(update="adjusting"): 2364 with self.subTest(update="adjusting"):
2518 portfolio.ReportStore.log_order(order_mock, 3, 2365 report_store.log_order(order_mock, 3,
2519 update="adjusting", new_order=new_order_mock, 2366 update="adjusting", new_order=new_order_mock,
2520 compute_value="default") 2367 compute_value="default")
2521 print_log.assert_called_once_with("[Order] Order Mock, tick 3, cancelling and adjusting to New order Mock") 2368 print_log.assert_called_once_with("[Order] Order Mock, tick 3, cancelling and adjusting to New order Mock")
@@ -2531,7 +2378,7 @@ class ReportStoreTest(WebMockTestCase):
2531 add_log.reset_mock() 2378 add_log.reset_mock()
2532 print_log.reset_mock() 2379 print_log.reset_mock()
2533 with self.subTest(update="market_fallback"): 2380 with self.subTest(update="market_fallback"):
2534 portfolio.ReportStore.log_order(order_mock, 7, 2381 report_store.log_order(order_mock, 7,
2535 update="market_fallback", new_order=new_order_mock) 2382 update="market_fallback", new_order=new_order_mock)
2536 print_log.assert_called_once_with("[Order] Order Mock, tick 7, fallbacking to market value") 2383 print_log.assert_called_once_with("[Order] Order Mock, tick 7, fallbacking to market value")
2537 add_log.assert_called_once_with({ 2384 add_log.assert_called_once_with({
@@ -2546,7 +2393,7 @@ class ReportStoreTest(WebMockTestCase):
2546 add_log.reset_mock() 2393 add_log.reset_mock()
2547 print_log.reset_mock() 2394 print_log.reset_mock()
2548 with self.subTest(update="market_adjusting"): 2395 with self.subTest(update="market_adjusting"):
2549 portfolio.ReportStore.log_order(order_mock, 17, 2396 report_store.log_order(order_mock, 17,
2550 update="market_adjust", new_order=new_order_mock) 2397 update="market_adjust", new_order=new_order_mock)
2551 print_log.assert_called_once_with("[Order] Order Mock, tick 17, market value, cancelling and adjusting to New order Mock") 2398 print_log.assert_called_once_with("[Order] Order Mock, tick 17, market value, cancelling and adjusting to New order Mock")
2552 add_log.assert_called_once_with({ 2399 add_log.assert_called_once_with({
@@ -2558,9 +2405,10 @@ class ReportStoreTest(WebMockTestCase):
2558 'new_order': 'new_order' 2405 'new_order': 'new_order'
2559 }) 2406 })
2560 2407
2561 @mock.patch.object(portfolio.ReportStore, "print_log") 2408 @mock.patch.object(market.ReportStore, "print_log")
2562 @mock.patch.object(portfolio.ReportStore, "add_log") 2409 @mock.patch.object(market.ReportStore, "add_log")
2563 def test_log_move_balances(self, add_log, print_log): 2410 def test_log_move_balances(self, add_log, print_log):
2411 report_store = market.ReportStore(self.m)
2564 needed = { 2412 needed = {
2565 "BTC": portfolio.Amount("BTC", 10), 2413 "BTC": portfolio.Amount("BTC", 10),
2566 "USDT": 1 2414 "USDT": 1
@@ -2569,11 +2417,11 @@ class ReportStoreTest(WebMockTestCase):
2569 "BTC": portfolio.Amount("BTC", 3), 2417 "BTC": portfolio.Amount("BTC", 3),
2570 "USDT": -2 2418 "USDT": -2
2571 } 2419 }
2572 portfolio.ReportStore.log_move_balances(needed, moving, True) 2420 report_store.log_move_balances(needed, moving)
2573 print_log.assert_not_called() 2421 print_log.assert_not_called()
2574 add_log.assert_called_once_with({ 2422 add_log.assert_called_once_with({
2575 'type': 'move_balances', 2423 'type': 'move_balances',
2576 'debug': True, 2424 'debug': False,
2577 'needed': { 2425 'needed': {
2578 'BTC': D('10'), 2426 'BTC': D('10'),
2579 'USDT': 1 2427 'USDT': 1
@@ -2584,14 +2432,15 @@ class ReportStoreTest(WebMockTestCase):
2584 } 2432 }
2585 }) 2433 })
2586 2434
2587 @mock.patch.object(portfolio.ReportStore, "print_log") 2435 @mock.patch.object(market.ReportStore, "print_log")
2588 @mock.patch.object(portfolio.ReportStore, "add_log") 2436 @mock.patch.object(market.ReportStore, "add_log")
2589 def test_log_http_request(self, add_log, print_log): 2437 def test_log_http_request(self, add_log, print_log):
2438 report_store = market.ReportStore(self.m)
2590 response = mock.Mock() 2439 response = mock.Mock()
2591 response.status_code = 200 2440 response.status_code = 200
2592 response.text = "Hey" 2441 response.text = "Hey"
2593 2442
2594 portfolio.ReportStore.log_http_request("method", "url", "body", 2443 report_store.log_http_request("method", "url", "body",
2595 "headers", response) 2444 "headers", response)
2596 print_log.assert_not_called() 2445 print_log.assert_not_called()
2597 add_log.assert_called_once_with({ 2446 add_log.assert_called_once_with({
@@ -2604,11 +2453,12 @@ class ReportStoreTest(WebMockTestCase):
2604 'response': 'Hey' 2453 'response': 'Hey'
2605 }) 2454 })
2606 2455
2607 @mock.patch.object(portfolio.ReportStore, "print_log") 2456 @mock.patch.object(market.ReportStore, "print_log")
2608 @mock.patch.object(portfolio.ReportStore, "add_log") 2457 @mock.patch.object(market.ReportStore, "add_log")
2609 def test_log_error(self, add_log, print_log): 2458 def test_log_error(self, add_log, print_log):
2459 report_store = market.ReportStore(self.m)
2610 with self.subTest(message=None, exception=None): 2460 with self.subTest(message=None, exception=None):
2611 portfolio.ReportStore.log_error("action") 2461 report_store.log_error("action")
2612 print_log.assert_called_once_with("[Error] action") 2462 print_log.assert_called_once_with("[Error] action")
2613 add_log.assert_called_once_with({ 2463 add_log.assert_called_once_with({
2614 'type': 'error', 2464 'type': 'error',
@@ -2621,7 +2471,7 @@ class ReportStoreTest(WebMockTestCase):
2621 print_log.reset_mock() 2471 print_log.reset_mock()
2622 add_log.reset_mock() 2472 add_log.reset_mock()
2623 with self.subTest(message="Hey", exception=None): 2473 with self.subTest(message="Hey", exception=None):
2624 portfolio.ReportStore.log_error("action", message="Hey") 2474 report_store.log_error("action", message="Hey")
2625 print_log.assert_has_calls([ 2475 print_log.assert_has_calls([
2626 mock.call("[Error] action"), 2476 mock.call("[Error] action"),
2627 mock.call("\tHey") 2477 mock.call("\tHey")
@@ -2637,7 +2487,7 @@ class ReportStoreTest(WebMockTestCase):
2637 print_log.reset_mock() 2487 print_log.reset_mock()
2638 add_log.reset_mock() 2488 add_log.reset_mock()
2639 with self.subTest(message=None, exception=Exception("bouh")): 2489 with self.subTest(message=None, exception=Exception("bouh")):
2640 portfolio.ReportStore.log_error("action", exception=Exception("bouh")) 2490 report_store.log_error("action", exception=Exception("bouh"))
2641 print_log.assert_has_calls([ 2491 print_log.assert_has_calls([
2642 mock.call("[Error] action"), 2492 mock.call("[Error] action"),
2643 mock.call("\tException: bouh") 2493 mock.call("\tException: bouh")
@@ -2653,7 +2503,7 @@ class ReportStoreTest(WebMockTestCase):
2653 print_log.reset_mock() 2503 print_log.reset_mock()
2654 add_log.reset_mock() 2504 add_log.reset_mock()
2655 with self.subTest(message="Hey", exception=Exception("bouh")): 2505 with self.subTest(message="Hey", exception=Exception("bouh")):
2656 portfolio.ReportStore.log_error("action", message="Hey", exception=Exception("bouh")) 2506 report_store.log_error("action", message="Hey", exception=Exception("bouh"))
2657 print_log.assert_has_calls([ 2507 print_log.assert_has_calls([
2658 mock.call("[Error] action"), 2508 mock.call("[Error] action"),
2659 mock.call("\tException: bouh"), 2509 mock.call("\tException: bouh"),
@@ -2667,10 +2517,11 @@ class ReportStoreTest(WebMockTestCase):
2667 'message': "Hey" 2517 'message': "Hey"
2668 }) 2518 })
2669 2519
2670 @mock.patch.object(portfolio.ReportStore, "print_log") 2520 @mock.patch.object(market.ReportStore, "print_log")
2671 @mock.patch.object(portfolio.ReportStore, "add_log") 2521 @mock.patch.object(market.ReportStore, "add_log")
2672 def test_log_debug_action(self, add_log, print_log): 2522 def test_log_debug_action(self, add_log, print_log):
2673 portfolio.ReportStore.log_debug_action("Hey") 2523 report_store = market.ReportStore(self.m)
2524 report_store.log_debug_action("Hey")
2674 2525
2675 print_log.assert_called_once_with("[Debug] Hey") 2526 print_log.assert_called_once_with("[Debug] Hey")
2676 add_log.assert_called_once_with({ 2527 add_log.assert_called_once_with({
@@ -2678,12 +2529,263 @@ class ReportStoreTest(WebMockTestCase):
2678 'action': 'Hey' 2529 'action': 'Hey'
2679 }) 2530 })
2680 2531
2532@unittest.skipUnless("unit" in limits, "Unit skipped")
2533class HelperTest(WebMockTestCase):
2534 def test_main_store_report(self):
2535 file_open = mock.mock_open()
2536 with self.subTest(file=None), mock.patch("__main__.open", file_open):
2537 helper.main_store_report(None, 1, self.m)
2538 file_open.assert_not_called()
2539
2540 file_open = mock.mock_open()
2541 with self.subTest(file="present"), mock.patch("helper.open", file_open),\
2542 mock.patch.object(helper, "datetime") as time_mock:
2543 time_mock.now.return_value = datetime.datetime(2018, 2, 25)
2544 self.m.report.to_json.return_value = "json_content"
2545
2546 helper.main_store_report("present", 1, self.m)
2547
2548 file_open.assert_any_call("present/2018-02-25T00:00:00_1.json", "w")
2549 file_open().write.assert_called_once_with("json_content")
2550 self.m.report.to_json.assert_called_once_with()
2551
2552 with self.subTest(file="error"),\
2553 mock.patch("helper.open") as file_open,\
2554 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
2555 file_open.side_effect = FileNotFoundError
2556
2557 helper.main_store_report("error", 1, self.m)
2558
2559 self.assertRegex(stdout_mock.getvalue(), "impossible to store report file: FileNotFoundError;")
2560
2561 @mock.patch("helper.process_sell_all__1_all_sell")
2562 @mock.patch("helper.process_sell_all__2_all_buy")
2563 @mock.patch("portfolio.Portfolio.wait_for_recent")
2564 def test_main_process_market(self, wait, buy, sell):
2565 with self.subTest(before=False, after=False):
2566 helper.main_process_market("user")
2567
2568 wait.assert_not_called()
2569 buy.assert_not_called()
2570 sell.assert_not_called()
2571
2572 buy.reset_mock()
2573 wait.reset_mock()
2574 sell.reset_mock()
2575 with self.subTest(before=True, after=False):
2576 helper.main_process_market("user", before=True)
2577
2578 wait.assert_not_called()
2579 buy.assert_not_called()
2580 sell.assert_called_once_with("user")
2581
2582 buy.reset_mock()
2583 wait.reset_mock()
2584 sell.reset_mock()
2585 with self.subTest(before=False, after=True):
2586 helper.main_process_market("user", after=True)
2587
2588 wait.assert_called_once_with("user")
2589 buy.assert_called_once_with("user")
2590 sell.assert_not_called()
2591
2592 buy.reset_mock()
2593 wait.reset_mock()
2594 sell.reset_mock()
2595 with self.subTest(before=True, after=True):
2596 helper.main_process_market("user", before=True, after=True)
2597
2598 wait.assert_called_once_with("user")
2599 buy.assert_called_once_with("user")
2600 sell.assert_called_once_with("user")
2601
2602 @mock.patch.object(helper, "psycopg2")
2603 def test_fetch_markets(self, psycopg2):
2604 connect_mock = mock.Mock()
2605 cursor_mock = mock.MagicMock()
2606 cursor_mock.__iter__.return_value = ["row_1", "row_2"]
2607
2608 connect_mock.cursor.return_value = cursor_mock
2609 psycopg2.connect.return_value = connect_mock
2610
2611 rows = list(helper.main_fetch_markets({"foo": "bar"}))
2612
2613 psycopg2.connect.assert_called_once_with(foo="bar")
2614 cursor_mock.execute.assert_called_once_with("SELECT config,user_id FROM market_configs")
2615
2616 self.assertEqual(["row_1", "row_2"], rows)
2617
2618 @mock.patch.object(helper.sys, "exit")
2619 def test_main_parse_args(self, exit):
2620 with self.subTest(config="config.ini"):
2621 args = helper.main_parse_args([])
2622 self.assertEqual("config.ini", args.config)
2623 self.assertFalse(args.before)
2624 self.assertFalse(args.after)
2625 self.assertFalse(args.debug)
2626
2627 args = helper.main_parse_args(["--before", "--after", "--debug"])
2628 self.assertTrue(args.before)
2629 self.assertTrue(args.after)
2630 self.assertTrue(args.debug)
2631
2632 exit.assert_not_called()
2633
2634 with self.subTest(config="inexistant"),\
2635 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
2636 args = helper.main_parse_args(["--config", "foo.bar"])
2637 exit.assert_called_once_with(1)
2638 self.assertEqual("no config file found, exiting\n", stdout_mock.getvalue())
2639
2640 @mock.patch.object(helper.sys, "exit")
2641 @mock.patch("helper.configparser")
2642 @mock.patch("helper.os")
2643 def test_main_parse_config(self, os, configparser, exit):
2644 with self.subTest(pg_config=True, report_path=None):
2645 config_mock = mock.MagicMock()
2646 configparser.ConfigParser.return_value = config_mock
2647 def config(element):
2648 return element == "postgresql"
2649
2650 config_mock.__contains__.side_effect = config
2651 config_mock.__getitem__.return_value = "pg_config"
2652
2653 result = helper.main_parse_config("configfile")
2654
2655 config_mock.read.assert_called_with("configfile")
2656
2657 self.assertEqual(["pg_config", None], result)
2658
2659 with self.subTest(pg_config=True, report_path="present"):
2660 config_mock = mock.MagicMock()
2661 configparser.ConfigParser.return_value = config_mock
2662
2663 config_mock.__contains__.return_value = True
2664 config_mock.__getitem__.side_effect = [
2665 {"report_path": "report_path"},
2666 {"report_path": "report_path"},
2667 "pg_config",
2668 ]
2669
2670 os.path.exists.return_value = False
2671 result = helper.main_parse_config("configfile")
2672
2673 config_mock.read.assert_called_with("configfile")
2674 self.assertEqual(["pg_config", "report_path"], result)
2675 os.path.exists.assert_called_once_with("report_path")
2676 os.makedirs.assert_called_once_with("report_path")
2677
2678 with self.subTest(pg_config=False),\
2679 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
2680 config_mock = mock.MagicMock()
2681 configparser.ConfigParser.return_value = config_mock
2682 result = helper.main_parse_config("configfile")
2683
2684 config_mock.read.assert_called_with("configfile")
2685 exit.assert_called_once_with(1)
2686 self.assertEqual("no configuration for postgresql in config file\n", stdout_mock.getvalue())
2687
2688
2689 def test_print_orders(self):
2690 helper.print_orders(self.m)
2691
2692 self.m.report.log_stage.assert_called_with("print_orders")
2693 self.m.balances.fetch_balances.assert_called_with(tag="print_orders")
2694 self.m.prepare_trades.assert_called_with(base_currency="BTC",
2695 compute_value="average")
2696 self.m.trades.prepare_orders.assert_called_with(compute_value="average")
2697
2698 def test_print_balances(self):
2699 self.m.balances.in_currency.return_value = {
2700 "BTC": portfolio.Amount("BTC", "0.65"),
2701 "ETH": portfolio.Amount("BTC", "0.3"),
2702 }
2703
2704 helper.print_balances(self.m)
2705
2706 self.m.balances.fetch_balances.assert_called_with()
2707 self.m.report.print_log.assert_has_calls([
2708 mock.call("total:"),
2709 mock.call(portfolio.Amount("BTC", "0.95")),
2710 ])
2711
2712 def test_process_sell_needed__1_sell(self):
2713 helper.process_sell_needed__1_sell(self.m)
2714
2715 self.m.balances.fetch_balances.assert_has_calls([
2716 mock.call(tag="process_sell_needed__1_sell_begin"),
2717 mock.call(tag="process_sell_needed__1_sell_end"),
2718 ])
2719 self.m.prepare_trades.assert_called_with(base_currency="BTC",
2720 liquidity="medium")
2721 self.m.trades.prepare_orders.assert_called_with(compute_value="average",
2722 only="dispose")
2723 self.m.trades.run_orders.assert_called()
2724 self.m.follow_orders.assert_called()
2725 self.m.report.log_stage.assert_has_calls([
2726 mock.call("process_sell_needed__1_sell_begin"),
2727 mock.call("process_sell_needed__1_sell_end")
2728 ])
2729
2730 def test_process_sell_needed__2_buy(self):
2731 helper.process_sell_needed__2_buy(self.m)
2732
2733 self.m.balances.fetch_balances.assert_has_calls([
2734 mock.call(tag="process_sell_needed__2_buy_begin"),
2735 mock.call(tag="process_sell_needed__2_buy_end"),
2736 ])
2737 self.m.update_trades.assert_called_with(base_currency="BTC",
2738 liquidity="medium", only="acquire")
2739 self.m.trades.prepare_orders.assert_called_with(compute_value="average",
2740 only="acquire")
2741 self.m.move_balances.assert_called_with()
2742 self.m.trades.run_orders.assert_called()
2743 self.m.follow_orders.assert_called()
2744 self.m.report.log_stage.assert_has_calls([
2745 mock.call("process_sell_needed__2_buy_begin"),
2746 mock.call("process_sell_needed__2_buy_end")
2747 ])
2748
2749 def test_process_sell_all__1_sell(self):
2750 helper.process_sell_all__1_all_sell(self.m)
2751
2752 self.m.balances.fetch_balances.assert_has_calls([
2753 mock.call(tag="process_sell_all__1_all_sell_begin"),
2754 mock.call(tag="process_sell_all__1_all_sell_end"),
2755 ])
2756 self.m.prepare_trades_to_sell_all.assert_called_with(base_currency="BTC")
2757 self.m.trades.prepare_orders.assert_called_with(compute_value="average")
2758 self.m.trades.run_orders.assert_called()
2759 self.m.follow_orders.assert_called()
2760 self.m.report.log_stage.assert_has_calls([
2761 mock.call("process_sell_all__1_all_sell_begin"),
2762 mock.call("process_sell_all__1_all_sell_end")
2763 ])
2764
2765 def test_process_sell_all__2_all_buy(self):
2766 helper.process_sell_all__2_all_buy(self.m)
2767
2768 self.m.balances.fetch_balances.assert_has_calls([
2769 mock.call(tag="process_sell_all__2_all_buy_begin"),
2770 mock.call(tag="process_sell_all__2_all_buy_end"),
2771 ])
2772 self.m.prepare_trades.assert_called_with(base_currency="BTC",
2773 liquidity="medium")
2774 self.m.trades.prepare_orders.assert_called_with(compute_value="average")
2775 self.m.move_balances.assert_called_with()
2776 self.m.trades.run_orders.assert_called()
2777 self.m.follow_orders.assert_called()
2778 self.m.report.log_stage.assert_has_calls([
2779 mock.call("process_sell_all__2_all_buy_begin"),
2780 mock.call("process_sell_all__2_all_buy_end")
2781 ])
2782
2681@unittest.skipUnless("acceptance" in limits, "Acceptance skipped") 2783@unittest.skipUnless("acceptance" in limits, "Acceptance skipped")
2682class AcceptanceTest(WebMockTestCase): 2784class AcceptanceTest(WebMockTestCase):
2683 @unittest.expectedFailure 2785 @unittest.expectedFailure
2684 def test_success_sell_only_necessary(self): 2786 def test_success_sell_only_necessary(self):
2685 # FIXME: catch stdout 2787 # FIXME: catch stdout
2686 portfolio.ReportStore.verbose_print = False 2788 self.m.report.verbose_print = False
2687 fetch_balance = { 2789 fetch_balance = {
2688 "ETH": { 2790 "ETH": {
2689 "exchange_free": D("1.0"), 2791 "exchange_free": D("1.0"),