diff options
-rw-r--r-- | main.py | 8 | ||||
-rw-r--r-- | market.py | 11 | ||||
-rw-r--r-- | test.py | 51 |
3 files changed, 43 insertions, 27 deletions
@@ -63,7 +63,8 @@ def make_order(market, value, currency, action="acquire", | |||
63 | def get_user_market(config_path, user_id, debug=False): | 63 | def get_user_market(config_path, user_id, debug=False): |
64 | pg_config, report_path = parse_config(config_path) | 64 | pg_config, report_path = parse_config(config_path) |
65 | market_config = list(fetch_markets(pg_config, str(user_id)))[0][0] | 65 | market_config = list(fetch_markets(pg_config, str(user_id)))[0][0] |
66 | return market.Market.from_config(market_config, debug=debug) | 66 | args = type('Args', (object,), { "debug": debug, "quiet": False })() |
67 | return market.Market.from_config(market_config, args, user_id=user_id, report_path=report_path) | ||
67 | 68 | ||
68 | def fetch_markets(pg_config, user): | 69 | def fetch_markets(pg_config, user): |
69 | connection = psycopg2.connect(**pg_config) | 70 | connection = psycopg2.connect(**pg_config) |
@@ -109,6 +110,9 @@ def parse_args(argv): | |||
109 | parser.add_argument("--after", | 110 | parser.add_argument("--after", |
110 | default=False, action='store_const', const=True, | 111 | default=False, action='store_const', const=True, |
111 | help="Run the steps after the cryptoportfolio update") | 112 | help="Run the steps after the cryptoportfolio update") |
113 | parser.add_argument("--quiet", | ||
114 | default=False, action='store_const', const=True, | ||
115 | help="Don't print messages") | ||
112 | parser.add_argument("--debug", | 116 | parser.add_argument("--debug", |
113 | default=False, action='store_const', const=True, | 117 | default=False, action='store_const', const=True, |
114 | help="Run in debug mode") | 118 | help="Run in debug mode") |
@@ -131,7 +135,7 @@ def parse_args(argv): | |||
131 | def process(market_config, user_id, report_path, args): | 135 | def process(market_config, user_id, report_path, args): |
132 | try: | 136 | try: |
133 | market.Market\ | 137 | market.Market\ |
134 | .from_config(market_config, debug=args.debug, user_id=user_id, report_path=report_path)\ | 138 | .from_config(market_config, args, user_id=user_id, report_path=report_path)\ |
135 | .process(args.action, before=args.before, after=args.after) | 139 | .process(args.action, before=args.before, after=args.after) |
136 | except Exception as e: | 140 | except Exception as e: |
137 | print("{}: {}".format(e.__class__.__name__, e)) | 141 | print("{}: {}".format(e.__class__.__name__, e)) |
@@ -13,11 +13,12 @@ class Market: | |||
13 | trades = None | 13 | trades = None |
14 | balances = None | 14 | balances = None |
15 | 15 | ||
16 | def __init__(self, ccxt_instance, debug=False, user_id=None, report_path=None): | 16 | def __init__(self, ccxt_instance, args, user_id=None, report_path=None): |
17 | self.debug = debug | 17 | self.args = args |
18 | self.debug = args.debug | ||
18 | self.ccxt = ccxt_instance | 19 | self.ccxt = ccxt_instance |
19 | self.ccxt._market = self | 20 | self.ccxt._market = self |
20 | self.report = ReportStore(self) | 21 | self.report = ReportStore(self, verbose_print=(not args.quiet)) |
21 | self.trades = TradeStore(self) | 22 | self.trades = TradeStore(self) |
22 | self.balances = BalanceStore(self) | 23 | self.balances = BalanceStore(self) |
23 | self.processor = Processor(self) | 24 | self.processor = Processor(self) |
@@ -26,7 +27,7 @@ class Market: | |||
26 | self.report_path = report_path | 27 | self.report_path = report_path |
27 | 28 | ||
28 | @classmethod | 29 | @classmethod |
29 | def from_config(cls, config, debug=False, user_id=None, report_path=None): | 30 | def from_config(cls, config, args, user_id=None, report_path=None): |
30 | config["apiKey"] = config.pop("key", None) | 31 | config["apiKey"] = config.pop("key", None) |
31 | 32 | ||
32 | ccxt_instance = ccxt.poloniexE(config) | 33 | ccxt_instance = ccxt.poloniexE(config) |
@@ -43,7 +44,7 @@ class Market: | |||
43 | ccxt_instance.session.request = request_wrap.__get__(ccxt_instance.session, | 44 | ccxt_instance.session.request = request_wrap.__get__(ccxt_instance.session, |
44 | ccxt_instance.session.__class__) | 45 | ccxt_instance.session.__class__) |
45 | 46 | ||
46 | return cls(ccxt_instance, debug=debug, user_id=user_id, report_path=report_path) | 47 | return cls(ccxt_instance, args, user_id=user_id, report_path=report_path) |
47 | 48 | ||
48 | def store_report(self): | 49 | def store_report(self): |
49 | self.report.merge(Portfolio.report) | 50 | self.report.merge(Portfolio.report) |
@@ -23,6 +23,9 @@ for test_type in limits: | |||
23 | class WebMockTestCase(unittest.TestCase): | 23 | class WebMockTestCase(unittest.TestCase): |
24 | import time | 24 | import time |
25 | 25 | ||
26 | def market_args(self, debug=False, quiet=False): | ||
27 | return type('Args', (object,), { "debug": debug, "quiet": quiet })() | ||
28 | |||
26 | def setUp(self): | 29 | def setUp(self): |
27 | super(WebMockTestCase, self).setUp() | 30 | super(WebMockTestCase, self).setUp() |
28 | self.wm = requests_mock.Mocker() | 31 | self.wm = requests_mock.Mocker() |
@@ -1092,7 +1095,7 @@ class MarketTest(WebMockTestCase): | |||
1092 | self.ccxt = mock.Mock(spec=market.ccxt.poloniexE) | 1095 | self.ccxt = mock.Mock(spec=market.ccxt.poloniexE) |
1093 | 1096 | ||
1094 | def test_values(self): | 1097 | def test_values(self): |
1095 | m = market.Market(self.ccxt) | 1098 | m = market.Market(self.ccxt, self.market_args()) |
1096 | 1099 | ||
1097 | self.assertEqual(self.ccxt, m.ccxt) | 1100 | self.assertEqual(self.ccxt, m.ccxt) |
1098 | self.assertFalse(m.debug) | 1101 | self.assertFalse(m.debug) |
@@ -1104,19 +1107,27 @@ class MarketTest(WebMockTestCase): | |||
1104 | self.assertEqual(m, m.balances.market) | 1107 | self.assertEqual(m, m.balances.market) |
1105 | self.assertEqual(m, m.ccxt._market) | 1108 | self.assertEqual(m, m.ccxt._market) |
1106 | 1109 | ||
1107 | m = market.Market(self.ccxt, debug=True) | 1110 | m = market.Market(self.ccxt, self.market_args(debug=True)) |
1108 | self.assertTrue(m.debug) | 1111 | self.assertTrue(m.debug) |
1109 | 1112 | ||
1110 | m = market.Market(self.ccxt, debug=False) | 1113 | m = market.Market(self.ccxt, self.market_args(debug=False)) |
1111 | self.assertFalse(m.debug) | 1114 | self.assertFalse(m.debug) |
1112 | 1115 | ||
1116 | with mock.patch("market.ReportStore") as report_store: | ||
1117 | with self.subTest(quiet=False): | ||
1118 | m = market.Market(self.ccxt, self.market_args(quiet=False)) | ||
1119 | report_store.assert_called_with(m, verbose_print=True) | ||
1120 | with self.subTest(quiet=True): | ||
1121 | m = market.Market(self.ccxt, self.market_args(quiet=True)) | ||
1122 | report_store.assert_called_with(m, verbose_print=False) | ||
1123 | |||
1113 | @mock.patch("market.ccxt") | 1124 | @mock.patch("market.ccxt") |
1114 | def test_from_config(self, ccxt): | 1125 | def test_from_config(self, ccxt): |
1115 | with mock.patch("market.ReportStore"): | 1126 | with mock.patch("market.ReportStore"): |
1116 | ccxt.poloniexE.return_value = self.ccxt | 1127 | ccxt.poloniexE.return_value = self.ccxt |
1117 | self.ccxt.session.request.return_value = "response" | 1128 | self.ccxt.session.request.return_value = "response" |
1118 | 1129 | ||
1119 | m = market.Market.from_config({"key": "key", "secred": "secret"}) | 1130 | m = market.Market.from_config({"key": "key", "secred": "secret"}, self.market_args()) |
1120 | 1131 | ||
1121 | self.assertEqual(self.ccxt, m.ccxt) | 1132 | self.assertEqual(self.ccxt, m.ccxt) |
1122 | 1133 | ||
@@ -1125,7 +1136,7 @@ class MarketTest(WebMockTestCase): | |||
1125 | m.report.log_http_request.assert_called_with('GET', 'URL', 'data', | 1136 | m.report.log_http_request.assert_called_with('GET', 'URL', 'data', |
1126 | 'headers', 'response') | 1137 | 'headers', 'response') |
1127 | 1138 | ||
1128 | m = market.Market.from_config({"key": "key", "secred": "secret"}, debug=True) | 1139 | m = market.Market.from_config({"key": "key", "secred": "secret"}, self.market_args(debug=True)) |
1129 | self.assertEqual(True, m.debug) | 1140 | self.assertEqual(True, m.debug) |
1130 | 1141 | ||
1131 | def test_get_tickers(self): | 1142 | def test_get_tickers(self): |
@@ -1134,7 +1145,7 @@ class MarketTest(WebMockTestCase): | |||
1134 | market.NotSupported | 1145 | market.NotSupported |
1135 | ] | 1146 | ] |
1136 | 1147 | ||
1137 | m = market.Market(self.ccxt) | 1148 | m = market.Market(self.ccxt, self.market_args()) |
1138 | self.assertEqual("tickers", m.get_tickers()) | 1149 | self.assertEqual("tickers", m.get_tickers()) |
1139 | self.assertEqual("tickers", m.get_tickers()) | 1150 | self.assertEqual("tickers", m.get_tickers()) |
1140 | self.ccxt.fetch_tickers.assert_called_once() | 1151 | self.ccxt.fetch_tickers.assert_called_once() |
@@ -1147,7 +1158,7 @@ class MarketTest(WebMockTestCase): | |||
1147 | "ETH/ETC": { "bid": 1, "ask": 3 }, | 1158 | "ETH/ETC": { "bid": 1, "ask": 3 }, |
1148 | "XVG/ETH": { "bid": 10, "ask": 40 }, | 1159 | "XVG/ETH": { "bid": 10, "ask": 40 }, |
1149 | } | 1160 | } |
1150 | m = market.Market(self.ccxt) | 1161 | m = market.Market(self.ccxt, self.market_args()) |
1151 | 1162 | ||
1152 | ticker = m.get_ticker("ETH", "ETC") | 1163 | ticker = m.get_ticker("ETH", "ETC") |
1153 | self.assertEqual(1, ticker["bid"]) | 1164 | self.assertEqual(1, ticker["bid"]) |
@@ -1175,7 +1186,7 @@ class MarketTest(WebMockTestCase): | |||
1175 | market.ExchangeError("foo"), | 1186 | market.ExchangeError("foo"), |
1176 | ] | 1187 | ] |
1177 | 1188 | ||
1178 | m = market.Market(self.ccxt) | 1189 | m = market.Market(self.ccxt, self.market_args()) |
1179 | 1190 | ||
1180 | ticker = m.get_ticker("ETH", "ETC") | 1191 | ticker = m.get_ticker("ETH", "ETC") |
1181 | self.ccxt.fetch_ticker.assert_called_with("ETH/ETC") | 1192 | self.ccxt.fetch_ticker.assert_called_with("ETH/ETC") |
@@ -1195,7 +1206,7 @@ class MarketTest(WebMockTestCase): | |||
1195 | self.assertIsNone(ticker) | 1206 | self.assertIsNone(ticker) |
1196 | 1207 | ||
1197 | def test_fetch_fees(self): | 1208 | def test_fetch_fees(self): |
1198 | m = market.Market(self.ccxt) | 1209 | m = market.Market(self.ccxt, self.market_args()) |
1199 | self.ccxt.fetch_fees.return_value = "Foo" | 1210 | self.ccxt.fetch_fees.return_value = "Foo" |
1200 | self.assertEqual("Foo", m.fetch_fees()) | 1211 | self.assertEqual("Foo", m.fetch_fees()) |
1201 | self.ccxt.fetch_fees.assert_called_once() | 1212 | self.ccxt.fetch_fees.assert_called_once() |
@@ -1222,7 +1233,7 @@ class MarketTest(WebMockTestCase): | |||
1222 | get_ticker.side_effect = _get_ticker | 1233 | get_ticker.side_effect = _get_ticker |
1223 | 1234 | ||
1224 | with mock.patch("market.ReportStore"): | 1235 | with mock.patch("market.ReportStore"): |
1225 | m = market.Market(self.ccxt) | 1236 | m = market.Market(self.ccxt, self.market_args()) |
1226 | self.ccxt.fetch_all_balances.return_value = { | 1237 | self.ccxt.fetch_all_balances.return_value = { |
1227 | "USDT": { | 1238 | "USDT": { |
1228 | "exchange_free": D("10000.0"), | 1239 | "exchange_free": D("10000.0"), |
@@ -1262,7 +1273,7 @@ class MarketTest(WebMockTestCase): | |||
1262 | (False, 12), (True, 12)]: | 1273 | (False, 12), (True, 12)]: |
1263 | with self.subTest(sleep=sleep, debug=debug), \ | 1274 | with self.subTest(sleep=sleep, debug=debug), \ |
1264 | mock.patch("market.ReportStore"): | 1275 | mock.patch("market.ReportStore"): |
1265 | m = market.Market(self.ccxt, debug=debug) | 1276 | m = market.Market(self.ccxt, self.market_args(debug=debug)) |
1266 | 1277 | ||
1267 | order_mock1 = mock.Mock() | 1278 | order_mock1 = mock.Mock() |
1268 | order_mock2 = mock.Mock() | 1279 | order_mock2 = mock.Mock() |
@@ -1339,7 +1350,7 @@ class MarketTest(WebMockTestCase): | |||
1339 | for debug in [True, False]: | 1350 | for debug in [True, False]: |
1340 | with self.subTest(debug=debug),\ | 1351 | with self.subTest(debug=debug),\ |
1341 | mock.patch("market.ReportStore"): | 1352 | mock.patch("market.ReportStore"): |
1342 | m = market.Market(self.ccxt, debug=debug) | 1353 | m = market.Market(self.ccxt, self.market_args(debug=debug)) |
1343 | 1354 | ||
1344 | value_from = portfolio.Amount("BTC", "1.0") | 1355 | value_from = portfolio.Amount("BTC", "1.0") |
1345 | value_from.linked_to = portfolio.Amount("ETH", "10.0") | 1356 | value_from.linked_to = portfolio.Amount("ETH", "10.0") |
@@ -1378,7 +1389,7 @@ class MarketTest(WebMockTestCase): | |||
1378 | def test_store_report(self): | 1389 | def test_store_report(self): |
1379 | 1390 | ||
1380 | file_open = mock.mock_open() | 1391 | file_open = mock.mock_open() |
1381 | m = market.Market(self.ccxt, user_id=1) | 1392 | m = market.Market(self.ccxt, self.market_args(), user_id=1) |
1382 | with self.subTest(file=None),\ | 1393 | with self.subTest(file=None),\ |
1383 | mock.patch.object(m, "report") as report,\ | 1394 | mock.patch.object(m, "report") as report,\ |
1384 | mock.patch("market.open", file_open): | 1395 | mock.patch("market.open", file_open): |
@@ -1388,7 +1399,7 @@ class MarketTest(WebMockTestCase): | |||
1388 | 1399 | ||
1389 | report.reset_mock() | 1400 | report.reset_mock() |
1390 | file_open = mock.mock_open() | 1401 | file_open = mock.mock_open() |
1391 | m = market.Market(self.ccxt, report_path="present", user_id=1) | 1402 | m = market.Market(self.ccxt, self.market_args(), report_path="present", user_id=1) |
1392 | with self.subTest(file="present"),\ | 1403 | with self.subTest(file="present"),\ |
1393 | mock.patch("market.open", file_open),\ | 1404 | mock.patch("market.open", file_open),\ |
1394 | mock.patch.object(m, "report") as report,\ | 1405 | mock.patch.object(m, "report") as report,\ |
@@ -1409,7 +1420,7 @@ class MarketTest(WebMockTestCase): | |||
1409 | 1420 | ||
1410 | report.reset_mock() | 1421 | report.reset_mock() |
1411 | 1422 | ||
1412 | m = market.Market(self.ccxt, report_path="error", user_id=1) | 1423 | m = market.Market(self.ccxt, self.market_args(), report_path="error", user_id=1) |
1413 | with self.subTest(file="error"),\ | 1424 | with self.subTest(file="error"),\ |
1414 | mock.patch("market.open") as file_open,\ | 1425 | mock.patch("market.open") as file_open,\ |
1415 | mock.patch.object(m, "report") as report,\ | 1426 | mock.patch.object(m, "report") as report,\ |
@@ -1422,7 +1433,7 @@ class MarketTest(WebMockTestCase): | |||
1422 | self.assertRegex(stdout_mock.getvalue(), "impossible to store report file: FileNotFoundError;") | 1433 | self.assertRegex(stdout_mock.getvalue(), "impossible to store report file: FileNotFoundError;") |
1423 | 1434 | ||
1424 | def test_print_orders(self): | 1435 | def test_print_orders(self): |
1425 | m = market.Market(self.ccxt) | 1436 | m = market.Market(self.ccxt, self.market_args()) |
1426 | with mock.patch.object(m.report, "log_stage") as log_stage,\ | 1437 | with mock.patch.object(m.report, "log_stage") as log_stage,\ |
1427 | mock.patch.object(m.balances, "fetch_balances") as fetch_balances,\ | 1438 | mock.patch.object(m.balances, "fetch_balances") as fetch_balances,\ |
1428 | mock.patch.object(m, "prepare_trades") as prepare_trades,\ | 1439 | mock.patch.object(m, "prepare_trades") as prepare_trades,\ |
@@ -1436,7 +1447,7 @@ class MarketTest(WebMockTestCase): | |||
1436 | prepare_orders.assert_called_with(compute_value="average") | 1447 | prepare_orders.assert_called_with(compute_value="average") |
1437 | 1448 | ||
1438 | def test_print_balances(self): | 1449 | def test_print_balances(self): |
1439 | m = market.Market(self.ccxt) | 1450 | m = market.Market(self.ccxt, self.market_args()) |
1440 | 1451 | ||
1441 | with mock.patch.object(m.balances, "in_currency") as in_currency,\ | 1452 | with mock.patch.object(m.balances, "in_currency") as in_currency,\ |
1442 | mock.patch.object(m.report, "log_stage") as log_stage,\ | 1453 | mock.patch.object(m.report, "log_stage") as log_stage,\ |
@@ -1461,7 +1472,7 @@ class MarketTest(WebMockTestCase): | |||
1461 | @mock.patch("market.ReportStore.log_error") | 1472 | @mock.patch("market.ReportStore.log_error") |
1462 | @mock.patch("market.Market.store_report") | 1473 | @mock.patch("market.Market.store_report") |
1463 | def test_process(self, store_report, log_error, process): | 1474 | def test_process(self, store_report, log_error, process): |
1464 | m = market.Market(self.ccxt) | 1475 | m = market.Market(self.ccxt, self.market_args()) |
1465 | with self.subTest(before=False, after=False): | 1476 | with self.subTest(before=False, after=False): |
1466 | m.process(None) | 1477 | m.process(None) |
1467 | 1478 | ||
@@ -3571,7 +3582,7 @@ class MainTest(WebMockTestCase): | |||
3571 | main.process("config", 1, "report_path", args_mock) | 3582 | main.process("config", 1, "report_path", args_mock) |
3572 | 3583 | ||
3573 | market_mock.from_config.assert_has_calls([ | 3584 | market_mock.from_config.assert_has_calls([ |
3574 | mock.call("config", debug="debug", user_id=1, report_path="report_path"), | 3585 | mock.call("config", args_mock, user_id=1, report_path="report_path"), |
3575 | mock.call().process("action", before="before", after="after"), | 3586 | mock.call().process("action", before="before", after="after"), |
3576 | ]) | 3587 | ]) |
3577 | 3588 | ||
@@ -3797,7 +3808,7 @@ class ProcessorTest(WebMockTestCase): | |||
3797 | 3808 | ||
3798 | def test_method_arguments(self): | 3809 | def test_method_arguments(self): |
3799 | ccxt = mock.Mock(spec=market.ccxt.poloniexE) | 3810 | ccxt = mock.Mock(spec=market.ccxt.poloniexE) |
3800 | m = market.Market(ccxt) | 3811 | m = market.Market(ccxt, self.market_args()) |
3801 | 3812 | ||
3802 | processor = market.Processor(m) | 3813 | processor = market.Processor(m) |
3803 | 3814 | ||