diff options
author | Ismaël Bouya <ismael.bouya@normalesup.org> | 2018-02-25 18:02:44 +0100 |
---|---|---|
committer | Ismaël Bouya <ismael.bouya@normalesup.org> | 2018-02-25 18:22:08 +0100 |
commit | f86ee14037646bedc3a3dee4a48f085308981757 (patch) | |
tree | e2190bf9d7cde94c4e8879ba4955691aeac40950 /helper.py | |
parent | eb9c92e155941b51042ba57e23f651454bd8e55a (diff) | |
download | Trader-f86ee14037646bedc3a3dee4a48f085308981757.tar.gz Trader-f86ee14037646bedc3a3dee4a48f085308981757.tar.zst Trader-f86ee14037646bedc3a3dee4a48f085308981757.zip |
Refactor the store to be more conciliant with multiple marketsv0.2
Diffstat (limited to 'helper.py')
-rw-r--r-- | helper.py | 295 |
1 files changed, 125 insertions, 170 deletions
@@ -1,178 +1,133 @@ | |||
1 | import time | 1 | from datetime import datetime |
2 | from ccxt import ExchangeError | 2 | import argparse |
3 | from store import * | 3 | import configparser |
4 | 4 | import psycopg2 | |
5 | def move_balances(market, debug=False): | 5 | import os |
6 | needed_in_margin = {} | 6 | import sys |
7 | moving_to_margin = {} | 7 | |
8 | 8 | import portfolio | |
9 | for currency in BalanceStore.all: | 9 | |
10 | if BalanceStore.all[currency].margin_free != 0: | 10 | def main_parse_args(argv): |
11 | needed_in_margin[currency] = 0 | 11 | parser = argparse.ArgumentParser( |
12 | for trade in TradeStore.all: | 12 | description="Run the trade bot") |
13 | if trade.value_to.currency not in needed_in_margin: | 13 | |
14 | needed_in_margin[trade.value_to.currency] = 0 | 14 | parser.add_argument("-c", "--config", |
15 | if trade.trade_type == "short": | 15 | default="config.ini", |
16 | needed_in_margin[trade.value_to.currency] += abs(trade.value_to) | 16 | required=False, |
17 | for currency, needed in needed_in_margin.items(): | 17 | help="Config file to load (default: config.ini)") |
18 | current_balance = BalanceStore.all[currency].margin_free | 18 | parser.add_argument("--before", |
19 | moving_to_margin[currency] = (needed - current_balance) | 19 | default=False, action='store_const', const=True, |
20 | delta = moving_to_margin[currency].value | 20 | help="Run the steps before the cryptoportfolio update") |
21 | if debug: | 21 | parser.add_argument("--after", |
22 | ReportStore.log_debug_action("Moving {} from exchange to margin".format(moving_to_margin[currency])) | 22 | default=False, action='store_const', const=True, |
23 | continue | 23 | help="Run the steps after the cryptoportfolio update") |
24 | if delta > 0: | 24 | parser.add_argument("--debug", |
25 | market.transfer_balance(currency, delta, "exchange", "margin") | 25 | default=False, action='store_const', const=True, |
26 | elif delta < 0: | 26 | help="Run in debug mode") |
27 | market.transfer_balance(currency, -delta, "margin", "exchange") | 27 | |
28 | ReportStore.log_move_balances(needed_in_margin, moving_to_margin, debug) | 28 | args = parser.parse_args(argv) |
29 | 29 | ||
30 | BalanceStore.fetch_balances(market) | 30 | if not os.path.exists(args.config): |
31 | 31 | print("no config file found, exiting") | |
32 | ticker_cache = {} | 32 | sys.exit(1) |
33 | ticker_cache_timestamp = time.time() | 33 | |
34 | def get_ticker(c1, c2, market, refresh=False): | 34 | return args |
35 | global ticker_cache, ticker_cache_timestamp | 35 | |
36 | def invert(ticker): | 36 | def main_parse_config(config_file): |
37 | return { | 37 | config = configparser.ConfigParser() |
38 | "inverted": True, | 38 | config.read(config_file) |
39 | "average": (1/ticker["bid"] + 1/ticker["ask"]) / 2, | 39 | |
40 | "original": ticker, | 40 | if "postgresql" not in config: |
41 | } | 41 | print("no configuration for postgresql in config file") |
42 | def augment_ticker(ticker): | 42 | sys.exit(1) |
43 | ticker.update({ | 43 | |
44 | "inverted": False, | 44 | if "app" in config and "report_path" in config["app"]: |
45 | "average": (ticker["bid"] + ticker["ask"] ) / 2, | 45 | report_path = config["app"]["report_path"] |
46 | }) | 46 | |
47 | 47 | if not os.path.exists(report_path): | |
48 | if time.time() - ticker_cache_timestamp > 5: | 48 | os.makedirs(report_path) |
49 | ticker_cache = {} | 49 | else: |
50 | ticker_cache_timestamp = time.time() | 50 | report_path = None |
51 | elif not refresh: | 51 | |
52 | if (c1, c2, market.__class__) in ticker_cache: | 52 | return [config["postgresql"], report_path] |
53 | return ticker_cache[(c1, c2, market.__class__)] | 53 | |
54 | if (c2, c1, market.__class__) in ticker_cache: | 54 | def main_fetch_markets(pg_config): |
55 | return invert(ticker_cache[(c2, c1, market.__class__)]) | 55 | connection = psycopg2.connect(**pg_config) |
56 | 56 | cursor = connection.cursor() | |
57 | |||
58 | cursor.execute("SELECT config,user_id FROM market_configs") | ||
59 | |||
60 | for row in cursor: | ||
61 | yield row | ||
62 | |||
63 | def main_process_market(user_market, before=False, after=False): | ||
64 | if before: | ||
65 | process_sell_all__1_all_sell(user_market) | ||
66 | if after: | ||
67 | portfolio.Portfolio.wait_for_recent(user_market) | ||
68 | process_sell_all__2_all_buy(user_market) | ||
69 | |||
70 | def main_store_report(report_path, user_id, user_market): | ||
57 | try: | 71 | try: |
58 | ticker_cache[(c1, c2, market.__class__)] = market.fetch_ticker("{}/{}".format(c1, c2)) | 72 | if report_path is not None: |
59 | augment_ticker(ticker_cache[(c1, c2, market.__class__)]) | 73 | report_file = "{}/{}_{}.json".format(report_path, datetime.now().isoformat(), user_id) |
60 | except ExchangeError: | 74 | with open(report_file, "w") as f: |
61 | try: | 75 | f.write(user_market.report.to_json()) |
62 | ticker_cache[(c2, c1, market.__class__)] = market.fetch_ticker("{}/{}".format(c2, c1)) | 76 | except Exception as e: |
63 | augment_ticker(ticker_cache[(c2, c1, market.__class__)]) | 77 | print("impossible to store report file: {}; {}".format(e.__class__.__name__, e)) |
64 | except ExchangeError: | ||
65 | ticker_cache[(c1, c2, market.__class__)] = None | ||
66 | return get_ticker(c1, c2, market) | ||
67 | |||
68 | fees_cache = {} | ||
69 | def fetch_fees(market): | ||
70 | global fees_cache | ||
71 | if market.__class__ not in fees_cache: | ||
72 | fees_cache[market.__class__] = market.fetch_fees() | ||
73 | return fees_cache[market.__class__] | ||
74 | |||
75 | def prepare_trades(market, base_currency="BTC", liquidity="medium", compute_value="average", debug=False): | ||
76 | ReportStore.log_stage("prepare_trades") | ||
77 | values_in_base = BalanceStore.in_currency(base_currency, market, compute_value=compute_value) | ||
78 | total_base_value = sum(values_in_base.values()) | ||
79 | new_repartition = BalanceStore.dispatch_assets(total_base_value, liquidity=liquidity) | ||
80 | # Recompute it in case we have new currencies | ||
81 | values_in_base = BalanceStore.in_currency(base_currency, market, compute_value=compute_value) | ||
82 | TradeStore.compute_trades(values_in_base, new_repartition, market=market, debug=debug) | ||
83 | |||
84 | def update_trades(market, base_currency="BTC", liquidity="medium", compute_value="average", only=None, debug=False): | ||
85 | ReportStore.log_stage("update_trades") | ||
86 | values_in_base = BalanceStore.in_currency(base_currency, market, compute_value=compute_value) | ||
87 | total_base_value = sum(values_in_base.values()) | ||
88 | new_repartition = BalanceStore.dispatch_assets(total_base_value, liquidity=liquidity) | ||
89 | TradeStore.compute_trades(values_in_base, new_repartition, only=only, market=market, debug=debug) | ||
90 | |||
91 | def prepare_trades_to_sell_all(market, base_currency="BTC", compute_value="average", debug=False): | ||
92 | ReportStore.log_stage("prepare_trades_to_sell_all") | ||
93 | values_in_base = BalanceStore.in_currency(base_currency, market, compute_value=compute_value) | ||
94 | total_base_value = sum(values_in_base.values()) | ||
95 | new_repartition = BalanceStore.dispatch_assets(total_base_value, repartition={ base_currency: (1, "long") }) | ||
96 | TradeStore.compute_trades(values_in_base, new_repartition, market=market, debug=debug) | ||
97 | |||
98 | def follow_orders(sleep=None): | ||
99 | if sleep is None: | ||
100 | sleep = 7 if TradeStore.debug else 30 | ||
101 | if TradeStore.debug: | ||
102 | ReportStore.log_debug_action("Set follow_orders tick to {}s".format(sleep)) | ||
103 | tick = 0 | ||
104 | ReportStore.log_stage("follow_orders_begin") | ||
105 | while len(TradeStore.all_orders(state="open")) > 0: | ||
106 | time.sleep(sleep) | ||
107 | tick += 1 | ||
108 | open_orders = TradeStore.all_orders(state="open") | ||
109 | ReportStore.log_stage("follow_orders_tick_{}".format(tick)) | ||
110 | ReportStore.log_orders(open_orders, tick=tick) | ||
111 | for order in open_orders: | ||
112 | if order.get_status() != "open": | ||
113 | ReportStore.log_order(order, tick, finished=True) | ||
114 | else: | ||
115 | order.trade.update_order(order, tick) | ||
116 | ReportStore.log_stage("follow_orders_end") | ||
117 | 78 | ||
118 | def print_orders(market, base_currency="BTC"): | 79 | def print_orders(market, base_currency="BTC"): |
119 | ReportStore.log_stage("print_orders") | 80 | market.report.log_stage("print_orders") |
120 | BalanceStore.fetch_balances(market, tag="print_orders") | 81 | market.balances.fetch_balances(tag="print_orders") |
121 | prepare_trades(market, base_currency=base_currency, compute_value="average", debug=True) | 82 | market.prepare_trades(base_currency=base_currency, compute_value="average") |
122 | TradeStore.prepare_orders(compute_value="average") | 83 | market.trades.prepare_orders(compute_value="average") |
123 | 84 | ||
124 | def print_balances(market, base_currency="BTC"): | 85 | def print_balances(market, base_currency="BTC"): |
125 | BalanceStore.fetch_balances(market) | 86 | market.balances.fetch_balances() |
126 | if base_currency is not None: | 87 | if base_currency is not None: |
127 | ReportStore.print_log("total:") | 88 | market.report.print_log("total:") |
128 | ReportStore.print_log(sum(BalanceStore.in_currency(base_currency, market).values())) | 89 | market.report.print_log(sum(market.balances.in_currency(base_currency).values())) |
129 | 90 | ||
130 | def reset_all(): | 91 | def process_sell_needed__1_sell(market, liquidity="medium", base_currency="BTC"): |
131 | # use them as regular classes, sub-object of market | 92 | market.report.log_stage("process_sell_needed__1_sell_begin") |
132 | ReportStore.logs = [] | 93 | market.balances.fetch_balances(tag="process_sell_needed__1_sell_begin") |
133 | BalanceStore.all = {} | 94 | market.prepare_trades(liquidity=liquidity, base_currency=base_currency) |
134 | TradeStore.all = [] | 95 | market.trades.prepare_orders(compute_value="average", only="dispose") |
135 | 96 | market.trades.run_orders() | |
136 | def process_sell_needed__1_sell(market, liquidity="medium", base_currency="BTC", debug=False): | 97 | market.follow_orders() |
137 | ReportStore.log_stage("process_sell_needed__1_sell_begin") | 98 | market.balances.fetch_balances(tag="process_sell_needed__1_sell_end") |
138 | BalanceStore.fetch_balances(market, tag="process_sell_needed__1_sell_begin") | 99 | market.report.log_stage("process_sell_needed__1_sell_end") |
139 | prepare_trades(market, liquidity=liquidity, base_currency=base_currency, debug=debug) | 100 | |
140 | TradeStore.prepare_orders(compute_value="average", only="dispose") | 101 | def process_sell_needed__2_buy(market, liquidity="medium", base_currency="BTC"): |
141 | TradeStore.run_orders() | 102 | market.report.log_stage("process_sell_needed__2_buy_begin") |
142 | follow_orders() | 103 | market.balances.fetch_balances(tag="process_sell_needed__2_buy_begin") |
143 | BalanceStore.fetch_balances(market, tag="process_sell_needed__1_sell_end") | 104 | market.update_trades(base_currency=base_currency, liquidity=liquidity, only="acquire") |
144 | ReportStore.log_stage("process_sell_needed__1_sell_end") | 105 | market.trades.prepare_orders(compute_value="average", only="acquire") |
145 | 106 | market.move_balances() | |
146 | def process_sell_needed__2_buy(market, liquidity="medium", base_currency="BTC", debug=False): | 107 | market.trades.run_orders() |
147 | ReportStore.log_stage("process_sell_needed__2_buy_begin") | 108 | market.follow_orders() |
148 | BalanceStore.fetch_balances(market, tag="process_sell_needed__2_buy_begin") | 109 | market.balances.fetch_balances(tag="process_sell_needed__2_buy_end") |
149 | update_trades(market, base_currency=base_currency, liquidity=liquidity, debug=debug, only="acquire") | 110 | market.report.log_stage("process_sell_needed__2_buy_end") |
150 | TradeStore.prepare_orders(compute_value="average", only="acquire") | 111 | |
151 | move_balances(market, debug=debug) | 112 | def process_sell_all__1_all_sell(market, base_currency="BTC", liquidity="medium"): |
152 | TradeStore.run_orders() | 113 | market.report.log_stage("process_sell_all__1_all_sell_begin") |
153 | follow_orders() | 114 | market.balances.fetch_balances(tag="process_sell_all__1_all_sell_begin") |
154 | BalanceStore.fetch_balances(market, tag="process_sell_needed__2_buy_end") | 115 | market.prepare_trades_to_sell_all(base_currency=base_currency) |
155 | ReportStore.log_stage("process_sell_needed__2_buy_end") | 116 | market.trades.prepare_orders(compute_value="average") |
156 | 117 | market.trades.run_orders() | |
157 | def process_sell_all__1_all_sell(market, base_currency="BTC", debug=False, liquidity="medium"): | 118 | market.follow_orders() |
158 | ReportStore.log_stage("process_sell_all__1_all_sell_begin") | 119 | market.balances.fetch_balances(tag="process_sell_all__1_all_sell_end") |
159 | BalanceStore.fetch_balances(market, tag="process_sell_all__1_all_sell_begin") | 120 | market.report.log_stage("process_sell_all__1_all_sell_end") |
160 | prepare_trades_to_sell_all(market, base_currency=base_currency, debug=debug) | 121 | |
161 | TradeStore.prepare_orders(compute_value="average") | 122 | def process_sell_all__2_all_buy(market, base_currency="BTC", liquidity="medium"): |
162 | TradeStore.run_orders() | 123 | market.report.log_stage("process_sell_all__2_all_buy_begin") |
163 | follow_orders() | 124 | market.balances.fetch_balances(tag="process_sell_all__2_all_buy_begin") |
164 | BalanceStore.fetch_balances(market, tag="process_sell_all__1_all_sell_end") | 125 | market.prepare_trades(liquidity=liquidity, base_currency=base_currency) |
165 | ReportStore.log_stage("process_sell_all__1_all_sell_end") | 126 | market.trades.prepare_orders(compute_value="average") |
166 | 127 | market.move_balances() | |
167 | def process_sell_all__2_all_buy(market, base_currency="BTC", debug=False, liquidity="medium"): | 128 | market.trades.run_orders() |
168 | ReportStore.log_stage("process_sell_all__2_all_buy_begin") | 129 | market.follow_orders() |
169 | BalanceStore.fetch_balances(market, tag="process_sell_all__2_all_buy_begin") | 130 | market.balances.fetch_balances(tag="process_sell_all__2_all_buy_end") |
170 | prepare_trades(market, liquidity=liquidity, base_currency=base_currency, debug=debug) | 131 | market.report.log_stage("process_sell_all__2_all_buy_end") |
171 | TradeStore.prepare_orders(compute_value="average") | ||
172 | move_balances(market, debug=debug) | ||
173 | TradeStore.run_orders() | ||
174 | follow_orders() | ||
175 | BalanceStore.fetch_balances(market, tag="process_sell_all__2_all_buy_end") | ||
176 | ReportStore.log_stage("process_sell_all__2_all_buy_end") | ||
177 | 132 | ||
178 | 133 | ||