diff options
author | Ismaël Bouya <ismael.bouya@normalesup.org> | 2018-03-06 00:21:52 +0100 |
---|---|---|
committer | Ismaël Bouya <ismael.bouya@normalesup.org> | 2018-03-06 00:21:52 +0100 |
commit | 1f117ac79e10c3c9728d3b267d134dec2a165603 (patch) | |
tree | 681df08b12cb059d0eba02710746f33a133fd187 /helper.py | |
parent | f9226903cb53a9b303a26de562e321159349f8df (diff) | |
download | Trader-1f117ac79e10c3c9728d3b267d134dec2a165603.tar.gz Trader-1f117ac79e10c3c9728d3b267d134dec2a165603.tar.zst Trader-1f117ac79e10c3c9728d3b267d134dec2a165603.zip |
Move helper methods to their due places
Diffstat (limited to 'helper.py')
-rw-r--r-- | helper.py | 320 |
1 files changed, 0 insertions, 320 deletions
diff --git a/helper.py b/helper.py deleted file mode 100644 index 8f726d5..0000000 --- a/helper.py +++ /dev/null | |||
@@ -1,320 +0,0 @@ | |||
1 | from datetime import datetime | ||
2 | import argparse | ||
3 | import configparser | ||
4 | import psycopg2 | ||
5 | import os | ||
6 | import sys | ||
7 | |||
8 | import portfolio | ||
9 | |||
10 | def make_order(market, value, currency, action="acquire", | ||
11 | close_if_possible=False, base_currency="BTC", follow=True, | ||
12 | compute_value="average"): | ||
13 | """ | ||
14 | Make an order on market | ||
15 | "market": The market on which to place the order | ||
16 | "value": The value in *base_currency* to acquire, | ||
17 | or in *currency* to dispose. | ||
18 | use negative for margin trade. | ||
19 | "action": "acquire" or "dispose". | ||
20 | "acquire" will buy long or sell short, | ||
21 | "dispose" will sell long or buy short. | ||
22 | "currency": The currency to acquire or dispose | ||
23 | "base_currency": The base currency. The value is expressed in that | ||
24 | currency (default: BTC) | ||
25 | "follow": Whether to follow the order once run (default: True) | ||
26 | "close_if_possible": Whether to try to close the position at the end | ||
27 | of the trade, i.e. reach exactly 0 at the end | ||
28 | (only meaningful in "dispose"). May have | ||
29 | unwanted effects if the end value of the | ||
30 | currency is not 0. | ||
31 | "compute_value": Compute value to place the order | ||
32 | """ | ||
33 | market.report.log_stage("make_order_begin") | ||
34 | market.balances.fetch_balances(tag="make_order_begin") | ||
35 | if action == "acquire": | ||
36 | trade = portfolio.Trade( | ||
37 | portfolio.Amount(base_currency, 0), | ||
38 | portfolio.Amount(base_currency, value), | ||
39 | currency, market) | ||
40 | else: | ||
41 | amount = portfolio.Amount(currency, value) | ||
42 | trade = portfolio.Trade( | ||
43 | amount.in_currency(base_currency, market, compute_value=compute_value), | ||
44 | portfolio.Amount(base_currency, 0), | ||
45 | currency, market) | ||
46 | market.trades.all.append(trade) | ||
47 | order = trade.prepare_order( | ||
48 | close_if_possible=close_if_possible, | ||
49 | compute_value=compute_value) | ||
50 | market.report.log_orders([order], None, compute_value) | ||
51 | market.trades.run_orders() | ||
52 | if follow: | ||
53 | market.follow_orders() | ||
54 | market.balances.fetch_balances(tag="make_order_end") | ||
55 | else: | ||
56 | market.report.log_stage("make_order_end_not_followed") | ||
57 | return order | ||
58 | market.report.log_stage("make_order_end") | ||
59 | |||
60 | def get_user_market(config_path, user_id, debug=False): | ||
61 | import market | ||
62 | pg_config, report_path = main_parse_config(config_path) | ||
63 | market_config = list(main_fetch_markets(pg_config, str(user_id)))[0][0] | ||
64 | return market.Market.from_config(market_config, debug=debug) | ||
65 | |||
66 | def main_parse_args(argv): | ||
67 | parser = argparse.ArgumentParser( | ||
68 | description="Run the trade bot") | ||
69 | |||
70 | parser.add_argument("-c", "--config", | ||
71 | default="config.ini", | ||
72 | required=False, | ||
73 | help="Config file to load (default: config.ini)") | ||
74 | parser.add_argument("--before", | ||
75 | default=False, action='store_const', const=True, | ||
76 | help="Run the steps before the cryptoportfolio update") | ||
77 | parser.add_argument("--after", | ||
78 | default=False, action='store_const', const=True, | ||
79 | help="Run the steps after the cryptoportfolio update") | ||
80 | parser.add_argument("--debug", | ||
81 | default=False, action='store_const', const=True, | ||
82 | help="Run in debug mode") | ||
83 | parser.add_argument("--user", | ||
84 | default=None, required=False, help="Only run for that user") | ||
85 | parser.add_argument("--action", | ||
86 | action='append', | ||
87 | help="Do a different action than trading (add several times to chain)") | ||
88 | |||
89 | args = parser.parse_args(argv) | ||
90 | |||
91 | if not os.path.exists(args.config): | ||
92 | print("no config file found, exiting") | ||
93 | sys.exit(1) | ||
94 | |||
95 | return args | ||
96 | |||
97 | def main_parse_config(config_file): | ||
98 | config = configparser.ConfigParser() | ||
99 | config.read(config_file) | ||
100 | |||
101 | if "postgresql" not in config: | ||
102 | print("no configuration for postgresql in config file") | ||
103 | sys.exit(1) | ||
104 | |||
105 | if "app" in config and "report_path" in config["app"]: | ||
106 | report_path = config["app"]["report_path"] | ||
107 | |||
108 | if not os.path.exists(report_path): | ||
109 | os.makedirs(report_path) | ||
110 | else: | ||
111 | report_path = None | ||
112 | |||
113 | return [config["postgresql"], report_path] | ||
114 | |||
115 | def main_fetch_markets(pg_config, user): | ||
116 | connection = psycopg2.connect(**pg_config) | ||
117 | cursor = connection.cursor() | ||
118 | |||
119 | if user is None: | ||
120 | cursor.execute("SELECT config,user_id FROM market_configs") | ||
121 | else: | ||
122 | cursor.execute("SELECT config,user_id FROM market_configs WHERE user_id = %s", user) | ||
123 | |||
124 | for row in cursor: | ||
125 | yield row | ||
126 | |||
127 | def main_process_market(user_market, actions, before=False, after=False): | ||
128 | if len(actions or []) == 0: | ||
129 | if before: | ||
130 | Processor(user_market).process("sell_all", steps="before") | ||
131 | if after: | ||
132 | Processor(user_market).process("sell_all", steps="after") | ||
133 | else: | ||
134 | for action in actions: | ||
135 | if action in globals(): | ||
136 | (globals()[action])(user_market) | ||
137 | else: | ||
138 | raise NotImplementedError("Unknown action {}".format(action)) | ||
139 | |||
140 | def main_store_report(report_path, user_id, user_market): | ||
141 | try: | ||
142 | if report_path is not None: | ||
143 | report_file = "{}/{}_{}.json".format(report_path, datetime.now().isoformat(), user_id) | ||
144 | with open(report_file, "w") as f: | ||
145 | f.write(user_market.report.to_json()) | ||
146 | except Exception as e: | ||
147 | print("impossible to store report file: {}; {}".format(e.__class__.__name__, e)) | ||
148 | |||
149 | def print_orders(market, base_currency="BTC"): | ||
150 | market.report.log_stage("print_orders") | ||
151 | market.balances.fetch_balances(tag="print_orders") | ||
152 | market.prepare_trades(base_currency=base_currency, compute_value="average") | ||
153 | market.trades.prepare_orders(compute_value="average") | ||
154 | |||
155 | def print_balances(market, base_currency="BTC"): | ||
156 | market.report.log_stage("print_balances") | ||
157 | market.balances.fetch_balances() | ||
158 | if base_currency is not None: | ||
159 | market.report.print_log("total:") | ||
160 | market.report.print_log(sum(market.balances.in_currency(base_currency).values())) | ||
161 | |||
162 | class Processor: | ||
163 | scenarios = { | ||
164 | "sell_needed": [ | ||
165 | { | ||
166 | "name": "wait", | ||
167 | "number": 0, | ||
168 | "before": False, | ||
169 | "after": True, | ||
170 | "wait_for_recent": {}, | ||
171 | }, | ||
172 | { | ||
173 | "name": "sell", | ||
174 | "number": 1, | ||
175 | "before": False, | ||
176 | "after": True, | ||
177 | "fetch_balances": ["begin", "end"], | ||
178 | "prepare_trades": {}, | ||
179 | "prepare_orders": { "only": "dispose", "compute_value": "average" }, | ||
180 | "run_orders": {}, | ||
181 | "follow_orders": {}, | ||
182 | "close_trades": {}, | ||
183 | }, | ||
184 | { | ||
185 | "name": "buy", | ||
186 | "number": 2, | ||
187 | "before": False, | ||
188 | "after": True, | ||
189 | "fetch_balances": ["begin", "end"], | ||
190 | "prepare_trades": { "only": "acquire" }, | ||
191 | "prepare_orders": { "only": "acquire", "compute_value": "average" }, | ||
192 | "move_balances": {}, | ||
193 | "run_orders": {}, | ||
194 | "follow_orders": {}, | ||
195 | "close_trades": {}, | ||
196 | }, | ||
197 | ], | ||
198 | "sell_all": [ | ||
199 | { | ||
200 | "name": "all_sell", | ||
201 | "number": 1, | ||
202 | "before": True, | ||
203 | "after": False, | ||
204 | "fetch_balances": ["begin", "end"], | ||
205 | "prepare_trades": { "repartition": { "base_currency": (1, "long") } }, | ||
206 | "prepare_orders": { "compute_value": "average" }, | ||
207 | "run_orders": {}, | ||
208 | "follow_orders": {}, | ||
209 | "close_trades": {}, | ||
210 | }, | ||
211 | { | ||
212 | "name": "wait", | ||
213 | "number": 2, | ||
214 | "before": False, | ||
215 | "after": True, | ||
216 | "wait_for_recent": {}, | ||
217 | }, | ||
218 | { | ||
219 | "name": "all_buy", | ||
220 | "number": 3, | ||
221 | "before": False, | ||
222 | "after": True, | ||
223 | "fetch_balances": ["begin", "end"], | ||
224 | "prepare_trades": {}, | ||
225 | "prepare_orders": { "compute_value": "average" }, | ||
226 | "move_balances": {}, | ||
227 | "run_orders": {}, | ||
228 | "follow_orders": {}, | ||
229 | "close_trades": {}, | ||
230 | }, | ||
231 | ] | ||
232 | } | ||
233 | |||
234 | ordered_actions = [ | ||
235 | "wait_for_recent", "prepare_trades", "prepare_orders", | ||
236 | "move_balances", "run_orders", "follow_orders", | ||
237 | "close_trades"] | ||
238 | |||
239 | def __init__(self, market): | ||
240 | self.market = market | ||
241 | |||
242 | def select_steps(self, scenario, step): | ||
243 | if step == "all": | ||
244 | return scenario | ||
245 | elif step == "before" or step == "after": | ||
246 | return list(filter(lambda x: step in x and x[step], scenario)) | ||
247 | elif type(step) == int: | ||
248 | return [scenario[step-1]] | ||
249 | elif type(step) == str: | ||
250 | return list(filter(lambda x: x["name"] == step, scenario)) | ||
251 | else: | ||
252 | raise TypeError("Unknown step {}".format(step)) | ||
253 | |||
254 | def process(self, scenario_name, steps="all", **kwargs): | ||
255 | scenario = self.scenarios[scenario_name] | ||
256 | selected_steps = [] | ||
257 | |||
258 | if type(steps) == str or type(steps) == int: | ||
259 | selected_steps += self.select_steps(scenario, steps) | ||
260 | else: | ||
261 | for step in steps: | ||
262 | selected_steps += self.select_steps(scenario, step) | ||
263 | for step in selected_steps: | ||
264 | self.process_step(scenario_name, step, kwargs) | ||
265 | |||
266 | def process_step(self, scenario_name, step, kwargs): | ||
267 | process_name = "process_{}__{}_{}".format(scenario_name, step["number"], step["name"]) | ||
268 | self.market.report.log_stage("{}_begin".format(process_name)) | ||
269 | if "begin" in step.get("fetch_balances", []): | ||
270 | self.market.balances.fetch_balances(tag="{}_begin".format(process_name)) | ||
271 | |||
272 | for action in self.ordered_actions: | ||
273 | if action in step: | ||
274 | self.run_action(action, step[action], kwargs) | ||
275 | |||
276 | if "end" in step.get("fetch_balances", []): | ||
277 | self.market.balances.fetch_balances(tag="{}_end".format(process_name)) | ||
278 | self.market.report.log_stage("{}_end".format(process_name)) | ||
279 | |||
280 | def method_arguments(self, action): | ||
281 | import inspect | ||
282 | |||
283 | if action == "wait_for_recent": | ||
284 | method = portfolio.Portfolio.wait_for_recent | ||
285 | elif action == "prepare_trades": | ||
286 | method = self.market.prepare_trades | ||
287 | elif action == "prepare_orders": | ||
288 | method = self.market.trades.prepare_orders | ||
289 | elif action == "move_balances": | ||
290 | method = self.market.move_balances | ||
291 | elif action == "run_orders": | ||
292 | method = self.market.trades.run_orders | ||
293 | elif action == "follow_orders": | ||
294 | method = self.market.follow_orders | ||
295 | elif action == "close_trades": | ||
296 | method = self.market.trades.close_trades | ||
297 | |||
298 | signature = inspect.getfullargspec(method) | ||
299 | defaults = signature.defaults or [] | ||
300 | kwargs = signature.args[-len(defaults):] | ||
301 | |||
302 | return [method, kwargs] | ||
303 | |||
304 | def parse_args(self, action, default_args, kwargs): | ||
305 | method, allowed_arguments = self.method_arguments(action) | ||
306 | args = {k: v for k, v in {**default_args, **kwargs}.items() if k in allowed_arguments } | ||
307 | |||
308 | if "repartition" in args and "base_currency" in args["repartition"]: | ||
309 | r = args["repartition"] | ||
310 | r[args.get("base_currency", "BTC")] = r.pop("base_currency") | ||
311 | |||
312 | return method, args | ||
313 | |||
314 | def run_action(self, action, default_args, kwargs): | ||
315 | method, args = self.parse_args(action, default_args, kwargs) | ||
316 | |||
317 | if action == "wait_for_recent": | ||
318 | method(self.market, **args) | ||
319 | else: | ||
320 | method(**args) | ||