Merge pull request #3452 from freqtrade/bt_report_sorting
Optimize sorting, rename column when loading backtest data
This commit is contained in:
commit
143197c5d2
@ -63,8 +63,8 @@ class SuperDuperHyperOptLoss(IHyperOptLoss):
|
||||
* 0.25: Avoiding trade loss
|
||||
* 1.0 to total profit, compared to the expected value (`EXPECTED_MAX_PROFIT`) defined above
|
||||
"""
|
||||
total_profit = results.profit_percent.sum()
|
||||
trade_duration = results.trade_duration.mean()
|
||||
total_profit = results['profit_percent'].sum()
|
||||
trade_duration = results['trade_duration'].mean()
|
||||
|
||||
trade_loss = 1 - 0.25 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.8)
|
||||
profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT)
|
||||
|
@ -16,7 +16,7 @@ from freqtrade.persistence import Trade
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# must align with columns in backtest.py
|
||||
BT_DATA_COLUMNS = ["pair", "profitperc", "open_time", "close_time", "index", "duration",
|
||||
BT_DATA_COLUMNS = ["pair", "profit_percent", "open_time", "close_time", "index", "duration",
|
||||
"open_rate", "close_rate", "open_at_end", "sell_reason"]
|
||||
|
||||
|
||||
@ -99,7 +99,7 @@ def load_trades_from_db(db_url: str) -> pd.DataFrame:
|
||||
trades: pd.DataFrame = pd.DataFrame([], columns=BT_DATA_COLUMNS)
|
||||
persistence.init(db_url, clean_open_orders=False)
|
||||
|
||||
columns = ["pair", "open_time", "close_time", "profit", "profitperc",
|
||||
columns = ["pair", "open_time", "close_time", "profit", "profit_percent",
|
||||
"open_rate", "close_rate", "amount", "duration", "sell_reason",
|
||||
"fee_open", "fee_close", "open_rate_requested", "close_rate_requested",
|
||||
"stake_amount", "max_rate", "min_rate", "id", "exchange",
|
||||
@ -190,7 +190,7 @@ def create_cum_profit(df: pd.DataFrame, trades: pd.DataFrame, col_name: str,
|
||||
"""
|
||||
Adds a column `col_name` with the cumulative profit for the given trades array.
|
||||
:param df: DataFrame with date index
|
||||
:param trades: DataFrame containing trades (requires columns close_time and profitperc)
|
||||
:param trades: DataFrame containing trades (requires columns close_time and profit_percent)
|
||||
:param col_name: Column name that will be assigned the results
|
||||
:param timeframe: Timeframe used during the operations
|
||||
:return: Returns df with one additional column, col_name, containing the cumulative profit.
|
||||
@ -201,7 +201,8 @@ def create_cum_profit(df: pd.DataFrame, trades: pd.DataFrame, col_name: str,
|
||||
from freqtrade.exchange import timeframe_to_minutes
|
||||
timeframe_minutes = timeframe_to_minutes(timeframe)
|
||||
# Resample to timeframe to make sure trades match candles
|
||||
_trades_sum = trades.resample(f'{timeframe_minutes}min', on='close_time')[['profitperc']].sum()
|
||||
_trades_sum = trades.resample(f'{timeframe_minutes}min', on='close_time'
|
||||
)[['profit_percent']].sum()
|
||||
df.loc[:, col_name] = _trades_sum.cumsum()
|
||||
# Set first value to 0
|
||||
df.loc[df.iloc[0].name, col_name] = 0
|
||||
@ -211,13 +212,13 @@ def create_cum_profit(df: pd.DataFrame, trades: pd.DataFrame, col_name: str,
|
||||
|
||||
|
||||
def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_time',
|
||||
value_col: str = 'profitperc'
|
||||
value_col: str = 'profit_percent'
|
||||
) -> Tuple[float, pd.Timestamp, pd.Timestamp]:
|
||||
"""
|
||||
Calculate max drawdown and the corresponding close dates
|
||||
:param trades: DataFrame containing trades (requires columns close_time and profitperc)
|
||||
:param trades: DataFrame containing trades (requires columns close_time and profit_percent)
|
||||
:param date_col: Column in DataFrame to use for dates (defaults to 'close_time')
|
||||
:param value_col: Column in DataFrame to use for values (defaults to 'profitperc')
|
||||
:param value_col: Column in DataFrame to use for values (defaults to 'profit_percent')
|
||||
:return: Tuple (float, highdate, lowdate) with absolute max drawdown, high and low time
|
||||
:raise: ValueError if trade-dataframe was found empty.
|
||||
"""
|
||||
|
@ -42,8 +42,8 @@ class DefaultHyperOptLoss(IHyperOptLoss):
|
||||
* 0.25: Avoiding trade loss
|
||||
* 1.0 to total profit, compared to the expected value (`EXPECTED_MAX_PROFIT`) defined above
|
||||
"""
|
||||
total_profit = results.profit_percent.sum()
|
||||
trade_duration = results.trade_duration.mean()
|
||||
total_profit = results['profit_percent'].sum()
|
||||
trade_duration = results['trade_duration'].mean()
|
||||
|
||||
trade_loss = 1 - 0.25 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.8)
|
||||
profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT)
|
||||
|
@ -34,5 +34,5 @@ class OnlyProfitHyperOptLoss(IHyperOptLoss):
|
||||
"""
|
||||
Objective function, returns smaller number for better results.
|
||||
"""
|
||||
total_profit = results.profit_percent.sum()
|
||||
total_profit = results['profit_percent'].sum()
|
||||
return 1 - total_profit / EXPECTED_MAX_PROFIT
|
||||
|
@ -65,25 +65,25 @@ def _generate_result_line(result: DataFrame, max_open_trades: int, first_column:
|
||||
"""
|
||||
return {
|
||||
'key': first_column,
|
||||
'trades': len(result.index),
|
||||
'profit_mean': result.profit_percent.mean(),
|
||||
'profit_mean_pct': result.profit_percent.mean() * 100.0,
|
||||
'profit_sum': result.profit_percent.sum(),
|
||||
'profit_sum_pct': result.profit_percent.sum() * 100.0,
|
||||
'profit_total_abs': result.profit_abs.sum(),
|
||||
'profit_total_pct': result.profit_percent.sum() * 100.0 / max_open_trades,
|
||||
'trades': len(result),
|
||||
'profit_mean': result['profit_percent'].mean(),
|
||||
'profit_mean_pct': result['profit_percent'].mean() * 100.0,
|
||||
'profit_sum': result['profit_percent'].sum(),
|
||||
'profit_sum_pct': result['profit_percent'].sum() * 100.0,
|
||||
'profit_total_abs': result['profit_abs'].sum(),
|
||||
'profit_total_pct': result['profit_percent'].sum() * 100.0 / max_open_trades,
|
||||
'duration_avg': str(timedelta(
|
||||
minutes=round(result.trade_duration.mean()))
|
||||
minutes=round(result['trade_duration'].mean()))
|
||||
) if not result.empty else '0:00',
|
||||
# 'duration_max': str(timedelta(
|
||||
# minutes=round(result.trade_duration.max()))
|
||||
# minutes=round(result['trade_duration'].max()))
|
||||
# ) if not result.empty else '0:00',
|
||||
# 'duration_min': str(timedelta(
|
||||
# minutes=round(result.trade_duration.min()))
|
||||
# minutes=round(result['trade_duration'].min()))
|
||||
# ) if not result.empty else '0:00',
|
||||
'wins': len(result[result.profit_abs > 0]),
|
||||
'draws': len(result[result.profit_abs == 0]),
|
||||
'losses': len(result[result.profit_abs < 0]),
|
||||
'wins': len(result[result['profit_abs'] > 0]),
|
||||
'draws': len(result[result['profit_abs'] == 0]),
|
||||
'losses': len(result[result['profit_abs'] < 0]),
|
||||
}
|
||||
|
||||
|
||||
@ -102,8 +102,8 @@ def generate_pair_metrics(data: Dict[str, Dict], stake_currency: str, max_open_t
|
||||
tabular_data = []
|
||||
|
||||
for pair in data:
|
||||
result = results[results.pair == pair]
|
||||
if skip_nan and result.profit_abs.isnull().all():
|
||||
result = results[results['pair'] == pair]
|
||||
if skip_nan and result['profit_abs'].isnull().all():
|
||||
continue
|
||||
|
||||
tabular_data.append(_generate_result_line(result, max_open_trades, pair))
|
||||
@ -113,25 +113,6 @@ def generate_pair_metrics(data: Dict[str, Dict], stake_currency: str, max_open_t
|
||||
return tabular_data
|
||||
|
||||
|
||||
def generate_text_table(pair_results: List[Dict[str, Any]], stake_currency: str) -> str:
|
||||
"""
|
||||
Generates and returns a text table for the given backtest data and the results dataframe
|
||||
:param pair_results: List of Dictionaries - one entry per pair + final TOTAL row
|
||||
:param stake_currency: stake-currency - used to correctly name headers
|
||||
:return: pretty printed table with tabulate as string
|
||||
"""
|
||||
|
||||
headers = _get_line_header('Pair', stake_currency)
|
||||
floatfmt = _get_line_floatfmt()
|
||||
output = [[
|
||||
t['key'], t['trades'], t['profit_mean_pct'], t['profit_sum_pct'], t['profit_total_abs'],
|
||||
t['profit_total_pct'], t['duration_avg'], t['wins'], t['draws'], t['losses']
|
||||
] for t in pair_results]
|
||||
# Ignore type as floatfmt does allow tuples but mypy does not know that
|
||||
return tabulate(output, headers=headers,
|
||||
floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore
|
||||
|
||||
|
||||
def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List[Dict]:
|
||||
"""
|
||||
Generate small table outlining Backtest results
|
||||
@ -166,33 +147,6 @@ def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List
|
||||
return tabular_data
|
||||
|
||||
|
||||
def generate_text_table_sell_reason(sell_reason_stats: List[Dict[str, Any]],
|
||||
stake_currency: str) -> str:
|
||||
"""
|
||||
Generate small table outlining Backtest results
|
||||
:param sell_reason_stats: Sell reason metrics
|
||||
:param stake_currency: Stakecurrency used
|
||||
:return: pretty printed table with tabulate as string
|
||||
"""
|
||||
headers = [
|
||||
'Sell Reason',
|
||||
'Sells',
|
||||
'Wins',
|
||||
'Draws',
|
||||
'Losses',
|
||||
'Avg Profit %',
|
||||
'Cum Profit %',
|
||||
f'Tot Profit {stake_currency}',
|
||||
'Tot Profit %',
|
||||
]
|
||||
|
||||
output = [[
|
||||
t['sell_reason'], t['trades'], t['wins'], t['draws'], t['losses'],
|
||||
t['profit_mean_pct'], t['profit_sum_pct'], t['profit_total_abs'], t['profit_pct_total'],
|
||||
] for t in sell_reason_stats]
|
||||
return tabulate(output, headers=headers, tablefmt="orgtbl", stralign="right")
|
||||
|
||||
|
||||
def generate_strategy_metrics(stake_currency: str, max_open_trades: int,
|
||||
all_results: Dict) -> List[Dict]:
|
||||
"""
|
||||
@ -209,26 +163,6 @@ def generate_strategy_metrics(stake_currency: str, max_open_trades: int,
|
||||
return tabular_data
|
||||
|
||||
|
||||
def generate_text_table_strategy(strategy_results, stake_currency: str) -> str:
|
||||
"""
|
||||
Generate summary table per strategy
|
||||
:param stake_currency: stake-currency - used to correctly name headers
|
||||
:param max_open_trades: Maximum allowed open trades used for backtest
|
||||
:param all_results: Dict of <Strategyname: BacktestResult> containing results for all strategies
|
||||
:return: pretty printed table with tabulate as string
|
||||
"""
|
||||
floatfmt = _get_line_floatfmt()
|
||||
headers = _get_line_header('Strategy', stake_currency)
|
||||
|
||||
output = [[
|
||||
t['key'], t['trades'], t['profit_mean_pct'], t['profit_sum_pct'], t['profit_total_abs'],
|
||||
t['profit_total_pct'], t['duration_avg'], t['wins'], t['draws'], t['losses']
|
||||
] for t in strategy_results]
|
||||
# Ignore type as floatfmt does allow tuples but mypy does not know that
|
||||
return tabulate(output, headers=headers,
|
||||
floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore
|
||||
|
||||
|
||||
def generate_edge_table(results: dict) -> str:
|
||||
|
||||
floatfmt = ('s', '.10g', '.2f', '.2f', '.2f', '.2f', 'd', 'd', 'd')
|
||||
@ -256,7 +190,14 @@ def generate_edge_table(results: dict) -> str:
|
||||
|
||||
|
||||
def generate_backtest_stats(config: Dict, btdata: Dict[str, DataFrame],
|
||||
all_results: Dict[str, DataFrame]):
|
||||
all_results: Dict[str, DataFrame]) -> Dict[str, Any]:
|
||||
"""
|
||||
:param config: Configuration object used for backtest
|
||||
:param btdata: Backtest data
|
||||
:param all_results: backtest result - dictionary with { Strategy: results}.
|
||||
:return:
|
||||
Dictionary containing results per strategy and a stratgy summary.
|
||||
"""
|
||||
stake_currency = config['stake_currency']
|
||||
max_open_trades = config['max_open_trades']
|
||||
result: Dict[str, Any] = {'strategy': {}}
|
||||
@ -288,6 +229,75 @@ def generate_backtest_stats(config: Dict, btdata: Dict[str, DataFrame],
|
||||
return result
|
||||
|
||||
|
||||
###
|
||||
# Start output section
|
||||
###
|
||||
|
||||
def text_table_bt_results(pair_results: List[Dict[str, Any]], stake_currency: str) -> str:
|
||||
"""
|
||||
Generates and returns a text table for the given backtest data and the results dataframe
|
||||
:param pair_results: List of Dictionaries - one entry per pair + final TOTAL row
|
||||
:param stake_currency: stake-currency - used to correctly name headers
|
||||
:return: pretty printed table with tabulate as string
|
||||
"""
|
||||
|
||||
headers = _get_line_header('Pair', stake_currency)
|
||||
floatfmt = _get_line_floatfmt()
|
||||
output = [[
|
||||
t['key'], t['trades'], t['profit_mean_pct'], t['profit_sum_pct'], t['profit_total_abs'],
|
||||
t['profit_total_pct'], t['duration_avg'], t['wins'], t['draws'], t['losses']
|
||||
] for t in pair_results]
|
||||
# Ignore type as floatfmt does allow tuples but mypy does not know that
|
||||
return tabulate(output, headers=headers,
|
||||
floatfmt=floatfmt, tablefmt="orgtbl", stralign="right")
|
||||
|
||||
|
||||
def text_table_sell_reason(sell_reason_stats: List[Dict[str, Any]], stake_currency: str) -> str:
|
||||
"""
|
||||
Generate small table outlining Backtest results
|
||||
:param sell_reason_stats: Sell reason metrics
|
||||
:param stake_currency: Stakecurrency used
|
||||
:return: pretty printed table with tabulate as string
|
||||
"""
|
||||
headers = [
|
||||
'Sell Reason',
|
||||
'Sells',
|
||||
'Wins',
|
||||
'Draws',
|
||||
'Losses',
|
||||
'Avg Profit %',
|
||||
'Cum Profit %',
|
||||
f'Tot Profit {stake_currency}',
|
||||
'Tot Profit %',
|
||||
]
|
||||
|
||||
output = [[
|
||||
t['sell_reason'], t['trades'], t['wins'], t['draws'], t['losses'],
|
||||
t['profit_mean_pct'], t['profit_sum_pct'], t['profit_total_abs'], t['profit_pct_total'],
|
||||
] for t in sell_reason_stats]
|
||||
return tabulate(output, headers=headers, tablefmt="orgtbl", stralign="right")
|
||||
|
||||
|
||||
def text_table_strategy(strategy_results, stake_currency: str) -> str:
|
||||
"""
|
||||
Generate summary table per strategy
|
||||
:param stake_currency: stake-currency - used to correctly name headers
|
||||
:param max_open_trades: Maximum allowed open trades used for backtest
|
||||
:param all_results: Dict of <Strategyname: BacktestResult> containing results for all strategies
|
||||
:return: pretty printed table with tabulate as string
|
||||
"""
|
||||
floatfmt = _get_line_floatfmt()
|
||||
headers = _get_line_header('Strategy', stake_currency)
|
||||
|
||||
output = [[
|
||||
t['key'], t['trades'], t['profit_mean_pct'], t['profit_sum_pct'], t['profit_total_abs'],
|
||||
t['profit_total_pct'], t['duration_avg'], t['wins'], t['draws'], t['losses']
|
||||
] for t in strategy_results]
|
||||
# Ignore type as floatfmt does allow tuples but mypy does not know that
|
||||
return tabulate(output, headers=headers,
|
||||
floatfmt=floatfmt, tablefmt="orgtbl", stralign="right")
|
||||
|
||||
|
||||
def show_backtest_results(config: Dict, backtest_stats: Dict):
|
||||
stake_currency = config['stake_currency']
|
||||
|
||||
@ -295,19 +305,18 @@ def show_backtest_results(config: Dict, backtest_stats: Dict):
|
||||
|
||||
# Print results
|
||||
print(f"Result for strategy {strategy}")
|
||||
table = generate_text_table(results['results_per_pair'], stake_currency=stake_currency)
|
||||
table = text_table_bt_results(results['results_per_pair'], stake_currency=stake_currency)
|
||||
if isinstance(table, str):
|
||||
print(' BACKTESTING REPORT '.center(len(table.splitlines()[0]), '='))
|
||||
print(table)
|
||||
|
||||
table = generate_text_table_sell_reason(sell_reason_stats=results['sell_reason_summary'],
|
||||
stake_currency=stake_currency,
|
||||
)
|
||||
table = text_table_sell_reason(sell_reason_stats=results['sell_reason_summary'],
|
||||
stake_currency=stake_currency)
|
||||
if isinstance(table, str):
|
||||
print(' SELL REASON STATS '.center(len(table.splitlines()[0]), '='))
|
||||
print(table)
|
||||
|
||||
table = generate_text_table(results['left_open_trades'], stake_currency=stake_currency)
|
||||
table = text_table_bt_results(results['left_open_trades'], stake_currency=stake_currency)
|
||||
if isinstance(table, str):
|
||||
print(' LEFT OPEN TRADES REPORT '.center(len(table.splitlines()[0]), '='))
|
||||
print(table)
|
||||
@ -318,7 +327,7 @@ def show_backtest_results(config: Dict, backtest_stats: Dict):
|
||||
if len(backtest_stats['strategy']) > 1:
|
||||
# Print Strategy summary table
|
||||
|
||||
table = generate_text_table_strategy(backtest_stats['strategy_comparison'], stake_currency)
|
||||
table = text_table_strategy(backtest_stats['strategy_comparison'], stake_currency)
|
||||
print(' STRATEGY SUMMARY '.center(len(table.splitlines()[0]), '='))
|
||||
print(table)
|
||||
print('=' * len(table.splitlines()[0]))
|
||||
|
@ -162,7 +162,7 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
|
||||
# Trades can be empty
|
||||
if trades is not None and len(trades) > 0:
|
||||
# Create description for sell summarizing the trade
|
||||
trades['desc'] = trades.apply(lambda row: f"{round(row['profitperc'] * 100, 1)}%, "
|
||||
trades['desc'] = trades.apply(lambda row: f"{round(row['profit_percent'] * 100, 1)}%, "
|
||||
f"{row['sell_reason']}, {row['duration']} min",
|
||||
axis=1)
|
||||
trade_buys = go.Scatter(
|
||||
@ -181,9 +181,9 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
|
||||
)
|
||||
|
||||
trade_sells = go.Scatter(
|
||||
x=trades.loc[trades['profitperc'] > 0, "close_time"],
|
||||
y=trades.loc[trades['profitperc'] > 0, "close_rate"],
|
||||
text=trades.loc[trades['profitperc'] > 0, "desc"],
|
||||
x=trades.loc[trades['profit_percent'] > 0, "close_time"],
|
||||
y=trades.loc[trades['profit_percent'] > 0, "close_rate"],
|
||||
text=trades.loc[trades['profit_percent'] > 0, "desc"],
|
||||
mode='markers',
|
||||
name='Sell - Profit',
|
||||
marker=dict(
|
||||
@ -194,9 +194,9 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
|
||||
)
|
||||
)
|
||||
trade_sells_loss = go.Scatter(
|
||||
x=trades.loc[trades['profitperc'] <= 0, "close_time"],
|
||||
y=trades.loc[trades['profitperc'] <= 0, "close_rate"],
|
||||
text=trades.loc[trades['profitperc'] <= 0, "desc"],
|
||||
x=trades.loc[trades['profit_percent'] <= 0, "close_time"],
|
||||
y=trades.loc[trades['profit_percent'] <= 0, "close_rate"],
|
||||
text=trades.loc[trades['profit_percent'] <= 0, "desc"],
|
||||
mode='markers',
|
||||
name='Sell - Loss',
|
||||
marker=dict(
|
||||
|
@ -47,7 +47,7 @@ def test_load_trades_from_db(default_conf, fee, mocker):
|
||||
assert isinstance(trades, DataFrame)
|
||||
assert "pair" in trades.columns
|
||||
assert "open_time" in trades.columns
|
||||
assert "profitperc" in trades.columns
|
||||
assert "profit_percent" in trades.columns
|
||||
|
||||
for col in BT_DATA_COLUMNS:
|
||||
if col not in ['index', 'open_at_end']:
|
||||
|
@ -659,17 +659,17 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
|
||||
mocker.patch('freqtrade.pairlist.pairlistmanager.PairListManager.whitelist',
|
||||
PropertyMock(return_value=['UNITTEST/BTC']))
|
||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock)
|
||||
gen_table_mock = MagicMock()
|
||||
text_table_mock = MagicMock()
|
||||
sell_reason_mock = MagicMock()
|
||||
gen_strattable_mock = MagicMock()
|
||||
gen_strat_summary = MagicMock()
|
||||
strattable_mock = MagicMock()
|
||||
strat_summary = MagicMock()
|
||||
|
||||
mocker.patch.multiple('freqtrade.optimize.optimize_reports',
|
||||
generate_text_table=gen_table_mock,
|
||||
generate_text_table_strategy=gen_strattable_mock,
|
||||
text_table_bt_results=text_table_mock,
|
||||
text_table_strategy=strattable_mock,
|
||||
generate_pair_metrics=MagicMock(),
|
||||
generate_sell_reason_stats=sell_reason_mock,
|
||||
generate_strategy_metrics=gen_strat_summary,
|
||||
generate_strategy_metrics=strat_summary,
|
||||
)
|
||||
patched_configuration_load_config_file(mocker, default_conf)
|
||||
|
||||
@ -690,10 +690,10 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
|
||||
start_backtesting(args)
|
||||
# 2 backtests, 4 tables
|
||||
assert backtestmock.call_count == 2
|
||||
assert gen_table_mock.call_count == 4
|
||||
assert gen_strattable_mock.call_count == 1
|
||||
assert text_table_mock.call_count == 4
|
||||
assert strattable_mock.call_count == 1
|
||||
assert sell_reason_mock.call_count == 2
|
||||
assert gen_strat_summary.call_count == 1
|
||||
assert strat_summary.call_count == 1
|
||||
|
||||
# check the logs, that will contain the backtest result
|
||||
exists = [
|
||||
|
@ -7,13 +7,13 @@ from arrow import Arrow
|
||||
from freqtrade.edge import PairInfo
|
||||
from freqtrade.optimize.optimize_reports import (
|
||||
generate_pair_metrics, generate_edge_table, generate_sell_reason_stats,
|
||||
generate_text_table, generate_text_table_sell_reason, generate_strategy_metrics,
|
||||
generate_text_table_strategy, store_backtest_result)
|
||||
text_table_bt_results, text_table_sell_reason, generate_strategy_metrics,
|
||||
text_table_strategy, store_backtest_result)
|
||||
from freqtrade.strategy.interface import SellType
|
||||
from tests.conftest import patch_exchange
|
||||
|
||||
|
||||
def test_generate_text_table(default_conf, mocker):
|
||||
def test_text_table_bt_results(default_conf, mocker):
|
||||
|
||||
results = pd.DataFrame(
|
||||
{
|
||||
@ -40,8 +40,7 @@ def test_generate_text_table(default_conf, mocker):
|
||||
|
||||
pair_results = generate_pair_metrics(data={'ETH/BTC': {}}, stake_currency='BTC',
|
||||
max_open_trades=2, results=results)
|
||||
assert generate_text_table(pair_results,
|
||||
stake_currency='BTC') == result_str
|
||||
assert text_table_bt_results(pair_results, stake_currency='BTC') == result_str
|
||||
|
||||
|
||||
def test_generate_pair_metrics(default_conf, mocker):
|
||||
@ -69,7 +68,7 @@ def test_generate_pair_metrics(default_conf, mocker):
|
||||
pytest.approx(pair_results[-1]['profit_sum_pct']) == pair_results[-1]['profit_sum'] * 100)
|
||||
|
||||
|
||||
def test_generate_text_table_sell_reason(default_conf):
|
||||
def test_text_table_sell_reason(default_conf):
|
||||
|
||||
results = pd.DataFrame(
|
||||
{
|
||||
@ -97,8 +96,8 @@ def test_generate_text_table_sell_reason(default_conf):
|
||||
|
||||
sell_reason_stats = generate_sell_reason_stats(max_open_trades=2,
|
||||
results=results)
|
||||
assert generate_text_table_sell_reason(sell_reason_stats=sell_reason_stats,
|
||||
stake_currency='BTC') == result_str
|
||||
assert text_table_sell_reason(sell_reason_stats=sell_reason_stats,
|
||||
stake_currency='BTC') == result_str
|
||||
|
||||
|
||||
def test_generate_sell_reason_stats(default_conf):
|
||||
@ -136,7 +135,7 @@ def test_generate_sell_reason_stats(default_conf):
|
||||
assert stop_result['profit_mean_pct'] == round(stop_result['profit_mean'] * 100, 2)
|
||||
|
||||
|
||||
def test_generate_text_table_strategy(default_conf, mocker):
|
||||
def test_text_table_strategy(default_conf, mocker):
|
||||
results = {}
|
||||
results['TestStrategy1'] = pd.DataFrame(
|
||||
{
|
||||
@ -178,7 +177,7 @@ def test_generate_text_table_strategy(default_conf, mocker):
|
||||
max_open_trades=2,
|
||||
all_results=results)
|
||||
|
||||
assert generate_text_table_strategy(strategy_results, 'BTC') == result_str
|
||||
assert text_table_strategy(strategy_results, 'BTC') == result_str
|
||||
|
||||
|
||||
def test_generate_edge_table(edge_conf, mocker):
|
||||
|
@ -298,7 +298,7 @@ def test_calc_profit(limit_buy_order, limit_sell_order, fee):
|
||||
fee_close=fee.return_value,
|
||||
exchange='bittrex',
|
||||
)
|
||||
trade.open_order_id = 'profit_percent'
|
||||
trade.open_order_id = 'something'
|
||||
trade.update(limit_buy_order) # Buy @ 0.00001099
|
||||
|
||||
# Custom closing rate and regular fee rate
|
||||
@ -332,7 +332,7 @@ def test_calc_profit_ratio(limit_buy_order, limit_sell_order, fee):
|
||||
fee_close=fee.return_value,
|
||||
exchange='bittrex',
|
||||
)
|
||||
trade.open_order_id = 'profit_percent'
|
||||
trade.open_order_id = 'something'
|
||||
trade.update(limit_buy_order) # Buy @ 0.00001099
|
||||
|
||||
# Get percent of profit with a custom rate (Higher than open rate)
|
||||
|
@ -124,7 +124,7 @@ def test_plot_trades(testdatadir, caplog):
|
||||
trade_sell = find_trace_in_fig_data(figure.data, 'Sell - Profit')
|
||||
assert isinstance(trade_sell, go.Scatter)
|
||||
assert trade_sell.yaxis == 'y'
|
||||
assert len(trades.loc[trades['profitperc'] > 0]) == len(trade_sell.x)
|
||||
assert len(trades.loc[trades['profit_percent'] > 0]) == len(trade_sell.x)
|
||||
assert trade_sell.marker.color == 'green'
|
||||
assert trade_sell.marker.symbol == 'square-open'
|
||||
assert trade_sell.text[0] == '4.0%, roi, 15 min'
|
||||
@ -132,7 +132,7 @@ def test_plot_trades(testdatadir, caplog):
|
||||
trade_sell_loss = find_trace_in_fig_data(figure.data, 'Sell - Loss')
|
||||
assert isinstance(trade_sell_loss, go.Scatter)
|
||||
assert trade_sell_loss.yaxis == 'y'
|
||||
assert len(trades.loc[trades['profitperc'] <= 0]) == len(trade_sell_loss.x)
|
||||
assert len(trades.loc[trades['profit_percent'] <= 0]) == len(trade_sell_loss.x)
|
||||
assert trade_sell_loss.marker.color == 'red'
|
||||
assert trade_sell_loss.marker.symbol == 'square-open'
|
||||
assert trade_sell_loss.text[5] == '-10.4%, stop_loss, 720 min'
|
||||
|
Loading…
Reference in New Issue
Block a user