Merge branch 'freqtrade:develop' into dca
This commit is contained in:
commit
8e424f7c73
|
@ -196,7 +196,7 @@ jobs:
|
|||
uses: rjstone/discord-webhook-notify@v1
|
||||
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
||||
with:
|
||||
severity: error
|
||||
severity: info
|
||||
details: Test Succeeded!
|
||||
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -6,16 +6,16 @@ python -m pip install --upgrade pip wheel
|
|||
$pyv = python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"
|
||||
|
||||
if ($pyv -eq '3.7') {
|
||||
pip install build_helpers\TA_Lib-0.4.22-cp37-cp37m-win_amd64.whl
|
||||
pip install build_helpers\TA_Lib-0.4.23-cp37-cp37m-win_amd64.whl
|
||||
}
|
||||
if ($pyv -eq '3.8') {
|
||||
pip install build_helpers\TA_Lib-0.4.22-cp38-cp38-win_amd64.whl
|
||||
pip install build_helpers\TA_Lib-0.4.23-cp38-cp38-win_amd64.whl
|
||||
}
|
||||
if ($pyv -eq '3.9') {
|
||||
pip install build_helpers\TA_Lib-0.4.22-cp39-cp39-win_amd64.whl
|
||||
pip install build_helpers\TA_Lib-0.4.23-cp39-cp39-win_amd64.whl
|
||||
}
|
||||
if ($pyv -eq '3.10') {
|
||||
pip install build_helpers\TA_Lib-0.4.22-cp310-cp310-win_amd64.whl
|
||||
pip install build_helpers\TA_Lib-0.4.23-cp310-cp310-win_amd64.whl
|
||||
}
|
||||
pip install -r requirements-dev.txt
|
||||
pip install -e .
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
"sell_profit_only": false,
|
||||
"sell_profit_offset": 0.0,
|
||||
"ignore_roi_if_buy_signal": false,
|
||||
"ignore_buying_expired_candle_after": 300,
|
||||
"minimal_roi": {
|
||||
"40": 0.0,
|
||||
"30": 0.01,
|
||||
|
|
|
@ -312,7 +312,7 @@ A backtesting result will look like that:
|
|||
| | |
|
||||
| Min balance | 0.00945123 BTC |
|
||||
| Max balance | 0.01846651 BTC |
|
||||
| Drawdown | 50.63% |
|
||||
| Drawdown (Account) | 13.33% |
|
||||
| Drawdown | 0.0015 BTC |
|
||||
| Drawdown high | 0.0013 BTC |
|
||||
| Drawdown low | -0.0002 BTC |
|
||||
|
@ -399,7 +399,7 @@ It contains some useful key metrics about performance of your strategy on backte
|
|||
| | |
|
||||
| Min balance | 0.00945123 BTC |
|
||||
| Max balance | 0.01846651 BTC |
|
||||
| Drawdown | 50.63% |
|
||||
| Drawdown (Account) | 13.33% |
|
||||
| Drawdown | 0.0015 BTC |
|
||||
| Drawdown high | 0.0013 BTC |
|
||||
| Drawdown low | -0.0002 BTC |
|
||||
|
@ -426,7 +426,8 @@ It contains some useful key metrics about performance of your strategy on backte
|
|||
- `Avg. Duration Winners` / `Avg. Duration Loser`: Average durations for winning and losing trades.
|
||||
- `Rejected Buy signals`: Buy signals that could not be acted upon due to max_open_trades being reached.
|
||||
- `Min balance` / `Max balance`: Lowest and Highest Wallet balance during the backtest period.
|
||||
- `Drawdown`: Maximum drawdown experienced. For example, the value of 50% means that from highest to subsequent lowest point, a 50% drop was experienced).
|
||||
- `Drawdown (Account)`: Maximum Account Drawdown experienced. Calculated as $(Absolute Drawdown) / (DrawdownHigh + startingBalance)$.
|
||||
- `Drawdown`: Maximum, absolute drawdown experienced. Difference between Drawdown High and Subsequent Low point.
|
||||
- `Drawdown high` / `Drawdown low`: Profit at the beginning and end of the largest drawdown period. A negative low value means initial capital lost.
|
||||
- `Drawdown Start` / `Drawdown End`: Start and end datetime for this largest drawdown (can also be visualized via the `plot-dataframe` sub-command).
|
||||
- `Market change`: Change of the market during the backtest period. Calculated as average of all pairs changes from the first to the last candle using the "close" column.
|
||||
|
|
|
@ -15,8 +15,8 @@ This command line option was deprecated in 2019.7-dev (develop branch) and remov
|
|||
|
||||
### The **--dynamic-whitelist** command line option
|
||||
|
||||
This command line option was deprecated in 2018 and removed freqtrade 2019.6-dev (develop branch)
|
||||
and in freqtrade 2019.7.
|
||||
This command line option was deprecated in 2018 and removed freqtrade 2019.6-dev (develop branch) and in freqtrade 2019.7.
|
||||
Please refer to [pairlists](plugins.md#pairlists-and-pairlist-handlers) instead.
|
||||
|
||||
### the `--live` command line option
|
||||
|
||||
|
|
|
@ -222,9 +222,9 @@ should be rewritten to
|
|||
```python
|
||||
frames = [dataframe]
|
||||
for val in self.buy_ema_short.range:
|
||||
frames.append({
|
||||
frames.append(DataFrame({
|
||||
f'ema_short_{val}': ta.EMA(dataframe, timeperiod=val)
|
||||
})
|
||||
}))
|
||||
|
||||
# Append columns to existing dataframe
|
||||
merged_frame = pd.concat(frames, axis=1)
|
||||
|
|
|
@ -23,7 +23,7 @@ git clone https://github.com/freqtrade/freqtrade.git
|
|||
|
||||
Install ta-lib according to the [ta-lib documentation](https://github.com/mrjbq7/ta-lib#windows).
|
||||
|
||||
As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), there is also a repository of unofficial pre-compiled windows Wheels [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib), which need to be downloaded and installed using `pip install TA_Lib‑0.4.22‑cp38‑cp38‑win_amd64.whl` (make sure to use the version matching your python version).
|
||||
As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), there is also a repository of unofficial pre-compiled windows Wheels [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib), which need to be downloaded and installed using `pip install TA_Lib-0.4.23-cp38-cp38-win_amd64.whl` (make sure to use the version matching your python version).
|
||||
|
||||
Freqtrade provides these dependencies for the latest 3 Python versions (3.7, 3.8, 3.9 and 3.10) and for 64bit Windows.
|
||||
Other versions must be downloaded from the above link.
|
||||
|
|
|
@ -9,21 +9,13 @@ import numpy as np
|
|||
import pandas as pd
|
||||
|
||||
from freqtrade.constants import LAST_BT_RESULT_FN
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.misc import json_load
|
||||
from freqtrade.persistence import LocalTrade, Trade, init_db
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Old format - maybe remove?
|
||||
BT_DATA_COLUMNS_OLD = ["pair", "profit_percent", "open_date", "close_date", "index",
|
||||
"trade_duration", "open_rate", "close_rate", "open_at_end", "sell_reason"]
|
||||
|
||||
# Mid-term format, created by BacktestResult Named Tuple
|
||||
BT_DATA_COLUMNS_MID = ['pair', 'profit_percent', 'open_date', 'close_date', 'trade_duration',
|
||||
'open_rate', 'close_rate', 'open_at_end', 'sell_reason', 'fee_open',
|
||||
'fee_close', 'amount', 'profit_abs', 'profit_ratio']
|
||||
|
||||
# Newest format
|
||||
BT_DATA_COLUMNS = ['pair', 'stake_amount', 'amount', 'open_date', 'close_date',
|
||||
'open_rate', 'close_rate',
|
||||
|
@ -167,23 +159,9 @@ def load_backtest_data(filename: Union[Path, str], strategy: Optional[str] = Non
|
|||
)
|
||||
else:
|
||||
# old format - only with lists.
|
||||
df = pd.DataFrame(data, columns=BT_DATA_COLUMNS_OLD)
|
||||
if not df.empty:
|
||||
df['open_date'] = pd.to_datetime(df['open_date'],
|
||||
unit='s',
|
||||
utc=True,
|
||||
infer_datetime_format=True
|
||||
)
|
||||
df['close_date'] = pd.to_datetime(df['close_date'],
|
||||
unit='s',
|
||||
utc=True,
|
||||
infer_datetime_format=True
|
||||
)
|
||||
# Create compatibility with new format
|
||||
df['profit_abs'] = df['close_rate'] - df['open_rate']
|
||||
raise OperationalException(
|
||||
"Backtest-results with only trades data are no longer supported.")
|
||||
if not df.empty:
|
||||
if 'profit_ratio' not in df.columns:
|
||||
df['profit_ratio'] = df['profit_percent']
|
||||
df = df.sort_values("open_date").reset_index(drop=True)
|
||||
return df
|
||||
|
||||
|
@ -392,15 +370,17 @@ def calculate_underwater(trades: pd.DataFrame, *, date_col: str = 'close_date',
|
|||
|
||||
|
||||
def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_date',
|
||||
value_col: str = 'profit_ratio'
|
||||
) -> Tuple[float, pd.Timestamp, pd.Timestamp, float, float]:
|
||||
value_col: str = 'profit_abs', starting_balance: float = 0
|
||||
) -> Tuple[float, pd.Timestamp, pd.Timestamp, float, float, float]:
|
||||
"""
|
||||
Calculate max drawdown and the corresponding close dates
|
||||
:param trades: DataFrame containing trades (requires columns close_date and profit_ratio)
|
||||
:param date_col: Column in DataFrame to use for dates (defaults to 'close_date')
|
||||
:param value_col: Column in DataFrame to use for values (defaults to 'profit_ratio')
|
||||
:return: Tuple (float, highdate, lowdate, highvalue, lowvalue) with absolute max drawdown,
|
||||
high and low time and high and low value.
|
||||
:param value_col: Column in DataFrame to use for values (defaults to 'profit_abs')
|
||||
:param starting_balance: Portfolio starting balance - properly calculate relative drawdown.
|
||||
:return: Tuple (float, highdate, lowdate, highvalue, lowvalue, relative_drawdown)
|
||||
with absolute max drawdown, high and low time and high and low value,
|
||||
and the relative account drawdown
|
||||
:raise: ValueError if trade-dataframe was found empty.
|
||||
"""
|
||||
if len(trades) == 0:
|
||||
|
@ -416,7 +396,18 @@ def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_date'
|
|||
high_val = max_drawdown_df.loc[max_drawdown_df.iloc[:idxmin]
|
||||
['high_value'].idxmax(), 'cumulative']
|
||||
low_val = max_drawdown_df.loc[idxmin, 'cumulative']
|
||||
return abs(min(max_drawdown_df['drawdown'])), high_date, low_date, high_val, low_val
|
||||
max_drawdown_rel = 0.0
|
||||
if high_val + starting_balance != 0:
|
||||
max_drawdown_rel = (high_val - low_val) / (high_val + starting_balance)
|
||||
|
||||
return (
|
||||
abs(min(max_drawdown_df['drawdown'])),
|
||||
high_date,
|
||||
low_date,
|
||||
high_val,
|
||||
low_val,
|
||||
max_drawdown_rel
|
||||
)
|
||||
|
||||
|
||||
def calculate_csum(trades: pd.DataFrame, starting_balance: float = 0) -> Tuple[float, float]:
|
||||
|
|
|
@ -67,6 +67,8 @@ class Exchange:
|
|||
"ohlcv_params": {},
|
||||
"ohlcv_candle_limit": 500,
|
||||
"ohlcv_partial_candle": True,
|
||||
# Check https://github.com/ccxt/ccxt/issues/10767 for removal of ohlcv_volume_currency
|
||||
"ohlcv_volume_currency": "base", # "base" or "quote"
|
||||
"trades_pagination": "time", # Possible are "time" or "id"
|
||||
"trades_pagination_arg": "since",
|
||||
"l2_limit_range": None,
|
||||
|
@ -656,7 +658,8 @@ class Exchange:
|
|||
max_slippage_val = rate * ((1 + slippage) if side == 'buy' else (1 - slippage))
|
||||
|
||||
remaining_amount = amount
|
||||
filled_amount = 0
|
||||
filled_amount = 0.0
|
||||
book_entry_price = 0.0
|
||||
for book_entry in ob[ob_type]:
|
||||
book_entry_price = book_entry[0]
|
||||
book_entry_coin_volume = book_entry[1]
|
||||
|
|
|
@ -19,6 +19,7 @@ class Ftx(Exchange):
|
|||
_ft_has: Dict = {
|
||||
"stoploss_on_exchange": True,
|
||||
"ohlcv_candle_limit": 1500,
|
||||
"ohlcv_volume_currency": "quote",
|
||||
}
|
||||
|
||||
def market_is_tradable(self, market: Dict[str, Any]) -> bool:
|
||||
|
|
|
@ -21,6 +21,7 @@ class Gateio(Exchange):
|
|||
|
||||
_ft_has: Dict = {
|
||||
"ohlcv_candle_limit": 1000,
|
||||
"ohlcv_volume_currency": "quote",
|
||||
}
|
||||
|
||||
_headers = {'X-Gate-Channel-Id': 'freqtrade'}
|
||||
|
|
|
@ -14,5 +14,5 @@ class Okex(Exchange):
|
|||
"""
|
||||
|
||||
_ft_has: Dict = {
|
||||
"ohlcv_candle_limit": 100,
|
||||
"ohlcv_candle_limit": 300,
|
||||
}
|
||||
|
|
|
@ -270,8 +270,8 @@ class Backtesting:
|
|||
df_analyzed = self.strategy.advise_sell(
|
||||
self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair}).copy()
|
||||
# Trim startup period from analyzed dataframe
|
||||
df_analyzed = trim_dataframe(df_analyzed, self.timerange,
|
||||
startup_candles=self.required_startup)
|
||||
df_analyzed = processed[pair] = pair_data = trim_dataframe(
|
||||
df_analyzed, self.timerange, startup_candles=self.required_startup)
|
||||
# To avoid using data from future, we use buy/sell signals shifted
|
||||
# from the previous candle
|
||||
df_analyzed.loc[:, 'buy'] = df_analyzed.loc[:, 'buy'].shift(1)
|
||||
|
@ -287,9 +287,6 @@ class Backtesting:
|
|||
# Convert from Pandas to list for performance reasons
|
||||
# (Looping Pandas is slow.)
|
||||
data[pair] = df_analyzed[headers].values.tolist()
|
||||
|
||||
# Do not hold on to old data to reduce memory usage
|
||||
processed[pair] = pair_data = None
|
||||
return data
|
||||
|
||||
def _get_close_rate(self, sell_row: Tuple, trade: LocalTrade, sell: SellCheckTuple,
|
||||
|
@ -445,7 +442,9 @@ class Backtesting:
|
|||
return self._get_sell_trade_entry_for_candle(trade, sell_row)
|
||||
detail_data.loc[:, 'buy'] = sell_row[BUY_IDX]
|
||||
detail_data.loc[:, 'sell'] = sell_row[SELL_IDX]
|
||||
headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high']
|
||||
detail_data.loc[:, 'buy_tag'] = sell_row[BUY_TAG_IDX]
|
||||
detail_data.loc[:, 'exit_tag'] = sell_row[EXIT_TAG_IDX]
|
||||
headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high', 'buy_tag', 'exit_tag']
|
||||
for det_row in detail_data[headers].values.tolist():
|
||||
res = self._get_sell_trade_entry_for_candle(trade, det_row)
|
||||
if res:
|
||||
|
|
|
@ -76,6 +76,7 @@ class Hyperopt:
|
|||
self.config = config
|
||||
|
||||
self.backtesting = Backtesting(self.config)
|
||||
self.pairlist = self.backtesting.pairlists.whitelist
|
||||
|
||||
if not self.config.get('hyperopt'):
|
||||
self.custom_hyperopt = HyperOptAuto(self.config)
|
||||
|
@ -332,7 +333,7 @@ class Hyperopt:
|
|||
params_details = self._get_params_details(params_dict)
|
||||
|
||||
strat_stats = generate_strategy_stats(
|
||||
processed, self.backtesting.strategy.get_strategy_name(),
|
||||
self.pairlist, self.backtesting.strategy.get_strategy_name(),
|
||||
backtesting_results, min_date, max_date, market_change=0
|
||||
)
|
||||
results_explanation = HyperoptTools.format_results_explanation_string(
|
||||
|
|
|
@ -47,10 +47,9 @@ class CalmarHyperOptLoss(IHyperOptLoss):
|
|||
|
||||
# calculate max drawdown
|
||||
try:
|
||||
_, _, _, high_val, low_val = calculate_max_drawdown(
|
||||
_, _, _, _, _, max_drawdown = calculate_max_drawdown(
|
||||
results, value_col="profit_abs"
|
||||
)
|
||||
max_drawdown = (high_val - low_val) / high_val
|
||||
except ValueError:
|
||||
max_drawdown = 0
|
||||
|
||||
|
|
|
@ -299,8 +299,7 @@ class HyperoptTools():
|
|||
f"Objective: {results['loss']:.5f}")
|
||||
|
||||
@staticmethod
|
||||
def prepare_trials_columns(trials: pd.DataFrame, legacy_mode: bool,
|
||||
has_drawdown: bool) -> pd.DataFrame:
|
||||
def prepare_trials_columns(trials: pd.DataFrame, has_drawdown: bool) -> pd.DataFrame:
|
||||
trials['Best'] = ''
|
||||
|
||||
if 'results_metrics.winsdrawslosses' not in trials.columns:
|
||||
|
@ -309,33 +308,26 @@ class HyperoptTools():
|
|||
|
||||
if not has_drawdown:
|
||||
# Ensure compatibility with older versions of hyperopt results
|
||||
trials['results_metrics.max_drawdown_abs'] = None
|
||||
trials['results_metrics.max_drawdown'] = None
|
||||
trials['results_metrics.max_drawdown_account'] = None
|
||||
|
||||
if not legacy_mode:
|
||||
# New mode, using backtest result for metrics
|
||||
trials['results_metrics.winsdrawslosses'] = trials.apply(
|
||||
lambda x: f"{x['results_metrics.wins']} {x['results_metrics.draws']:>4} "
|
||||
f"{x['results_metrics.losses']:>4}", axis=1)
|
||||
trials = trials[['Best', 'current_epoch', 'results_metrics.total_trades',
|
||||
'results_metrics.winsdrawslosses',
|
||||
'results_metrics.profit_mean', 'results_metrics.profit_total_abs',
|
||||
'results_metrics.profit_total', 'results_metrics.holding_avg',
|
||||
'results_metrics.max_drawdown', 'results_metrics.max_drawdown_abs',
|
||||
'loss', 'is_initial_point', 'is_best']]
|
||||
# New mode, using backtest result for metrics
|
||||
trials['results_metrics.winsdrawslosses'] = trials.apply(
|
||||
lambda x: f"{x['results_metrics.wins']} {x['results_metrics.draws']:>4} "
|
||||
f"{x['results_metrics.losses']:>4}", axis=1)
|
||||
|
||||
else:
|
||||
# Legacy mode
|
||||
trials = trials[['Best', 'current_epoch', 'results_metrics.trade_count',
|
||||
'results_metrics.winsdrawslosses', 'results_metrics.avg_profit',
|
||||
'results_metrics.total_profit', 'results_metrics.profit',
|
||||
'results_metrics.duration', 'results_metrics.max_drawdown',
|
||||
'results_metrics.max_drawdown_abs', 'loss', 'is_initial_point',
|
||||
'is_best']]
|
||||
trials = trials[['Best', 'current_epoch', 'results_metrics.total_trades',
|
||||
'results_metrics.winsdrawslosses',
|
||||
'results_metrics.profit_mean', 'results_metrics.profit_total_abs',
|
||||
'results_metrics.profit_total', 'results_metrics.holding_avg',
|
||||
'results_metrics.max_drawdown',
|
||||
'results_metrics.max_drawdown_account', 'results_metrics.max_drawdown_abs',
|
||||
'loss', 'is_initial_point', 'is_best']]
|
||||
|
||||
trials.columns = ['Best', 'Epoch', 'Trades', ' Win Draw Loss', 'Avg profit',
|
||||
'Total profit', 'Profit', 'Avg duration', 'Max Drawdown',
|
||||
'max_drawdown_abs', 'Objective', 'is_initial_point', 'is_best']
|
||||
trials.columns = [
|
||||
'Best', 'Epoch', 'Trades', ' Win Draw Loss', 'Avg profit',
|
||||
'Total profit', 'Profit', 'Avg duration', 'max_drawdown', 'max_drawdown_account',
|
||||
'max_drawdown_abs', 'Objective', 'is_initial_point', 'is_best'
|
||||
]
|
||||
|
||||
return trials
|
||||
|
||||
|
@ -351,10 +343,9 @@ class HyperoptTools():
|
|||
tabulate.PRESERVE_WHITESPACE = True
|
||||
trials = json_normalize(results, max_level=1)
|
||||
|
||||
legacy_mode = 'results_metrics.total_trades' not in trials
|
||||
has_drawdown = 'results_metrics.max_drawdown_abs' in trials.columns
|
||||
has_account_drawdown = 'results_metrics.max_drawdown_account' in trials.columns
|
||||
|
||||
trials = HyperoptTools.prepare_trials_columns(trials, legacy_mode, has_drawdown)
|
||||
trials = HyperoptTools.prepare_trials_columns(trials, has_account_drawdown)
|
||||
|
||||
trials['is_profit'] = False
|
||||
trials.loc[trials['is_initial_point'], 'Best'] = '* '
|
||||
|
@ -362,12 +353,12 @@ class HyperoptTools():
|
|||
trials.loc[trials['is_initial_point'] & trials['is_best'], 'Best'] = '* Best'
|
||||
trials.loc[trials['Total profit'] > 0, 'is_profit'] = True
|
||||
trials['Trades'] = trials['Trades'].astype(str)
|
||||
perc_multi = 1 if legacy_mode else 100
|
||||
# perc_multi = 1 if legacy_mode else 100
|
||||
trials['Epoch'] = trials['Epoch'].apply(
|
||||
lambda x: '{}/{}'.format(str(x).rjust(len(str(total_epochs)), ' '), total_epochs)
|
||||
)
|
||||
trials['Avg profit'] = trials['Avg profit'].apply(
|
||||
lambda x: f'{x * perc_multi:,.2f}%'.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(
|
||||
lambda x: f'{x:,.1f} m'.rjust(7, ' ') if isinstance(x, float) else f"{x}"
|
||||
|
@ -379,24 +370,25 @@ class HyperoptTools():
|
|||
|
||||
stake_currency = config['stake_currency']
|
||||
|
||||
if has_drawdown:
|
||||
trials['Max Drawdown'] = trials.apply(
|
||||
lambda x: '{} {}'.format(
|
||||
round_coin_value(x['max_drawdown_abs'], stake_currency),
|
||||
'({:,.2f}%)'.format(x['Max Drawdown'] * perc_multi).rjust(10, ' ')
|
||||
).rjust(25 + len(stake_currency))
|
||||
if x['Max Drawdown'] != 0.0 else '--'.rjust(25 + len(stake_currency)),
|
||||
axis=1
|
||||
)
|
||||
else:
|
||||
trials = trials.drop(columns=['Max Drawdown'])
|
||||
trials[f"Max Drawdown{' (Acct)' if has_account_drawdown else ''}"] = trials.apply(
|
||||
lambda x: "{} {}".format(
|
||||
round_coin_value(x['max_drawdown_abs'], stake_currency),
|
||||
(f"({x['max_drawdown_account']:,.2%})"
|
||||
if has_account_drawdown
|
||||
else f"({x['max_drawdown']:,.2%})"
|
||||
).rjust(10, ' ')
|
||||
).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
|
||||
)
|
||||
|
||||
trials = trials.drop(columns=['max_drawdown_abs'])
|
||||
trials = trials.drop(columns=['max_drawdown_abs', 'max_drawdown', 'max_drawdown_account'])
|
||||
|
||||
trials['Profit'] = trials.apply(
|
||||
lambda x: '{} {}'.format(
|
||||
round_coin_value(x['Total profit'], stake_currency),
|
||||
'({:,.2f}%)'.format(x['Profit'] * perc_multi).rjust(10, ' ')
|
||||
f"({x['Profit']:,.2%})".rjust(10, ' ')
|
||||
).rjust(25+len(stake_currency))
|
||||
if x['Total profit'] != 0.0 else '--'.rjust(25+len(stake_currency)),
|
||||
axis=1
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import logging
|
||||
from copy import deepcopy
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Union
|
||||
|
@ -98,11 +99,11 @@ def _generate_result_line(result: DataFrame, starting_balance: int, first_column
|
|||
}
|
||||
|
||||
|
||||
def generate_pair_metrics(data: Dict[str, Dict], stake_currency: str, starting_balance: int,
|
||||
def generate_pair_metrics(pairlist: List[str], stake_currency: str, starting_balance: int,
|
||||
results: DataFrame, skip_nan: bool = False) -> List[Dict]:
|
||||
"""
|
||||
Generates and returns a list for the given backtest data and the results dataframe
|
||||
:param data: Dict of <pair: dataframe> containing data that was used during backtesting.
|
||||
:param pairlist: Pairlist used
|
||||
:param stake_currency: stake-currency - used to correctly name headers
|
||||
:param starting_balance: Starting balance
|
||||
:param results: Dataframe containing the backtest results
|
||||
|
@ -112,7 +113,7 @@ def generate_pair_metrics(data: Dict[str, Dict], stake_currency: str, starting_b
|
|||
|
||||
tabular_data = []
|
||||
|
||||
for pair in data:
|
||||
for pair in pairlist:
|
||||
result = results[results['pair'] == pair]
|
||||
if skip_nan and result['profit_abs'].isnull().all():
|
||||
continue
|
||||
|
@ -194,29 +195,21 @@ def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List
|
|||
return tabular_data
|
||||
|
||||
|
||||
def generate_strategy_comparison(all_results: Dict) -> List[Dict]:
|
||||
def generate_strategy_comparison(bt_stats: Dict) -> List[Dict]:
|
||||
"""
|
||||
Generate summary per strategy
|
||||
:param all_results: Dict of <Strategyname: DataFrame> containing results for all strategies
|
||||
:param bt_stats: Dict of <Strategyname: DataFrame> containing results for all strategies
|
||||
:return: List of Dicts containing the metrics per Strategy
|
||||
"""
|
||||
|
||||
tabular_data = []
|
||||
for strategy, results in all_results.items():
|
||||
tabular_data.append(_generate_result_line(
|
||||
results['results'], results['config']['dry_run_wallet'], strategy)
|
||||
)
|
||||
try:
|
||||
max_drawdown_per, _, _, _, _ = calculate_max_drawdown(results['results'],
|
||||
value_col='profit_ratio')
|
||||
max_drawdown_abs, _, _, _, _ = calculate_max_drawdown(results['results'],
|
||||
value_col='profit_abs')
|
||||
except ValueError:
|
||||
max_drawdown_per = 0
|
||||
max_drawdown_abs = 0
|
||||
tabular_data[-1]['max_drawdown_per'] = round(max_drawdown_per * 100, 2)
|
||||
tabular_data[-1]['max_drawdown_abs'] = \
|
||||
round_coin_value(max_drawdown_abs, results['config']['stake_currency'], False)
|
||||
for strategy, result in bt_stats.items():
|
||||
tabular_data.append(deepcopy(result['results_per_pair'][-1]))
|
||||
# Update "key" to strategy (results_per_pair has it as "Total").
|
||||
tabular_data[-1]['key'] = strategy
|
||||
tabular_data[-1]['max_drawdown_account'] = result['max_drawdown_account']
|
||||
tabular_data[-1]['max_drawdown_abs'] = round_coin_value(
|
||||
result['max_drawdown_abs'], result['stake_currency'], False)
|
||||
return tabular_data
|
||||
|
||||
|
||||
|
@ -352,14 +345,14 @@ def generate_daily_stats(results: DataFrame) -> Dict[str, Any]:
|
|||
}
|
||||
|
||||
|
||||
def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
||||
def generate_strategy_stats(pairlist: List[str],
|
||||
strategy: str,
|
||||
content: Dict[str, Any],
|
||||
min_date: datetime, max_date: datetime,
|
||||
market_change: float
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
:param btdata: Backtest data
|
||||
:param pairlist: List of pairs to backtest
|
||||
:param strategy: Strategy name
|
||||
:param content: Backtest result data in the format:
|
||||
{'results: results, 'config: config}}.
|
||||
|
@ -372,11 +365,11 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
|||
if not isinstance(results, DataFrame):
|
||||
return {}
|
||||
config = content['config']
|
||||
max_open_trades = min(config['max_open_trades'], len(btdata.keys()))
|
||||
max_open_trades = min(config['max_open_trades'], len(pairlist))
|
||||
starting_balance = config['dry_run_wallet']
|
||||
stake_currency = config['stake_currency']
|
||||
|
||||
pair_results = generate_pair_metrics(btdata, stake_currency=stake_currency,
|
||||
pair_results = generate_pair_metrics(pairlist, stake_currency=stake_currency,
|
||||
starting_balance=starting_balance,
|
||||
results=results, skip_nan=False)
|
||||
|
||||
|
@ -385,7 +378,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
|||
|
||||
sell_reason_stats = generate_sell_reason_stats(max_open_trades=max_open_trades,
|
||||
results=results)
|
||||
left_open_results = generate_pair_metrics(btdata, stake_currency=stake_currency,
|
||||
left_open_results = generate_pair_metrics(pairlist, stake_currency=stake_currency,
|
||||
starting_balance=starting_balance,
|
||||
results=results.loc[results['is_open']],
|
||||
skip_nan=True)
|
||||
|
@ -429,7 +422,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
|||
|
||||
'trades_per_day': round(len(results) / backtest_days, 2),
|
||||
'market_change': market_change,
|
||||
'pairlist': list(btdata.keys()),
|
||||
'pairlist': pairlist,
|
||||
'stake_amount': config['stake_amount'],
|
||||
'stake_currency': config['stake_currency'],
|
||||
'stake_currency_decimals': decimals_per_coin(config['stake_currency']),
|
||||
|
@ -462,12 +455,14 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
|||
}
|
||||
|
||||
try:
|
||||
max_drawdown, _, _, _, _ = calculate_max_drawdown(
|
||||
max_drawdown_legacy, _, _, _, _, _ = calculate_max_drawdown(
|
||||
results, value_col='profit_ratio')
|
||||
drawdown_abs, drawdown_start, drawdown_end, high_val, low_val = calculate_max_drawdown(
|
||||
results, value_col='profit_abs')
|
||||
(drawdown_abs, drawdown_start, drawdown_end, high_val, low_val,
|
||||
max_drawdown) = calculate_max_drawdown(
|
||||
results, value_col='profit_abs', starting_balance=starting_balance)
|
||||
strat_stats.update({
|
||||
'max_drawdown': max_drawdown,
|
||||
'max_drawdown': max_drawdown_legacy, # Deprecated - do not use
|
||||
'max_drawdown_account': max_drawdown,
|
||||
'max_drawdown_abs': drawdown_abs,
|
||||
'drawdown_start': drawdown_start.strftime(DATETIME_PRINT_FORMAT),
|
||||
'drawdown_start_ts': drawdown_start.timestamp() * 1000,
|
||||
|
@ -487,6 +482,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
|||
except ValueError:
|
||||
strat_stats.update({
|
||||
'max_drawdown': 0.0,
|
||||
'max_drawdown_account': 0.0,
|
||||
'max_drawdown_abs': 0.0,
|
||||
'max_drawdown_low': 0.0,
|
||||
'max_drawdown_high': 0.0,
|
||||
|
@ -515,13 +511,13 @@ def generate_backtest_stats(btdata: Dict[str, DataFrame],
|
|||
"""
|
||||
result: Dict[str, Any] = {'strategy': {}}
|
||||
market_change = calculate_market_change(btdata, 'close')
|
||||
|
||||
pairlist = list(btdata.keys())
|
||||
for strategy, content in all_results.items():
|
||||
strat_stats = generate_strategy_stats(btdata, strategy, content,
|
||||
strat_stats = generate_strategy_stats(pairlist, strategy, content,
|
||||
min_date, max_date, market_change=market_change)
|
||||
result['strategy'][strategy] = strat_stats
|
||||
|
||||
strategy_results = generate_strategy_comparison(all_results=all_results)
|
||||
strategy_results = generate_strategy_comparison(bt_stats=result['strategy'])
|
||||
|
||||
result['strategy_comparison'] = strategy_results
|
||||
|
||||
|
@ -646,7 +642,12 @@ def text_table_strategy(strategy_results, stake_currency: str) -> str:
|
|||
headers.append('Drawdown')
|
||||
|
||||
# Align drawdown string on the center two space separator.
|
||||
drawdown = [f'{t["max_drawdown_per"]:.2f}' for t in strategy_results]
|
||||
if 'max_drawdown_account' in strategy_results[0]:
|
||||
drawdown = [f'{t["max_drawdown_account"] * 100:.2f}' for t in strategy_results]
|
||||
else:
|
||||
# Support for prior backtest results
|
||||
drawdown = [f'{t["max_drawdown_per"]:.2f}' for t in strategy_results]
|
||||
|
||||
dd_pad_abs = max([len(t['max_drawdown_abs']) for t in strategy_results])
|
||||
dd_pad_per = max([len(dd) for dd in drawdown])
|
||||
drawdown = [f'{t["max_drawdown_abs"]:>{dd_pad_abs}} {stake_currency} {dd:>{dd_pad_per}}%'
|
||||
|
@ -716,7 +717,10 @@ def text_table_add_metrics(strat_results: Dict) -> str:
|
|||
('Max balance', round_coin_value(strat_results['csum_max'],
|
||||
strat_results['stake_currency'])),
|
||||
|
||||
('Drawdown', f"{strat_results['max_drawdown']:.2%}"),
|
||||
# Compatibility to show old hyperopt results
|
||||
('Drawdown (Account)', f"{strat_results['max_drawdown_account']:.2%}")
|
||||
if 'max_drawdown_account' in strat_results else (
|
||||
'Drawdown', f"{strat_results['max_drawdown']:.2%}"),
|
||||
('Drawdown', round_coin_value(strat_results['max_drawdown_abs'],
|
||||
strat_results['stake_currency'])),
|
||||
('Drawdown high', round_coin_value(strat_results['max_drawdown_high'],
|
||||
|
|
|
@ -161,7 +161,7 @@ def add_max_drawdown(fig, row, trades: pd.DataFrame, df_comb: pd.DataFrame,
|
|||
Add scatter points indicating max drawdown
|
||||
"""
|
||||
try:
|
||||
max_drawdown, highdate, lowdate, _, _ = calculate_max_drawdown(trades)
|
||||
_, highdate, lowdate, _, _, max_drawdown = calculate_max_drawdown(trades)
|
||||
|
||||
drawdown = go.Scatter(
|
||||
x=[highdate, lowdate],
|
||||
|
|
|
@ -4,7 +4,6 @@ Volume PairList provider
|
|||
Provides dynamic pair list based on trade volumes
|
||||
"""
|
||||
import logging
|
||||
from functools import partial
|
||||
from typing import Any, Dict, List
|
||||
|
||||
import arrow
|
||||
|
@ -120,10 +119,17 @@ class VolumePairList(IPairList):
|
|||
else:
|
||||
# Use fresh pairlist
|
||||
# Check if pair quote currency equals to the stake currency.
|
||||
_pairlist = [k for k in self._exchange.get_markets(
|
||||
quote_currencies=[self._stake_currency],
|
||||
pairs_only=True, active_only=True).keys()]
|
||||
# No point in testing for blacklisted pairs...
|
||||
_pairlist = self.verify_blacklist(_pairlist, logger.info)
|
||||
|
||||
filtered_tickers = [
|
||||
v for k, v in tickers.items()
|
||||
if (self._exchange.get_pair_quote_currency(k) == self._stake_currency
|
||||
and (self._use_range or v[self._sort_key] is not None))]
|
||||
and (self._use_range or v[self._sort_key] is not None)
|
||||
and v['symbol'] in _pairlist)]
|
||||
pairlist = [s['symbol'] for s in filtered_tickers]
|
||||
|
||||
pairlist = self.filter_pairlist(pairlist, tickers)
|
||||
|
@ -178,12 +184,16 @@ class VolumePairList(IPairList):
|
|||
] if (p['symbol'], self._lookback_timeframe) in candles else None
|
||||
# in case of candle data calculate typical price and quoteVolume for candle
|
||||
if pair_candles is not None and not pair_candles.empty:
|
||||
pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low']
|
||||
+ pair_candles['close']) / 3
|
||||
pair_candles['quoteVolume'] = (
|
||||
pair_candles['volume'] * pair_candles['typical_price']
|
||||
)
|
||||
if self._exchange._ft_has["ohlcv_volume_currency"] == "base":
|
||||
pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low']
|
||||
+ pair_candles['close']) / 3
|
||||
|
||||
pair_candles['quoteVolume'] = (
|
||||
pair_candles['volume'] * pair_candles['typical_price']
|
||||
)
|
||||
else:
|
||||
# Exchange ohlcv data is in quote volume already.
|
||||
pair_candles['quoteVolume'] = pair_candles['volume']
|
||||
# ensure that a rolling sum over the lookback_period is built
|
||||
# if pair_candles contains more candles than lookback_period
|
||||
quoteVolume = (pair_candles['quoteVolume']
|
||||
|
@ -204,7 +214,7 @@ class VolumePairList(IPairList):
|
|||
|
||||
# Validate whitelist to only have active market pairs
|
||||
pairs = self._whitelist_for_active_markets([s['symbol'] for s in sorted_tickers])
|
||||
pairs = self.verify_blacklist(pairs, partial(self.log_once, logmethod=logger.info))
|
||||
pairs = self.verify_blacklist(pairs, logmethod=logger.info)
|
||||
# Limit pairlist to the requested number of pairs
|
||||
pairs = pairs[:self._number_pairs]
|
||||
|
||||
|
|
|
@ -55,7 +55,8 @@ class MaxDrawdown(IProtection):
|
|||
|
||||
# Drawdown is always positive
|
||||
try:
|
||||
drawdown, _, _, _, _ = calculate_max_drawdown(trades_df, value_col='close_profit')
|
||||
# TODO: This should use absolute profit calculation, considering account balance.
|
||||
drawdown, _, _, _, _, _ = calculate_max_drawdown(trades_df, value_col='close_profit')
|
||||
except ValueError:
|
||||
return False, None, None
|
||||
|
||||
|
|
|
@ -7,5 +7,4 @@ scikit-learn==1.0.2
|
|||
scikit-optimize==0.9.0
|
||||
filelock==3.4.2
|
||||
joblib==1.1.0
|
||||
psutil==5.8.0
|
||||
progressbar2==3.55.0
|
||||
|
|
|
@ -3,7 +3,7 @@ numpy==1.22.0; python_version > '3.7'
|
|||
pandas==1.3.5
|
||||
pandas-ta==0.3.14b
|
||||
|
||||
ccxt==1.66.20
|
||||
ccxt==1.66.32
|
||||
# Pin cryptography for now due to rust build errors with piwheels
|
||||
cryptography==36.0.1
|
||||
aiohttp==3.8.1
|
||||
|
@ -14,7 +14,7 @@ cachetools==4.2.2
|
|||
requests==2.26.0
|
||||
urllib3==1.26.7
|
||||
jsonschema==4.3.3
|
||||
TA-Lib==0.4.22
|
||||
TA-Lib==0.4.23
|
||||
technical==1.3.0
|
||||
tabulate==0.8.9
|
||||
pycoingecko==2.2.0
|
||||
|
@ -36,7 +36,7 @@ fastapi==0.70.1
|
|||
uvicorn==0.16.0
|
||||
pyjwt==2.3.0
|
||||
aiofiles==0.8.0
|
||||
psutil==5.8.0
|
||||
psutil==5.9.0
|
||||
|
||||
# Support for colorized terminal output
|
||||
colorama==0.4.4
|
||||
|
|
2
setup.py
2
setup.py
|
@ -43,7 +43,7 @@ setup(
|
|||
],
|
||||
install_requires=[
|
||||
# from requirements.txt
|
||||
'ccxt>=1.60.11',
|
||||
'ccxt>=1.66.32',
|
||||
'SQLAlchemy',
|
||||
'python-telegram-bot>=13.4',
|
||||
'arrow>=0.17.0',
|
||||
|
|
|
@ -2020,7 +2020,7 @@ def saved_hyperopt_results():
|
|||
'params_dict': {
|
||||
'mfi-value': 15, 'fastd-value': 20, 'adx-value': 25, 'rsi-value': 28, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 88, 'sell-fastd-value': 97, 'sell-adx-value': 51, 'sell-rsi-value': 67, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1190, 'roi_t2': 541, 'roi_t3': 408, 'roi_p1': 0.026035863879169705, 'roi_p2': 0.12508730043628782, 'roi_p3': 0.27766427921605896, 'stoploss': -0.2562930402099556}, # noqa: E501
|
||||
'params_details': {'buy': {'mfi-value': 15, 'fastd-value': 20, 'adx-value': 25, 'rsi-value': 28, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 88, 'sell-fastd-value': 97, 'sell-adx-value': 51, 'sell-rsi-value': 67, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.4287874435315165, 408: 0.15112316431545753, 949: 0.026035863879169705, 2139: 0}, 'stoploss': {'stoploss': -0.2562930402099556}}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 2, 'wins': 0, 'draws': 0, 'losses': 2, 'profit_mean': -0.01254995, 'profit_median': -0.012222, 'profit_total': -0.00125625, 'profit_total_abs': -2.50999, 'holding_avg': timedelta(minutes=3930.0), 'stake_currency': 'BTC', 'strategy_name': 'SampleStrategy'}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 2, 'wins': 0, 'draws': 0, 'losses': 2, 'profit_mean': -0.01254995, 'profit_median': -0.012222, 'profit_total': -0.00125625, 'profit_total_abs': -2.50999, 'max_drawdown': 0.23, 'max_drawdown_abs': -0.00125625, 'holding_avg': timedelta(minutes=3930.0), 'stake_currency': 'BTC', 'strategy_name': 'SampleStrategy'}, # noqa: E501
|
||||
'results_explanation': ' 2 trades. Avg profit -1.25%. Total profit -0.00125625 BTC ( -2.51Σ%). Avg duration 3930.0 min.', # noqa: E501
|
||||
'total_profit': -0.00125625,
|
||||
'current_epoch': 1,
|
||||
|
@ -2036,7 +2036,7 @@ def saved_hyperopt_results():
|
|||
'sell': {'sell-mfi-value': 96, 'sell-fastd-value': 68, 'sell-adx-value': 63, 'sell-rsi-value': 81, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal'}, # noqa: E501
|
||||
'roi': {0: 0.4449309386008759, 140: 0.11955965746663, 823: 0.06403981740598495, 1157: 0}, # noqa: E501
|
||||
'stoploss': {'stoploss': -0.338070047333259}},
|
||||
'results_metrics': {'total_trades': 1, 'wins': 0, 'draws': 0, 'losses': 1, 'profit_mean': 0.012357, 'profit_median': -0.012222, 'profit_total': 6.185e-05, 'profit_total_abs': 0.12357, 'holding_avg': timedelta(minutes=1200.0)}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 1, 'wins': 0, 'draws': 0, 'losses': 1, 'profit_mean': 0.012357, 'profit_median': -0.012222, 'profit_total': 6.185e-05, 'profit_total_abs': 0.12357, 'max_drawdown': 0.23, 'max_drawdown_abs': -0.00125625, 'holding_avg': timedelta(minutes=1200.0)}, # noqa: E501
|
||||
'results_explanation': ' 1 trades. Avg profit 0.12%. Total profit 0.00006185 BTC ( 0.12Σ%). Avg duration 1200.0 min.', # noqa: E501
|
||||
'total_profit': 6.185e-05,
|
||||
'current_epoch': 2,
|
||||
|
@ -2046,7 +2046,7 @@ def saved_hyperopt_results():
|
|||
'loss': 14.241196856510731,
|
||||
'params_dict': {'mfi-value': 25, 'fastd-value': 16, 'adx-value': 29, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 98, 'sell-fastd-value': 72, 'sell-adx-value': 51, 'sell-rsi-value': 82, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 889, 'roi_t2': 533, 'roi_t3': 263, 'roi_p1': 0.04759065393663096, 'roi_p2': 0.1488819964638463, 'roi_p3': 0.4102801822104605, 'stoploss': -0.05394588767607611}, # noqa: E501
|
||||
'params_details': {'buy': {'mfi-value': 25, 'fastd-value': 16, 'adx-value': 29, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 98, 'sell-fastd-value': 72, 'sell-adx-value': 51, 'sell-rsi-value': 82, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.6067528326109377, 263: 0.19647265040047726, 796: 0.04759065393663096, 1685: 0}, 'stoploss': {'stoploss': -0.05394588767607611}}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 621, 'wins': 320, 'draws': 0, 'losses': 301, 'profit_mean': -0.043883302093397747, 'profit_median': -0.012222, 'profit_total': -0.13639474, 'profit_total_abs': -272.515306, 'holding_avg': timedelta(minutes=1691.207729468599)}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 621, 'wins': 320, 'draws': 0, 'losses': 301, 'profit_mean': -0.043883302093397747, 'profit_median': -0.012222, 'profit_total': -0.13639474, 'profit_total_abs': -272.515306, 'max_drawdown': 0.25, 'max_drawdown_abs': -272.515306, 'holding_avg': timedelta(minutes=1691.207729468599)}, # noqa: E501
|
||||
'results_explanation': ' 621 trades. Avg profit -0.44%. Total profit -0.13639474 BTC (-272.52Σ%). Avg duration 1691.2 min.', # noqa: E501
|
||||
'total_profit': -0.13639474,
|
||||
'current_epoch': 3,
|
||||
|
@ -2063,7 +2063,7 @@ def saved_hyperopt_results():
|
|||
'loss': 0.22195522184191518,
|
||||
'params_dict': {'mfi-value': 17, 'fastd-value': 21, 'adx-value': 38, 'rsi-value': 33, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 87, 'sell-fastd-value': 82, 'sell-adx-value': 78, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 1269, 'roi_t2': 601, 'roi_t3': 444, 'roi_p1': 0.07280999507931168, 'roi_p2': 0.08946698095898986, 'roi_p3': 0.1454876733325284, 'stoploss': -0.18181041180901014}, # noqa: E501
|
||||
'params_details': {'buy': {'mfi-value': 17, 'fastd-value': 21, 'adx-value': 38, 'rsi-value': 33, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 87, 'sell-fastd-value': 82, 'sell-adx-value': 78, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.3077646493708299, 444: 0.16227697603830155, 1045: 0.07280999507931168, 2314: 0}, 'stoploss': {'stoploss': -0.18181041180901014}}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 14, 'wins': 6, 'draws': 0, 'losses': 8, 'profit_mean': -0.003539515, 'profit_median': -0.012222, 'profit_total': -0.002480140000000001, 'profit_total_abs': -4.955321, 'holding_avg': timedelta(minutes=3402.8571428571427)}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 14, 'wins': 6, 'draws': 0, 'losses': 8, 'profit_mean': -0.003539515, 'profit_median': -0.012222, 'profit_total': -0.002480140000000001, 'profit_total_abs': -4.955321, 'max_drawdown': 0.34, 'max_drawdown_abs': -4.955321, 'holding_avg': timedelta(minutes=3402.8571428571427)}, # noqa: E501
|
||||
'results_explanation': ' 14 trades. Avg profit -0.35%. Total profit -0.00248014 BTC ( -4.96Σ%). Avg duration 3402.9 min.', # noqa: E501
|
||||
'total_profit': -0.002480140000000001,
|
||||
'current_epoch': 5,
|
||||
|
@ -2073,7 +2073,7 @@ def saved_hyperopt_results():
|
|||
'loss': 0.545315889154162,
|
||||
'params_dict': {'mfi-value': 22, 'fastd-value': 43, 'adx-value': 46, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'bb_lower', 'sell-mfi-value': 87, 'sell-fastd-value': 65, 'sell-adx-value': 94, 'sell-rsi-value': 63, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 319, 'roi_t2': 556, 'roi_t3': 216, 'roi_p1': 0.06251955472249589, 'roi_p2': 0.11659519602202795, 'roi_p3': 0.0953744132197762, 'stoploss': -0.024551752215582423}, # noqa: E501
|
||||
'params_details': {'buy': {'mfi-value': 22, 'fastd-value': 43, 'adx-value': 46, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'bb_lower'}, 'sell': {'sell-mfi-value': 87, 'sell-fastd-value': 65, 'sell-adx-value': 94, 'sell-rsi-value': 63, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.2744891639643, 216: 0.17911475074452382, 772: 0.06251955472249589, 1091: 0}, 'stoploss': {'stoploss': -0.024551752215582423}}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 39, 'wins': 20, 'draws': 0, 'losses': 19, 'profit_mean': -0.0021400679487179478, 'profit_median': -0.012222, 'profit_total': -0.0041773, 'profit_total_abs': -8.346264999999997, 'holding_avg': timedelta(minutes=636.9230769230769)}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 39, 'wins': 20, 'draws': 0, 'losses': 19, 'profit_mean': -0.0021400679487179478, 'profit_median': -0.012222, 'profit_total': -0.0041773, 'profit_total_abs': -8.346264999999997, 'max_drawdown': 0.45, 'max_drawdown_abs': -4.955321, 'holding_avg': timedelta(minutes=636.9230769230769)}, # noqa: E501
|
||||
'results_explanation': ' 39 trades. Avg profit -0.21%. Total profit -0.00417730 BTC ( -8.35Σ%). Avg duration 636.9 min.', # noqa: E501
|
||||
'total_profit': -0.0041773,
|
||||
'current_epoch': 6,
|
||||
|
@ -2085,7 +2085,7 @@ def saved_hyperopt_results():
|
|||
'params_details': {
|
||||
'buy': {'mfi-value': 13, 'fastd-value': 41, 'adx-value': 21, 'rsi-value': 29, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower'}, 'sell': {'sell-mfi-value': 99, 'sell-fastd-value': 60, 'sell-adx-value': 81, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.4837436938134452, 145: 0.10853310701097472, 765: 0.0586919200378493, 1536: 0}, # noqa: E501
|
||||
'stoploss': {'stoploss': -0.14613268022709905}}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 318, 'wins': 100, 'draws': 0, 'losses': 218, 'profit_mean': -0.0039833954716981146, 'profit_median': -0.012222, 'profit_total': -0.06339929, 'profit_total_abs': -126.67197600000004, 'holding_avg': timedelta(minutes=3140.377358490566)}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 318, 'wins': 100, 'draws': 0, 'losses': 218, 'profit_mean': -0.0039833954716981146, 'profit_median': -0.012222, 'profit_total': -0.06339929, 'profit_total_abs': -126.67197600000004, 'max_drawdown': 0.50, 'max_drawdown_abs': -200.955321, 'holding_avg': timedelta(minutes=3140.377358490566)}, # noqa: E501
|
||||
'results_explanation': ' 318 trades. Avg profit -0.40%. Total profit -0.06339929 BTC (-126.67Σ%). Avg duration 3140.4 min.', # noqa: E501
|
||||
'total_profit': -0.06339929,
|
||||
'current_epoch': 7,
|
||||
|
@ -2095,7 +2095,7 @@ def saved_hyperopt_results():
|
|||
'loss': 20.0, # noqa: E501
|
||||
'params_dict': {'mfi-value': 24, 'fastd-value': 43, 'adx-value': 33, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'sar_reversal', 'sell-mfi-value': 89, 'sell-fastd-value': 74, 'sell-adx-value': 70, 'sell-rsi-value': 70, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': False, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal', 'roi_t1': 1149, 'roi_t2': 375, 'roi_t3': 289, 'roi_p1': 0.05571820757172588, 'roi_p2': 0.0606240398618907, 'roi_p3': 0.1729012220156157, 'stoploss': -0.1588514289110401}, # noqa: E501
|
||||
'params_details': {'buy': {'mfi-value': 24, 'fastd-value': 43, 'adx-value': 33, 'rsi-value': 20, 'mfi-enabled': False, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': True, 'trigger': 'sar_reversal'}, 'sell': {'sell-mfi-value': 89, 'sell-fastd-value': 74, 'sell-adx-value': 70, 'sell-rsi-value': 70, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': False, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal'}, 'roi': {0: 0.2892434694492323, 289: 0.11634224743361658, 664: 0.05571820757172588, 1813: 0}, 'stoploss': {'stoploss': -0.1588514289110401}}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 1, 'wins': 0, 'draws': 1, 'losses': 0, 'profit_mean': 0.0, 'profit_median': 0.0, 'profit_total': 0.0, 'profit_total_abs': 0.0, 'holding_avg': timedelta(minutes=5340.0)}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 1, 'wins': 0, 'draws': 1, 'losses': 0, 'profit_mean': 0.0, 'profit_median': 0.0, 'profit_total': 0.0, 'profit_total_abs': 0.0, 'max_drawdown': 0.0, 'max_drawdown_abs': 0.52, 'holding_avg': timedelta(minutes=5340.0)}, # noqa: E501
|
||||
'results_explanation': ' 1 trades. Avg profit 0.00%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration 5340.0 min.', # noqa: E501
|
||||
'total_profit': 0.0,
|
||||
'current_epoch': 8,
|
||||
|
@ -2105,7 +2105,7 @@ def saved_hyperopt_results():
|
|||
'loss': 2.4731817780991223,
|
||||
'params_dict': {'mfi-value': 22, 'fastd-value': 20, 'adx-value': 29, 'rsi-value': 40, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'sar_reversal', 'sell-mfi-value': 97, 'sell-fastd-value': 65, 'sell-adx-value': 81, 'sell-rsi-value': 64, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1012, 'roi_t2': 584, 'roi_t3': 422, 'roi_p1': 0.036764323603472565, 'roi_p2': 0.10335480573205287, 'roi_p3': 0.10322347377503042, 'stoploss': -0.2780610808108503}, # noqa: E501
|
||||
'params_details': {'buy': {'mfi-value': 22, 'fastd-value': 20, 'adx-value': 29, 'rsi-value': 40, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'sar_reversal'}, 'sell': {'sell-mfi-value': 97, 'sell-fastd-value': 65, 'sell-adx-value': 81, 'sell-rsi-value': 64, 'sell-mfi-enabled': True, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.2433426031105559, 422: 0.14011912933552545, 1006: 0.036764323603472565, 2018: 0}, 'stoploss': {'stoploss': -0.2780610808108503}}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 229, 'wins': 150, 'draws': 0, 'losses': 79, 'profit_mean': -0.0038433433624454144, 'profit_median': -0.012222, 'profit_total': -0.044050070000000004, 'profit_total_abs': -88.01256299999999, 'holding_avg': timedelta(minutes=6505.676855895196)}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 229, 'wins': 150, 'draws': 0, 'losses': 79, 'profit_mean': -0.0038433433624454144, 'profit_median': -0.012222, 'profit_total': -0.044050070000000004, 'profit_total_abs': -88.01256299999999, 'max_drawdown': 0.41, 'max_drawdown_abs': -150.955321, 'holding_avg': timedelta(minutes=6505.676855895196)}, # noqa: E501
|
||||
'results_explanation': ' 229 trades. Avg profit -0.38%. Total profit -0.04405007 BTC ( -88.01Σ%). Avg duration 6505.7 min.', # noqa: E501
|
||||
'total_profit': -0.044050070000000004, # noqa: E501
|
||||
'current_epoch': 9,
|
||||
|
@ -2115,7 +2115,7 @@ def saved_hyperopt_results():
|
|||
'loss': -0.2604606005845212, # noqa: E501
|
||||
'params_dict': {'mfi-value': 23, 'fastd-value': 24, 'adx-value': 22, 'rsi-value': 24, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 97, 'sell-fastd-value': 70, 'sell-adx-value': 64, 'sell-rsi-value': 80, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal', 'roi_t1': 792, 'roi_t2': 464, 'roi_t3': 215, 'roi_p1': 0.04594053535385903, 'roi_p2': 0.09623192684243963, 'roi_p3': 0.04428219070850663, 'stoploss': -0.16992287161634415}, # noqa: E501
|
||||
'params_details': {'buy': {'mfi-value': 23, 'fastd-value': 24, 'adx-value': 22, 'rsi-value': 24, 'mfi-enabled': False, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 97, 'sell-fastd-value': 70, 'sell-adx-value': 64, 'sell-rsi-value': 80, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-sar_reversal'}, 'roi': {0: 0.18645465290480528, 215: 0.14217246219629864, 679: 0.04594053535385903, 1471: 0}, 'stoploss': {'stoploss': -0.16992287161634415}}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 4, 'wins': 0, 'draws': 0, 'losses': 4, 'profit_mean': 0.001080385, 'profit_median': -0.012222, 'profit_total': 0.00021629, 'profit_total_abs': 0.432154, 'holding_avg': timedelta(minutes=2850.0)}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 4, 'wins': 0, 'draws': 0, 'losses': 4, 'profit_mean': 0.001080385, 'profit_median': -0.012222, 'profit_total': 0.00021629, 'profit_total_abs': 0.432154, 'max_drawdown': 0.13, 'max_drawdown_abs': -4.955321, 'holding_avg': timedelta(minutes=2850.0)}, # noqa: E501
|
||||
'results_explanation': ' 4 trades. Avg profit 0.11%. Total profit 0.00021629 BTC ( 0.43Σ%). Avg duration 2850.0 min.', # noqa: E501
|
||||
'total_profit': 0.00021629,
|
||||
'current_epoch': 10,
|
||||
|
@ -2126,7 +2126,7 @@ def saved_hyperopt_results():
|
|||
'params_dict': {'mfi-value': 20, 'fastd-value': 32, 'adx-value': 49, 'rsi-value': 23, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower', 'sell-mfi-value': 75, 'sell-fastd-value': 56, 'sell-adx-value': 61, 'sell-rsi-value': 62, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 579, 'roi_t2': 614, 'roi_t3': 273, 'roi_p1': 0.05307643172744114, 'roi_p2': 0.1352282078262871, 'roi_p3': 0.1913307406325751, 'stoploss': -0.25728526022513887}, # noqa: E501
|
||||
'params_details': {'buy': {'mfi-value': 20, 'fastd-value': 32, 'adx-value': 49, 'rsi-value': 23, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': False, 'rsi-enabled': False, 'trigger': 'bb_lower'}, 'sell': {'sell-mfi-value': 75, 'sell-fastd-value': 56, 'sell-adx-value': 61, 'sell-rsi-value': 62, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-macd_cross_signal'}, 'roi': {0: 0.3796353801863034, 273: 0.18830463955372825, 887: 0.05307643172744114, 1466: 0}, 'stoploss': {'stoploss': -0.25728526022513887}}, # noqa: E501
|
||||
# New Hyperopt mode!
|
||||
'results_metrics': {'total_trades': 117, 'wins': 67, 'draws': 0, 'losses': 50, 'profit_mean': -0.012698609145299145, 'profit_median': -0.012222, 'profit_total': -0.07436117, 'profit_total_abs': -148.573727, 'holding_avg': timedelta(minutes=4282.5641025641025)}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 117, 'wins': 67, 'draws': 0, 'losses': 50, 'profit_mean': -0.012698609145299145, 'profit_median': -0.012222, 'profit_total': -0.07436117, 'profit_total_abs': -148.573727, 'max_drawdown': 0.52, 'max_drawdown_abs': -224.955321, 'holding_avg': timedelta(minutes=4282.5641025641025)}, # noqa: E501
|
||||
'results_explanation': ' 117 trades. Avg profit -1.27%. Total profit -0.07436117 BTC (-148.57Σ%). Avg duration 4282.6 min.', # noqa: E501
|
||||
'total_profit': -0.07436117,
|
||||
'current_epoch': 11,
|
||||
|
@ -2136,7 +2136,7 @@ def saved_hyperopt_results():
|
|||
'loss': 100000,
|
||||
'params_dict': {'mfi-value': 10, 'fastd-value': 36, 'adx-value': 31, 'rsi-value': 22, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'sar_reversal', 'sell-mfi-value': 80, 'sell-fastd-value': 71, 'sell-adx-value': 60, 'sell-rsi-value': 85, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper', 'roi_t1': 1156, 'roi_t2': 581, 'roi_t3': 408, 'roi_p1': 0.06860454019988212, 'roi_p2': 0.12473718444931989, 'roi_p3': 0.2896360635226823, 'stoploss': -0.30889015124682806}, # noqa: E501
|
||||
'params_details': {'buy': {'mfi-value': 10, 'fastd-value': 36, 'adx-value': 31, 'rsi-value': 22, 'mfi-enabled': True, 'fastd-enabled': True, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'sar_reversal'}, 'sell': {'sell-mfi-value': 80, 'sell-fastd-value': 71, 'sell-adx-value': 60, 'sell-rsi-value': 85, 'sell-mfi-enabled': False, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.4829777881718843, 408: 0.19334172464920202, 989: 0.06860454019988212, 2145: 0}, 'stoploss': {'stoploss': -0.30889015124682806}}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 0, 'wins': 0, 'draws': 0, 'losses': 0, 'profit_mean': None, 'profit_median': None, 'profit_total': 0, 'profit_total_abs': 0.0, 'holding_avg': timedelta()}, # noqa: E501
|
||||
'results_metrics': {'total_trades': 0, 'wins': 0, 'draws': 0, 'losses': 0, 'profit_mean': None, 'profit_median': None, 'profit_total': 0, 'profit_total_abs': 0.0, 'max_drawdown': 0.0, 'max_drawdown_abs': 0.0, 'holding_avg': timedelta()}, # noqa: E501
|
||||
'results_explanation': ' 0 trades. Avg profit nan%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration nan min.', # noqa: E501
|
||||
'total_profit': 0,
|
||||
'current_epoch': 12,
|
||||
|
|
|
@ -8,14 +8,14 @@ from pandas import DataFrame, DateOffset, Timestamp, to_datetime
|
|||
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.constants import LAST_BT_RESULT_FN
|
||||
from freqtrade.data.btanalysis import (BT_DATA_COLUMNS, BT_DATA_COLUMNS_MID, BT_DATA_COLUMNS_OLD,
|
||||
analyze_trade_parallelism, calculate_csum,
|
||||
from freqtrade.data.btanalysis import (BT_DATA_COLUMNS, analyze_trade_parallelism, calculate_csum,
|
||||
calculate_market_change, calculate_max_drawdown,
|
||||
calculate_underwater, combine_dataframes_with_mean,
|
||||
create_cum_profit, extract_trades_of_period,
|
||||
get_latest_backtest_filename, get_latest_hyperopt_file,
|
||||
load_backtest_data, load_trades, load_trades_from_db)
|
||||
from freqtrade.data.history import load_data, load_pair_history
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from tests.conftest import create_mock_trades
|
||||
from tests.conftest_trades import MOCK_TRADE_COUNT
|
||||
|
||||
|
@ -51,20 +51,14 @@ def test_get_latest_hyperopt_file(testdatadir, mocker):
|
|||
assert res == testdatadir.parent / "hyperopt_results.pickle"
|
||||
|
||||
|
||||
def test_load_backtest_data_old_format(testdatadir):
|
||||
def test_load_backtest_data_old_format(testdatadir, mocker):
|
||||
|
||||
filename = testdatadir / "backtest-result_test.json"
|
||||
bt_data = load_backtest_data(filename)
|
||||
assert isinstance(bt_data, DataFrame)
|
||||
assert list(bt_data.columns) == BT_DATA_COLUMNS_OLD + ['profit_abs', 'profit_ratio']
|
||||
assert len(bt_data) == 179
|
||||
filename = testdatadir / "backtest-result_test222.json"
|
||||
mocker.patch('freqtrade.data.btanalysis.load_backtest_stats', return_value=[])
|
||||
|
||||
# Test loading from string (must yield same result)
|
||||
bt_data2 = load_backtest_data(str(filename))
|
||||
assert bt_data.equals(bt_data2)
|
||||
|
||||
with pytest.raises(ValueError, match=r"File .* does not exist\."):
|
||||
load_backtest_data(str("filename") + "nofile")
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"Backtest-results with only trades data are no longer supported."):
|
||||
load_backtest_data(filename)
|
||||
|
||||
|
||||
def test_load_backtest_data_new_format(testdatadir):
|
||||
|
@ -72,7 +66,7 @@ def test_load_backtest_data_new_format(testdatadir):
|
|||
filename = testdatadir / "backtest-result_new.json"
|
||||
bt_data = load_backtest_data(filename)
|
||||
assert isinstance(bt_data, DataFrame)
|
||||
assert set(bt_data.columns) == set(BT_DATA_COLUMNS_MID)
|
||||
assert set(bt_data.columns) == set(BT_DATA_COLUMNS + ['close_timestamp', 'open_timestamp'])
|
||||
assert len(bt_data) == 179
|
||||
|
||||
# Test loading from string (must yield same result)
|
||||
|
@ -96,7 +90,7 @@ def test_load_backtest_data_multi(testdatadir):
|
|||
for strategy in ('StrategyTestV2', 'TestStrategy'):
|
||||
bt_data = load_backtest_data(filename, strategy=strategy)
|
||||
assert isinstance(bt_data, DataFrame)
|
||||
assert set(bt_data.columns) == set(BT_DATA_COLUMNS_MID)
|
||||
assert set(bt_data.columns) == set(BT_DATA_COLUMNS + ['close_timestamp', 'open_timestamp'])
|
||||
assert len(bt_data) == 179
|
||||
|
||||
# Test loading from string (must yield same result)
|
||||
|
@ -167,8 +161,8 @@ def test_extract_trades_of_period(testdatadir):
|
|||
assert trades1.iloc[-1].close_date == Arrow(2017, 11, 14, 15, 25, 0).datetime
|
||||
|
||||
|
||||
def test_analyze_trade_parallelism(default_conf, mocker, testdatadir):
|
||||
filename = testdatadir / "backtest-result_test.json"
|
||||
def test_analyze_trade_parallelism(testdatadir):
|
||||
filename = testdatadir / "backtest-result_new.json"
|
||||
bt_data = load_backtest_data(filename)
|
||||
|
||||
res = analyze_trade_parallelism(bt_data, "5m")
|
||||
|
@ -242,7 +236,7 @@ def test_combine_dataframes_with_mean_no_data(testdatadir):
|
|||
|
||||
|
||||
def test_create_cum_profit(testdatadir):
|
||||
filename = testdatadir / "backtest-result_test.json"
|
||||
filename = testdatadir / "backtest-result_new.json"
|
||||
bt_data = load_backtest_data(filename)
|
||||
timerange = TimeRange.parse_timerange("20180110-20180112")
|
||||
|
||||
|
@ -258,7 +252,7 @@ def test_create_cum_profit(testdatadir):
|
|||
|
||||
|
||||
def test_create_cum_profit1(testdatadir):
|
||||
filename = testdatadir / "backtest-result_test.json"
|
||||
filename = testdatadir / "backtest-result_new.json"
|
||||
bt_data = load_backtest_data(filename)
|
||||
# Move close-time to "off" the candle, to make sure the logic still works
|
||||
bt_data.loc[:, 'close_date'] = bt_data.loc[:, 'close_date'] + DateOffset(seconds=20)
|
||||
|
@ -280,30 +274,31 @@ def test_create_cum_profit1(testdatadir):
|
|||
|
||||
|
||||
def test_calculate_max_drawdown(testdatadir):
|
||||
filename = testdatadir / "backtest-result_test.json"
|
||||
filename = testdatadir / "backtest-result_new.json"
|
||||
bt_data = load_backtest_data(filename)
|
||||
drawdown, hdate, lowdate, hval, lval = calculate_max_drawdown(bt_data)
|
||||
_, hdate, lowdate, hval, lval, drawdown = calculate_max_drawdown(
|
||||
bt_data, value_col="profit_abs")
|
||||
assert isinstance(drawdown, float)
|
||||
assert pytest.approx(drawdown) == 0.21142322
|
||||
assert pytest.approx(drawdown) == 0.12071099
|
||||
assert isinstance(hdate, Timestamp)
|
||||
assert isinstance(lowdate, Timestamp)
|
||||
assert isinstance(hval, float)
|
||||
assert isinstance(lval, float)
|
||||
assert hdate == Timestamp('2018-01-24 14:25:00', tz='UTC')
|
||||
assert lowdate == Timestamp('2018-01-30 04:45:00', tz='UTC')
|
||||
assert hdate == Timestamp('2018-01-25 01:30:00', tz='UTC')
|
||||
assert lowdate == Timestamp('2018-01-25 03:50:00', tz='UTC')
|
||||
|
||||
underwater = calculate_underwater(bt_data)
|
||||
assert isinstance(underwater, DataFrame)
|
||||
|
||||
with pytest.raises(ValueError, match='Trade dataframe empty.'):
|
||||
drawdown, hdate, lowdate, hval, lval = calculate_max_drawdown(DataFrame())
|
||||
calculate_max_drawdown(DataFrame())
|
||||
|
||||
with pytest.raises(ValueError, match='Trade dataframe empty.'):
|
||||
calculate_underwater(DataFrame())
|
||||
|
||||
|
||||
def test_calculate_csum(testdatadir):
|
||||
filename = testdatadir / "backtest-result_test.json"
|
||||
filename = testdatadir / "backtest-result_new.json"
|
||||
bt_data = load_backtest_data(filename)
|
||||
csum_min, csum_max = calculate_csum(bt_data)
|
||||
|
||||
|
@ -331,12 +326,13 @@ def test_calculate_max_drawdown2():
|
|||
# sort by profit and reset index
|
||||
df = df.sort_values('profit').reset_index(drop=True)
|
||||
df1 = df.copy()
|
||||
drawdown, hdate, ldate, hval, lval = calculate_max_drawdown(
|
||||
drawdown, hdate, ldate, hval, lval, drawdown_rel = calculate_max_drawdown(
|
||||
df, date_col='open_date', value_col='profit')
|
||||
# Ensure df has not been altered.
|
||||
assert df.equals(df1)
|
||||
|
||||
assert isinstance(drawdown, float)
|
||||
assert isinstance(drawdown_rel, float)
|
||||
# High must be before low
|
||||
assert hdate < ldate
|
||||
# High value must be higher than low value
|
||||
|
|
|
@ -52,6 +52,12 @@ EXCHANGES = {
|
|||
'hasQuoteVolume': True,
|
||||
'timeframe': '5m',
|
||||
},
|
||||
'bitvavo': {
|
||||
'pair': 'BTC/EUR',
|
||||
'stake_currency': 'EUR',
|
||||
'hasQuoteVolume': True,
|
||||
'timeframe': '5m',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
@ -68,6 +74,8 @@ def exchange_conf():
|
|||
@pytest.fixture(params=EXCHANGES, scope="class")
|
||||
def exchange(request, exchange_conf):
|
||||
exchange_conf['exchange']['name'] = request.param
|
||||
exchange_conf['stake_currency'] = EXCHANGES[request.param].get(
|
||||
'stake_currency', exchange_conf['stake_currency'])
|
||||
exchange = ExchangeResolver.load_exchange(request.param, exchange_conf, validate=True)
|
||||
|
||||
yield exchange, request.param
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# pragma pylint: disable=missing-docstring,W0212,C0103
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
from unittest.mock import ANY, MagicMock
|
||||
|
||||
|
@ -22,6 +22,29 @@ from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
|
|||
patched_configuration_load_config_file)
|
||||
|
||||
|
||||
def generate_result_metrics():
|
||||
return {
|
||||
'trade_count': 1,
|
||||
'total_trades': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 0.01,
|
||||
'duration': 20.0,
|
||||
'wins': 1,
|
||||
'draws': 0,
|
||||
'losses': 0,
|
||||
'profit_mean': 0.01,
|
||||
'profit_total_abs': 0.001,
|
||||
'profit_total': 0.01,
|
||||
'holding_avg': timedelta(minutes=20),
|
||||
'max_drawdown': 0.001,
|
||||
'max_drawdown_abs': 0.001,
|
||||
'loss': 0.001,
|
||||
'is_initial_point': 0.001,
|
||||
'is_best': 1,
|
||||
}
|
||||
|
||||
|
||||
def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, caplog) -> None:
|
||||
patched_configuration_load_config_file(mocker, default_conf)
|
||||
|
||||
|
@ -168,8 +191,8 @@ def test_start_no_hyperopt_allowed(mocker, hyperopt_conf, caplog) -> None:
|
|||
start_hyperopt(pargs)
|
||||
|
||||
|
||||
def test_start_no_data(mocker, hyperopt_conf) -> None:
|
||||
hyperopt_conf['user_data_dir'] = Path("tests")
|
||||
def test_start_no_data(mocker, hyperopt_conf, tmpdir) -> None:
|
||||
hyperopt_conf['user_data_dir'] = Path(tmpdir)
|
||||
patched_configuration_load_config_file(mocker, hyperopt_conf)
|
||||
mocker.patch('freqtrade.data.history.load_pair_history', MagicMock(return_value=pd.DataFrame))
|
||||
mocker.patch(
|
||||
|
@ -178,7 +201,6 @@ def test_start_no_data(mocker, hyperopt_conf) -> None:
|
|||
)
|
||||
|
||||
patch_exchange(mocker)
|
||||
# TODO: migrate to strategy-based hyperopt
|
||||
args = [
|
||||
'hyperopt',
|
||||
'--config', 'config.json',
|
||||
|
@ -222,14 +244,7 @@ def test_log_results_if_loss_improves(hyperopt, capsys) -> None:
|
|||
hyperopt.print_results(
|
||||
{
|
||||
'loss': 1,
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
},
|
||||
'results_metrics': generate_result_metrics(),
|
||||
'total_profit': 0,
|
||||
'current_epoch': 2, # This starts from 1 (in a human-friendly manner)
|
||||
'is_initial_point': False,
|
||||
|
@ -238,7 +253,7 @@ def test_log_results_if_loss_improves(hyperopt, capsys) -> None:
|
|||
)
|
||||
out, err = capsys.readouterr()
|
||||
assert all(x in out
|
||||
for x in ["Best", "2/2", " 1", "0.10%", "0.00100000 BTC (1.00%)", "20.0 m"])
|
||||
for x in ["Best", "2/2", " 1", "0.10%", "0.00100000 BTC (1.00%)", "00:20:00"])
|
||||
|
||||
|
||||
def test_no_log_if_loss_does_not_improve(hyperopt, caplog) -> None:
|
||||
|
@ -295,14 +310,7 @@ def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None:
|
|||
MagicMock(return_value=[{
|
||||
'loss': 1, 'results_explanation': 'foo result',
|
||||
'params': {'buy': {}, 'sell': {}, 'roi': {}, 'stoploss': 0.0},
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
},
|
||||
'results_metrics': generate_result_metrics(),
|
||||
}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
|
@ -359,7 +367,7 @@ def test_hyperopt_format_results(hyperopt):
|
|||
'backtest_start_time': 1619718665,
|
||||
'backtest_end_time': 1619718665,
|
||||
}
|
||||
results_metrics = generate_strategy_stats({'XRP/BTC': None}, '', bt_result,
|
||||
results_metrics = generate_strategy_stats(['XRP/BTC'], '', bt_result,
|
||||
Arrow(2017, 11, 14, 19, 32, 00),
|
||||
Arrow(2017, 12, 14, 19, 32, 00), market_change=0)
|
||||
|
||||
|
@ -528,14 +536,7 @@ def test_print_json_spaces_all(mocker, hyperopt_conf, capsys) -> None:
|
|||
'roi': {}, 'stoploss': {'stoploss': None},
|
||||
'trailing': {'trailing_stop': None}
|
||||
},
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
}
|
||||
'results_metrics': generate_result_metrics(),
|
||||
}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
|
@ -584,14 +585,7 @@ def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None:
|
|||
'sell': {'sell-mfi-value': None},
|
||||
'roi': {}, 'stoploss': {'stoploss': None}
|
||||
},
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
}
|
||||
'results_metrics': generate_result_metrics(),
|
||||
}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
|
@ -629,14 +623,7 @@ def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
|
|||
MagicMock(return_value=[{
|
||||
'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
||||
'params_details': {'roi': {}, 'stoploss': {'stoploss': None}},
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
}
|
||||
'results_metrics': generate_result_metrics(),
|
||||
}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
|
@ -676,14 +663,7 @@ def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> Non
|
|||
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
||||
MagicMock(return_value=[{
|
||||
'loss': 1, 'results_explanation': 'foo result', 'params': {'stoploss': 0.0},
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
}
|
||||
'results_metrics': generate_result_metrics(),
|
||||
}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
|
@ -756,14 +736,7 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
|
|||
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
||||
MagicMock(return_value=[{
|
||||
'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
}
|
||||
'results_metrics': generate_result_metrics(),
|
||||
}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
|
@ -805,14 +778,7 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
|
|||
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
||||
MagicMock(return_value=[{
|
||||
'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
||||
'results_metrics':
|
||||
{
|
||||
'trade_count': 1,
|
||||
'avg_profit': 0.1,
|
||||
'total_profit': 0.001,
|
||||
'profit': 1.0,
|
||||
'duration': 20.0
|
||||
}
|
||||
'results_metrics': generate_result_metrics(),
|
||||
}])
|
||||
)
|
||||
patch_exchange(mocker)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import datetime
|
||||
import re
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
|
@ -49,7 +48,7 @@ def test_text_table_bt_results():
|
|||
' 0:20:00 | 2 0 1 66.7 |'
|
||||
)
|
||||
|
||||
pair_results = generate_pair_metrics(data={'ETH/BTC': {}}, stake_currency='BTC',
|
||||
pair_results = generate_pair_metrics(['ETH/BTC'], stake_currency='BTC',
|
||||
starting_balance=4, results=results)
|
||||
assert text_table_bt_results(pair_results, stake_currency='BTC') == result_str
|
||||
|
||||
|
@ -103,7 +102,7 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmpdir):
|
|||
assert strat_stats['backtest_end'] == max_date.strftime(DATETIME_PRINT_FORMAT)
|
||||
assert strat_stats['total_trades'] == len(results['DefStrat']['results'])
|
||||
# Above sample had no loosing trade
|
||||
assert strat_stats['max_drawdown'] == 0.0
|
||||
assert strat_stats['max_drawdown_account'] == 0.0
|
||||
|
||||
# Retry with losing trade
|
||||
results = {'DefStrat': {
|
||||
|
@ -143,7 +142,7 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmpdir):
|
|||
assert 'strategy_comparison' in stats
|
||||
strat_stats = stats['strategy']['DefStrat']
|
||||
|
||||
assert strat_stats['max_drawdown'] == 0.013803
|
||||
assert pytest.approx(strat_stats['max_drawdown_account']) == 1.399999e-08
|
||||
assert strat_stats['drawdown_start'] == '2017-11-14 22:10:00'
|
||||
assert strat_stats['drawdown_end'] == '2017-11-14 22:43:00'
|
||||
assert strat_stats['drawdown_end_ts'] == 1510699380000
|
||||
|
@ -165,7 +164,7 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmpdir):
|
|||
filename1 = Path(tmpdir / last_fn)
|
||||
assert filename1.is_file()
|
||||
content = filename1.read_text()
|
||||
assert 'max_drawdown' in content
|
||||
assert 'max_drawdown_account' in content
|
||||
assert 'strategy' in content
|
||||
assert 'pairlist' in content
|
||||
|
||||
|
@ -208,7 +207,7 @@ def test_generate_pair_metrics():
|
|||
}
|
||||
)
|
||||
|
||||
pair_results = generate_pair_metrics(data={'ETH/BTC': {}}, stake_currency='BTC',
|
||||
pair_results = generate_pair_metrics(['ETH/BTC'], stake_currency='BTC',
|
||||
starting_balance=2, results=results)
|
||||
assert isinstance(pair_results, list)
|
||||
assert len(pair_results) == 2
|
||||
|
@ -227,9 +226,9 @@ def test_generate_daily_stats(testdatadir):
|
|||
assert isinstance(res, dict)
|
||||
assert round(res['backtest_best_day'], 4) == 0.1796
|
||||
assert round(res['backtest_worst_day'], 4) == -0.1468
|
||||
assert res['winning_days'] == 14
|
||||
assert res['draw_days'] == 4
|
||||
assert res['losing_days'] == 3
|
||||
assert res['winning_days'] == 19
|
||||
assert res['draw_days'] == 0
|
||||
assert res['losing_days'] == 2
|
||||
|
||||
# Select empty dataframe!
|
||||
res = generate_daily_stats(bt_data.loc[bt_data['open_date'] == '2000-01-01', :])
|
||||
|
@ -324,51 +323,25 @@ def test_generate_sell_reason_stats():
|
|||
assert stop_result['profit_mean_pct'] == round(stop_result['profit_mean'] * 100, 2)
|
||||
|
||||
|
||||
def test_text_table_strategy(default_conf):
|
||||
default_conf['max_open_trades'] = 2
|
||||
default_conf['dry_run_wallet'] = 3
|
||||
results = {}
|
||||
date = datetime.datetime(year=2020, month=1, day=1, hour=12, minute=30)
|
||||
delta = datetime.timedelta(days=1)
|
||||
results['TestStrategy1'] = {'results': pd.DataFrame(
|
||||
{
|
||||
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
|
||||
'close_date': [date, date + delta, date + delta * 2],
|
||||
'profit_ratio': [0.1, 0.2, 0.3],
|
||||
'profit_abs': [0.2, 0.4, 0.5],
|
||||
'trade_duration': [10, 30, 10],
|
||||
'wins': [2, 0, 0],
|
||||
'draws': [0, 0, 0],
|
||||
'losses': [0, 0, 1],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
|
||||
}
|
||||
), 'config': default_conf}
|
||||
results['TestStrategy2'] = {'results': pd.DataFrame(
|
||||
{
|
||||
'pair': ['LTC/BTC', 'LTC/BTC', 'LTC/BTC'],
|
||||
'close_date': [date, date + delta, date + delta * 2],
|
||||
'profit_ratio': [0.4, 0.2, 0.3],
|
||||
'profit_abs': [0.4, 0.4, 0.5],
|
||||
'trade_duration': [15, 30, 15],
|
||||
'wins': [4, 1, 0],
|
||||
'draws': [0, 0, 0],
|
||||
'losses': [0, 0, 1],
|
||||
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
|
||||
}
|
||||
), 'config': default_conf}
|
||||
def test_text_table_strategy(testdatadir):
|
||||
filename = testdatadir / "backtest-result_multistrat.json"
|
||||
bt_res_data = load_backtest_stats(filename)
|
||||
|
||||
bt_res_data_comparison = bt_res_data.pop('strategy_comparison')
|
||||
|
||||
result_str = (
|
||||
'| Strategy | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC |'
|
||||
'| Strategy | Buys | Avg Profit % | Cum Profit % | Tot Profit BTC |'
|
||||
' Tot Profit % | Avg Duration | Win Draw Loss Win% | Drawdown |\n'
|
||||
'|---------------+--------+----------------+----------------+------------------+'
|
||||
'|----------------+--------+----------------+----------------+------------------+'
|
||||
'----------------+----------------+-------------------------+-----------------------|\n'
|
||||
'| TestStrategy1 | 3 | 20.00 | 60.00 | 1.10000000 |'
|
||||
' 36.67 | 0:17:00 | 3 0 0 100 | 0.00000000 BTC 0.00% |\n'
|
||||
'| TestStrategy2 | 3 | 30.00 | 90.00 | 1.30000000 |'
|
||||
' 43.33 | 0:20:00 | 3 0 0 100 | 0.00000000 BTC 0.00% |'
|
||||
'| StrategyTestV2 | 179 | 0.08 | 14.39 | 0.02608550 |'
|
||||
' 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |\n'
|
||||
'| TestStrategy | 179 | 0.08 | 14.39 | 0.02608550 |'
|
||||
' 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |'
|
||||
)
|
||||
|
||||
strategy_results = generate_strategy_comparison(all_results=results)
|
||||
strategy_results = generate_strategy_comparison(bt_stats=bt_res_data['strategy'])
|
||||
assert strategy_results == bt_res_data_comparison
|
||||
assert text_table_strategy(strategy_results, 'BTC') == result_str
|
||||
|
||||
|
||||
|
|
|
@ -565,36 +565,41 @@ def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, shitcoinmarkets, t
|
|||
assert log_has_re(r'^Removed .* from whitelist, because volatility.*$', caplog)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pairlists,base_currency,volumefilter_result", [
|
||||
@pytest.mark.parametrize("pairlists,base_currency,exchange,volumefilter_result", [
|
||||
# default refresh of 1800 to small for daily candle lookback
|
||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||
"lookback_days": 1}],
|
||||
"BTC", "default_refresh_too_short"), # OperationalException expected
|
||||
"BTC", "binance", "default_refresh_too_short"), # OperationalException expected
|
||||
# ambigous configuration with lookback days and period
|
||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||
"lookback_days": 1, "lookback_period": 1}],
|
||||
"BTC", "lookback_days_and_period"), # OperationalException expected
|
||||
"BTC", "binance", "lookback_days_and_period"), # OperationalException expected
|
||||
# negative lookback period
|
||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||
"lookback_timeframe": "1d", "lookback_period": -1}],
|
||||
"BTC", "lookback_period_negative"), # OperationalException expected
|
||||
"BTC", "binance", "lookback_period_negative"), # OperationalException expected
|
||||
# lookback range exceedes exchange limit
|
||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||
"lookback_timeframe": "1m", "lookback_period": 2000, "refresh_period": 3600}],
|
||||
"BTC", 'lookback_exceeds_exchange_request_size'), # OperationalException expected
|
||||
"BTC", "binance", "lookback_exceeds_exchange_request_size"), # OperationalException expected
|
||||
# expecing pairs as given
|
||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||
"lookback_timeframe": "1d", "lookback_period": 1, "refresh_period": 86400}],
|
||||
"BTC", ['HOT/BTC', 'LTC/BTC', 'ETH/BTC', 'TKN/BTC', 'XRP/BTC']),
|
||||
"BTC", "binance", ['LTC/BTC', 'ETH/BTC', 'TKN/BTC', 'XRP/BTC', 'HOT/BTC']),
|
||||
# expecting pairs from default tickers, because 1h candles are not available
|
||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||
"lookback_timeframe": "1h", "lookback_period": 2, "refresh_period": 3600}],
|
||||
"BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'HOT/BTC', 'FUEL/BTC']),
|
||||
"BTC", "binance", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'HOT/BTC', 'FUEL/BTC']),
|
||||
# ftx data is already in Quote currency, therefore won't require conversion
|
||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||
"lookback_timeframe": "1d", "lookback_period": 1, "refresh_period": 86400}],
|
||||
"BTC", "ftx", ['HOT/BTC', 'LTC/BTC', 'ETH/BTC', 'TKN/BTC', 'XRP/BTC']),
|
||||
])
|
||||
def test_VolumePairList_range(mocker, whitelist_conf, shitcoinmarkets, tickers, ohlcv_history,
|
||||
pairlists, base_currency, volumefilter_result, caplog) -> None:
|
||||
pairlists, base_currency, exchange, volumefilter_result) -> None:
|
||||
whitelist_conf['pairlists'] = pairlists
|
||||
whitelist_conf['stake_currency'] = base_currency
|
||||
whitelist_conf['exchange']['name'] = exchange
|
||||
|
||||
ohlcv_history_high_vola = ohlcv_history.copy()
|
||||
ohlcv_history_high_vola.loc[ohlcv_history_high_vola.index == 1, 'close'] = 0.00090
|
||||
|
@ -603,9 +608,14 @@ def test_VolumePairList_range(mocker, whitelist_conf, shitcoinmarkets, tickers,
|
|||
ohlcv_history_medium_volume = ohlcv_history.copy()
|
||||
ohlcv_history_medium_volume.loc[ohlcv_history_medium_volume.index == 2, 'volume'] = 5
|
||||
|
||||
# create candles for high volume with all candles high volume
|
||||
# create candles for high volume with all candles high volume, but very low price.
|
||||
ohlcv_history_high_volume = ohlcv_history.copy()
|
||||
ohlcv_history_high_volume.loc[:, 'volume'] = 10
|
||||
ohlcv_history_high_volume.loc[:, 'low'] = ohlcv_history_high_volume.loc[:, 'low'] * 0.01
|
||||
ohlcv_history_high_volume.loc[:, 'high'] = ohlcv_history_high_volume.loc[:, 'high'] * 0.01
|
||||
ohlcv_history_high_volume.loc[:, 'close'] = ohlcv_history_high_volume.loc[:, 'close'] * 0.01
|
||||
|
||||
mocker.patch('freqtrade.exchange.ftx.Ftx.market_is_tradable', return_value=True)
|
||||
|
||||
ohlcv_data = {
|
||||
('ETH/BTC', '1d'): ohlcv_history,
|
||||
|
|
|
@ -45,7 +45,7 @@ def test_init_plotscript(default_conf, mocker, testdatadir):
|
|||
default_conf['trade_source'] = "file"
|
||||
default_conf['timeframe'] = "5m"
|
||||
default_conf["datadir"] = testdatadir
|
||||
default_conf['exportfilename'] = testdatadir / "backtest-result_test.json"
|
||||
default_conf['exportfilename'] = testdatadir / "backtest-result_new.json"
|
||||
supported_markets = ["TRX/BTC", "ADA/BTC"]
|
||||
ret = init_plotscript(default_conf, supported_markets)
|
||||
assert "ohlcv" in ret
|
||||
|
@ -157,7 +157,7 @@ def test_plot_trades(testdatadir, caplog):
|
|||
assert fig == fig1
|
||||
assert log_has("No trades found.", caplog)
|
||||
pair = "ADA/BTC"
|
||||
filename = testdatadir / "backtest-result_test.json"
|
||||
filename = testdatadir / "backtest-result_new.json"
|
||||
trades = load_backtest_data(filename)
|
||||
trades = trades.loc[trades['pair'] == pair]
|
||||
|
||||
|
@ -294,7 +294,7 @@ def test_generate_plot_file(mocker, caplog):
|
|||
|
||||
|
||||
def test_add_profit(testdatadir):
|
||||
filename = testdatadir / "backtest-result_test.json"
|
||||
filename = testdatadir / "backtest-result_new.json"
|
||||
bt_data = load_backtest_data(filename)
|
||||
timerange = TimeRange.parse_timerange("20180110-20180112")
|
||||
|
||||
|
@ -314,7 +314,7 @@ def test_add_profit(testdatadir):
|
|||
|
||||
|
||||
def test_generate_profit_graph(testdatadir):
|
||||
filename = testdatadir / "backtest-result_test.json"
|
||||
filename = testdatadir / "backtest-result_new.json"
|
||||
trades = load_backtest_data(filename)
|
||||
timerange = TimeRange.parse_timerange("20180110-20180112")
|
||||
pairs = ["TRX/BTC", "XLM/BTC"]
|
||||
|
@ -343,7 +343,7 @@ def test_generate_profit_graph(testdatadir):
|
|||
|
||||
profit = find_trace_in_fig_data(figure.data, "Profit")
|
||||
assert isinstance(profit, go.Scatter)
|
||||
drawdown = find_trace_in_fig_data(figure.data, "Max drawdown 10.45%")
|
||||
drawdown = find_trace_in_fig_data(figure.data, "Max drawdown 35.69%")
|
||||
assert isinstance(drawdown, go.Scatter)
|
||||
parallel = find_trace_in_fig_data(figure.data, "Parallel trades")
|
||||
assert isinstance(parallel, go.Scatter)
|
||||
|
@ -381,7 +381,7 @@ def test_load_and_plot_trades(default_conf, mocker, caplog, testdatadir):
|
|||
|
||||
default_conf['trade_source'] = 'file'
|
||||
default_conf["datadir"] = testdatadir
|
||||
default_conf['exportfilename'] = testdatadir / "backtest-result_test.json"
|
||||
default_conf['exportfilename'] = testdatadir / "backtest-result_new.json"
|
||||
default_conf['indicators1'] = ["sma5", "ema10"]
|
||||
default_conf['indicators2'] = ["macd"]
|
||||
default_conf['pairs'] = ["ETH/BTC", "LTC/BTC"]
|
||||
|
@ -452,7 +452,7 @@ def test_plot_profit(default_conf, mocker, testdatadir):
|
|||
match=r"No trades found, cannot generate Profit-plot.*"):
|
||||
plot_profit(default_conf)
|
||||
|
||||
default_conf['exportfilename'] = testdatadir / "backtest-result_test.json"
|
||||
default_conf['exportfilename'] = testdatadir / "backtest-result_new.json"
|
||||
|
||||
plot_profit(default_conf)
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue