Merge branch 'develop' into no-ticker-2

This commit is contained in:
hroff-1902 2020-03-13 16:43:52 +03:00 committed by GitHub
commit 51f52c8609
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 252 additions and 74 deletions

View File

@ -23,7 +23,7 @@
"ask_strategy":{ "ask_strategy":{
"use_order_book": false, "use_order_book": false,
"order_book_min": 1, "order_book_min": 1,
"order_book_max": 9, "order_book_max": 1,
"use_sell_signal": true, "use_sell_signal": true,
"sell_profit_only": false, "sell_profit_only": false,
"ignore_roi_if_buy_signal": false "ignore_roi_if_buy_signal": false

View File

@ -23,7 +23,7 @@
"ask_strategy":{ "ask_strategy":{
"use_order_book": false, "use_order_book": false,
"order_book_min": 1, "order_book_min": 1,
"order_book_max": 9, "order_book_max": 1,
"use_sell_signal": true, "use_sell_signal": true,
"sell_profit_only": false, "sell_profit_only": false,
"ignore_roi_if_buy_signal": false "ignore_roi_if_buy_signal": false

View File

@ -38,7 +38,7 @@
"price_side": "ask", "price_side": "ask",
"use_order_book": false, "use_order_book": false,
"order_book_min": 1, "order_book_min": 1,
"order_book_max": 9, "order_book_max": 1,
"use_sell_signal": true, "use_sell_signal": true,
"sell_profit_only": false, "sell_profit_only": false,
"ignore_roi_if_buy_signal": false "ignore_roi_if_buy_signal": false

View File

@ -23,7 +23,7 @@
"ask_strategy":{ "ask_strategy":{
"use_order_book": false, "use_order_book": false,
"order_book_min": 1, "order_book_min": 1,
"order_book_max": 9, "order_book_max": 1,
"use_sell_signal": true, "use_sell_signal": true,
"sell_profit_only": false, "sell_profit_only": false,
"ignore_roi_if_buy_signal": false "ignore_roi_if_buy_signal": false

View File

@ -536,8 +536,14 @@ The idea here is to place the sell order early, to be ahead in the queue.
A fixed slot (mirroring `bid_strategy.order_book_top`) can be defined by setting `ask_strategy.order_book_min` and `ask_strategy.order_book_max` to the same number. A fixed slot (mirroring `bid_strategy.order_book_top`) can be defined by setting `ask_strategy.order_book_min` and `ask_strategy.order_book_max` to the same number.
!!! Warning "Orderbook and stoploss_on_exchange" !!! Warning "Order_book_max > 1 - increased risks for stoplosses!"
Using `ask_strategy.order_book_max` higher than 1 may increase the risk, since an eventual [stoploss on exchange](#understand-order_types) will be needed to be cancelled as soon as the order is placed. Using `ask_strategy.order_book_max` higher than 1 will increase the risk the stoploss on exchange is cancelled too early, since an eventual [stoploss on exchange](#understand-order_types) will be cancelled as soon as the order is placed.
Also, the sell order will remain on the exchange for `unfilledtimeout.sell` (or until it's filled) - which can lead to missed stoplosses (with or without using stoploss on exchange).
!!! Warning "Order_book_max > 1 in dry-run"
Using `ask_strategy.order_book_max` higher than 1 will result in improper dry-run results (significantly better than real orders executed on exchange), since dry-run assumes orders to be filled almost instantly.
It is therefore advised to not use this setting for dry-runs.
#### Sell price without Orderbook enabled #### Sell price without Orderbook enabled

View File

@ -160,6 +160,9 @@ So let's write the buy strategy using these values:
dataframe['macd'], dataframe['macdsignal'] dataframe['macd'], dataframe['macdsignal']
)) ))
# Check that volume is not 0
conditions.append(dataframe['volume'] > 0)
if conditions: if conditions:
dataframe.loc[ dataframe.loc[
reduce(lambda x, y: x & y, conditions), reduce(lambda x, y: x & y, conditions),

View File

@ -429,6 +429,7 @@ usage: freqtrade hyperopt-list [-h] [-v] [--logfile FILE] [-V] [-c PATH]
[--min-total-profit FLOAT] [--min-total-profit FLOAT]
[--max-total-profit FLOAT] [--no-color] [--max-total-profit FLOAT] [--no-color]
[--print-json] [--no-details] [--print-json] [--no-details]
[--export-csv FILE]
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
@ -450,6 +451,8 @@ optional arguments:
useful if you are redirecting output to a file. useful if you are redirecting output to a file.
--print-json Print best result detailization in JSON format. --print-json Print best result detailization in JSON format.
--no-details Do not print best epoch details. --no-details Do not print best epoch details.
--export-csv FILE Export to CSV-File. This will disable table print.
Example: --export-csv hyperopt.csv
Common arguments: Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages). -v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
@ -458,9 +461,10 @@ Common arguments:
details. details.
-V, --version show program's version number and exit -V, --version show program's version number and exit
-c PATH, --config PATH -c PATH, --config PATH
Specify configuration file (default: `config.json`). Specify configuration file (default:
Multiple --config options may be used. Can be set to `userdir/config.json` or `config.json` whichever
`-` to read config from stdin. exists). Multiple --config options may be used. Can be
set to `-` to read config from stdin.
-d PATH, --datadir PATH -d PATH, --datadir PATH
Path to directory with historical backtesting data. Path to directory with historical backtesting data.
--userdir PATH, --user-data-dir PATH --userdir PATH, --user-data-dir PATH

View File

@ -69,7 +69,8 @@ ARGS_HYPEROPT_LIST = ["hyperopt_list_best", "hyperopt_list_profitable",
"hyperopt_list_min_avg_time", "hyperopt_list_max_avg_time", "hyperopt_list_min_avg_time", "hyperopt_list_max_avg_time",
"hyperopt_list_min_avg_profit", "hyperopt_list_max_avg_profit", "hyperopt_list_min_avg_profit", "hyperopt_list_max_avg_profit",
"hyperopt_list_min_total_profit", "hyperopt_list_max_total_profit", "hyperopt_list_min_total_profit", "hyperopt_list_max_total_profit",
"print_colorized", "print_json", "hyperopt_list_no_details"] "print_colorized", "print_json", "hyperopt_list_no_details",
"export_csv"]
ARGS_HYPEROPT_SHOW = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperopt_show_index", ARGS_HYPEROPT_SHOW = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperopt_show_index",
"print_json", "hyperopt_show_no_header"] "print_json", "hyperopt_show_no_header"]

View File

@ -221,6 +221,13 @@ AVAILABLE_CLI_OPTIONS = {
action='store_true', action='store_true',
default=False, default=False,
), ),
"export_csv": Arg(
'--export-csv',
help='Export to CSV-File.'
' This will disable table print.'
' Example: --export-csv hyperopt.csv',
metavar='FILE',
),
"hyperopt_jobs": Arg( "hyperopt_jobs": Arg(
'-j', '--job-workers', '-j', '--job-workers',
help='The number of concurrently running jobs for hyperoptimization ' help='The number of concurrently running jobs for hyperoptimization '

View File

@ -21,6 +21,7 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None:
print_colorized = config.get('print_colorized', False) print_colorized = config.get('print_colorized', False)
print_json = config.get('print_json', False) print_json = config.get('print_json', False)
export_csv = config.get('export_csv', None)
no_details = config.get('hyperopt_list_no_details', False) no_details = config.get('hyperopt_list_no_details', False)
no_header = False no_header = False
@ -49,6 +50,7 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None:
if print_colorized: if print_colorized:
colorama_init(autoreset=True) colorama_init(autoreset=True)
if not export_csv:
try: try:
Hyperopt.print_result_table(config, trials, total_epochs, Hyperopt.print_result_table(config, trials, total_epochs,
not filteroptions['only_best'], print_colorized, 0) not filteroptions['only_best'], print_colorized, 0)
@ -60,6 +62,11 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None:
results = sorted_trials[0] results = sorted_trials[0]
Hyperopt.print_epoch_details(results, total_epochs, print_json, no_header) Hyperopt.print_epoch_details(results, total_epochs, print_json, no_header)
if trials and export_csv:
Hyperopt.export_csv_file(
config, trials, total_epochs, not filteroptions['only_best'], export_csv
)
def start_hyperopt_show(args: Dict[str, Any]) -> None: def start_hyperopt_show(args: Dict[str, Any]) -> None:
""" """

View File

@ -17,10 +17,15 @@ def setup_optimize_configuration(args: Dict[str, Any], method: RunMode) -> Dict[
""" """
config = setup_utils_configuration(args, method) config = setup_utils_configuration(args, method)
if method == RunMode.BACKTEST: no_unlimited_runmodes = {
if config['stake_amount'] == constants.UNLIMITED_STAKE_AMOUNT: RunMode.BACKTEST: 'backtesting',
raise DependencyException('stake amount could not be "%s" for backtesting' % RunMode.HYPEROPT: 'hyperoptimization',
constants.UNLIMITED_STAKE_AMOUNT) }
if (method in no_unlimited_runmodes.keys() and
config['stake_amount'] == constants.UNLIMITED_STAKE_AMOUNT):
raise DependencyException(
f'The value of `stake_amount` cannot be set as "{constants.UNLIMITED_STAKE_AMOUNT}" '
f'for {no_unlimited_runmodes[method]}')
return config return config

View File

@ -282,6 +282,9 @@ class Configuration:
self._args_to_config(config, argname='print_json', self._args_to_config(config, argname='print_json',
logstring='Parameter --print-json detected ...') logstring='Parameter --print-json detected ...')
self._args_to_config(config, argname='export_csv',
logstring='Parameter --export-csv detected: {}')
self._args_to_config(config, argname='hyperopt_jobs', self._args_to_config(config, argname='hyperopt_jobs',
logstring='Parameter -j/--job-workers detected: {}') logstring='Parameter -j/--job-workers detected: {}')

View File

@ -145,27 +145,40 @@ class IDataHandler(ABC):
if startup_candles > 0 and timerange_startup: if startup_candles > 0 and timerange_startup:
timerange_startup.subtract_start(timeframe_to_seconds(timeframe) * startup_candles) timerange_startup.subtract_start(timeframe_to_seconds(timeframe) * startup_candles)
df = self._ohlcv_load(pair, timeframe, timerange=timerange_startup) pairdf = self._ohlcv_load(pair, timeframe,
if df.empty: timerange=timerange_startup)
if self._check_empty_df(pairdf, pair, timeframe, warn_no_data):
return pairdf
else:
enddate = df.iloc[-1]['date']
if timerange_startup:
self._validate_pairdata(pair, pairdf, timerange_startup)
pairdf = trim_dataframe(pairdf, timerange_startup)
if self._check_empty_df(pairdf, pair, timeframe, warn_no_data):
return pairdf
# incomplete candles should only be dropped if we didn't trim the end beforehand.
pairdf = clean_ohlcv_dataframe(pairdf, timeframe,
pair=pair,
fill_missing=fill_missing,
drop_incomplete=(drop_incomplete and
enddate == pairdf.iloc[-1]['date']))
self._check_empty_df(pairdf, pair, timeframe, warn_no_data)
return pairdf
def _check_empty_df(self, pairdf: DataFrame, pair: str, timeframe: str, warn_no_data: bool):
"""
Warn on empty dataframe
"""
if pairdf.empty:
if warn_no_data: if warn_no_data:
logger.warning( logger.warning(
f'No history data for pair: "{pair}", timeframe: {timeframe}. ' f'No history data for pair: "{pair}", timeframe: {timeframe}. '
'Use `freqtrade download-data` to download the data' 'Use `freqtrade download-data` to download the data'
) )
return df return True
else: return False
enddate = df.iloc[-1]['date']
if timerange_startup:
self._validate_pairdata(pair, df, timerange_startup)
df = trim_dataframe(df, timerange_startup)
# incomplete candles should only be dropped if we didn't trim the end beforehand.
return clean_ohlcv_dataframe(df, timeframe,
pair=pair,
fill_missing=fill_missing,
drop_incomplete=(drop_incomplete and
enddate == df.iloc[-1]['date']))
def _validate_pairdata(self, pair, pairdata: DataFrame, timerange: TimeRange): def _validate_pairdata(self, pair, pairdata: DataFrame, timerange: TimeRange):
""" """

View File

@ -23,6 +23,8 @@ from joblib import (Parallel, cpu_count, delayed, dump, load,
wrap_non_picklable_objects) wrap_non_picklable_objects)
from pandas import DataFrame, json_normalize, isna from pandas import DataFrame, json_normalize, isna
import tabulate import tabulate
from os import path
import io
from freqtrade.data.converter import trim_dataframe from freqtrade.data.converter import trim_dataframe
from freqtrade.data.history import get_timerange from freqtrade.data.history import get_timerange
@ -330,10 +332,10 @@ class Hyperopt:
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: ('{:,.2f}%'.format(x)).rjust(7, ' ') if not isna(x) else "--".rjust(7, ' ') lambda x: '{:,.2f}%'.format(x).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: ('{:,.1f} m'.format(x)).rjust(7, ' ') if not isna(x) else "--".rjust(7, ' ') lambda x: '{:,.1f} m'.format(x).rjust(7, ' ') if not isna(x) else "--".rjust(7, ' ')
) )
trials['Objective'] = trials['Objective'].apply( trials['Objective'] = trials['Objective'].apply(
lambda x: '{:,.5f}'.format(x).rjust(8, ' ') if x != 100000 else "N/A".rjust(8, ' ') lambda x: '{:,.5f}'.format(x).rjust(8, ' ') if x != 100000 else "N/A".rjust(8, ' ')
@ -381,6 +383,62 @@ class Hyperopt:
) )
print(table) print(table)
@staticmethod
def export_csv_file(config: dict, results: list, total_epochs: int, highlight_best: bool,
csv_file: str) -> None:
"""
Log result to csv-file
"""
if not results:
return
# Verification for overwrite
if path.isfile(csv_file):
logger.error("CSV-File already exists!")
return
try:
io.open(csv_file, 'w+').close()
except IOError:
logger.error("Filed to create CSV-File!")
return
trials = json_normalize(results, max_level=1)
trials['Best'] = ''
trials['Stake currency'] = config['stake_currency']
trials = trials[['Best', 'current_epoch', 'results_metrics.trade_count',
'results_metrics.avg_profit', 'results_metrics.total_profit',
'Stake currency', 'results_metrics.profit', 'results_metrics.duration',
'loss', 'is_initial_point', 'is_best']]
trials.columns = ['Best', 'Epoch', 'Trades', 'Avg profit', 'Total profit', 'Stake currency',
'Profit', 'Avg duration', 'Objective', 'is_initial_point', 'is_best']
trials['is_profit'] = False
trials.loc[trials['is_initial_point'], 'Best'] = '*'
trials.loc[trials['is_best'], 'Best'] = 'Best'
trials.loc[trials['Total profit'] > 0, 'is_profit'] = True
trials['Epoch'] = trials['Epoch'].astype(str)
trials['Trades'] = trials['Trades'].astype(str)
trials['Total profit'] = trials['Total profit'].apply(
lambda x: '{:,.8f}'.format(x) if x != 0.0 else ""
)
trials['Profit'] = trials['Profit'].apply(
lambda x: '{:,.2f}'.format(x) if not isna(x) else ""
)
trials['Avg profit'] = trials['Avg profit'].apply(
lambda x: '{:,.2f}%'.format(x) if not isna(x) else ""
)
trials['Avg duration'] = trials['Avg duration'].apply(
lambda x: '{:,.1f} m'.format(x) if not isna(x) else ""
)
trials['Objective'] = trials['Objective'].apply(
lambda x: '{:,.5f}'.format(x) if x != 100000 else ""
)
trials = trials.drop(columns=['is_initial_point', 'is_best', 'is_profit'])
trials.to_csv(csv_file, index=False, header=True, mode='w', encoding='UTF-8')
print("CSV-File created!")
def has_space(self, space: str) -> bool: def has_space(self, space: str) -> bool:
""" """
Tell if the space value is contained in the configuration Tell if the space value is contained in the configuration

View File

@ -36,7 +36,7 @@ class SharpeHyperOptLoss(IHyperOptLoss):
expected_returns_mean = total_profit.sum() / days_period expected_returns_mean = total_profit.sum() / days_period
up_stdev = np.std(total_profit) up_stdev = np.std(total_profit)
if (np.std(total_profit) != 0.): if up_stdev != 0:
sharp_ratio = expected_returns_mean / up_stdev * np.sqrt(365) sharp_ratio = expected_returns_mean / up_stdev * np.sqrt(365)
else: else:
# Define high (negative) sharpe ratio to be clear that this is NOT optimal. # Define high (negative) sharpe ratio to be clear that this is NOT optimal.

View File

@ -51,7 +51,7 @@ class SharpeHyperOptLossDaily(IHyperOptLoss):
expected_returns_mean = total_profit.mean() expected_returns_mean = total_profit.mean()
up_stdev = total_profit.std() up_stdev = total_profit.std()
if (up_stdev != 0.): if up_stdev != 0:
sharp_ratio = expected_returns_mean / up_stdev * math.sqrt(days_in_year) sharp_ratio = expected_returns_mean / up_stdev * math.sqrt(days_in_year)
else: else:
# Define high (negative) sharpe ratio to be clear that this is NOT optimal. # Define high (negative) sharpe ratio to be clear that this is NOT optimal.

View File

@ -39,7 +39,7 @@ class SortinoHyperOptLoss(IHyperOptLoss):
results.loc[total_profit < 0, 'downside_returns'] = results['profit_percent'] results.loc[total_profit < 0, 'downside_returns'] = results['profit_percent']
down_stdev = np.std(results['downside_returns']) down_stdev = np.std(results['downside_returns'])
if np.std(total_profit) != 0.0: if down_stdev != 0:
sortino_ratio = expected_returns_mean / down_stdev * np.sqrt(365) sortino_ratio = expected_returns_mean / down_stdev * np.sqrt(365)
else: else:
# Define high (negative) sortino ratio to be clear that this is NOT optimal. # Define high (negative) sortino ratio to be clear that this is NOT optimal.

View File

@ -59,7 +59,7 @@ class SortinoHyperOptLossDaily(IHyperOptLoss):
# where P = sum_daily["profit_percent_after_slippage"] # where P = sum_daily["profit_percent_after_slippage"]
down_stdev = math.sqrt((total_downside**2).sum() / len(total_downside)) down_stdev = math.sqrt((total_downside**2).sum() / len(total_downside))
if (down_stdev != 0.): if down_stdev != 0:
sortino_ratio = expected_returns_mean / down_stdev * math.sqrt(days_in_year) sortino_ratio = expected_returns_mean / down_stdev * math.sqrt(days_in_year)
else: else:
# Define high (negative) sortino ratio to be clear that this is NOT optimal. # Define high (negative) sortino ratio to be clear that this is NOT optimal.

View File

@ -67,21 +67,37 @@ class IPairList(ABC):
""" """
@staticmethod @staticmethod
def verify_blacklist(pairlist: List[str], blacklist: List[str]) -> List[str]: def verify_blacklist(pairlist: List[str], blacklist: List[str],
aswarning: bool) -> List[str]:
""" """
Verify and remove items from pairlist - returning a filtered pairlist. Verify and remove items from pairlist - returning a filtered pairlist.
Logs a warning or info depending on `aswarning`.
Pairlists explicitly using this method shall use `aswarning=False`!
:param pairlist: Pairlist to validate
:param blacklist: Blacklist to validate pairlist against
:param aswarning: Log message as Warning or info
:return: pairlist - blacklisted pairs
""" """
for pair in deepcopy(pairlist): for pair in deepcopy(pairlist):
if pair in blacklist: if pair in blacklist:
if aswarning:
logger.warning(f"Pair {pair} in your blacklist. Removing it from whitelist...") logger.warning(f"Pair {pair} in your blacklist. Removing it from whitelist...")
else:
logger.info(f"Pair {pair} in your blacklist. Removing it from whitelist...")
pairlist.remove(pair) pairlist.remove(pair)
return pairlist return pairlist
def _verify_blacklist(self, pairlist: List[str]) -> List[str]: def _verify_blacklist(self, pairlist: List[str], aswarning: bool = True) -> List[str]:
""" """
Proxy method to verify_blacklist for easy access for child classes. Proxy method to verify_blacklist for easy access for child classes.
Logs a warning or info depending on `aswarning`.
Pairlists explicitly using this method shall use aswarning=False!
:param pairlist: Pairlist to validate
:param aswarning: Log message as Warning or info.
:return: pairlist - blacklisted pairs
""" """
return IPairList.verify_blacklist(pairlist, self._pairlistmanager.blacklist) return IPairList.verify_blacklist(pairlist, self._pairlistmanager.blacklist,
aswarning=aswarning)
def _whitelist_for_active_markets(self, pairlist: List[str]) -> List[str]: def _whitelist_for_active_markets(self, pairlist: List[str]) -> List[str]:
""" """
@ -113,6 +129,5 @@ class IPairList(ABC):
if pair not in sanitized_whitelist: if pair not in sanitized_whitelist:
sanitized_whitelist.append(pair) sanitized_whitelist.append(pair)
sanitized_whitelist = self._verify_blacklist(sanitized_whitelist)
# We need to remove pairs that are unknown # We need to remove pairs that are unknown
return sanitized_whitelist return sanitized_whitelist

View File

@ -106,7 +106,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) pairs = self._verify_blacklist(pairs, aswarning=False)
# Limit to X number of pairs # Limit to X number of pairs
pairs = pairs[:self._number_pairs] pairs = pairs[:self._number_pairs]
logger.info(f"Searching {self._number_pairs} pairs: {pairs}") logger.info(f"Searching {self._number_pairs} pairs: {pairs}")

