Merge pull request #883 from freqtrade/fstrings-in-use

fstrings in use
This commit is contained in:
Janne Sinivirta 2018-06-09 08:53:54 +03:00 committed by GitHub
commit efd69b2cd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 86 additions and 126 deletions

View File

@ -72,7 +72,7 @@ class Arguments(object):
self.parser.add_argument( self.parser.add_argument(
'--version', '--version',
action='version', action='version',
version='%(prog)s {}'.format(__version__), version=f'%(prog)s {__version__}'
) )
self.parser.add_argument( self.parser.add_argument(
'-c', '--config', '-c', '--config',

View File

@ -236,9 +236,8 @@ class Configuration(object):
exchange = config.get('exchange', {}).get('name').lower() exchange = config.get('exchange', {}).get('name').lower()
if exchange not in ccxt.exchanges: if exchange not in ccxt.exchanges:
exception_msg = 'Exchange "{}" not supported.\n' \ exception_msg = f'Exchange "{exchange}" not supported.\n' \
'The following exchanges are supported: {}'\ f'The following exchanges are supported: {", ".join(ccxt.exchanges)}'
.format(exchange, ', '.join(ccxt.exchanges))
logger.critical(exception_msg) logger.critical(exception_msg)
raise OperationalException( raise OperationalException(

View File

@ -59,7 +59,7 @@ def init_ccxt(exchange_config: dict) -> ccxt.Exchange:
name = exchange_config['name'] name = exchange_config['name']
if name not in ccxt.exchanges: if name not in ccxt.exchanges:
raise OperationalException('Exchange {} is not supported'.format(name)) raise OperationalException(f'Exchange {name} is not supported')
try: try:
api = getattr(ccxt, name.lower())({ api = getattr(ccxt, name.lower())({
'apiKey': exchange_config.get('key'), 'apiKey': exchange_config.get('key'),
@ -69,7 +69,7 @@ def init_ccxt(exchange_config: dict) -> ccxt.Exchange:
'enableRateLimit': True, 'enableRateLimit': True,
}) })
except (KeyError, AttributeError): except (KeyError, AttributeError):
raise OperationalException('Exchange {} is not supported'.format(name)) raise OperationalException(f'Exchange {name} is not supported')
return api return api
@ -118,11 +118,10 @@ def validate_pairs(pairs: List[str]) -> None:
# TODO: add a support for having coins in BTC/USDT format # TODO: add a support for having coins in BTC/USDT format
if not pair.endswith(stake_cur): if not pair.endswith(stake_cur):
raise OperationalException( raise OperationalException(
'Pair {} not compatible with stake_currency: {}'.format(pair, stake_cur) f'Pair {pair} not compatible with stake_currency: {stake_cur}')
)
if pair not in markets: if pair not in markets:
raise OperationalException( raise OperationalException(
'Pair {} is not available at {}'.format(pair, get_name())) f'Pair {pair} is not available at {get_name()}')
def exchange_has(endpoint: str) -> bool: def exchange_has(endpoint: str) -> bool:
@ -138,7 +137,7 @@ def exchange_has(endpoint: str) -> bool:
def buy(pair: str, rate: float, amount: float) -> Dict: def buy(pair: str, rate: float, amount: float) -> Dict:
if _CONF['dry_run']: if _CONF['dry_run']:
global _DRY_RUN_OPEN_ORDERS global _DRY_RUN_OPEN_ORDERS
order_id = 'dry_run_buy_{}'.format(randint(0, 10**6)) order_id = f'dry_run_buy_{randint(0, 10**6)}'
_DRY_RUN_OPEN_ORDERS[order_id] = { _DRY_RUN_OPEN_ORDERS[order_id] = {
'pair': pair, 'pair': pair,
'price': rate, 'price': rate,
@ -156,20 +155,17 @@ def buy(pair: str, rate: float, amount: float) -> Dict:
return _API.create_limit_buy_order(pair, amount, rate) return _API.create_limit_buy_order(pair, amount, rate)
except ccxt.InsufficientFunds as e: except ccxt.InsufficientFunds as e:
raise DependencyException( raise DependencyException(
'Insufficient funds to create limit buy order on market {}.' f'Insufficient funds to create limit buy order on market {pair}.'
'Tried to buy amount {} at rate {} (total {}).' f'Tried to buy amount {amount} at rate {rate} (total {rate*amount}).'
'Message: {}'.format(pair, amount, rate, rate*amount, e) f'Message: {e}')
)
except ccxt.InvalidOrder as e: except ccxt.InvalidOrder as e:
raise DependencyException( raise DependencyException(
'Could not create limit buy order on market {}.' f'Could not create limit buy order on market {pair}.'
'Tried to buy amount {} at rate {} (total {}).' f'Tried to buy amount {amount} at rate {rate} (total {rate*amount}).'
'Message: {}'.format(pair, amount, rate, rate*amount, e) f'Message: {e}')
)
except (ccxt.NetworkError, ccxt.ExchangeError) as e: except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError( raise TemporaryError(
'Could not place buy order due to {}. Message: {}'.format( f'Could not place buy order due to {e.__class__.__name__}. Message: {e}')
e.__class__.__name__, e))
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) raise OperationalException(e)
@ -177,7 +173,7 @@ def buy(pair: str, rate: float, amount: float) -> Dict:
def sell(pair: str, rate: float, amount: float) -> Dict: def sell(pair: str, rate: float, amount: float) -> Dict:
if _CONF['dry_run']: if _CONF['dry_run']:
global _DRY_RUN_OPEN_ORDERS global _DRY_RUN_OPEN_ORDERS
order_id = 'dry_run_sell_{}'.format(randint(0, 10**6)) order_id = f'dry_run_sell_{randint(0, 10**6)}'
_DRY_RUN_OPEN_ORDERS[order_id] = { _DRY_RUN_OPEN_ORDERS[order_id] = {
'pair': pair, 'pair': pair,
'price': rate, 'price': rate,
@ -194,20 +190,17 @@ def sell(pair: str, rate: float, amount: float) -> Dict:
return _API.create_limit_sell_order(pair, amount, rate) return _API.create_limit_sell_order(pair, amount, rate)
except ccxt.InsufficientFunds as e: except ccxt.InsufficientFunds as e:
raise DependencyException( raise DependencyException(
'Insufficient funds to create limit sell order on market {}.' f'Insufficient funds to create limit sell order on market {pair}.'
'Tried to sell amount {} at rate {} (total {}).' f'Tried to sell amount {amount} at rate {rate} (total {rate*amount}).'
'Message: {}'.format(pair, amount, rate, rate*amount, e) f'Message: {e}')
)
except ccxt.InvalidOrder as e: except ccxt.InvalidOrder as e:
raise DependencyException( raise DependencyException(
'Could not create limit sell order on market {}.' f'Could not create limit sell order on market {pair}.'
'Tried to sell amount {} at rate {} (total {}).' f'Tried to sell amount {amount} at rate {rate} (total {rate*amount}).'
'Message: {}'.format(pair, amount, rate, rate*amount, e) f'Message: {e}')
)
except (ccxt.NetworkError, ccxt.ExchangeError) as e: except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError( raise TemporaryError(
'Could not place sell order due to {}. Message: {}'.format( f'Could not place sell order due to {e.__class__.__name__}. Message: {e}')
e.__class__.__name__, e))
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) raise OperationalException(e)
@ -222,8 +215,7 @@ def get_balance(currency: str) -> float:
balance = balances.get(currency) balance = balances.get(currency)
if balance is None: if balance is None:
raise TemporaryError( raise TemporaryError(
'Could not get {} balance due to malformed exchange response: {}'.format( f'Could not get {currency} balance due to malformed exchange response: {balances}')
currency, balances))
return balance['free'] return balance['free']
@ -243,8 +235,7 @@ def get_balances() -> dict:
return balances return balances
except (ccxt.NetworkError, ccxt.ExchangeError) as e: except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError( raise TemporaryError(
'Could not get balance due to {}. Message: {}'.format( f'Could not get balance due to {e.__class__.__name__}. Message: {e}')
e.__class__.__name__, e))
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) raise OperationalException(e)
@ -255,13 +246,11 @@ def get_tickers() -> Dict:
return _API.fetch_tickers() return _API.fetch_tickers()
except ccxt.NotSupported as e: except ccxt.NotSupported as e:
raise OperationalException( raise OperationalException(
'Exchange {} does not support fetching tickers in batch.' f'Exchange {_API.name} does not support fetching tickers in batch.'
'Message: {}'.format(_API.name, e) f'Message: {e}')
)
except (ccxt.NetworkError, ccxt.ExchangeError) as e: except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError( raise TemporaryError(
'Could not load tickers due to {}. Message: {}'.format( f'Could not load tickers due to {e.__class__.__name__}. Message: {e}')
e.__class__.__name__, e))
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) raise OperationalException(e)
@ -282,8 +271,7 @@ def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict:
return data return data
except (ccxt.NetworkError, ccxt.ExchangeError) as e: except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError( raise TemporaryError(
'Could not load ticker history due to {}. Message: {}'.format( f'Could not load ticker history due to {e.__class__.__name__}. Message: {e}')
e.__class__.__name__, e))
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) raise OperationalException(e)
else: else:
@ -327,15 +315,13 @@ def get_ticker_history(pair: str, tick_interval: str, since_ms: Optional[int] =
return data return data
except ccxt.NotSupported as e: except ccxt.NotSupported as e:
raise OperationalException( raise OperationalException(
'Exchange {} does not support fetching historical candlestick data.' f'Exchange {_API.name} does not support fetching historical candlestick data.'
'Message: {}'.format(_API.name, e) f'Message: {e}')
)
except (ccxt.NetworkError, ccxt.ExchangeError) as e: except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError( raise TemporaryError(
'Could not load ticker history due to {}. Message: {}'.format( f'Could not load ticker history due to {e.__class__.__name__}. Message: {e}')
e.__class__.__name__, e))
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException('Could not fetch ticker data. Msg: {}'.format(e)) raise OperationalException(f'Could not fetch ticker data. Msg: {e}')
@retrier @retrier
@ -347,12 +333,10 @@ def cancel_order(order_id: str, pair: str) -> None:
return _API.cancel_order(order_id, pair) return _API.cancel_order(order_id, pair)
except ccxt.InvalidOrder as e: except ccxt.InvalidOrder as e:
raise DependencyException( raise DependencyException(
'Could not cancel order. Message: {}'.format(e) f'Could not cancel order. Message: {e}')
)
except (ccxt.NetworkError, ccxt.ExchangeError) as e: except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError( raise TemporaryError(
'Could not cancel order due to {}. Message: {}'.format( f'Could not cancel order due to {e.__class__.__name__}. Message: {e}')
e.__class__.__name__, e))
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) raise OperationalException(e)
@ -369,12 +353,10 @@ def get_order(order_id: str, pair: str) -> Dict:
return _API.fetch_order(order_id, pair) return _API.fetch_order(order_id, pair)
except ccxt.InvalidOrder as e: except ccxt.InvalidOrder as e:
raise DependencyException( raise DependencyException(
'Could not get order. Message: {}'.format(e) f'Could not get order. Message: {e}')
)
except (ccxt.NetworkError, ccxt.ExchangeError) as e: except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError( raise TemporaryError(
'Could not get order due to {}. Message: {}'.format( f'Could not get order due to {e.__class__.__name__}. Message: {e}')
e.__class__.__name__, e))
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) raise OperationalException(e)
@ -393,8 +375,7 @@ def get_trades_for_order(order_id: str, pair: str, since: datetime) -> List:
except ccxt.NetworkError as e: except ccxt.NetworkError as e:
raise TemporaryError( raise TemporaryError(
'Could not get trades due to networking error. Message: {}'.format(e) f'Could not get trades due to networking error. Message: {e}')
)
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) raise OperationalException(e)
@ -416,8 +397,7 @@ def get_markets() -> List[dict]:
return _API.fetch_markets() return _API.fetch_markets()
except (ccxt.NetworkError, ccxt.ExchangeError) as e: except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError( raise TemporaryError(
'Could not load markets due to {}. Message: {}'.format( f'Could not load markets due to {e.__class__.__name__}. Message: {e}')
e.__class__.__name__, e))
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) raise OperationalException(e)
@ -442,8 +422,7 @@ def get_fee(symbol='ETH/BTC', type='', side='', amount=1,
price=price, takerOrMaker=taker_or_maker)['rate'] price=price, takerOrMaker=taker_or_maker)['rate']
except (ccxt.NetworkError, ccxt.ExchangeError) as e: except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError( raise TemporaryError(
'Could not get fee info due to {}. Message: {}'.format( f'Could not get fee info due to {e.__class__.__name__}. Message: {e}')
e.__class__.__name__, e))
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) raise OperationalException(e)

