From 9db7d156833cd384baa64b6148b5c646bfcc41f8 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Isma=C3=ABl=20Bouya?= Date: Fri, 2 Mar 2018 13:59:25 +0100 Subject: [PATCH] Add processors Fixes https://git.immae.eu/mantisbt/view.php?id=45 --- helper.py | 189 ++++++++++++++++++++++++++++++++++++++++++------------ market.py | 22 +++---- test.py | 111 ++++++++------------------------ 3 files changed, 184 insertions(+), 138 deletions(-) diff --git a/helper.py b/helper.py index be480e2..f14fd58 100644 --- a/helper.py +++ b/helper.py @@ -127,10 +127,9 @@ def main_fetch_markets(pg_config, user): def main_process_market(user_market, actions, before=False, after=False): if len(actions or []) == 0: if before: - process_sell_all__1_all_sell(user_market) + Processor(user_market).process("sell_all", steps="before") if after: - process_sell_all__2_wait(user_market) - process_sell_all__3_all_buy(user_market) + Processor(user_market).process("sell_all", steps="after") else: for action in actions: if action in globals(): @@ -160,51 +159,159 @@ def print_balances(market, base_currency="BTC"): market.report.print_log("total:") market.report.print_log(sum(market.balances.in_currency(base_currency).values())) +class Processor: + scenarios = { + "sell_needed": [ + { + "name": "wait", + "number": 0, + "before": False, + "after": True, + "wait_for_recent": {}, + }, + { + "name": "sell", + "number": 1, + "before": False, + "after": True, + "fetch_balances": ["begin", "end"], + "prepare_trades": {}, + "prepare_orders": { "only": "dispose", "compute_value": "average" }, + "run_orders": {}, + "follow_orders": {}, + }, + { + "name": "buy", + "number": 2, + "before": False, + "after": True, + "fetch_balances": ["begin", "end"], + "prepare_trades": { "only": "acquire" }, + "prepare_orders": { "only": "acquire", "compute_value": "average" }, + "move_balances": {}, + "run_orders": {}, + "follow_orders": {}, + }, + ], + "sell_all": [ + { + "name": "all_sell", + "number": 1, + "before": True, + "after": False, + "fetch_balances": ["begin", "end"], + "prepare_trades": { "repartition": { "BTC": (1, "long") } }, + "prepare_orders": { "compute_value": "average" }, + "run_orders": {}, + "follow_orders": {}, + }, + { + "name": "wait", + "number": 2, + "before": False, + "after": True, + "wait_for_recent": {}, + }, + { + "name": "all_buy", + "number": 3, + "before": False, + "after": True, + "fetch_balances": ["begin", "end"], + "prepare_trades": {}, + "prepare_orders": { "compute_value": "average" }, + "move_balances": {}, + "run_orders": {}, + "follow_orders": {}, + }, + ] + } + + allowed_arguments = { + "wait_for_recent": ["delta"], + "prepare_trades": ["base_currency", "liquidity", "compute_value", "repartition", "only"], + "prepare_orders": ["only", "compute_value"], + "move_balances": [], + "run_orders": [], + "follow_orders": ["sleep"], + } + + def __init__(self, market): + self.market = market + + def select_steps(self, scenario, step): + if step == "all": + return scenario + elif step == "before" or step == "after": + return list(filter(lambda x: step in x and x[step], scenario)) + elif type(step) == int: + return [scenario[step-1]] + elif type(step) == str: + return list(filter(lambda x: x["name"] == step, scenario)) + else: + raise TypeError("Unknown step {}".format(step)) + + def process(self, scenario_name, steps="all", **kwargs): + scenario = self.scenarios[scenario_name] + selected_steps = [] + + if type(steps) == str or type(steps) == int: + selected_steps += self.select_steps(scenario, steps) + else: + for step in steps: + selected_steps += self.select_steps(scenario, step) + for step in selected_steps: + self.process_step(scenario_name, step, **kwargs) + + def process_step(self, scenario_name, step, **kwargs): + process_name = "process_{}__{}_{}".format(scenario_name, step["number"], step["name"]) + self.market.report.log_stage("{}_begin".format(process_name)) + if "begin" in step.get("fetch_balances", []): + self.market.balances.fetch_balances(tag="{}_begin".format(process_name)) + + for action in ["wait_for_recent", "prepare_trades", + "prepare_orders", "move_balances", "run_orders", + "follow_orders"]: + if action in step: + self.run_action(action, step[action], **kwargs) + + if "end" in step.get("fetch_balances", []): + self.market.balances.fetch_balances(tag="{}_end".format(process_name)) + self.market.report.log_stage("{}_end".format(process_name)) + + def run_action(self, action, default_args, **kwargs): + args = {k: v for k, v in {**default_args, **kwargs}.items() if k in self.allowed_arguments[action] } + + if action == "wait_for_recent": + portfolio.Portfolio.wait_for_recent(self.market, **args) + if action == "prepare_trades": + self.market.prepare_trades(**args) + if action == "prepare_orders": + self.market.trades.prepare_orders(**args) + if action == "move_balances": + self.market.move_balances(**args) + if action == "run_orders": + self.market.trades.run_orders(**args) + if action == "follow_orders": + self.market.follow_orders(**args) + def process_sell_needed__1_sell(market, liquidity="medium", base_currency="BTC"): - market.report.log_stage("process_sell_needed__1_sell_begin") - market.balances.fetch_balances(tag="process_sell_needed__1_sell_begin") - market.prepare_trades(liquidity=liquidity, base_currency=base_currency) - market.trades.prepare_orders(compute_value="average", only="dispose") - market.trades.run_orders() - market.follow_orders() - market.balances.fetch_balances(tag="process_sell_needed__1_sell_end") - market.report.log_stage("process_sell_needed__1_sell_end") + Processor(market).process("sell_needed", steps="sell", + liquidity=liquidity, base_currency=base_currency) def process_sell_needed__2_buy(market, liquidity="medium", base_currency="BTC"): - market.report.log_stage("process_sell_needed__2_buy_begin") - market.balances.fetch_balances(tag="process_sell_needed__2_buy_begin") - market.prepare_trades(base_currency=base_currency, liquidity=liquidity, only="acquire") - market.trades.prepare_orders(compute_value="average", only="acquire") - market.move_balances() - market.trades.run_orders() - market.follow_orders() - market.balances.fetch_balances(tag="process_sell_needed__2_buy_end") - market.report.log_stage("process_sell_needed__2_buy_end") + Processor(market).process("sell_needed", steps="buy", + liquidity=liquidity, base_currency=base_currency) def process_sell_all__1_all_sell(market, base_currency="BTC", liquidity="medium"): - market.report.log_stage("process_sell_all__1_all_sell_begin") - market.balances.fetch_balances(tag="process_sell_all__1_all_sell_begin") - market.prepare_trades_to_sell_all(base_currency=base_currency) - market.trades.prepare_orders(compute_value="average") - market.trades.run_orders() - market.follow_orders() - market.balances.fetch_balances(tag="process_sell_all__1_all_sell_end") - market.report.log_stage("process_sell_all__1_all_sell_end") + Processor(market).process("sell_all", steps="all_sell", + liquidity=liquidity, base_currency=base_currency) def process_sell_all__2_wait(market, liquidity="medium", base_currency="BTC"): - market.report.log_stage("process_sell_all__2_wait_begin") - portfolio.Portfolio.wait_for_recent(market) - market.report.log_stage("process_sell_all__2_wait_end") + Processor(market).process("sell_all", steps="wait", + liquidity=liquidity, base_currency=base_currency) def process_sell_all__3_all_buy(market, base_currency="BTC", liquidity="medium"): - market.report.log_stage("process_sell_all__3_all_buy_begin") - market.balances.fetch_balances(tag="process_sell_all__3_all_buy_begin") - market.prepare_trades(liquidity=liquidity, base_currency=base_currency) - market.trades.prepare_orders(compute_value="average") - market.move_balances() - market.trades.run_orders() - market.follow_orders() - market.balances.fetch_balances(tag="process_sell_all__3_all_buy_end") - market.report.log_stage("process_sell_all__3_all_buy_end") - + Processor(market).process("sell_all", steps="all_buy", + liquidity=liquidity, base_currency=base_currency) diff --git a/market.py b/market.py index ca365bd..3381d1e 100644 --- a/market.py +++ b/market.py @@ -128,20 +128,18 @@ class Market: order.trade.update_order(order, tick) self.report.log_stage("follow_orders_end") - def prepare_trades(self, base_currency="BTC", liquidity="medium", compute_value="average", only=None): + def prepare_trades(self, base_currency="BTC", liquidity="medium", + compute_value="average", repartition=None, only=None): + self.report.log_stage("prepare_trades", base_currency=base_currency, liquidity=liquidity, - compute_value=compute_value, only=only) - values_in_base = self.balances.in_currency(base_currency, compute_value=compute_value) - total_base_value = sum(values_in_base.values()) - new_repartition = self.balances.dispatch_assets(total_base_value, liquidity=liquidity) - self.trades.compute_trades(values_in_base, new_repartition, only=only) + compute_value=compute_value, only=only, + repartition=repartition) - def prepare_trades_to_sell_all(self, base_currency="BTC", compute_value="average"): - self.report.log_stage("prepare_trades_to_sell_all") - values_in_base = self.balances.in_currency(base_currency, compute_value=compute_value) + values_in_base = self.balances.in_currency(base_currency, + compute_value=compute_value) total_base_value = sum(values_in_base.values()) - new_repartition = self.balances.dispatch_assets(total_base_value, repartition={ base_currency: (1, "long") }) - self.trades.compute_trades(values_in_base, new_repartition) - + new_repartition = self.balances.dispatch_assets(total_base_value, + liquidity=liquidity, repartition=repartition) + self.trades.compute_trades(values_in_base, new_repartition, only=only) diff --git a/test.py b/test.py index 7ec8ba7..7409212 100644 --- a/test.py +++ b/test.py @@ -783,51 +783,9 @@ class MarketTest(WebMockTestCase): self.assertEqual(D("0.7575"), call[0][1]["XEM"].value) m.report.log_stage.assert_called_once_with("prepare_trades", base_currency='BTC', compute_value='average', - liquidity='medium', only=None) + liquidity='medium', only=None, repartition=None) m.report.log_balances.assert_called_once_with(tag="tag") - @mock.patch.object(portfolio.Portfolio, "repartition") - @mock.patch.object(market.Market, "get_ticker") - @mock.patch.object(market.TradeStore, "compute_trades") - def test_prepare_trades_to_sell_all(self, compute_trades, get_ticker, repartition): - def _get_ticker(c1, c2): - if c1 == "USDT" and c2 == "BTC": - return { "average": D("0.0001") } - if c1 == "XVG" and c2 == "BTC": - return { "average": D("0.000001") } - self.fail("Should be called with {}, {}".format(c1, c2)) - get_ticker.side_effect = _get_ticker - - with mock.patch("market.ReportStore"): - m = market.Market(self.ccxt) - self.ccxt.fetch_all_balances.return_value = { - "USDT": { - "exchange_free": D("10000.0"), - "exchange_used": D("0.0"), - "exchange_total": D("10000.0"), - "total": D("10000.0") - }, - "XVG": { - "exchange_free": D("10000.0"), - "exchange_used": D("0.0"), - "exchange_total": D("10000.0"), - "total": D("10000.0") - }, - } - - m.balances.fetch_balances(tag="tag") - - m.prepare_trades_to_sell_all() - - repartition.assert_not_called() - compute_trades.assert_called() - - call = compute_trades.call_args - self.assertEqual(1, call[0][0]["USDT"].value) - self.assertEqual(D("0.01"), call[0][0]["XVG"].value) - self.assertEqual(D("1.01"), call[0][1]["BTC"].value) - m.report.log_stage.assert_called_once_with("prepare_trades_to_sell_all") - m.report.log_balances.assert_called_once_with(tag="tag") @mock.patch.object(portfolio.time, "sleep") @mock.patch.object(market.TradeStore, "all_orders") @@ -2840,57 +2798,41 @@ class HelperTest(WebMockTestCase): self.assertRegex(stdout_mock.getvalue(), "impossible to store report file: FileNotFoundError;") - @mock.patch("helper.process_sell_all__1_all_sell") - @mock.patch("helper.process_sell_all__2_wait") - @mock.patch("helper.process_sell_all__3_all_buy") - def test_main_process_market(self, buy, wait, sell): + @mock.patch("helper.Processor.process") + def test_main_process_market(self, process): with self.subTest(before=False, after=False): - helper.main_process_market("user", None) - - wait.assert_not_called() - buy.assert_not_called() - sell.assert_not_called() + m = mock.Mock() + helper.main_process_market(m, None) + + process.assert_not_called() - buy.reset_mock() - wait.reset_mock() - sell.reset_mock() + process.reset_mock() with self.subTest(before=True, after=False): - helper.main_process_market("user", None, before=True) - - wait.assert_not_called() - buy.assert_not_called() - sell.assert_called_once_with("user") + helper.main_process_market(m, None, before=True) - buy.reset_mock() - wait.reset_mock() - sell.reset_mock() + process.assert_called_once_with("sell_all", steps="before") + + process.reset_mock() with self.subTest(before=False, after=True): - helper.main_process_market("user", None, after=True) + helper.main_process_market(m, None, after=True) - wait.assert_called_once_with("user") - buy.assert_called_once_with("user") - sell.assert_not_called() + process.assert_called_once_with("sell_all", steps="after") - buy.reset_mock() - wait.reset_mock() - sell.reset_mock() + process.reset_mock() with self.subTest(before=True, after=True): - helper.main_process_market("user", None, before=True, after=True) - - wait.assert_called_once_with("user") - buy.assert_called_once_with("user") - sell.assert_called_once_with("user") + helper.main_process_market(m, None, before=True, after=True) - buy.reset_mock() - wait.reset_mock() - sell.reset_mock() + process.assert_has_calls([ + mock.call("sell_all", steps="before"), + mock.call("sell_all", steps="after"), + ]) + + process.reset_mock() with self.subTest(action="print_balances"),\ mock.patch("helper.print_balances") as print_balances: helper.main_process_market("user", ["print_balances"]) - buy.assert_not_called() - wait.assert_not_called() - sell.assert_not_called() + process.assert_not_called() print_balances.assert_called_once_with("user") with self.subTest(action="print_orders"),\ @@ -2898,9 +2840,7 @@ class HelperTest(WebMockTestCase): mock.patch("helper.print_balances") as print_balances: helper.main_process_market("user", ["print_orders", "print_balances"]) - buy.assert_not_called() - wait.assert_not_called() - sell.assert_not_called() + process.assert_not_called() print_orders.assert_called_once_with("user") print_balances.assert_called_once_with("user") @@ -3074,7 +3014,8 @@ class HelperTest(WebMockTestCase): mock.call(tag="process_sell_all__1_all_sell_begin"), mock.call(tag="process_sell_all__1_all_sell_end"), ]) - self.m.prepare_trades_to_sell_all.assert_called_with(base_currency="BTC") + self.m.prepare_trades.assert_called_with(base_currency="BTC", + liquidity="medium", repartition={'BTC': (1, 'long')}) self.m.trades.prepare_orders.assert_called_with(compute_value="average") self.m.trades.run_orders.assert_called() self.m.follow_orders.assert_called() -- 2.41.0