diff options
-rw-r--r-- | main.py | 29 | ||||
-rw-r--r-- | market.py | 4 | ||||
-rw-r--r-- | tests/test_main.py | 65 | ||||
-rw-r--r-- | tests/test_market.py | 5 |
4 files changed, 73 insertions, 30 deletions
@@ -64,19 +64,30 @@ def get_user_market(config_path, user_id, debug=False): | |||
64 | args.append("--debug") | 64 | args.append("--debug") |
65 | args = parse_args(args) | 65 | args = parse_args(args) |
66 | parse_config(args) | 66 | parse_config(args) |
67 | market_id, market_config, user_id = list(fetch_markets(str(user_id)))[0] | 67 | market_id, market_config, user_id, options = list(fetch_markets(str(user_id)))[0] |
68 | return market.Market.from_config(market_config, args, user_id=user_id) | 68 | return market.Market.from_config(market_config, args, user_id=user_id, options=options) |
69 | 69 | ||
70 | def fetch_markets(user): | 70 | def fetch_markets(user): |
71 | cursor = dbs.psql.cursor() | 71 | cursor = dbs.psql.cursor() |
72 | 72 | ||
73 | if user is None: | 73 | if user is None: |
74 | cursor.execute("SELECT id,config,user_id FROM market_configs WHERE status='enabled'") | 74 | cursor.execute("SELECT id,config,user_id,portfolio_profile FROM market_configs_augmented WHERE status='enabled'") |
75 | else: | 75 | else: |
76 | cursor.execute("SELECT id,config,user_id FROM market_configs WHERE status='enabled' AND user_id = %s", [user]) | 76 | cursor.execute("SELECT id,config,user_id,portfolio_profile FROM market_configs_augmented WHERE status='enabled' AND user_id = %s", [user]) |
77 | 77 | ||
78 | for row in cursor: | 78 | for row in cursor: |
79 | yield row | 79 | options = { |
80 | "liquidity": parse_liquidity(row[3]) | ||
81 | } | ||
82 | yield row[0:3] + (options,) | ||
83 | |||
84 | def parse_liquidity(value): | ||
85 | if value == "high-liquidity": | ||
86 | return "high" | ||
87 | elif value == "medium-liquidity": | ||
88 | return "medium" | ||
89 | else: | ||
90 | return None | ||
80 | 91 | ||
81 | def parse_config(args): | 92 | def parse_config(args): |
82 | if args.db_host is not None: | 93 | if args.db_host is not None: |
@@ -152,11 +163,11 @@ def parse_args(argv): | |||
152 | parsed.action = ["sell_all"] | 163 | parsed.action = ["sell_all"] |
153 | return parsed | 164 | return parsed |
154 | 165 | ||
155 | def process(market_config, market_id, user_id, args): | 166 | def process(market_config, market_id, user_id, args, options): |
156 | try: | 167 | try: |
157 | market.Market\ | 168 | market.Market\ |
158 | .from_config(market_config, args, market_id=market_id, | 169 | .from_config(market_config, args, market_id=market_id, |
159 | user_id=user_id)\ | 170 | user_id=user_id, options=options)\ |
160 | .process(args.action, before=args.before, after=args.after) | 171 | .process(args.action, before=args.before, after=args.after) |
161 | except Exception as e: | 172 | except Exception as e: |
162 | print("{}: {}".format(e.__class__.__name__, e)) | 173 | print("{}: {}".format(e.__class__.__name__, e)) |
@@ -180,8 +191,8 @@ def main(argv): | |||
180 | else: | 191 | else: |
181 | process_ = process | 192 | process_ = process |
182 | 193 | ||
183 | for market_id, market_config, user_id in fetch_markets(args.user): | 194 | for market_id, market_config, user_id, options in fetch_markets(args.user): |
184 | process_(market_config, market_id, user_id, args) | 195 | process_(market_config, market_id, user_id, args, options) |
185 | 196 | ||
186 | if args.parallel: | 197 | if args.parallel: |
187 | for thread in threads: | 198 | for thread in threads: |
@@ -15,6 +15,7 @@ class Market: | |||
15 | report = None | 15 | report = None |
16 | trades = None | 16 | trades = None |
17 | balances = None | 17 | balances = None |
18 | options = None | ||
18 | 19 | ||
19 | def __init__(self, ccxt_instance, args, **kwargs): | 20 | def __init__(self, ccxt_instance, args, **kwargs): |
20 | self.args = args | 21 | self.args = args |
@@ -26,6 +27,7 @@ class Market: | |||
26 | self.balances = BalanceStore(self) | 27 | self.balances = BalanceStore(self) |
27 | self.processor = Processor(self) | 28 | self.processor = Processor(self) |
28 | 29 | ||
30 | self.options = kwargs.get("options", {}) | ||
29 | for key in ["user_id", "market_id"]: | 31 | for key in ["user_id", "market_id"]: |
30 | setattr(self, key, kwargs.get(key, None)) | 32 | setattr(self, key, kwargs.get(key, None)) |
31 | 33 | ||
@@ -466,7 +468,7 @@ class Processor: | |||
466 | 468 | ||
467 | def parse_args(self, action, default_args, kwargs): | 469 | def parse_args(self, action, default_args, kwargs): |
468 | method, allowed_arguments = self.method_arguments(action) | 470 | method, allowed_arguments = self.method_arguments(action) |
469 | args = {k: v for k, v in {**default_args, **kwargs}.items() if k in allowed_arguments } | 471 | args = {k: v for k, v in {**default_args, **kwargs, **self.market.options}.items() if k in allowed_arguments } |
470 | 472 | ||
471 | if "repartition" in args and "base_currency" in args["repartition"]: | 473 | if "repartition" in args and "base_currency" in args["repartition"]: |
472 | r = args["repartition"] | 474 | r = args["repartition"] |
diff --git a/tests/test_main.py b/tests/test_main.py index 0b4745f..3735a3b 100644 --- a/tests/test_main.py +++ b/tests/test_main.py | |||
@@ -103,23 +103,29 @@ class MainTest(WebMockTestCase): | |||
103 | mock.patch("main.parse_config") as main_parse_config: | 103 | mock.patch("main.parse_config") as main_parse_config: |
104 | with self.subTest(debug=False): | 104 | with self.subTest(debug=False): |
105 | main_parse_args.return_value = self.market_args() | 105 | main_parse_args.return_value = self.market_args() |
106 | main_fetch_markets.return_value = [(1, {"key": "market_config"}, 3)] | 106 | main_fetch_markets.return_value = [(1, {"key": "market_config"}, 3, { "foo": "bar" })] |
107 | m = main.get_user_market("config_path.ini", 1) | 107 | m = main.get_user_market("config_path.ini", 1) |
108 | 108 | ||
109 | self.assertIsInstance(m, market.Market) | 109 | self.assertIsInstance(m, market.Market) |
110 | self.assertFalse(m.debug) | 110 | self.assertFalse(m.debug) |
111 | self.assertEqual("bar", m.options["foo"]) | ||
111 | main_parse_args.assert_called_once_with(["--config", "config_path.ini"]) | 112 | main_parse_args.assert_called_once_with(["--config", "config_path.ini"]) |
112 | 113 | ||
113 | main_parse_args.reset_mock() | 114 | main_parse_args.reset_mock() |
114 | with self.subTest(debug=True): | 115 | with self.subTest(debug=True): |
115 | main_parse_args.return_value = self.market_args(debug=True) | 116 | main_parse_args.return_value = self.market_args(debug=True) |
116 | main_fetch_markets.return_value = [(1, {"key": "market_config"}, 3)] | 117 | main_fetch_markets.return_value = [(1, {"key": "market_config"}, 3, { "foo": "bar" })] |
117 | m = main.get_user_market("config_path.ini", 1, debug=True) | 118 | m = main.get_user_market("config_path.ini", 1, debug=True) |
118 | 119 | ||
119 | self.assertIsInstance(m, market.Market) | 120 | self.assertIsInstance(m, market.Market) |
120 | self.assertTrue(m.debug) | 121 | self.assertTrue(m.debug) |
121 | main_parse_args.assert_called_once_with(["--config", "config_path.ini", "--debug"]) | 122 | main_parse_args.assert_called_once_with(["--config", "config_path.ini", "--debug"]) |
122 | 123 | ||
124 | def test_parse_liquidity(self): | ||
125 | self.assertEqual("high", main.parse_liquidity("high-liquidity")) | ||
126 | self.assertEqual("medium", main.parse_liquidity("medium-liquidity")) | ||
127 | self.assertIsNone(main.parse_liquidity("foo")) | ||
128 | |||
123 | def test_process(self): | 129 | def test_process(self): |
124 | with mock.patch("market.Market") as market_mock,\ | 130 | with mock.patch("market.Market") as market_mock,\ |
125 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: | 131 | mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock: |
@@ -133,16 +139,16 @@ class MainTest(WebMockTestCase): | |||
133 | args_mock.after = "after" | 139 | args_mock.after = "after" |
134 | self.assertEqual("", stdout_mock.getvalue()) | 140 | self.assertEqual("", stdout_mock.getvalue()) |
135 | 141 | ||
136 | main.process("config", 3, 1, args_mock) | 142 | main.process("config", 3, 1, args_mock, "options") |
137 | 143 | ||
138 | market_mock.from_config.assert_has_calls([ | 144 | market_mock.from_config.assert_has_calls([ |
139 | mock.call("config", args_mock, market_id=3, user_id=1), | 145 | mock.call("config", args_mock, market_id=3, user_id=1, options="options"), |
140 | mock.call().process("action", before="before", after="after"), | 146 | mock.call().process("action", before="before", after="after"), |
141 | ]) | 147 | ]) |
142 | 148 | ||
143 | with self.subTest(exception=True): | 149 | with self.subTest(exception=True): |
144 | market_mock.from_config.side_effect = Exception("boo") | 150 | market_mock.from_config.side_effect = Exception("boo") |
145 | main.process(3, "config", 1, args_mock) | 151 | main.process(3, "config", 1, args_mock, "options") |
146 | self.assertEqual("Exception: boo\n", stdout_mock.getvalue()) | 152 | self.assertEqual("Exception: boo\n", stdout_mock.getvalue()) |
147 | 153 | ||
148 | def test_main(self): | 154 | def test_main(self): |
@@ -157,7 +163,10 @@ class MainTest(WebMockTestCase): | |||
157 | args_mock.user = "user" | 163 | args_mock.user = "user" |
158 | parse_args.return_value = args_mock | 164 | parse_args.return_value = args_mock |
159 | 165 | ||
160 | fetch_markets.return_value = [[3, "config1", 1], [1, "config2", 2]] | 166 | fetch_markets.return_value = [ |
167 | [3, "config1", 1, "options"], | ||
168 | [1, "config2", 2, "options"] | ||
169 | ] | ||
161 | 170 | ||
162 | main.main(["Foo", "Bar"]) | 171 | main.main(["Foo", "Bar"]) |
163 | 172 | ||
@@ -167,9 +176,10 @@ class MainTest(WebMockTestCase): | |||
167 | 176 | ||
168 | self.assertEqual(2, process.call_count) | 177 | self.assertEqual(2, process.call_count) |
169 | process.assert_has_calls([ | 178 | process.assert_has_calls([ |
170 | mock.call("config1", 3, 1, args_mock), | 179 | mock.call("config1", 3, 1, args_mock, "options"), |
171 | mock.call("config2", 1, 2, args_mock), | 180 | mock.call("config2", 1, 2, args_mock, "options"), |
172 | ]) | 181 | ]) |
182 | |||
173 | with self.subTest(parallel=True): | 183 | with self.subTest(parallel=True): |
174 | with mock.patch("main.parse_args") as parse_args,\ | 184 | with mock.patch("main.parse_args") as parse_args,\ |
175 | mock.patch("main.parse_config") as parse_config,\ | 185 | mock.patch("main.parse_config") as parse_config,\ |
@@ -183,7 +193,10 @@ class MainTest(WebMockTestCase): | |||
183 | args_mock.user = "user" | 193 | args_mock.user = "user" |
184 | parse_args.return_value = args_mock | 194 | parse_args.return_value = args_mock |
185 | 195 | ||
186 | fetch_markets.return_value = [[3, "config1", 1], [1, "config2", 2]] | 196 | fetch_markets.return_value = [ |
197 | [3, "config1", 1, "options"], | ||
198 | [1, "config2", 2, "options"] | ||
199 | ] | ||
187 | 200 | ||
188 | main.main(["Foo", "Bar"]) | 201 | main.main(["Foo", "Bar"]) |
189 | 202 | ||
@@ -196,10 +209,11 @@ class MainTest(WebMockTestCase): | |||
196 | self.assertEqual(2, process.call_count) | 209 | self.assertEqual(2, process.call_count) |
197 | process.assert_has_calls([ | 210 | process.assert_has_calls([ |
198 | mock.call.__bool__(), | 211 | mock.call.__bool__(), |
199 | mock.call("config1", 3, 1, args_mock), | 212 | mock.call("config1", 3, 1, args_mock, "options"), |
200 | mock.call.__bool__(), | 213 | mock.call.__bool__(), |
201 | mock.call("config2", 1, 2, args_mock), | 214 | mock.call("config2", 1, 2, args_mock, "options"), |
202 | ]) | 215 | ]) |
216 | |||
203 | with self.subTest(quiet=True): | 217 | with self.subTest(quiet=True): |
204 | with mock.patch("main.parse_args") as parse_args,\ | 218 | with mock.patch("main.parse_args") as parse_args,\ |
205 | mock.patch("main.parse_config") as parse_config,\ | 219 | mock.patch("main.parse_config") as parse_config,\ |
@@ -213,7 +227,10 @@ class MainTest(WebMockTestCase): | |||
213 | args_mock.user = "user" | 227 | args_mock.user = "user" |
214 | parse_args.return_value = args_mock | 228 | parse_args.return_value = args_mock |
215 | 229 | ||
216 | fetch_markets.return_value = [[3, "config1", 1], [1, "config2", 2]] | 230 | fetch_markets.return_value = [ |
231 | [3, "config1", 1, "options"], | ||
232 | [1, "config2", 2, "options"] | ||
233 | ] | ||
217 | 234 | ||
218 | main.main(["Foo", "Bar"]) | 235 | main.main(["Foo", "Bar"]) |
219 | 236 | ||
@@ -232,7 +249,10 @@ class MainTest(WebMockTestCase): | |||
232 | args_mock.user = "user" | 249 | args_mock.user = "user" |
233 | parse_args.return_value = args_mock | 250 | parse_args.return_value = args_mock |
234 | 251 | ||
235 | fetch_markets.return_value = [[3, "config1", 1], [1, "config2", 2]] | 252 | fetch_markets.return_value = [ |
253 | [3, "config1", 1, "options"], | ||
254 | [1, "config2", 2, "options"] | ||
255 | ] | ||
236 | 256 | ||
237 | main.main(["Foo", "Bar"]) | 257 | main.main(["Foo", "Bar"]) |
238 | 258 | ||
@@ -318,23 +338,32 @@ class MainTest(WebMockTestCase): | |||
318 | @mock.patch.object(main.dbs, "psql") | 338 | @mock.patch.object(main.dbs, "psql") |
319 | def test_fetch_markets(self, psql): | 339 | def test_fetch_markets(self, psql): |
320 | cursor_mock = mock.MagicMock() | 340 | cursor_mock = mock.MagicMock() |
321 | cursor_mock.__iter__.return_value = ["row_1", "row_2"] | 341 | cursor_mock.__iter__.return_value = [ |
342 | (1, "cfg", 1, "high-liquidity"), | ||
343 | (2, "cfg2", 3, "medium-liquidity") | ||
344 | ] | ||
322 | 345 | ||
323 | psql.cursor.return_value = cursor_mock | 346 | psql.cursor.return_value = cursor_mock |
324 | 347 | ||
325 | with self.subTest(user=None): | 348 | with self.subTest(user=None): |
326 | rows = list(main.fetch_markets(None)) | 349 | rows = list(main.fetch_markets(None)) |
327 | 350 | ||
328 | cursor_mock.execute.assert_called_once_with("SELECT id,config,user_id FROM market_configs WHERE status='enabled'") | 351 | cursor_mock.execute.assert_called_once_with("SELECT id,config,user_id,portfolio_profile FROM market_configs_augmented WHERE status='enabled'") |
329 | 352 | ||
330 | self.assertEqual(["row_1", "row_2"], rows) | 353 | self.assertEqual([ |
354 | (1, 'cfg', 1, {'liquidity': 'high'}), | ||
355 | (2, 'cfg2', 3, {'liquidity': 'medium'}) | ||
356 | ], rows) | ||
331 | 357 | ||
332 | cursor_mock.execute.reset_mock() | 358 | cursor_mock.execute.reset_mock() |
333 | with self.subTest(user=1): | 359 | with self.subTest(user=1): |
334 | rows = list(main.fetch_markets(1)) | 360 | rows = list(main.fetch_markets(1)) |
335 | 361 | ||
336 | cursor_mock.execute.assert_called_once_with("SELECT id,config,user_id FROM market_configs WHERE status='enabled' AND user_id = %s", [1]) | 362 | cursor_mock.execute.assert_called_once_with("SELECT id,config,user_id,portfolio_profile FROM market_configs_augmented WHERE status='enabled' AND user_id = %s", [1]) |
337 | 363 | ||
338 | self.assertEqual(["row_1", "row_2"], rows) | 364 | self.assertEqual([ |
365 | (1, 'cfg', 1, {'liquidity': 'high'}), | ||
366 | (2, 'cfg2', 3, {'liquidity': 'medium'}) | ||
367 | ], rows) | ||
339 | 368 | ||
340 | 369 | ||
diff --git a/tests/test_market.py b/tests/test_market.py index c029686..aeb9f8e 100644 --- a/tests/test_market.py +++ b/tests/test_market.py | |||
@@ -1126,12 +1126,13 @@ class ProcessorTest(WebMockTestCase): | |||
1126 | method_mock = mock.Mock() | 1126 | method_mock = mock.Mock() |
1127 | method_arguments.return_value = [ | 1127 | method_arguments.return_value = [ |
1128 | method_mock, | 1128 | method_mock, |
1129 | ["foo2", "foo"] | 1129 | ["foo2", "foo", "foo3"] |
1130 | ] | 1130 | ] |
1131 | self.m.options = { "foo3": "coucou"} | ||
1131 | method, args = processor.parse_args("action", {"foo": "bar", "foo2": "bar"}, {"foo": "bar2", "bla": "bla"}) | 1132 | method, args = processor.parse_args("action", {"foo": "bar", "foo2": "bar"}, {"foo": "bar2", "bla": "bla"}) |
1132 | 1133 | ||
1133 | self.assertEqual(method_mock, method) | 1134 | self.assertEqual(method_mock, method) |
1134 | self.assertEqual({"foo": "bar2", "foo2": "bar"}, args) | 1135 | self.assertEqual({"foo": "bar2", "foo2": "bar", "foo3": "coucou"}, args) |
1135 | 1136 | ||
1136 | with mock.patch.object(processor, "method_arguments") as method_arguments: | 1137 | with mock.patch.object(processor, "method_arguments") as method_arguments: |
1137 | method_mock = mock.Mock() | 1138 | method_mock = mock.Mock() |