]>
Commit | Line | Data |
---|---|---|
1f117ac7 IB |
1 | from datetime import datetime |
2 | import argparse | |
3 | import configparser | |
4 | import psycopg2 | |
5 | import os | |
eb9c92e1 | 6 | import sys |
eb9c92e1 | 7 | |
1f117ac7 IB |
8 | import market |
9 | import portfolio | |
eb9c92e1 | 10 | |
1f117ac7 | 11 | __all__ = ["make_order", "get_user_market"] |
eb9c92e1 | 12 | |
1f117ac7 IB |
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)") | |
dc1ca9a3 IB |
120 | parser.add_argument("--parallel", action='store_true', default=True, dest="parallel") |
121 | parser.add_argument("--no-parallel", action='store_false', dest="parallel") | |
1f117ac7 IB |
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 | ||
a18ce2f1 IB |
131 | def process(market_config, user_id, report_path, args): |
132 | try: | |
133 | market.Market\ | |
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) | |
136 | except Exception as e: | |
137 | print("{}: {}".format(e.__class__.__name__, e)) | |
138 | ||
1f117ac7 IB |
139 | def main(argv): |
140 | args = parse_args(argv) | |
141 | ||
142 | pg_config, report_path = parse_config(args.config) | |
143 | ||
dc1ca9a3 IB |
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) | |
1f117ac7 IB |
153 | |
154 | if __name__ == '__main__': # pragma: no cover | |
155 | main(sys.argv[1:]) |