def update_min_cap(cfg, deal_cap, processor): cur_timest_sec = get_now_seconds_utc() tickers = get_ticker_for_arbitrage( cfg.pair_id, cur_timest_sec, [cfg.buy_exchange_id, cfg.sell_exchange_id], processor) new_cap = compute_new_min_cap_from_tickers(cfg.pair_id, tickers) if new_cap > 0: msg = "Updating old cap {op}".format(op=deal_cap) log_to_file(msg, CAP_ADJUSTMENT_TRACE_LOG_FILE_NAME) deal_cap.update_min_volume_cap(new_cap, cur_timest_sec) msg = "New cap {op}".format(op=deal_cap) log_to_file(msg, CAP_ADJUSTMENT_TRACE_LOG_FILE_NAME) else: msg = """CAN'T update minimum_volume_cap for {pair_id} at following exchanges: {exch1} {exch2}""".format( pair_id=cfg.pair_id, exch1=get_exchange_name_by_id(cfg.buy_exchange_id), exch2=get_exchange_name_by_id(cfg.sell_exchange_id)) print_to_console(msg, LOG_ALL_ERRORS) log_to_file(msg, cfg.log_file_name) log_to_file(msg, CAP_ADJUSTMENT_TRACE_LOG_FILE_NAME)
def __str__(self): str_repr = """Sell=Bid exchange - {sell_exch} id = {id1} Buy-Ask exchange - {buy_exch} id = {id2} currency pair - {pair} Arbitrage Threshold = {thrshld} Reverse Threshold = {rv_thr} Balance Threshold = {b_thr} deal_expire_timeout = {deal_expire_timeout} cfg_file_name = {cfg_file_name} log_file_name = {log_file_name} cap_update_timeout = {cap_update_timeout} balance_update_timeout = {balance_update_timeout} """.format( sell_exch=get_exchange_name_by_id(self.sell_exchange_id), id1=self.sell_exchange_id, buy_exch=get_exchange_name_by_id(self.buy_exchange_id), id2=self.buy_exchange_id, pair=get_pair_name_by_id(self.pair_id), pair_id = self.pair_id, thrshld=self.threshold, rv_thr=self.reverse_threshold, b_thr=self.balance_threshold, cfg_file_name=get_debug_level_name_by_id(self.cfg_file_name), deal_expire_timeout=self.deal_expire_timeout, log_file_name=self.log_file_name, cap_update_timeout=self.cap_update_timeout, balance_update_timeout=self.balance_update_timeout ) return str_repr
def log_currency_disbalance_heart_beat(src_exchange_id, dst_exchange_id, currency_id, treshold_reverse): msg = "No disbalance at Exchanges {exch1} {exch2} for {pair_id} with {thrs}".format( exch1=get_exchange_name_by_id(src_exchange_id), exch2=get_exchange_name_by_id(dst_exchange_id), pair_id=get_currency_name_by_id(currency_id), thrs=treshold_reverse ) print_to_console(msg, LOG_ALL_MARKET_NETWORK_RELATED_CRAP) log_to_file(msg, DEBUG_LOG_FILE_NAME)
def log_cant_update_volume_cap(pair_id, buy_exchange_id, sell_exchange_id, log_file_name): msg = """CAN'T update minimum_volume_cap for {pair_id} at following exchanges: {exch1} {exch2}""".format( pair_id=pair_id, exch1=get_exchange_name_by_id(buy_exchange_id), exch2=get_exchange_name_by_id(sell_exchange_id)) log_to_file(msg, log_file_name) log_to_file(msg, CAP_ADJUSTMENT_TRACE_LOG_FILE_NAME) print_to_console(msg, LOG_ALL_ERRORS)
def log_arbitrage_heart_beat(sell_order_book, buy_order_book, difference): msg = """check_highest_bid_bigger_than_lowest_ask: \tFor pair - {pair_name} \tExchange1 - {exch1} BID = {bid} \tExchange2 - {exch2} ASK = {ask} \tDIFF = {diff}""".format(pair_name=get_pair_name_by_id(sell_order_book.pair_id), exch1=get_exchange_name_by_id(sell_order_book.exchange_id), bid=float_to_str(sell_order_book.bid[FIRST].price), exch2=get_exchange_name_by_id(buy_order_book.exchange_id), ask=float_to_str(buy_order_book.ask[LAST].price), diff=difference) print_to_console(msg, LOG_ALL_MARKET_NETWORK_RELATED_CRAP) log_to_file(msg, DEBUG_LOG_FILE_NAME)
def log_arbitrage_determined_volume_not_enough(sell_order_book, buy_order_book, msg_queue): msg = """analyse order book - DETERMINED volume of deal is not ENOUGH {pair_name}: first_exchange: {first_exchange} first exchange volume: <b>{vol1}</b> second_exchange: {second_exchange} second_exchange_volume: <b>{vol2}</b>""".format( pair_name=get_pair_name_by_id(sell_order_book.pair_id), first_exchange=get_exchange_name_by_id(sell_order_book.exchange_id), second_exchange=get_exchange_name_by_id(buy_order_book.exchange_id), vol1=float_to_str(sell_order_book.bid[FIRST].volume), vol2=float_to_str(buy_order_book.ask[LAST].volume)) print_to_console(msg, LOG_ALL_MARKET_NETWORK_RELATED_CRAP) log_to_file(msg, DEBUG_LOG_FILE_NAME) if get_logging_level() >= LOG_ALL_TRACE: msg_queue.add_message(DEBUG_INFO_MSG, msg)
def log_balance_expired_errors(cfg, msg_queue, balance_state): msg = """<b> !!! CRITICAL !!! </b> Balance is OUTDATED for {exch1} or {exch2} for more than {tt} seconds Arbitrage process will be stopped just in case. Check log file: {lf}""".format( exch1=get_exchange_name_by_id(cfg.buy_exchange_id), exch2=get_exchange_name_by_id(cfg.sell_exchange_id), tt=BALANCE_EXPIRED_THRESHOLD, lf=cfg.log_file_name) print_to_console(msg, LOG_ALL_ERRORS) msg_queue.add_message(DEAL_INFO_MSG, msg) log_to_file(msg, cfg.log_file_name) log_to_file(balance_state, cfg.log_file_name)
def log_dont_supported_currency(cfg, exchange_id, pair_id): msg = "Not supported currency {idx}-{name} for {exch}".format( idx=cfg.pair_id, name=pair_id, exch=get_exchange_name_by_id(exchange_id)) print_to_console(msg, LOG_ALL_ERRORS) log_to_file(msg, cfg.log_file_name)
def log_dublicative_order_book(log_file_name, msg_queue, order_book, prev_order_book): msg = """ <b> !!! WARNING !!! </b> Number of similar asks OR bids are the same for the most recent and cached version of order book for exchange_name {exch} pair_name {pn} cached timest: {ts1} {dt1} recent timest: {ts2} {dt2} Verbose information can be found in logs error & """.format(exch=get_exchange_name_by_id(order_book.exchange_id), pn=get_currency_pair_name_by_exchange_id( order_book.pair_id, order_book.exchange_id), ts1=prev_order_book.timest, dt1=ts_to_string_utc(prev_order_book.timest), ts2=order_book.timest, dt2=ts_to_string_utc(order_book.timest)) msg_queue.add_message(DEAL_INFO_MSG, msg) print_to_console(msg, LOG_ALL_ERRORS) log_to_file(msg, log_file_name) msg = """Cached version of order book: {o} Recent version of order book: {oo} """.format(o=str(prev_order_book), oo=str(order_book)) log_to_file(msg, log_file_name)
def log_order_book_update_failed_pre_sync(kind, exchange_id, order_book_updates): msg = "Reset stage will be initiated because Orderbook update FAILED during pre-SYNC stage - {kind} - " \ "for {exch_name} Update itself: {upd}".format(kind=kind, exch_name=get_exchange_name_by_id(exchange_id), upd=order_book_updates) log_to_file(msg, SOCKET_ERRORS_LOG_FILE_NAME) print_to_console(msg, LOG_ALL_ERRORS)
def log_currency_disbalance_present(src_exchange_id, dst_exchange_id, pair_id, currency_id, balance_threshold, new_max_cap_volume, treshold): msg = """We have disbalance! Exchanges {exch1} {exch2} for {pair_id} with {balance_threshold}. Set max cap for {currency} to {vol} and try to find price diff more than {thrs}""".format( exch1=get_exchange_name_by_id(src_exchange_id), exch2=get_exchange_name_by_id(dst_exchange_id), pair_id=get_pair_name_by_id(pair_id), balance_threshold=balance_threshold, currency=get_currency_name_by_id(currency_id), vol=new_max_cap_volume, thrs=treshold ) print_to_console(msg, LOG_ALL_MARKET_NETWORK_RELATED_CRAP) log_to_file(msg, "history_trades.log") log_to_file(msg, "cap_price_adjustment.log")
def __init__(self, pair_id, lowest_ask, highest_bid, timest, exchange_id): self.pair_id = int(pair_id) self.pair = get_pair_name_by_id(self.pair_id) self.ask = Decimal(lowest_ask) self.bid = Decimal(highest_bid) self.timest = long(timest) self.exchange_id = int(exchange_id) self.exchange = get_exchange_name_by_id(self.exchange_id)
def log_balance_expired(exchange_id, threshold, balance_state, msg_queue): msg = """<b> !!! CRITICAL !!! </b> Balance is OUTDATED for {exch1} for more than {tt} seconds Expired or failed orders service will be stopped just in case. """.format(exch1=get_exchange_name_by_id(exchange_id), tt=threshold) print_to_console(msg, LOG_ALL_ERRORS) msg_queue.add_message(DEAL_INFO_MSG, msg) log_to_file(msg, ERROR_LOG_FILE_NAME) log_to_file(balance_state, ERROR_LOG_FILE_NAME)
def __iter__(self): return iter([ self.arbitrage_id, get_exchange_name_by_id(self.exchange_id), get_pair_name_by_id(self.pair_id), get_order_type_by_id(self.trade_type), self.price, self.volume, self.order_book_time, self.create_time, self.execute_time, ts_to_string_local(self.execute_time), self.order_id, self.trade_id, self.executed_volume ])
def get_balance(exchange_id, cache=get_cache()): exchange_name = get_exchange_name_by_id(exchange_id) balance = cache.get_balance(exchange_id) while balance is None: status_code, balance = update_balance_by_exchange(exchange_id) if not balance: msg = "ERROR: BALANCE IS STILL NONE!!! for {n}".format( n=exchange_name) print_to_console(msg, LOG_ALL_ERRORS) return balance
def __init__(self, pair_id, timest, deal_type, price, amount, total, exchange_id): # FIXME NOTE - various volume data? self.pair_id = int(pair_id) self.pair = get_pair_name_by_id(self.pair_id) self.timest = long(timest) self.deal_type = deal_type self.price = Decimal(price) self.amount = Decimal(amount) self.total = Decimal(total) self.exchange_id = int(exchange_id) self.exchange = get_exchange_name_by_id(self.exchange_id)
def __init__(self, pair_id, timest, price_high, price_low, price_open, price_close, exchange_id): # FIXME NOTE - various volume data? self.pair_id = int(pair_id) self.pair = get_pair_name_by_id(self.pair_id) self.timest = long(timest) self.high = Decimal(price_high) self.low = Decimal(price_low) self.open = Decimal(price_open) self.close = Decimal(price_close) self.exchange_id = int(exchange_id) self.exchange = get_exchange_name_by_id(self.exchange_id)
def load_keys(path): """ :param path: full path to folder with public keys, each key should be named as corresponding exchange :return: """ global access_keys for exchange_id in EXCHANGE.values(): exchange_name = get_exchange_name_by_id(exchange_id) key = ExchangeKey.from_file(path, exchange_name) access_keys[exchange_id] = key
def log_not_enough_base_currency(exchange_id, currency_id, threshold, balance_for_exchange, msg_queue): msg = """<b> !!! INFO !!! </b> {base_currency} balance on exchange {exch} BELOW threshold {thrs} - only {am} LEFT!""".format( base_currency=get_currency_name_by_id(currency_id), thrs=threshold, exch=get_exchange_name_by_id(exchange_id), am=balance_for_exchange.get_balance(currency_id)) msg_queue.add_message(DEAL_INFO_MSG, msg) print_to_console(msg, LOG_ALL_ERRORS) print_to_console(balance_for_exchange, LOG_ALL_MARKET_RELATED_CRAP) log_to_file(str(balance_for_exchange), "balance.log")
def print_top10(exchange_id, order_book_buy, order_book_sell): header = "Number of threads: {tn} Last update {ts} {td} from {up_exch_name}\n" \ "\n{buy_exchange}\t\t\t\t\t\t\t\t\t{sell_exchange}\n".\ format(tn=threading.active_count(), ts=get_now_seconds_utc(), td=ts_to_string_utc(get_now_seconds_utc()), up_exch_name=get_exchange_name_by_id(exchange_id), buy_exchange=get_exchange_name_by_id(order_book_buy.exchange_id), sell_exchange=get_exchange_name_by_id(order_book_sell.exchange_id) ) os.system('clear') print(header) print(BIDS_HEADLINE) lb1 = len(order_book_buy.bid) lb2 = len(order_book_sell.bid) for idx in xrange(0, 10): if idx < lb1: print order_book_buy.bid[idx], else: print None, print ORDER_BOOK_DELIMITER, if idx < lb2: print order_book_sell.bid[idx] else: print None print(ASKS_HEADLINE) la1 = len(order_book_buy.ask) la2 = len(order_book_sell.ask) for idx in xrange(0, 10): if idx < la1: print order_book_buy.ask[idx], else: print None print ORDER_BOOK_DELIMITER, if idx < la2: print order_book_sell.ask[idx] else: print None
def update_balance_by_exchange(exchange_id, cache=get_cache()): status_code, balance = get_balance_by_exchange(exchange_id) exchange_name = get_exchange_name_by_id(exchange_id) if status_code == STATUS.SUCCESS and balance is not None: cache.update_balance(exchange_name, balance) log_to_file("Update balance at cache", "balance.log") log_to_file(balance, "balance.log") msg = "Can't update balance for exchange_id = {exch1} {exch_name}".format( exch1=exchange_id, exch_name=exchange_name) log_to_file(msg, "cache.log") log_to_file(msg, "balance.log") return status_code, balance
def __str__(self): str_repr = "Balance at Exchange: {exch} Last updated: {dt} timest: {ts} %".format( exch=get_exchange_name_by_id(self.exchange_id), dt=ts_to_string_local(self.last_update), ts=self.last_update) str_repr += " Available balance:" for currency_id in self.available_balance: str_repr += " " + get_currency_name_by_id(currency_id) + " - " + str(self.available_balance[currency_id]) str_repr += " Total balance:" for currency_id in self.total_balance: str_repr += " " + get_currency_name_by_id(currency_id) + " - " + str(self.total_balance[currency_id]) return str_repr
def __init__(self, pair_id, timest, sell_bids, buy_bids, exchange_id, sequence_id=None): # FIXME NOTE - various volume data? self.pair_id = int(pair_id) self.pair_name = get_pair_name_by_id(self.pair_id) self.timest = timest self.ask = sell_bids self.bid = buy_bids self.exchange_id = int(exchange_id) self.exchange = get_exchange_name_by_id(self.exchange_id) self.sequence_id = sequence_id
def __str__(self): str_repr = """ Trade at Exchange: {exch} Type: {deal_type} Pair: {pair} for volume {vol} with price {price} order_book_time {ob_time} create_time {ct_time} execute_time {ex_time} Executed at: {dt} order_id {order_id} trade_id {trade_id} executed_volume {ex_volume} arbitrage_id {a_id} """.format(exch=get_exchange_name_by_id(self.exchange_id), deal_type=get_order_type_by_id(self.trade_type), pair=get_pair_name_by_id(self.pair_id), vol=truncate_float(self.volume, 8), price=truncate_float(self.price, 8), ob_time=self.order_book_time, ct_time=self.create_time, ex_time=self.execute_time, dt=ts_to_string_local(self.execute_time), order_id=self.order_id, trade_id=self.trade_id, ex_volume=self.executed_volume, a_id=self.arbitrage_id) return str_repr
def on_order_book_update(self, exchange_id, order_book_updates): """ :param exchange_id: :param order_book_updates: parsed OrderBook or OrderBookUpdates according to exchange specs :param stage: whether BOTH orderbook synced or NOT :return: """ exchange_name = get_exchange_name_by_id(exchange_id) print_to_console("Got update for {exch} Current number of threads: {thr_num}" .format(exch=exchange_name, thr_num=threading.active_count()), LOG_ALL_ERRORS) current_stage = get_stage() if not self.buy_subscription.is_running() or not self.sell_subscription.is_running(): log_one_of_subscriptions_failed(self.buy_subscription.is_running(), self.sell_subscription.is_running(), current_stage) self.shutdown_subscriptions() return if order_book_updates is None: print_to_console("Order book update is NONE! for {}".format(exchange_name), LOG_ALL_ERRORS) return if current_stage == ORDER_BOOK_SYNC_STAGES.BEFORE_SYNC: print_to_console("Syncing in progress ...", LOG_ALL_ERRORS) if exchange_id == self.buy_exchange_id: if self.buy_order_book_synced: order_book_update_status = self.order_book_buy.update(exchange_id, order_book_updates) if order_book_update_status == STATUS.FAILURE: log_order_book_update_failed_pre_sync("BUY", exchange_id, order_book_updates) self.shutdown_subscriptions() else: self.buy_exchange_updates.put(order_book_updates) else: if self.sell_order_book_synced: order_book_update_status = self.order_book_sell.update(exchange_id, order_book_updates) if order_book_update_status == STATUS.FAILURE: log_order_book_update_failed_pre_sync("SELL", exchange_id, order_book_updates) self.shutdown_subscriptions() else: self.sell_exchange_updates.put(order_book_updates) elif current_stage == ORDER_BOOK_SYNC_STAGES.AFTER_SYNC: print_to_console("Update after syncing... {}".format(exchange_name), LOG_ALL_ERRORS) if exchange_id == self.buy_exchange_id: order_book_update_status = self.order_book_buy.update(exchange_id, order_book_updates) if order_book_update_status == STATUS.FAILURE: log_order_book_update_failed_post_sync(exchange_id, order_book_updates) self.shutdown_subscriptions() return else: order_book_update_status = self.order_book_sell.update(exchange_id, order_book_updates) if order_book_update_status == STATUS.FAILURE: log_order_book_update_failed_post_sync(exchange_id, order_book_updates) self.shutdown_subscriptions() return # # Remove this line to activate trading # print_top10(exchange_id, self.order_book_buy, self.order_book_sell) if not YES_I_KNOW_WHAT_AM_I_DOING: die_hard("LIVE TRADING!") # DK NOTE: only at this stage we are ready for searching for arbitrage # for mode_id in [DEAL_TYPE.ARBITRAGE, DEAL_TYPE.REVERSE]: # method = search_for_arbitrage if mode_id == DEAL_TYPE.ARBITRAGE else adjust_currency_balance # active_threshold = self.threshold if mode_id == DEAL_TYPE.ARBITRAGE else self.reverse_threshold # FIXME NOTE: order book expiration check # FIXME NOTE: src dst vs buy sell ts1 = get_now_seconds_utc_ms() status_code, deal_pair = search_for_arbitrage(self.order_book_sell, self.order_book_buy, self.threshold, self.balance_threshold, init_deals_with_logging_speedy, self.balance_state, self.deal_cap, type_of_deal=DEAL_TYPE.ARBITRAGE, worker_pool=self.processor, msg_queue=self.msg_queue) ts2 = get_now_seconds_utc_ms() msg = "Start: {ts1} ms End: {ts2} ms Runtime: {d} ms".format(ts1=ts1, ts2=ts2, d=ts2-ts1) # # FIXME # # Yeah, we write to disk after every trade # Yeah, it is not really about speed :( # log_to_file(msg, "profile.txt") add_orders_to_watch_list(deal_pair, self.priority_queue) self.deal_cap.update_max_volume_cap(NO_MAX_CAP_LIMIT)
def load_key_by_exchange(path, exchange_id): global access_keys exchange_name = get_exchange_name_by_id(exchange_id) key = ExchangeKey.from_file(path, exchange_name) access_keys[exchange_id] = key
def save_report(start_time, end_time, profit_by_base, profit_details, missing_orders, failed_orders, loss_details, loss_by_base, orders, history_trades, file_name="what_we_have_at_the_end.log"): msg = "Profit report for time period of {t1} - {t2}".format( t1=ts_to_string_local(start_time), t2=ts_to_string_local(end_time)) log_to_file(msg, file_name) msg = "Epoch format: {t1} - {t2}".format(t1=start_time, t2=end_time) log_to_file(msg, file_name) orders_by_arbitrage_id = group_orders_by_arbitrage_id(orders) log_to_file( "Total number of arbitrage events - {p}".format( p=len(orders_by_arbitrage_id)), file_name) log_to_file("For them we registered - {p} orders".format(p=len(orders)), file_name) log_to_file( "Resulted number of trades - {p}".format(p=len(history_trades)), file_name) for base_currency_id in profit_details: log_to_file( "\t\tTotal profit by {cn} - {nn}".format( cn=get_currency_name_by_id(base_currency_id), nn=float_to_str(profit_by_base[base_currency_id])), file_name) for details_by_pair_id in profit_details[base_currency_id]: log_to_file(details_by_pair_id, file_name) total_number_missing = 0 for entry in missing_orders: total_number_missing += len(missing_orders[entry]) msg = "Total number of orders without trades (Expired?): {n}".format( n=total_number_missing) log_to_file(msg, file_name) for exchange_id in missing_orders: msg = "\t{exch} Number of orders without trades: {n}".format( exch=get_exchange_name_by_id(exchange_id), n=len(missing_orders[exchange_id])) log_to_file(msg, file_name) for exchange_id in missing_orders: msg = "Missing orders for {exch}".format( exch=get_exchange_name_by_id(exchange_id)) log_to_file(msg, "missing_orders.log") for x in missing_orders[exchange_id]: log_to_file(x, "missing_orders.log") total_number_failed = 0 for exchange_id in failed_orders: total_number_failed += len(failed_orders[exchange_id]) msg = "Total number of orders without order_id (Failed?): {n}".format( n=total_number_failed) log_to_file(msg, file_name) for exchange_id in failed_orders: msg = "\t{exch} Number of orders without trades: {n}".format( exch=get_exchange_name_by_id(exchange_id), n=len(failed_orders[exchange_id])) log_to_file(msg, file_name) for exchange_id in failed_orders: msg = "Failed orders for {exch}".format( exch=get_exchange_name_by_id(exchange_id)) log_to_file(msg, "failed_orders.log") for x in failed_orders[exchange_id]: log_to_file(x, "failed_orders.log") log_to_file("\t\tLOSS DETAILS", file_name) for base_currency_id in loss_details: log_to_file( "\t\tLoss details by {cn} - {nn}".format( cn=get_currency_name_by_id(base_currency_id), nn=float_to_str(loss_by_base[base_currency_id])), file_name) for details_by_pair_id in loss_details[base_currency_id]: log_to_file(details_by_pair_id, file_name)
exchanges = [x.strip() for x in config.get("common","exchanges").split(",") if len(x.strip()) > 0] exchange_settings = defaultdict(list) for exchange_name in exchanges: exchange_id = get_exchange_id_by_name(exchange_name) trade_with = [x.strip() for x in config.get(exchange_name,"exchanges").split(",") if len(x.strip()) > 0] if len(trade_with) == 0: continue for dst_exchange_name in trade_with: pairs = [x.strip() for x in config.get(exchange_name, dst_exchange_name).split(",") if len(x.strip()) > 0] if len(pairs) > 0: exchange_settings[exchange_id].append(ExchangeArbitrageSettings(exchange_name, dst_exchange_name, pairs)) for b in exchange_settings: print b, get_exchange_name_by_id(b) for x in exchange_settings[b]: print x arbitrage_unit = {} for exchange_id in exchange_settings: for settings in exchange_settings[exchange_id]: dst_exchanges_id = settings.dst_exchange_id exchange_pairs = [[exchange_id, dst_exchanges_id], [dst_exchanges_id, exchange_id]] for sell_exchange_id, buy_exchange_id in exchange_pairs: screen_name = generate_screen_name(sell_exchange_id, buy_exchange_id)
def log_order_book_update_failed_post_sync(exchange_id, order_book_updates): msg = "Update after syncing FAILED = Order book update is FAILED! for {exch_name} Update itself: {upd}".format( exch_name=get_exchange_name_by_id(exchange_id), upd=order_book_updates) log_to_file(msg, SOCKET_ERRORS_LOG_FILE_NAME) print_to_console(msg, LOG_ALL_ERRORS)
def log_failed_to_retrieve_order_book(cfg): msg = "CAN'T retrieve order book for {nn} or {nnn}".format( nn=get_exchange_name_by_id(cfg.sell_exchange_id), nnn=get_exchange_name_by_id(cfg.buy_exchange_id)) print_to_console(msg, LOG_ALL_ERRORS) log_to_file(msg, cfg.log_file_name)