aboutsummaryrefslogtreecommitdiff
path: root/test.py
diff options
context:
space:
mode:
Diffstat (limited to 'test.py')
-rw-r--r--test.py1257
1 files changed, 943 insertions, 314 deletions
diff --git a/test.py b/test.py
index 921af9f..ac9a6cd 100644
--- a/test.py
+++ b/test.py
@@ -7,7 +7,8 @@ from unittest import mock
7import requests 7import requests
8import requests_mock 8import requests_mock
9from io import StringIO 9from io import StringIO
10import portfolio, helper, market 10import threading
11import portfolio, market, main, store
11 12
12limits = ["acceptance", "unit"] 13limits = ["acceptance", "unit"]
13for test_type in limits: 14for test_type in limits:
@@ -32,7 +33,15 @@ class WebMockTestCase(unittest.TestCase):
32 self.m.debug = False 33 self.m.debug = False
33 34
34 self.patchers = [ 35 self.patchers = [
35 mock.patch.multiple(portfolio.Portfolio, last_date=None, data=None, liquidities={}), 36 mock.patch.multiple(market.Portfolio,
37 data=store.LockedVar(None),
38 liquidities=store.LockedVar({}),
39 last_date=store.LockedVar(None),
40 report=mock.Mock(),
41 worker=None,
42 worker_notify=None,
43 worker_started=False,
44 callback=None),
36 mock.patch.multiple(portfolio.Computation, 45 mock.patch.multiple(portfolio.Computation,
37 computations=portfolio.Computation.computations), 46 computations=portfolio.Computation.computations),
38 ] 47 ]
@@ -126,174 +135,632 @@ class poloniexETest(unittest.TestCase):
126 } 135 }
127 self.assertEqual(expected, self.s.margin_summary()) 136 self.assertEqual(expected, self.s.margin_summary())
128 137
138 def test_create_order(self):
139 with mock.patch.object(self.s, "create_exchange_order") as exchange,\
140 mock.patch.object(self.s, "create_margin_order") as margin:
141 with self.subTest(account="unspecified"):
142 self.s.create_order("symbol", "type", "side", "amount", price="price", lending_rate="lending_rate", params="params")
143 exchange.assert_called_once_with("symbol", "type", "side", "amount", price="price", params="params")
144 margin.assert_not_called()
145 exchange.reset_mock()
146 margin.reset_mock()
147
148 with self.subTest(account="exchange"):
149 self.s.create_order("symbol", "type", "side", "amount", account="exchange", price="price", lending_rate="lending_rate", params="params")
150 exchange.assert_called_once_with("symbol", "type", "side", "amount", price="price", params="params")
151 margin.assert_not_called()
152 exchange.reset_mock()
153 margin.reset_mock()
154
155 with self.subTest(account="margin"):
156 self.s.create_order("symbol", "type", "side", "amount", account="margin", price="price", lending_rate="lending_rate", params="params")
157 margin.assert_called_once_with("symbol", "type", "side", "amount", lending_rate="lending_rate", price="price", params="params")
158 exchange.assert_not_called()
159 exchange.reset_mock()
160 margin.reset_mock()
161
162 with self.subTest(account="unknown"), self.assertRaises(NotImplementedError):
163 self.s.create_order("symbol", "type", "side", "amount", account="unknown")
164
165 def test_parse_ticker(self):
166 ticker = {
167 "high24hr": "12",
168 "low24hr": "10",
169 "highestBid": "10.5",
170 "lowestAsk": "11.5",
171 "last": "11",
172 "percentChange": "0.1",
173 "quoteVolume": "10",
174 "baseVolume": "20"
175 }
176 market = {
177 "symbol": "BTC/ETC"
178 }
179 with mock.patch.object(self.s, "milliseconds") as ms:
180 ms.return_value = 1520292715123
181 result = self.s.parse_ticker(ticker, market)
182
183 expected = {
184 "symbol": "BTC/ETC",
185 "timestamp": 1520292715123,
186 "datetime": "2018-03-05T23:31:55.123Z",
187 "high": D("12"),
188 "low": D("10"),
189 "bid": D("10.5"),
190 "ask": D("11.5"),
191 "vwap": None,
192 "open": None,
193 "close": None,
194 "first": None,
195 "last": D("11"),
196 "change": D("0.1"),
197 "percentage": None,
198 "average": None,
199 "baseVolume": D("10"),
200 "quoteVolume": D("20"),
201 "info": ticker
202 }
203 self.assertEqual(expected, result)
204
205 def test_fetch_margin_balance(self):
206 with mock.patch.object(self.s, "privatePostGetMarginPosition") as get_margin_position:
207 get_margin_position.return_value = {
208 "BTC_DASH": {
209 "amount": "-0.1",
210 "basePrice": "0.06818560",
211 "lendingFees": "0.00000001",
212 "liquidationPrice": "0.15107132",
213 "pl": "-0.00000371",
214 "total": "0.00681856",
215 "type": "short"
216 },
217 "BTC_ETC": {
218 "amount": "-0.6",
219 "basePrice": "0.1",
220 "lendingFees": "0.00000001",
221 "liquidationPrice": "0.6",
222 "pl": "0.00000371",
223 "total": "0.06",
224 "type": "short"
225 },
226 "BTC_ETH": {
227 "amount": "0",
228 "basePrice": "0",
229 "lendingFees": "0",
230 "liquidationPrice": "-1",
231 "pl": "0",
232 "total": "0",
233 "type": "none"
234 }
235 }
236 balances = self.s.fetch_margin_balance()
237 self.assertEqual(2, len(balances))
238 expected = {
239 "DASH": {
240 "amount": D("-0.1"),
241 "borrowedPrice": D("0.06818560"),
242 "lendingFees": D("1E-8"),
243 "pl": D("-0.00000371"),
244 "liquidationPrice": D("0.15107132"),
245 "type": "short",
246 "total": D("0.00681856"),
247 "baseCurrency": "BTC"
248 },
249 "ETC": {
250 "amount": D("-0.6"),
251 "borrowedPrice": D("0.1"),
252 "lendingFees": D("1E-8"),
253 "pl": D("0.00000371"),
254 "liquidationPrice": D("0.6"),
255 "type": "short",
256 "total": D("0.06"),
257 "baseCurrency": "BTC"
258 }
259 }
260 self.assertEqual(expected, balances)
261
262 def test_sum(self):
263 self.assertEqual(D("1.1"), self.s.sum(D("1"), D("0.1")))
264
265 def test_fetch_balance(self):
266 with mock.patch.object(self.s, "load_markets") as load_markets,\
267 mock.patch.object(self.s, "privatePostReturnCompleteBalances") as balances,\
268 mock.patch.object(self.s, "common_currency_code") as ccc:
269 ccc.side_effect = ["ETH", "BTC", "DASH"]
270 balances.return_value = {
271 "ETH": {
272 "available": "10",
273 "onOrders": "1",
274 },
275 "BTC": {
276 "available": "1",
277 "onOrders": "0",
278 },
279 "DASH": {
280 "available": "0",
281 "onOrders": "3"
282 }
283 }
284
285 expected = {
286 "info": {
287 "ETH": {"available": "10", "onOrders": "1"},
288 "BTC": {"available": "1", "onOrders": "0"},
289 "DASH": {"available": "0", "onOrders": "3"}
290 },
291 "ETH": {"free": D("10"), "used": D("1"), "total": D("11")},
292 "BTC": {"free": D("1"), "used": D("0"), "total": D("1")},
293 "DASH": {"free": D("0"), "used": D("3"), "total": D("3")},
294 "free": {"ETH": D("10"), "BTC": D("1"), "DASH": D("0")},
295 "used": {"ETH": D("1"), "BTC": D("0"), "DASH": D("3")},
296 "total": {"ETH": D("11"), "BTC": D("1"), "DASH": D("3")}
297 }
298 result = self.s.fetch_balance()
299 load_markets.assert_called_once()
300 self.assertEqual(expected, result)
301
302 def test_fetch_balance_per_type(self):
303 with mock.patch.object(self.s, "privatePostReturnAvailableAccountBalances") as balances:
304 balances.return_value = {
305 "exchange": {
306 "BLK": "159.83673869",
307 "BTC": "0.00005959",
308 "USDT": "0.00002625",
309 "XMR": "0.18719303"
310 },
311 "margin": {
312 "BTC": "0.03019227"
313 }
314 }
315 expected = {
316 "info": {
317 "exchange": {
318 "BLK": "159.83673869",
319 "BTC": "0.00005959",
320 "USDT": "0.00002625",
321 "XMR": "0.18719303"
322 },
323 "margin": {
324 "BTC": "0.03019227"
325 }
326 },
327 "exchange": {
328 "BLK": D("159.83673869"),
329 "BTC": D("0.00005959"),
330 "USDT": D("0.00002625"),
331 "XMR": D("0.18719303")
332 },
333 "margin": {"BTC": D("0.03019227")},
334 "BLK": {"exchange": D("159.83673869")},
335 "BTC": {"exchange": D("0.00005959"), "margin": D("0.03019227")},
336 "USDT": {"exchange": D("0.00002625")},
337 "XMR": {"exchange": D("0.18719303")}
338 }
339 result = self.s.fetch_balance_per_type()
340 self.assertEqual(expected, result)
341
342 def test_fetch_all_balances(self):
343 import json
344 with mock.patch.object(self.s, "load_markets") as load_markets,\
345 mock.patch.object(self.s, "privatePostGetMarginPosition") as margin_balance,\
346 mock.patch.object(self.s, "privatePostReturnCompleteBalances") as balance,\
347 mock.patch.object(self.s, "privatePostReturnAvailableAccountBalances") as balance_per_type:
348
349 with open("test_samples/poloniexETest.test_fetch_all_balances.1.json") as f:
350 balance.return_value = json.load(f)
351 with open("test_samples/poloniexETest.test_fetch_all_balances.2.json") as f:
352 margin_balance.return_value = json.load(f)
353 with open("test_samples/poloniexETest.test_fetch_all_balances.3.json") as f:
354 balance_per_type.return_value = json.load(f)
355
356 result = self.s.fetch_all_balances()
357 expected_doge = {
358 "total": D("-12779.79821852"),
359 "exchange_used": D("0E-8"),
360 "exchange_total": D("0E-8"),
361 "exchange_free": D("0E-8"),
362 "margin_available": 0,
363 "margin_in_position": 0,
364 "margin_borrowed": D("12779.79821852"),
365 "margin_total": D("-12779.79821852"),
366 "margin_pending_gain": 0,
367 "margin_lending_fees": D("-9E-8"),
368 "margin_pending_base_gain": D("0.00024059"),
369 "margin_position_type": "short",
370 "margin_liquidation_price": D("0.00000246"),
371 "margin_borrowed_base_price": D("0.00599149"),
372 "margin_borrowed_base_currency": "BTC"
373 }
374 expected_btc = {"total": D("0.05432165"),
375 "exchange_used": D("0E-8"),
376 "exchange_total": D("0.00005959"),
377 "exchange_free": D("0.00005959"),
378 "margin_available": D("0.03019227"),
379 "margin_in_position": D("0.02406979"),
380 "margin_borrowed": 0,
381 "margin_total": D("0.05426206"),
382 "margin_pending_gain": D("0.00093955"),
383 "margin_lending_fees": 0,
384 "margin_pending_base_gain": 0,
385 "margin_position_type": None,
386 "margin_liquidation_price": 0,
387 "margin_borrowed_base_price": 0,
388 "margin_borrowed_base_currency": None
389 }
390 expected_xmr = {"total": D("0.18719303"),
391 "exchange_used": D("0E-8"),
392 "exchange_total": D("0.18719303"),
393 "exchange_free": D("0.18719303"),
394 "margin_available": 0,
395 "margin_in_position": 0,
396 "margin_borrowed": 0,
397 "margin_total": 0,
398 "margin_pending_gain": 0,
399 "margin_lending_fees": 0,
400 "margin_pending_base_gain": 0,
401 "margin_position_type": None,
402 "margin_liquidation_price": 0,
403 "margin_borrowed_base_price": 0,
404 "margin_borrowed_base_currency": None
405 }
406 self.assertEqual(expected_xmr, result["XMR"])
407 self.assertEqual(expected_doge, result["DOGE"])
408 self.assertEqual(expected_btc, result["BTC"])
409
410 def test_create_margin_order(self):
411 with self.assertRaises(market.ExchangeError):
412 self.s.create_margin_order("FOO", "market", "buy", "10")
413
414 with mock.patch.object(self.s, "load_markets") as load_markets,\
415 mock.patch.object(self.s, "privatePostMarginBuy") as margin_buy,\
416 mock.patch.object(self.s, "privatePostMarginSell") as margin_sell,\
417 mock.patch.object(self.s, "market") as market_mock,\
418 mock.patch.object(self.s, "price_to_precision") as ptp,\
419 mock.patch.object(self.s, "amount_to_precision") as atp:
420
421 margin_buy.return_value = {
422 "orderNumber": 123
423 }
424 margin_sell.return_value = {
425 "orderNumber": 456
426 }
427 market_mock.return_value = { "id": "BTC_ETC", "symbol": "BTC_ETC" }
428 ptp.return_value = D("0.1")
429 atp.return_value = D("12")
430
431 order = self.s.create_margin_order("BTC_ETC", "margin", "buy", "12", price="0.1")
432 self.assertEqual(123, order["id"])
433 margin_buy.assert_called_once_with({"currencyPair": "BTC_ETC", "rate": D("0.1"), "amount": D("12")})
434 margin_sell.assert_not_called()
435 margin_buy.reset_mock()
436 margin_sell.reset_mock()
437
438 order = self.s.create_margin_order("BTC_ETC", "margin", "sell", "12", lending_rate="0.01", price="0.1")
439 self.assertEqual(456, order["id"])
440 margin_sell.assert_called_once_with({"currencyPair": "BTC_ETC", "rate": D("0.1"), "amount": D("12"), "lendingRate": "0.01"})
441 margin_buy.assert_not_called()
442
443 def test_create_exchange_order(self):
444 with mock.patch.object(market.ccxt.poloniex, "create_order") as create_order:
445 self.s.create_order("symbol", "type", "side", "amount", price="price", params="params")
446
447 create_order.assert_called_once_with("symbol", "type", "side", "amount", price="price", params="params")
448
129@unittest.skipUnless("unit" in limits, "Unit skipped") 449@unittest.skipUnless("unit" in limits, "Unit skipped")
130class PortfolioTest(WebMockTestCase): 450class NoopLockTest(unittest.TestCase):
131 def fill_data(self): 451 def test_with(self):
132 if self.json_response is not None: 452 noop_lock = store.NoopLock()
133 portfolio.Portfolio.data = self.json_response 453 with noop_lock:
454 self.assertTrue(True)
455
456@unittest.skipUnless("unit" in limits, "Unit skipped")
457class LockedVar(unittest.TestCase):
458
459 def test_values(self):
460 locked_var = store.LockedVar("Foo")
461 self.assertIsInstance(locked_var.lock, store.NoopLock)
462 self.assertEqual("Foo", locked_var.val)
463
464 def test_get(self):
465 with self.subTest(desc="Normal case"):
466 locked_var = store.LockedVar("Foo")
467 self.assertEqual("Foo", locked_var.get())
468 with self.subTest(desc="Dict"):
469 locked_var = store.LockedVar({"foo": "bar"})
470 self.assertEqual({"foo": "bar"}, locked_var.get())
471 self.assertEqual("bar", locked_var.get("foo"))
472 self.assertIsNone(locked_var.get("other"))
473
474 def test_set(self):
475 locked_var = store.LockedVar("Foo")
476 locked_var.set("Bar")
477 self.assertEqual("Bar", locked_var.get())
478
479 def test__getattr(self):
480 dummy = type('Dummy', (object,), {})()
481 dummy.attribute = "Hey"
482
483 locked_var = store.LockedVar(dummy)
484 self.assertEqual("Hey", locked_var.attribute)
485 with self.assertRaises(AttributeError):
486 locked_var.other
487
488 def test_start_lock(self):
489 locked_var = store.LockedVar("Foo")
490 locked_var.start_lock()
491 self.assertEqual("lock", locked_var.lock.__class__.__name__)
492
493 thread1 = threading.Thread(target=locked_var.set, args=["Bar1"])
494 thread2 = threading.Thread(target=locked_var.set, args=["Bar2"])
495 thread3 = threading.Thread(target=locked_var.set, args=["Bar3"])
496
497 with locked_var.lock:
498 thread1.start()
499 thread2.start()
500 thread3.start()
501
502 self.assertEqual("Foo", locked_var.val)
503 thread1.join()
504 thread2.join()
505 thread3.join()
506 self.assertEqual("Bar", locked_var.get()[0:3])
507
508 def test_wait_for_notification(self):
509 with self.assertRaises(RuntimeError):
510 store.Portfolio.wait_for_notification()
511
512 with mock.patch.object(store.Portfolio, "get_cryptoportfolio") as get,\
513 mock.patch.object(store.Portfolio, "report") as report,\
514 mock.patch.object(store.time, "sleep") as sleep:
515 store.Portfolio.start_worker(poll=3)
516
517 store.Portfolio.worker_notify.set()
518
519 store.Portfolio.callback.wait()
520
521 report.print_log.assert_called_once_with("Fetching cryptoportfolio")
522 get.assert_called_once_with(refetch=True)
523 sleep.assert_called_once_with(3)
524 self.assertFalse(store.Portfolio.worker_notify.is_set())
525 self.assertTrue(store.Portfolio.worker.is_alive())
526
527 store.Portfolio.callback.clear()
528 store.Portfolio.worker_started = False
529 store.Portfolio.worker_notify.set()
530 store.Portfolio.callback.wait()
531
532 self.assertFalse(store.Portfolio.worker.is_alive())
533
534 def test_notify_and_wait(self):
535 with mock.patch.object(store.Portfolio, "callback") as callback,\
536 mock.patch.object(store.Portfolio, "worker_notify") as worker_notify:
537 store.Portfolio.notify_and_wait()
538 callback.clear.assert_called_once_with()
539 worker_notify.set.assert_called_once_with()
540 callback.wait.assert_called_once_with()
134 541
542@unittest.skipUnless("unit" in limits, "Unit skipped")
543class PortfolioTest(WebMockTestCase):
135 def setUp(self): 544 def setUp(self):
136 super(PortfolioTest, self).setUp() 545 super(PortfolioTest, self).setUp()
137 546
138 with open("test_portfolio.json") as example: 547 with open("test_samples/test_portfolio.json") as example:
139 self.json_response = example.read() 548 self.json_response = example.read()
140 549
141 self.wm.get(portfolio.Portfolio.URL, text=self.json_response) 550 self.wm.get(market.Portfolio.URL, text=self.json_response)
142 551
143 def test_get_cryptoportfolio(self): 552 @mock.patch.object(market.Portfolio, "parse_cryptoportfolio")
144 self.wm.get(portfolio.Portfolio.URL, [ 553 def test_get_cryptoportfolio(self, parse_cryptoportfolio):
145 {"text":'{ "foo": "bar" }', "status_code": 200}, 554 with self.subTest(parallel=False):
146 {"text": "System Error", "status_code": 500}, 555 self.wm.get(market.Portfolio.URL, [
147 {"exc": requests.exceptions.ConnectTimeout}, 556 {"text":'{ "foo": "bar" }', "status_code": 200},
148 ]) 557 {"text": "System Error", "status_code": 500},
149 portfolio.Portfolio.get_cryptoportfolio(self.m) 558 {"exc": requests.exceptions.ConnectTimeout},
150 self.assertIn("foo", portfolio.Portfolio.data) 559 ])
151 self.assertEqual("bar", portfolio.Portfolio.data["foo"]) 560 market.Portfolio.get_cryptoportfolio()
152 self.assertTrue(self.wm.called) 561 self.assertIn("foo", market.Portfolio.data.get())
153 self.assertEqual(1, self.wm.call_count) 562 self.assertEqual("bar", market.Portfolio.data.get()["foo"])
154 self.m.report.log_error.assert_not_called() 563 self.assertTrue(self.wm.called)
155 self.m.report.log_http_request.assert_called_once() 564 self.assertEqual(1, self.wm.call_count)
156 self.m.report.log_http_request.reset_mock() 565 market.Portfolio.report.log_error.assert_not_called()
157 566 market.Portfolio.report.log_http_request.assert_called_once()
158 portfolio.Portfolio.get_cryptoportfolio(self.m) 567 parse_cryptoportfolio.assert_called_once_with()
159 self.assertIsNone(portfolio.Portfolio.data) 568 market.Portfolio.report.log_http_request.reset_mock()
160 self.assertEqual(2, self.wm.call_count) 569 parse_cryptoportfolio.reset_mock()
161 self.m.report.log_error.assert_not_called() 570 market.Portfolio.data = store.LockedVar(None)
162 self.m.report.log_http_request.assert_called_once() 571
163 self.m.report.log_http_request.reset_mock() 572 market.Portfolio.get_cryptoportfolio()
164 573 self.assertIsNone(market.Portfolio.data.get())
165 574 self.assertEqual(2, self.wm.call_count)
166 portfolio.Portfolio.data = "Foo" 575 parse_cryptoportfolio.assert_not_called()
167 portfolio.Portfolio.get_cryptoportfolio(self.m) 576 market.Portfolio.report.log_error.assert_not_called()
168 self.assertEqual("Foo", portfolio.Portfolio.data) 577 market.Portfolio.report.log_http_request.assert_called_once()
169 self.assertEqual(3, self.wm.call_count) 578 market.Portfolio.report.log_http_request.reset_mock()
170 self.m.report.log_error.assert_called_once_with("get_cryptoportfolio", 579 parse_cryptoportfolio.reset_mock()
171 exception=mock.ANY) 580
172 self.m.report.log_http_request.assert_not_called() 581 market.Portfolio.data = store.LockedVar("Foo")
582 market.Portfolio.get_cryptoportfolio()
583 self.assertEqual(2, self.wm.call_count)
584 parse_cryptoportfolio.assert_not_called()
585
586 market.Portfolio.get_cryptoportfolio(refetch=True)
587 self.assertEqual("Foo", market.Portfolio.data.get())
588 self.assertEqual(3, self.wm.call_count)
589 market.Portfolio.report.log_error.assert_called_once_with("get_cryptoportfolio",
590 exception=mock.ANY)
591 market.Portfolio.report.log_http_request.assert_not_called()
592 with self.subTest(parallel=True):
593 with mock.patch.object(market.Portfolio, "is_worker_thread") as is_worker,\
594 mock.patch.object(market.Portfolio, "notify_and_wait") as notify:
595 with self.subTest(worker=True):
596 market.Portfolio.data = store.LockedVar(None)
597 market.Portfolio.worker = mock.Mock()
598 is_worker.return_value = True
599 self.wm.get(market.Portfolio.URL, [
600 {"text":'{ "foo": "bar" }', "status_code": 200},
601 ])
602 market.Portfolio.get_cryptoportfolio()
603 self.assertIn("foo", market.Portfolio.data.get())
604 parse_cryptoportfolio.reset_mock()
605 with self.subTest(worker=False):
606 market.Portfolio.data = store.LockedVar(None)
607 market.Portfolio.worker = mock.Mock()
608 is_worker.return_value = False
609 market.Portfolio.get_cryptoportfolio()
610 notify.assert_called_once_with()
611 parse_cryptoportfolio.assert_not_called()
173 612
174 def test_parse_cryptoportfolio(self): 613 def test_parse_cryptoportfolio(self):
175 portfolio.Portfolio.parse_cryptoportfolio(self.m) 614 with self.subTest(description="Normal case"):
176 615 market.Portfolio.data = store.LockedVar(store.json.loads(
177 self.assertListEqual( 616 self.json_response, parse_int=D, parse_float=D))
178 ["medium", "high"], 617 market.Portfolio.parse_cryptoportfolio()
179 list(portfolio.Portfolio.liquidities.keys()))
180
181 liquidities = portfolio.Portfolio.liquidities
182 self.assertEqual(10, len(liquidities["medium"].keys()))
183 self.assertEqual(10, len(liquidities["high"].keys()))
184
185 expected = {
186 'BTC': (D("0.2857"), "long"),
187 'DGB': (D("0.1015"), "long"),
188 'DOGE': (D("0.1805"), "long"),
189 'SC': (D("0.0623"), "long"),
190 'ZEC': (D("0.3701"), "long"),
191 }
192 date = portfolio.datetime(2018, 1, 8)
193 self.assertDictEqual(expected, liquidities["high"][date])
194
195 expected = {
196 'BTC': (D("1.1102e-16"), "long"),
197 'ETC': (D("0.1"), "long"),
198 'FCT': (D("0.1"), "long"),
199 'GAS': (D("0.1"), "long"),
200 'NAV': (D("0.1"), "long"),
201 'OMG': (D("0.1"), "long"),
202 'OMNI': (D("0.1"), "long"),
203 'PPC': (D("0.1"), "long"),
204 'RIC': (D("0.1"), "long"),
205 'VIA': (D("0.1"), "long"),
206 'XCP': (D("0.1"), "long"),
207 }
208 self.assertDictEqual(expected, liquidities["medium"][date])
209 self.assertEqual(portfolio.datetime(2018, 1, 15), portfolio.Portfolio.last_date)
210
211 self.m.report.log_http_request.assert_called_once_with("GET",
212 portfolio.Portfolio.URL, None, mock.ANY, mock.ANY)
213 self.m.report.log_http_request.reset_mock()
214
215 # It doesn't refetch the data when available
216 portfolio.Portfolio.parse_cryptoportfolio(self.m)
217 self.m.report.log_http_request.assert_not_called()
218
219 self.assertEqual(1, self.wm.call_count)
220
221 portfolio.Portfolio.parse_cryptoportfolio(self.m, refetch=True)
222 self.assertEqual(2, self.wm.call_count)
223 self.m.report.log_http_request.assert_called_once()
224
225 def test_repartition(self):
226 expected_medium = {
227 'BTC': (D("1.1102e-16"), "long"),
228 'USDT': (D("0.1"), "long"),
229 'ETC': (D("0.1"), "long"),
230 'FCT': (D("0.1"), "long"),
231 'OMG': (D("0.1"), "long"),
232 'STEEM': (D("0.1"), "long"),
233 'STRAT': (D("0.1"), "long"),
234 'XEM': (D("0.1"), "long"),
235 'XMR': (D("0.1"), "long"),
236 'XVC': (D("0.1"), "long"),
237 'ZRX': (D("0.1"), "long"),
238 }
239 expected_high = {
240 'USDT': (D("0.1226"), "long"),
241 'BTC': (D("0.1429"), "long"),
242 'ETC': (D("0.1127"), "long"),
243 'ETH': (D("0.1569"), "long"),
244 'FCT': (D("0.3341"), "long"),
245 'GAS': (D("0.1308"), "long"),
246 }
247 618
248 self.assertEqual(expected_medium, portfolio.Portfolio.repartition(self.m)) 619 self.assertListEqual(
249 self.assertEqual(expected_medium, portfolio.Portfolio.repartition(self.m, liquidity="medium")) 620 ["medium", "high"],
250 self.assertEqual(expected_high, portfolio.Portfolio.repartition(self.m, liquidity="high")) 621 list(market.Portfolio.liquidities.get().keys()))
251 622
252 self.assertEqual(1, self.wm.call_count) 623 liquidities = market.Portfolio.liquidities.get()
624 self.assertEqual(10, len(liquidities["medium"].keys()))
625 self.assertEqual(10, len(liquidities["high"].keys()))
253 626
254 portfolio.Portfolio.repartition(self.m) 627 expected = {
255 self.assertEqual(1, self.wm.call_count) 628 'BTC': (D("0.2857"), "long"),
629 'DGB': (D("0.1015"), "long"),
630 'DOGE': (D("0.1805"), "long"),
631 'SC': (D("0.0623"), "long"),
632 'ZEC': (D("0.3701"), "long"),
633 }
634 date = portfolio.datetime(2018, 1, 8)
635 self.assertDictEqual(expected, liquidities["high"][date])
256 636
257 portfolio.Portfolio.repartition(self.m, refetch=True) 637 expected = {
258 self.assertEqual(2, self.wm.call_count) 638 'BTC': (D("1.1102e-16"), "long"),
259 self.m.report.log_http_request.assert_called() 639 'ETC': (D("0.1"), "long"),
260 self.assertEqual(2, self.m.report.log_http_request.call_count) 640 'FCT': (D("0.1"), "long"),
641 'GAS': (D("0.1"), "long"),
642 'NAV': (D("0.1"), "long"),
643 'OMG': (D("0.1"), "long"),
644 'OMNI': (D("0.1"), "long"),
645 'PPC': (D("0.1"), "long"),
646 'RIC': (D("0.1"), "long"),
647 'VIA': (D("0.1"), "long"),
648 'XCP': (D("0.1"), "long"),
649 }
650 self.assertDictEqual(expected, liquidities["medium"][date])
651 self.assertEqual(portfolio.datetime(2018, 1, 15), market.Portfolio.last_date.get())
652
653 with self.subTest(description="Missing weight"):
654 data = store.json.loads(self.json_response, parse_int=D, parse_float=D)
655 del(data["portfolio_2"]["weights"])
656 market.Portfolio.data = store.LockedVar(data)
657
658 market.Portfolio.parse_cryptoportfolio()
659 self.assertListEqual(
660 ["medium", "high"],
661 list(market.Portfolio.liquidities.get().keys()))
662 self.assertEqual({}, market.Portfolio.liquidities.get("medium"))
663
664 with self.subTest(description="All missing weights"):
665 data = store.json.loads(self.json_response, parse_int=D, parse_float=D)
666 del(data["portfolio_1"]["weights"])
667 del(data["portfolio_2"]["weights"])
668 market.Portfolio.data = store.LockedVar(data)
669
670 market.Portfolio.parse_cryptoportfolio()
671 self.assertEqual({}, market.Portfolio.liquidities.get("medium"))
672 self.assertEqual({}, market.Portfolio.liquidities.get("high"))
673 self.assertEqual(datetime.datetime(1,1,1), market.Portfolio.last_date.get())
674
675
676 @mock.patch.object(market.Portfolio, "get_cryptoportfolio")
677 def test_repartition(self, get_cryptoportfolio):
678 market.Portfolio.liquidities = store.LockedVar({
679 "medium": {
680 "2018-03-01": "medium_2018-03-01",
681 "2018-03-08": "medium_2018-03-08",
682 },
683 "high": {
684 "2018-03-01": "high_2018-03-01",
685 "2018-03-08": "high_2018-03-08",
686 }
687 })
688 market.Portfolio.last_date = store.LockedVar("2018-03-08")
261 689
262 @mock.patch.object(portfolio.time, "sleep") 690 self.assertEqual("medium_2018-03-08", market.Portfolio.repartition())
263 @mock.patch.object(portfolio.Portfolio, "repartition") 691 get_cryptoportfolio.assert_called_once_with()
264 def test_wait_for_recent(self, repartition, sleep): 692 self.assertEqual("medium_2018-03-08", market.Portfolio.repartition(liquidity="medium"))
693 self.assertEqual("high_2018-03-08", market.Portfolio.repartition(liquidity="high"))
694
695 @mock.patch.object(market.time, "sleep")
696 @mock.patch.object(market.Portfolio, "get_cryptoportfolio")
697 def test_wait_for_recent(self, get_cryptoportfolio, sleep):
265 self.call_count = 0 698 self.call_count = 0
266 def _repartition(market, refetch): 699 def _get(refetch=False):
267 self.assertEqual(self.m, market) 700 if self.call_count != 0:
268 self.assertTrue(refetch) 701 self.assertTrue(refetch)
702 else:
703 self.assertFalse(refetch)
269 self.call_count += 1 704 self.call_count += 1
270 portfolio.Portfolio.last_date = portfolio.datetime.now()\ 705 market.Portfolio.last_date = store.LockedVar(store.datetime.now()\
271 - portfolio.timedelta(10)\ 706 - store.timedelta(10)\
272 + portfolio.timedelta(self.call_count) 707 + store.timedelta(self.call_count))
273 repartition.side_effect = _repartition 708 get_cryptoportfolio.side_effect = _get
274 709
275 portfolio.Portfolio.wait_for_recent(self.m) 710 market.Portfolio.wait_for_recent()
276 sleep.assert_called_with(30) 711 sleep.assert_called_with(30)
277 self.assertEqual(6, sleep.call_count) 712 self.assertEqual(6, sleep.call_count)
278 self.assertEqual(7, repartition.call_count) 713 self.assertEqual(7, get_cryptoportfolio.call_count)
279 self.m.report.print_log.assert_called_with("Attempt to fetch up-to-date cryptoportfolio") 714 market.Portfolio.report.print_log.assert_called_with("Attempt to fetch up-to-date cryptoportfolio")
280 715
281 sleep.reset_mock() 716 sleep.reset_mock()
282 repartition.reset_mock() 717 get_cryptoportfolio.reset_mock()
283 portfolio.Portfolio.last_date = None 718 market.Portfolio.last_date = store.LockedVar(None)
284 self.call_count = 0 719 self.call_count = 0
285 portfolio.Portfolio.wait_for_recent(self.m, delta=15) 720 market.Portfolio.wait_for_recent(delta=15)
286 sleep.assert_not_called() 721 sleep.assert_not_called()
287 self.assertEqual(1, repartition.call_count) 722 self.assertEqual(1, get_cryptoportfolio.call_count)
288 723
289 sleep.reset_mock() 724 sleep.reset_mock()
290 repartition.reset_mock() 725 get_cryptoportfolio.reset_mock()
291 portfolio.Portfolio.last_date = None 726 market.Portfolio.last_date = store.LockedVar(None)
292 self.call_count = 0 727 self.call_count = 0
293 portfolio.Portfolio.wait_for_recent(self.m, delta=1) 728 market.Portfolio.wait_for_recent(delta=1)
294 sleep.assert_called_with(30) 729 sleep.assert_called_with(30)
295 self.assertEqual(9, sleep.call_count) 730 self.assertEqual(9, sleep.call_count)
296 self.assertEqual(10, repartition.call_count) 731 self.assertEqual(10, get_cryptoportfolio.call_count)
732
733 def test_is_worker_thread(self):
734 with self.subTest(worker=None):
735 self.assertFalse(store.Portfolio.is_worker_thread())
736
737 with self.subTest(worker="not self"),\
738 mock.patch("threading.current_thread") as current_thread:
739 current = mock.Mock()
740 current_thread.return_value = current
741 store.Portfolio.worker = mock.Mock()
742 self.assertFalse(store.Portfolio.is_worker_thread())
743
744 with self.subTest(worker="self"),\
745 mock.patch("threading.current_thread") as current_thread:
746 current = mock.Mock()
747 current_thread.return_value = current
748 store.Portfolio.worker = current
749 self.assertTrue(store.Portfolio.is_worker_thread())
750
751 def test_start_worker(self):
752 with mock.patch.object(store.Portfolio, "wait_for_notification") as notification:
753 store.Portfolio.start_worker()
754 notification.assert_called_once_with(poll=30)
755
756 self.assertEqual("lock", store.Portfolio.last_date.lock.__class__.__name__)
757 self.assertEqual("lock", store.Portfolio.liquidities.lock.__class__.__name__)
758 store.Portfolio.report.start_lock.assert_called_once_with()
759
760 self.assertIsNotNone(store.Portfolio.worker)
761 self.assertIsNotNone(store.Portfolio.worker_notify)
762 self.assertIsNotNone(store.Portfolio.callback)
763 self.assertTrue(store.Portfolio.worker_started)
297 764
298@unittest.skipUnless("unit" in limits, "Unit skipped") 765@unittest.skipUnless("unit" in limits, "Unit skipped")
299class AmountTest(WebMockTestCase): 766class AmountTest(WebMockTestCase):
@@ -736,7 +1203,7 @@ class MarketTest(WebMockTestCase):
736 self.assertEqual("Foo", m.fetch_fees()) 1203 self.assertEqual("Foo", m.fetch_fees())
737 self.ccxt.fetch_fees.assert_not_called() 1204 self.ccxt.fetch_fees.assert_not_called()
738 1205
739 @mock.patch.object(portfolio.Portfolio, "repartition") 1206 @mock.patch.object(market.Portfolio, "repartition")
740 @mock.patch.object(market.Market, "get_ticker") 1207 @mock.patch.object(market.Market, "get_ticker")
741 @mock.patch.object(market.TradeStore, "compute_trades") 1208 @mock.patch.object(market.TradeStore, "compute_trades")
742 def test_prepare_trades(self, compute_trades, get_ticker, repartition): 1209 def test_prepare_trades(self, compute_trades, get_ticker, repartition):
@@ -787,7 +1254,7 @@ class MarketTest(WebMockTestCase):
787 m.report.log_balances.assert_called_once_with(tag="tag") 1254 m.report.log_balances.assert_called_once_with(tag="tag")
788 1255
789 1256
790 @mock.patch.object(portfolio.time, "sleep") 1257 @mock.patch.object(market.time, "sleep")
791 @mock.patch.object(market.TradeStore, "all_orders") 1258 @mock.patch.object(market.TradeStore, "all_orders")
792 def test_follow_orders(self, all_orders, time_mock): 1259 def test_follow_orders(self, all_orders, time_mock):
793 for debug, sleep in [ 1260 for debug, sleep in [
@@ -907,7 +1374,172 @@ class MarketTest(WebMockTestCase):
907 self.ccxt.transfer_balance.assert_any_call("BTC", 3, "exchange", "margin") 1374 self.ccxt.transfer_balance.assert_any_call("BTC", 3, "exchange", "margin")
908 self.ccxt.transfer_balance.assert_any_call("USDT", 100, "exchange", "margin") 1375 self.ccxt.transfer_balance.assert_any_call("USDT", 100, "exchange", "margin")
909 self.ccxt.transfer_balance.assert_any_call("ETC", 5, "margin", "exchange") 1376 self.ccxt.transfer_balance.assert_any_call("ETC", 5, "margin", "exchange")
910 1377
1378 def test_store_report(self):
1379
1380 file_open = mock.mock_open()
1381 m = market.Market(self.ccxt, user_id=1)
1382 with self.subTest(file=None),\
1383 mock.patch.object(m, "report") as report,\
1384 mock.patch("market.open", file_open):
1385 m.store_report()
1386 report.merge.assert_called_with(store.Portfolio.report)
1387 file_open.assert_not_called()
1388
1389 report.reset_mock()
1390 file_open = mock.mock_open()
1391 m = market.Market(self.ccxt, report_path="present", user_id=1)
1392 with self.subTest(file="present"),\
1393 mock.patch("market.open", file_open),\
1394 mock.patch.object(m, "report") as report,\
1395 mock.patch.object(market, "datetime") as time_mock:
1396
1397 time_mock.now.return_value = datetime.datetime(2018, 2, 25)
1398 report.to_json.return_value = "json_content"
1399
1400 m.store_report()
1401
1402 file_open.assert_any_call("present/2018-02-25T00:00:00_1.json", "w")
1403 file_open().write.assert_called_once_with("json_content")
1404 m.report.to_json.assert_called_once_with()
1405 report.merge.assert_called_with(store.Portfolio.report)
1406
1407 report.reset_mock()
1408
1409 m = market.Market(self.ccxt, report_path="error", user_id=1)
1410 with self.subTest(file="error"),\
1411 mock.patch("market.open") as file_open,\
1412 mock.patch.object(m, "report") as report,\
1413 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
1414 file_open.side_effect = FileNotFoundError
1415
1416 m.store_report()
1417
1418 report.merge.assert_called_with(store.Portfolio.report)
1419 self.assertRegex(stdout_mock.getvalue(), "impossible to store report file: FileNotFoundError;")
1420
1421 def test_print_orders(self):
1422 m = market.Market(self.ccxt)
1423 with mock.patch.object(m.report, "log_stage") as log_stage,\
1424 mock.patch.object(m.balances, "fetch_balances") as fetch_balances,\
1425 mock.patch.object(m, "prepare_trades") as prepare_trades,\
1426 mock.patch.object(m.trades, "prepare_orders") as prepare_orders:
1427 m.print_orders()
1428
1429 log_stage.assert_called_with("print_orders")
1430 fetch_balances.assert_called_with(tag="print_orders")
1431 prepare_trades.assert_called_with(base_currency="BTC",
1432 compute_value="average")
1433 prepare_orders.assert_called_with(compute_value="average")
1434
1435 def test_print_balances(self):
1436 m = market.Market(self.ccxt)
1437
1438 with mock.patch.object(m.balances, "in_currency") as in_currency,\
1439 mock.patch.object(m.report, "log_stage") as log_stage,\
1440 mock.patch.object(m.balances, "fetch_balances") as fetch_balances,\
1441 mock.patch.object(m.report, "print_log") as print_log:
1442
1443 in_currency.return_value = {
1444 "BTC": portfolio.Amount("BTC", "0.65"),
1445 "ETH": portfolio.Amount("BTC", "0.3"),
1446 }
1447
1448 m.print_balances()
1449
1450 log_stage.assert_called_once_with("print_balances")
1451 fetch_balances.assert_called_with()
1452 print_log.assert_has_calls([
1453 mock.call("total:"),
1454 mock.call(portfolio.Amount("BTC", "0.95")),
1455 ])
1456
1457 @mock.patch("market.Processor.process")
1458 @mock.patch("market.ReportStore.log_error")
1459 @mock.patch("market.Market.store_report")
1460 def test_process(self, store_report, log_error, process):
1461 m = market.Market(self.ccxt)
1462 with self.subTest(before=False, after=False):
1463 m.process(None)
1464
1465 process.assert_not_called()
1466 store_report.assert_called_once()
1467 log_error.assert_not_called()
1468
1469 process.reset_mock()
1470 log_error.reset_mock()
1471 store_report.reset_mock()
1472 with self.subTest(before=True, after=False):
1473 m.process(None, before=True)
1474
1475 process.assert_called_once_with("sell_all", steps="before")
1476 store_report.assert_called_once()
1477 log_error.assert_not_called()
1478
1479 process.reset_mock()
1480 log_error.reset_mock()
1481 store_report.reset_mock()
1482 with self.subTest(before=False, after=True):
1483 m.process(None, after=True)
1484
1485 process.assert_called_once_with("sell_all", steps="after")
1486 store_report.assert_called_once()
1487 log_error.assert_not_called()
1488
1489 process.reset_mock()
1490 log_error.reset_mock()
1491 store_report.reset_mock()
1492 with self.subTest(before=True, after=True):
1493 m.process(None, before=True, after=True)
1494
1495 process.assert_has_calls([
1496 mock.call("sell_all", steps="before"),
1497 mock.call("sell_all", steps="after"),
1498 ])
1499 store_report.assert_called_once()
1500 log_error.assert_not_called()
1501
1502 process.reset_mock()
1503 log_error.reset_mock()
1504 store_report.reset_mock()
1505 with self.subTest(action="print_balances"),\
1506 mock.patch.object(m, "print_balances") as print_balances:
1507 m.process(["print_balances"])
1508
1509 process.assert_not_called()
1510 log_error.assert_not_called()
1511 store_report.assert_called_once()
1512 print_balances.assert_called_once_with()
1513
1514 log_error.reset_mock()
1515 store_report.reset_mock()
1516 with self.subTest(action="print_orders"),\
1517 mock.patch.object(m, "print_orders") as print_orders,\
1518 mock.patch.object(m, "print_balances") as print_balances:
1519 m.process(["print_orders", "print_balances"])
1520
1521 process.assert_not_called()
1522 log_error.assert_not_called()
1523 store_report.assert_called_once()
1524 print_orders.assert_called_once_with()
1525 print_balances.assert_called_once_with()
1526
1527 log_error.reset_mock()
1528 store_report.reset_mock()
1529 with self.subTest(action="unknown"):
1530 m.process(["unknown"])
1531 log_error.assert_called_once_with("market_process", message="Unknown action unknown")
1532 store_report.assert_called_once()
1533
1534 log_error.reset_mock()
1535 store_report.reset_mock()
1536 with self.subTest(unhandled_exception=True):
1537 process.side_effect = Exception("bouh")
1538
1539 m.process(None, before=True)
1540 log_error.assert_called_with("market_process", exception=mock.ANY)
1541 store_report.assert_called_once()
1542
911@unittest.skipUnless("unit" in limits, "Unit skipped") 1543@unittest.skipUnless("unit" in limits, "Unit skipped")
912class TradeStoreTest(WebMockTestCase): 1544class TradeStoreTest(WebMockTestCase):
913 def test_compute_trades(self): 1545 def test_compute_trades(self):
@@ -1226,7 +1858,7 @@ class BalanceStoreTest(WebMockTestCase):
1226 self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(balance_store.currencies())) 1858 self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(balance_store.currencies()))
1227 self.m.report.log_balances.assert_called_with(tag="foo") 1859 self.m.report.log_balances.assert_called_with(tag="foo")
1228 1860
1229 @mock.patch.object(portfolio.Portfolio, "repartition") 1861 @mock.patch.object(market.Portfolio, "repartition")
1230 def test_dispatch_assets(self, repartition): 1862 def test_dispatch_assets(self, repartition):
1231 self.m.ccxt.fetch_all_balances.return_value = self.fetch_balance 1863 self.m.ccxt.fetch_all_balances.return_value = self.fetch_balance
1232 1864
@@ -1243,7 +1875,7 @@ class BalanceStoreTest(WebMockTestCase):
1243 repartition.return_value = repartition_hash 1875 repartition.return_value = repartition_hash
1244 1876
1245 amounts = balance_store.dispatch_assets(portfolio.Amount("BTC", "11.1")) 1877 amounts = balance_store.dispatch_assets(portfolio.Amount("BTC", "11.1"))
1246 repartition.assert_called_with(self.m, liquidity="medium") 1878 repartition.assert_called_with(liquidity="medium")
1247 self.assertIn("XEM", balance_store.currencies()) 1879 self.assertIn("XEM", balance_store.currencies())
1248 self.assertEqual(D("2.6"), amounts["BTC"].value) 1880 self.assertEqual(D("2.6"), amounts["BTC"].value)
1249 self.assertEqual(D("7.5"), amounts["XEM"].value) 1881 self.assertEqual(D("7.5"), amounts["XEM"].value)
@@ -2372,6 +3004,19 @@ class ReportStoreTest(WebMockTestCase):
2372 report_store.set_verbose(False) 3004 report_store.set_verbose(False)
2373 self.assertFalse(report_store.verbose_print) 3005 self.assertFalse(report_store.verbose_print)
2374 3006
3007 def test_merge(self):
3008 report_store1 = market.ReportStore(self.m, verbose_print=False)
3009 report_store2 = market.ReportStore(None, verbose_print=False)
3010
3011 report_store2.log_stage("1")
3012 report_store1.log_stage("2")
3013 report_store2.log_stage("3")
3014
3015 report_store1.merge(report_store2)
3016
3017 self.assertEqual(3, len(report_store1.logs))
3018 self.assertEqual(["1", "2", "3"], list(map(lambda x: x["stage"], report_store1.logs)))
3019
2375 def test_print_log(self): 3020 def test_print_log(self):
2376 report_store = market.ReportStore(self.m) 3021 report_store = market.ReportStore(self.m)
2377 with self.subTest(verbose=True),\ 3022 with self.subTest(verbose=True),\
@@ -2790,7 +3435,7 @@ class ReportStoreTest(WebMockTestCase):
2790 }) 3435 })
2791 3436
2792@unittest.skipUnless("unit" in limits, "Unit skipped") 3437@unittest.skipUnless("unit" in limits, "Unit skipped")
2793class HelperTest(WebMockTestCase): 3438class MainTest(WebMockTestCase):
2794 def test_make_order(self): 3439 def test_make_order(self):
2795 self.m.get_ticker.return_value = { 3440 self.m.get_ticker.return_value = {
2796 "inverted": False, 3441 "inverted": False,
@@ -2800,7 +3445,7 @@ class HelperTest(WebMockTestCase):
2800 } 3445 }
2801 3446
2802 with self.subTest(description="nominal case"): 3447 with self.subTest(description="nominal case"):
2803 helper.make_order(self.m, 10, "ETH") 3448 main.make_order(self.m, 10, "ETH")
2804 3449
2805 self.m.report.log_stage.assert_has_calls([ 3450 self.m.report.log_stage.assert_has_calls([
2806 mock.call("make_order_begin"), 3451 mock.call("make_order_begin"),
@@ -2825,7 +3470,7 @@ class HelperTest(WebMockTestCase):
2825 3470
2826 self.m.reset_mock() 3471 self.m.reset_mock()
2827 with self.subTest(compute_value="default"): 3472 with self.subTest(compute_value="default"):
2828 helper.make_order(self.m, 10, "ETH", action="dispose", 3473 main.make_order(self.m, 10, "ETH", action="dispose",
2829 compute_value="ask") 3474 compute_value="ask")
2830 3475
2831 trade = self.m.trades.all.append.mock_calls[0][1][0] 3476 trade = self.m.trades.all.append.mock_calls[0][1][0]
@@ -2834,7 +3479,7 @@ class HelperTest(WebMockTestCase):
2834 3479
2835 self.m.reset_mock() 3480 self.m.reset_mock()
2836 with self.subTest(follow=False): 3481 with self.subTest(follow=False):
2837 result = helper.make_order(self.m, 10, "ETH", follow=False) 3482 result = main.make_order(self.m, 10, "ETH", follow=False)
2838 3483
2839 self.m.report.log_stage.assert_has_calls([ 3484 self.m.report.log_stage.assert_has_calls([
2840 mock.call("make_order_begin"), 3485 mock.call("make_order_begin"),
@@ -2854,7 +3499,7 @@ class HelperTest(WebMockTestCase):
2854 3499
2855 self.m.reset_mock() 3500 self.m.reset_mock()
2856 with self.subTest(base_currency="USDT"): 3501 with self.subTest(base_currency="USDT"):
2857 helper.make_order(self.m, 1, "BTC", base_currency="USDT") 3502 main.make_order(self.m, 1, "BTC", base_currency="USDT")
2858 3503
2859 trade = self.m.trades.all.append.mock_calls[0][1][0] 3504 trade = self.m.trades.all.append.mock_calls[0][1][0]
2860 self.assertEqual("BTC", trade.currency) 3505 self.assertEqual("BTC", trade.currency)
@@ -2862,14 +3507,14 @@ class HelperTest(WebMockTestCase):
2862 3507
2863 self.m.reset_mock() 3508 self.m.reset_mock()
2864 with self.subTest(close_if_possible=True): 3509 with self.subTest(close_if_possible=True):
2865 helper.make_order(self.m, 10, "ETH", close_if_possible=True) 3510 main.make_order(self.m, 10, "ETH", close_if_possible=True)
2866 3511
2867 trade = self.m.trades.all.append.mock_calls[0][1][0] 3512 trade = self.m.trades.all.append.mock_calls[0][1][0]
2868 self.assertEqual(True, trade.orders[0].close_if_possible) 3513 self.assertEqual(True, trade.orders[0].close_if_possible)
2869 3514
2870 self.m.reset_mock() 3515 self.m.reset_mock()
2871 with self.subTest(action="dispose"): 3516 with self.subTest(action="dispose"):
2872 helper.make_order(self.m, 10, "ETH", action="dispose") 3517 main.make_order(self.m, 10, "ETH", action="dispose")
2873 3518
2874 trade = self.m.trades.all.append.mock_calls[0][1][0] 3519 trade = self.m.trades.all.append.mock_calls[0][1][0]
2875 self.assertEqual(0, trade.value_to) 3520 self.assertEqual(0, trade.value_to)
@@ -2879,19 +3524,19 @@ class HelperTest(WebMockTestCase):
2879 3524
2880 self.m.reset_mock() 3525 self.m.reset_mock()
2881 with self.subTest(compute_value="default"): 3526 with self.subTest(compute_value="default"):
2882 helper.make_order(self.m, 10, "ETH", action="dispose", 3527 main.make_order(self.m, 10, "ETH", action="dispose",
2883 compute_value="bid") 3528 compute_value="bid")
2884 3529
2885 trade = self.m.trades.all.append.mock_calls[0][1][0] 3530 trade = self.m.trades.all.append.mock_calls[0][1][0]
2886 self.assertEqual(D("0.9"), trade.value_from.value) 3531 self.assertEqual(D("0.9"), trade.value_from.value)
2887 3532
2888 def test_user_market(self): 3533 def test_get_user_market(self):
2889 with mock.patch("helper.main_fetch_markets") as main_fetch_markets,\ 3534 with mock.patch("main.fetch_markets") as main_fetch_markets,\
2890 mock.patch("helper.main_parse_config") as main_parse_config: 3535 mock.patch("main.parse_config") as main_parse_config:
2891 with self.subTest(debug=False): 3536 with self.subTest(debug=False):
2892 main_parse_config.return_value = ["pg_config", "report_path"] 3537 main_parse_config.return_value = ["pg_config", "report_path"]
2893 main_fetch_markets.return_value = [({"key": "market_config"},)] 3538 main_fetch_markets.return_value = [({"key": "market_config"},)]
2894 m = helper.get_user_market("config_path.ini", 1) 3539 m = main.get_user_market("config_path.ini", 1)
2895 3540
2896 self.assertIsInstance(m, market.Market) 3541 self.assertIsInstance(m, market.Market)
2897 self.assertFalse(m.debug) 3542 self.assertFalse(m.debug)
@@ -2899,141 +3544,100 @@ class HelperTest(WebMockTestCase):
2899 with self.subTest(debug=True): 3544 with self.subTest(debug=True):
2900 main_parse_config.return_value = ["pg_config", "report_path"] 3545 main_parse_config.return_value = ["pg_config", "report_path"]
2901 main_fetch_markets.return_value = [({"key": "market_config"},)] 3546 main_fetch_markets.return_value = [({"key": "market_config"},)]
2902 m = helper.get_user_market("config_path.ini", 1, debug=True) 3547 m = main.get_user_market("config_path.ini", 1, debug=True)
2903 3548
2904 self.assertIsInstance(m, market.Market) 3549 self.assertIsInstance(m, market.Market)
2905 self.assertTrue(m.debug) 3550 self.assertTrue(m.debug)
2906 3551
2907 def test_main_store_report(self): 3552 def test_process(self):
2908 file_open = mock.mock_open() 3553 with mock.patch("market.Market") as market_mock,\
2909 with self.subTest(file=None), mock.patch("__main__.open", file_open):
2910 helper.main_store_report(None, 1, self.m)
2911 file_open.assert_not_called()
2912
2913 file_open = mock.mock_open()
2914 with self.subTest(file="present"), mock.patch("helper.open", file_open),\
2915 mock.patch.object(helper, "datetime") as time_mock:
2916 time_mock.now.return_value = datetime.datetime(2018, 2, 25)
2917 self.m.report.to_json.return_value = "json_content"
2918
2919 helper.main_store_report("present", 1, self.m)
2920
2921 file_open.assert_any_call("present/2018-02-25T00:00:00_1.json", "w")
2922 file_open().write.assert_called_once_with("json_content")
2923 self.m.report.to_json.assert_called_once_with()
2924
2925 with self.subTest(file="error"),\
2926 mock.patch("helper.open") as file_open,\
2927 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: 3554 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
2928 file_open.side_effect = FileNotFoundError
2929 3555
2930 helper.main_store_report("error", 1, self.m) 3556 args_mock = mock.Mock()
2931 3557 args_mock.action = "action"
2932 self.assertRegex(stdout_mock.getvalue(), "impossible to store report file: FileNotFoundError;") 3558 args_mock.config = "config"
2933 3559 args_mock.user = "user"
2934 @mock.patch("helper.Processor.process") 3560 args_mock.debug = "debug"
2935 def test_main_process_market(self, process): 3561 args_mock.before = "before"
2936 with self.subTest(before=False, after=False): 3562 args_mock.after = "after"
2937 m = mock.Mock() 3563 self.assertEqual("", stdout_mock.getvalue())
2938 helper.main_process_market(m, None)
2939
2940 process.assert_not_called()
2941
2942 process.reset_mock()
2943 with self.subTest(before=True, after=False):
2944 helper.main_process_market(m, None, before=True)
2945
2946 process.assert_called_once_with("sell_all", steps="before")
2947
2948 process.reset_mock()
2949 with self.subTest(before=False, after=True):
2950 helper.main_process_market(m, None, after=True)
2951
2952 process.assert_called_once_with("sell_all", steps="after")
2953 3564
2954 process.reset_mock() 3565 main.process("config", 1, "report_path", args_mock)
2955 with self.subTest(before=True, after=True):
2956 helper.main_process_market(m, None, before=True, after=True)
2957 3566
2958 process.assert_has_calls([ 3567 market_mock.from_config.assert_has_calls([
2959 mock.call("sell_all", steps="before"), 3568 mock.call("config", debug="debug", user_id=1, report_path="report_path"),
2960 mock.call("sell_all", steps="after"), 3569 mock.call().process("action", before="before", after="after"),
2961 ]) 3570 ])
2962 3571
2963 process.reset_mock() 3572 with self.subTest(exception=True):
2964 with self.subTest(action="print_balances"),\ 3573 market_mock.from_config.side_effect = Exception("boo")
2965 mock.patch("helper.print_balances") as print_balances: 3574 main.process("config", 1, "report_path", args_mock)
2966 helper.main_process_market("user", ["print_balances"]) 3575 self.assertEqual("Exception: boo\n", stdout_mock.getvalue())
2967
2968 process.assert_not_called()
2969 print_balances.assert_called_once_with("user")
2970
2971 with self.subTest(action="print_orders"),\
2972 mock.patch("helper.print_orders") as print_orders,\
2973 mock.patch("helper.print_balances") as print_balances:
2974 helper.main_process_market("user", ["print_orders", "print_balances"])
2975
2976 process.assert_not_called()
2977 print_orders.assert_called_once_with("user")
2978 print_balances.assert_called_once_with("user")
2979
2980 with self.subTest(action="unknown"),\
2981 self.assertRaises(NotImplementedError):
2982 helper.main_process_market("user", ["unknown"])
2983 3576
2984 @mock.patch.object(helper, "psycopg2") 3577 def test_main(self):
2985 def test_fetch_markets(self, psycopg2): 3578 with self.subTest(parallel=False):
2986 connect_mock = mock.Mock() 3579 with mock.patch("main.parse_args") as parse_args,\
2987 cursor_mock = mock.MagicMock() 3580 mock.patch("main.parse_config") as parse_config,\
2988 cursor_mock.__iter__.return_value = ["row_1", "row_2"] 3581 mock.patch("main.fetch_markets") as fetch_markets,\
2989 3582 mock.patch("main.process") as process:
2990 connect_mock.cursor.return_value = cursor_mock
2991 psycopg2.connect.return_value = connect_mock
2992
2993 with self.subTest(user=None):
2994 rows = list(helper.main_fetch_markets({"foo": "bar"}, None))
2995
2996 psycopg2.connect.assert_called_once_with(foo="bar")
2997 cursor_mock.execute.assert_called_once_with("SELECT config,user_id FROM market_configs")
2998
2999 self.assertEqual(["row_1", "row_2"], rows)
3000
3001 psycopg2.connect.reset_mock()
3002 cursor_mock.execute.reset_mock()
3003 with self.subTest(user=1):
3004 rows = list(helper.main_fetch_markets({"foo": "bar"}, 1))
3005 3583
3006 psycopg2.connect.assert_called_once_with(foo="bar") 3584 args_mock = mock.Mock()
3007 cursor_mock.execute.assert_called_once_with("SELECT config,user_id FROM market_configs WHERE user_id = %s", 1) 3585 args_mock.parallel = False
3586 args_mock.config = "config"
3587 args_mock.user = "user"
3588 parse_args.return_value = args_mock
3008 3589
3009 self.assertEqual(["row_1", "row_2"], rows) 3590 parse_config.return_value = ["pg_config", "report_path"]
3010 3591
3011 @mock.patch.object(helper.sys, "exit") 3592 fetch_markets.return_value = [["config1", 1], ["config2", 2]]
3012 def test_main_parse_args(self, exit):
3013 with self.subTest(config="config.ini"):
3014 args = helper.main_parse_args([])
3015 self.assertEqual("config.ini", args.config)
3016 self.assertFalse(args.before)
3017 self.assertFalse(args.after)
3018 self.assertFalse(args.debug)
3019 3593
3020 args = helper.main_parse_args(["--before", "--after", "--debug"]) 3594 main.main(["Foo", "Bar"])
3021 self.assertTrue(args.before)
3022 self.assertTrue(args.after)
3023 self.assertTrue(args.debug)
3024 3595
3025 exit.assert_not_called() 3596 parse_args.assert_called_with(["Foo", "Bar"])
3597 parse_config.assert_called_with("config")
3598 fetch_markets.assert_called_with("pg_config", "user")
3026 3599
3027 with self.subTest(config="inexistant"),\ 3600 self.assertEqual(2, process.call_count)
3028 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: 3601 process.assert_has_calls([
3029 args = helper.main_parse_args(["--config", "foo.bar"]) 3602 mock.call("config1", 1, "report_path", args_mock),
3030 exit.assert_called_once_with(1) 3603 mock.call("config2", 2, "report_path", args_mock),
3031 self.assertEqual("no config file found, exiting\n", stdout_mock.getvalue()) 3604 ])
3605 with self.subTest(parallel=True):
3606 with mock.patch("main.parse_args") as parse_args,\
3607 mock.patch("main.parse_config") as parse_config,\
3608 mock.patch("main.fetch_markets") as fetch_markets,\
3609 mock.patch("main.process") as process,\
3610 mock.patch("store.Portfolio.start_worker") as start:
3611
3612 args_mock = mock.Mock()
3613 args_mock.parallel = True
3614 args_mock.config = "config"
3615 args_mock.user = "user"
3616 parse_args.return_value = args_mock
3617
3618 parse_config.return_value = ["pg_config", "report_path"]
3619
3620 fetch_markets.return_value = [["config1", 1], ["config2", 2]]
3621
3622 main.main(["Foo", "Bar"])
3623
3624 parse_args.assert_called_with(["Foo", "Bar"])
3625 parse_config.assert_called_with("config")
3626 fetch_markets.assert_called_with("pg_config", "user")
3627
3628 start.assert_called_once_with()
3629 self.assertEqual(2, process.call_count)
3630 process.assert_has_calls([
3631 mock.call.__bool__(),
3632 mock.call("config1", 1, "report_path", args_mock),
3633 mock.call.__bool__(),
3634 mock.call("config2", 2, "report_path", args_mock),
3635 ])
3032 3636
3033 @mock.patch.object(helper.sys, "exit") 3637 @mock.patch.object(main.sys, "exit")
3034 @mock.patch("helper.configparser") 3638 @mock.patch("main.configparser")
3035 @mock.patch("helper.os") 3639 @mock.patch("main.os")
3036 def test_main_parse_config(self, os, configparser, exit): 3640 def test_parse_config(self, os, configparser, exit):
3037 with self.subTest(pg_config=True, report_path=None): 3641 with self.subTest(pg_config=True, report_path=None):
3038 config_mock = mock.MagicMock() 3642 config_mock = mock.MagicMock()
3039 configparser.ConfigParser.return_value = config_mock 3643 configparser.ConfigParser.return_value = config_mock
@@ -3043,7 +3647,7 @@ class HelperTest(WebMockTestCase):
3043 config_mock.__contains__.side_effect = config 3647 config_mock.__contains__.side_effect = config
3044 config_mock.__getitem__.return_value = "pg_config" 3648 config_mock.__getitem__.return_value = "pg_config"
3045 3649
3046 result = helper.main_parse_config("configfile") 3650 result = main.parse_config("configfile")
3047 3651
3048 config_mock.read.assert_called_with("configfile") 3652 config_mock.read.assert_called_with("configfile")
3049 3653
@@ -3061,7 +3665,7 @@ class HelperTest(WebMockTestCase):
3061 ] 3665 ]
3062 3666
3063 os.path.exists.return_value = False 3667 os.path.exists.return_value = False
3064 result = helper.main_parse_config("configfile") 3668 result = main.parse_config("configfile")
3065 3669
3066 config_mock.read.assert_called_with("configfile") 3670 config_mock.read.assert_called_with("configfile")
3067 self.assertEqual(["pg_config", "report_path"], result) 3671 self.assertEqual(["pg_config", "report_path"], result)
@@ -3072,46 +3676,71 @@ class HelperTest(WebMockTestCase):
3072 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: 3676 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
3073 config_mock = mock.MagicMock() 3677 config_mock = mock.MagicMock()
3074 configparser.ConfigParser.return_value = config_mock 3678 configparser.ConfigParser.return_value = config_mock
3075 result = helper.main_parse_config("configfile") 3679 result = main.parse_config("configfile")
3076 3680
3077 config_mock.read.assert_called_with("configfile") 3681 config_mock.read.assert_called_with("configfile")
3078 exit.assert_called_once_with(1) 3682 exit.assert_called_once_with(1)
3079 self.assertEqual("no configuration for postgresql in config file\n", stdout_mock.getvalue()) 3683 self.assertEqual("no configuration for postgresql in config file\n", stdout_mock.getvalue())
3080 3684
3685 @mock.patch.object(main.sys, "exit")
3686 def test_parse_args(self, exit):
3687 with self.subTest(config="config.ini"):
3688 args = main.parse_args([])
3689 self.assertEqual("config.ini", args.config)
3690 self.assertFalse(args.before)
3691 self.assertFalse(args.after)
3692 self.assertFalse(args.debug)
3081 3693
3082 def test_print_orders(self): 3694 args = main.parse_args(["--before", "--after", "--debug"])
3083 helper.print_orders(self.m) 3695 self.assertTrue(args.before)
3696 self.assertTrue(args.after)
3697 self.assertTrue(args.debug)
3084 3698
3085 self.m.report.log_stage.assert_called_with("print_orders") 3699 exit.assert_not_called()
3086 self.m.balances.fetch_balances.assert_called_with(tag="print_orders") 3700
3087 self.m.prepare_trades.assert_called_with(base_currency="BTC", 3701 with self.subTest(config="inexistant"),\
3088 compute_value="average") 3702 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
3089 self.m.trades.prepare_orders.assert_called_with(compute_value="average") 3703 args = main.parse_args(["--config", "foo.bar"])
3704 exit.assert_called_once_with(1)
3705 self.assertEqual("no config file found, exiting\n", stdout_mock.getvalue())
3706
3707 @mock.patch.object(main, "psycopg2")
3708 def test_fetch_markets(self, psycopg2):
3709 connect_mock = mock.Mock()
3710 cursor_mock = mock.MagicMock()
3711 cursor_mock.__iter__.return_value = ["row_1", "row_2"]
3712
3713 connect_mock.cursor.return_value = cursor_mock
3714 psycopg2.connect.return_value = connect_mock
3715
3716 with self.subTest(user=None):
3717 rows = list(main.fetch_markets({"foo": "bar"}, None))
3718
3719 psycopg2.connect.assert_called_once_with(foo="bar")
3720 cursor_mock.execute.assert_called_once_with("SELECT config,user_id FROM market_configs")
3721
3722 self.assertEqual(["row_1", "row_2"], rows)
3723
3724 psycopg2.connect.reset_mock()
3725 cursor_mock.execute.reset_mock()
3726 with self.subTest(user=1):
3727 rows = list(main.fetch_markets({"foo": "bar"}, 1))
3728
3729 psycopg2.connect.assert_called_once_with(foo="bar")
3730 cursor_mock.execute.assert_called_once_with("SELECT config,user_id FROM market_configs WHERE user_id = %s", 1)
3731
3732 self.assertEqual(["row_1", "row_2"], rows)
3090 3733
3091 def test_print_balances(self):
3092 self.m.balances.in_currency.return_value = {
3093 "BTC": portfolio.Amount("BTC", "0.65"),
3094 "ETH": portfolio.Amount("BTC", "0.3"),
3095 }
3096
3097 helper.print_balances(self.m)
3098
3099 self.m.report.log_stage.assert_called_once_with("print_balances")
3100 self.m.balances.fetch_balances.assert_called_with()
3101 self.m.report.print_log.assert_has_calls([
3102 mock.call("total:"),
3103 mock.call(portfolio.Amount("BTC", "0.95")),
3104 ])
3105 3734
3106@unittest.skipUnless("unit" in limits, "Unit skipped") 3735@unittest.skipUnless("unit" in limits, "Unit skipped")
3107class ProcessorTest(WebMockTestCase): 3736class ProcessorTest(WebMockTestCase):
3108 def test_values(self): 3737 def test_values(self):
3109 processor = helper.Processor(self.m) 3738 processor = market.Processor(self.m)
3110 3739
3111 self.assertEqual(self.m, processor.market) 3740 self.assertEqual(self.m, processor.market)
3112 3741
3113 def test_run_action(self): 3742 def test_run_action(self):
3114 processor = helper.Processor(self.m) 3743 processor = market.Processor(self.m)
3115 3744
3116 with mock.patch.object(processor, "parse_args") as parse_args: 3745 with mock.patch.object(processor, "parse_args") as parse_args:
3117 method_mock = mock.Mock() 3746 method_mock = mock.Mock()
@@ -3125,10 +3754,10 @@ class ProcessorTest(WebMockTestCase):
3125 3754
3126 processor.run_action("wait_for_recent", "bar", "baz") 3755 processor.run_action("wait_for_recent", "bar", "baz")
3127 3756
3128 method_mock.assert_called_with(self.m, foo="bar") 3757 method_mock.assert_called_with(foo="bar")
3129 3758
3130 def test_select_step(self): 3759 def test_select_step(self):
3131 processor = helper.Processor(self.m) 3760 processor = market.Processor(self.m)
3132 3761
3133 scenario = processor.scenarios["sell_all"] 3762 scenario = processor.scenarios["sell_all"]
3134 3763
@@ -3141,9 +3770,9 @@ class ProcessorTest(WebMockTestCase):
3141 with self.assertRaises(TypeError): 3770 with self.assertRaises(TypeError):
3142 processor.select_steps(scenario, ["wait"]) 3771 processor.select_steps(scenario, ["wait"])
3143 3772
3144 @mock.patch("helper.Processor.process_step") 3773 @mock.patch("market.Processor.process_step")
3145 def test_process(self, process_step): 3774 def test_process(self, process_step):
3146 processor = helper.Processor(self.m) 3775 processor = market.Processor(self.m)
3147 3776
3148 processor.process("sell_all", foo="bar") 3777 processor.process("sell_all", foo="bar")
3149 self.assertEqual(3, process_step.call_count) 3778 self.assertEqual(3, process_step.call_count)
@@ -3164,11 +3793,11 @@ class ProcessorTest(WebMockTestCase):
3164 ccxt = mock.Mock(spec=market.ccxt.poloniexE) 3793 ccxt = mock.Mock(spec=market.ccxt.poloniexE)
3165 m = market.Market(ccxt) 3794 m = market.Market(ccxt)
3166 3795
3167 processor = helper.Processor(m) 3796 processor = market.Processor(m)
3168 3797
3169 method, arguments = processor.method_arguments("wait_for_recent") 3798 method, arguments = processor.method_arguments("wait_for_recent")
3170 self.assertEqual(portfolio.Portfolio.wait_for_recent, method) 3799 self.assertEqual(market.Portfolio.wait_for_recent, method)
3171 self.assertEqual(["delta"], arguments) 3800 self.assertEqual(["delta", "poll"], arguments)
3172 3801
3173 method, arguments = processor.method_arguments("prepare_trades") 3802 method, arguments = processor.method_arguments("prepare_trades")
3174 self.assertEqual(m.prepare_trades, method) 3803 self.assertEqual(m.prepare_trades, method)
@@ -3190,7 +3819,7 @@ class ProcessorTest(WebMockTestCase):
3190 self.assertEqual(m.trades.close_trades, method) 3819 self.assertEqual(m.trades.close_trades, method)
3191 3820
3192 def test_process_step(self): 3821 def test_process_step(self):
3193 processor = helper.Processor(self.m) 3822 processor = market.Processor(self.m)
3194 3823
3195 with mock.patch.object(processor, "run_action") as run_action: 3824 with mock.patch.object(processor, "run_action") as run_action:
3196 step = processor.scenarios["sell_needed"][1] 3825 step = processor.scenarios["sell_needed"][1]
@@ -3224,7 +3853,7 @@ class ProcessorTest(WebMockTestCase):
3224 self.m.balances.fetch_balances.assert_not_called() 3853 self.m.balances.fetch_balances.assert_not_called()
3225 3854
3226 def test_parse_args(self): 3855 def test_parse_args(self):
3227 processor = helper.Processor(self.m) 3856 processor = market.Processor(self.m)
3228 3857
3229 with mock.patch.object(processor, "method_arguments") as method_arguments: 3858 with mock.patch.object(processor, "method_arguments") as method_arguments:
3230 method_mock = mock.Mock() 3859 method_mock = mock.Mock()
@@ -3350,7 +3979,7 @@ class AcceptanceTest(WebMockTestCase):
3350 market = mock.Mock() 3979 market = mock.Mock()
3351 market.fetch_all_balances.return_value = fetch_balance 3980 market.fetch_all_balances.return_value = fetch_balance
3352 market.fetch_ticker.side_effect = fetch_ticker 3981 market.fetch_ticker.side_effect = fetch_ticker
3353 with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition): 3982 with mock.patch.object(market.Portfolio, "repartition", return_value=repartition):
3354 # Action 1 3983 # Action 1
3355 helper.prepare_trades(market) 3984 helper.prepare_trades(market)
3356 3985
@@ -3429,7 +4058,7 @@ class AcceptanceTest(WebMockTestCase):
3429 "amount": "10", "total": "1" 4058 "amount": "10", "total": "1"
3430 } 4059 }
3431 ] 4060 ]
3432 with mock.patch.object(portfolio.time, "sleep") as sleep: 4061 with mock.patch.object(market.time, "sleep") as sleep:
3433 # Action 4 4062 # Action 4
3434 helper.follow_orders(verbose=False) 4063 helper.follow_orders(verbose=False)
3435 4064
@@ -3470,7 +4099,7 @@ class AcceptanceTest(WebMockTestCase):
3470 } 4099 }
3471 market.fetch_all_balances.return_value = fetch_balance 4100 market.fetch_all_balances.return_value = fetch_balance
3472 4101
3473 with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition): 4102 with mock.patch.object(market.Portfolio, "repartition", return_value=repartition):
3474 # Action 5 4103 # Action 5
3475 helper.prepare_trades(market, only="acquire", compute_value="average") 4104 helper.prepare_trades(market, only="acquire", compute_value="average")
3476 4105
@@ -3542,7 +4171,7 @@ class AcceptanceTest(WebMockTestCase):
3542 # TODO 4171 # TODO
3543 # portfolio.TradeStore.run_orders() 4172 # portfolio.TradeStore.run_orders()
3544 4173
3545 with mock.patch.object(portfolio.time, "sleep") as sleep: 4174 with mock.patch.object(market.time, "sleep") as sleep:
3546 # Action 8 4175 # Action 8
3547 helper.follow_orders(verbose=False) 4176 helper.follow_orders(verbose=False)
3548 4177