]>
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) | |
35667b31 | 65 | market_id, market_config, user_id = list(fetch_markets(pg_config, str(user_id)))[0] |
07fa7a4b | 66 | args = type('Args', (object,), { "debug": debug, "quiet": False })() |
35667b31 IB |
67 | return market.Market.from_config(market_config, args, |
68 | pg_config=pg_config, market_id=market_id, | |
69 | user_id=user_id, report_path=report_path) | |
1f117ac7 IB |
70 | |
71 | def fetch_markets(pg_config, user): | |
72 | connection = psycopg2.connect(**pg_config) | |
73 | cursor = connection.cursor() | |
74 | ||
75 | if user is None: | |
b4e0ba0b | 76 | cursor.execute("SELECT id,config,user_id FROM market_configs") |
1f117ac7 | 77 | else: |
b4e0ba0b | 78 | cursor.execute("SELECT id,config,user_id FROM market_configs WHERE user_id = %s", user) |
1f117ac7 IB |
79 | |
80 | for row in cursor: | |
81 | yield row | |
82 | ||
83 | def parse_config(config_file): | |
84 | config = configparser.ConfigParser() | |
85 | config.read(config_file) | |
86 | ||
87 | if "postgresql" not in config: | |
88 | print("no configuration for postgresql in config file") | |
89 | sys.exit(1) | |
90 | ||
91 | if "app" in config and "report_path" in config["app"]: | |
92 | report_path = config["app"]["report_path"] | |
93 | ||
94 | if not os.path.exists(report_path): | |
95 | os.makedirs(report_path) | |
96 | else: | |
97 | report_path = None | |
98 | ||
99 | return [config["postgresql"], report_path] | |
100 | ||
101 | def parse_args(argv): | |
102 | parser = argparse.ArgumentParser( | |
103 | description="Run the trade bot") | |
104 | ||
105 | parser.add_argument("-c", "--config", | |
106 | default="config.ini", | |
107 | required=False, | |
108 | help="Config file to load (default: config.ini)") | |
109 | parser.add_argument("--before", | |
110 | default=False, action='store_const', const=True, | |
111 | help="Run the steps before the cryptoportfolio update") | |
112 | parser.add_argument("--after", | |
113 | default=False, action='store_const', const=True, | |
114 | help="Run the steps after the cryptoportfolio update") | |
07fa7a4b IB |
115 | parser.add_argument("--quiet", |
116 | default=False, action='store_const', const=True, | |
117 | help="Don't print messages") | |
1f117ac7 IB |
118 | parser.add_argument("--debug", |
119 | default=False, action='store_const', const=True, | |
120 | help="Run in debug mode") | |
121 | parser.add_argument("--user", | |
122 | default=None, required=False, help="Only run for that user") | |
123 | parser.add_argument("--action", | |
124 | action='append', | |
125 | help="Do a different action than trading (add several times to chain)") | |
dc1ca9a3 IB |
126 | parser.add_argument("--parallel", action='store_true', default=True, dest="parallel") |
127 | parser.add_argument("--no-parallel", action='store_false', dest="parallel") | |
1f117ac7 IB |
128 | |
129 | args = parser.parse_args(argv) | |
130 | ||
131 | if not os.path.exists(args.config): | |
132 | print("no config file found, exiting") | |
133 | sys.exit(1) | |
134 | ||
135 | return args | |
136 | ||
35667b31 | 137 | def process(market_config, market_id, user_id, args, report_path, pg_config): |
a18ce2f1 IB |
138 | try: |
139 | market.Market\ | |
b4e0ba0b IB |
140 | .from_config(market_config, args, |
141 | pg_config=pg_config, market_id=market_id, | |
142 | user_id=user_id, report_path=report_path)\ | |
a18ce2f1 IB |
143 | .process(args.action, before=args.before, after=args.after) |
144 | except Exception as e: | |
145 | print("{}: {}".format(e.__class__.__name__, e)) | |
146 | ||
1f117ac7 IB |
147 | def main(argv): |
148 | args = parse_args(argv) | |
149 | ||
150 | pg_config, report_path = parse_config(args.config) | |
151 | ||
dc1ca9a3 IB |
152 | if args.parallel: |
153 | import threading | |
154 | market.Portfolio.start_worker() | |
155 | ||
35667b31 IB |
156 | def process_(*args): |
157 | threading.Thread(target=process, args=args).start() | |
dc1ca9a3 | 158 | else: |
35667b31 IB |
159 | process_ = process |
160 | ||
161 | for market_id, market_config, user_id in fetch_markets(pg_config, args.user): | |
162 | process_(market_config, market_id, user_id, args, report_path, pg_config) | |
1f117ac7 IB |
163 | |
164 | if __name__ == '__main__': # pragma: no cover | |
165 | main(sys.argv[1:]) |