1 from datetime
import datetime
10 def make_order(market
, value
, currency
, action
="acquire",
11 close_if_possible
=False, base_currency
="BTC", follow
=True,
12 compute_value
="average"):
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
31 "compute_value": Compute value to place the order
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
),
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),
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()
53 market
.follow_orders()
54 market
.balances
.fetch_balances(tag
="make_order_end")
56 market
.report
.log_stage("make_order_end_not_followed")
58 market
.report
.log_stage("make_order_end")
60 def get_user_market(config_path
, user_id
, debug
=False):
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
)
66 def main_parse_args(argv
):
67 parser
= argparse
.ArgumentParser(
68 description
="Run the trade bot")
70 parser
.add_argument("-c", "--config",
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",
87 help="Do a different action than trading (add several times to chain)")
89 args
= parser
.parse_args(argv
)
91 if not os
.path
.exists(args
.config
):
92 print("no config file found, exiting")
97 def main_parse_config(config_file
):
98 config
= configparser
.ConfigParser()
99 config
.read(config_file
)
101 if "postgresql" not in config
:
102 print("no configuration for postgresql in config file")
105 if "app" in config
and "report_path" in config
["app"]:
106 report_path
= config
["app"]["report_path"]
108 if not os
.path
.exists(report_path
):
109 os
.makedirs(report_path
)
113 return [config
["postgresql"], report_path
]
115 def main_fetch_markets(pg_config
, user
):
116 connection
= psycopg2
.connect(**pg_config
)
117 cursor
= connection
.cursor()
120 cursor
.execute("SELECT config,user_id FROM market_configs")
122 cursor
.execute("SELECT config,user_id FROM market_configs WHERE user_id = %s", user
)
127 def main_process_market(user_market
, actions
, before
=False, after
=False):
128 if len(actions
or []) == 0:
130 Processor(user_market
).process("sell_all", steps
="before")
132 Processor(user_market
).process("sell_all", steps
="after")
134 for action
in actions
:
135 if action
in globals():
136 (globals()[action
])(user_market
)
138 raise NotImplementedError("Unknown action {}".format(action
))
140 def main_store_report(report_path
, user_id
, user_market
):
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
))
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")
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()))
170 "wait_for_recent": {},
177 "fetch_balances": ["begin", "end"],
178 "prepare_trades": {},
179 "prepare_orders": { "only": "dispose", "compute_value": "average" }
,
188 "fetch_balances": ["begin", "end"],
189 "prepare_trades": { "only": "acquire" }
,
190 "prepare_orders": { "only": "acquire", "compute_value": "average" }
,
202 "fetch_balances": ["begin", "end"],
203 "prepare_trades": { "repartition": { "BTC": (1, "long") }
},
204 "prepare_orders": { "compute_value": "average" }
,
213 "wait_for_recent": {},
220 "fetch_balances": ["begin", "end"],
221 "prepare_trades": {},
222 "prepare_orders": { "compute_value": "average" }
,
230 allowed_arguments
= {
231 "wait_for_recent": ["delta"],
232 "prepare_trades": ["base_currency", "liquidity", "compute_value", "repartition", "only"],
233 "prepare_orders": ["only", "compute_value"],
236 "follow_orders": ["sleep"],
239 def __init__(self
, market
):
242 def select_steps(self
, scenario
, step
):
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
))
252 raise TypeError("Unknown step {}".format(step
))
254 def process(self
, scenario_name
, steps
="all", **kwargs
):
255 scenario
= self
.scenarios
[scenario_name
]
258 if type(steps
) == str or type(steps
) == int:
259 selected_steps
+= self
.select_steps(scenario
, steps
)
262 selected_steps
+= self
.select_steps(scenario
, step
)
263 for step
in selected_steps
:
264 self
.process_step(scenario_name
, step
, **kwargs
)
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
))
272 for action
in ["wait_for_recent", "prepare_trades",
273 "prepare_orders", "move_balances", "run_orders",
276 self
.run_action(action
, step
[action
], **kwargs
)
278 if "end" in step
.get("fetch_balances", []):
279 self
.market
.balances
.fetch_balances(tag
="{}_end".format(process_name
))
280 self
.market
.report
.log_stage("{}_end".format(process_name
))
282 def run_action(self
, action
, default_args
, **kwargs
):
283 args
= {k: v for k, v in {**default_args, **kwargs}
.items() if k
in self
.allowed_arguments
[action
] }
285 if action
== "wait_for_recent":
286 portfolio
.Portfolio
.wait_for_recent(self
.market
, **args
)
287 if action
== "prepare_trades":
288 self
.market
.prepare_trades(**args
)
289 if action
== "prepare_orders":
290 self
.market
.trades
.prepare_orders(**args
)
291 if action
== "move_balances":
292 self
.market
.move_balances(**args
)
293 if action
== "run_orders":
294 self
.market
.trades
.run_orders(**args
)
295 if action
== "follow_orders":
296 self
.market
.follow_orders(**args
)
298 def process_sell_needed__1_sell(market
, liquidity
="medium", base_currency
="BTC"):
299 Processor(market
).process("sell_needed", steps
="sell",
300 liquidity
=liquidity
, base_currency
=base_currency
)
302 def process_sell_needed__2_buy(market
, liquidity
="medium", base_currency
="BTC"):
303 Processor(market
).process("sell_needed", steps
="buy",
304 liquidity
=liquidity
, base_currency
=base_currency
)
306 def process_sell_all__1_all_sell(market
, base_currency
="BTC", liquidity
="medium"):
307 Processor(market
).process("sell_all", steps
="all_sell",
308 liquidity
=liquidity
, base_currency
=base_currency
)
310 def process_sell_all__2_wait(market
, liquidity
="medium", base_currency
="BTC"):
311 Processor(market
).process("sell_all", steps
="wait",
312 liquidity
=liquidity
, base_currency
=base_currency
)
314 def process_sell_all__3_all_buy(market
, base_currency
="BTC", liquidity
="medium"):
315 Processor(market
).process("sell_all", steps
="all_buy",
316 liquidity
=liquidity
, base_currency
=base_currency
)