View File

@ -91,6 +91,6 @@ class PairListManager():
pairlist = pl.filter_pairlist(pairlist, tickers) pairlist = pl.filter_pairlist(pairlist, tickers)
# Validation against blacklist happens after the pairlists to ensure blacklist is respected. # Validation against blacklist happens after the pairlists to ensure blacklist is respected.
pairlist = IPairList.verify_blacklist(pairlist, self.blacklist) pairlist = IPairList.verify_blacklist(pairlist, self.blacklist, True)
self._whitelist = pairlist self._whitelist = pairlist

View File

@ -24,7 +24,7 @@
"price_side": "ask", "price_side": "ask",
"use_order_book": false, "use_order_book": false,
"order_book_min": 1, "order_book_min": 1,
"order_book_max": 9, "order_book_max": 1,
"use_sell_signal": true, "use_sell_signal": true,
"sell_profit_only": false, "sell_profit_only": false,
"ignore_roi_if_buy_signal": false "ignore_roi_if_buy_signal": false

View File

@ -21,7 +21,7 @@ class {{ hyperopt }}(IHyperOpt):
""" """
This is a Hyperopt template to get you started. This is a Hyperopt template to get you started.
More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/
You should: You should:
- Add any lib you need to build your hyperopt. - Add any lib you need to build your hyperopt.
@ -29,11 +29,14 @@ class {{ hyperopt }}(IHyperOpt):
You must keep: You must keep:
- The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator. - The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator.
The roi_space, generate_roi_table, stoploss_space methods are no longer required to be The methods roi_space, generate_roi_table and stoploss_space are not required
copied in every custom hyperopt. However, you may override them if you need the and are provided by default.
'roi' and the 'stoploss' spaces that differ from the defaults offered by Freqtrade. However, you may override them if you need 'roi' and 'stoploss' spaces that
Sample implementation of these methods can be found in differ from the defaults offered by Freqtrade.
https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/sample_hyperopt_advanced.py Sample implementation of these methods will be copied to `user_data/hyperopts` when
creating the user-data directory using `freqtrade create-userdir --userdir user_data`,
or is available online under the following URL:
https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py.
""" """
@staticmethod @staticmethod
@ -63,6 +66,9 @@ class {{ hyperopt }}(IHyperOpt):
dataframe['close'], dataframe['sar'] dataframe['close'], dataframe['sar']
)) ))
# Check that the candle had volume
conditions.append(dataframe['volume'] > 0)
if conditions: if conditions:
dataframe.loc[ dataframe.loc[
reduce(lambda x, y: x & y, conditions), reduce(lambda x, y: x & y, conditions),
@ -108,6 +114,9 @@ class {{ hyperopt }}(IHyperOpt):
dataframe['sar'], dataframe['close'] dataframe['sar'], dataframe['close']
)) ))
# Check that the candle had volume
conditions.append(dataframe['volume'] > 0)
if conditions: if conditions:
dataframe.loc[ dataframe.loc[
reduce(lambda x, y: x & y, conditions), reduce(lambda x, y: x & y, conditions),

View File

@ -20,23 +20,28 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib
class SampleHyperOpt(IHyperOpt): class SampleHyperOpt(IHyperOpt):
""" """
This is a sample Hyperopt to inspire you. This is a sample Hyperopt to inspire you.
Feel free to customize it.
More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/
You should: You should:
- Rename the class name to some unique name. - Rename the class name to some unique name.
- Add any methods you want to build your hyperopt. - Add any methods you want to build your hyperopt.
- Add any lib you need to build your hyperopt. - Add any lib you need to build your hyperopt.
An easier way to get a new hyperopt file is by using
`freqtrade new-hyperopt --hyperopt MyCoolHyperopt`.
You must keep: You must keep:
- The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator. - The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator.
The roi_space, generate_roi_table, stoploss_space methods are no longer required to be The methods roi_space, generate_roi_table and stoploss_space are not required
copied in every custom hyperopt. However, you may override them if you need the and are provided by default.
'roi' and the 'stoploss' spaces that differ from the defaults offered by Freqtrade. However, you may override them if you need 'roi' and 'stoploss' spaces that
Sample implementation of these methods can be found in differ from the defaults offered by Freqtrade.
https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/sample_hyperopt_advanced.py Sample implementation of these methods will be copied to `user_data/hyperopts` when
creating the user-data directory using `freqtrade create-userdir --userdir user_data`,
or is available online under the following URL:
https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py.
""" """
@staticmethod @staticmethod
@ -73,6 +78,9 @@ class SampleHyperOpt(IHyperOpt):
dataframe['close'], dataframe['sar'] dataframe['close'], dataframe['sar']
)) ))
# Check that volume is not 0
conditions.append(dataframe['volume'] > 0)
if conditions: if conditions:
dataframe.loc[ dataframe.loc[
reduce(lambda x, y: x & y, conditions), reduce(lambda x, y: x & y, conditions),
@ -133,6 +141,9 @@ class SampleHyperOpt(IHyperOpt):
dataframe['sar'], dataframe['close'] dataframe['sar'], dataframe['close']
)) ))
# Check that volume is not 0
conditions.append(dataframe['volume'] > 0)
if conditions: if conditions:
dataframe.loc[ dataframe.loc[
reduce(lambda x, y: x & y, conditions), reduce(lambda x, y: x & y, conditions),

View File

@ -22,7 +22,7 @@ class AdvancedSampleHyperOpt(IHyperOpt):
This is a sample hyperopt to inspire you. This is a sample hyperopt to inspire you.
Feel free to customize it. Feel free to customize it.
More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/
You should: You should:
- Rename the class name to some unique name. - Rename the class name to some unique name.
@ -32,8 +32,9 @@ class AdvancedSampleHyperOpt(IHyperOpt):
You must keep: You must keep:
- The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator. - The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator.
The roi_space, generate_roi_table, stoploss_space methods are no longer required to be The methods roi_space, generate_roi_table and stoploss_space are not required
copied in every custom hyperopt. However, you may override them if you need the and are provided by default.
However, you may override them if you need the
'roi' and the 'stoploss' spaces that differ from the defaults offered by Freqtrade. 'roi' and the 'stoploss' spaces that differ from the defaults offered by Freqtrade.
This sample illustrates how to override these methods. This sample illustrates how to override these methods.
@ -92,6 +93,9 @@ class AdvancedSampleHyperOpt(IHyperOpt):
dataframe['close'], dataframe['sar'] dataframe['close'], dataframe['sar']
)) ))
# Check that volume is not 0
conditions.append(dataframe['volume'] > 0)
if conditions: if conditions:
dataframe.loc[ dataframe.loc[
reduce(lambda x, y: x & y, conditions), reduce(lambda x, y: x & y, conditions),
@ -152,6 +156,9 @@ class AdvancedSampleHyperOpt(IHyperOpt):
dataframe['sar'], dataframe['close'] dataframe['sar'], dataframe['close']
)) ))
# Check that volume is not 0
conditions.append(dataframe['volume'] > 0)
if conditions: if conditions:
dataframe.loc[ dataframe.loc[
reduce(lambda x, y: x & y, conditions), reduce(lambda x, y: x & y, conditions),

View File

@ -1,13 +1,13 @@
# requirements without requirements installable via conda # requirements without requirements installable via conda
# mainly used for Raspberry pi installs # mainly used for Raspberry pi installs
ccxt==1.23.30 ccxt==1.23.81
SQLAlchemy==1.3.13 SQLAlchemy==1.3.13
python-telegram-bot==12.4.2 python-telegram-bot==12.4.2
arrow==0.15.5 arrow==0.15.5
cachetools==4.0.0 cachetools==4.0.0
requests==2.23.0 requests==2.23.0
urllib3==1.25.8 urllib3==1.25.8
wrapt==1.12.0 wrapt==1.12.1
jsonschema==3.2.0 jsonschema==3.2.0
TA-Lib==0.4.17 TA-Lib==0.4.17
tabulate==0.8.6 tabulate==0.8.6
@ -30,4 +30,4 @@ flask==1.1.1
colorama==0.4.3 colorama==0.4.3
# Building config files interactively # Building config files interactively
questionary==1.5.1 questionary==1.5.1
prompt-toolkit==3.0.3 prompt-toolkit==3.0.4

View File

@ -3,7 +3,7 @@
# Required for hyperopt # Required for hyperopt
scipy==1.4.1 scipy==1.4.1
scikit-learn==0.22.2 scikit-learn==0.22.2.post1
scikit-optimize==0.7.4 scikit-optimize==0.7.4
filelock==3.0.12 filelock==3.0.12
joblib==0.14.1 joblib==0.14.1

View File

@ -1,5 +1,5 @@
# Include all requirements to run the bot. # Include all requirements to run the bot.
-r requirements.txt -r requirements.txt
plotly==4.5.2 plotly==4.5.3

View File

@ -902,6 +902,21 @@ def test_hyperopt_list(mocker, capsys, hyperopt_results):
assert all(x not in captured.out assert all(x not in captured.out
for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 7/12", " 8/12" for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 7/12", " 8/12"
" 9/12", " 10/12", " 11/12", " 12/12"]) " 9/12", " 10/12", " 11/12", " 12/12"])
args = [
"hyperopt-list",
"--no-details",
"--export-csv", "test_file.csv"
]
pargs = get_args(args)
pargs['config'] = None
start_hyperopt_list(pargs)
captured = capsys.readouterr()
assert all(x in captured.out
for x in ["CSV-File created!"])
f = Path("test_file.csv")
assert 'Best,1,2,-1.25%,-0.00125625,,-2.51,"3,930.0 m",0.43662' in f.read_text()
assert f.is_file()
f.unlink()
def test_hyperopt_show(mocker, capsys, hyperopt_results): def test_hyperopt_show(mocker, capsys, hyperopt_results):

