def perform_trade(self, sell_amt, sell_coin, buy_coin): """Performs the trade from 'sell_coin' to 'buy_coin'. Returns a 'Trade' object """ # Find the trading fee for the given pairget_trade_fee( fee_amt_perc, fee_coin, fee_literal = self.get_trade_fee( sell_amt, sell_coin, buy_coin) if fee_amt_perc is None: self.logger.warning("perform_trade: No trade fee for '{}[{}/{}]'." "Skipping trade calculation.".format( self.exchange.id, sell_coin.id, buy_coin.id)) return None # Perform trade: buy_amt = fx_exchange(sell_coin.id, buy_coin.id, sell_amt * (1 - fee_amt_perc / 100), self.logger) if buy_amt is None: self.logger.warning("perform_trade [2]: Trade could not be " "performed '{}[{}/{}]'. " "Skipping trade calculation.".format( self.exchange.id, sell_coin.id, buy_coin.id)) return None elif buy_amt <= 0: self.logger.warning( "perform_trade [3]: Trade performed with negative outcome '{}[{} {} --> {} {}]'. " "Skipping trade calculation.".format(self.exchange.id, sell_amt, sell_coin.id, buy_amt, buy_coin.id)) return None # Fees calculated by default in 'sell_coin' fee_amt = fee_amt_perc / 100 * sell_amt # If 'FeeCoin' has a value, calculate fees in 'FeeCoin' if fee_coin and fee_coin is not '-': fee_amt = fx_exchange(sell_coin.id, fee_coin, fee_amt, self.logger) fee_coin = get_coin(fee_coin) else: fee_coin = sell_coin # Return calculated trade self.logger.debug("perform_trade [3]: Trade for '{}[{}/{}]:" " Sell={} {} / Buy={} {} / Fee={} {}'" "".format(self.exchange.id, sell_coin.id, buy_coin.id, sell_amt, sell_coin.id, buy_amt, buy_coin.id, fee_amt, fee_coin.id)) return Trade(sell_amt, sell_coin, buy_amt, buy_coin, fee_amt, fee_coin, fee_literal)
def calc_transfer_fees(self, fee_lst, coin, to_curr=False): """ Returns a string that contains the sum of the fees provided in 'fee_lst' (withdrawal & deposit fees). If 'to_curr' is equeal to 'True', the function converts the sum to the calculation currency. """ fee_sum = sum(filter(None, fee_lst)) if fee_sum is not None: if to_curr: fee_sum = fx_exchange(coin, self.currency, fee_sum, self.logger) return num_2_str(fee_sum, self.currency) else: return "{} {}".format(fee_sum, coin) else: return ""
def exch_results(url_orig_coin=None, url_dest_coin=None): """There are two ways of landing in this page: - Search form was filled: performs search and returns results - Direct external link (no form was filled!): in this case, a page with no results is shown, and then from the page a proper calculation is triggered. It is done like this to let the user load the page as soon as possible. """ session_id = request.cookies.get('session') if not session_id: new_session_id = token_hex(8) sorted_paths = [] input_form = SearchForm() # Choose currency: 1) Form 2) Cookie 3) Default currency = request.cookies.get('calc_currency') if input_form.currency.data != 'Empty': curr = input_form.currency.data elif currency: curr = currency input_form.currency.data = curr else: curr = Params.DEFAULT_CURRENCY input_form.currency.data = curr auto_search = False feedback_form = FeedbackForm() exchanges = get_exchanges(['Exchange']) user_exchanges = [exch.id for exch in exchanges] path_results = None open_fbck_modal = False # Get Meta tags (in case form was not filled) title = get_meta_tags('Exchanges|Results', 'Title', [url_orig_coin.upper(), url_dest_coin.upper()]) description = get_meta_tags('Exchanges|Results', 'Description', [url_orig_coin.upper(), url_dest_coin.upper()]) # 1) ACTIONS IF *SEARCH* FORM WAS FILLED if input_form.search_submit.data: if input_form.validate(): curr = input_form.currency.data orig_loc = get_exch_by_name(input_form.orig_loc.data) orig_coin = get_coin_by_longname(input_form.orig_coin.data) # Save 'orig_amt' as Float or integer depending on value num = float(input_form.orig_amt.data) if num % 1 == 0: num = int(num) orig_amt = num dest_loc = get_exch_by_name(input_form.dest_loc.data) dest_coin = get_coin_by_longname(input_form.dest_coin.data) user_exchanges = input_form.exchanges.data # Get Meta tags (again, if form was filled) title = get_meta_tags('Exchanges|Results', 'Title', [orig_coin.symbol, dest_coin.symbol]) description = get_meta_tags('Exchanges|Results', 'Description', [orig_coin.long_name, dest_coin.long_name]) # If user selected all Exchanges or none of them, don't filter if len(user_exchanges) == len(exchanges): user_exchanges = [] fee_settings = {"CEP": input_form.cep_promos.data, "Default": input_form.default_fee.data, "Binance": input_form.binance_fee.data} # start_time = datetime.datetime.now() try: paths = calc_paths(orig_loc, orig_coin, orig_amt, dest_loc, dest_coin, curr, fee_settings, logger) path_results = len(paths) # Catch generic exception just in case anything went wront in logic except Exception as e: db.session.rollback() error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) paths = [] path_results = -1 # If no results were found, send worning email if path_results == 0: args_dic = {"orig_amt": orig_amt, "orig_coin": orig_coin.id, "orig_loc": orig_loc.id, "dest_coin": dest_coin.id, "dest_loc": dest_loc.id, "currency": curr} warning_notifier("Search with no results", args_dic, mail, logger) # Register query # finish_time = datetime.datetime.now() # results = len(paths) # exchs = "" # for exch in user_exchanges: # exchs += exch + '|' # exchs = exchs[:-1] # try: # query = QueryRegister(session_id=session_id, # orig_amt=orig_amt, # orig_coin=orig_coin.id, # orig_loc=orig_loc.id, # dest_coin=dest_coin.id, # dest_loc=dest_loc.id, # currency=curr, # connection_type=connection_type, # exchanges=exchs, # results=results, # start_time=start_time, # finish_time=finish_time) # db.session.add(query) # db.session.commit() # except Exception as e: # db.session.rollback() # error_notifier(type(e).__name__, # traceback.format_exc(), # mail, # logger) # Select all Exchanges if no partial selection was made if not user_exchanges: user_exchanges = [exch.id for exch in exchanges] # Return capped list of results sorted_paths = sorted(paths, key=lambda x: x.total_fees) sorted_paths = sorted_paths[0:Params.MAX_PATHS] # 2) ACTIONS IF *NO* FORM WAS FILLED (DIRECT LINK!) else: orig_coin = get_coin(url_orig_coin.upper()) dest_coin = get_coin(url_dest_coin.upper()) if orig_coin: input_form.orig_coin.data = orig_coin.long_name amt = fx_exchange("USD", orig_coin.id, 3000, logger) input_form.orig_amt.data = str(math.ceil(amt)) + " " if dest_coin: input_form.dest_coin.data = dest_coin.long_name auto_search = True resp = make_response(render_template('exch_results.html', form=input_form, curr=curr, exchanges=exchanges, user_exchanges=user_exchanges, paths=sorted_paths, path_results=path_results, auto_search=auto_search, feedback_form=feedback_form, title=title, description=description, open_feedback_modal=open_fbck_modal, url_orig_coin=url_orig_coin, url_dest_coin=url_dest_coin)) # Store session ID & Currency in cookie if there are not already stored if not session_id: resp.set_cookie('session', new_session_id) if currency != curr: resp.set_cookie('calc_currency', curr) return resp
def exch_results(url_orig_coin=None, url_dest_coin=None): """There are two ways of landing in this page: - Search form was filled: performs search and returns results - Direct external link (no form was filled!): in this case, a page with no results is shown, and then from the page a proper calculation is triggered. It is done like this to let the user load the page as soon as possible. """ try: session_id = request.cookies.get('session') if not session_id: new_session_id = token_hex(8) sorted_paths = [] input_form = SearchForm() # Choose currency: 1) Form 2) Cookie 3) Default currency = request.cookies.get('calc_currency') if input_form.currency.data != 'Empty': curr = input_form.currency.data elif currency: curr = currency input_form.currency.data = curr else: curr = Params.DEFAULT_CURRENCY input_form.currency.data = curr curr = get_coin(curr) if not curr: curr = get_coin('usd-us-dollars') auto_search = False feedback_form = FeedbackForm() exchanges = get_exchanges(['Exchange'], status='Active') user_exchanges = [exch.id for exch in exchanges] path_results = None amt_warning = None # Get Meta tags (in case form was not filled) title = get_meta_tags('Exchanges|Results', 'Title', [url_orig_coin.upper(), url_dest_coin.upper()]) description = get_meta_tags('Exchanges|Results', 'Description', [url_orig_coin.upper(), url_dest_coin.upper()]) # 1) ACTIONS IF *SEARCH* FORM WAS FILLED if input_form.search_submit.data: if input_form.validate(): curr = input_form.currency.data curr = get_coin(curr) orig_loc = get_exch_by_name(input_form.orig_loc.data) orig_coin = get_coin_by_longname(input_form.orig_coin.data) # Save 'orig_amt' as Float or integer depending on value num = float(input_form.orig_amt.data) if num % 1 == 0: num = int(num) orig_amt = num dest_loc = get_exch_by_name(input_form.dest_loc.data) dest_coin = get_coin_by_longname(input_form.dest_coin.data) user_exchanges = input_form.exchanges.data # Get Meta tags (again, if form was filled) title = get_meta_tags('Exchanges|Results', 'Title', [orig_coin.symbol, dest_coin.symbol]) description = get_meta_tags('Exchanges|Results', 'Description', [orig_coin.long_name, dest_coin.long_name]) # If user selected all Exchanges or none of them, don't filter if len(user_exchanges) == len(exchanges): user_exchanges = [] fee_settings = {"CEP": input_form.cep_promos.data, "Default": input_form.default_fee.data, "Binance": input_form.binance_fee.data} # start_time = datetime.datetime.now() try: paths = calc_paths(orig_loc, orig_coin, orig_amt, dest_loc, dest_coin, curr, fee_settings, logger) path_results = len(paths) # Catch generic exception if anything went wrong in logic except Exception as e: db.session.rollback() error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) paths = [] path_results = -1 # If no results were found, check "orign_amt" to try again if path_results == 0: amount_usd = fx_exchange(orig_coin.id, 'usd-us-dollars', orig_amt, logger) if amount_usd and amount_usd < Params.RECALC_AMOUNT: orig_amt = round_number(orig_amt * Params.RECALC_AMOUNT / amount_usd) orig_amt = round_big_number(orig_amt) input_form.orig_amt.data = orig_amt try: paths = calc_paths(orig_loc, orig_coin, orig_amt, dest_loc, dest_coin, curr, fee_settings, logger) path_results = len(paths) amt_warning = True # Catch generic exception if anything went wrong except Exception as e: db.session.rollback() error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) paths = [] path_results = -1 # If no results were found, send worning email if path_results == 0: args_dic = {"orig_amt": orig_amt, "orig_coin": orig_coin.id, "orig_loc": orig_loc.id, "dest_coin": dest_coin.id, "dest_loc": dest_loc.id, "currency": curr.id} warning_notifier("Search with no results", args_dic, mail, logger) # Select all Exchanges if no partial selection was made if not user_exchanges: user_exchanges = [exch.id for exch in exchanges] # Return capped list of results sorted_paths = sorted(paths, key=lambda x: x.total_fees) sorted_paths = sorted_paths[0:Params.MAX_PATHS] # 2) ACTIONS IF *NO* FORM WAS FILLED (DIRECT LINK!) else: orig_coin = get_coin_by_symbol(url_orig_coin.upper()) dest_coin = get_coin_by_symbol(url_dest_coin.upper()) # If 'orig_coin' or 'dest_coin' not found, try in mappings table if not orig_coin: new_symbol = get_mapping('Coin', 'symbol', url_orig_coin.upper()) if new_symbol: orig_coin = get_coin_by_symbol(new_symbol) if not dest_coin: new_symbol = get_mapping('Coin', 'symbol', url_dest_coin.upper()) if new_symbol: dest_coin = get_coin_by_symbol(new_symbol) # Procced with function if orig_coin: input_form.orig_coin.data = orig_coin.long_name amt = fx_exchange('usd-us-dollars', orig_coin.id, 3000, logger) if amt: amt = round_big_number(amt) input_form.orig_amt.data = str(math.ceil(amt)) + " " if dest_coin: input_form.dest_coin.data = dest_coin.long_name auto_search = True # Actions if Feedback Form was filled if feedback_form.feedback_submit.data: if feedback_form.validate(): manage_feedback_form(feedback_form, request.path) # Catch generic exception just in case anything went wront in logic except Exception as e: db.session.rollback() logger.error("Routes: Non-handled exception at '{}'" .format(request.url)) error_notifier(type(e).__name__, traceback.format_exc(), mail, logger) return redirect(url_for('exchanges')) resp = make_response(render_template('exch_results.html', form=input_form, curr=curr, exchanges=exchanges, user_exchanges=user_exchanges, paths=sorted_paths, amt_warning=amt_warning, path_results=path_results, auto_search=auto_search, feedback_form=feedback_form, title=title, description=description, url_orig_coin=url_orig_coin, url_dest_coin=url_dest_coin)) # Store session ID & Currency in cookie if there are not already stored if not session_id: resp.set_cookie('session', new_session_id) if currency != curr.id: resp.set_cookie('calc_currency', curr.id) return resp
def calc_fees(self, currency, logger): """Calculates the overall path fees in the given 'currency'. Adds up: - Origin Withdraw fees (origin.withdraw_fee) - Hop 1 Deposit fees (hop_1.deposit_fee) - Hop 1 Trade fees (hop_1.trade.fee_amt) - Hop 1 Withdraw fees (hop_1.withdraw_fee) - Hop 2 Deposit fees (hop_2.deposit_fee) - Hop 2 Trade fees (hop_2.trade.fee_amt) - Hop 2 Withdraw fees (hop_2.withdraw_fee) - Destination Deposit fees (destination.deposit_fee) """ total_fees = 0 # Add Origin Withdraw fees (origin.withdraw_fee) if self.origin.withdraw_fee: fee = fx_exchange(self.origin.coin.id, currency, self.origin.withdraw_fee, logger) if fee: total_fees += fee msg = "calc_fees: {} = {} {} ({} {})"\ .format("origin.withdraw_fee", self.origin.withdraw_fee, self.origin.coin.id, fee, currency) logger.debug(msg) # Hop 1 Deposit fees (hop_1.deposit_fee) if self.hop_1.deposit_fee: fee = fx_exchange(self.hop_1.trade.sell_coin.id, currency, self.hop_1.deposit_fee, logger) if fee: total_fees += fee msg = "calc_fees: {} = {} {} ({} {})"\ .format("hop_1.deposit_fee", self.hop_1.deposit_fee, self.hop_1.trade.sell_coin.id, fee, currency) logger.debug(msg) # Hop 1 Trade fees (hop_1.trade.fee_amt) if self.hop_1.trade.fee_amt: fee = fx_exchange(self.hop_1.trade.fee_coin.id, currency, self.hop_1.trade.fee_amt, logger) if fee: total_fees += fee msg = "calc_fees: {} = {} {} ({} {})"\ .format("hop_1.trade.fee_amt", self.hop_1.trade.fee_amt, self.hop_1.trade.fee_coin.id, fee, currency) logger.debug(msg) # Add Hop 1 Withdraw fees (hop_1.withdraw_fee) if self.hop_1.withdraw_fee: fee = fx_exchange(self.hop_1.trade.buy_coin.id, currency, self.hop_1.withdraw_fee, logger) if fee: total_fees += fee msg = "calc_fees: {} = {} {} ({} {})"\ .format("hop_1.withdraw_fee", self.hop_1.withdraw_fee, self.hop_1.trade.buy_coin.id, fee, currency) logger.debug(msg) # Add Hop 2 Deposit fees (hop_2.deposit_fee) if self.hop_2 and self.hop_2.deposit_fee: fee = fx_exchange(self.hop_2.trade.sell_coin.id, currency, self.hop_2.deposit_fee, logger) if fee: total_fees += fee msg = "calc_fees: {} = {} {} ({} {})"\ .format("hop_2.deposit_fee", self.hop_2.deposit_fee, self.hop_2.trade.sell_coin.id, fee, currency) logger.debug(msg) # Add Hop 2 Trade fees (hop_2.trade.fee_amt) if self.hop_2 and self.hop_2.trade.fee_amt: fee = fx_exchange(self.hop_2.trade.fee_coin.id, currency, self.hop_2.trade.fee_amt, logger) if fee: total_fees += fee msg = "calc_fees: {} = {} {} ({} {})"\ .format("hop_2.trade.fee_amt", self.hop_2.trade.fee_amt, self.hop_2.trade.fee_coin.id, fee, currency) logger.debug(msg) # Add Hop 2 Withdraw fees (hop_2.withdraw_fee) if self.hop_2 and self.hop_2.withdraw_fee: fee = fx_exchange(self.hop_2.trade.buy_coin.id, currency, self.hop_2.withdraw_fee, logger) if fee: total_fees += fee msg = "calc_fees: {} = {} {} ({} {})"\ .format("hop_2.withdraw_fee", self.hop_2.withdraw_fee, self.hop_2.trade.buy_coin.id, fee, currency) logger.debug(msg) # Add Destination Deposit fees (destination.deposit_fee) if self.destination.deposit_fee: fee = fx_exchange(self.destination.coin.id, currency, self.destination.deposit_fee, logger) if fee: total_fees += fee msg = "calc_fees: {} = {} {} ({} {})"\ .format("destination.deposit_fee", self.destination.deposit_fee, self.destination.coin.id, fee, currency) logger.debug(msg) return total_fees
def fee_to_currency(self, amount, orig_coin): fee_curr = fx_exchange(orig_coin, self.currency, amount, self.logger) return num_2_str(fee_curr, self.currency)