aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2018-03-08 02:04:50 +0100
committerIsmaël Bouya <ismael.bouya@normalesup.org>2018-03-08 11:35:15 +0100
commitada1b5f109ebaa6f3adb7cd87b007c6db891811c (patch)
tree7b4b40d2c08f21793da62b1708c649ec72fe5bae
parenta18ce2f16973155c81f983643aba675f62dea7af (diff)
downloadTrader-ada1b5f109ebaa6f3adb7cd87b007c6db891811c.tar.gz
Trader-ada1b5f109ebaa6f3adb7cd87b007c6db891811c.tar.zst
Trader-ada1b5f109ebaa6f3adb7cd87b007c6db891811c.zip
Move Portfolio to store and cleanup methods
Make report stored in portfolio class instead of market
-rw-r--r--market.py8
-rw-r--r--portfolio.py79
-rw-r--r--store.py85
-rw-r--r--test.py203
4 files changed, 177 insertions, 198 deletions
diff --git a/market.py b/market.py
index 6c14ae2..388dea0 100644
--- a/market.py
+++ b/market.py
@@ -312,7 +312,7 @@ class Processor:
312 import inspect 312 import inspect
313 313
314 if action == "wait_for_recent": 314 if action == "wait_for_recent":
315 method = portfolio.Portfolio.wait_for_recent 315 method = Portfolio.wait_for_recent
316 elif action == "prepare_trades": 316 elif action == "prepare_trades":
317 method = self.market.prepare_trades 317 method = self.market.prepare_trades
318 elif action == "prepare_orders": 318 elif action == "prepare_orders":
@@ -345,8 +345,4 @@ class Processor:
345 def run_action(self, action, default_args, kwargs): 345 def run_action(self, action, default_args, kwargs):
346 method, args = self.parse_args(action, default_args, kwargs) 346 method, args = self.parse_args(action, default_args, kwargs)
347 347
348 if action == "wait_for_recent": 348 method(**args)
349 method(self.market, **args)
350 else:
351 method(**args)
352
diff --git a/portfolio.py b/portfolio.py
index 0f2c011..554b34f 100644
--- a/portfolio.py
+++ b/portfolio.py
@@ -1,87 +1,10 @@
1import time 1from datetime import datetime
2from datetime import datetime, timedelta
3from decimal import Decimal as D, ROUND_DOWN 2from decimal import Decimal as D, ROUND_DOWN
4from json import JSONDecodeError
5from simplejson.errors import JSONDecodeError as SimpleJSONDecodeError
6from ccxt import ExchangeError, InsufficientFunds, ExchangeNotAvailable, InvalidOrder, OrderNotCached, OrderNotFound 3from ccxt import ExchangeError, InsufficientFunds, ExchangeNotAvailable, InvalidOrder, OrderNotCached, OrderNotFound
7from retry import retry 4from retry import retry
8import requests
9 5
10# FIXME: correctly handle web call timeouts 6# FIXME: correctly handle web call timeouts
11 7
12class Portfolio:
13 URL = "https://cryptoportfolio.io/wp-content/uploads/portfolio/json/cryptoportfolio.json"
14 liquidities = {}
15 data = None
16 last_date = None
17
18 @classmethod
19 def wait_for_recent(cls, market, delta=4):
20 cls.repartition(market, refetch=True)
21 while cls.last_date is None or datetime.now() - cls.last_date > timedelta(delta):
22 time.sleep(30)
23 market.report.print_log("Attempt to fetch up-to-date cryptoportfolio")
24 cls.repartition(market, refetch=True)
25
26 @classmethod
27 def repartition(cls, market, liquidity="medium", refetch=False):
28 cls.parse_cryptoportfolio(market, refetch=refetch)
29 liquidities = cls.liquidities[liquidity]
30 return liquidities[cls.last_date]
31
32 @classmethod
33 def get_cryptoportfolio(cls, market):
34 try:
35 r = requests.get(cls.URL)
36 market.report.log_http_request(r.request.method,
37 r.request.url, r.request.body, r.request.headers, r)
38 except Exception as e:
39 market.report.log_error("get_cryptoportfolio", exception=e)
40 return
41 try:
42 cls.data = r.json(parse_int=D, parse_float=D)
43 except (JSONDecodeError, SimpleJSONDecodeError):
44 cls.data = None
45
46 @classmethod
47 def parse_cryptoportfolio(cls, market, refetch=False):
48 if refetch or cls.data is None:
49 cls.get_cryptoportfolio(market)
50
51 def filter_weights(weight_hash):
52 if weight_hash[1][0] == 0:
53 return False
54 if weight_hash[0] == "_row":
55 return False
56 return True
57
58 def clean_weights(i):
59 def clean_weights_(h):
60 if h[0].endswith("s"):
61 return [h[0][0:-1], (h[1][i], "short")]
62 else:
63 return [h[0], (h[1][i], "long")]
64 return clean_weights_
65
66 def parse_weights(portfolio_hash):
67 weights_hash = portfolio_hash["weights"]
68 weights = {}
69 for i in range(len(weights_hash["_row"])):
70 date = datetime.strptime(weights_hash["_row"][i], "%Y-%m-%d")
71 weights[date] = dict(filter(
72 filter_weights,
73 map(clean_weights(i), weights_hash.items())))
74 return weights
75
76 high_liquidity = parse_weights(cls.data["portfolio_1"])
77 medium_liquidity = parse_weights(cls.data["portfolio_2"])
78
79 cls.liquidities = {
80 "medium": medium_liquidity,
81 "high": high_liquidity,
82 }
83 cls.last_date = max(max(medium_liquidity.keys()), max(high_liquidity.keys()))
84
85class Computation: 8class Computation:
86 computations = { 9 computations = {
87 "default": lambda x, y: x[y], 10 "default": lambda x, y: x[y],
diff --git a/store.py b/store.py
index d25dd35..78dfe2d 100644
--- a/store.py
+++ b/store.py
@@ -1,10 +1,14 @@
1import time
2import requests
1import portfolio 3import portfolio
2import simplejson as json 4import simplejson as json
3from decimal import Decimal as D, ROUND_DOWN 5from decimal import Decimal as D, ROUND_DOWN
4from datetime import date, datetime 6from datetime import date, datetime, timedelta
5import inspect 7import inspect
8from json import JSONDecodeError
9from simplejson.errors import JSONDecodeError as SimpleJSONDecodeError
6 10
7__all__ = ["BalanceStore", "ReportStore", "TradeStore"] 11__all__ = ["Portfolio", "BalanceStore", "ReportStore", "TradeStore"]
8 12
9class ReportStore: 13class ReportStore:
10 def __init__(self, market, verbose_print=True): 14 def __init__(self, market, verbose_print=True):
@@ -213,7 +217,7 @@ class BalanceStore:
213 217
214 def dispatch_assets(self, amount, liquidity="medium", repartition=None): 218 def dispatch_assets(self, amount, liquidity="medium", repartition=None):
215 if repartition is None: 219 if repartition is None:
216 repartition = portfolio.Portfolio.repartition(self.market, liquidity=liquidity) 220 repartition = Portfolio.repartition(liquidity=liquidity)
217 sum_ratio = sum([v[0] for k, v in repartition.items()]) 221 sum_ratio = sum([v[0] for k, v in repartition.items()])
218 amounts = {} 222 amounts = {}
219 for currency, (ptt, trade_type) in repartition.items(): 223 for currency, (ptt, trade_type) in repartition.items():
@@ -301,4 +305,79 @@ class TradeStore:
301 for order in self.all_orders(state="open"): 305 for order in self.all_orders(state="open"):
302 order.get_status() 306 order.get_status()
303 307
308class Portfolio:
309 URL = "https://cryptoportfolio.io/wp-content/uploads/portfolio/json/cryptoportfolio.json"
310 liquidities = {}
311 data = None
312 last_date = None
313 report = ReportStore(None)
314
315 @classmethod
316 def wait_for_recent(cls, delta=4):
317 cls.get_cryptoportfolio()
318 while cls.last_date is None or datetime.now() - cls.last_date > timedelta(delta):
319 time.sleep(30)
320 cls.report.print_log("Attempt to fetch up-to-date cryptoportfolio")
321 cls.get_cryptoportfolio(refetch=True)
322
323 @classmethod
324 def repartition(cls, liquidity="medium"):
325 cls.get_cryptoportfolio()
326 liquidities = cls.liquidities[liquidity]
327 return liquidities[cls.last_date]
328
329 @classmethod
330 def get_cryptoportfolio(cls, refetch=False):
331 if cls.data is not None and not refetch:
332 return
333 try:
334 r = requests.get(cls.URL)
335 cls.report.log_http_request(r.request.method,
336 r.request.url, r.request.body, r.request.headers, r)
337 except Exception as e:
338 cls.report.log_error("get_cryptoportfolio", exception=e)
339 return
340 try:
341 cls.data = r.json(parse_int=D, parse_float=D)
342 cls.parse_cryptoportfolio()
343 except (JSONDecodeError, SimpleJSONDecodeError):
344 cls.data = None
345 cls.liquidities = {}
346
347 @classmethod
348 def parse_cryptoportfolio(cls):
349 def filter_weights(weight_hash):
350 if weight_hash[1][0] == 0:
351 return False
352 if weight_hash[0] == "_row":
353 return False
354 return True
355
356 def clean_weights(i):
357 def clean_weights_(h):
358 if h[0].endswith("s"):
359 return [h[0][0:-1], (h[1][i], "short")]
360 else:
361 return [h[0], (h[1][i], "long")]
362 return clean_weights_
363
364 def parse_weights(portfolio_hash):
365 weights_hash = portfolio_hash["weights"]
366 weights = {}
367 for i in range(len(weights_hash["_row"])):
368 date = datetime.strptime(weights_hash["_row"][i], "%Y-%m-%d")
369 weights[date] = dict(filter(
370 filter_weights,
371 map(clean_weights(i), weights_hash.items())))
372 return weights
373
374 high_liquidity = parse_weights(cls.data["portfolio_1"])
375 medium_liquidity = parse_weights(cls.data["portfolio_2"])
376
377 cls.liquidities = {
378 "medium": medium_liquidity,
379 "high": high_liquidity,
380 }
381 cls.last_date = max(max(medium_liquidity.keys()), max(high_liquidity.keys()))
382
304 383
diff --git a/test.py b/test.py
index bbe0697..d4432f6 100644
--- a/test.py
+++ b/test.py
@@ -7,7 +7,7 @@ from unittest import mock
7import requests 7import requests
8import requests_mock 8import requests_mock
9from io import StringIO 9from io import StringIO
10import portfolio, market, main 10import portfolio, market, main, store
11 11
12limits = ["acceptance", "unit"] 12limits = ["acceptance", "unit"]
13for test_type in limits: 13for test_type in limits:
@@ -32,7 +32,11 @@ class WebMockTestCase(unittest.TestCase):
32 self.m.debug = False 32 self.m.debug = False
33 33
34 self.patchers = [ 34 self.patchers = [
35 mock.patch.multiple(portfolio.Portfolio, last_date=None, data=None, liquidities={}), 35 mock.patch.multiple(market.Portfolio,
36 last_date=None,
37 data=None,
38 liquidities={},
39 report=mock.Mock()),
36 mock.patch.multiple(portfolio.Computation, 40 mock.patch.multiple(portfolio.Computation,
37 computations=portfolio.Computation.computations), 41 computations=portfolio.Computation.computations),
38 ] 42 ]
@@ -439,57 +443,64 @@ class poloniexETest(unittest.TestCase):
439 443
440@unittest.skipUnless("unit" in limits, "Unit skipped") 444@unittest.skipUnless("unit" in limits, "Unit skipped")
441class PortfolioTest(WebMockTestCase): 445class PortfolioTest(WebMockTestCase):
442 def fill_data(self):
443 if self.json_response is not None:
444 portfolio.Portfolio.data = self.json_response
445
446 def setUp(self): 446 def setUp(self):
447 super(PortfolioTest, self).setUp() 447 super(PortfolioTest, self).setUp()
448 448
449 with open("test_samples/test_portfolio.json") as example: 449 with open("test_samples/test_portfolio.json") as example:
450 self.json_response = example.read() 450 self.json_response = example.read()
451 451
452 self.wm.get(portfolio.Portfolio.URL, text=self.json_response) 452 self.wm.get(market.Portfolio.URL, text=self.json_response)
453 453
454 def test_get_cryptoportfolio(self): 454 @mock.patch.object(market.Portfolio, "parse_cryptoportfolio")
455 self.wm.get(portfolio.Portfolio.URL, [ 455 def test_get_cryptoportfolio(self, parse_cryptoportfolio):
456 self.wm.get(market.Portfolio.URL, [
456 {"text":'{ "foo": "bar" }', "status_code": 200}, 457 {"text":'{ "foo": "bar" }', "status_code": 200},
457 {"text": "System Error", "status_code": 500}, 458 {"text": "System Error", "status_code": 500},
458 {"exc": requests.exceptions.ConnectTimeout}, 459 {"exc": requests.exceptions.ConnectTimeout},
459 ]) 460 ])
460 portfolio.Portfolio.get_cryptoportfolio(self.m) 461 market.Portfolio.get_cryptoportfolio()
461 self.assertIn("foo", portfolio.Portfolio.data) 462 self.assertIn("foo", market.Portfolio.data)
462 self.assertEqual("bar", portfolio.Portfolio.data["foo"]) 463 self.assertEqual("bar", market.Portfolio.data["foo"])
463 self.assertTrue(self.wm.called) 464 self.assertTrue(self.wm.called)
464 self.assertEqual(1, self.wm.call_count) 465 self.assertEqual(1, self.wm.call_count)
465 self.m.report.log_error.assert_not_called() 466 market.Portfolio.report.log_error.assert_not_called()
466 self.m.report.log_http_request.assert_called_once() 467 market.Portfolio.report.log_http_request.assert_called_once()
467 self.m.report.log_http_request.reset_mock() 468 parse_cryptoportfolio.assert_called_once_with()
468 469 market.Portfolio.report.log_http_request.reset_mock()
469 portfolio.Portfolio.get_cryptoportfolio(self.m) 470 parse_cryptoportfolio.reset_mock()
470 self.assertIsNone(portfolio.Portfolio.data) 471 market.Portfolio.data = None
472
473 market.Portfolio.get_cryptoportfolio()
474 self.assertIsNone(market.Portfolio.data)
471 self.assertEqual(2, self.wm.call_count) 475 self.assertEqual(2, self.wm.call_count)
472 self.m.report.log_error.assert_not_called() 476 parse_cryptoportfolio.assert_not_called()
473 self.m.report.log_http_request.assert_called_once() 477 market.Portfolio.report.log_error.assert_not_called()
474 self.m.report.log_http_request.reset_mock() 478 market.Portfolio.report.log_http_request.assert_called_once()
475 479 market.Portfolio.report.log_http_request.reset_mock()
480 parse_cryptoportfolio.reset_mock()
481
482 market.Portfolio.data = "Foo"
483 market.Portfolio.get_cryptoportfolio()
484 self.assertEqual(2, self.wm.call_count)
485 parse_cryptoportfolio.assert_not_called()
476 486
477 portfolio.Portfolio.data = "Foo" 487 market.Portfolio.get_cryptoportfolio(refetch=True)
478 portfolio.Portfolio.get_cryptoportfolio(self.m) 488 self.assertEqual("Foo", market.Portfolio.data)
479 self.assertEqual("Foo", portfolio.Portfolio.data)
480 self.assertEqual(3, self.wm.call_count) 489 self.assertEqual(3, self.wm.call_count)
481 self.m.report.log_error.assert_called_once_with("get_cryptoportfolio", 490 market.Portfolio.report.log_error.assert_called_once_with("get_cryptoportfolio",
482 exception=mock.ANY) 491 exception=mock.ANY)
483 self.m.report.log_http_request.assert_not_called() 492 market.Portfolio.report.log_http_request.assert_not_called()
484 493
485 def test_parse_cryptoportfolio(self): 494 def test_parse_cryptoportfolio(self):
486 portfolio.Portfolio.parse_cryptoportfolio(self.m) 495 market.Portfolio.data = store.json.loads(self.json_response, parse_int=D,
496 parse_float=D)
497 market.Portfolio.parse_cryptoportfolio()
487 498
488 self.assertListEqual( 499 self.assertListEqual(
489 ["medium", "high"], 500 ["medium", "high"],
490 list(portfolio.Portfolio.liquidities.keys())) 501 list(market.Portfolio.liquidities.keys()))
491 502
492 liquidities = portfolio.Portfolio.liquidities 503 liquidities = market.Portfolio.liquidities
493 self.assertEqual(10, len(liquidities["medium"].keys())) 504 self.assertEqual(10, len(liquidities["medium"].keys()))
494 self.assertEqual(10, len(liquidities["high"].keys())) 505 self.assertEqual(10, len(liquidities["high"].keys()))
495 506
@@ -517,94 +528,64 @@ class PortfolioTest(WebMockTestCase):
517 'XCP': (D("0.1"), "long"), 528 'XCP': (D("0.1"), "long"),
518 } 529 }
519 self.assertDictEqual(expected, liquidities["medium"][date]) 530 self.assertDictEqual(expected, liquidities["medium"][date])
520 self.assertEqual(portfolio.datetime(2018, 1, 15), portfolio.Portfolio.last_date) 531 self.assertEqual(portfolio.datetime(2018, 1, 15), market.Portfolio.last_date)
521 532
522 self.m.report.log_http_request.assert_called_once_with("GET", 533 @mock.patch.object(market.Portfolio, "get_cryptoportfolio")
523 portfolio.Portfolio.URL, None, mock.ANY, mock.ANY) 534 def test_repartition(self, get_cryptoportfolio):
524 self.m.report.log_http_request.reset_mock() 535 market.Portfolio.liquidities = {
525 536 "medium": {
526 # It doesn't refetch the data when available 537 "2018-03-01": "medium_2018-03-01",
527 portfolio.Portfolio.parse_cryptoportfolio(self.m) 538 "2018-03-08": "medium_2018-03-08",
528 self.m.report.log_http_request.assert_not_called() 539 },
529 540 "high": {
530 self.assertEqual(1, self.wm.call_count) 541 "2018-03-01": "high_2018-03-01",
531 542 "2018-03-08": "high_2018-03-08",
532 portfolio.Portfolio.parse_cryptoportfolio(self.m, refetch=True) 543 }
533 self.assertEqual(2, self.wm.call_count)
534 self.m.report.log_http_request.assert_called_once()
535
536 def test_repartition(self):
537 expected_medium = {
538 'BTC': (D("1.1102e-16"), "long"),
539 'USDT': (D("0.1"), "long"),
540 'ETC': (D("0.1"), "long"),
541 'FCT': (D("0.1"), "long"),
542 'OMG': (D("0.1"), "long"),
543 'STEEM': (D("0.1"), "long"),
544 'STRAT': (D("0.1"), "long"),
545 'XEM': (D("0.1"), "long"),
546 'XMR': (D("0.1"), "long"),
547 'XVC': (D("0.1"), "long"),
548 'ZRX': (D("0.1"), "long"),
549 }
550 expected_high = {
551 'USDT': (D("0.1226"), "long"),
552 'BTC': (D("0.1429"), "long"),
553 'ETC': (D("0.1127"), "long"),
554 'ETH': (D("0.1569"), "long"),
555 'FCT': (D("0.3341"), "long"),
556 'GAS': (D("0.1308"), "long"),
557 } 544 }
545 market.Portfolio.last_date = "2018-03-08"
558 546
559 self.assertEqual(expected_medium, portfolio.Portfolio.repartition(self.m)) 547 self.assertEqual("medium_2018-03-08", market.Portfolio.repartition())
560 self.assertEqual(expected_medium, portfolio.Portfolio.repartition(self.m, liquidity="medium")) 548 get_cryptoportfolio.assert_called_once_with()
561 self.assertEqual(expected_high, portfolio.Portfolio.repartition(self.m, liquidity="high")) 549 self.assertEqual("medium_2018-03-08", market.Portfolio.repartition(liquidity="medium"))
562 550 self.assertEqual("high_2018-03-08", market.Portfolio.repartition(liquidity="high"))
563 self.assertEqual(1, self.wm.call_count)
564
565 portfolio.Portfolio.repartition(self.m)
566 self.assertEqual(1, self.wm.call_count)
567
568 portfolio.Portfolio.repartition(self.m, refetch=True)
569 self.assertEqual(2, self.wm.call_count)
570 self.m.report.log_http_request.assert_called()
571 self.assertEqual(2, self.m.report.log_http_request.call_count)
572 551
573 @mock.patch.object(portfolio.time, "sleep") 552 @mock.patch.object(market.time, "sleep")
574 @mock.patch.object(portfolio.Portfolio, "repartition") 553 @mock.patch.object(market.Portfolio, "get_cryptoportfolio")
575 def test_wait_for_recent(self, repartition, sleep): 554 def test_wait_for_recent(self, get_cryptoportfolio, sleep):
576 self.call_count = 0 555 self.call_count = 0
577 def _repartition(market, refetch): 556 def _get(refetch=False):
578 self.assertEqual(self.m, market) 557 if self.call_count != 0:
579 self.assertTrue(refetch) 558 self.assertTrue(refetch)
559 else:
560 self.assertFalse(refetch)
580 self.call_count += 1 561 self.call_count += 1
581 portfolio.Portfolio.last_date = portfolio.datetime.now()\ 562 market.Portfolio.last_date = store.datetime.now()\
582 - portfolio.timedelta(10)\ 563 - store.timedelta(10)\
583 + portfolio.timedelta(self.call_count) 564 + store.timedelta(self.call_count)
584 repartition.side_effect = _repartition 565 get_cryptoportfolio.side_effect = _get
585 566
586 portfolio.Portfolio.wait_for_recent(self.m) 567 market.Portfolio.wait_for_recent()
587 sleep.assert_called_with(30) 568 sleep.assert_called_with(30)
588 self.assertEqual(6, sleep.call_count) 569 self.assertEqual(6, sleep.call_count)
589 self.assertEqual(7, repartition.call_count) 570 self.assertEqual(7, get_cryptoportfolio.call_count)
590 self.m.report.print_log.assert_called_with("Attempt to fetch up-to-date cryptoportfolio") 571 market.Portfolio.report.print_log.assert_called_with("Attempt to fetch up-to-date cryptoportfolio")
591 572
592 sleep.reset_mock() 573 sleep.reset_mock()
593 repartition.reset_mock() 574 get_cryptoportfolio.reset_mock()
594 portfolio.Portfolio.last_date = None 575 market.Portfolio.last_date = None
595 self.call_count = 0 576 self.call_count = 0
596 portfolio.Portfolio.wait_for_recent(self.m, delta=15) 577 market.Portfolio.wait_for_recent(delta=15)
597 sleep.assert_not_called() 578 sleep.assert_not_called()
598 self.assertEqual(1, repartition.call_count) 579 self.assertEqual(1, get_cryptoportfolio.call_count)
599 580
600 sleep.reset_mock() 581 sleep.reset_mock()
601 repartition.reset_mock() 582 get_cryptoportfolio.reset_mock()
602 portfolio.Portfolio.last_date = None 583 market.Portfolio.last_date = None
603 self.call_count = 0 584 self.call_count = 0
604 portfolio.Portfolio.wait_for_recent(self.m, delta=1) 585 market.Portfolio.wait_for_recent(delta=1)
605 sleep.assert_called_with(30) 586 sleep.assert_called_with(30)
606 self.assertEqual(9, sleep.call_count) 587 self.assertEqual(9, sleep.call_count)
607 self.assertEqual(10, repartition.call_count) 588 self.assertEqual(10, get_cryptoportfolio.call_count)
608 589
609@unittest.skipUnless("unit" in limits, "Unit skipped") 590@unittest.skipUnless("unit" in limits, "Unit skipped")
610class AmountTest(WebMockTestCase): 591class AmountTest(WebMockTestCase):
@@ -1047,7 +1028,7 @@ class MarketTest(WebMockTestCase):
1047 self.assertEqual("Foo", m.fetch_fees()) 1028 self.assertEqual("Foo", m.fetch_fees())
1048 self.ccxt.fetch_fees.assert_not_called() 1029 self.ccxt.fetch_fees.assert_not_called()
1049 1030
1050 @mock.patch.object(portfolio.Portfolio, "repartition") 1031 @mock.patch.object(market.Portfolio, "repartition")
1051 @mock.patch.object(market.Market, "get_ticker") 1032 @mock.patch.object(market.Market, "get_ticker")
1052 @mock.patch.object(market.TradeStore, "compute_trades") 1033 @mock.patch.object(market.TradeStore, "compute_trades")
1053 def test_prepare_trades(self, compute_trades, get_ticker, repartition): 1034 def test_prepare_trades(self, compute_trades, get_ticker, repartition):
@@ -1098,7 +1079,7 @@ class MarketTest(WebMockTestCase):
1098 m.report.log_balances.assert_called_once_with(tag="tag") 1079 m.report.log_balances.assert_called_once_with(tag="tag")
1099 1080
1100 1081
1101 @mock.patch.object(portfolio.time, "sleep") 1082 @mock.patch.object(market.time, "sleep")
1102 @mock.patch.object(market.TradeStore, "all_orders") 1083 @mock.patch.object(market.TradeStore, "all_orders")
1103 def test_follow_orders(self, all_orders, time_mock): 1084 def test_follow_orders(self, all_orders, time_mock):
1104 for debug, sleep in [ 1085 for debug, sleep in [
@@ -1693,7 +1674,7 @@ class BalanceStoreTest(WebMockTestCase):
1693 self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(balance_store.currencies())) 1674 self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(balance_store.currencies()))
1694 self.m.report.log_balances.assert_called_with(tag="foo") 1675 self.m.report.log_balances.assert_called_with(tag="foo")
1695 1676
1696 @mock.patch.object(portfolio.Portfolio, "repartition") 1677 @mock.patch.object(market.Portfolio, "repartition")
1697 def test_dispatch_assets(self, repartition): 1678 def test_dispatch_assets(self, repartition):
1698 self.m.ccxt.fetch_all_balances.return_value = self.fetch_balance 1679 self.m.ccxt.fetch_all_balances.return_value = self.fetch_balance
1699 1680
@@ -1710,7 +1691,7 @@ class BalanceStoreTest(WebMockTestCase):
1710 repartition.return_value = repartition_hash 1691 repartition.return_value = repartition_hash
1711 1692
1712 amounts = balance_store.dispatch_assets(portfolio.Amount("BTC", "11.1")) 1693 amounts = balance_store.dispatch_assets(portfolio.Amount("BTC", "11.1"))
1713 repartition.assert_called_with(self.m, liquidity="medium") 1694 repartition.assert_called_with(liquidity="medium")
1714 self.assertIn("XEM", balance_store.currencies()) 1695 self.assertIn("XEM", balance_store.currencies())
1715 self.assertEqual(D("2.6"), amounts["BTC"].value) 1696 self.assertEqual(D("2.6"), amounts["BTC"].value)
1716 self.assertEqual(D("7.5"), amounts["XEM"].value) 1697 self.assertEqual(D("7.5"), amounts["XEM"].value)
@@ -3505,7 +3486,7 @@ class ProcessorTest(WebMockTestCase):
3505 3486
3506 processor.run_action("wait_for_recent", "bar", "baz") 3487 processor.run_action("wait_for_recent", "bar", "baz")
3507 3488
3508 method_mock.assert_called_with(self.m, foo="bar") 3489 method_mock.assert_called_with(foo="bar")
3509 3490
3510 def test_select_step(self): 3491 def test_select_step(self):
3511 processor = market.Processor(self.m) 3492 processor = market.Processor(self.m)
@@ -3547,7 +3528,7 @@ class ProcessorTest(WebMockTestCase):
3547 processor = market.Processor(m) 3528 processor = market.Processor(m)
3548 3529
3549 method, arguments = processor.method_arguments("wait_for_recent") 3530 method, arguments = processor.method_arguments("wait_for_recent")
3550 self.assertEqual(portfolio.Portfolio.wait_for_recent, method) 3531 self.assertEqual(market.Portfolio.wait_for_recent, method)
3551 self.assertEqual(["delta"], arguments) 3532 self.assertEqual(["delta"], arguments)
3552 3533
3553 method, arguments = processor.method_arguments("prepare_trades") 3534 method, arguments = processor.method_arguments("prepare_trades")
@@ -3730,7 +3711,7 @@ class AcceptanceTest(WebMockTestCase):
3730 market = mock.Mock() 3711 market = mock.Mock()
3731 market.fetch_all_balances.return_value = fetch_balance 3712 market.fetch_all_balances.return_value = fetch_balance
3732 market.fetch_ticker.side_effect = fetch_ticker 3713 market.fetch_ticker.side_effect = fetch_ticker
3733 with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition): 3714 with mock.patch.object(market.Portfolio, "repartition", return_value=repartition):
3734 # Action 1 3715 # Action 1
3735 helper.prepare_trades(market) 3716 helper.prepare_trades(market)
3736 3717
@@ -3809,7 +3790,7 @@ class AcceptanceTest(WebMockTestCase):
3809 "amount": "10", "total": "1" 3790 "amount": "10", "total": "1"
3810 } 3791 }
3811 ] 3792 ]
3812 with mock.patch.object(portfolio.time, "sleep") as sleep: 3793 with mock.patch.object(market.time, "sleep") as sleep:
3813 # Action 4 3794 # Action 4
3814 helper.follow_orders(verbose=False) 3795 helper.follow_orders(verbose=False)
3815 3796
@@ -3850,7 +3831,7 @@ class AcceptanceTest(WebMockTestCase):
3850 } 3831 }
3851 market.fetch_all_balances.return_value = fetch_balance 3832 market.fetch_all_balances.return_value = fetch_balance
3852 3833
3853 with mock.patch.object(portfolio.Portfolio, "repartition", return_value=repartition): 3834 with mock.patch.object(market.Portfolio, "repartition", return_value=repartition):
3854 # Action 5 3835 # Action 5
3855 helper.prepare_trades(market, only="acquire", compute_value="average") 3836 helper.prepare_trades(market, only="acquire", compute_value="average")
3856 3837
@@ -3922,7 +3903,7 @@ class AcceptanceTest(WebMockTestCase):
3922 # TODO 3903 # TODO
3923 # portfolio.TradeStore.run_orders() 3904 # portfolio.TradeStore.run_orders()
3924 3905
3925 with mock.patch.object(portfolio.time, "sleep") as sleep: 3906 with mock.patch.object(market.time, "sleep") as sleep:
3926 # Action 8 3907 # Action 8
3927 helper.follow_orders(verbose=False) 3908 helper.follow_orders(verbose=False)
3928 3909