]>
Commit | Line | Data |
---|---|---|
1f117ac7 | 1 | from datetime import datetime |
a42d6cc8 | 2 | import configargparse |
1f117ac7 IB |
3 | import psycopg2 |
4 | import os | |
eb9c92e1 | 5 | import sys |
eb9c92e1 | 6 | |
1f117ac7 IB |
7 | import market |
8 | import portfolio | |
eb9c92e1 | 9 | |
1f117ac7 | 10 | __all__ = ["make_order", "get_user_market"] |
eb9c92e1 | 11 | |
1f117ac7 IB |
12 | def make_order(market, value, currency, action="acquire", |
13 | close_if_possible=False, base_currency="BTC", follow=True, | |
14 | compute_value="average"): | |
15 | """ | |
16 | Make an order on market | |
17 | "market": The market on which to place the order | |
18 | "value": The value in *base_currency* to acquire, | |
19 | or in *currency* to dispose. | |
20 | use negative for margin trade. | |
21 | "action": "acquire" or "dispose". | |
22 | "acquire" will buy long or sell short, | |
23 | "dispose" will sell long or buy short. | |
24 | "currency": The currency to acquire or dispose | |
25 | "base_currency": The base currency. The value is expressed in that | |
26 | currency (default: BTC) | |
27 | "follow": Whether to follow the order once run (default: True) | |
28 | "close_if_possible": Whether to try to close the position at the end | |
29 | of the trade, i.e. reach exactly 0 at the end | |
30 | (only meaningful in "dispose"). May have | |
31 | unwanted effects if the end value of the | |
32 | currency is not 0. | |
33 | "compute_value": Compute value to place the order | |
34 | """ | |
35 | market.report.log_stage("make_order_begin") | |
36 | market.balances.fetch_balances(tag="make_order_begin") | |
37 | if action == "acquire": | |
38 | trade = portfolio.Trade( | |
39 | portfolio.Amount(base_currency, 0), | |
40 | portfolio.Amount(base_currency, value), | |
41 | currency, market) | |
42 | else: | |
43 | amount = portfolio.Amount(currency, value) | |
44 | trade = portfolio.Trade( | |
45 | amount.in_currency(base_currency, market, compute_value=compute_value), | |
46 | portfolio.Amount(base_currency, 0), | |
47 | currency, market) | |
48 | market.trades.all.append(trade) | |
49 | order = trade.prepare_order( | |
50 | close_if_possible=close_if_possible, | |
51 | compute_value=compute_value) | |
52 | market.report.log_orders([order], None, compute_value) | |
53 | market.trades.run_orders() | |
54 | if follow: | |
55 | market.follow_orders() | |
56 | market.balances.fetch_balances(tag="make_order_end") | |
57 | else: | |
58 | market.report.log_stage("make_order_end_not_followed") | |
59 | return order | |
60 | market.report.log_stage("make_order_end") | |
61 | ||
62 | def get_user_market(config_path, user_id, debug=False): | |
63 | pg_config, report_path = parse_config(config_path) | |
35667b31 | 64 | market_id, market_config, user_id = list(fetch_markets(pg_config, str(user_id)))[0] |
07fa7a4b | 65 | args = type('Args', (object,), { "debug": debug, "quiet": False })() |
35667b31 IB |
66 | return market.Market.from_config(market_config, args, |
67 | pg_config=pg_config, market_id=market_id, | |
68 | user_id=user_id, report_path=report_path) | |
1f117ac7 IB |
69 | |
70 | def fetch_markets(pg_config, user): | |
71 | connection = psycopg2.connect(**pg_config) | |
72 | cursor = connection.cursor() | |
73 | ||
74 | if user is None: | |
b4e0ba0b | 75 | cursor.execute("SELECT id,config,user_id FROM market_configs") |
1f117ac7 | 76 | else: |
b4e0ba0b | 77 | cursor.execute("SELECT id,config,user_id FROM market_configs WHERE user_id = %s", user) |
1f117ac7 IB |
78 | |
79 | for row in cursor: | |
80 | yield row | |
81 | ||
a42d6cc8 IB |
82 | def parse_config(args): |
83 | pg_config = { | |
84 | "host": args.db_host, | |
85 | "port": args.db_port, | |
86 | "user": args.db_user, | |
87 | "password": args.db_password, | |
88 | "database": args.db_database, | |
89 | } | |
90 | del(args.db_host) | |
91 | del(args.db_port) | |
92 | del(args.db_user) | |
93 | del(args.db_password) | |
94 | del(args.db_database) | |
1f117ac7 | 95 | |
a42d6cc8 | 96 | report_path = args.report_path |
1f117ac7 | 97 | |
a42d6cc8 IB |
98 | if report_path is not None and not \ |
99 | os.path.exists(report_path): | |
100 | os.makedirs(report_path) | |
1f117ac7 | 101 | |
a42d6cc8 | 102 | return pg_config |
1f117ac7 IB |
103 | |
104 | def parse_args(argv): | |
a42d6cc8 IB |
105 | parser = configargparse.ArgumentParser( |
106 | description="Run the trade bot.") | |
1f117ac7 IB |
107 | |
108 | parser.add_argument("-c", "--config", | |
109 | default="config.ini", | |
a42d6cc8 | 110 | required=False, is_config_file=True, |
1f117ac7 IB |
111 | help="Config file to load (default: config.ini)") |
112 | parser.add_argument("--before", | |
113 | default=False, action='store_const', const=True, | |
114 | help="Run the steps before the cryptoportfolio update") | |
115 | parser.add_argument("--after", | |
116 | default=False, action='store_const', const=True, | |
117 | help="Run the steps after the cryptoportfolio update") | |
07fa7a4b IB |
118 | parser.add_argument("--quiet", |
119 | default=False, action='store_const', const=True, | |
120 | help="Don't print messages") | |
1f117ac7 IB |
121 | parser.add_argument("--debug", |
122 | default=False, action='store_const', const=True, | |
123 | help="Run in debug mode") | |
124 | parser.add_argument("--user", | |
125 | default=None, required=False, help="Only run for that user") | |
126 | parser.add_argument("--action", | |
127 | action='append', | |
128 | help="Do a different action than trading (add several times to chain)") | |
dc1ca9a3 IB |
129 | parser.add_argument("--parallel", action='store_true', default=True, dest="parallel") |
130 | parser.add_argument("--no-parallel", action='store_false', dest="parallel") | |
a42d6cc8 IB |
131 | parser.add_argument("--report-db", action='store_true', default=True, dest="report_db", |
132 | help="Store report to database (default)") | |
133 | parser.add_argument("--no-report-db", action='store_false', dest="report_db", | |
134 | help="Don't store report to database") | |
135 | parser.add_argument("--report-path", required=False, | |
136 | help="Where to store the reports (default: absent, don't store)") | |
137 | parser.add_argument("--no-report-path", action='store_const', dest='report_path', const=None, | |
138 | help="Don't store the report to file (default)") | |
139 | parser.add_argument("--db-host", default="localhost", | |
140 | help="Host access to database (default: localhost)") | |
141 | parser.add_argument("--db-port", default=5432, | |
142 | help="Port access to database (default: 5432)") | |
143 | parser.add_argument("--db-user", default="cryptoportfolio", | |
144 | help="User access to database (default: cryptoportfolio)") | |
145 | parser.add_argument("--db-password", default="cryptoportfolio", | |
146 | help="Password access to database (default: cryptoportfolio)") | |
147 | parser.add_argument("--db-database", default="cryptoportfolio", | |
148 | help="Database access to database (default: cryptoportfolio)") | |
149 | ||
150 | return parser.parse_args(argv) | |
151 | ||
152 | def process(market_config, market_id, user_id, args, pg_config): | |
a18ce2f1 IB |
153 | try: |
154 | market.Market\ | |
a42d6cc8 IB |
155 | .from_config(market_config, args, market_id=market_id, |
156 | pg_config=pg_config, user_id=user_id)\ | |
a18ce2f1 IB |
157 | .process(args.action, before=args.before, after=args.after) |
158 | except Exception as e: | |
159 | print("{}: {}".format(e.__class__.__name__, e)) | |
160 | ||
1f117ac7 IB |
161 | def main(argv): |
162 | args = parse_args(argv) | |
163 | ||
a42d6cc8 | 164 | pg_config = parse_config(args) |
1f117ac7 | 165 | |
dc1ca9a3 IB |
166 | if args.parallel: |
167 | import threading | |
168 | market.Portfolio.start_worker() | |
169 | ||
35667b31 IB |
170 | def process_(*args): |
171 | threading.Thread(target=process, args=args).start() | |
dc1ca9a3 | 172 | else: |
35667b31 IB |
173 | process_ = process |
174 | ||
175 | for market_id, market_config, user_id in fetch_markets(pg_config, args.user): | |
a42d6cc8 | 176 | process_(market_config, market_id, user_id, args, pg_config) |
1f117ac7 IB |
177 | |
178 | if __name__ == '__main__': # pragma: no cover | |
179 | main(sys.argv[1:]) |