diff options
author | Ismaël Bouya <ismael.bouya@normalesup.org> | 2018-03-09 19:18:02 +0100 |
---|---|---|
committer | Ismaël Bouya <ismael.bouya@normalesup.org> | 2018-03-09 19:18:02 +0100 |
commit | 34eb08f759a440af0376727664d9422041dfbd18 (patch) | |
tree | d1a94be893451a4e182f3e75e9afb01749172bb4 /main.py | |
parent | f9226903cb53a9b303a26de562e321159349f8df (diff) | |
parent | dc1ca9a306f09886c6c57f8d426c59a9d084b2b3 (diff) | |
download | Trader-34eb08f759a440af0376727664d9422041dfbd18.tar.gz Trader-34eb08f759a440af0376727664d9422041dfbd18.tar.zst Trader-34eb08f759a440af0376727664d9422041dfbd18.zip |
Merge branch 'immae/parallelize' into dev
Fixes https://git.immae.eu/mantisbt/view.php?id=51
Diffstat (limited to 'main.py')
-rw-r--r-- | main.py | 161 |
1 files changed, 149 insertions, 12 deletions
@@ -1,18 +1,155 @@ | |||
1 | from datetime import datetime | ||
2 | import argparse | ||
3 | import configparser | ||
4 | import psycopg2 | ||
5 | import os | ||
1 | import sys | 6 | import sys |
2 | import helper, market | ||
3 | 7 | ||
4 | args = helper.main_parse_args(sys.argv[1:]) | 8 | import market |
9 | import portfolio | ||
5 | 10 | ||
6 | pg_config, report_path = helper.main_parse_config(args.config) | 11 | __all__ = ["make_order", "get_user_market"] |
7 | 12 | ||
8 | for market_config, user_id in helper.main_fetch_markets(pg_config, args.user): | 13 | def make_order(market, value, currency, action="acquire", |
14 | close_if_possible=False, base_currency="BTC", follow=True, | ||
15 | compute_value="average"): | ||
16 | """ | ||
17 | Make an order on market | ||
18 | "market": The market on which to place the order | ||
19 | "value": The value in *base_currency* to acquire, | ||
20 | or in *currency* to dispose. | ||
21 | use negative for margin trade. | ||
22 | "action": "acquire" or "dispose". | ||
23 | "acquire" will buy long or sell short, | ||
24 | "dispose" will sell long or buy short. | ||
25 | "currency": The currency to acquire or dispose | ||
26 | "base_currency": The base currency. The value is expressed in that | ||
27 | currency (default: BTC) | ||
28 | "follow": Whether to follow the order once run (default: True) | ||
29 | "close_if_possible": Whether to try to close the position at the end | ||
30 | of the trade, i.e. reach exactly 0 at the end | ||
31 | (only meaningful in "dispose"). May have | ||
32 | unwanted effects if the end value of the | ||
33 | currency is not 0. | ||
34 | "compute_value": Compute value to place the order | ||
35 | """ | ||
36 | market.report.log_stage("make_order_begin") | ||
37 | market.balances.fetch_balances(tag="make_order_begin") | ||
38 | if action == "acquire": | ||
39 | trade = portfolio.Trade( | ||
40 | portfolio.Amount(base_currency, 0), | ||
41 | portfolio.Amount(base_currency, value), | ||
42 | currency, market) | ||
43 | else: | ||
44 | amount = portfolio.Amount(currency, value) | ||
45 | trade = portfolio.Trade( | ||
46 | amount.in_currency(base_currency, market, compute_value=compute_value), | ||
47 | portfolio.Amount(base_currency, 0), | ||
48 | currency, market) | ||
49 | market.trades.all.append(trade) | ||
50 | order = trade.prepare_order( | ||
51 | close_if_possible=close_if_possible, | ||
52 | compute_value=compute_value) | ||
53 | market.report.log_orders([order], None, compute_value) | ||
54 | market.trades.run_orders() | ||
55 | if follow: | ||
56 | market.follow_orders() | ||
57 | market.balances.fetch_balances(tag="make_order_end") | ||
58 | else: | ||
59 | market.report.log_stage("make_order_end_not_followed") | ||
60 | return order | ||
61 | market.report.log_stage("make_order_end") | ||
62 | |||
63 | def get_user_market(config_path, user_id, debug=False): | ||
64 | pg_config, report_path = parse_config(config_path) | ||
65 | market_config = list(fetch_markets(pg_config, str(user_id)))[0][0] | ||
66 | return market.Market.from_config(market_config, debug=debug) | ||
67 | |||
68 | def fetch_markets(pg_config, user): | ||
69 | connection = psycopg2.connect(**pg_config) | ||
70 | cursor = connection.cursor() | ||
71 | |||
72 | if user is None: | ||
73 | cursor.execute("SELECT config,user_id FROM market_configs") | ||
74 | else: | ||
75 | cursor.execute("SELECT config,user_id FROM market_configs WHERE user_id = %s", user) | ||
76 | |||
77 | for row in cursor: | ||
78 | yield row | ||
79 | |||
80 | def parse_config(config_file): | ||
81 | config = configparser.ConfigParser() | ||
82 | config.read(config_file) | ||
83 | |||
84 | if "postgresql" not in config: | ||
85 | print("no configuration for postgresql in config file") | ||
86 | sys.exit(1) | ||
87 | |||
88 | if "app" in config and "report_path" in config["app"]: | ||
89 | report_path = config["app"]["report_path"] | ||
90 | |||
91 | if not os.path.exists(report_path): | ||
92 | os.makedirs(report_path) | ||
93 | else: | ||
94 | report_path = None | ||
95 | |||
96 | return [config["postgresql"], report_path] | ||
97 | |||
98 | def parse_args(argv): | ||
99 | parser = argparse.ArgumentParser( | ||
100 | description="Run the trade bot") | ||
101 | |||
102 | parser.add_argument("-c", "--config", | ||
103 | default="config.ini", | ||
104 | required=False, | ||
105 | help="Config file to load (default: config.ini)") | ||
106 | parser.add_argument("--before", | ||
107 | default=False, action='store_const', const=True, | ||
108 | help="Run the steps before the cryptoportfolio update") | ||
109 | parser.add_argument("--after", | ||
110 | default=False, action='store_const', const=True, | ||
111 | help="Run the steps after the cryptoportfolio update") | ||
112 | parser.add_argument("--debug", | ||
113 | default=False, action='store_const', const=True, | ||
114 | help="Run in debug mode") | ||
115 | parser.add_argument("--user", | ||
116 | default=None, required=False, help="Only run for that user") | ||
117 | parser.add_argument("--action", | ||
118 | action='append', | ||
119 | help="Do a different action than trading (add several times to chain)") | ||
120 | parser.add_argument("--parallel", action='store_true', default=True, dest="parallel") | ||
121 | parser.add_argument("--no-parallel", action='store_false', dest="parallel") | ||
122 | |||
123 | args = parser.parse_args(argv) | ||
124 | |||
125 | if not os.path.exists(args.config): | ||
126 | print("no config file found, exiting") | ||
127 | sys.exit(1) | ||
128 | |||
129 | return args | ||
130 | |||
131 | def process(market_config, user_id, report_path, args): | ||
9 | try: | 132 | try: |
10 | user_market = market.Market.from_config(market_config, debug=args.debug) | 133 | market.Market\ |
11 | helper.main_process_market(user_market, args.action, before=args.before, after=args.after) | 134 | .from_config(market_config, debug=args.debug, user_id=user_id, report_path=report_path)\ |
135 | .process(args.action, before=args.before, after=args.after) | ||
12 | except Exception as e: | 136 | except Exception as e: |
13 | try: | 137 | print("{}: {}".format(e.__class__.__name__, e)) |
14 | user_market.report.log_error("main", exception=e) | 138 | |
15 | except: | 139 | def main(argv): |
16 | print("{}: {}".format(e.__class__.__name__, e)) | 140 | args = parse_args(argv) |
17 | finally: | 141 | |
18 | helper.main_store_report(report_path, user_id, user_market) | 142 | pg_config, report_path = parse_config(args.config) |
143 | |||
144 | if args.parallel: | ||
145 | import threading | ||
146 | market.Portfolio.start_worker() | ||
147 | |||
148 | for market_config, user_id in fetch_markets(pg_config, args.user): | ||
149 | threading.Thread(target=process, args=[market_config, user_id, report_path, args]).start() | ||
150 | else: | ||
151 | for market_config, user_id in fetch_markets(pg_config, args.user): | ||
152 | process(market_config, user_id, report_path, args) | ||
153 | |||
154 | if __name__ == '__main__': # pragma: no cover | ||
155 | main(sys.argv[1:]) | ||