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 a45010b..f61e739 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)
@@ -2334,6 +2966,19 @@ class ReportStoreTest(WebMockTestCase):
2334 report_store.set_verbose(False) 2966 report_store.set_verbose(False)
2335 self.assertFalse(report_store.verbose_print) 2967 self.assertFalse(report_store.verbose_print)
2336 2968
2969 def test_merge(self):
2970 report_store1 = market.ReportStore(self.m, verbose_print=False)
2971 report_store2 = market.ReportStore(None, verbose_print=False)
2972
2973 report_store2.log_stage("1")
2974 report_store1.log_stage("2")
2975 report_store2.log_stage("3")
2976
2977 report_store1.merge(report_store2)
2978
2979 self.assertEqual(3, len(report_store1.logs))
2980 self.assertEqual(["1", "2", "3"], list(map(lambda x: x["stage"], report_store1.logs)))
2981
2337 def test_print_log(self): 2982 def test_print_log(self):
2338 report_store = market.ReportStore(self.m) 2983 report_store = market.ReportStore(self.m)
2339 with self.subTest(verbose=True),\ 2984 with self.subTest(verbose=True),\
@@ -2752,7 +3397,7 @@ class ReportStoreTest(WebMockTestCase):
2752 }) 3397 })
2753 3398
2754@unittest.skipUnless("unit" in limits, "Unit skipped") 3399@unittest.skipUnless("unit" in limits, "Unit skipped")
2755class HelperTest(WebMockTestCase): 3400class MainTest(WebMockTestCase):
2756 def test_make_order(self): 3401 def test_make_order(self):
2757 self.m.get_ticker.return_value = { 3402 self.m.get_ticker.return_value = {
2758 "inverted": False, 3403 "inverted": False,
@@ -2762,7 +3407,7 @@ class HelperTest(WebMockTestCase):
2762 } 3407 }
2763 3408
2764 with self.subTest(description="nominal case"): 3409 with self.subTest(description="nominal case"):
2765 helper.make_order(self.m, 10, "ETH") 3410 main.make_order(self.m, 10, "ETH")
2766 3411
2767 self.m.report.log_stage.assert_has_calls([ 3412 self.m.report.log_stage.assert_has_calls([
2768 mock.call("make_order_begin"), 3413 mock.call("make_order_begin"),
@@ -2787,7 +3432,7 @@ class HelperTest(WebMockTestCase):
2787 3432
2788 self.m.reset_mock() 3433 self.m.reset_mock()
2789 with self.subTest(compute_value="default"): 3434 with self.subTest(compute_value="default"):
2790 helper.make_order(self.m, 10, "ETH", action="dispose", 3435 main.make_order(self.m, 10, "ETH", action="dispose",
2791 compute_value="ask") 3436 compute_value="ask")
2792 3437
2793 trade = self.m.trades.all.append.mock_calls[0][1][0] 3438 trade = self.m.trades.all.append.mock_calls[0][1][0]
@@ -2796,7 +3441,7 @@ class HelperTest(WebMockTestCase):
2796 3441
2797 self.m.reset_mock() 3442 self.m.reset_mock()
2798 with self.subTest(follow=False): 3443 with self.subTest(follow=False):
2799 result = helper.make_order(self.m, 10, "ETH", follow=False) 3444 result = main.make_order(self.m, 10, "ETH", follow=False)
2800 3445
2801 self.m.report.log_stage.assert_has_calls([ 3446 self.m.report.log_stage.assert_has_calls([
2802 mock.call("make_order_begin"), 3447 mock.call("make_order_begin"),
@@ -2816,7 +3461,7 @@ class HelperTest(WebMockTestCase):
2816 3461
2817 self.m.reset_mock() 3462 self.m.reset_mock()
2818 with self.subTest(base_currency="USDT"): 3463 with self.subTest(base_currency="USDT"):
2819 helper.make_order(self.m, 1, "BTC", base_currency="USDT") 3464 main.make_order(self.m, 1, "BTC", base_currency="USDT")
2820 3465
2821 trade = self.m.trades.all.append.mock_calls[0][1][0] 3466 trade = self.m.trades.all.append.mock_calls[0][1][0]
2822 self.assertEqual("BTC", trade.currency) 3467 self.assertEqual("BTC", trade.currency)
@@ -2824,14 +3469,14 @@ class HelperTest(WebMockTestCase):
2824 3469
2825 self.m.reset_mock() 3470 self.m.reset_mock()
2826 with self.subTest(close_if_possible=True): 3471 with self.subTest(close_if_possible=True):
2827 helper.make_order(self.m, 10, "ETH", close_if_possible=True) 3472 main.make_order(self.m, 10, "ETH", close_if_possible=True)
2828 3473
2829 trade = self.m.trades.all.append.mock_calls[0][1][0] 3474 trade = self.m.trades.all.append.mock_calls[0][1][0]
2830 self.assertEqual(True, trade.orders[0].close_if_possible) 3475 self.assertEqual(True, trade.orders[0].close_if_possible)
2831 3476
2832 self.m.reset_mock() 3477 self.m.reset_mock()
2833 with self.subTest(action="dispose"): 3478 with self.subTest(action="dispose"):
2834 helper.make_order(self.m, 10, "ETH", action="dispose") 3479 main.make_order(self.m, 10, "ETH", action="dispose")
2835 3480
2836 trade = self.m.trades.all.append.mock_calls[0][1][0] 3481 trade = self.m.trades.all.append.mock_calls[0][1][0]
2837 self.assertEqual(0, trade.value_to) 3482 self.assertEqual(0, trade.value_to)
@@ -2841,19 +3486,19 @@ class HelperTest(WebMockTestCase):
2841 3486
2842 self.m.reset_mock() 3487 self.m.reset_mock()
2843 with self.subTest(compute_value="default"): 3488 with self.subTest(compute_value="default"):
2844 helper.make_order(self.m, 10, "ETH", action="dispose", 3489 main.make_order(self.m, 10, "ETH", action="dispose",
2845 compute_value="bid") 3490 compute_value="bid")
2846 3491
2847 trade = self.m.trades.all.append.mock_calls[0][1][0] 3492 trade = self.m.trades.all.append.mock_calls[0][1][0]
2848 self.assertEqual(D("0.9"), trade.value_from.value) 3493 self.assertEqual(D("0.9"), trade.value_from.value)
2849 3494
2850 def test_user_market(self): 3495 def test_get_user_market(self):
2851 with mock.patch("helper.main_fetch_markets") as main_fetch_markets,\ 3496 with mock.patch("main.fetch_markets") as main_fetch_markets,\
2852 mock.patch("helper.main_parse_config") as main_parse_config: 3497 mock.patch("main.parse_config") as main_parse_config:
2853 with self.subTest(debug=False): 3498 with self.subTest(debug=False):
2854 main_parse_config.return_value = ["pg_config", "report_path"] 3499 main_parse_config.return_value = ["pg_config", "report_path"]
2855 main_fetch_markets.return_value = [({"key": "market_config"},)] 3500 main_fetch_markets.return_value = [({"key": "market_config"},)]
2856 m = helper.get_user_market("config_path.ini", 1) 3501 m = main.get_user_market("config_path.ini", 1)
2857 3502
2858 self.assertIsInstance(m, market.Market) 3503 self.assertIsInstance(m, market.Market)
2859 self.assertFalse(m.debug) 3504 self.assertFalse(m.debug)
@@ -2861,141 +3506,100 @@ class HelperTest(WebMockTestCase):
2861 with self.subTest(debug=True): 3506 with self.subTest(debug=True):
2862 main_parse_config.return_value = ["pg_config", "report_path"] 3507 main_parse_config.return_value = ["pg_config", "report_path"]
2863 main_fetch_markets.return_value = [({"key": "market_config"},)] 3508 main_fetch_markets.return_value = [({"key": "market_config"},)]
2864 m = helper.get_user_market("config_path.ini", 1, debug=True) 3509 m = main.get_user_market("config_path.ini", 1, debug=True)
2865 3510
2866 self.assertIsInstance(m, market.Market) 3511 self.assertIsInstance(m, market.Market)
2867 self.assertTrue(m.debug) 3512 self.assertTrue(m.debug)
2868 3513
2869 def test_main_store_report(self): 3514 def test_process(self):
2870 file_open = mock.mock_open() 3515 with mock.patch("market.Market") as market_mock,\
2871 with self.subTest(file=None), mock.patch("__main__.open", file_open):
2872 helper.main_store_report(None, 1, self.m)
2873 file_open.assert_not_called()
2874
2875 file_open = mock.mock_open()
2876 with self.subTest(file="present"), mock.patch("helper.open", file_open),\
2877 mock.patch.object(helper, "datetime") as time_mock:
2878 time_mock.now.return_value = datetime.datetime(2018, 2, 25)
2879 self.m.report.to_json.return_value = "json_content"
2880
2881 helper.main_store_report("present", 1, self.m)
2882
2883 file_open.assert_any_call("present/2018-02-25T00:00:00_1.json", "w")
2884 file_open().write.assert_called_once_with("json_content")
2885 self.m.report.to_json.assert_called_once_with()
2886
2887 with self.subTest(file="error"),\
2888 mock.patch("helper.open") as file_open,\
2889 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: 3516 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
2890 file_open.side_effect = FileNotFoundError
2891 3517
2892 helper.main_store_report("error", 1, self.m) 3518 args_mock = mock.Mock()
2893 3519 args_mock.action = "action"
2894 self.assertRegex(stdout_mock.getvalue(), "impossible to store report file: FileNotFoundError;") 3520 args_mock.config = "config"
2895 3521 args_mock.user = "user"
2896 @mock.patch("helper.Processor.process") 3522 args_mock.debug = "debug"
2897 def test_main_process_market(self, process): 3523 args_mock.before = "before"
2898 with self.subTest(before=False, after=False): 3524 args_mock.after = "after"
2899 m = mock.Mock() 3525 self.assertEqual("", stdout_mock.getvalue())
2900 helper.main_process_market(m, None)
2901
2902 process.assert_not_called()
2903
2904 process.reset_mock()
2905 with self.subTest(before=True, after=False):
2906 helper.main_process_market(m, None, before=True)
2907
2908 process.assert_called_once_with("sell_all", steps="before")
2909
2910 process.reset_mock()
2911 with self.subTest(before=False, after=True):
2912 helper.main_process_market(m, None, after=True)
2913
2914 process.assert_called_once_with("sell_all", steps="after")
2915 3526
2916 process.reset_mock() 3527 main.process("config", 1, "report_path", args_mock)
2917 with self.subTest(before=True, after=True):
2918 helper.main_process_market(m, None, before=True, after=True)
2919 3528
2920 process.assert_has_calls([ 3529 market_mock.from_config.assert_has_calls([
2921 mock.call("sell_all", steps="before"), 3530 mock.call("config", debug="debug", user_id=1, report_path="report_path"),
2922 mock.call("sell_all", steps="after"), 3531 mock.call().process("action", before="before", after="after"),
2923 ]) 3532 ])
2924 3533
2925 process.reset_mock() 3534 with self.subTest(exception=True):
2926 with self.subTest(action="print_balances"),\ 3535 market_mock.from_config.side_effect = Exception("boo")
2927 mock.patch("helper.print_balances") as print_balances: 3536 main.process("config", 1, "report_path", args_mock)
2928 helper.main_process_market("user", ["print_balances"]) 3537 self.assertEqual("Exception: boo\n", stdout_mock.getvalue())
2929
2930 process.assert_not_called()
2931 print_balances.assert_called_once_with("user")
2932
2933 with self.subTest(action="print_orders"),\
2934 mock.patch("helper.print_orders") as print_orders,\
2935 mock.patch("helper.print_balances") as print_balances:
2936 helper.main_process_market("user", ["print_orders", "print_balances"])
2937
2938 process.assert_not_called()
2939 print_orders.assert_called_once_with("user")
2940 print_balances.assert_called_once_with("user")
2941
2942 with self.subTest(action="unknown"),\
2943 self.assertRaises(NotImplementedError):
2944 helper.main_process_market("user", ["unknown"])
2945 3538
2946 @mock.patch.object(helper, "psycopg2") 3539 def test_main(self):
2947 def test_fetch_markets(self, psycopg2): 3540 with self.subTest(parallel=False):
2948 connect_mock = mock.Mock() 3541 with mock.patch("main.parse_args") as parse_args,\
2949 cursor_mock = mock.MagicMock() 3542 mock.patch("main.parse_config") as parse_config,\
2950 cursor_mock.__iter__.return_value = ["row_1", "row_2"] 3543 mock.patch("main.fetch_markets") as fetch_markets,\
2951 3544 mock.patch("main.process") as process:
2952 connect_mock.cursor.return_value = cursor_mock
2953 psycopg2.connect.return_value = connect_mock
2954
2955 with self.subTest(user=None):
2956 rows = list(helper.main_fetch_markets({"foo": "bar"}, None))
2957
2958 psycopg2.connect.assert_called_once_with(foo="bar")
2959 cursor_mock.execute.assert_called_once_with("SELECT config,user_id FROM market_configs")
2960
2961 self.assertEqual(["row_1", "row_2"], rows)
2962
2963 psycopg2.connect.reset_mock()
2964 cursor_mock.execute.reset_mock()
2965 with self.subTest(user=1):
2966 rows = list(helper.main_fetch_markets({"foo": "bar"}, 1))
2967 3545
2968 psycopg2.connect.assert_called_once_with(foo="bar") 3546 args_mock = mock.Mock()
2969 cursor_mock.execute.assert_called_once_with("SELECT config,user_id FROM market_configs WHERE user_id = %s", 1) 3547 args_mock.parallel = False
3548 args_mock.config = "config"
3549 args_mock.user = "user"
3550 parse_args.return_value = args_mock
2970 3551
2971 self.assertEqual(["row_1", "row_2"], rows) 3552 parse_config.return_value = ["pg_config", "report_path"]
2972 3553
2973 @mock.patch.object(helper.sys, "exit") 3554 fetch_markets.return_value = [["config1", 1], ["config2", 2]]
2974 def test_main_parse_args(self, exit):
2975 with self.subTest(config="config.ini"):
2976 args = helper.main_parse_args([])
2977 self.assertEqual("config.ini", args.config)
2978 self.assertFalse(args.before)
2979 self.assertFalse(args.after)
2980 self.assertFalse(args.debug)
2981 3555
2982 args = helper.main_parse_args(["--before", "--after", "--debug"]) 3556 main.main(["Foo", "Bar"])
2983 self.assertTrue(args.before)
2984 self.assertTrue(args.after)
2985 self.assertTrue(args.debug)
2986 3557
2987 exit.assert_not_called() 3558 parse_args.assert_called_with(["Foo", "Bar"])
3559 parse_config.assert_called_with("config")
3560 fetch_markets.assert_called_with("pg_config", "user")
2988 3561
2989 with self.subTest(config="inexistant"),\ 3562 self.assertEqual(2, process.call_count)
2990 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: 3563 process.assert_has_calls([
2991 args = helper.main_parse_args(["--config", "foo.bar"]) 3564 mock.call("config1", 1, "report_path", args_mock),
2992 exit.assert_called_once_with(1) 3565 mock.call("config2", 2, "report_path", args_mock),
2993 self.assertEqual("no config file found, exiting\n", stdout_mock.getvalue()) 3566 ])
3567 with self.subTest(parallel=True):
3568 with mock.patch("main.parse_args") as parse_args,\
3569 mock.patch("main.parse_config") as parse_config,\
3570 mock.patch("main.fetch_markets") as fetch_markets,\
3571 mock.patch("main.process") as process,\
3572 mock.patch("store.Portfolio.start_worker") as start:
3573
3574 args_mock = mock.Mock()
3575 args_mock.parallel = True
3576 args_mock.config = "config"
3577 args_mock.user = "user"
3578 parse_args.return_value = args_mock
3579
3580 parse_config.return_value = ["pg_config", "report_path"]
3581
3582 fetch_markets.return_value = [["config1", 1], ["config2", 2]]
3583
3584 main.main(["Foo", "Bar"])
3585
3586 parse_args.assert_called_with(["Foo", "Bar"])
3587 parse_config.assert_called_with("config")
3588 fetch_markets.assert_called_with("pg_config", "user")
3589
3590 start.assert_called_once_with()
3591 self.assertEqual(2, process.call_count)
3592 process.assert_has_calls([
3593 mock.call.__bool__(),
3594 mock.call("config1", 1, "report_path", args_mock),
3595 mock.call.__bool__(),
3596 mock.call("config2", 2, "report_path", args_mock),
3597 ])
2994 3598
2995 @mock.patch.object(helper.sys, "exit") 3599 @mock.patch.object(main.sys, "exit")
2996 @mock.patch("helper.configparser") 3600 @mock.patch("main.configparser")
2997 @mock.patch("helper.os") 3601 @mock.patch("main.os")
2998 def test_main_parse_config(self, os, configparser, exit): 3602 def test_parse_config(self, os, configparser, exit):
2999 with self.subTest(pg_config=True, report_path=None): 3603 with self.subTest(pg_config=True, report_path=None):
3000 config_mock = mock.MagicMock() 3604 config_mock = mock.MagicMock()
3001 configparser.ConfigParser.return_value = config_mock 3605 configparser.ConfigParser.return_value = config_mock
@@ -3005,7 +3609,7 @@ class HelperTest(WebMockTestCase):
3005 config_mock.__contains__.side_effect = config 3609 config_mock.__contains__.side_effect = config
3006 config_mock.__getitem__.return_value = "pg_config" 3610 config_mock.__getitem__.return_value = "pg_config"
3007 3611
3008 result = helper.main_parse_config("configfile") 3612 result = main.parse_config("configfile")
3009 3613
3010 config_mock.read.assert_called_with("configfile") 3614 config_mock.read.assert_called_with("configfile")
3011 3615
@@ -3023,7 +3627,7 @@ class HelperTest(WebMockTestCase):
3023 ] 3627 ]
3024 3628
3025 os.path.exists.return_value = False 3629 os.path.exists.return_value = False
3026 result = helper.main_parse_config("configfile") 3630 result = main.parse_config("configfile")
3027 3631
3028 config_mock.read.assert_called_with("configfile") 3632 config_mock.read.assert_called_with("configfile")
3029 self.assertEqual(["pg_config", "report_path"], result) 3633 self.assertEqual(["pg_config", "report_path"], result)
@@ -3034,46 +3638,71 @@ class HelperTest(WebMockTestCase):
3034 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: 3638 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
3035 config_mock = mock.MagicMock() 3639 config_mock = mock.MagicMock()
3036 configparser.ConfigParser.return_value = config_mock 3640 configparser.ConfigParser.return_value = config_mock
3037 result = helper.main_parse_config("configfile") 3641 result = main.parse_config("configfile")
3038 3642
3039 config_mock.read.assert_called_with("configfile") 3643 config_mock.read.assert_called_with("configfile")
3040 exit.assert_called_once_with(1) 3644 exit.assert_called_once_with(1)
3041 self.assertEqual("no configuration for postgresql in config file\n", stdout_mock.getvalue()) 3645 self.assertEqual("no configuration for postgresql in config file\n", stdout_mock.getvalue())
3042 3646
3647 @mock.patch.object(main.sys, "exit")
3648 def test_parse_args(self, exit):
3649 with self.subTest(config="config.ini"):
3650 args = main.parse_args([])
3651 self.assertEqual("config.ini", args.config)
3652 self.assertFalse(args.before)
3653 self.assertFalse(args.after)
3654 self.assertFalse(args.debug)
3043 3655
3044 def test_print_orders(self): 3656 args = main.parse_args(["--before", "--after", "--debug"])
3045 helper.print_orders(self.m) 3657 self.assertTrue(args.before)
3658 self.assertTrue(args.after)
3659 self.assertTrue(args.debug)
3046 3660
3047 self.m.report.log_stage.assert_called_with("print_orders") 3661 exit.assert_not_called()
3048 self.m.balances.fetch_balances.assert_called_with(tag="print_orders") 3662
3049 self.m.prepare_trades.assert_called_with(base_currency="BTC", 3663 with self.subTest(config="inexistant"),\
3050 compute_value="average") 3664 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
3051 self.m.trades.prepare_orders.assert_called_with(compute_value="average") 3665 args = main.parse_args(["--config", "foo.bar"])
3666 exit.assert_called_once_with(1)
3667 self.assertEqual("no config file found, exiting\n", stdout_mock.getvalue())
3668
3669 @mock.patch.object(main, "psycopg2")
3670 def test_fetch_markets(self, psycopg2):
3671 connect_mock = mock.Mock()
3672 cursor_mock = mock.MagicMock()
3673 cursor_mock.__iter__.return_value = ["row_1", "row_2"]
3674
3675 connect_mock.cursor.return_value = cursor_mock
3676 psycopg2.connect.return_value = connect_mock
3677
3678 with self.subTest(user=None):
3679 rows = list(main.fetch_markets({"foo": "bar"}, None))
3680
3681 psycopg2.connect.assert_called_once_with(foo="bar")
3682 cursor_mock.execute.assert_called_once_with("SELECT config,user_id FROM market_configs")
3683
3684 self.assertEqual(["row_1", "row_2"], rows)
3685
3686 psycopg2.connect.reset_mock()
3687 cursor_mock.execute.reset_mock()
3688 with self.subTest(user=1):
3689 rows = list(main.fetch_markets({"foo": "bar"}, 1))
3690
3691 psycopg2.connect.assert_called_once_with(foo="bar")
3692 cursor_mock.execute.assert_called_once_with("SELECT config,user_id FROM market_configs WHERE user_id = %s", 1)
3693
3694 self.assertEqual(["row_1", "row_2"], rows)
3052 3695
3053 def test_print_balances(self):
3054 self.m.balances.in_currency.return_value = {
3055 "BTC": portfolio.Amount("BTC", "0.65"),
3056 "ETH": portfolio.Amount("BTC", "0.3"),
3057 }
3058
3059 helper.print_balances(self.m)
3060
3061 self.m.report.log_stage.assert_called_once_with("print_balances")
3062 self.m.balances.fetch_balances.assert_called_with()
3063 self.m.report.print_log.assert_has_calls([
3064 mock.call("total:"),
3065 mock.call(portfolio.Amount("BTC", "0.95")),
3066 ])
3067 3696
3068@unittest.skipUnless("unit" in limits, "Unit skipped") 3697@unittest.skipUnless("unit" in limits, "Unit skipped")
3069class ProcessorTest(WebMockTestCase): 3698class ProcessorTest(WebMockTestCase):
3070 def test_values(self): 3699 def test_values(self):
3071 processor = helper.Processor(self.m) 3700 processor = market.Processor(self.m)
3072 3701
3073 self.assertEqual(self.m, processor.market) 3702 self.assertEqual(self.m, processor.market)
3074 3703
3075 def test_run_action(self): 3704 def test_run_action(self):
3076 processor = helper.Processor(self.m) 3705 processor = market.Processor(self.m)
3077 3706
3078 with mock.patch.object(processor, "parse_args") as parse_args: 3707 with mock.patch.object(processor, "parse_args") as parse_args:
3079 method_mock = mock.Mock() 3708 method_mock = mock.Mock()
@@ -3087,10 +3716,10 @@ class ProcessorTest(WebMockTestCase):
3087 3716
3088 processor.run_action("wait_for_recent", "bar", "baz") 3717 processor.run_action("wait_for_recent", "bar", "baz")
3089 3718
3090 method_mock.assert_called_with(self.m, foo="bar") 3719 method_mock.assert_called_with(foo="bar")
3091 3720
3092 def test_select_step(self): 3721 def test_select_step(self):
3093 processor = helper.Processor(self.m) 3722 processor = market.Processor(self.m)
3094 3723
3095 scenario = processor.scenarios["sell_all"] 3724 scenario = processor.scenarios["sell_all"]
3096 3725
@@ -3103,9 +3732,9 @@ class ProcessorTest(WebMockTestCase):
3103 with self.assertRaises(TypeError): 3732 with self.assertRaises(TypeError):
3104 processor.select_steps(scenario, ["wait"]) 3733 processor.select_steps(scenario, ["wait"])
3105 3734
3106 @mock.patch("helper.Processor.process_step") 3735 @mock.patch("market.Processor.process_step")
3107 def test_process(self, process_step): 3736 def test_process(self, process_step):
3108 processor = helper.Processor(self.m) 3737 processor = market.Processor(self.m)
3109 3738
3110 processor.process("sell_all", foo="bar") 3739 processor.process("sell_all", foo="bar")
3111 self.assertEqual(3, process_step.call_count) 3740 self.assertEqual(3, process_step.call_count)
@@ -3126,11 +3755,11 @@ class ProcessorTest(WebMockTestCase):
3126 ccxt = mock.Mock(spec=market.ccxt.poloniexE) 3755 ccxt = mock.Mock(spec=market.ccxt.poloniexE)
3127 m = market.Market(ccxt) 3756 m = market.Market(ccxt)
3128 3757
3129 processor = helper.Processor(m) 3758 processor = market.Processor(m)
3130 3759
3131 method, arguments = processor.method_arguments("wait_for_recent") 3760 method, arguments = processor.method_arguments("wait_for_recent")
3132 self.assertEqual(portfolio.Portfolio.wait_for_recent, method) 3761 self.assertEqual(market.Portfolio.wait_for_recent, method)
3133 self.assertEqual(["delta"], arguments) 3762 self.assertEqual(["delta", "poll"], arguments)
3134 3763
3135 method, arguments = processor.method_arguments("prepare_trades") 3764 method, arguments = processor.method_arguments("prepare_trades")
3136 self.assertEqual(m.prepare_trades, method) 3765 self.assertEqual(m.prepare_trades, method)
@@ -3152,7 +3781,7 @@ class ProcessorTest(WebMockTestCase):
3152 self.assertEqual(m.trades.close_trades, method) 3781 self.assertEqual(m.trades.close_trades, method)
3153 3782
3154 def test_process_step(self): 3783 def test_process_step(self):
3155 processor = helper.Processor(self.m) 3784 processor = market.Processor(self.m)
3156 3785
3157 with mock.patch.object(processor, "run_action") as run_action: 3786 with mock.patch.object(processor, "run_action") as run_action:
3158 step = processor.scenarios["sell_needed"][1] 3787 step = processor.scenarios["sell_needed"][1]
@@ -3186,7 +3815,7 @@ class ProcessorTest(WebMockTestCase):
3186 self.m.balances.fetch_balances.assert_not_called() 3815 self.m.balances.fetch_balances.assert_not_called()
3187 3816
3188 def test_parse_args(self): 3817 def test_parse_args(self):
3189 processor = helper.Processor(self.m) 3818 processor = market.Processor(self.m)
3190 3819
3191 with mock.patch.object(processor, "method_arguments") as method_arguments: 3820 with mock.patch.object(processor, "method_arguments") as method_arguments:
3192 method_mock = mock.Mock() 3821 method_mock = mock.Mock()
@@ -3312,7 +3941,7 @@ class AcceptanceTest(WebMockTestCase):
3312 market = mock.Mock() 3941 market = mock.Mock()
3313 market.fetch_all_balances.return_value = fetch_balance 3942 market.fetch_all_balances.return_value = fetch_balance
3314 market.fetch_ticker.side_effect = fetch_ticker 3943 market.fetch_ticker.side_effect = fetch_ticker
3315 with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition): 3944 with mock.patch.object(market.Portfolio, "repartition", return_value=repartition):
3316 # Action 1 3945 # Action 1
3317 helper.prepare_trades(market) 3946 helper.prepare_trades(market)
3318 3947
@@ -3391,7 +4020,7 @@ class AcceptanceTest(WebMockTestCase):
3391 "amount": "10", "total": "1" 4020 "amount": "10", "total": "1"
3392 } 4021 }
3393 ] 4022 ]
3394 with mock.patch.object(portfolio.time, "sleep") as sleep: 4023 with mock.patch.object(market.time, "sleep") as sleep:
3395 # Action 4 4024 # Action 4
3396 helper.follow_orders(verbose=False) 4025 helper.follow_orders(verbose=False)
3397 4026
@@ -3432,7 +4061,7 @@ class AcceptanceTest(WebMockTestCase):
3432 } 4061 }
3433 market.fetch_all_balances.return_value = fetch_balance 4062 market.fetch_all_balances.return_value = fetch_balance
3434 4063
3435 with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition): 4064 with mock.patch.object(market.Portfolio, "repartition", return_value=repartition):
3436 # Action 5 4065 # Action 5
3437 helper.prepare_trades(market, only="acquire", compute_value="average") 4066 helper.prepare_trades(market, only="acquire", compute_value="average")
3438 4067
@@ -3504,7 +4133,7 @@ class AcceptanceTest(WebMockTestCase):
3504 # TODO 4133 # TODO
3505 # portfolio.TradeStore.run_orders() 4134 # portfolio.TradeStore.run_orders()
3506 4135
3507 with mock.patch.object(portfolio.time, "sleep") as sleep: 4136 with mock.patch.object(market.time, "sleep") as sleep:
3508 # Action 8 4137 # Action 8
3509 helper.follow_orders(verbose=False) 4138 helper.follow_orders(verbose=False)
3510 4139