View File

@ -241,7 +241,7 @@ def test_setup_optimize_configuration_unlimited_stake_amount(mocker, default_con
'--strategy', 'DefaultStrategy', '--strategy', 'DefaultStrategy',
] ]
with pytest.raises(DependencyException, match=r'.*stake amount.*'): with pytest.raises(DependencyException, match=r'.`stake_amount`.*'):
setup_optimize_configuration(get_args(args), RunMode.BACKTEST) setup_optimize_configuration(get_args(args), RunMode.BACKTEST)

View File

@ -10,10 +10,11 @@ import pytest
from arrow import Arrow from arrow import Arrow
from filelock import Timeout from filelock import Timeout
from freqtrade import constants
from freqtrade.commands.optimize_commands import (setup_optimize_configuration, from freqtrade.commands.optimize_commands import (setup_optimize_configuration,
start_hyperopt) start_hyperopt)
from freqtrade.data.history import load_data from freqtrade.data.history import load_data
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.optimize.default_hyperopt import DefaultHyperOpt from freqtrade.optimize.default_hyperopt import DefaultHyperOpt
from freqtrade.optimize.default_hyperopt_loss import DefaultHyperOptLoss from freqtrade.optimize.default_hyperopt_loss import DefaultHyperOptLoss
from freqtrade.optimize.hyperopt import Hyperopt from freqtrade.optimize.hyperopt import Hyperopt
@ -158,6 +159,21 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
assert log_has('Parameter --print-all detected ...', caplog) assert log_has('Parameter --print-all detected ...', caplog)
def test_setup_hyperopt_configuration_unlimited_stake_amount(mocker, default_conf, caplog) -> None:
default_conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
patched_configuration_load_config_file(mocker, default_conf)
args = [
'hyperopt',
'--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt',
]
with pytest.raises(DependencyException, match=r'.`stake_amount`.*'):
setup_optimize_configuration(get_args(args), RunMode.HYPEROPT)
def test_hyperoptresolver(mocker, default_conf, caplog) -> None: def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)

View File

@ -240,8 +240,6 @@ def test_pairlist_class(mocker, whitelist_conf, markets, pairlist):
(['ETH/BTC', 'TKN/BTC', 'ETH/USDT'], "is not compatible with your stake currency"), (['ETH/BTC', 'TKN/BTC', 'ETH/USDT'], "is not compatible with your stake currency"),
# BCH/BTC not available # BCH/BTC not available
(['ETH/BTC', 'TKN/BTC', 'BCH/BTC'], "is not compatible with exchange"), (['ETH/BTC', 'TKN/BTC', 'BCH/BTC'], "is not compatible with exchange"),
# BLK/BTC in blacklist
(['ETH/BTC', 'TKN/BTC', 'BLK/BTC'], "in your blacklist. Removing "),
# BTT/BTC is inactive # BTT/BTC is inactive
(['ETH/BTC', 'TKN/BTC', 'BTT/BTC'], "Market is not active") (['ETH/BTC', 'TKN/BTC', 'BTT/BTC'], "Market is not active")
]) ])