]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git/blame - tests/test_main.py
Add liquidity option for cryptoportfolio users
[perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Trader.git] / tests / test_main.py
CommitLineData
c682bdf4
IB
1from .helper import *
2import main, market
3
3080f31d 4@unittest.skipUnless("unit" in limits, "Unit skipped")
c682bdf4
IB
5class MainTest(WebMockTestCase):
6 def test_make_order(self):
7 self.m.get_ticker.return_value = {
8 "inverted": False,
9 "average": D("0.1"),
10 "bid": D("0.09"),
11 "ask": D("0.11"),
12 }
13
14 with self.subTest(description="nominal case"):
15 main.make_order(self.m, 10, "ETH")
16
17 self.m.report.log_stage.assert_has_calls([
18 mock.call("make_order_begin"),
19 mock.call("make_order_end"),
20 ])
21 self.m.balances.fetch_balances.assert_has_calls([
22 mock.call(tag="make_order_begin"),
23 mock.call(tag="make_order_end"),
24 ])
25 self.m.trades.all.append.assert_called_once()
26 trade = self.m.trades.all.append.mock_calls[0][1][0]
27 self.assertEqual(False, trade.orders[0].close_if_possible)
28 self.assertEqual(0, trade.value_from)
29 self.assertEqual("ETH", trade.currency)
30 self.assertEqual("BTC", trade.base_currency)
31 self.m.report.log_orders.assert_called_once_with([trade.orders[0]], None, "average")
32 self.m.trades.run_orders.assert_called_once_with()
33 self.m.follow_orders.assert_called_once_with()
34
35 order = trade.orders[0]
36 self.assertEqual(D("0.10"), order.rate)
37
38 self.m.reset_mock()
39 with self.subTest(compute_value="default"):
40 main.make_order(self.m, 10, "ETH", action="dispose",
41 compute_value="ask")
42
43 trade = self.m.trades.all.append.mock_calls[0][1][0]
44 order = trade.orders[0]
45 self.assertEqual(D("0.11"), order.rate)
46
47 self.m.reset_mock()
48 with self.subTest(follow=False):
49 result = main.make_order(self.m, 10, "ETH", follow=False)
50
51 self.m.report.log_stage.assert_has_calls([
52 mock.call("make_order_begin"),
53 mock.call("make_order_end_not_followed"),
54 ])
55 self.m.balances.fetch_balances.assert_called_once_with(tag="make_order_begin")
56
57 self.m.trades.all.append.assert_called_once()
58 trade = self.m.trades.all.append.mock_calls[0][1][0]
59 self.assertEqual(0, trade.value_from)
60 self.assertEqual("ETH", trade.currency)
61 self.assertEqual("BTC", trade.base_currency)
62 self.m.report.log_orders.assert_called_once_with([trade.orders[0]], None, "average")
63 self.m.trades.run_orders.assert_called_once_with()
64 self.m.follow_orders.assert_not_called()
65 self.assertEqual(trade.orders[0], result)
66
67 self.m.reset_mock()
68 with self.subTest(base_currency="USDT"):
69 main.make_order(self.m, 1, "BTC", base_currency="USDT")
70
71 trade = self.m.trades.all.append.mock_calls[0][1][0]
72 self.assertEqual("BTC", trade.currency)
73 self.assertEqual("USDT", trade.base_currency)
74
75 self.m.reset_mock()
76 with self.subTest(close_if_possible=True):
77 main.make_order(self.m, 10, "ETH", close_if_possible=True)
78
79 trade = self.m.trades.all.append.mock_calls[0][1][0]
80 self.assertEqual(True, trade.orders[0].close_if_possible)
81
82 self.m.reset_mock()
83 with self.subTest(action="dispose"):
84 main.make_order(self.m, 10, "ETH", action="dispose")
85
86 trade = self.m.trades.all.append.mock_calls[0][1][0]
87 self.assertEqual(0, trade.value_to)
88 self.assertEqual(1, trade.value_from.value)
89 self.assertEqual("ETH", trade.currency)
90 self.assertEqual("BTC", trade.base_currency)
91
92 self.m.reset_mock()
93 with self.subTest(compute_value="default"):
94 main.make_order(self.m, 10, "ETH", action="dispose",
95 compute_value="bid")
96
97 trade = self.m.trades.all.append.mock_calls[0][1][0]
98 self.assertEqual(D("0.9"), trade.value_from.value)
99
100 def test_get_user_market(self):
101 with mock.patch("main.fetch_markets") as main_fetch_markets,\
102 mock.patch("main.parse_args") as main_parse_args,\
103 mock.patch("main.parse_config") as main_parse_config:
104 with self.subTest(debug=False):
105 main_parse_args.return_value = self.market_args()
ef8fa5e5 106 main_fetch_markets.return_value = [(1, {"key": "market_config"}, 3, { "foo": "bar" })]
c682bdf4
IB
107 m = main.get_user_market("config_path.ini", 1)
108
109 self.assertIsInstance(m, market.Market)
110 self.assertFalse(m.debug)
ef8fa5e5 111 self.assertEqual("bar", m.options["foo"])
c682bdf4
IB
112 main_parse_args.assert_called_once_with(["--config", "config_path.ini"])
113
114 main_parse_args.reset_mock()
115 with self.subTest(debug=True):
116 main_parse_args.return_value = self.market_args(debug=True)
ef8fa5e5 117 main_fetch_markets.return_value = [(1, {"key": "market_config"}, 3, { "foo": "bar" })]
c682bdf4
IB
118 m = main.get_user_market("config_path.ini", 1, debug=True)
119
120 self.assertIsInstance(m, market.Market)
121 self.assertTrue(m.debug)
122 main_parse_args.assert_called_once_with(["--config", "config_path.ini", "--debug"])
123
ef8fa5e5
IB
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
c682bdf4
IB
129 def test_process(self):
130 with mock.patch("market.Market") as market_mock,\
131 mock.patch('sys.stdout', new_callable=StringIO) as stdout_mock:
132
133 args_mock = mock.Mock()
134 args_mock.action = "action"
135 args_mock.config = "config"
136 args_mock.user = "user"
137 args_mock.debug = "debug"
138 args_mock.before = "before"
139 args_mock.after = "after"
140 self.assertEqual("", stdout_mock.getvalue())
141
ef8fa5e5 142 main.process("config", 3, 1, args_mock, "options")
c682bdf4
IB
143
144 market_mock.from_config.assert_has_calls([
ef8fa5e5 145 mock.call("config", args_mock, market_id=3, user_id=1, options="options"),
c682bdf4
IB
146 mock.call().process("action", before="before", after="after"),
147 ])
148
149 with self.subTest(exception=True):
150 market_mock.from_config.side_effect = Exception("boo")
ef8fa5e5 151 main.process(3, "config", 1, args_mock, "options")
c682bdf4
IB
152 self.assertEqual("Exception: boo\n", stdout_mock.getvalue())
153
154 def test_main(self):
155 with self.subTest(parallel=False):
156 with mock.patch("main.parse_args") as parse_args,\
157 mock.patch("main.parse_config") as parse_config,\
158 mock.patch("main.fetch_markets") as fetch_markets,\
159 mock.patch("main.process") as process:
160
161 args_mock = mock.Mock()
162 args_mock.parallel = False
163 args_mock.user = "user"
164 parse_args.return_value = args_mock
165
ef8fa5e5
IB
166 fetch_markets.return_value = [
167 [3, "config1", 1, "options"],
168 [1, "config2", 2, "options"]
169 ]
c682bdf4
IB
170
171 main.main(["Foo", "Bar"])
172
173 parse_args.assert_called_with(["Foo", "Bar"])
174 parse_config.assert_called_with(args_mock)
30700830 175 fetch_markets.assert_called_with("user")
c682bdf4
IB
176
177 self.assertEqual(2, process.call_count)
178 process.assert_has_calls([
ef8fa5e5
IB
179 mock.call("config1", 3, 1, args_mock, "options"),
180 mock.call("config2", 1, 2, args_mock, "options"),
c682bdf4 181 ])
ef8fa5e5 182
c682bdf4
IB
183 with self.subTest(parallel=True):
184 with mock.patch("main.parse_args") as parse_args,\
185 mock.patch("main.parse_config") as parse_config,\
186 mock.patch("main.fetch_markets") as fetch_markets,\
187 mock.patch("main.process") as process,\
e7d7c0e5
IB
188 mock.patch("store.Portfolio.start_worker") as start,\
189 mock.patch("store.Portfolio.stop_worker") as stop:
c682bdf4
IB
190
191 args_mock = mock.Mock()
192 args_mock.parallel = True
193 args_mock.user = "user"
194 parse_args.return_value = args_mock
195
ef8fa5e5
IB
196 fetch_markets.return_value = [
197 [3, "config1", 1, "options"],
198 [1, "config2", 2, "options"]
199 ]
c682bdf4
IB
200
201 main.main(["Foo", "Bar"])
202
203 parse_args.assert_called_with(["Foo", "Bar"])
204 parse_config.assert_called_with(args_mock)
30700830 205 fetch_markets.assert_called_with("user")
c682bdf4 206
e7d7c0e5 207 stop.assert_called_once_with()
c682bdf4
IB
208 start.assert_called_once_with()
209 self.assertEqual(2, process.call_count)
210 process.assert_has_calls([
211 mock.call.__bool__(),
ef8fa5e5 212 mock.call("config1", 3, 1, args_mock, "options"),
c682bdf4 213 mock.call.__bool__(),
ef8fa5e5 214 mock.call("config2", 1, 2, args_mock, "options"),
c682bdf4 215 ])
ef8fa5e5 216
a0dcf4e0
IB
217 with self.subTest(quiet=True):
218 with mock.patch("main.parse_args") as parse_args,\
219 mock.patch("main.parse_config") as parse_config,\
220 mock.patch("main.fetch_markets") as fetch_markets,\
221 mock.patch("store.Portfolio.report") as report,\
222 mock.patch("main.process") as process:
223
224 args_mock = mock.Mock()
225 args_mock.parallel = False
226 args_mock.quiet = True
227 args_mock.user = "user"
228 parse_args.return_value = args_mock
229
ef8fa5e5
IB
230 fetch_markets.return_value = [
231 [3, "config1", 1, "options"],
232 [1, "config2", 2, "options"]
233 ]
a0dcf4e0
IB
234
235 main.main(["Foo", "Bar"])
236
237 report.set_verbose.assert_called_once_with(False)
238
239 with self.subTest(quiet=False):
240 with mock.patch("main.parse_args") as parse_args,\
241 mock.patch("main.parse_config") as parse_config,\
242 mock.patch("main.fetch_markets") as fetch_markets,\
243 mock.patch("store.Portfolio.report") as report,\
244 mock.patch("main.process") as process:
245
246 args_mock = mock.Mock()
247 args_mock.parallel = False
248 args_mock.quiet = False
249 args_mock.user = "user"
250 parse_args.return_value = args_mock
251
ef8fa5e5
IB
252 fetch_markets.return_value = [
253 [3, "config1", 1, "options"],
254 [1, "config2", 2, "options"]
255 ]
a0dcf4e0
IB
256
257 main.main(["Foo", "Bar"])
258
259 report.set_verbose.assert_called_once_with(True)
260
c682bdf4
IB
261
262 @mock.patch.object(main.sys, "exit")
263 @mock.patch("main.os")
264 def test_parse_config(self, os, exit):
30700830
IB
265 with self.subTest(report_path=None),\
266 mock.patch.object(main.dbs, "connect_psql") as psql,\
267 mock.patch.object(main.dbs, "connect_redis") as redis:
c682bdf4
IB
268 args = main.configargparse.Namespace(**{
269 "db_host": "host",
1593c7a9 270 "redis_host": "rhost",
c682bdf4
IB
271 "report_path": None,
272 })
273
30700830
IB
274 main.parse_config(args)
275 psql.assert_called_once_with(args)
276 redis.assert_called_once_with(args)
277
278 with self.subTest(report_path=None, db=None),\
279 mock.patch.object(main.dbs, "connect_psql") as psql,\
280 mock.patch.object(main.dbs, "connect_redis") as redis:
281 args = main.configargparse.Namespace(**{
282 "db_host": None,
283 "redis_host": "rhost",
284 "report_path": None,
285 })
1593c7a9 286
30700830
IB
287 main.parse_config(args)
288 psql.assert_not_called()
289 redis.assert_called_once_with(args)
1593c7a9 290
30700830
IB
291 with self.subTest(report_path=None, redis=None),\
292 mock.patch.object(main.dbs, "connect_psql") as psql,\
293 mock.patch.object(main.dbs, "connect_redis") as redis:
1593c7a9
IB
294 args = main.configargparse.Namespace(**{
295 "db_host": "host",
30700830 296 "redis_host": None,
1593c7a9
IB
297 "report_path": None,
298 })
299
30700830
IB
300 main.parse_config(args)
301 redis.assert_not_called()
302 psql.assert_called_once_with(args)
c682bdf4 303
30700830
IB
304 with self.subTest(report_path="present"),\
305 mock.patch.object(main.dbs, "connect_psql") as psql,\
306 mock.patch.object(main.dbs, "connect_redis") as redis:
c682bdf4
IB
307 args = main.configargparse.Namespace(**{
308 "db_host": "host",
1593c7a9 309 "redis_host": "rhost",
c682bdf4
IB
310 "report_path": "report_path",
311 })
312
313 os.path.exists.return_value = False
314
30700830 315 main.parse_config(args)
c682bdf4
IB
316
317 os.path.exists.assert_called_once_with("report_path")
318 os.makedirs.assert_called_once_with("report_path")
319
320 def test_parse_args(self):
321 with self.subTest(config="config.ini"):
322 args = main.parse_args([])
323 self.assertEqual("config.ini", args.config)
324 self.assertFalse(args.before)
325 self.assertFalse(args.after)
326 self.assertFalse(args.debug)
327
328 args = main.parse_args(["--before", "--after", "--debug"])
329 self.assertTrue(args.before)
330 self.assertTrue(args.after)
331 self.assertTrue(args.debug)
332
333 with self.subTest(config="inexistant"), \
334 self.assertRaises(SystemExit), \
335 mock.patch('sys.stderr', new_callable=StringIO) as stdout_mock:
336 args = main.parse_args(["--config", "foo.bar"])
337
30700830
IB
338 @mock.patch.object(main.dbs, "psql")
339 def test_fetch_markets(self, psql):
c682bdf4 340 cursor_mock = mock.MagicMock()
ef8fa5e5
IB
341 cursor_mock.__iter__.return_value = [
342 (1, "cfg", 1, "high-liquidity"),
343 (2, "cfg2", 3, "medium-liquidity")
344 ]
c682bdf4 345
30700830 346 psql.cursor.return_value = cursor_mock
c682bdf4
IB
347
348 with self.subTest(user=None):
30700830 349 rows = list(main.fetch_markets(None))
c682bdf4 350
ef8fa5e5 351 cursor_mock.execute.assert_called_once_with("SELECT id,config,user_id,portfolio_profile FROM market_configs_augmented WHERE status='enabled'")
c682bdf4 352
ef8fa5e5
IB
353 self.assertEqual([
354 (1, 'cfg', 1, {'liquidity': 'high'}),
355 (2, 'cfg2', 3, {'liquidity': 'medium'})
356 ], rows)
c682bdf4 357
c682bdf4
IB
358 cursor_mock.execute.reset_mock()
359 with self.subTest(user=1):
30700830 360 rows = list(main.fetch_markets(1))
c682bdf4 361
ef8fa5e5 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])
c682bdf4 363
ef8fa5e5
IB
364 self.assertEqual([
365 (1, 'cfg', 1, {'liquidity': 'high'}),
366 (2, 'cfg2', 3, {'liquidity': 'medium'})
367 ], rows)
c682bdf4
IB
368
369