Don't use profit_percent for backtesting results anymore

This commit is contained in:
Matthias 2021-01-23 13:02:48 +01:00
parent 48977493bb
commit 8ee264bc59
17 changed files with 78 additions and 101 deletions

View File

@ -3,11 +3,12 @@
""" """
This module contains the backtesting logic This module contains the backtesting logic
""" """
from freqtrade.data.btanalysis import BT_DATA_COLUMNS
import logging import logging
from collections import defaultdict from collections import defaultdict
from copy import deepcopy from copy import deepcopy
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from typing import Any, Dict, List, NamedTuple, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple
from pandas import DataFrame, to_datetime from pandas import DataFrame, to_datetime
@ -41,25 +42,6 @@ LOW_IDX = 5
HIGH_IDX = 6 HIGH_IDX = 6
class BacktestResult(NamedTuple):
"""
NamedTuple Defining BacktestResults inputs.
"""
pair: str
profit_percent: float
profit_abs: float
open_date: datetime
open_rate: float
open_fee: float
close_date: datetime
close_rate: float
close_fee: float
amount: float
trade_duration: float
open_at_end: bool
sell_reason: SellType
class Backtesting: class Backtesting:
""" """
Backtesting class, this class contains all the logic to run a backtest Backtesting class, this class contains all the logic to run a backtest
@ -403,12 +385,7 @@ class Backtesting:
trades += self.handle_left_open(open_trades, data=data) trades += self.handle_left_open(open_trades, data=data)
cols = ['pair', 'stake_amount', 'amount', 'open_date', 'close_date', df = DataFrame.from_records([t.to_json() for t in trades], columns=BT_DATA_COLUMNS)
'open_fee', 'close_fee', 'trade_duration',
'profit_ratio', 'profit_percent', 'profit_abs', 'sell_reason',
'initial_stop_loss_abs', 'initial_stop_loss_ratio' 'stop_loss', 'stop_loss_ratio',
'min_rate', 'max_rate', 'is_open', ]
df = DataFrame.from_records([t.to_json() for t in trades], columns=cols)
if len(df) > 0: if len(df) > 0:
df.loc[:, 'close_date'] = to_datetime(df['close_date'], utc=True) df.loc[:, 'close_date'] = to_datetime(df['close_date'], utc=True)
df.loc[:, 'open_date'] = to_datetime(df['open_date'], utc=True) df.loc[:, 'open_date'] = to_datetime(df['open_date'], utc=True)

View File

@ -42,7 +42,7 @@ class ShortTradeDurHyperOptLoss(IHyperOptLoss):
* 0.25: Avoiding trade loss * 0.25: Avoiding trade loss
* 1.0 to total profit, compared to the expected value (`EXPECTED_MAX_PROFIT`) defined above * 1.0 to total profit, compared to the expected value (`EXPECTED_MAX_PROFIT`) defined above
""" """
total_profit = results['profit_percent'].sum() total_profit = results['profit_ratio'].sum()
trade_duration = results['trade_duration'].mean() trade_duration = results['trade_duration'].mean()
trade_loss = 1 - 0.25 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.8) trade_loss = 1 - 0.25 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.8)

View File

@ -574,19 +574,19 @@ class Hyperopt:
} }
def _calculate_results_metrics(self, backtesting_results: DataFrame) -> Dict: def _calculate_results_metrics(self, backtesting_results: DataFrame) -> Dict:
wins = len(backtesting_results[backtesting_results.profit_percent > 0]) wins = len(backtesting_results[backtesting_results['profit_ratio'] > 0])
draws = len(backtesting_results[backtesting_results.profit_percent == 0]) draws = len(backtesting_results[backtesting_results['profit_ratio'] == 0])
losses = len(backtesting_results[backtesting_results.profit_percent < 0]) losses = len(backtesting_results[backtesting_results['profit_ratio'] < 0])
return { return {
'trade_count': len(backtesting_results.index), 'trade_count': len(backtesting_results.index),
'wins': wins, 'wins': wins,
'draws': draws, 'draws': draws,
'losses': losses, 'losses': losses,
'winsdrawslosses': f"{wins:>4} {draws:>4} {losses:>4}", 'winsdrawslosses': f"{wins:>4} {draws:>4} {losses:>4}",
'avg_profit': backtesting_results.profit_percent.mean() * 100.0, 'avg_profit': backtesting_results['profit_ratio'].mean() * 100.0,
'median_profit': backtesting_results.profit_percent.median() * 100.0, 'median_profit': backtesting_results['profit_ratio'].median() * 100.0,
'total_profit': backtesting_results.profit_abs.sum(), 'total_profit': backtesting_results.profit_abs.sum(),
'profit': backtesting_results.profit_percent.sum() * 100.0, 'profit': backtesting_results['profit_ratio'].sum() * 100.0,
'duration': backtesting_results.trade_duration.mean(), 'duration': backtesting_results.trade_duration.mean(),
} }

