aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2018-05-05 14:41:03 +0200
committerIsmaël Bouya <ismael.bouya@normalesup.org>2018-05-05 14:41:03 +0200
commitbb127bc87c2b2880469bfab230415c85e589421a (patch)
tree90cce895ea70aa3127fe764ac4da7e1d510b1589
parent56c3a6078a2740d43072dfe30f07b17b442e3cc2 (diff)
downloadTrader-bb127bc87c2b2880469bfab230415c85e589421a.tar.gz
Trader-bb127bc87c2b2880469bfab230415c85e589421a.tar.zst
Trader-bb127bc87c2b2880469bfab230415c85e589421a.zip
Add checkpoints when fetching balance
-rw-r--r--market.py54
-rw-r--r--store.py10
-rw-r--r--tests/test_market.py32
-rw-r--r--tests/test_store.py10
4 files changed, 77 insertions, 29 deletions
diff --git a/market.py b/market.py
index 5876071..7996a58 100644
--- a/market.py
+++ b/market.py
@@ -254,8 +254,7 @@ class Processor:
254 { 254 {
255 "name": "print_balances", 255 "name": "print_balances",
256 "number": 1, 256 "number": 1,
257 "fetch_balances": ["begin"], 257 "fetch_balances_begin": { "log_tickers": True, "add_portfolio": True },
258 "fetch_balances_args": { "add_portfolio": True },
259 "print_tickers": { "base_currency": "BTC" }, 258 "print_tickers": { "base_currency": "BTC" },
260 } 259 }
261 ], 260 ],
@@ -272,25 +271,37 @@ class Processor:
272 "number": 2, 271 "number": 2,
273 "before": False, 272 "before": False,
274 "after": True, 273 "after": True,
275 "fetch_balances": ["begin"], 274 "fetch_balances_begin": {},
276 "prepare_trades": { "compute_value": "average" }, 275 "prepare_trades": { "compute_value": "average" },
277 "prepare_orders": { "compute_value": "average" }, 276 "prepare_orders": { "compute_value": "average" },
278 }, 277 },
279 ], 278 ],
280 "sell_needed": [ 279 "sell_needed": [
281 { 280 {
282 "name": "wait", 281 "name": "print_balances",
283 "number": 0, 282 "number": 0,
283 "before": True,
284 "after": False,
285 "fetch_balances_begin": {
286 "checkpoint": "end",
287 "log_tickers": True,
288 "add_portfolio": True
289 },
290 },
291 {
292 "name": "wait",
293 "number": 1,
284 "before": False, 294 "before": False,
285 "after": True, 295 "after": True,
286 "wait_for_recent": {}, 296 "wait_for_recent": {},
287 }, 297 },
288 { 298 {
289 "name": "sell", 299 "name": "sell",
290 "number": 1, 300 "number": 2,
291 "before": False, 301 "before": False,
292 "after": True, 302 "after": True,
293 "fetch_balances": ["begin", "end"], 303 "fetch_balances_begin": {},
304 "fetch_balances_end": {},
294 "prepare_trades": {}, 305 "prepare_trades": {},
295 "prepare_orders": { "only": "dispose", "compute_value": "average" }, 306 "prepare_orders": { "only": "dispose", "compute_value": "average" },
296 "run_orders": {}, 307 "run_orders": {},
@@ -299,10 +310,14 @@ class Processor:
299 }, 310 },
300 { 311 {
301 "name": "buy", 312 "name": "buy",
302 "number": 2, 313 "number": 3,
303 "before": False, 314 "before": False,
304 "after": True, 315 "after": True,
305 "fetch_balances": ["begin", "end"], 316 "fetch_balances_begin": {},
317 "fetch_balances_end": {
318 "checkpoint": "begin",
319 "log_tickers": True
320 },
306 "prepare_trades": { "only": "acquire", "available_balance_only": True }, 321 "prepare_trades": { "only": "acquire", "available_balance_only": True },
307 "prepare_orders": { "only": "acquire", "compute_value": "average" }, 322 "prepare_orders": { "only": "acquire", "compute_value": "average" },
308 "move_balances": {}, 323 "move_balances": {},
@@ -317,7 +332,12 @@ class Processor:
317 "number": 1, 332 "number": 1,
318 "before": True, 333 "before": True,
319 "after": False, 334 "after": False,
320 "fetch_balances": ["begin", "end"], 335 "fetch_balances_begin": {
336 "checkpoint": "end",
337 "log_tickers": True,
338 "add_portfolio": True
339 },
340 "fetch_balances_end": {},
321 "prepare_trades": { "repartition": { "base_currency": (1, "long") } }, 341 "prepare_trades": { "repartition": { "base_currency": (1, "long") } },
322 "prepare_orders": { "compute_value": "average" }, 342 "prepare_orders": { "compute_value": "average" },
323 "run_orders": {}, 343 "run_orders": {},
@@ -336,7 +356,11 @@ class Processor:
336 "number": 3, 356 "number": 3,
337 "before": False, 357 "before": False,
338 "after": True, 358 "after": True,
339 "fetch_balances": ["begin", "end"], 359 "fetch_balances_begin": {},
360 "fetch_balances_end": {
361 "checkpoint": "begin",
362 "log_tickers": True
363 },
340 "prepare_trades": { "available_balance_only": True }, 364 "prepare_trades": { "available_balance_only": True },
341 "prepare_orders": { "compute_value": "average" }, 365 "prepare_orders": { "compute_value": "average" },
342 "move_balances": {}, 366 "move_balances": {},
@@ -388,18 +412,18 @@ class Processor:
388 process_name = "process_{}__{}_{}".format(scenario_name, step["number"], step["name"]) 412 process_name = "process_{}__{}_{}".format(scenario_name, step["number"], step["name"])
389 self.market.report.log_stage("{}_begin".format(process_name)) 413 self.market.report.log_stage("{}_begin".format(process_name))
390 414
391 fetch_args = step.get("fetch_balances_args", {}) 415 if "fetch_balances_begin" in step:
392 if "begin" in step.get("fetch_balances", []):
393 self.market.balances.fetch_balances(tag="{}_begin".format(process_name), 416 self.market.balances.fetch_balances(tag="{}_begin".format(process_name),
394 log_tickers=True, **fetch_args) 417 **step["fetch_balances_begin"])
395 418
396 for action in self.ordered_actions: 419 for action in self.ordered_actions:
397 if action in step: 420 if action in step:
398 self.run_action(action, step[action], kwargs) 421 self.run_action(action, step[action], kwargs)
399 422
400 if "end" in step.get("fetch_balances", []): 423 if "fetch_balances_end" in step:
401 self.market.balances.fetch_balances(tag="{}_end".format(process_name), 424 self.market.balances.fetch_balances(tag="{}_end".format(process_name),
402 log_tickers=True, **fetch_args) 425 **step["fetch_balances_end"])
426
403 self.market.report.log_stage("{}_end".format(process_name)) 427 self.market.report.log_stage("{}_end".format(process_name))
404 428
405 def method_arguments(self, action): 429 def method_arguments(self, action):
diff --git a/store.py b/store.py
index 76cfec8..2a71bed 100644
--- a/store.py
+++ b/store.py
@@ -99,7 +99,7 @@ class ReportStore:
99 "args": args, 99 "args": args,
100 }) 100 })
101 101
102 def log_balances(self, tag=None, tickers=None, 102 def log_balances(self, tag=None, checkpoint=None, tickers=None,
103 ticker_currency=None, compute_value=None, type=None): 103 ticker_currency=None, compute_value=None, type=None):
104 self.print_log("[Balance]") 104 self.print_log("[Balance]")
105 for currency, balance in self.market.balances.all.items(): 105 for currency, balance in self.market.balances.all.items():
@@ -108,6 +108,7 @@ class ReportStore:
108 log = { 108 log = {
109 "type": "balance", 109 "type": "balance",
110 "tag": tag, 110 "tag": tag,
111 "checkpoint": checkpoint,
111 "balances": self.market.balances.as_json() 112 "balances": self.market.balances.as_json()
112 } 113 }
113 114
@@ -303,7 +304,8 @@ class BalanceStore:
303 compute_value, type) 304 compute_value, type)
304 return amounts 305 return amounts
305 306
306 def fetch_balances(self, tag=None, add_portfolio=False, log_tickers=False, 307 def fetch_balances(self, tag=None, add_portfolio=False,
308 checkpoint=None, log_tickers=False,
307 ticker_currency="BTC", ticker_compute_value="average", ticker_type="total"): 309 ticker_currency="BTC", ticker_compute_value="average", ticker_type="total"):
308 all_balances = self.market.ccxt.fetch_all_balances() 310 all_balances = self.market.ccxt.fetch_all_balances()
309 for currency, balance in all_balances.items(): 311 for currency, balance in all_balances.items():
@@ -315,11 +317,11 @@ class BalanceStore:
315 self.all.setdefault(currency, portfolio.Balance(currency, {})) 317 self.all.setdefault(currency, portfolio.Balance(currency, {}))
316 if log_tickers: 318 if log_tickers:
317 tickers = self.in_currency(ticker_currency, compute_value=ticker_compute_value, type=ticker_type) 319 tickers = self.in_currency(ticker_currency, compute_value=ticker_compute_value, type=ticker_type)
318 self.market.report.log_balances(tag=tag, 320 self.market.report.log_balances(tag=tag, checkpoint=checkpoint,
319 tickers=tickers, ticker_currency=ticker_currency, 321 tickers=tickers, ticker_currency=ticker_currency,
320 compute_value=ticker_compute_value, type=ticker_type) 322 compute_value=ticker_compute_value, type=ticker_type)
321 else: 323 else:
322 self.market.report.log_balances(tag=tag) 324 self.market.report.log_balances(tag=tag, checkpoint=checkpoint)
323 325
324 def dispatch_assets(self, amount, liquidity="medium", repartition=None): 326 def dispatch_assets(self, amount, liquidity="medium", repartition=None):
325 if repartition is None: 327 if repartition is None:
diff --git a/tests/test_market.py b/tests/test_market.py
index 46fad53..37c009b 100644
--- a/tests/test_market.py
+++ b/tests/test_market.py
@@ -174,7 +174,7 @@ class MarketTest(WebMockTestCase):
174 base_currency='BTC', compute_value='average', 174 base_currency='BTC', compute_value='average',
175 available_balance_only=False, liquidity='medium', 175 available_balance_only=False, liquidity='medium',
176 only=None, repartition=None) 176 only=None, repartition=None)
177 m.report.log_balances.assert_called_once_with(tag="tag") 177 m.report.log_balances.assert_called_once_with(tag="tag", checkpoint=None)
178 178
179 compute_trades.reset_mock() 179 compute_trades.reset_mock()
180 with self.subTest(available_balance_only=True),\ 180 with self.subTest(available_balance_only=True),\
@@ -964,7 +964,7 @@ class ProcessorTest(WebMockTestCase):
964 process_step.reset_mock() 964 process_step.reset_mock()
965 965
966 processor.process("sell_needed", steps=["before", "after"]) 966 processor.process("sell_needed", steps=["before", "after"])
967 self.assertEqual(3, process_step.call_count) 967 self.assertEqual(4, process_step.call_count)
968 968
969 def test_method_arguments(self): 969 def test_method_arguments(self):
970 ccxt = mock.Mock(spec=market.ccxt.poloniexE) 970 ccxt = mock.Mock(spec=market.ccxt.poloniexE)
@@ -1002,17 +1002,17 @@ class ProcessorTest(WebMockTestCase):
1002 processor = market.Processor(self.m) 1002 processor = market.Processor(self.m)
1003 1003
1004 with mock.patch.object(processor, "run_action") as run_action: 1004 with mock.patch.object(processor, "run_action") as run_action:
1005 step = processor.scenarios["sell_needed"][1] 1005 step = processor.scenarios["sell_needed"][2]
1006 1006
1007 processor.process_step("foo", step, {"foo":"bar"}) 1007 processor.process_step("foo", step, {"foo":"bar"})
1008 1008
1009 self.m.report.log_stage.assert_has_calls([ 1009 self.m.report.log_stage.assert_has_calls([
1010 mock.call("process_foo__1_sell_begin"), 1010 mock.call("process_foo__2_sell_begin"),
1011 mock.call("process_foo__1_sell_end"), 1011 mock.call("process_foo__2_sell_end"),
1012 ]) 1012 ])
1013 self.m.balances.fetch_balances.assert_has_calls([ 1013 self.m.balances.fetch_balances.assert_has_calls([
1014 mock.call(tag="process_foo__1_sell_begin", log_tickers=True), 1014 mock.call(tag="process_foo__2_sell_begin"),
1015 mock.call(tag="process_foo__1_sell_end", log_tickers=True), 1015 mock.call(tag="process_foo__2_sell_end"),
1016 ]) 1016 ])
1017 1017
1018 self.assertEqual(5, run_action.call_count) 1018 self.assertEqual(5, run_action.call_count)
@@ -1030,6 +1030,24 @@ class ProcessorTest(WebMockTestCase):
1030 step = processor.scenarios["sell_needed"][0] 1030 step = processor.scenarios["sell_needed"][0]
1031 1031
1032 processor.process_step("foo", step, {"foo":"bar"}) 1032 processor.process_step("foo", step, {"foo":"bar"})
1033
1034 self.m.report.log_stage.assert_has_calls([
1035 mock.call("process_foo__0_print_balances_begin"),
1036 mock.call("process_foo__0_print_balances_end"),
1037 ])
1038 self.m.balances.fetch_balances.assert_has_calls([
1039 mock.call(add_portfolio=True, checkpoint='end',
1040 log_tickers=True,
1041 tag='process_foo__0_print_balances_begin')
1042 ])
1043
1044 self.assertEqual(0, run_action.call_count)
1045
1046 self.m.reset_mock()
1047 with mock.patch.object(processor, "run_action") as run_action:
1048 step = processor.scenarios["sell_needed"][1]
1049
1050 processor.process_step("foo", step, {"foo":"bar"})
1033 self.m.balances.fetch_balances.assert_not_called() 1051 self.m.balances.fetch_balances.assert_not_called()
1034 1052
1035 self.m.reset_mock() 1053 self.m.reset_mock()
diff --git a/tests/test_store.py b/tests/test_store.py
index ee7e063..58e76e0 100644
--- a/tests/test_store.py
+++ b/tests/test_store.py
@@ -380,7 +380,7 @@ class BalanceStoreTest(WebMockTestCase):
380 balance_store.fetch_balances(tag="foo") 380 balance_store.fetch_balances(tag="foo")
381 self.assertEqual(0, balance_store.all["ETC"].total) 381 self.assertEqual(0, balance_store.all["ETC"].total)
382 self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(balance_store.currencies())) 382 self.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(balance_store.currencies()))
383 self.m.report.log_balances.assert_called_with(tag="foo") 383 self.m.report.log_balances.assert_called_with(tag="foo", checkpoint=None)
384 384
385 with self.subTest(log_tickers=True),\ 385 with self.subTest(log_tickers=True),\
386 mock.patch.object(balance_store, "in_currency") as in_currency: 386 mock.patch.object(balance_store, "in_currency") as in_currency:
@@ -388,7 +388,7 @@ class BalanceStoreTest(WebMockTestCase):
388 balance_store.fetch_balances(log_tickers=True, ticker_currency="FOO", 388 balance_store.fetch_balances(log_tickers=True, ticker_currency="FOO",
389 ticker_compute_value="compute", ticker_type="type") 389 ticker_compute_value="compute", ticker_type="type")
390 self.m.report.log_balances.assert_called_with(compute_value='compute', 390 self.m.report.log_balances.assert_called_with(compute_value='compute',
391 tag=None, ticker_currency='FOO', tickers='tickers', 391 tag=None, checkpoint=None, ticker_currency='FOO', tickers='tickers',
392 type='type') 392 type='type')
393 393
394 balance_store = market.BalanceStore(self.m) 394 balance_store = market.BalanceStore(self.m)
@@ -425,7 +425,7 @@ class BalanceStoreTest(WebMockTestCase):
425 self.assertEqual(D("2.6"), amounts["BTC"].value) 425 self.assertEqual(D("2.6"), amounts["BTC"].value)
426 self.assertEqual(D("7.5"), amounts["XEM"].value) 426 self.assertEqual(D("7.5"), amounts["XEM"].value)
427 self.assertEqual(D("-1.0"), amounts["DASH"].value) 427 self.assertEqual(D("-1.0"), amounts["DASH"].value)
428 self.m.report.log_balances.assert_called_with(tag=None) 428 self.m.report.log_balances.assert_called_with(tag=None, checkpoint=None)
429 self.m.report.log_dispatch.assert_called_once_with(portfolio.Amount("BTC", 429 self.m.report.log_dispatch.assert_called_once_with(portfolio.Amount("BTC",
430 "11.1"), amounts, "medium", repartition_hash) 430 "11.1"), amounts, "medium", repartition_hash)
431 431
@@ -617,12 +617,14 @@ class ReportStoreTest(WebMockTestCase):
617 ]) 617 ])
618 add_log.assert_called_once_with({ 618 add_log.assert_called_once_with({
619 'type': 'balance', 619 'type': 'balance',
620 'checkpoint': None,
620 'balances': 'json', 621 'balances': 'json',
621 'tag': 'tag' 622 'tag': 'tag'
622 }) 623 })
623 add_redis_status.assert_called_once_with({ 624 add_redis_status.assert_called_once_with({
624 'type': 'balance', 625 'type': 'balance',
625 'balances': 'json', 626 'balances': 'json',
627 'checkpoint': None,
626 'tag': 'tag' 628 'tag': 'tag'
627 }) 629 })
628 add_log.reset_mock() 630 add_log.reset_mock()
@@ -639,6 +641,7 @@ class ReportStoreTest(WebMockTestCase):
639 type="total") 641 type="total")
640 add_log.assert_called_once_with({ 642 add_log.assert_called_once_with({
641 'type': 'balance', 643 'type': 'balance',
644 'checkpoint': None,
642 'balances': 'json', 645 'balances': 'json',
643 'tag': 'tag', 646 'tag': 'tag',
644 'tickers': { 647 'tickers': {
@@ -658,6 +661,7 @@ class ReportStoreTest(WebMockTestCase):
658 }) 661 })
659 add_redis_status.assert_called_once_with({ 662 add_redis_status.assert_called_once_with({
660 'type': 'balance', 663 'type': 'balance',
664 'checkpoint': None,
661 'balances': 'json', 665 'balances': 'json',
662 'tag': 'tag', 666 'tag': 'tag',
663 'tickers': { 667 'tickers': {