mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 18:23:55 +00:00
commit
4f642b769c
|
@ -62,8 +62,8 @@ class Configuration(object):
|
|||
conf = json.load(file)
|
||||
except FileNotFoundError:
|
||||
raise OperationalException(
|
||||
'Config file "{}" not found!'
|
||||
' Please create a config file or check whether it exists.'.format(path))
|
||||
f'Config file "{path}" not found!'
|
||||
' Please create a config file or check whether it exists.')
|
||||
|
||||
if 'internals' not in conf:
|
||||
conf['internals'] = {}
|
||||
|
@ -109,7 +109,7 @@ class Configuration(object):
|
|||
config['db_url'] = constants.DEFAULT_DB_PROD_URL
|
||||
logger.info('Dry run is disabled')
|
||||
|
||||
logger.info('Using DB: "{}"'.format(config['db_url']))
|
||||
logger.info(f'Using DB: "{config["db_url"]}"')
|
||||
|
||||
# Check if the exchange set by the user is supported
|
||||
self.check_exchange(config)
|
||||
|
|
|
@ -626,12 +626,8 @@ with limit `{buy_limit:.8f} ({stake_amount:.6f} \
|
|||
# Because telegram._forcesell does not have the configuration
|
||||
# Ignore the FIAT value and does not show the stake_currency as well
|
||||
else:
|
||||
message += '` ({gain}: {profit_percent:.2f}%, {profit_coin:.8f})`'.format(
|
||||
gain="profit" if fmt_exp_profit > 0 else "loss",
|
||||
profit_percent=fmt_exp_profit,
|
||||
profit_coin=profit_trade
|
||||
)
|
||||
|
||||
gain = "profit" if fmt_exp_profit > 0 else "loss"
|
||||
message += f'` ({gain}: {fmt_exp_profit:.2f}%, {profit_trade:.8f})`'
|
||||
# Send the message
|
||||
self.rpc.send_msg(message)
|
||||
Trade.session.flush()
|
||||
|
|
|
@ -74,10 +74,7 @@ def reconfigure(freqtrade: FreqtradeBot, args: Namespace) -> FreqtradeBot:
|
|||
# Create new instance
|
||||
freqtrade = FreqtradeBot(Configuration(args).get_config())
|
||||
freqtrade.rpc.send_msg(
|
||||
'*Status:* `Config reloaded ...`'.format(
|
||||
freqtrade.state.name.lower()
|
||||
)
|
||||
)
|
||||
'*Status:* `Config reloaded {freqtrade.state.name.lower()}...`')
|
||||
return freqtrade
|
||||
|
||||
|
||||
|
|
|
@ -54,11 +54,8 @@ def load_tickerdata_file(
|
|||
:return dict OR empty if unsuccesful
|
||||
"""
|
||||
path = make_testdata_path(datadir)
|
||||
pair_file_string = pair.replace('/', '_')
|
||||
file = os.path.join(path, '{pair}-{ticker_interval}.json'.format(
|
||||
pair=pair_file_string,
|
||||
ticker_interval=ticker_interval,
|
||||
))
|
||||
pair_s = pair.replace('/', '_')
|
||||
file = os.path.join(path, f'{pair_s}-{ticker_interval}.json')
|
||||
gzipfile = file + '.gz'
|
||||
|
||||
# If the file does not exist we download it when None is returned.
|
||||
|
|
|
@ -128,13 +128,12 @@ class Hyperopt(Backtesting):
|
|||
Log results if it is better than any previous evaluation
|
||||
"""
|
||||
if results['loss'] < self.current_best_loss:
|
||||
current = results['current_tries']
|
||||
total = results['total_tries']
|
||||
res = results['result']
|
||||
loss = results['loss']
|
||||
self.current_best_loss = results['loss']
|
||||
log_msg = '\n{:5d}/{}: {}. Loss {:.5f}'.format(
|
||||
results['current_tries'],
|
||||
results['total_tries'],
|
||||
results['result'],
|
||||
results['loss']
|
||||
)
|
||||
log_msg = f'\n{current:5d}/{total}: {res}. Loss {loss:.5f}'
|
||||
print(log_msg)
|
||||
else:
|
||||
print('.', end='')
|
||||
|
@ -309,15 +308,16 @@ class Hyperopt(Backtesting):
|
|||
"""
|
||||
Return the format result in a string
|
||||
"""
|
||||
return ('{:6d} trades. Avg profit {: 5.2f}%. '
|
||||
'Total profit {: 11.8f} {} ({:.4f}Σ%). Avg duration {:5.1f} mins.').format(
|
||||
len(results.index),
|
||||
results.profit_percent.mean() * 100.0,
|
||||
results.profit_abs.sum(),
|
||||
self.config['stake_currency'],
|
||||
results.profit_percent.sum(),
|
||||
results.trade_duration.mean(),
|
||||
)
|
||||
trades = len(results.index)
|
||||
avg_profit = results.profit_percent.mean() * 100.0
|
||||
total_profit = results.profit_abs.sum()
|
||||
stake_cur = self.config['stake_currency']
|
||||
profit = results.profit_percent.sum()
|
||||
duration = results.trade_duration.mean()
|
||||
|
||||
return (f'{trades:6d} trades. Avg profit {avg_profit: 5.2f}%. '
|
||||
f'Total profit {total_profit: 11.8f} {stake_cur} '
|
||||
f'({profit:.4f}Σ%). Avg duration {duration:5.1f} mins.')
|
||||
|
||||
def get_optimizer(self, cpu_count) -> Optimizer:
|
||||
return Optimizer(
|
||||
|
|
|
@ -21,6 +21,7 @@ from freqtrade import OperationalException
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
_DECL_BASE: Any = declarative_base()
|
||||
_SQL_DOCS_URL = 'http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls'
|
||||
|
||||
|
||||
def init(config: Dict) -> None:
|
||||
|
@ -45,10 +46,8 @@ def init(config: Dict) -> None:
|
|||
try:
|
||||
engine = create_engine(db_url, **kwargs)
|
||||
except NoSuchModuleError:
|
||||
error = 'Given value for db_url: \'{}\' is no valid database URL! (See {}).'.format(
|
||||
db_url, 'http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls'
|
||||
)
|
||||
raise OperationalException(error)
|
||||
raise OperationalException(f'Given value for db_url: \'{db_url}\' '
|
||||
f'is no valid database URL! (See {_SQL_DOCS_URL})')
|
||||
|
||||
session = scoped_session(sessionmaker(bind=engine, autoflush=True, autocommit=True))
|
||||
Trade.session = session()
|
||||
|
@ -173,13 +172,10 @@ class Trade(_DECL_BASE):
|
|||
max_rate = Column(Float, nullable=True, default=0.0)
|
||||
|
||||
def __repr__(self):
|
||||
return 'Trade(id={}, pair={}, amount={:.8f}, open_rate={:.8f}, open_since={})'.format(
|
||||
self.id,
|
||||
self.pair,
|
||||
self.amount,
|
||||
self.open_rate,
|
||||
arrow.get(self.open_date).humanize() if self.is_open else 'closed'
|
||||
)
|
||||
open_since = arrow.get(self.open_date).humanize() if self.is_open else 'closed'
|
||||
|
||||
return (f'Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, '
|
||||
f'open_rate={self.open_rate:.8f}, open_since={open_since})')
|
||||
|
||||
def adjust_stop_loss(self, current_price: float, stoploss: float, initial: bool = False):
|
||||
"""this adjusts the stop loss to it's most recently observed setting"""
|
||||
|
@ -226,6 +222,7 @@ class Trade(_DECL_BASE):
|
|||
:param order: order retrieved by exchange.get_order()
|
||||
:return: None
|
||||
"""
|
||||
order_type = order['type']
|
||||
# Ignore open and cancelled orders
|
||||
if order['status'] == 'open' or order['price'] is None:
|
||||
return
|
||||
|
@ -233,16 +230,16 @@ class Trade(_DECL_BASE):
|
|||
logger.info('Updating trade (id=%d) ...', self.id)
|
||||
|
||||
getcontext().prec = 8 # Bittrex do not go above 8 decimal
|
||||
if order['type'] == 'limit' and order['side'] == 'buy':
|
||||
if order_type == 'limit' and order['side'] == 'buy':
|
||||
# Update open rate and actual amount
|
||||
self.open_rate = Decimal(order['price'])
|
||||
self.amount = Decimal(order['amount'])
|
||||
logger.info('LIMIT_BUY has been fulfilled for %s.', self)
|
||||
self.open_order_id = None
|
||||
elif order['type'] == 'limit' and order['side'] == 'sell':
|
||||
elif order_type == 'limit' and order['side'] == 'sell':
|
||||
self.close(order['price'])
|
||||
else:
|
||||
raise ValueError('Unknown order type: {}'.format(order['type']))
|
||||
raise ValueError(f'Unknown order type: {order_type}')
|
||||
cleanup()
|
||||
|
||||
def close(self, rate: float) -> None:
|
||||
|
@ -313,7 +310,8 @@ class Trade(_DECL_BASE):
|
|||
rate=(rate or self.close_rate),
|
||||
fee=(fee or self.fee_close)
|
||||
)
|
||||
return float("{0:.8f}".format(close_trade_price - open_trade_price))
|
||||
profit = close_trade_price - open_trade_price
|
||||
return float(f"{profit:.8f}")
|
||||
|
||||
def calc_profit_percent(
|
||||
self,
|
||||
|
@ -333,5 +331,5 @@ class Trade(_DECL_BASE):
|
|||
rate=(rate or self.close_rate),
|
||||
fee=(fee or self.fee_close)
|
||||
)
|
||||
|
||||
return float("{0:.8f}".format((close_trade_price / open_trade_price) - 1))
|
||||
profit_percent = (close_trade_price / open_trade_price) - 1
|
||||
return float(f"{profit_percent:.8f}")
|
||||
|
|
|
@ -74,34 +74,32 @@ class RPC(object):
|
|||
# calculate profit and send message to user
|
||||
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid']
|
||||
current_profit = trade.calc_profit_percent(current_rate)
|
||||
fmt_close_profit = '{:.2f}%'.format(
|
||||
round(trade.close_profit * 100, 2)
|
||||
) if trade.close_profit else None
|
||||
message = "*Trade ID:* `{trade_id}`\n" \
|
||||
"*Current Pair:* [{pair}]({market_url})\n" \
|
||||
"*Open Since:* `{date}`\n" \
|
||||
"*Amount:* `{amount}`\n" \
|
||||
"*Open Rate:* `{open_rate:.8f}`\n" \
|
||||
"*Close Rate:* `{close_rate}`\n" \
|
||||
"*Current Rate:* `{current_rate:.8f}`\n" \
|
||||
"*Close Profit:* `{close_profit}`\n" \
|
||||
"*Current Profit:* `{current_profit:.2f}%`\n" \
|
||||
"*Open Order:* `{open_order}`"\
|
||||
.format(
|
||||
trade_id=trade.id,
|
||||
pair=trade.pair,
|
||||
market_url=self._freqtrade.exchange.get_pair_detail_url(trade.pair),
|
||||
date=arrow.get(trade.open_date).humanize(),
|
||||
open_rate=trade.open_rate,
|
||||
close_rate=trade.close_rate,
|
||||
current_rate=current_rate,
|
||||
amount=round(trade.amount, 8),
|
||||
close_profit=fmt_close_profit,
|
||||
current_profit=round(current_profit * 100, 2),
|
||||
open_order='({} {} rem={:.8f})'.format(
|
||||
order['type'], order['side'], order['remaining']
|
||||
) if order else None,
|
||||
)
|
||||
fmt_close_profit = (f'{round(trade.close_profit * 100, 2):.2f}%'
|
||||
if trade.close_profit else None)
|
||||
market_url = self._freqtrade.exchange.get_pair_detail_url(trade.pair)
|
||||
trade_date = arrow.get(trade.open_date).humanize()
|
||||
open_rate = trade.open_rate
|
||||
close_rate = trade.close_rate
|
||||
amount = round(trade.amount, 8)
|
||||
current_profit = round(current_profit * 100, 2)
|
||||
open_order = ''
|
||||
if order:
|
||||
order_type = order['type']
|
||||
order_side = order['side']
|
||||
order_rem = order['remaining']
|
||||
open_order = f'({order_type} {order_side} rem={order_rem:.8f})'
|
||||
|
||||
message = f"*Trade ID:* `{trade.id}`\n" \
|
||||
f"*Current Pair:* [{trade.pair}]({market_url})\n" \
|
||||
f"*Open Since:* `{trade_date}`\n" \
|
||||
f"*Amount:* `{amount}`\n" \
|
||||
f"*Open Rate:* `{open_rate:.8f}`\n" \
|
||||
f"*Close Rate:* `{close_rate}`\n" \
|
||||
f"*Current Rate:* `{current_rate:.8f}`\n" \
|
||||
f"*Close Profit:* `{fmt_close_profit}`\n" \
|
||||
f"*Current Profit:* `{current_profit:.2f}%`\n" \
|
||||
f"*Open Order:* `{open_order}`"\
|
||||
|
||||
result.append(message)
|
||||
return result
|
||||
|
||||
|
@ -116,11 +114,12 @@ class RPC(object):
|
|||
for trade in trades:
|
||||
# calculate profit and send message to user
|
||||
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid']
|
||||
trade_perc = (100 * trade.calc_profit_percent(current_rate))
|
||||
trades_list.append([
|
||||
trade.id,
|
||||
trade.pair,
|
||||
shorten_date(arrow.get(trade.open_date).humanize(only_distance=True)),
|
||||
'{:.2f}%'.format(100 * trade.calc_profit_percent(current_rate))
|
||||
f'{trade_perc:.2f}%'
|
||||
])
|
||||
|
||||
columns = ['ID', 'Pair', 'Since', 'Profit']
|
||||
|
@ -148,7 +147,7 @@ class RPC(object):
|
|||
.all()
|
||||
curdayprofit = sum(trade.calc_profit() for trade in trades)
|
||||
profit_days[profitday] = {
|
||||
'amount': format(curdayprofit, '.8f'),
|
||||
'amount': f'{curdayprofit:.8f}',
|
||||
'trades': len(trades)
|
||||
}
|
||||
|
||||
|
|
|
@ -153,7 +153,7 @@ class Telegram(RPC):
|
|||
try:
|
||||
df_statuses = self._rpc_status_table()
|
||||
message = tabulate(df_statuses, headers='keys', tablefmt='simple')
|
||||
self._send_msg("<pre>{}</pre>".format(message), parse_mode=ParseMode.HTML)
|
||||
self._send_msg(f"<pre>{message}</pre>", parse_mode=ParseMode.HTML)
|
||||
except RPCException as e:
|
||||
self._send_msg(str(e), bot=bot)
|
||||
|
||||
|
@ -166,6 +166,8 @@ class Telegram(RPC):
|
|||
:param update: message update
|
||||
:return: None
|
||||
"""
|
||||
stake_cur = self._config['stake_currency']
|
||||
fiat_disp_cur = self._config['fiat_display_currency']
|
||||
try:
|
||||
timescale = int(update.message.text.replace('/daily', '').strip())
|
||||
except (TypeError, ValueError):
|
||||
|
@ -173,18 +175,17 @@ class Telegram(RPC):
|
|||
try:
|
||||
stats = self._rpc_daily_profit(
|
||||
timescale,
|
||||
self._config['stake_currency'],
|
||||
self._config['fiat_display_currency']
|
||||
stake_cur,
|
||||
fiat_disp_cur
|
||||
)
|
||||
stats = tabulate(stats,
|
||||
headers=[
|
||||
'Day',
|
||||
'Profit {}'.format(self._config['stake_currency']),
|
||||
'Profit {}'.format(self._config['fiat_display_currency'])
|
||||
f'Profit {stake_cur}',
|
||||
f'Profit {fiat_disp_cur}'
|
||||
],
|
||||
tablefmt='simple')
|
||||
message = '<b>Daily Profit over the last {} days</b>:\n<pre>{}</pre>'\
|
||||
.format(timescale, stats)
|
||||
message = f'<b>Daily Profit over the last {timescale} days</b>:\n<pre>{stats}</pre>'
|
||||
self._send_msg(message, bot=bot, parse_mode=ParseMode.HTML)
|
||||
except RPCException as e:
|
||||
self._send_msg(str(e), bot=bot)
|
||||
|
@ -198,39 +199,38 @@ class Telegram(RPC):
|
|||
:param update: message update
|
||||
:return: None
|
||||
"""
|
||||
stake_cur = self._config['stake_currency']
|
||||
fiat_disp_cur = self._config['fiat_display_currency']
|
||||
|
||||
try:
|
||||
stats = self._rpc_trade_statistics(
|
||||
self._config['stake_currency'],
|
||||
self._config['fiat_display_currency'])
|
||||
|
||||
stake_cur,
|
||||
fiat_disp_cur)
|
||||
profit_closed_coin = stats['profit_closed_coin']
|
||||
profit_closed_percent = stats['profit_closed_percent']
|
||||
profit_closed_fiat = stats['profit_closed_fiat']
|
||||
profit_all_coin = stats['profit_all_coin']
|
||||
profit_all_percent = stats['profit_all_percent']
|
||||
profit_all_fiat = stats['profit_all_fiat']
|
||||
trade_count = stats['trade_count']
|
||||
first_trade_date = stats['first_trade_date']
|
||||
latest_trade_date = stats['latest_trade_date']
|
||||
avg_duration = stats['avg_duration']
|
||||
best_pair = stats['best_pair']
|
||||
best_rate = stats['best_rate']
|
||||
# Message to display
|
||||
markdown_msg = "*ROI:* Close trades\n" \
|
||||
"∙ `{profit_closed_coin:.8f} {coin} ({profit_closed_percent:.2f}%)`\n" \
|
||||
"∙ `{profit_closed_fiat:.3f} {fiat}`\n" \
|
||||
"*ROI:* All trades\n" \
|
||||
"∙ `{profit_all_coin:.8f} {coin} ({profit_all_percent:.2f}%)`\n" \
|
||||
"∙ `{profit_all_fiat:.3f} {fiat}`\n" \
|
||||
"*Total Trade Count:* `{trade_count}`\n" \
|
||||
"*First Trade opened:* `{first_trade_date}`\n" \
|
||||
"*Latest Trade opened:* `{latest_trade_date}`\n" \
|
||||
"*Avg. Duration:* `{avg_duration}`\n" \
|
||||
"*Best Performing:* `{best_pair}: {best_rate:.2f}%`"\
|
||||
.format(
|
||||
coin=self._config['stake_currency'],
|
||||
fiat=self._config['fiat_display_currency'],
|
||||
profit_closed_coin=stats['profit_closed_coin'],
|
||||
profit_closed_percent=stats['profit_closed_percent'],
|
||||
profit_closed_fiat=stats['profit_closed_fiat'],
|
||||
profit_all_coin=stats['profit_all_coin'],
|
||||
profit_all_percent=stats['profit_all_percent'],
|
||||
profit_all_fiat=stats['profit_all_fiat'],
|
||||
trade_count=stats['trade_count'],
|
||||
first_trade_date=stats['first_trade_date'],
|
||||
latest_trade_date=stats['latest_trade_date'],
|
||||
avg_duration=stats['avg_duration'],
|
||||
best_pair=stats['best_pair'],
|
||||
best_rate=stats['best_rate']
|
||||
)
|
||||
f"∙ `{profit_closed_coin:.8f} {stake_cur} "\
|
||||
f"({profit_closed_percent:.2f}%)`\n" \
|
||||
f"∙ `{profit_closed_fiat:.3f} {fiat_disp_cur}`\n" \
|
||||
f"*ROI:* All trades\n" \
|
||||
f"∙ `{profit_all_coin:.8f} {stake_cur} ({profit_all_percent:.2f}%)`\n" \
|
||||
f"∙ `{profit_all_fiat:.3f} {fiat_disp_cur}`\n" \
|
||||
f"*Total Trade Count:* `{trade_count}`\n" \
|
||||
f"*First Trade opened:* `{first_trade_date}`\n" \
|
||||
f"*Latest Trade opened:* `{latest_trade_date}`\n" \
|
||||
f"*Avg. Duration:* `{avg_duration}`\n" \
|
||||
f"*Best Performing:* `{best_pair}: {best_rate:.2f}%`"
|
||||
self._send_msg(markdown_msg, bot=bot)
|
||||
except RPCException as e:
|
||||
self._send_msg(str(e), bot=bot)
|
||||
|
|
|
@ -143,15 +143,14 @@ def convert_main(args: Namespace) -> None:
|
|||
interval = str_interval
|
||||
break
|
||||
# change order on pairs if old ticker interval found
|
||||
|
||||
filename_new = path.join(path.dirname(filename),
|
||||
"{}_{}-{}.json".format(currencies[1],
|
||||
currencies[0], interval))
|
||||
f"{currencies[1]}_{currencies[0]}-{interval}.json")
|
||||
|
||||
elif ret_string:
|
||||
interval = ret_string.group(0)
|
||||
filename_new = path.join(path.dirname(filename),
|
||||
"{}_{}-{}.json".format(currencies[0],
|
||||
currencies[1], interval))
|
||||
f"{currencies[0]}_{currencies[1]}-{interval}.json")
|
||||
|
||||
else:
|
||||
logger.warning("file %s could not be converted, interval not found", filename)
|
||||
|
|
Loading…
Reference in New Issue
Block a user