View File

@ -34,5 +34,5 @@ class OnlyProfitHyperOptLoss(IHyperOptLoss):
""" """
Objective function, returns smaller number for better results. Objective function, returns smaller number for better results.
""" """
total_profit = results['profit_percent'].sum() total_profit = results['profit_ratio'].sum()
return 1 - total_profit / EXPECTED_MAX_PROFIT return 1 - total_profit / EXPECTED_MAX_PROFIT

View File

@ -28,7 +28,7 @@ class SharpeHyperOptLoss(IHyperOptLoss):
Uses Sharpe Ratio calculation. Uses Sharpe Ratio calculation.
""" """
total_profit = results["profit_percent"] total_profit = results["profit_ratio"]
days_period = (max_date - min_date).days days_period = (max_date - min_date).days
# adding slippage of 0.1% per trade # adding slippage of 0.1% per trade

View File

@ -34,9 +34,9 @@ class SharpeHyperOptLossDaily(IHyperOptLoss):
annual_risk_free_rate = 0.0 annual_risk_free_rate = 0.0
risk_free_rate = annual_risk_free_rate / days_in_year risk_free_rate = annual_risk_free_rate / days_in_year
# apply slippage per trade to profit_percent # apply slippage per trade to profit_ratio
results.loc[:, 'profit_percent_after_slippage'] = \ results.loc[:, 'profit_ratio_after_slippage'] = \
results['profit_percent'] - slippage_per_trade_ratio results['profit_ratio'] - slippage_per_trade_ratio
# create the index within the min_date and end max_date # create the index within the min_date and end max_date
t_index = date_range(start=min_date, end=max_date, freq=resample_freq, t_index = date_range(start=min_date, end=max_date, freq=resample_freq,
@ -44,10 +44,10 @@ class SharpeHyperOptLossDaily(IHyperOptLoss):
sum_daily = ( sum_daily = (
results.resample(resample_freq, on='close_date').agg( results.resample(resample_freq, on='close_date').agg(
{"profit_percent_after_slippage": sum}).reindex(t_index).fillna(0) {"profit_ratio_after_slippage": sum}).reindex(t_index).fillna(0)
) )
total_profit = sum_daily["profit_percent_after_slippage"] - risk_free_rate total_profit = sum_daily["profit_ratio_after_slippage"] - risk_free_rate
expected_returns_mean = total_profit.mean() expected_returns_mean = total_profit.mean()
up_stdev = total_profit.std() up_stdev = total_profit.std()

View File

@ -28,7 +28,7 @@ class SortinoHyperOptLoss(IHyperOptLoss):
Uses Sortino Ratio calculation. Uses Sortino Ratio calculation.
""" """
total_profit = results["profit_percent"] total_profit = results["profit_ratio"]
days_period = (max_date - min_date).days days_period = (max_date - min_date).days
# adding slippage of 0.1% per trade # adding slippage of 0.1% per trade
@ -36,7 +36,7 @@ class SortinoHyperOptLoss(IHyperOptLoss):
expected_returns_mean = total_profit.sum() / days_period expected_returns_mean = total_profit.sum() / days_period
results['downside_returns'] = 0 results['downside_returns'] = 0
results.loc[total_profit < 0, 'downside_returns'] = results['profit_percent'] results.loc[total_profit < 0, 'downside_returns'] = results['profit_ratio']
down_stdev = np.std(results['downside_returns']) down_stdev = np.std(results['downside_returns'])
if down_stdev != 0: if down_stdev != 0:

View File

@ -36,9 +36,9 @@ class SortinoHyperOptLossDaily(IHyperOptLoss):
days_in_year = 365 days_in_year = 365
minimum_acceptable_return = 0.0 minimum_acceptable_return = 0.0
# apply slippage per trade to profit_percent # apply slippage per trade to profit_ratio
results.loc[:, 'profit_percent_after_slippage'] = \ results.loc[:, 'profit_ratio_after_slippage'] = \
results['profit_percent'] - slippage_per_trade_ratio results['profit_ratio'] - slippage_per_trade_ratio
# create the index within the min_date and end max_date # create the index within the min_date and end max_date
t_index = date_range(start=min_date, end=max_date, freq=resample_freq, t_index = date_range(start=min_date, end=max_date, freq=resample_freq,
@ -46,17 +46,17 @@ class SortinoHyperOptLossDaily(IHyperOptLoss):
sum_daily = ( sum_daily = (
results.resample(resample_freq, on='close_date').agg( results.resample(resample_freq, on='close_date').agg(
{"profit_percent_after_slippage": sum}).reindex(t_index).fillna(0) {"profit_ratio_after_slippage": sum}).reindex(t_index).fillna(0)
) )
total_profit = sum_daily["profit_percent_after_slippage"] - minimum_acceptable_return total_profit = sum_daily["profit_ratio_after_slippage"] - minimum_acceptable_return
expected_returns_mean = total_profit.mean() expected_returns_mean = total_profit.mean()
sum_daily['downside_returns'] = 0 sum_daily['downside_returns'] = 0
sum_daily.loc[total_profit < 0, 'downside_returns'] = total_profit sum_daily.loc[total_profit < 0, 'downside_returns'] = total_profit
total_downside = sum_daily['downside_returns'] total_downside = sum_daily['downside_returns']
# Here total_downside contains min(0, P - MAR) values, # Here total_downside contains min(0, P - MAR) values,
# where P = sum_daily["profit_percent_after_slippage"] # where P = sum_daily["profit_ratio_after_slippage"]
down_stdev = math.sqrt((total_downside**2).sum() / len(total_downside)) down_stdev = math.sqrt((total_downside**2).sum() / len(total_downside))
if down_stdev != 0: if down_stdev != 0:

View File

@ -58,14 +58,14 @@ def _generate_result_line(result: DataFrame, max_open_trades: int, first_column:
""" """
Generate one result dict, with "first_column" as key. Generate one result dict, with "first_column" as key.
""" """
profit_sum = result['profit_percent'].sum() profit_sum = result['profit_ratio'].sum()
profit_total = profit_sum / max_open_trades profit_total = profit_sum / max_open_trades
return { return {
'key': first_column, 'key': first_column,
'trades': len(result), 'trades': len(result),
'profit_mean': result['profit_percent'].mean() if len(result) > 0 else 0.0, 'profit_mean': result['profit_ratio'].mean() if len(result) > 0 else 0.0,
'profit_mean_pct': result['profit_percent'].mean() * 100.0 if len(result) > 0 else 0.0, 'profit_mean_pct': result['profit_ratio'].mean() * 100.0 if len(result) > 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(),
@ -124,8 +124,8 @@ def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List
for reason, count in results['sell_reason'].value_counts().iteritems(): for reason, count in results['sell_reason'].value_counts().iteritems():
result = results.loc[results['sell_reason'] == reason] result = results.loc[results['sell_reason'] == reason]
profit_mean = result['profit_percent'].mean() profit_mean = result['profit_ratio'].mean()
profit_sum = result['profit_percent'].sum() profit_sum = result['profit_ratio'].sum()
profit_total = profit_sum / max_open_trades profit_total = profit_sum / max_open_trades
tabular_data.append( tabular_data.append(
@ -150,7 +150,7 @@ def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List
def generate_strategy_metrics(all_results: Dict) -> List[Dict]: def generate_strategy_metrics(all_results: Dict) -> List[Dict]:
""" """
Generate summary per strategy Generate summary per strategy
:param all_results: Dict of <Strategyname: BacktestResult> containing results for all strategies :param all_results: Dict of <Strategyname: DataFrame> containing results for all strategies
:return: List of Dicts containing the metrics per Strategy :return: List of Dicts containing the metrics per Strategy
""" """
@ -199,15 +199,15 @@ def generate_daily_stats(results: DataFrame) -> Dict[str, Any]:
'winner_holding_avg': timedelta(), 'winner_holding_avg': timedelta(),
'loser_holding_avg': timedelta(), 'loser_holding_avg': timedelta(),
} }
daily_profit = results.resample('1d', on='close_date')['profit_percent'].sum() daily_profit = results.resample('1d', on='close_date')['profit_ratio'].sum()
worst = min(daily_profit) worst = min(daily_profit)
best = max(daily_profit) best = max(daily_profit)
winning_days = sum(daily_profit > 0) winning_days = sum(daily_profit > 0)
draw_days = sum(daily_profit == 0) draw_days = sum(daily_profit == 0)
losing_days = sum(daily_profit < 0) losing_days = sum(daily_profit < 0)
winning_trades = results.loc[results['profit_percent'] > 0] winning_trades = results.loc[results['profit_ratio'] > 0]
losing_trades = results.loc[results['profit_percent'] < 0] losing_trades = results.loc[results['profit_ratio'] < 0]
return { return {
'backtest_best_day': best, 'backtest_best_day': best,
@ -273,8 +273,8 @@ def generate_backtest_stats(btdata: Dict[str, DataFrame],
'sell_reason_summary': sell_reason_stats, 'sell_reason_summary': sell_reason_stats,
'left_open_trades': left_open_results, 'left_open_trades': left_open_results,
'total_trades': len(results), 'total_trades': len(results),
'profit_mean': results['profit_percent'].mean() if len(results) > 0 else 0, 'profit_mean': results['profit_ratio'].mean() if len(results) > 0 else 0,
'profit_total': results['profit_percent'].sum(), 'profit_total': results['profit_ratio'].sum(),
'profit_total_abs': results['profit_abs'].sum(), 'profit_total_abs': results['profit_abs'].sum(),
'backtest_start': min_date.datetime, 'backtest_start': min_date.datetime,
'backtest_start_ts': min_date.int_timestamp * 1000, 'backtest_start_ts': min_date.int_timestamp * 1000,
@ -314,7 +314,7 @@ def generate_backtest_stats(btdata: Dict[str, DataFrame],
try: try:
max_drawdown, drawdown_start, drawdown_end = calculate_max_drawdown( max_drawdown, drawdown_start, drawdown_end = calculate_max_drawdown(
results, value_col='profit_percent') results, value_col='profit_ratio')
strat_stats.update({ strat_stats.update({
'max_drawdown': max_drawdown, 'max_drawdown': max_drawdown,
'drawdown_start': drawdown_start, 'drawdown_start': drawdown_start,
@ -392,7 +392,7 @@ def text_table_strategy(strategy_results, stake_currency: str) -> str:
Generate summary table per strategy Generate summary table per strategy
:param stake_currency: stake-currency - used to correctly name headers :param stake_currency: stake-currency - used to correctly name headers
:param max_open_trades: Maximum allowed open trades used for backtest :param max_open_trades: Maximum allowed open trades used for backtest
:param all_results: Dict of <Strategyname: BacktestResult> containing results for all strategies :param all_results: Dict of <Strategyname: DataFrame> containing results for all strategies
:return: pretty printed table with tabulate as string :return: pretty printed table with tabulate as string
""" """
floatfmt = _get_line_floatfmt() floatfmt = _get_line_floatfmt()
@ -409,8 +409,8 @@ def text_table_strategy(strategy_results, stake_currency: str) -> str:
def text_table_add_metrics(strat_results: Dict) -> str: def text_table_add_metrics(strat_results: Dict) -> str:
if len(strat_results['trades']) > 0: if len(strat_results['trades']) > 0:
best_trade = max(strat_results['trades'], key=lambda x: x['profit_percent']) best_trade = max(strat_results['trades'], key=lambda x: x['profit_ratio'])
worst_trade = min(strat_results['trades'], key=lambda x: x['profit_percent']) worst_trade = min(strat_results['trades'], key=lambda x: x['profit_ratio'])
metrics = [ metrics = [
('Backtesting from', strat_results['backtest_start'].strftime(DATETIME_PRINT_FORMAT)), ('Backtesting from', strat_results['backtest_start'].strftime(DATETIME_PRINT_FORMAT)),
('Backtesting to', strat_results['backtest_end'].strftime(DATETIME_PRINT_FORMAT)), ('Backtesting to', strat_results['backtest_end'].strftime(DATETIME_PRINT_FORMAT)),
@ -424,9 +424,9 @@ def text_table_add_metrics(strat_results: Dict) -> str:
f"{round(strat_results['best_pair']['profit_sum_pct'], 2)}%"), f"{round(strat_results['best_pair']['profit_sum_pct'], 2)}%"),
('Worst Pair', f"{strat_results['worst_pair']['key']} " ('Worst Pair', f"{strat_results['worst_pair']['key']} "
f"{round(strat_results['worst_pair']['profit_sum_pct'], 2)}%"), f"{round(strat_results['worst_pair']['profit_sum_pct'], 2)}%"),
('Best trade', f"{best_trade['pair']} {round(best_trade['profit_percent'] * 100, 2)}%"), ('Best trade', f"{best_trade['pair']} {round(best_trade['profit_ratio'] * 100, 2)}%"),
('Worst trade', f"{worst_trade['pair']} " ('Worst trade', f"{worst_trade['pair']} "
f"{round(worst_trade['profit_percent'] * 100, 2)}%"), f"{round(worst_trade['profit_ratio'] * 100, 2)}%"),
('Best day', f"{round(strat_results['backtest_best_day'] * 100, 2)}%"), ('Best day', f"{round(strat_results['backtest_best_day'] * 100, 2)}%"),
('Worst day', f"{round(strat_results['backtest_worst_day'] * 100, 2)}%"), ('Worst day', f"{round(strat_results['backtest_worst_day'] * 100, 2)}%"),

View File

@ -175,7 +175,7 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
# Trades can be empty # Trades can be empty
if trades is not None and len(trades) > 0: if trades is not None and len(trades) > 0:
# Create description for sell summarizing the trade # Create description for sell summarizing the trade
trades['desc'] = trades.apply(lambda row: f"{round(row['profit_percent'] * 100, 1)}%, " trades['desc'] = trades.apply(lambda row: f"{round(row['profit_ratio'] * 100, 1)}%, "
f"{row['sell_reason']}, " f"{row['sell_reason']}, "
f"{row['trade_duration']} min", f"{row['trade_duration']} min",
axis=1) axis=1)
@ -195,9 +195,9 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
) )
trade_sells = go.Scatter( trade_sells = go.Scatter(
x=trades.loc[trades['profit_percent'] > 0, "close_date"], x=trades.loc[trades['profit_ratio'] > 0, "close_date"],
y=trades.loc[trades['profit_percent'] > 0, "close_rate"], y=trades.loc[trades['profit_ratio'] > 0, "close_rate"],
text=trades.loc[trades['profit_percent'] > 0, "desc"], text=trades.loc[trades['profit_ratio'] > 0, "desc"],
mode='markers', mode='markers',
name='Sell - Profit', name='Sell - Profit',
marker=dict( marker=dict(
@ -208,9 +208,9 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
) )
) )
trade_sells_loss = go.Scatter( trade_sells_loss = go.Scatter(
x=trades.loc[trades['profit_percent'] <= 0, "close_date"], x=trades.loc[trades['profit_ratio'] <= 0, "close_date"],
y=trades.loc[trades['profit_percent'] <= 0, "close_rate"], y=trades.loc[trades['profit_ratio'] <= 0, "close_rate"],
text=trades.loc[trades['profit_percent'] <= 0, "desc"], text=trades.loc[trades['profit_ratio'] <= 0, "desc"],
mode='markers', mode='markers',
name='Sell - Loss', name='Sell - Loss',
marker=dict( marker=dict(

View File

@ -39,8 +39,8 @@ class SampleHyperOptLoss(IHyperOptLoss):
""" """
Objective function, returns smaller number for better results Objective function, returns smaller number for better results
""" """
total_profit = results.profit_percent.sum() total_profit = results['profit_ratio'].sum()
trade_duration = results.trade_duration.mean() trade_duration = results['trade_duration'].mean()
trade_loss = 1 - 0.25 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.8) trade_loss = 1 - 0.25 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.8)
profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT) profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT)

View File

@ -37,7 +37,7 @@ def hyperopt_results():
return pd.DataFrame( return pd.DataFrame(
{ {
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'], 'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
'profit_percent': [-0.1, 0.2, 0.3], 'profit_ratio': [-0.1, 0.2, 0.3],
'profit_abs': [-0.2, 0.4, 0.6], 'profit_abs': [-0.2, 0.4, 0.6],
'trade_duration': [10, 30, 10], 'trade_duration': [10, 30, 10],
'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.ROI], 'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.ROI],

View File

@ -510,7 +510,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
) )
assert len(results) == len(data.trades) assert len(results) == len(data.trades)
assert round(results["profit_percent"].sum(), 3) == round(data.profit_perc, 3) assert round(results["profit_ratio"].sum(), 3) == round(data.profit_perc, 3)
for c, trade in enumerate(data.trades): for c, trade in enumerate(data.trades):
res = results.iloc[c] res = results.iloc[c]

View File

@ -469,7 +469,7 @@ def test_backtest(default_conf, fee, mocker, testdatadir) -> None:
expected = pd.DataFrame( expected = pd.DataFrame(
{'pair': [pair, pair], {'pair': [pair, pair],
'profit_percent': [0.0, 0.0], 'profit_ratio': [0.0, 0.0],
'profit_abs': [0.0, 0.0], 'profit_abs': [0.0, 0.0],
'open_date': pd.to_datetime([Arrow(2018, 1, 29, 18, 40, 0).datetime, 'open_date': pd.to_datetime([Arrow(2018, 1, 29, 18, 40, 0).datetime,
Arrow(2018, 1, 30, 3, 30, 0).datetime], utc=True Arrow(2018, 1, 30, 3, 30, 0).datetime], utc=True
@ -803,7 +803,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
patch_exchange(mocker) patch_exchange(mocker)
backtestmock = MagicMock(side_effect=[ backtestmock = MagicMock(side_effect=[
pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC'], pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC'],
'profit_percent': [0.0, 0.0], 'profit_ratio': [0.0, 0.0],
'profit_abs': [0.0, 0.0], 'profit_abs': [0.0, 0.0],
'open_date': pd.to_datetime(['2018-01-29 18:40:00', 'open_date': pd.to_datetime(['2018-01-29 18:40:00',
'2018-01-30 03:30:00', ], utc=True '2018-01-30 03:30:00', ], utc=True
@ -817,7 +817,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
'sell_reason': [SellType.ROI, SellType.ROI] 'sell_reason': [SellType.ROI, SellType.ROI]
}), }),
pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC', 'ETH/BTC'], pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC', 'ETH/BTC'],
'profit_percent': [0.03, 0.01, 0.1], 'profit_ratio': [0.03, 0.01, 0.1],
'profit_abs': [0.01, 0.02, 0.2], 'profit_abs': [0.01, 0.02, 0.2],
'open_date': pd.to_datetime(['2018-01-29 18:40:00', 'open_date': pd.to_datetime(['2018-01-29 18:40:00',
'2018-01-30 03:30:00', '2018-01-30 03:30:00',

View File

@ -427,7 +427,7 @@ def test_format_results(hyperopt):
('LTC/BTC', 1, 1, 123), ('LTC/BTC', 1, 1, 123),
('XPR/BTC', -1, -2, -246) ('XPR/BTC', -1, -2, -246)
] ]
labels = ['currency', 'profit_percent', 'profit_abs', 'trade_duration'] labels = ['currency', 'profit_ratio', 'profit_abs', 'trade_duration']
df = pd.DataFrame.from_records(trades, columns=labels) df = pd.DataFrame.from_records(trades, columns=labels)
results_metrics = hyperopt._calculate_results_metrics(df) results_metrics = hyperopt._calculate_results_metrics(df)
results_explanation = hyperopt._format_results_explanation_string(results_metrics) results_explanation = hyperopt._format_results_explanation_string(results_metrics)
@ -567,7 +567,7 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None:
trades = [ trades = [
('TRX/BTC', 0.023117, 0.000233, 100) ('TRX/BTC', 0.023117, 0.000233, 100)
] ]
labels = ['currency', 'profit_percent', 'profit_abs', 'trade_duration'] labels = ['currency', 'profit_ratio', 'profit_abs', 'trade_duration']
backtest_result = pd.DataFrame.from_records(trades, columns=labels) backtest_result = pd.DataFrame.from_records(trades, columns=labels)
mocker.patch( mocker.patch(

View File

@ -60,9 +60,9 @@ def test_loss_calculation_prefer_shorter_trades(hyperopt_conf, hyperopt_results)
def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) -> None: def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) -> None:
results_over = hyperopt_results.copy() results_over = hyperopt_results.copy()
results_over['profit_percent'] = hyperopt_results['profit_percent'] * 2 results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2
results_under = hyperopt_results.copy() results_under = hyperopt_results.copy()
results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2 results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2
hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf) hl = HyperOptLossResolver.load_hyperoptloss(hyperopt_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, 600, correct = hl.hyperopt_loss_function(hyperopt_results, 600,
@ -77,9 +77,9 @@ def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) ->
def test_sharpe_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None: def test_sharpe_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None:
results_over = hyperopt_results.copy() results_over = hyperopt_results.copy()
results_over['profit_percent'] = hyperopt_results['profit_percent'] * 2 results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2
results_under = hyperopt_results.copy() results_under = hyperopt_results.copy()
results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2 results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2
default_conf.update({'hyperopt_loss': 'SharpeHyperOptLoss'}) default_conf.update({'hyperopt_loss': 'SharpeHyperOptLoss'})
hl = HyperOptLossResolver.load_hyperoptloss(default_conf) hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
@ -95,9 +95,9 @@ def test_sharpe_loss_prefers_higher_profits(default_conf, hyperopt_results) -> N
def test_sharpe_loss_daily_prefers_higher_profits(default_conf, hyperopt_results) -> None: def test_sharpe_loss_daily_prefers_higher_profits(default_conf, hyperopt_results) -> None:
results_over = hyperopt_results.copy() results_over = hyperopt_results.copy()
results_over['profit_percent'] = hyperopt_results['profit_percent'] * 2 results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2
results_under = hyperopt_results.copy() results_under = hyperopt_results.copy()
results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2 results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2
default_conf.update({'hyperopt_loss': 'SharpeHyperOptLossDaily'}) default_conf.update({'hyperopt_loss': 'SharpeHyperOptLossDaily'})
hl = HyperOptLossResolver.load_hyperoptloss(default_conf) hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
@ -113,9 +113,9 @@ def test_sharpe_loss_daily_prefers_higher_profits(default_conf, hyperopt_results
def test_sortino_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None: def test_sortino_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None:
results_over = hyperopt_results.copy() results_over = hyperopt_results.copy()
results_over['profit_percent'] = hyperopt_results['profit_percent'] * 2 results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2
results_under = hyperopt_results.copy() results_under = hyperopt_results.copy()
results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2 results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2
default_conf.update({'hyperopt_loss': 'SortinoHyperOptLoss'}) default_conf.update({'hyperopt_loss': 'SortinoHyperOptLoss'})
hl = HyperOptLossResolver.load_hyperoptloss(default_conf) hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
@ -131,9 +131,9 @@ def test_sortino_loss_prefers_higher_profits(default_conf, hyperopt_results) ->
def test_sortino_loss_daily_prefers_higher_profits(default_conf, hyperopt_results) -> None: def test_sortino_loss_daily_prefers_higher_profits(default_conf, hyperopt_results) -> None:
results_over = hyperopt_results.copy() results_over = hyperopt_results.copy()
results_over['profit_percent'] = hyperopt_results['profit_percent'] * 2 results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2
results_under = hyperopt_results.copy() results_under = hyperopt_results.copy()
results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2 results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2
default_conf.update({'hyperopt_loss': 'SortinoHyperOptLossDaily'}) default_conf.update({'hyperopt_loss': 'SortinoHyperOptLossDaily'})
hl = HyperOptLossResolver.load_hyperoptloss(default_conf) hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
@ -149,9 +149,9 @@ def test_sortino_loss_daily_prefers_higher_profits(default_conf, hyperopt_result
def test_onlyprofit_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None: def test_onlyprofit_loss_prefers_higher_profits(default_conf, hyperopt_results) -> None:
results_over = hyperopt_results.copy() results_over = hyperopt_results.copy()
results_over['profit_percent'] = hyperopt_results['profit_percent'] * 2 results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2
results_under = hyperopt_results.copy() results_under = hyperopt_results.copy()
results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2 results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2
default_conf.update({'hyperopt_loss': 'OnlyProfitHyperOptLoss'}) default_conf.update({'hyperopt_loss': 'OnlyProfitHyperOptLoss'})
hl = HyperOptLossResolver.load_hyperoptloss(default_conf) hl = HyperOptLossResolver.load_hyperoptloss(default_conf)

View File

@ -27,7 +27,7 @@ def test_text_table_bt_results():
results = pd.DataFrame( results = pd.DataFrame(
{ {
'pair': ['ETH/BTC', 'ETH/BTC'], 'pair': ['ETH/BTC', 'ETH/BTC'],
'profit_percent': [0.1, 0.2], 'profit_ratio': [0.1, 0.2],
'profit_abs': [0.2, 0.4], 'profit_abs': [0.2, 0.4],
'trade_duration': [10, 30], 'trade_duration': [10, 30],
'wins': [2, 0], 'wins': [2, 0],
@ -59,7 +59,7 @@ def test_generate_backtest_stats(default_conf, testdatadir):
results = {'DefStrat': { results = {'DefStrat': {
'results': pd.DataFrame({"pair": ["UNITTEST/BTC", "UNITTEST/BTC", 'results': pd.DataFrame({"pair": ["UNITTEST/BTC", "UNITTEST/BTC",
"UNITTEST/BTC", "UNITTEST/BTC"], "UNITTEST/BTC", "UNITTEST/BTC"],
"profit_percent": [0.003312, 0.010801, 0.013803, 0.002780], "profit_ratio": [0.003312, 0.010801, 0.013803, 0.002780],
"profit_abs": [0.000003, 0.000011, 0.000014, 0.000003], "profit_abs": [0.000003, 0.000011, 0.000014, 0.000003],
"open_date": [Arrow(2017, 11, 14, 19, 32, 00).datetime, "open_date": [Arrow(2017, 11, 14, 19, 32, 00).datetime,
Arrow(2017, 11, 14, 21, 36, 00).datetime, Arrow(2017, 11, 14, 21, 36, 00).datetime,
@ -103,7 +103,7 @@ def test_generate_backtest_stats(default_conf, testdatadir):
results = {'DefStrat': { results = {'DefStrat': {
'results': pd.DataFrame( 'results': pd.DataFrame(
{"pair": ["UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC"], {"pair": ["UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC", "UNITTEST/BTC"],
"profit_percent": [0.003312, 0.010801, -0.013803, 0.002780], "profit_ratio": [0.003312, 0.010801, -0.013803, 0.002780],
"profit_abs": [0.000003, 0.000011, -0.000014, 0.000003], "profit_abs": [0.000003, 0.000011, -0.000014, 0.000003],
"open_date": [Arrow(2017, 11, 14, 19, 32, 00).datetime, "open_date": [Arrow(2017, 11, 14, 19, 32, 00).datetime,
Arrow(2017, 11, 14, 21, 36, 00).datetime, Arrow(2017, 11, 14, 21, 36, 00).datetime,
@ -179,7 +179,7 @@ def test_generate_pair_metrics():
results = pd.DataFrame( results = pd.DataFrame(
{ {
'pair': ['ETH/BTC', 'ETH/BTC'], 'pair': ['ETH/BTC', 'ETH/BTC'],
'profit_percent': [0.1, 0.2], 'profit_ratio': [0.1, 0.2],
'profit_abs': [0.2, 0.4], 'profit_abs': [0.2, 0.4],
'trade_duration': [10, 30], 'trade_duration': [10, 30],
'wins': [2, 0], 'wins': [2, 0],
@ -227,7 +227,7 @@ def test_text_table_sell_reason():
results = pd.DataFrame( results = pd.DataFrame(
{ {
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'], 'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
'profit_percent': [0.1, 0.2, -0.1], 'profit_ratio': [0.1, 0.2, -0.1],
'profit_abs': [0.2, 0.4, -0.2], 'profit_abs': [0.2, 0.4, -0.2],
'trade_duration': [10, 30, 10], 'trade_duration': [10, 30, 10],
'wins': [2, 0, 0], 'wins': [2, 0, 0],
@ -259,7 +259,7 @@ def test_generate_sell_reason_stats():
results = pd.DataFrame( results = pd.DataFrame(
{ {
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'], 'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
'profit_percent': [0.1, 0.2, -0.1], 'profit_ratio': [0.1, 0.2, -0.1],
'profit_abs': [0.2, 0.4, -0.2], 'profit_abs': [0.2, 0.4, -0.2],
'trade_duration': [10, 30, 10], 'trade_duration': [10, 30, 10],
'wins': [2, 0, 0], 'wins': [2, 0, 0],
@ -295,7 +295,7 @@ def test_text_table_strategy(default_conf):
results['TestStrategy1'] = {'results': pd.DataFrame( results['TestStrategy1'] = {'results': pd.DataFrame(
{ {
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'], 'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
'profit_percent': [0.1, 0.2, 0.3], 'profit_ratio': [0.1, 0.2, 0.3],
'profit_abs': [0.2, 0.4, 0.5], 'profit_abs': [0.2, 0.4, 0.5],
'trade_duration': [10, 30, 10], 'trade_duration': [10, 30, 10],
'wins': [2, 0, 0], 'wins': [2, 0, 0],
@ -307,7 +307,7 @@ def test_text_table_strategy(default_conf):
results['TestStrategy2'] = {'results': pd.DataFrame( results['TestStrategy2'] = {'results': pd.DataFrame(
{ {
'pair': ['LTC/BTC', 'LTC/BTC', 'LTC/BTC'], 'pair': ['LTC/BTC', 'LTC/BTC', 'LTC/BTC'],
'profit_percent': [0.4, 0.2, 0.3], 'profit_ratio': [0.4, 0.2, 0.3],
'profit_abs': [0.4, 0.4, 0.5], 'profit_abs': [0.4, 0.4, 0.5],
'trade_duration': [15, 30, 15], 'trade_duration': [15, 30, 15],
'wins': [4, 1, 0], 'wins': [4, 1, 0],