View File

@ -119,7 +119,7 @@ class CryptoToFiatConverter(object):
# Check if the fiat convertion you want is supported # Check if the fiat convertion you want is supported
if not self._is_supported_fiat(fiat=fiat_symbol): if not self._is_supported_fiat(fiat=fiat_symbol):
raise ValueError('The fiat {} is not supported.'.format(fiat_symbol)) raise ValueError(f'The fiat {fiat_symbol} is not supported.')
# Get the pair that interest us and return the price in fiat # Get the pair that interest us and return the price in fiat
for pair in self._pairs: for pair in self._pairs:
@ -182,7 +182,7 @@ class CryptoToFiatConverter(object):
""" """
# Check if the fiat convertion you want is supported # Check if the fiat convertion you want is supported
if not self._is_supported_fiat(fiat=fiat_symbol): if not self._is_supported_fiat(fiat=fiat_symbol):
raise ValueError('The fiat {} is not supported.'.format(fiat_symbol)) raise ValueError(f'The fiat {fiat_symbol} is not supported.')
# No need to convert if both crypto and fiat are the same # No need to convert if both crypto and fiat are the same
if crypto_symbol == fiat_symbol: if crypto_symbol == fiat_symbol:

View File

@ -97,7 +97,7 @@ class FreqtradeBot(object):
# Log state transition # Log state transition
state = self.state state = self.state
if state != old_state: if state != old_state:
self.rpc.send_msg('*Status:* `{}`'.format(state.name.lower())) self.rpc.send_msg(f'*Status:* `{state.name.lower()}`')
logger.info('Changing state to: %s', state.name) logger.info('Changing state to: %s', state.name)
if state == State.STOPPED: if state == State.STOPPED:
@ -171,12 +171,10 @@ class FreqtradeBot(object):
logger.warning('%s, retrying in 30 seconds...', error) logger.warning('%s, retrying in 30 seconds...', error)
time.sleep(constants.RETRY_TIMEOUT) time.sleep(constants.RETRY_TIMEOUT)
except OperationalException: except OperationalException:
tb = traceback.format_exc()
hint = 'Issue `/start` if you think it is safe to restart.'
self.rpc.send_msg( self.rpc.send_msg(
'*Status:* OperationalException:\n```\n{traceback}```{hint}' f'*Status:* OperationalException:\n```\n{tb}```{hint}'
.format(
traceback=traceback.format_exc(),
hint='Issue `/start` if you think it is safe to restart.'
)
) )
logger.exception('OperationalException. Stopping trader ...') logger.exception('OperationalException. Stopping trader ...')
self.state = State.STOPPED self.state = State.STOPPED
@ -260,6 +258,9 @@ class FreqtradeBot(object):
""" """
stake_amount = self.config['stake_amount'] stake_amount = self.config['stake_amount']
interval = self.analyze.get_ticker_interval() interval = self.analyze.get_ticker_interval()
stake_currency = self.config['stake_currency']
fiat_currency = self.config['fiat_display_currency']
exc_name = exchange.get_name()
logger.info( logger.info(
'Checking buy signals to create a new trade with stake_amount: %f ...', 'Checking buy signals to create a new trade with stake_amount: %f ...',
@ -267,10 +268,9 @@ class FreqtradeBot(object):
) )
whitelist = copy.deepcopy(self.config['exchange']['pair_whitelist']) whitelist = copy.deepcopy(self.config['exchange']['pair_whitelist'])
# Check if stake_amount is fulfilled # Check if stake_amount is fulfilled
if exchange.get_balance(self.config['stake_currency']) < stake_amount: if exchange.get_balance(stake_currency) < stake_amount:
raise DependencyException( raise DependencyException(
'stake amount is not fulfilled (currency={})'.format(self.config['stake_currency']) f'stake amount is not fulfilled (currency={stake_currency})')
)
# Remove currently opened and latest pairs from whitelist # Remove currently opened and latest pairs from whitelist
for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
@ -289,7 +289,8 @@ class FreqtradeBot(object):
break break
else: else:
return False return False
pair_s = pair.replace('_', '/')
pair_url = exchange.get_pair_detail_url(pair)
# Calculate amount # Calculate amount
buy_limit = self.get_target_bid(exchange.get_ticker(pair)) buy_limit = self.get_target_bid(exchange.get_ticker(pair))
amount = stake_amount / buy_limit amount = stake_amount / buy_limit
@ -298,23 +299,15 @@ class FreqtradeBot(object):
stake_amount_fiat = self.fiat_converter.convert_amount( stake_amount_fiat = self.fiat_converter.convert_amount(
stake_amount, stake_amount,
self.config['stake_currency'], stake_currency,
self.config['fiat_display_currency'] fiat_currency
) )
# Create trade entity and return # Create trade entity and return
self.rpc.send_msg( self.rpc.send_msg(
'*{}:* Buying [{}]({}) with limit `{:.8f} ({:.6f} {}, {:.3f} {})` ' f"""*{exc_name}:* Buying [{pair_s}]({pair_url}) \
.format( with limit `{buy_limit:.8f} ({stake_amount:.6f} \
exchange.get_name(), {stake_currency}, {stake_amount_fiat:.3f} {fiat_currency})`"""
pair.replace('_', '/'),
exchange.get_pair_detail_url(pair),
buy_limit,
stake_amount,
self.config['stake_currency'],
stake_amount_fiat,
self.config['fiat_display_currency']
)
) )
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
fee = exchange.get_fee(symbol=pair, taker_or_maker='maker') fee = exchange.get_fee(symbol=pair, taker_or_maker='maker')
@ -415,12 +408,12 @@ class FreqtradeBot(object):
fee_abs += exectrade['fee']['cost'] fee_abs += exectrade['fee']['cost']
if amount != order_amount: if amount != order_amount:
logger.warning("amount {} does not match amount {}".format(amount, trade.amount)) logger.warning(f"amount {amount} does not match amount {trade.amount}")
raise OperationalException("Half bought? Amounts don't match") raise OperationalException("Half bought? Amounts don't match")
real_amount = amount - fee_abs real_amount = amount - fee_abs
if fee_abs != 0: if fee_abs != 0:
logger.info("Applying fee on amount for {} (from {} to {}) from Trades".format( logger.info(f"""Applying fee on amount for {trade} \
trade, order['amount'], real_amount)) (from {order_amount} to {real_amount}) from Trades""")
return real_amount return real_amount
def handle_trade(self, trade: Trade) -> bool: def handle_trade(self, trade: Trade) -> bool:
@ -429,7 +422,7 @@ class FreqtradeBot(object):
:return: True if trade has been sold, False otherwise :return: True if trade has been sold, False otherwise
""" """
if not trade.is_open: if not trade.is_open:
raise ValueError('attempt to handle closed trade: {}'.format(trade)) raise ValueError(f'attempt to handle closed trade: {trade}')
logger.debug('Handling %s ...', trade) logger.debug('Handling %s ...', trade)
current_rate = exchange.get_ticker(trade.pair)['bid'] current_rate = exchange.get_ticker(trade.pair)['bid']
@ -480,6 +473,7 @@ class FreqtradeBot(object):
"""Buy timeout - cancel order """Buy timeout - cancel order
:return: True if order was fully cancelled :return: True if order was fully cancelled
""" """
pair_s = trade.pair.replace('_', '/')
exchange.cancel_order(trade.open_order_id, trade.pair) exchange.cancel_order(trade.open_order_id, trade.pair)
if order['remaining'] == order['amount']: if order['remaining'] == order['amount']:
# if trade is not partially completed, just delete the trade # if trade is not partially completed, just delete the trade
@ -488,8 +482,7 @@ class FreqtradeBot(object):
# check_handle_timedout will flush afterwards # check_handle_timedout will flush afterwards
Trade.session.flush() Trade.session.flush()
logger.info('Buy order timeout for %s.', trade) logger.info('Buy order timeout for %s.', trade)
self.rpc.send_msg('*Timeout:* Unfilled buy order for {} cancelled'.format( self.rpc.send_msg(f'*Timeout:* Unfilled buy order for {pair_s} cancelled')
trade.pair.replace('_', '/')))
return True return True
# if trade is partially complete, edit the stake details for the trade # if trade is partially complete, edit the stake details for the trade
@ -498,8 +491,7 @@ class FreqtradeBot(object):
trade.stake_amount = trade.amount * trade.open_rate trade.stake_amount = trade.amount * trade.open_rate
trade.open_order_id = None trade.open_order_id = None
logger.info('Partial buy order timeout for %s.', trade) logger.info('Partial buy order timeout for %s.', trade)
self.rpc.send_msg('*Timeout:* Remaining buy order for {} cancelled'.format( self.rpc.send_msg(f'*Timeout:* Remaining buy order for {pair_s} cancelled')
trade.pair.replace('_', '/')))
return False return False
# FIX: 20180110, should cancel_order() be cond. or unconditionally called? # FIX: 20180110, should cancel_order() be cond. or unconditionally called?
@ -508,6 +500,7 @@ class FreqtradeBot(object):
Sell timeout - cancel order and update trade Sell timeout - cancel order and update trade
:return: True if order was fully cancelled :return: True if order was fully cancelled
""" """
pair_s = trade.pair.replace('_', '/')
if order['remaining'] == order['amount']: if order['remaining'] == order['amount']:
# if trade is not partially completed, just cancel the trade # if trade is not partially completed, just cancel the trade
exchange.cancel_order(trade.open_order_id, trade.pair) exchange.cancel_order(trade.open_order_id, trade.pair)
@ -516,8 +509,7 @@ class FreqtradeBot(object):
trade.close_date = None trade.close_date = None
trade.is_open = True trade.is_open = True
trade.open_order_id = None trade.open_order_id = None
self.rpc.send_msg('*Timeout:* Unfilled sell order for {} cancelled'.format( self.rpc.send_msg(f'*Timeout:* Unfilled sell order for {pair_s} cancelled')
trade.pair.replace('_', '/')))
logger.info('Sell order timeout for %s.', trade) logger.info('Sell order timeout for %s.', trade)
return True return True
@ -531,6 +523,8 @@ class FreqtradeBot(object):
:param limit: limit rate for the sell order :param limit: limit rate for the sell order
:return: None :return: None
""" """
exc = trade.exchange
pair = trade.pair
# Execute sell and update trade record # Execute sell and update trade record
order_id = exchange.sell(str(trade.pair), limit, trade.amount)['id'] order_id = exchange.sell(str(trade.pair), limit, trade.amount)['id']
trade.open_order_id = order_id trade.open_order_id = order_id
@ -540,43 +534,31 @@ class FreqtradeBot(object):
profit_trade = trade.calc_profit(rate=limit) profit_trade = trade.calc_profit(rate=limit)
current_rate = exchange.get_ticker(trade.pair)['bid'] current_rate = exchange.get_ticker(trade.pair)['bid']
profit = trade.calc_profit_percent(limit) profit = trade.calc_profit_percent(limit)
pair_url = exchange.get_pair_detail_url(trade.pair)
gain = "profit" if fmt_exp_profit > 0 else "loss"
message = "*{exchange}:* Selling\n" \ message = f"*{exc}:* Selling\n" \
"*Current Pair:* [{pair}]({pair_url})\n" \ f"*Current Pair:* [{pair}]({pair_url})\n" \
"*Limit:* `{limit}`\n" \ f"*Limit:* `{limit}`\n" \
"*Amount:* `{amount}`\n" \ f"*Amount:* `{round(trade.amount, 8)}`\n" \
"*Open Rate:* `{open_rate:.8f}`\n" \ f"*Open Rate:* `{trade.open_rate:.8f}`\n" \
"*Current Rate:* `{current_rate:.8f}`\n" \ f"*Current Rate:* `{current_rate:.8f}`\n" \
"*Profit:* `{profit:.2f}%`" \ f"*Profit:* `{round(profit * 100, 2):.2f}%`" \
"".format( ""
exchange=trade.exchange,
pair=trade.pair,
pair_url=exchange.get_pair_detail_url(trade.pair),
limit=limit,
open_rate=trade.open_rate,
current_rate=current_rate,
amount=round(trade.amount, 8),
profit=round(profit * 100, 2),
)
# For regular case, when the configuration exists # For regular case, when the configuration exists
if 'stake_currency' in self.config and 'fiat_display_currency' in self.config: if 'stake_currency' in self.config and 'fiat_display_currency' in self.config:
stake = self.config['stake_currency']
fiat = self.config['fiat_display_currency']
fiat_converter = CryptoToFiatConverter() fiat_converter = CryptoToFiatConverter()
profit_fiat = fiat_converter.convert_amount( profit_fiat = fiat_converter.convert_amount(
profit_trade, profit_trade,
self.config['stake_currency'], stake,
self.config['fiat_display_currency'] fiat
)
message += '` ({gain}: {profit_percent:.2f}%, {profit_coin:.8f} {coin}`' \
'` / {profit_fiat:.3f} {fiat})`' \
''.format(
gain="profit" if fmt_exp_profit > 0 else "loss",
profit_percent=fmt_exp_profit,
profit_coin=profit_trade,
coin=self.config['stake_currency'],
profit_fiat=profit_fiat,
fiat=self.config['fiat_display_currency'],
) )
message += f'` ({gain}: {fmt_exp_profit:.2f}%, {profit_trade:.8f} {stake}`' \
f'` / {profit_fiat:.3f} {fiat})`'\
''
# Because telegram._forcesell does not have the configuration # Because telegram._forcesell does not have the configuration
# Ignore the FIAT value and does not show the stake_currency as well # Ignore the FIAT value and does not show the stake_currency as well
else: else: