mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 02:12:01 +00:00
A few more formatting updates
This commit is contained in:
parent
6a802f5624
commit
9291698561
|
@ -73,9 +73,9 @@ def ask_user_config() -> Dict[str, Any]:
|
||||||
"message": f"Please insert your stake amount (Number or '{UNLIMITED_STAKE_AMOUNT}'):",
|
"message": f"Please insert your stake amount (Number or '{UNLIMITED_STAKE_AMOUNT}'):",
|
||||||
"default": "unlimited",
|
"default": "unlimited",
|
||||||
"validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_float(val),
|
"validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_float(val),
|
||||||
"filter": lambda val: '"' + UNLIMITED_STAKE_AMOUNT + '"'
|
"filter": lambda val: (
|
||||||
if val == UNLIMITED_STAKE_AMOUNT
|
'"' + UNLIMITED_STAKE_AMOUNT + '"' if val == UNLIMITED_STAKE_AMOUNT else val
|
||||||
else val,
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
|
|
@ -340,9 +340,9 @@ AVAILABLE_CLI_OPTIONS = {
|
||||||
"hyperopt_loss": Arg(
|
"hyperopt_loss": Arg(
|
||||||
"--hyperopt-loss",
|
"--hyperopt-loss",
|
||||||
"--hyperoptloss",
|
"--hyperoptloss",
|
||||||
help='Specify the class name of the hyperopt loss function class (IHyperOptLoss). '
|
help="Specify the class name of the hyperopt loss function class (IHyperOptLoss). "
|
||||||
'Different functions can generate completely different results, '
|
"Different functions can generate completely different results, "
|
||||||
'since the target for optimization is different. Built-in Hyperopt-loss-functions are: '
|
"since the target for optimization is different. Built-in Hyperopt-loss-functions are: "
|
||||||
f'{", ".join(HYPEROPT_LOSS_BUILTIN)}',
|
f'{", ".join(HYPEROPT_LOSS_BUILTIN)}',
|
||||||
metavar="NAME",
|
metavar="NAME",
|
||||||
),
|
),
|
||||||
|
|
|
@ -41,9 +41,11 @@ def _flat_vars_to_nested_dict(env_dict: Dict[str, Any], prefix: str) -> Dict[str
|
||||||
key = env_var.replace(prefix, "")
|
key = env_var.replace(prefix, "")
|
||||||
for k in reversed(key.split("__")):
|
for k in reversed(key.split("__")):
|
||||||
val = {
|
val = {
|
||||||
k.lower(): _get_var_typed(val)
|
k.lower(): (
|
||||||
if not isinstance(val, dict) and k not in no_convert
|
_get_var_typed(val)
|
||||||
else val
|
if not isinstance(val, dict) and k not in no_convert
|
||||||
|
else val
|
||||||
|
)
|
||||||
}
|
}
|
||||||
relevant_vars = deep_merge_dicts(val, relevant_vars)
|
relevant_vars = deep_merge_dicts(val, relevant_vars)
|
||||||
return relevant_vars
|
return relevant_vars
|
||||||
|
|
|
@ -262,7 +262,7 @@ def _download_pair_history(
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f'({process}) - Download history data for "{pair}", {timeframe}, '
|
f'({process}) - Download history data for "{pair}", {timeframe}, '
|
||||||
f'{candle_type} and store in {datadir}. '
|
f"{candle_type} and store in {datadir}. "
|
||||||
f'From {format_ms_time(since_ms) if since_ms else "start"} to '
|
f'From {format_ms_time(since_ms) if since_ms else "start"} to '
|
||||||
f'{format_ms_time(until_ms) if until_ms else "now"}'
|
f'{format_ms_time(until_ms) if until_ms else "now"}'
|
||||||
)
|
)
|
||||||
|
@ -280,9 +280,11 @@ def _download_pair_history(
|
||||||
new_data = exchange.get_historic_ohlcv(
|
new_data = exchange.get_historic_ohlcv(
|
||||||
pair=pair,
|
pair=pair,
|
||||||
timeframe=timeframe,
|
timeframe=timeframe,
|
||||||
since_ms=since_ms
|
since_ms=(
|
||||||
if since_ms
|
since_ms
|
||||||
else int((datetime.now() - timedelta(days=new_pairs_days)).timestamp()) * 1000,
|
if since_ms
|
||||||
|
else int((datetime.now() - timedelta(days=new_pairs_days)).timestamp()) * 1000
|
||||||
|
),
|
||||||
is_new_pair=data.empty,
|
is_new_pair=data.empty,
|
||||||
candle_type=candle_type,
|
candle_type=candle_type,
|
||||||
until_ms=until_ms if until_ms else None,
|
until_ms=until_ms if until_ms else None,
|
||||||
|
|
|
@ -33,17 +33,17 @@ def check_exchange(config: Config, check_for_bad: bool = True) -> bool:
|
||||||
exchange = config.get("exchange", {}).get("name", "").lower()
|
exchange = config.get("exchange", {}).get("name", "").lower()
|
||||||
if not exchange:
|
if not exchange:
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
f'This command requires a configured exchange. You should either use '
|
f"This command requires a configured exchange. You should either use "
|
||||||
f'`--exchange <exchange_name>` or specify a configuration file via `--config`.\n'
|
f"`--exchange <exchange_name>` or specify a configuration file via `--config`.\n"
|
||||||
f'The following exchanges are available for Freqtrade: '
|
f"The following exchanges are available for Freqtrade: "
|
||||||
f'{", ".join(available_exchanges())}'
|
f'{", ".join(available_exchanges())}'
|
||||||
)
|
)
|
||||||
|
|
||||||
if not is_exchange_known_ccxt(exchange):
|
if not is_exchange_known_ccxt(exchange):
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
f'Exchange "{exchange}" is not known to the ccxt library '
|
f'Exchange "{exchange}" is not known to the ccxt library '
|
||||||
f'and therefore not available for the bot.\n'
|
f"and therefore not available for the bot.\n"
|
||||||
f'The following exchanges are available for Freqtrade: '
|
f"The following exchanges are available for Freqtrade: "
|
||||||
f'{", ".join(available_exchanges())}'
|
f'{", ".join(available_exchanges())}'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1310,8 +1310,8 @@ class FreqtradeBot(LoggingMixin):
|
||||||
if should_exit.exit_flag:
|
if should_exit.exit_flag:
|
||||||
exit_tag1 = exit_tag if should_exit.exit_type == ExitType.EXIT_SIGNAL else None
|
exit_tag1 = exit_tag if should_exit.exit_type == ExitType.EXIT_SIGNAL else None
|
||||||
logger.info(
|
logger.info(
|
||||||
f'Exit for {trade.pair} detected. Reason: {should_exit.exit_type}'
|
f"Exit for {trade.pair} detected. Reason: {should_exit.exit_type}"
|
||||||
f'{f" Tag: {exit_tag1}" if exit_tag1 is not None else ""}'
|
f"{f' Tag: {exit_tag1}' if exit_tag1 is not None else ''}"
|
||||||
)
|
)
|
||||||
exited = self.execute_trade_exit(trade, exit_rate, should_exit, exit_tag=exit_tag1)
|
exited = self.execute_trade_exit(trade, exit_rate, should_exit, exit_tag=exit_tag1)
|
||||||
if exited:
|
if exited:
|
||||||
|
|
|
@ -230,9 +230,11 @@ class Hyperopt:
|
||||||
result["trailing"] = self.custom_hyperopt.generate_trailing_params(params)
|
result["trailing"] = self.custom_hyperopt.generate_trailing_params(params)
|
||||||
if HyperoptTools.has_space(self.config, "trades"):
|
if HyperoptTools.has_space(self.config, "trades"):
|
||||||
result["max_open_trades"] = {
|
result["max_open_trades"] = {
|
||||||
"max_open_trades": self.backtesting.strategy.max_open_trades
|
"max_open_trades": (
|
||||||
if self.backtesting.strategy.max_open_trades != float("inf")
|
self.backtesting.strategy.max_open_trades
|
||||||
else -1
|
if self.backtesting.strategy.max_open_trades != float("inf")
|
||||||
|
else -1
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -457,11 +457,13 @@ class HyperoptTools:
|
||||||
lambda x: f"{x:,.2%}".rjust(7, " ") if not isna(x) else "--".rjust(7, " ")
|
lambda x: f"{x:,.2%}".rjust(7, " ") if not isna(x) else "--".rjust(7, " ")
|
||||||
)
|
)
|
||||||
trials["Avg duration"] = trials["Avg duration"].apply(
|
trials["Avg duration"] = trials["Avg duration"].apply(
|
||||||
lambda x: f"{x:,.1f} m".rjust(7, " ")
|
lambda x: (
|
||||||
if isinstance(x, float)
|
f"{x:,.1f} m".rjust(7, " ")
|
||||||
else f"{x}"
|
if isinstance(x, float)
|
||||||
if not isna(x)
|
else f"{x}"
|
||||||
else "--".rjust(7, " ")
|
if not isna(x)
|
||||||
|
else "--".rjust(7, " ")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
trials["Objective"] = trials["Objective"].apply(
|
trials["Objective"] = trials["Objective"].apply(
|
||||||
lambda x: f"{x:,.5f}".rjust(8, " ") if x != 100000 else "N/A".rjust(8, " ")
|
lambda x: f"{x:,.5f}".rjust(8, " ") if x != 100000 else "N/A".rjust(8, " ")
|
||||||
|
@ -470,28 +472,32 @@ class HyperoptTools:
|
||||||
stake_currency = config["stake_currency"]
|
stake_currency = config["stake_currency"]
|
||||||
|
|
||||||
trials[f"Max Drawdown{' (Acct)' if has_account_drawdown else ''}"] = trials.apply(
|
trials[f"Max Drawdown{' (Acct)' if has_account_drawdown else ''}"] = trials.apply(
|
||||||
lambda x: "{} {}".format(
|
lambda x: (
|
||||||
fmt_coin(x["max_drawdown_abs"], stake_currency, keep_trailing_zeros=True),
|
"{} {}".format(
|
||||||
(
|
fmt_coin(x["max_drawdown_abs"], stake_currency, keep_trailing_zeros=True),
|
||||||
f"({x['max_drawdown_account']:,.2%})"
|
(
|
||||||
if has_account_drawdown
|
f"({x['max_drawdown_account']:,.2%})"
|
||||||
else f"({x['max_drawdown']:,.2%})"
|
if has_account_drawdown
|
||||||
).rjust(10, " "),
|
else f"({x['max_drawdown']:,.2%})"
|
||||||
).rjust(25 + len(stake_currency))
|
).rjust(10, " "),
|
||||||
if x["max_drawdown"] != 0.0 or x["max_drawdown_account"] != 0.0
|
).rjust(25 + len(stake_currency))
|
||||||
else "--".rjust(25 + len(stake_currency)),
|
if x["max_drawdown"] != 0.0 or x["max_drawdown_account"] != 0.0
|
||||||
|
else "--".rjust(25 + len(stake_currency))
|
||||||
|
),
|
||||||
axis=1,
|
axis=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
trials = trials.drop(columns=["max_drawdown_abs", "max_drawdown", "max_drawdown_account"])
|
trials = trials.drop(columns=["max_drawdown_abs", "max_drawdown", "max_drawdown_account"])
|
||||||
|
|
||||||
trials["Profit"] = trials.apply(
|
trials["Profit"] = trials.apply(
|
||||||
lambda x: "{} {}".format(
|
lambda x: (
|
||||||
fmt_coin(x["Total profit"], stake_currency, keep_trailing_zeros=True),
|
"{} {}".format(
|
||||||
f"({x['Profit']:,.2%})".rjust(10, " "),
|
fmt_coin(x["Total profit"], stake_currency, keep_trailing_zeros=True),
|
||||||
).rjust(25 + len(stake_currency))
|
f"({x['Profit']:,.2%})".rjust(10, " "),
|
||||||
if x["Total profit"] != 0.0
|
).rjust(25 + len(stake_currency))
|
||||||
else "--".rjust(25 + len(stake_currency)),
|
if x["Total profit"] != 0.0
|
||||||
|
else "--".rjust(25 + len(stake_currency))
|
||||||
|
),
|
||||||
axis=1,
|
axis=1,
|
||||||
)
|
)
|
||||||
trials = trials.drop(columns=["Total profit"])
|
trials = trials.drop(columns=["Total profit"])
|
||||||
|
|
|
@ -89,9 +89,11 @@ def text_table_tags(tag_type: str, tag_results: List[Dict[str, Any]], stake_curr
|
||||||
floatfmt = _get_line_floatfmt(stake_currency)
|
floatfmt = _get_line_floatfmt(stake_currency)
|
||||||
output = [
|
output = [
|
||||||
[
|
[
|
||||||
t["key"]
|
(
|
||||||
if t.get("key") is not None and len(str(t["key"])) > 0
|
t["key"]
|
||||||
else t.get(fallback, "OTHER"),
|
if t.get("key") is not None and len(str(t["key"])) > 0
|
||||||
|
else t.get(fallback, "OTHER")
|
||||||
|
),
|
||||||
t["trades"],
|
t["trades"],
|
||||||
t["profit_mean_pct"],
|
t["profit_mean_pct"],
|
||||||
t["profit_total_abs"],
|
t["profit_total_abs"],
|
||||||
|
@ -218,9 +220,11 @@ def text_table_add_metrics(strat_results: Dict) -> str:
|
||||||
)
|
)
|
||||||
drawdown_metrics.extend(
|
drawdown_metrics.extend(
|
||||||
[
|
[
|
||||||
("Absolute Drawdown (Account)", f"{strat_results['max_drawdown_account']:.2%}")
|
(
|
||||||
if "max_drawdown_account" in strat_results
|
("Absolute Drawdown (Account)", f"{strat_results['max_drawdown_account']:.2%}")
|
||||||
else ("Drawdown", f"{strat_results['max_drawdown']:.2%}"),
|
if "max_drawdown_account" in strat_results
|
||||||
|
else ("Drawdown", f"{strat_results['max_drawdown']:.2%}")
|
||||||
|
),
|
||||||
(
|
(
|
||||||
"Absolute Drawdown",
|
"Absolute Drawdown",
|
||||||
fmt_coin(strat_results["max_drawdown_abs"], strat_results["stake_currency"]),
|
fmt_coin(strat_results["max_drawdown_abs"], strat_results["stake_currency"]),
|
||||||
|
@ -279,9 +283,11 @@ def text_table_add_metrics(strat_results: Dict) -> str:
|
||||||
("Calmar", f"{strat_results['calmar']:.2f}" if "calmar" in strat_results else "N/A"),
|
("Calmar", f"{strat_results['calmar']:.2f}" if "calmar" in strat_results else "N/A"),
|
||||||
(
|
(
|
||||||
"Profit factor",
|
"Profit factor",
|
||||||
f'{strat_results["profit_factor"]:.2f}'
|
(
|
||||||
if "profit_factor" in strat_results
|
f'{strat_results["profit_factor"]:.2f}'
|
||||||
else "N/A",
|
if "profit_factor" in strat_results
|
||||||
|
else "N/A"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"Expectancy (Ratio)",
|
"Expectancy (Ratio)",
|
||||||
|
@ -335,11 +341,13 @@ def text_table_add_metrics(strat_results: Dict) -> str:
|
||||||
(
|
(
|
||||||
"Max Consecutive Wins / Loss",
|
"Max Consecutive Wins / Loss",
|
||||||
(
|
(
|
||||||
f"{strat_results['max_consecutive_wins']} / "
|
(
|
||||||
f"{strat_results['max_consecutive_losses']}"
|
f"{strat_results['max_consecutive_wins']} / "
|
||||||
)
|
f"{strat_results['max_consecutive_losses']}"
|
||||||
if "max_consecutive_losses" in strat_results
|
)
|
||||||
else "N/A",
|
if "max_consecutive_losses" in strat_results
|
||||||
|
else "N/A"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
("Rejected Entry signals", strat_results.get("rejected_signals", "N/A")),
|
("Rejected Entry signals", strat_results.get("rejected_signals", "N/A")),
|
||||||
(
|
(
|
||||||
|
|
|
@ -80,17 +80,19 @@ def _generate_result_line(result: DataFrame, starting_balance: int, first_column
|
||||||
"key": first_column,
|
"key": first_column,
|
||||||
"trades": len(result),
|
"trades": len(result),
|
||||||
"profit_mean": result["profit_ratio"].mean() if len(result) > 0 else 0.0,
|
"profit_mean": result["profit_ratio"].mean() if len(result) > 0 else 0.0,
|
||||||
"profit_mean_pct": round(result["profit_ratio"].mean() * 100.0, 2)
|
"profit_mean_pct": (
|
||||||
if len(result) > 0
|
round(result["profit_ratio"].mean() * 100.0, 2) if len(result) > 0 else 0.0
|
||||||
else 0.0,
|
),
|
||||||
"profit_sum": profit_sum,
|
"profit_sum": profit_sum,
|
||||||
"profit_sum_pct": round(profit_sum * 100.0, 2),
|
"profit_sum_pct": round(profit_sum * 100.0, 2),
|
||||||
"profit_total_abs": result["profit_abs"].sum(),
|
"profit_total_abs": result["profit_abs"].sum(),
|
||||||
"profit_total": profit_total,
|
"profit_total": profit_total,
|
||||||
"profit_total_pct": round(profit_total * 100.0, 2),
|
"profit_total_pct": round(profit_total * 100.0, 2),
|
||||||
"duration_avg": str(timedelta(minutes=round(result["trade_duration"].mean())))
|
"duration_avg": (
|
||||||
if not result.empty
|
str(timedelta(minutes=round(result["trade_duration"].mean())))
|
||||||
else "0:00",
|
if not result.empty
|
||||||
|
else "0:00"
|
||||||
|
),
|
||||||
# 'duration_max': str(timedelta(
|
# 'duration_max': str(timedelta(
|
||||||
# minutes=round(result['trade_duration'].max()))
|
# minutes=round(result['trade_duration'].max()))
|
||||||
# ) if not result.empty else '0:00',
|
# ) if not result.empty else '0:00',
|
||||||
|
|
|
@ -164,7 +164,8 @@ def migrate_trades_and_orders_table(
|
||||||
# Copy data back - following the correct schema
|
# Copy data back - following the correct schema
|
||||||
with engine.begin() as connection:
|
with engine.begin() as connection:
|
||||||
connection.execute(
|
connection.execute(
|
||||||
text(f"""insert into trades
|
text(
|
||||||
|
f"""insert into trades
|
||||||
(id, exchange, pair, base_currency, stake_currency, is_open,
|
(id, exchange, pair, base_currency, stake_currency, is_open,
|
||||||
fee_open, fee_open_cost, fee_open_currency,
|
fee_open, fee_open_cost, fee_open_currency,
|
||||||
fee_close, fee_close_cost, fee_close_currency, open_rate,
|
fee_close, fee_close_cost, fee_close_currency, open_rate,
|
||||||
|
@ -209,7 +210,8 @@ def migrate_trades_and_orders_table(
|
||||||
{precision_mode} precision_mode, {contract_size} contract_size,
|
{precision_mode} precision_mode, {contract_size} contract_size,
|
||||||
{max_stake_amount} max_stake_amount
|
{max_stake_amount} max_stake_amount
|
||||||
from {trade_back_name}
|
from {trade_back_name}
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
migrate_orders_table(engine, order_back_name, cols_order)
|
migrate_orders_table(engine, order_back_name, cols_order)
|
||||||
|
@ -238,7 +240,8 @@ def migrate_orders_table(engine, table_back_name: str, cols_order: List):
|
||||||
# sqlite does not support literals for booleans
|
# sqlite does not support literals for booleans
|
||||||
with engine.begin() as connection:
|
with engine.begin() as connection:
|
||||||
connection.execute(
|
connection.execute(
|
||||||
text(f"""
|
text(
|
||||||
|
f"""
|
||||||
insert into orders (id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id,
|
insert into orders (id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id,
|
||||||
status, symbol, order_type, side, price, amount, filled, average, remaining, cost,
|
status, symbol, order_type, side, price, amount, filled, average, remaining, cost,
|
||||||
stop_price, order_date, order_filled_date, order_update_date, ft_fee_base, funding_fee,
|
stop_price, order_date, order_filled_date, order_update_date, ft_fee_base, funding_fee,
|
||||||
|
@ -251,7 +254,8 @@ def migrate_orders_table(engine, table_back_name: str, cols_order: List):
|
||||||
{ft_amount} ft_amount, {ft_price} ft_price, {ft_cancel_reason} ft_cancel_reason,
|
{ft_amount} ft_amount, {ft_price} ft_price, {ft_cancel_reason} ft_cancel_reason,
|
||||||
{ft_order_tag} ft_order_tag
|
{ft_order_tag} ft_order_tag
|
||||||
from {table_back_name}
|
from {table_back_name}
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -269,13 +273,15 @@ def migrate_pairlocks_table(decl_base, inspector, engine, pairlock_back_name: st
|
||||||
# Copy data back - following the correct schema
|
# Copy data back - following the correct schema
|
||||||
with engine.begin() as connection:
|
with engine.begin() as connection:
|
||||||
connection.execute(
|
connection.execute(
|
||||||
text(f"""insert into pairlocks
|
text(
|
||||||
|
f"""insert into pairlocks
|
||||||
(id, pair, side, reason, lock_time,
|
(id, pair, side, reason, lock_time,
|
||||||
lock_end_time, active)
|
lock_end_time, active)
|
||||||
select id, pair, {side} side, reason, lock_time,
|
select id, pair, {side} side, reason, lock_time,
|
||||||
lock_end_time, active
|
lock_end_time, active
|
||||||
from {pairlock_back_name}
|
from {pairlock_back_name}
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -266,17 +266,19 @@ class Order(ModelBase):
|
||||||
"cost": self.cost if self.cost else 0,
|
"cost": self.cost if self.cost else 0,
|
||||||
"filled": self.filled,
|
"filled": self.filled,
|
||||||
"is_open": self.ft_is_open,
|
"is_open": self.ft_is_open,
|
||||||
"order_date": self.order_date.strftime(DATETIME_PRINT_FORMAT)
|
"order_date": (
|
||||||
if self.order_date
|
self.order_date.strftime(DATETIME_PRINT_FORMAT) if self.order_date else None
|
||||||
else None,
|
),
|
||||||
"order_timestamp": int(
|
"order_timestamp": (
|
||||||
self.order_date.replace(tzinfo=timezone.utc).timestamp() * 1000
|
int(self.order_date.replace(tzinfo=timezone.utc).timestamp() * 1000)
|
||||||
)
|
if self.order_date
|
||||||
if self.order_date
|
else None
|
||||||
else None,
|
),
|
||||||
"order_filled_date": self.order_filled_date.strftime(DATETIME_PRINT_FORMAT)
|
"order_filled_date": (
|
||||||
if self.order_filled_date
|
self.order_filled_date.strftime(DATETIME_PRINT_FORMAT)
|
||||||
else None,
|
if self.order_filled_date
|
||||||
|
else None
|
||||||
|
),
|
||||||
"order_type": self.order_type,
|
"order_type": self.order_type,
|
||||||
"price": self.price,
|
"price": self.price,
|
||||||
"remaining": self.remaining,
|
"remaining": self.remaining,
|
||||||
|
|
|
@ -379,9 +379,9 @@ def pair_history_filtered(
|
||||||
{
|
{
|
||||||
"strategy": payload.strategy,
|
"strategy": payload.strategy,
|
||||||
"timerange": payload.timerange,
|
"timerange": payload.timerange,
|
||||||
"freqaimodel": payload.freqaimodel
|
"freqaimodel": (
|
||||||
if payload.freqaimodel
|
payload.freqaimodel if payload.freqaimodel else config.get("freqaimodel")
|
||||||
else config.get("freqaimodel"),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -148,9 +148,9 @@ class RPC:
|
||||||
"bot_name": config.get("bot_name", "freqtrade"),
|
"bot_name": config.get("bot_name", "freqtrade"),
|
||||||
"timeframe": config.get("timeframe"),
|
"timeframe": config.get("timeframe"),
|
||||||
"timeframe_ms": timeframe_to_msecs(config["timeframe"]) if "timeframe" in config else 0,
|
"timeframe_ms": timeframe_to_msecs(config["timeframe"]) if "timeframe" in config else 0,
|
||||||
"timeframe_min": timeframe_to_minutes(config["timeframe"])
|
"timeframe_min": (
|
||||||
if "timeframe" in config
|
timeframe_to_minutes(config["timeframe"]) if "timeframe" in config else 0
|
||||||
else 0,
|
),
|
||||||
"exchange": config["exchange"]["name"],
|
"exchange": config["exchange"]["name"],
|
||||||
"strategy": config["strategy"],
|
"strategy": config["strategy"],
|
||||||
"force_entry_enable": config.get("force_entry_enable", False),
|
"force_entry_enable": config.get("force_entry_enable", False),
|
||||||
|
@ -404,11 +404,13 @@ class RPC:
|
||||||
"abs_profit": value["amount"],
|
"abs_profit": value["amount"],
|
||||||
"starting_balance": value["daily_stake"],
|
"starting_balance": value["daily_stake"],
|
||||||
"rel_profit": value["rel_profit"],
|
"rel_profit": value["rel_profit"],
|
||||||
"fiat_value": self._fiat_converter.convert_amount(
|
"fiat_value": (
|
||||||
value["amount"], stake_currency, fiat_display_currency
|
self._fiat_converter.convert_amount(
|
||||||
)
|
value["amount"], stake_currency, fiat_display_currency
|
||||||
if self._fiat_converter
|
)
|
||||||
else 0,
|
if self._fiat_converter
|
||||||
|
else 0
|
||||||
|
),
|
||||||
"trade_count": value["trades"],
|
"trade_count": value["trades"],
|
||||||
}
|
}
|
||||||
for key, value in profit_units.items()
|
for key, value in profit_units.items()
|
||||||
|
|
|
@ -120,13 +120,13 @@ class RPCManager:
|
||||||
self.send_msg(
|
self.send_msg(
|
||||||
{
|
{
|
||||||
"type": RPCMessageType.STARTUP,
|
"type": RPCMessageType.STARTUP,
|
||||||
"status": f'*Exchange:* `{exchange_name}`\n'
|
"status": f"*Exchange:* `{exchange_name}`\n"
|
||||||
f'*Stake per trade:* `{stake_amount} {stake_currency}`\n'
|
f"*Stake per trade:* `{stake_amount} {stake_currency}`\n"
|
||||||
f'*Minimum ROI:* `{minimal_roi}`\n'
|
f"*Minimum ROI:* `{minimal_roi}`\n"
|
||||||
f'*{"Trailing " if trailing_stop else ""}Stoploss:* `{stoploss}`\n'
|
f"*{'Trailing ' if trailing_stop else ''}Stoploss:* `{stoploss}`\n"
|
||||||
f'*Position adjustment:* `{pos_adjust_enabled}`\n'
|
f"*Position adjustment:* `{pos_adjust_enabled}`\n"
|
||||||
f'*Timeframe:* `{timeframe}`\n'
|
f"*Timeframe:* `{timeframe}`\n"
|
||||||
f'*Strategy:* `{strategy_name}`',
|
f"*Strategy:* `{strategy_name}`",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.send_msg(
|
self.send_msg(
|
||||||
|
|
|
@ -729,9 +729,12 @@ class Telegram(RPCHandler):
|
||||||
lines = [
|
lines = [
|
||||||
"*Trade ID:* `{trade_id}`" + (" `(since {open_date_hum})`" if r["is_open"] else ""),
|
"*Trade ID:* `{trade_id}`" + (" `(since {open_date_hum})`" if r["is_open"] else ""),
|
||||||
"*Current Pair:* {pair}",
|
"*Current Pair:* {pair}",
|
||||||
f"*Direction:* {'`Short`' if r.get('is_short') else '`Long`'}" + " ` ({leverage}x)`"
|
(
|
||||||
if r.get("leverage")
|
f"*Direction:* {'`Short`' if r.get('is_short') else '`Long`'}"
|
||||||
else "",
|
+ " ` ({leverage}x)`"
|
||||||
|
if r.get("leverage")
|
||||||
|
else ""
|
||||||
|
),
|
||||||
"*Amount:* `{amount} ({stake_amount_r})`",
|
"*Amount:* `{amount} ({stake_amount_r})`",
|
||||||
"*Total invested:* `{max_stake_amount_r}`" if position_adjust else "",
|
"*Total invested:* `{max_stake_amount_r}`" if position_adjust else "",
|
||||||
"*Enter Tag:* `{enter_tag}`" if r["enter_tag"] else "",
|
"*Enter Tag:* `{enter_tag}`" if r["enter_tag"] else "",
|
||||||
|
@ -753,9 +756,11 @@ class Telegram(RPCHandler):
|
||||||
f"*Close Rate:* `{round_value(r['close_rate'], 8)}`" if r["close_rate"] else "",
|
f"*Close Rate:* `{round_value(r['close_rate'], 8)}`" if r["close_rate"] else "",
|
||||||
"*Open Date:* `{open_date}`",
|
"*Open Date:* `{open_date}`",
|
||||||
"*Close Date:* `{close_date}`" if r["close_date"] else "",
|
"*Close Date:* `{close_date}`" if r["close_date"] else "",
|
||||||
f" \n*Current Rate:* `{round_value(r['current_rate'], 8)}`"
|
(
|
||||||
if r["is_open"]
|
f" \n*Current Rate:* `{round_value(r['current_rate'], 8)}`"
|
||||||
else "",
|
if r["is_open"]
|
||||||
|
else ""
|
||||||
|
),
|
||||||
("*Unrealized Profit:* " if r["is_open"] else "*Close Profit: *")
|
("*Unrealized Profit:* " if r["is_open"] else "*Close Profit: *")
|
||||||
+ "`{profit_ratio:.2%}` `({profit_abs_r})`",
|
+ "`{profit_ratio:.2%}` `({profit_abs_r})`",
|
||||||
]
|
]
|
||||||
|
@ -1072,15 +1077,19 @@ class Telegram(RPCHandler):
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
"Wins",
|
"Wins",
|
||||||
str(timedelta(seconds=durations["wins"]))
|
(
|
||||||
if durations["wins"] is not None
|
str(timedelta(seconds=durations["wins"]))
|
||||||
else "N/A",
|
if durations["wins"] is not None
|
||||||
|
else "N/A"
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"Losses",
|
"Losses",
|
||||||
str(timedelta(seconds=durations["losses"]))
|
(
|
||||||
if durations["losses"] is not None
|
str(timedelta(seconds=durations["losses"]))
|
||||||
else "N/A",
|
if durations["losses"] is not None
|
||||||
|
else "N/A"
|
||||||
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
headers=["", "Avg. Duration"],
|
headers=["", "Avg. Duration"],
|
||||||
|
@ -1429,7 +1438,7 @@ class Telegram(RPCHandler):
|
||||||
msg = self._rpc._rpc_delete(trade_id)
|
msg = self._rpc._rpc_delete(trade_id)
|
||||||
await self._send_msg(
|
await self._send_msg(
|
||||||
f"`{msg['result_msg']}`\n"
|
f"`{msg['result_msg']}`\n"
|
||||||
'Please make sure to take care of this asset on the exchange manually.'
|
"Please make sure to take care of this asset on the exchange manually."
|
||||||
)
|
)
|
||||||
|
|
||||||
@authorized_only
|
@authorized_only
|
||||||
|
|
|
@ -927,9 +927,11 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||||
(
|
(
|
||||||
p[0],
|
p[0],
|
||||||
p[1],
|
p[1],
|
||||||
CandleType.from_string(p[2])
|
(
|
||||||
if len(p) > 2 and p[2] != ""
|
CandleType.from_string(p[2])
|
||||||
else self.config.get("candle_type_def", CandleType.SPOT),
|
if len(p) > 2 and p[2] != ""
|
||||||
|
else self.config.get("candle_type_def", CandleType.SPOT)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
for p in informative_pairs
|
for p in informative_pairs
|
||||||
]
|
]
|
||||||
|
|
|
@ -693,7 +693,7 @@ def test_process_trade_creation(
|
||||||
|
|
||||||
assert log_has(
|
assert log_has(
|
||||||
f'{"Short" if is_short else "Long"} signal found: about create a new trade for ETH/USDT '
|
f'{"Short" if is_short else "Long"} signal found: about create a new trade for ETH/USDT '
|
||||||
'with stake_amount: 60.0 ...',
|
"with stake_amount: 60.0 ...",
|
||||||
caplog,
|
caplog,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -508,9 +508,9 @@ def test_min_roi_reached3(default_conf, fee) -> None:
|
||||||
0.09,
|
0.09,
|
||||||
0.98,
|
0.98,
|
||||||
ExitType.NONE,
|
ExitType.NONE,
|
||||||
lambda current_profit, **kwargs: -0.1
|
lambda current_profit, **kwargs: (
|
||||||
if current_profit < 0.6
|
-0.1 if current_profit < 0.6 else -(current_profit * 2)
|
||||||
else -(current_profit * 2),
|
),
|
||||||
),
|
),
|
||||||
# Error case - static stoploss in place
|
# Error case - static stoploss in place
|
||||||
(
|
(
|
||||||
|
|
|
@ -47,7 +47,8 @@ def test_strategy_updater_start(user_dir, capsys) -> None:
|
||||||
|
|
||||||
def test_strategy_updater_methods(default_conf, caplog) -> None:
|
def test_strategy_updater_methods(default_conf, caplog) -> None:
|
||||||
instance_strategy_updater = StrategyUpdater()
|
instance_strategy_updater = StrategyUpdater()
|
||||||
modified_code1 = instance_strategy_updater.update_code("""
|
modified_code1 = instance_strategy_updater.update_code(
|
||||||
|
"""
|
||||||
class testClass(IStrategy):
|
class testClass(IStrategy):
|
||||||
def populate_buy_trend():
|
def populate_buy_trend():
|
||||||
pass
|
pass
|
||||||
|
@ -59,7 +60,8 @@ class testClass(IStrategy):
|
||||||
pass
|
pass
|
||||||
def custom_sell():
|
def custom_sell():
|
||||||
pass
|
pass
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
assert "populate_entry_trend" in modified_code1
|
assert "populate_entry_trend" in modified_code1
|
||||||
assert "populate_exit_trend" in modified_code1
|
assert "populate_exit_trend" in modified_code1
|
||||||
|
@ -72,11 +74,13 @@ class testClass(IStrategy):
|
||||||
def test_strategy_updater_params(default_conf, caplog) -> None:
|
def test_strategy_updater_params(default_conf, caplog) -> None:
|
||||||
instance_strategy_updater = StrategyUpdater()
|
instance_strategy_updater = StrategyUpdater()
|
||||||
|
|
||||||
modified_code2 = instance_strategy_updater.update_code("""
|
modified_code2 = instance_strategy_updater.update_code(
|
||||||
|
"""
|
||||||
ticker_interval = '15m'
|
ticker_interval = '15m'
|
||||||
buy_some_parameter = IntParameter(space='buy')
|
buy_some_parameter = IntParameter(space='buy')
|
||||||
sell_some_parameter = IntParameter(space='sell')
|
sell_some_parameter = IntParameter(space='sell')
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
assert "timeframe" in modified_code2
|
assert "timeframe" in modified_code2
|
||||||
# check for not editing hyperopt spaces
|
# check for not editing hyperopt spaces
|
||||||
|
@ -86,13 +90,15 @@ sell_some_parameter = IntParameter(space='sell')
|
||||||
|
|
||||||
def test_strategy_updater_constants(default_conf, caplog) -> None:
|
def test_strategy_updater_constants(default_conf, caplog) -> None:
|
||||||
instance_strategy_updater = StrategyUpdater()
|
instance_strategy_updater = StrategyUpdater()
|
||||||
modified_code3 = instance_strategy_updater.update_code("""
|
modified_code3 = instance_strategy_updater.update_code(
|
||||||
|
"""
|
||||||
use_sell_signal = True
|
use_sell_signal = True
|
||||||
sell_profit_only = True
|
sell_profit_only = True
|
||||||
sell_profit_offset = True
|
sell_profit_offset = True
|
||||||
ignore_roi_if_buy_signal = True
|
ignore_roi_if_buy_signal = True
|
||||||
forcebuy_enable = True
|
forcebuy_enable = True
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
assert "use_exit_signal" in modified_code3
|
assert "use_exit_signal" in modified_code3
|
||||||
assert "exit_profit_only" in modified_code3
|
assert "exit_profit_only" in modified_code3
|
||||||
|
@ -103,10 +109,12 @@ forcebuy_enable = True
|
||||||
|
|
||||||
def test_strategy_updater_df_columns(default_conf, caplog) -> None:
|
def test_strategy_updater_df_columns(default_conf, caplog) -> None:
|
||||||
instance_strategy_updater = StrategyUpdater()
|
instance_strategy_updater = StrategyUpdater()
|
||||||
modified_code = instance_strategy_updater.update_code("""
|
modified_code = instance_strategy_updater.update_code(
|
||||||
|
"""
|
||||||
dataframe.loc[reduce(lambda x, y: x & y, conditions), ["buy", "buy_tag"]] = (1, "buy_signal_1")
|
dataframe.loc[reduce(lambda x, y: x & y, conditions), ["buy", "buy_tag"]] = (1, "buy_signal_1")
|
||||||
dataframe.loc[reduce(lambda x, y: x & y, conditions), 'sell'] = 1
|
dataframe.loc[reduce(lambda x, y: x & y, conditions), 'sell'] = 1
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
assert "enter_long" in modified_code
|
assert "enter_long" in modified_code
|
||||||
assert "exit_long" in modified_code
|
assert "exit_long" in modified_code
|
||||||
|
@ -115,18 +123,21 @@ dataframe.loc[reduce(lambda x, y: x & y, conditions), 'sell'] = 1
|
||||||
|
|
||||||
def test_strategy_updater_method_params(default_conf, caplog) -> None:
|
def test_strategy_updater_method_params(default_conf, caplog) -> None:
|
||||||
instance_strategy_updater = StrategyUpdater()
|
instance_strategy_updater = StrategyUpdater()
|
||||||
modified_code = instance_strategy_updater.update_code("""
|
modified_code = instance_strategy_updater.update_code(
|
||||||
|
"""
|
||||||
def confirm_trade_exit(sell_reason: str):
|
def confirm_trade_exit(sell_reason: str):
|
||||||
nr_orders = trade.nr_of_successful_buys
|
nr_orders = trade.nr_of_successful_buys
|
||||||
pass
|
pass
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
assert "exit_reason" in modified_code
|
assert "exit_reason" in modified_code
|
||||||
assert "nr_orders = trade.nr_of_successful_entries" in modified_code
|
assert "nr_orders = trade.nr_of_successful_entries" in modified_code
|
||||||
|
|
||||||
|
|
||||||
def test_strategy_updater_dicts(default_conf, caplog) -> None:
|
def test_strategy_updater_dicts(default_conf, caplog) -> None:
|
||||||
instance_strategy_updater = StrategyUpdater()
|
instance_strategy_updater = StrategyUpdater()
|
||||||
modified_code = instance_strategy_updater.update_code("""
|
modified_code = instance_strategy_updater.update_code(
|
||||||
|
"""
|
||||||
order_time_in_force = {
|
order_time_in_force = {
|
||||||
'buy': 'gtc',
|
'buy': 'gtc',
|
||||||
'sell': 'ioc'
|
'sell': 'ioc'
|
||||||
|
@ -141,7 +152,8 @@ unfilledtimeout = {
|
||||||
'buy': 1,
|
'buy': 1,
|
||||||
'sell': 2
|
'sell': 2
|
||||||
}
|
}
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
assert "'entry': 'gtc'" in modified_code
|
assert "'entry': 'gtc'" in modified_code
|
||||||
assert "'exit': 'ioc'" in modified_code
|
assert "'exit': 'ioc'" in modified_code
|
||||||
|
@ -153,11 +165,13 @@ unfilledtimeout = {
|
||||||
|
|
||||||
def test_strategy_updater_comparisons(default_conf, caplog) -> None:
|
def test_strategy_updater_comparisons(default_conf, caplog) -> None:
|
||||||
instance_strategy_updater = StrategyUpdater()
|
instance_strategy_updater = StrategyUpdater()
|
||||||
modified_code = instance_strategy_updater.update_code("""
|
modified_code = instance_strategy_updater.update_code(
|
||||||
|
"""
|
||||||
def confirm_trade_exit(sell_reason):
|
def confirm_trade_exit(sell_reason):
|
||||||
if (sell_reason == 'stop_loss'):
|
if (sell_reason == 'stop_loss'):
|
||||||
pass
|
pass
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
assert "exit_reason" in modified_code
|
assert "exit_reason" in modified_code
|
||||||
assert "exit_reason == 'stop_loss'" in modified_code
|
assert "exit_reason == 'stop_loss'" in modified_code
|
||||||
|
|
||||||
|
@ -165,11 +179,13 @@ def confirm_trade_exit(sell_reason):
|
||||||
def test_strategy_updater_strings(default_conf, caplog) -> None:
|
def test_strategy_updater_strings(default_conf, caplog) -> None:
|
||||||
instance_strategy_updater = StrategyUpdater()
|
instance_strategy_updater = StrategyUpdater()
|
||||||
|
|
||||||
modified_code = instance_strategy_updater.update_code("""
|
modified_code = instance_strategy_updater.update_code(
|
||||||
|
"""
|
||||||
sell_reason == 'sell_signal'
|
sell_reason == 'sell_signal'
|
||||||
sell_reason == 'force_sell'
|
sell_reason == 'force_sell'
|
||||||
sell_reason == 'emergency_sell'
|
sell_reason == 'emergency_sell'
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
# those tests currently don't work, next in line.
|
# those tests currently don't work, next in line.
|
||||||
assert "exit_signal" in modified_code
|
assert "exit_signal" in modified_code
|
||||||
|
@ -180,7 +196,8 @@ sell_reason == 'emergency_sell'
|
||||||
|
|
||||||
def test_strategy_updater_comments(default_conf, caplog) -> None:
|
def test_strategy_updater_comments(default_conf, caplog) -> None:
|
||||||
instance_strategy_updater = StrategyUpdater()
|
instance_strategy_updater = StrategyUpdater()
|
||||||
modified_code = instance_strategy_updater.update_code("""
|
modified_code = instance_strategy_updater.update_code(
|
||||||
|
"""
|
||||||
# This is the 1st comment
|
# This is the 1st comment
|
||||||
import talib.abstract as ta
|
import talib.abstract as ta
|
||||||
# This is the 2nd comment
|
# This is the 2nd comment
|
||||||
|
@ -197,7 +214,8 @@ class someStrategy(IStrategy):
|
||||||
|
|
||||||
# This is the 4th comment
|
# This is the 4th comment
|
||||||
stoploss = -0.1
|
stoploss = -0.1
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
assert "This is the 1st comment" in modified_code
|
assert "This is the 1st comment" in modified_code
|
||||||
assert "This is the 2nd comment" in modified_code
|
assert "This is the 2nd comment" in modified_code
|
||||||
|
|
Loading…
Reference in New Issue
Block a user