Merge branch 'freqtrade:develop' into dca
This commit is contained in:
commit
8e424f7c73
|
@ -196,7 +196,7 @@ jobs:
|
||||||
uses: rjstone/discord-webhook-notify@v1
|
uses: rjstone/discord-webhook-notify@v1
|
||||||
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
||||||
with:
|
with:
|
||||||
severity: error
|
severity: info
|
||||||
details: Test Succeeded!
|
details: Test Succeeded!
|
||||||
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
|
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}')"
|
$pyv = python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"
|
||||||
|
|
||||||
if ($pyv -eq '3.7') {
|
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') {
|
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') {
|
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') {
|
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 -r requirements-dev.txt
|
||||||
pip install -e .
|
pip install -e .
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
"sell_profit_only": false,
|
"sell_profit_only": false,
|
||||||
"sell_profit_offset": 0.0,
|
"sell_profit_offset": 0.0,
|
||||||
"ignore_roi_if_buy_signal": false,
|
"ignore_roi_if_buy_signal": false,
|
||||||
|
"ignore_buying_expired_candle_after": 300,
|
||||||
"minimal_roi": {
|
"minimal_roi": {
|
||||||
"40": 0.0,
|
"40": 0.0,
|
||||||
"30": 0.01,
|
"30": 0.01,
|
||||||
|
|
|
@ -312,7 +312,7 @@ A backtesting result will look like that:
|
||||||
| | |
|
| | |
|
||||||
| Min balance | 0.00945123 BTC |
|
| Min balance | 0.00945123 BTC |
|
||||||
| Max balance | 0.01846651 BTC |
|
| Max balance | 0.01846651 BTC |
|
||||||
| Drawdown | 50.63% |
|
| Drawdown (Account) | 13.33% |
|
||||||
| Drawdown | 0.0015 BTC |
|
| Drawdown | 0.0015 BTC |
|
||||||
| Drawdown high | 0.0013 BTC |
|
| Drawdown high | 0.0013 BTC |
|
||||||
| Drawdown low | -0.0002 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 |
|
| Min balance | 0.00945123 BTC |
|
||||||
| Max balance | 0.01846651 BTC |
|
| Max balance | 0.01846651 BTC |
|
||||||
| Drawdown | 50.63% |
|
| Drawdown (Account) | 13.33% |
|
||||||
| Drawdown | 0.0015 BTC |
|
| Drawdown | 0.0015 BTC |
|
||||||
| Drawdown high | 0.0013 BTC |
|
| Drawdown high | 0.0013 BTC |
|
||||||
| Drawdown low | -0.0002 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.
|
- `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.
|
- `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.
|
- `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 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).
|
- `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.
|
- `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
|
### The **--dynamic-whitelist** command line option
|
||||||
|
|
||||||
This command line option was deprecated in 2018 and removed freqtrade 2019.6-dev (develop branch)
|
This command line option was deprecated in 2018 and removed freqtrade 2019.6-dev (develop branch) and in freqtrade 2019.7.
|
||||||
and in freqtrade 2019.7.
|
Please refer to [pairlists](plugins.md#pairlists-and-pairlist-handlers) instead.
|
||||||
|
|
||||||
### the `--live` command line option
|
### the `--live` command line option
|
||||||
|
|
||||||
|
|
|
@ -222,9 +222,9 @@ should be rewritten to
|
||||||
```python
|
```python
|
||||||
frames = [dataframe]
|
frames = [dataframe]
|
||||||
for val in self.buy_ema_short.range:
|
for val in self.buy_ema_short.range:
|
||||||
frames.append({
|
frames.append(DataFrame({
|
||||||
f'ema_short_{val}': ta.EMA(dataframe, timeperiod=val)
|
f'ema_short_{val}': ta.EMA(dataframe, timeperiod=val)
|
||||||
})
|
}))
|
||||||
|
|
||||||
# Append columns to existing dataframe
|
# Append columns to existing dataframe
|
||||||
merged_frame = pd.concat(frames, axis=1)
|
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).
|
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.
|
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.
|
Other versions must be downloaded from the above link.
|
||||||
|
|
|
@ -9,21 +9,13 @@ import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
from freqtrade.constants import LAST_BT_RESULT_FN
|
from freqtrade.constants import LAST_BT_RESULT_FN
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.misc import json_load
|
from freqtrade.misc import json_load
|
||||||
from freqtrade.persistence import LocalTrade, Trade, init_db
|
from freqtrade.persistence import LocalTrade, Trade, init_db
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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
|
# Newest format
|
||||||
BT_DATA_COLUMNS = ['pair', 'stake_amount', 'amount', 'open_date', 'close_date',
|
BT_DATA_COLUMNS = ['pair', 'stake_amount', 'amount', 'open_date', 'close_date',
|
||||||
'open_rate', 'close_rate',
|
'open_rate', 'close_rate',
|
||||||
|
@ -167,23 +159,9 @@ def load_backtest_data(filename: Union[Path, str], strategy: Optional[str] = Non
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# old format - only with lists.
|
# old format - only with lists.
|
||||||
df = pd.DataFrame(data, columns=BT_DATA_COLUMNS_OLD)
|
raise OperationalException(
|
||||||
if not df.empty:
|
"Backtest-results with only trades data are no longer supported.")
|
||||||
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']
|
|
||||||
if not df.empty:
|
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)
|
df = df.sort_values("open_date").reset_index(drop=True)
|
||||||
return df
|
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',
|
def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_date',
|
||||||
value_col: str = 'profit_ratio'
|
value_col: str = 'profit_abs', starting_balance: float = 0
|
||||||
) -> Tuple[float, pd.Timestamp, pd.Timestamp, float, float]:
|
) -> Tuple[float, pd.Timestamp, pd.Timestamp, float, float, float]:
|
||||||
"""
|
"""
|
||||||
Calculate max drawdown and the corresponding close dates
|
Calculate max drawdown and the corresponding close dates
|
||||||
:param trades: DataFrame containing trades (requires columns close_date and profit_ratio)
|
: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 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')
|
:param value_col: Column in DataFrame to use for values (defaults to 'profit_abs')
|
||||||
:return: Tuple (float, highdate, lowdate, highvalue, lowvalue) with absolute max drawdown,
|
:param starting_balance: Portfolio starting balance - properly calculate relative drawdown.
|
||||||
high and low time and high and low value.
|
: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.
|
:raise: ValueError if trade-dataframe was found empty.
|
||||||
"""
|
"""
|
||||||
if len(trades) == 0:
|
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_val = max_drawdown_df.loc[max_drawdown_df.iloc[:idxmin]
|
||||||
['high_value'].idxmax(), 'cumulative']
|
['high_value'].idxmax(), 'cumulative']
|
||||||
low_val = max_drawdown_df.loc[idxmin, '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]:
|
def calculate_csum(trades: pd.DataFrame, starting_balance: float = 0) -> Tuple[float, float]:
|
||||||
|
|
|
@ -67,6 +67,8 @@ class Exchange:
|
||||||
"ohlcv_params": {},
|
"ohlcv_params": {},
|
||||||
"ohlcv_candle_limit": 500,
|
"ohlcv_candle_limit": 500,
|
||||||
"ohlcv_partial_candle": True,
|
"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": "time", # Possible are "time" or "id"
|
||||||
"trades_pagination_arg": "since",
|
"trades_pagination_arg": "since",
|
||||||
"l2_limit_range": None,
|
"l2_limit_range": None,
|
||||||
|
@ -656,7 +658,8 @@ class Exchange:
|
||||||
max_slippage_val = rate * ((1 + slippage) if side == 'buy' else (1 - slippage))
|
max_slippage_val = rate * ((1 + slippage) if side == 'buy' else (1 - slippage))
|
||||||
|
|
||||||
remaining_amount = amount
|
remaining_amount = amount
|
||||||
filled_amount = 0
|
filled_amount = 0.0
|
||||||
|
book_entry_price = 0.0
|
||||||
for book_entry in ob[ob_type]:
|
for book_entry in ob[ob_type]:
|
||||||
book_entry_price = book_entry[0]
|
book_entry_price = book_entry[0]
|
||||||
book_entry_coin_volume = book_entry[1]
|
book_entry_coin_volume = book_entry[1]
|
||||||
|
|
|
@ -19,6 +19,7 @@ class Ftx(Exchange):
|
||||||
_ft_has: Dict = {
|
_ft_has: Dict = {
|
||||||
"stoploss_on_exchange": True,
|
"stoploss_on_exchange": True,
|
||||||
"ohlcv_candle_limit": 1500,
|
"ohlcv_candle_limit": 1500,
|
||||||
|
"ohlcv_volume_currency": "quote",
|
||||||
}
|
}
|
||||||
|
|
||||||
def market_is_tradable(self, market: Dict[str, Any]) -> bool:
|
def market_is_tradable(self, market: Dict[str, Any]) -> bool:
|
||||||
|
|
|
@ -21,6 +21,7 @@ class Gateio(Exchange):
|
||||||
|
|
||||||
_ft_has: Dict = {
|
_ft_has: Dict = {
|
||||||
"ohlcv_candle_limit": 1000,
|
"ohlcv_candle_limit": 1000,
|
||||||
|
"ohlcv_volume_currency": "quote",
|
||||||
}
|
}
|
||||||
|
|
||||||
_headers = {'X-Gate-Channel-Id': 'freqtrade'}
|
_headers = {'X-Gate-Channel-Id': 'freqtrade'}
|
||||||
|
|
|
@ -14,5 +14,5 @@ class Okex(Exchange):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_ft_has: Dict = {
|
_ft_has: Dict = {
|
||||||
"ohlcv_candle_limit": 100,
|
"ohlcv_candle_limit": 300,
|
||||||
}
|
}
|
||||||
|
|
|
@ -270,8 +270,8 @@ class Backtesting:
|
||||||
df_analyzed = self.strategy.advise_sell(
|
df_analyzed = self.strategy.advise_sell(
|
||||||
self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair}).copy()
|
self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair}).copy()
|
||||||
# Trim startup period from analyzed dataframe
|
# Trim startup period from analyzed dataframe
|
||||||
df_analyzed = trim_dataframe(df_analyzed, self.timerange,
|
df_analyzed = processed[pair] = pair_data = trim_dataframe(
|
||||||
startup_candles=self.required_startup)
|
df_analyzed, self.timerange, startup_candles=self.required_startup)
|
||||||
# To avoid using data from future, we use buy/sell signals shifted
|
# To avoid using data from future, we use buy/sell signals shifted
|
||||||
# from the previous candle
|
# from the previous candle
|
||||||
df_analyzed.loc[:, 'buy'] = df_analyzed.loc[:, 'buy'].shift(1)
|
df_analyzed.loc[:, 'buy'] = df_analyzed.loc[:, 'buy'].shift(1)
|
||||||
|
@ -287,9 +287,6 @@ class Backtesting:
|
||||||
# Convert from Pandas to list for performance reasons
|
# Convert from Pandas to list for performance reasons
|
||||||
# (Looping Pandas is slow.)
|
# (Looping Pandas is slow.)
|
||||||
data[pair] = df_analyzed[headers].values.tolist()
|
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
|
return data
|
||||||
|
|
||||||
def _get_close_rate(self, sell_row: Tuple, trade: LocalTrade, sell: SellCheckTuple,
|
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)
|
return self._get_sell_trade_entry_for_candle(trade, sell_row)
|
||||||
detail_data.loc[:, 'buy'] = sell_row[BUY_IDX]
|
detail_data.loc[:, 'buy'] = sell_row[BUY_IDX]
|
||||||
detail_data.loc[:, 'sell'] = sell_row[SELL_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():
|
for det_row in detail_data[headers].values.tolist():
|
||||||
res = self._get_sell_trade_entry_for_candle(trade, det_row)
|
res = self._get_sell_trade_entry_for_candle(trade, det_row)
|
||||||
if res:
|
if res:
|
||||||
|
|
|
@ -76,6 +76,7 @@ class Hyperopt:
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
self.backtesting = Backtesting(self.config)
|
self.backtesting = Backtesting(self.config)
|
||||||
|
self.pairlist = self.backtesting.pairlists.whitelist
|
||||||
|
|
||||||
if not self.config.get('hyperopt'):
|
if not self.config.get('hyperopt'):
|
||||||
self.custom_hyperopt = HyperOptAuto(self.config)
|
self.custom_hyperopt = HyperOptAuto(self.config)
|
||||||
|
@ -332,7 +333,7 @@ class Hyperopt:
|
||||||
params_details = self._get_params_details(params_dict)
|
params_details = self._get_params_details(params_dict)
|
||||||
|
|
||||||
strat_stats = generate_strategy_stats(
|
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
|
backtesting_results, min_date, max_date, market_change=0
|
||||||
)
|
)
|
||||||
results_explanation = HyperoptTools.format_results_explanation_string(
|
results_explanation = HyperoptTools.format_results_explanation_string(
|
||||||
|
|
|
@ -47,10 +47,9 @@ class CalmarHyperOptLoss(IHyperOptLoss):
|
||||||
|
|
||||||
# calculate max drawdown
|
# calculate max drawdown
|
||||||
try:
|
try:
|
||||||
_, _, _, high_val, low_val = calculate_max_drawdown(
|
_, _, _, _, _, max_drawdown = calculate_max_drawdown(
|
||||||
results, value_col="profit_abs"
|
results, value_col="profit_abs"
|
||||||
)
|
)
|
||||||
max_drawdown = (high_val - low_val) / high_val
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
max_drawdown = 0
|
max_drawdown = 0
|
||||||
|
|
||||||
|
|
|
@ -299,8 +299,7 @@ class HyperoptTools():
|
||||||
f"Objective: {results['loss']:.5f}")
|
f"Objective: {results['loss']:.5f}")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def prepare_trials_columns(trials: pd.DataFrame, legacy_mode: bool,
|
def prepare_trials_columns(trials: pd.DataFrame, has_drawdown: bool) -> pd.DataFrame:
|
||||||
has_drawdown: bool) -> pd.DataFrame:
|
|
||||||
trials['Best'] = ''
|
trials['Best'] = ''
|
||||||
|
|
||||||
if 'results_metrics.winsdrawslosses' not in trials.columns:
|
if 'results_metrics.winsdrawslosses' not in trials.columns:
|
||||||
|
@ -309,33 +308,26 @@ class HyperoptTools():
|
||||||
|
|
||||||
if not has_drawdown:
|
if not has_drawdown:
|
||||||
# Ensure compatibility with older versions of hyperopt results
|
# Ensure compatibility with older versions of hyperopt results
|
||||||
trials['results_metrics.max_drawdown_abs'] = None
|
trials['results_metrics.max_drawdown_account'] = None
|
||||||
trials['results_metrics.max_drawdown'] = None
|
|
||||||
|
|
||||||
if not legacy_mode:
|
# New mode, using backtest result for metrics
|
||||||
# New mode, using backtest result for metrics
|
trials['results_metrics.winsdrawslosses'] = trials.apply(
|
||||||
trials['results_metrics.winsdrawslosses'] = trials.apply(
|
lambda x: f"{x['results_metrics.wins']} {x['results_metrics.draws']:>4} "
|
||||||
lambda x: f"{x['results_metrics.wins']} {x['results_metrics.draws']:>4} "
|
f"{x['results_metrics.losses']:>4}", axis=1)
|
||||||
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']]
|
|
||||||
|
|
||||||
else:
|
trials = trials[['Best', 'current_epoch', 'results_metrics.total_trades',
|
||||||
# Legacy mode
|
'results_metrics.winsdrawslosses',
|
||||||
trials = trials[['Best', 'current_epoch', 'results_metrics.trade_count',
|
'results_metrics.profit_mean', 'results_metrics.profit_total_abs',
|
||||||
'results_metrics.winsdrawslosses', 'results_metrics.avg_profit',
|
'results_metrics.profit_total', 'results_metrics.holding_avg',
|
||||||
'results_metrics.total_profit', 'results_metrics.profit',
|
'results_metrics.max_drawdown',
|
||||||
'results_metrics.duration', 'results_metrics.max_drawdown',
|
'results_metrics.max_drawdown_account', 'results_metrics.max_drawdown_abs',
|
||||||
'results_metrics.max_drawdown_abs', 'loss', 'is_initial_point',
|
'loss', 'is_initial_point', 'is_best']]
|
||||||
'is_best']]
|
|
||||||
|
|
||||||
trials.columns = ['Best', 'Epoch', 'Trades', ' Win Draw Loss', 'Avg profit',
|
trials.columns = [
|
||||||
'Total profit', 'Profit', 'Avg duration', 'Max Drawdown',
|
'Best', 'Epoch', 'Trades', ' Win Draw Loss', 'Avg profit',
|
||||||
'max_drawdown_abs', 'Objective', 'is_initial_point', 'is_best']
|
'Total profit', 'Profit', 'Avg duration', 'max_drawdown', 'max_drawdown_account',
|
||||||
|
'max_drawdown_abs', 'Objective', 'is_initial_point', 'is_best'
|
||||||
|
]
|
||||||
|
|
||||||
return trials
|
return trials
|
||||||
|
|
||||||
|
@ -351,10 +343,9 @@ class HyperoptTools():
|
||||||
tabulate.PRESERVE_WHITESPACE = True
|
tabulate.PRESERVE_WHITESPACE = True
|
||||||
trials = json_normalize(results, max_level=1)
|
trials = json_normalize(results, max_level=1)
|
||||||
|
|
||||||
legacy_mode = 'results_metrics.total_trades' not in trials
|
has_account_drawdown = 'results_metrics.max_drawdown_account' in trials.columns
|
||||||
has_drawdown = 'results_metrics.max_drawdown_abs' 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['is_profit'] = False
|
||||||
trials.loc[trials['is_initial_point'], 'Best'] = '* '
|
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['is_initial_point'] & trials['is_best'], 'Best'] = '* Best'
|
||||||
trials.loc[trials['Total profit'] > 0, 'is_profit'] = True
|
trials.loc[trials['Total profit'] > 0, 'is_profit'] = True
|
||||||
trials['Trades'] = trials['Trades'].astype(str)
|
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(
|
trials['Epoch'] = trials['Epoch'].apply(
|
||||||
lambda x: '{}/{}'.format(str(x).rjust(len(str(total_epochs)), ' '), total_epochs)
|
lambda x: '{}/{}'.format(str(x).rjust(len(str(total_epochs)), ' '), total_epochs)
|
||||||
)
|
)
|
||||||
trials['Avg profit'] = trials['Avg profit'].apply(
|
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(
|
trials['Avg duration'] = trials['Avg duration'].apply(
|
||||||
lambda x: f'{x:,.1f} m'.rjust(7, ' ') if isinstance(x, float) else f"{x}"
|
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']
|
stake_currency = config['stake_currency']
|
||||||
|
|
||||||
if has_drawdown:
|
trials[f"Max Drawdown{' (Acct)' if has_account_drawdown else ''}"] = trials.apply(
|
||||||
trials['Max Drawdown'] = trials.apply(
|
lambda x: "{} {}".format(
|
||||||
lambda x: '{} {}'.format(
|
round_coin_value(x['max_drawdown_abs'], stake_currency),
|
||||||
round_coin_value(x['max_drawdown_abs'], stake_currency),
|
(f"({x['max_drawdown_account']:,.2%})"
|
||||||
'({:,.2f}%)'.format(x['Max Drawdown'] * perc_multi).rjust(10, ' ')
|
if has_account_drawdown
|
||||||
).rjust(25 + len(stake_currency))
|
else f"({x['max_drawdown']:,.2%})"
|
||||||
if x['Max Drawdown'] != 0.0 else '--'.rjust(25 + len(stake_currency)),
|
).rjust(10, ' ')
|
||||||
axis=1
|
).rjust(25 + len(stake_currency))
|
||||||
)
|
if x['max_drawdown'] != 0.0 or x['max_drawdown_account'] != 0.0
|
||||||
else:
|
else '--'.rjust(25 + len(stake_currency)),
|
||||||
trials = trials.drop(columns=['Max Drawdown'])
|
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(
|
trials['Profit'] = trials.apply(
|
||||||
lambda x: '{} {}'.format(
|
lambda x: '{} {}'.format(
|
||||||
round_coin_value(x['Total profit'], stake_currency),
|
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))
|
).rjust(25+len(stake_currency))
|
||||||
if x['Total profit'] != 0.0 else '--'.rjust(25+len(stake_currency)),
|
if x['Total profit'] != 0.0 else '--'.rjust(25+len(stake_currency)),
|
||||||
axis=1
|
axis=1
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
from copy import deepcopy
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Union
|
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]:
|
results: DataFrame, skip_nan: bool = False) -> List[Dict]:
|
||||||
"""
|
"""
|
||||||
Generates and returns a list for the given backtest data and the results dataframe
|
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 stake_currency: stake-currency - used to correctly name headers
|
||||||
:param starting_balance: Starting balance
|
:param starting_balance: Starting balance
|
||||||
:param results: Dataframe containing the backtest results
|
: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 = []
|
tabular_data = []
|
||||||
|
|
||||||
for pair in data:
|
for pair in pairlist:
|
||||||
result = results[results['pair'] == pair]
|
result = results[results['pair'] == pair]
|
||||||
if skip_nan and result['profit_abs'].isnull().all():
|
if skip_nan and result['profit_abs'].isnull().all():
|
||||||
continue
|
continue
|
||||||
|
@ -194,29 +195,21 @@ def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List
|
||||||
return tabular_data
|
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
|
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
|
:return: List of Dicts containing the metrics per Strategy
|
||||||
"""
|
"""
|
||||||
|
|
||||||
tabular_data = []
|
tabular_data = []
|
||||||
for strategy, results in all_results.items():
|
for strategy, result in bt_stats.items():
|
||||||
tabular_data.append(_generate_result_line(
|
tabular_data.append(deepcopy(result['results_per_pair'][-1]))
|
||||||
results['results'], results['config']['dry_run_wallet'], strategy)
|
# Update "key" to strategy (results_per_pair has it as "Total").
|
||||||
)
|
tabular_data[-1]['key'] = strategy
|
||||||
try:
|
tabular_data[-1]['max_drawdown_account'] = result['max_drawdown_account']
|
||||||
max_drawdown_per, _, _, _, _ = calculate_max_drawdown(results['results'],
|
tabular_data[-1]['max_drawdown_abs'] = round_coin_value(
|
||||||
value_col='profit_ratio')
|
result['max_drawdown_abs'], result['stake_currency'], False)
|
||||||
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)
|
|
||||||
return tabular_data
|
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,
|
strategy: str,
|
||||||
content: Dict[str, Any],
|
content: Dict[str, Any],
|
||||||
min_date: datetime, max_date: datetime,
|
min_date: datetime, max_date: datetime,
|
||||||
market_change: float
|
market_change: float
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
:param btdata: Backtest data
|
:param pairlist: List of pairs to backtest
|
||||||
:param strategy: Strategy name
|
:param strategy: Strategy name
|
||||||
:param content: Backtest result data in the format:
|
:param content: Backtest result data in the format:
|
||||||
{'results: results, 'config: config}}.
|
{'results: results, 'config: config}}.
|
||||||
|
@ -372,11 +365,11 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
||||||
if not isinstance(results, DataFrame):
|
if not isinstance(results, DataFrame):
|
||||||
return {}
|
return {}
|
||||||
config = content['config']
|
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']
|
starting_balance = config['dry_run_wallet']
|
||||||
stake_currency = config['stake_currency']
|
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,
|
starting_balance=starting_balance,
|
||||||
results=results, skip_nan=False)
|
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,
|
sell_reason_stats = generate_sell_reason_stats(max_open_trades=max_open_trades,
|
||||||
results=results)
|
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,
|
starting_balance=starting_balance,
|
||||||
results=results.loc[results['is_open']],
|
results=results.loc[results['is_open']],
|
||||||
skip_nan=True)
|
skip_nan=True)
|
||||||
|
@ -429,7 +422,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
||||||
|
|
||||||
'trades_per_day': round(len(results) / backtest_days, 2),
|
'trades_per_day': round(len(results) / backtest_days, 2),
|
||||||
'market_change': market_change,
|
'market_change': market_change,
|
||||||
'pairlist': list(btdata.keys()),
|
'pairlist': pairlist,
|
||||||
'stake_amount': config['stake_amount'],
|
'stake_amount': config['stake_amount'],
|
||||||
'stake_currency': config['stake_currency'],
|
'stake_currency': config['stake_currency'],
|
||||||
'stake_currency_decimals': decimals_per_coin(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:
|
try:
|
||||||
max_drawdown, _, _, _, _ = calculate_max_drawdown(
|
max_drawdown_legacy, _, _, _, _, _ = calculate_max_drawdown(
|
||||||
results, value_col='profit_ratio')
|
results, value_col='profit_ratio')
|
||||||
drawdown_abs, drawdown_start, drawdown_end, high_val, low_val = calculate_max_drawdown(
|
(drawdown_abs, drawdown_start, drawdown_end, high_val, low_val,
|
||||||
results, value_col='profit_abs')
|
max_drawdown) = calculate_max_drawdown(
|
||||||
|
results, value_col='profit_abs', starting_balance=starting_balance)
|
||||||
strat_stats.update({
|
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,
|
'max_drawdown_abs': drawdown_abs,
|
||||||
'drawdown_start': drawdown_start.strftime(DATETIME_PRINT_FORMAT),
|
'drawdown_start': drawdown_start.strftime(DATETIME_PRINT_FORMAT),
|
||||||
'drawdown_start_ts': drawdown_start.timestamp() * 1000,
|
'drawdown_start_ts': drawdown_start.timestamp() * 1000,
|
||||||
|
@ -487,6 +482,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
||||||
except ValueError:
|
except ValueError:
|
||||||
strat_stats.update({
|
strat_stats.update({
|
||||||
'max_drawdown': 0.0,
|
'max_drawdown': 0.0,
|
||||||
|
'max_drawdown_account': 0.0,
|
||||||
'max_drawdown_abs': 0.0,
|
'max_drawdown_abs': 0.0,
|
||||||
'max_drawdown_low': 0.0,
|
'max_drawdown_low': 0.0,
|
||||||
'max_drawdown_high': 0.0,
|
'max_drawdown_high': 0.0,
|
||||||
|
@ -515,13 +511,13 @@ def generate_backtest_stats(btdata: Dict[str, DataFrame],
|
||||||
"""
|
"""
|
||||||
result: Dict[str, Any] = {'strategy': {}}
|
result: Dict[str, Any] = {'strategy': {}}
|
||||||
market_change = calculate_market_change(btdata, 'close')
|
market_change = calculate_market_change(btdata, 'close')
|
||||||
|
pairlist = list(btdata.keys())
|
||||||
for strategy, content in all_results.items():
|
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)
|
min_date, max_date, market_change=market_change)
|
||||||
result['strategy'][strategy] = strat_stats
|
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
|
result['strategy_comparison'] = strategy_results
|
||||||
|
|
||||||
|
@ -646,7 +642,12 @@ def text_table_strategy(strategy_results, stake_currency: str) -> str:
|
||||||
headers.append('Drawdown')
|
headers.append('Drawdown')
|
||||||
|
|
||||||
# Align drawdown string on the center two space separator.
|
# 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_abs = max([len(t['max_drawdown_abs']) for t in strategy_results])
|
||||||
dd_pad_per = max([len(dd) for dd in drawdown])
|
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}}%'
|
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'],
|
('Max balance', round_coin_value(strat_results['csum_max'],
|
||||||
strat_results['stake_currency'])),
|
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'],
|
('Drawdown', round_coin_value(strat_results['max_drawdown_abs'],
|
||||||
strat_results['stake_currency'])),
|
strat_results['stake_currency'])),
|
||||||
('Drawdown high', round_coin_value(strat_results['max_drawdown_high'],
|
('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
|
Add scatter points indicating max drawdown
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
max_drawdown, highdate, lowdate, _, _ = calculate_max_drawdown(trades)
|
_, highdate, lowdate, _, _, max_drawdown = calculate_max_drawdown(trades)
|
||||||
|
|
||||||
drawdown = go.Scatter(
|
drawdown = go.Scatter(
|
||||||
x=[highdate, lowdate],
|
x=[highdate, lowdate],
|
||||||
|
|
|
@ -4,7 +4,6 @@ Volume PairList provider
|
||||||
Provides dynamic pair list based on trade volumes
|
Provides dynamic pair list based on trade volumes
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from functools import partial
|
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
|
@ -120,10 +119,17 @@ class VolumePairList(IPairList):
|
||||||
else:
|
else:
|
||||||
# Use fresh pairlist
|
# Use fresh pairlist
|
||||||
# Check if pair quote currency equals to the stake currency.
|
# 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 = [
|
filtered_tickers = [
|
||||||
v for k, v in tickers.items()
|
v for k, v in tickers.items()
|
||||||
if (self._exchange.get_pair_quote_currency(k) == self._stake_currency
|
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 = [s['symbol'] for s in filtered_tickers]
|
||||||
|
|
||||||
pairlist = self.filter_pairlist(pairlist, tickers)
|
pairlist = self.filter_pairlist(pairlist, tickers)
|
||||||
|
@ -178,12 +184,16 @@ class VolumePairList(IPairList):
|
||||||
] if (p['symbol'], self._lookback_timeframe) in candles else None
|
] if (p['symbol'], self._lookback_timeframe) in candles else None
|
||||||
# in case of candle data calculate typical price and quoteVolume for candle
|
# in case of candle data calculate typical price and quoteVolume for candle
|
||||||
if pair_candles is not None and not pair_candles.empty:
|
if pair_candles is not None and not pair_candles.empty:
|
||||||
pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low']
|
if self._exchange._ft_has["ohlcv_volume_currency"] == "base":
|
||||||
+ pair_candles['close']) / 3
|
pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low']
|
||||||
pair_candles['quoteVolume'] = (
|
+ pair_candles['close']) / 3
|
||||||
pair_candles['volume'] * pair_candles['typical_price']
|
|
||||||
)
|
|
||||||
|
|
||||||
|
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
|
# ensure that a rolling sum over the lookback_period is built
|
||||||
# if pair_candles contains more candles than lookback_period
|
# if pair_candles contains more candles than lookback_period
|
||||||
quoteVolume = (pair_candles['quoteVolume']
|
quoteVolume = (pair_candles['quoteVolume']
|
||||||
|
@ -204,7 +214,7 @@ class VolumePairList(IPairList):
|
||||||
|
|
||||||
# Validate whitelist to only have active market pairs
|
# Validate whitelist to only have active market pairs
|
||||||
pairs = self._whitelist_for_active_markets([s['symbol'] for s in sorted_tickers])
|
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
|
# Limit pairlist to the requested number of pairs
|
||||||
pairs = pairs[:self._number_pairs]
|
pairs = pairs[:self._number_pairs]
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,8 @@ class MaxDrawdown(IProtection):
|
||||||
|
|
||||||
# Drawdown is always positive
|
# Drawdown is always positive
|
||||||
try:
|
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:
|
except ValueError:
|
||||||
return False, None, None
|
return False, None, None
|
||||||
|
|
||||||
|
|
|
@ -7,5 +7,4 @@ scikit-learn==1.0.2
|
||||||
scikit-optimize==0.9.0
|
scikit-optimize==0.9.0
|
||||||
filelock==3.4.2
|
filelock==3.4.2
|
||||||
joblib==1.1.0
|
joblib==1.1.0
|
||||||
psutil==5.8.0
|
|
||||||
progressbar2==3.55.0
|
progressbar2==3.55.0
|
||||||
|
|
|
@ -3,7 +3,7 @@ numpy==1.22.0; python_version > '3.7'
|
||||||
pandas==1.3.5
|
pandas==1.3.5
|
||||||
pandas-ta==0.3.14b
|
pandas-ta==0.3.14b
|
||||||
|
|
||||||
ccxt==1.66.20
|
ccxt==1.66.32
|
||||||
# Pin cryptography for now due to rust build errors with piwheels
|
# Pin cryptography for now due to rust build errors with piwheels
|
||||||
cryptography==36.0.1
|
cryptography==36.0.1
|
||||||
aiohttp==3.8.1
|
aiohttp==3.8.1
|
||||||
|
@ -14,7 +14,7 @@ cachetools==4.2.2
|
||||||
requests==2.26.0
|
requests==2.26.0
|
||||||
urllib3==1.26.7
|
urllib3==1.26.7
|
||||||
jsonschema==4.3.3
|
jsonschema==4.3.3
|
||||||
TA-Lib==0.4.22
|
TA-Lib==0.4.23
|
||||||
technical==1.3.0
|
technical==1.3.0
|
||||||
tabulate==0.8.9
|
tabulate==0.8.9
|
||||||
pycoingecko==2.2.0
|
pycoingecko==2.2.0
|
||||||
|
@ -36,7 +36,7 @@ fastapi==0.70.1
|
||||||
uvicorn==0.16.0
|
uvicorn==0.16.0
|
||||||
pyjwt==2.3.0
|
pyjwt==2.3.0
|
||||||
aiofiles==0.8.0
|
aiofiles==0.8.0
|
||||||
psutil==5.8.0
|
psutil==5.9.0
|
||||||
|
|
||||||
# Support for colorized terminal output
|
# Support for colorized terminal output
|
||||||
colorama==0.4.4
|
colorama==0.4.4
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -43,7 +43,7 @@ setup(
|
||||||
],
|
],
|
||||||
install_requires=[
|
install_requires=[
|
||||||
# from requirements.txt
|
# from requirements.txt
|
||||||
'ccxt>=1.60.11',
|
'ccxt>=1.66.32',
|
||||||
'SQLAlchemy',
|
'SQLAlchemy',
|
||||||
'python-telegram-bot>=13.4',
|
'python-telegram-bot>=13.4',
|
||||||
'arrow>=0.17.0',
|
'arrow>=0.17.0',
|
||||||
|
|
|
@ -2020,7 +2020,7 @@ def saved_hyperopt_results():
|
||||||
'params_dict': {
|
'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
|
'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
|
'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
|
'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,
|
'total_profit': -0.00125625,
|
||||||
'current_epoch': 1,
|
'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
|
'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
|
'roi': {0: 0.4449309386008759, 140: 0.11955965746663, 823: 0.06403981740598495, 1157: 0}, # noqa: E501
|
||||||
'stoploss': {'stoploss': -0.338070047333259}},
|
'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
|
'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,
|
'total_profit': 6.185e-05,
|
||||||
'current_epoch': 2,
|
'current_epoch': 2,
|
||||||
|
@ -2046,7 +2046,7 @@ def saved_hyperopt_results():
|
||||||
'loss': 14.241196856510731,
|
'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_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
|
'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
|
'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,
|
'total_profit': -0.13639474,
|
||||||
'current_epoch': 3,
|
'current_epoch': 3,
|
||||||
|
@ -2063,7 +2063,7 @@ def saved_hyperopt_results():
|
||||||
'loss': 0.22195522184191518,
|
'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_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
|
'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
|
'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,
|
'total_profit': -0.002480140000000001,
|
||||||
'current_epoch': 5,
|
'current_epoch': 5,
|
||||||
|
@ -2073,7 +2073,7 @@ def saved_hyperopt_results():
|
||||||
'loss': 0.545315889154162,
|
'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_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
|
'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
|
'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,
|
'total_profit': -0.0041773,
|
||||||
'current_epoch': 6,
|
'current_epoch': 6,
|
||||||
|
@ -2085,7 +2085,7 @@ def saved_hyperopt_results():
|
||||||
'params_details': {
|
'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
|
'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
|
'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
|
'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,
|
'total_profit': -0.06339929,
|
||||||
'current_epoch': 7,
|
'current_epoch': 7,
|
||||||
|
@ -2095,7 +2095,7 @@ def saved_hyperopt_results():
|
||||||
'loss': 20.0, # noqa: E501
|
'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_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
|
'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
|
'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,
|
'total_profit': 0.0,
|
||||||
'current_epoch': 8,
|
'current_epoch': 8,
|
||||||
|
@ -2105,7 +2105,7 @@ def saved_hyperopt_results():
|
||||||
'loss': 2.4731817780991223,
|
'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_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
|
'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
|
'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
|
'total_profit': -0.044050070000000004, # noqa: E501
|
||||||
'current_epoch': 9,
|
'current_epoch': 9,
|
||||||
|
@ -2115,7 +2115,7 @@ def saved_hyperopt_results():
|
||||||
'loss': -0.2604606005845212, # noqa: E501
|
'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_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
|
'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
|
'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,
|
'total_profit': 0.00021629,
|
||||||
'current_epoch': 10,
|
'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_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
|
'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!
|
# 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
|
'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,
|
'total_profit': -0.07436117,
|
||||||
'current_epoch': 11,
|
'current_epoch': 11,
|
||||||
|
@ -2136,7 +2136,7 @@ def saved_hyperopt_results():
|
||||||
'loss': 100000,
|
'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_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
|
'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
|
'results_explanation': ' 0 trades. Avg profit nan%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration nan min.', # noqa: E501
|
||||||
'total_profit': 0,
|
'total_profit': 0,
|
||||||
'current_epoch': 12,
|
'current_epoch': 12,
|
||||||
|
|
|
@ -8,14 +8,14 @@ from pandas import DataFrame, DateOffset, Timestamp, to_datetime
|
||||||
|
|
||||||
from freqtrade.configuration import TimeRange
|
from freqtrade.configuration import TimeRange
|
||||||
from freqtrade.constants import LAST_BT_RESULT_FN
|
from freqtrade.constants import LAST_BT_RESULT_FN
|
||||||
from freqtrade.data.btanalysis import (BT_DATA_COLUMNS, BT_DATA_COLUMNS_MID, BT_DATA_COLUMNS_OLD,
|
from freqtrade.data.btanalysis import (BT_DATA_COLUMNS, analyze_trade_parallelism, calculate_csum,
|
||||||
analyze_trade_parallelism, calculate_csum,
|
|
||||||
calculate_market_change, calculate_max_drawdown,
|
calculate_market_change, calculate_max_drawdown,
|
||||||
calculate_underwater, combine_dataframes_with_mean,
|
calculate_underwater, combine_dataframes_with_mean,
|
||||||
create_cum_profit, extract_trades_of_period,
|
create_cum_profit, extract_trades_of_period,
|
||||||
get_latest_backtest_filename, get_latest_hyperopt_file,
|
get_latest_backtest_filename, get_latest_hyperopt_file,
|
||||||
load_backtest_data, load_trades, load_trades_from_db)
|
load_backtest_data, load_trades, load_trades_from_db)
|
||||||
from freqtrade.data.history import load_data, load_pair_history
|
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 import create_mock_trades
|
||||||
from tests.conftest_trades import MOCK_TRADE_COUNT
|
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"
|
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"
|
filename = testdatadir / "backtest-result_test222.json"
|
||||||
bt_data = load_backtest_data(filename)
|
mocker.patch('freqtrade.data.btanalysis.load_backtest_stats', return_value=[])
|
||||||
assert isinstance(bt_data, DataFrame)
|
|
||||||
assert list(bt_data.columns) == BT_DATA_COLUMNS_OLD + ['profit_abs', 'profit_ratio']
|
|
||||||
assert len(bt_data) == 179
|
|
||||||
|
|
||||||
# Test loading from string (must yield same result)
|
with pytest.raises(OperationalException,
|
||||||
bt_data2 = load_backtest_data(str(filename))
|
match=r"Backtest-results with only trades data are no longer supported."):
|
||||||
assert bt_data.equals(bt_data2)
|
load_backtest_data(filename)
|
||||||
|
|
||||||
with pytest.raises(ValueError, match=r"File .* does not exist\."):
|
|
||||||
load_backtest_data(str("filename") + "nofile")
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_backtest_data_new_format(testdatadir):
|
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"
|
filename = testdatadir / "backtest-result_new.json"
|
||||||
bt_data = load_backtest_data(filename)
|
bt_data = load_backtest_data(filename)
|
||||||
assert isinstance(bt_data, DataFrame)
|
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
|
assert len(bt_data) == 179
|
||||||
|
|
||||||
# Test loading from string (must yield same result)
|
# Test loading from string (must yield same result)
|
||||||
|
@ -96,7 +90,7 @@ def test_load_backtest_data_multi(testdatadir):
|
||||||
for strategy in ('StrategyTestV2', 'TestStrategy'):
|
for strategy in ('StrategyTestV2', 'TestStrategy'):
|
||||||
bt_data = load_backtest_data(filename, strategy=strategy)
|
bt_data = load_backtest_data(filename, strategy=strategy)
|
||||||
assert isinstance(bt_data, DataFrame)
|
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
|
assert len(bt_data) == 179
|
||||||
|
|
||||||
# Test loading from string (must yield same result)
|
# 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
|
assert trades1.iloc[-1].close_date == Arrow(2017, 11, 14, 15, 25, 0).datetime
|
||||||
|
|
||||||
|
|
||||||
def test_analyze_trade_parallelism(default_conf, mocker, testdatadir):
|
def test_analyze_trade_parallelism(testdatadir):
|
||||||
filename = testdatadir / "backtest-result_test.json"
|
filename = testdatadir / "backtest-result_new.json"
|
||||||
bt_data = load_backtest_data(filename)
|
bt_data = load_backtest_data(filename)
|
||||||
|
|
||||||
res = analyze_trade_parallelism(bt_data, "5m")
|
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):
|
def test_create_cum_profit(testdatadir):
|
||||||
filename = testdatadir / "backtest-result_test.json"
|
filename = testdatadir / "backtest-result_new.json"
|
||||||
bt_data = load_backtest_data(filename)
|
bt_data = load_backtest_data(filename)
|
||||||
timerange = TimeRange.parse_timerange("20180110-20180112")
|
timerange = TimeRange.parse_timerange("20180110-20180112")
|
||||||
|
|
||||||
|
@ -258,7 +252,7 @@ def test_create_cum_profit(testdatadir):
|
||||||
|
|
||||||
|
|
||||||
def test_create_cum_profit1(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)
|
bt_data = load_backtest_data(filename)
|
||||||
# Move close-time to "off" the candle, to make sure the logic still works
|
# 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)
|
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):
|
def test_calculate_max_drawdown(testdatadir):
|
||||||
filename = testdatadir / "backtest-result_test.json"
|
filename = testdatadir / "backtest-result_new.json"
|
||||||
bt_data = load_backtest_data(filename)
|
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 isinstance(drawdown, float)
|
||||||
assert pytest.approx(drawdown) == 0.21142322
|
assert pytest.approx(drawdown) == 0.12071099
|
||||||
assert isinstance(hdate, Timestamp)
|
assert isinstance(hdate, Timestamp)
|
||||||
assert isinstance(lowdate, Timestamp)
|
assert isinstance(lowdate, Timestamp)
|
||||||
assert isinstance(hval, float)
|
assert isinstance(hval, float)
|
||||||
assert isinstance(lval, float)
|
assert isinstance(lval, float)
|
||||||
assert hdate == Timestamp('2018-01-24 14:25:00', tz='UTC')
|
assert hdate == Timestamp('2018-01-25 01:30:00', tz='UTC')
|
||||||
assert lowdate == Timestamp('2018-01-30 04:45:00', tz='UTC')
|
assert lowdate == Timestamp('2018-01-25 03:50:00', tz='UTC')
|
||||||
|
|
||||||
underwater = calculate_underwater(bt_data)
|
underwater = calculate_underwater(bt_data)
|
||||||
assert isinstance(underwater, DataFrame)
|
assert isinstance(underwater, DataFrame)
|
||||||
|
|
||||||
with pytest.raises(ValueError, match='Trade dataframe empty.'):
|
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.'):
|
with pytest.raises(ValueError, match='Trade dataframe empty.'):
|
||||||
calculate_underwater(DataFrame())
|
calculate_underwater(DataFrame())
|
||||||
|
|
||||||
|
|
||||||
def test_calculate_csum(testdatadir):
|
def test_calculate_csum(testdatadir):
|
||||||
filename = testdatadir / "backtest-result_test.json"
|
filename = testdatadir / "backtest-result_new.json"
|
||||||
bt_data = load_backtest_data(filename)
|
bt_data = load_backtest_data(filename)
|
||||||
csum_min, csum_max = calculate_csum(bt_data)
|
csum_min, csum_max = calculate_csum(bt_data)
|
||||||
|
|
||||||
|
@ -331,12 +326,13 @@ def test_calculate_max_drawdown2():
|
||||||
# sort by profit and reset index
|
# sort by profit and reset index
|
||||||
df = df.sort_values('profit').reset_index(drop=True)
|
df = df.sort_values('profit').reset_index(drop=True)
|
||||||
df1 = df.copy()
|
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')
|
df, date_col='open_date', value_col='profit')
|
||||||
# Ensure df has not been altered.
|
# Ensure df has not been altered.
|
||||||
assert df.equals(df1)
|
assert df.equals(df1)
|
||||||
|
|
||||||
assert isinstance(drawdown, float)
|
assert isinstance(drawdown, float)
|
||||||
|
assert isinstance(drawdown_rel, float)
|
||||||
# High must be before low
|
# High must be before low
|
||||||
assert hdate < ldate
|
assert hdate < ldate
|
||||||
# High value must be higher than low value
|
# High value must be higher than low value
|
||||||
|
|
|
@ -52,6 +52,12 @@ EXCHANGES = {
|
||||||
'hasQuoteVolume': True,
|
'hasQuoteVolume': True,
|
||||||
'timeframe': '5m',
|
'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")
|
@pytest.fixture(params=EXCHANGES, scope="class")
|
||||||
def exchange(request, exchange_conf):
|
def exchange(request, exchange_conf):
|
||||||
exchange_conf['exchange']['name'] = request.param
|
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)
|
exchange = ExchangeResolver.load_exchange(request.param, exchange_conf, validate=True)
|
||||||
|
|
||||||
yield exchange, request.param
|
yield exchange, request.param
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# pragma pylint: disable=missing-docstring,W0212,C0103
|
# pragma pylint: disable=missing-docstring,W0212,C0103
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import ANY, MagicMock
|
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)
|
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:
|
def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, caplog) -> None:
|
||||||
patched_configuration_load_config_file(mocker, default_conf)
|
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)
|
start_hyperopt(pargs)
|
||||||
|
|
||||||
|
|
||||||
def test_start_no_data(mocker, hyperopt_conf) -> None:
|
def test_start_no_data(mocker, hyperopt_conf, tmpdir) -> None:
|
||||||
hyperopt_conf['user_data_dir'] = Path("tests")
|
hyperopt_conf['user_data_dir'] = Path(tmpdir)
|
||||||
patched_configuration_load_config_file(mocker, hyperopt_conf)
|
patched_configuration_load_config_file(mocker, hyperopt_conf)
|
||||||
mocker.patch('freqtrade.data.history.load_pair_history', MagicMock(return_value=pd.DataFrame))
|
mocker.patch('freqtrade.data.history.load_pair_history', MagicMock(return_value=pd.DataFrame))
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
|
@ -178,7 +201,6 @@ def test_start_no_data(mocker, hyperopt_conf) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
# TODO: migrate to strategy-based hyperopt
|
|
||||||
args = [
|
args = [
|
||||||
'hyperopt',
|
'hyperopt',
|
||||||
'--config', 'config.json',
|
'--config', 'config.json',
|
||||||
|
@ -222,14 +244,7 @@ def test_log_results_if_loss_improves(hyperopt, capsys) -> None:
|
||||||
hyperopt.print_results(
|
hyperopt.print_results(
|
||||||
{
|
{
|
||||||
'loss': 1,
|
'loss': 1,
|
||||||
'results_metrics':
|
'results_metrics': generate_result_metrics(),
|
||||||
{
|
|
||||||
'trade_count': 1,
|
|
||||||
'avg_profit': 0.1,
|
|
||||||
'total_profit': 0.001,
|
|
||||||
'profit': 1.0,
|
|
||||||
'duration': 20.0
|
|
||||||
},
|
|
||||||
'total_profit': 0,
|
'total_profit': 0,
|
||||||
'current_epoch': 2, # This starts from 1 (in a human-friendly manner)
|
'current_epoch': 2, # This starts from 1 (in a human-friendly manner)
|
||||||
'is_initial_point': False,
|
'is_initial_point': False,
|
||||||
|
@ -238,7 +253,7 @@ def test_log_results_if_loss_improves(hyperopt, capsys) -> None:
|
||||||
)
|
)
|
||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert all(x in out
|
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:
|
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=[{
|
MagicMock(return_value=[{
|
||||||
'loss': 1, 'results_explanation': 'foo result',
|
'loss': 1, 'results_explanation': 'foo result',
|
||||||
'params': {'buy': {}, 'sell': {}, 'roi': {}, 'stoploss': 0.0},
|
'params': {'buy': {}, 'sell': {}, 'roi': {}, 'stoploss': 0.0},
|
||||||
'results_metrics':
|
'results_metrics': generate_result_metrics(),
|
||||||
{
|
|
||||||
'trade_count': 1,
|
|
||||||
'avg_profit': 0.1,
|
|
||||||
'total_profit': 0.001,
|
|
||||||
'profit': 1.0,
|
|
||||||
'duration': 20.0
|
|
||||||
},
|
|
||||||
}])
|
}])
|
||||||
)
|
)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
|
@ -359,7 +367,7 @@ def test_hyperopt_format_results(hyperopt):
|
||||||
'backtest_start_time': 1619718665,
|
'backtest_start_time': 1619718665,
|
||||||
'backtest_end_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, 11, 14, 19, 32, 00),
|
||||||
Arrow(2017, 12, 14, 19, 32, 00), market_change=0)
|
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},
|
'roi': {}, 'stoploss': {'stoploss': None},
|
||||||
'trailing': {'trailing_stop': None}
|
'trailing': {'trailing_stop': None}
|
||||||
},
|
},
|
||||||
'results_metrics':
|
'results_metrics': generate_result_metrics(),
|
||||||
{
|
|
||||||
'trade_count': 1,
|
|
||||||
'avg_profit': 0.1,
|
|
||||||
'total_profit': 0.001,
|
|
||||||
'profit': 1.0,
|
|
||||||
'duration': 20.0
|
|
||||||
}
|
|
||||||
}])
|
}])
|
||||||
)
|
)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
|
@ -584,14 +585,7 @@ def test_print_json_spaces_default(mocker, hyperopt_conf, capsys) -> None:
|
||||||
'sell': {'sell-mfi-value': None},
|
'sell': {'sell-mfi-value': None},
|
||||||
'roi': {}, 'stoploss': {'stoploss': None}
|
'roi': {}, 'stoploss': {'stoploss': None}
|
||||||
},
|
},
|
||||||
'results_metrics':
|
'results_metrics': generate_result_metrics(),
|
||||||
{
|
|
||||||
'trade_count': 1,
|
|
||||||
'avg_profit': 0.1,
|
|
||||||
'total_profit': 0.001,
|
|
||||||
'profit': 1.0,
|
|
||||||
'duration': 20.0
|
|
||||||
}
|
|
||||||
}])
|
}])
|
||||||
)
|
)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
|
@ -629,14 +623,7 @@ def test_print_json_spaces_roi_stoploss(mocker, hyperopt_conf, capsys) -> None:
|
||||||
MagicMock(return_value=[{
|
MagicMock(return_value=[{
|
||||||
'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
||||||
'params_details': {'roi': {}, 'stoploss': {'stoploss': None}},
|
'params_details': {'roi': {}, 'stoploss': {'stoploss': None}},
|
||||||
'results_metrics':
|
'results_metrics': generate_result_metrics(),
|
||||||
{
|
|
||||||
'trade_count': 1,
|
|
||||||
'avg_profit': 0.1,
|
|
||||||
'total_profit': 0.001,
|
|
||||||
'profit': 1.0,
|
|
||||||
'duration': 20.0
|
|
||||||
}
|
|
||||||
}])
|
}])
|
||||||
)
|
)
|
||||||
patch_exchange(mocker)
|
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',
|
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
||||||
MagicMock(return_value=[{
|
MagicMock(return_value=[{
|
||||||
'loss': 1, 'results_explanation': 'foo result', 'params': {'stoploss': 0.0},
|
'loss': 1, 'results_explanation': 'foo result', 'params': {'stoploss': 0.0},
|
||||||
'results_metrics':
|
'results_metrics': generate_result_metrics(),
|
||||||
{
|
|
||||||
'trade_count': 1,
|
|
||||||
'avg_profit': 0.1,
|
|
||||||
'total_profit': 0.001,
|
|
||||||
'profit': 1.0,
|
|
||||||
'duration': 20.0
|
|
||||||
}
|
|
||||||
}])
|
}])
|
||||||
)
|
)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
|
@ -756,14 +736,7 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
|
||||||
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
||||||
MagicMock(return_value=[{
|
MagicMock(return_value=[{
|
||||||
'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
||||||
'results_metrics':
|
'results_metrics': generate_result_metrics(),
|
||||||
{
|
|
||||||
'trade_count': 1,
|
|
||||||
'avg_profit': 0.1,
|
|
||||||
'total_profit': 0.001,
|
|
||||||
'profit': 1.0,
|
|
||||||
'duration': 20.0
|
|
||||||
}
|
|
||||||
}])
|
}])
|
||||||
)
|
)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
|
@ -805,14 +778,7 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
|
||||||
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
|
||||||
MagicMock(return_value=[{
|
MagicMock(return_value=[{
|
||||||
'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
'loss': 1, 'results_explanation': 'foo result', 'params': {},
|
||||||
'results_metrics':
|
'results_metrics': generate_result_metrics(),
|
||||||
{
|
|
||||||
'trade_count': 1,
|
|
||||||
'avg_profit': 0.1,
|
|
||||||
'total_profit': 0.001,
|
|
||||||
'profit': 1.0,
|
|
||||||
'duration': 20.0
|
|
||||||
}
|
|
||||||
}])
|
}])
|
||||||
)
|
)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import datetime
|
|
||||||
import re
|
import re
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -49,7 +48,7 @@ def test_text_table_bt_results():
|
||||||
' 0:20:00 | 2 0 1 66.7 |'
|
' 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)
|
starting_balance=4, results=results)
|
||||||
assert text_table_bt_results(pair_results, stake_currency='BTC') == result_str
|
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['backtest_end'] == max_date.strftime(DATETIME_PRINT_FORMAT)
|
||||||
assert strat_stats['total_trades'] == len(results['DefStrat']['results'])
|
assert strat_stats['total_trades'] == len(results['DefStrat']['results'])
|
||||||
# Above sample had no loosing trade
|
# 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
|
# Retry with losing trade
|
||||||
results = {'DefStrat': {
|
results = {'DefStrat': {
|
||||||
|
@ -143,7 +142,7 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmpdir):
|
||||||
assert 'strategy_comparison' in stats
|
assert 'strategy_comparison' in stats
|
||||||
strat_stats = stats['strategy']['DefStrat']
|
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_start'] == '2017-11-14 22:10:00'
|
||||||
assert strat_stats['drawdown_end'] == '2017-11-14 22:43:00'
|
assert strat_stats['drawdown_end'] == '2017-11-14 22:43:00'
|
||||||
assert strat_stats['drawdown_end_ts'] == 1510699380000
|
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)
|
filename1 = Path(tmpdir / last_fn)
|
||||||
assert filename1.is_file()
|
assert filename1.is_file()
|
||||||
content = filename1.read_text()
|
content = filename1.read_text()
|
||||||
assert 'max_drawdown' in content
|
assert 'max_drawdown_account' in content
|
||||||
assert 'strategy' in content
|
assert 'strategy' in content
|
||||||
assert 'pairlist' 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)
|
starting_balance=2, results=results)
|
||||||
assert isinstance(pair_results, list)
|
assert isinstance(pair_results, list)
|
||||||
assert len(pair_results) == 2
|
assert len(pair_results) == 2
|
||||||
|
@ -227,9 +226,9 @@ def test_generate_daily_stats(testdatadir):
|
||||||
assert isinstance(res, dict)
|
assert isinstance(res, dict)
|
||||||
assert round(res['backtest_best_day'], 4) == 0.1796
|
assert round(res['backtest_best_day'], 4) == 0.1796
|
||||||
assert round(res['backtest_worst_day'], 4) == -0.1468
|
assert round(res['backtest_worst_day'], 4) == -0.1468
|
||||||
assert res['winning_days'] == 14
|
assert res['winning_days'] == 19
|
||||||
assert res['draw_days'] == 4
|
assert res['draw_days'] == 0
|
||||||
assert res['losing_days'] == 3
|
assert res['losing_days'] == 2
|
||||||
|
|
||||||
# Select empty dataframe!
|
# Select empty dataframe!
|
||||||
res = generate_daily_stats(bt_data.loc[bt_data['open_date'] == '2000-01-01', :])
|
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)
|
assert stop_result['profit_mean_pct'] == round(stop_result['profit_mean'] * 100, 2)
|
||||||
|
|
||||||
|
|
||||||
def test_text_table_strategy(default_conf):
|
def test_text_table_strategy(testdatadir):
|
||||||
default_conf['max_open_trades'] = 2
|
filename = testdatadir / "backtest-result_multistrat.json"
|
||||||
default_conf['dry_run_wallet'] = 3
|
bt_res_data = load_backtest_stats(filename)
|
||||||
results = {}
|
|
||||||
date = datetime.datetime(year=2020, month=1, day=1, hour=12, minute=30)
|
bt_res_data_comparison = bt_res_data.pop('strategy_comparison')
|
||||||
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}
|
|
||||||
|
|
||||||
result_str = (
|
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'
|
' Tot Profit % | Avg Duration | Win Draw Loss Win% | Drawdown |\n'
|
||||||
'|---------------+--------+----------------+----------------+------------------+'
|
'|----------------+--------+----------------+----------------+------------------+'
|
||||||
'----------------+----------------+-------------------------+-----------------------|\n'
|
'----------------+----------------+-------------------------+-----------------------|\n'
|
||||||
'| TestStrategy1 | 3 | 20.00 | 60.00 | 1.10000000 |'
|
'| StrategyTestV2 | 179 | 0.08 | 14.39 | 0.02608550 |'
|
||||||
' 36.67 | 0:17:00 | 3 0 0 100 | 0.00000000 BTC 0.00% |\n'
|
' 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |\n'
|
||||||
'| TestStrategy2 | 3 | 30.00 | 90.00 | 1.30000000 |'
|
'| TestStrategy | 179 | 0.08 | 14.39 | 0.02608550 |'
|
||||||
' 43.33 | 0:20:00 | 3 0 0 100 | 0.00000000 BTC 0.00% |'
|
' 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
|
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)
|
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
|
# default refresh of 1800 to small for daily candle lookback
|
||||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||||
"lookback_days": 1}],
|
"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
|
# ambigous configuration with lookback days and period
|
||||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||||
"lookback_days": 1, "lookback_period": 1}],
|
"lookback_days": 1, "lookback_period": 1}],
|
||||||
"BTC", "lookback_days_and_period"), # OperationalException expected
|
"BTC", "binance", "lookback_days_and_period"), # OperationalException expected
|
||||||
# negative lookback period
|
# negative lookback period
|
||||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||||
"lookback_timeframe": "1d", "lookback_period": -1}],
|
"lookback_timeframe": "1d", "lookback_period": -1}],
|
||||||
"BTC", "lookback_period_negative"), # OperationalException expected
|
"BTC", "binance", "lookback_period_negative"), # OperationalException expected
|
||||||
# lookback range exceedes exchange limit
|
# lookback range exceedes exchange limit
|
||||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||||
"lookback_timeframe": "1m", "lookback_period": 2000, "refresh_period": 3600}],
|
"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
|
# expecing pairs as given
|
||||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||||
"lookback_timeframe": "1d", "lookback_period": 1, "refresh_period": 86400}],
|
"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
|
# expecting pairs from default tickers, because 1h candles are not available
|
||||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume",
|
||||||
"lookback_timeframe": "1h", "lookback_period": 2, "refresh_period": 3600}],
|
"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,
|
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['pairlists'] = pairlists
|
||||||
whitelist_conf['stake_currency'] = base_currency
|
whitelist_conf['stake_currency'] = base_currency
|
||||||
|
whitelist_conf['exchange']['name'] = exchange
|
||||||
|
|
||||||
ohlcv_history_high_vola = ohlcv_history.copy()
|
ohlcv_history_high_vola = ohlcv_history.copy()
|
||||||
ohlcv_history_high_vola.loc[ohlcv_history_high_vola.index == 1, 'close'] = 0.00090
|
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 = ohlcv_history.copy()
|
||||||
ohlcv_history_medium_volume.loc[ohlcv_history_medium_volume.index == 2, 'volume'] = 5
|
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 = ohlcv_history.copy()
|
||||||
ohlcv_history_high_volume.loc[:, 'volume'] = 10
|
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 = {
|
ohlcv_data = {
|
||||||
('ETH/BTC', '1d'): ohlcv_history,
|
('ETH/BTC', '1d'): ohlcv_history,
|
||||||
|
|
|
@ -45,7 +45,7 @@ def test_init_plotscript(default_conf, mocker, testdatadir):
|
||||||
default_conf['trade_source'] = "file"
|
default_conf['trade_source'] = "file"
|
||||||
default_conf['timeframe'] = "5m"
|
default_conf['timeframe'] = "5m"
|
||||||
default_conf["datadir"] = testdatadir
|
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"]
|
supported_markets = ["TRX/BTC", "ADA/BTC"]
|
||||||
ret = init_plotscript(default_conf, supported_markets)
|
ret = init_plotscript(default_conf, supported_markets)
|
||||||
assert "ohlcv" in ret
|
assert "ohlcv" in ret
|
||||||
|
@ -157,7 +157,7 @@ def test_plot_trades(testdatadir, caplog):
|
||||||
assert fig == fig1
|
assert fig == fig1
|
||||||
assert log_has("No trades found.", caplog)
|
assert log_has("No trades found.", caplog)
|
||||||
pair = "ADA/BTC"
|
pair = "ADA/BTC"
|
||||||
filename = testdatadir / "backtest-result_test.json"
|
filename = testdatadir / "backtest-result_new.json"
|
||||||
trades = load_backtest_data(filename)
|
trades = load_backtest_data(filename)
|
||||||
trades = trades.loc[trades['pair'] == pair]
|
trades = trades.loc[trades['pair'] == pair]
|
||||||
|
|
||||||
|
@ -294,7 +294,7 @@ def test_generate_plot_file(mocker, caplog):
|
||||||
|
|
||||||
|
|
||||||
def test_add_profit(testdatadir):
|
def test_add_profit(testdatadir):
|
||||||
filename = testdatadir / "backtest-result_test.json"
|
filename = testdatadir / "backtest-result_new.json"
|
||||||
bt_data = load_backtest_data(filename)
|
bt_data = load_backtest_data(filename)
|
||||||
timerange = TimeRange.parse_timerange("20180110-20180112")
|
timerange = TimeRange.parse_timerange("20180110-20180112")
|
||||||
|
|
||||||
|
@ -314,7 +314,7 @@ def test_add_profit(testdatadir):
|
||||||
|
|
||||||
|
|
||||||
def test_generate_profit_graph(testdatadir):
|
def test_generate_profit_graph(testdatadir):
|
||||||
filename = testdatadir / "backtest-result_test.json"
|
filename = testdatadir / "backtest-result_new.json"
|
||||||
trades = load_backtest_data(filename)
|
trades = load_backtest_data(filename)
|
||||||
timerange = TimeRange.parse_timerange("20180110-20180112")
|
timerange = TimeRange.parse_timerange("20180110-20180112")
|
||||||
pairs = ["TRX/BTC", "XLM/BTC"]
|
pairs = ["TRX/BTC", "XLM/BTC"]
|
||||||
|
@ -343,7 +343,7 @@ def test_generate_profit_graph(testdatadir):
|
||||||
|
|
||||||
profit = find_trace_in_fig_data(figure.data, "Profit")
|
profit = find_trace_in_fig_data(figure.data, "Profit")
|
||||||
assert isinstance(profit, go.Scatter)
|
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)
|
assert isinstance(drawdown, go.Scatter)
|
||||||
parallel = find_trace_in_fig_data(figure.data, "Parallel trades")
|
parallel = find_trace_in_fig_data(figure.data, "Parallel trades")
|
||||||
assert isinstance(parallel, go.Scatter)
|
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['trade_source'] = 'file'
|
||||||
default_conf["datadir"] = testdatadir
|
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['indicators1'] = ["sma5", "ema10"]
|
||||||
default_conf['indicators2'] = ["macd"]
|
default_conf['indicators2'] = ["macd"]
|
||||||
default_conf['pairs'] = ["ETH/BTC", "LTC/BTC"]
|
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.*"):
|
match=r"No trades found, cannot generate Profit-plot.*"):
|
||||||
plot_profit(default_conf)
|
plot_profit(default_conf)
|
||||||
|
|
||||||
default_conf['exportfilename'] = testdatadir / "backtest-result_test.json"
|
default_conf['exportfilename'] = testdatadir / "backtest-result_new.json"
|
||||||
|
|
||||||
plot_profit(default_conf)
|
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