Merge pull request #1 from Bovhasselt/Bo

Bo
This commit is contained in:
Bo van Hasselt 2021-02-11 00:42:23 +01:00 committed by GitHub
commit 7e400aa917
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 202 additions and 20 deletions

View File

@ -12,6 +12,7 @@ from freqtrade.commands.data_commands import (start_convert_data, start_download
start_list_data)
from freqtrade.commands.deploy_commands import (start_create_userdir, start_install_ui,
start_new_hyperopt, start_new_strategy)
from freqtrade.commands.automation_commands import start_build_hyperopt
from freqtrade.commands.hyperopt_commands import start_hyperopt_list, start_hyperopt_show
from freqtrade.commands.list_commands import (start_list_exchanges, start_list_hyperopts,
start_list_markets, start_list_strategies,

View File

@ -55,6 +55,8 @@ ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"]
ARGS_BUILD_HYPEROPT = ["user_data_dir", "hyperopt", "template"]
ARGS_BUILD_CUSTOM_HYPEROPT = ["buy_indicators", "sell_indicators", "hyperopt"]
ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"]
ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes"]
@ -173,6 +175,7 @@ class Arguments:
start_list_data, start_list_exchanges, start_list_hyperopts,
start_list_markets, start_list_strategies,
start_list_timeframes, start_new_config, start_new_hyperopt,
start_build_hyperopt,
start_new_strategy, start_plot_dataframe, start_plot_profit,
start_show_trades, start_test_pairlist, start_trading)
@ -207,6 +210,12 @@ class Arguments:
build_hyperopt_cmd.set_defaults(func=start_new_hyperopt)
self._build_args(optionlist=ARGS_BUILD_HYPEROPT, parser=build_hyperopt_cmd)
# add build-hyperopt subcommand
build_custom_hyperopt_cmd = subparsers.add_parser('build-hyperopt',
help="Build a custom hyperopt")
build_custom_hyperopt_cmd.set_defaults(func=start_build_hyperopt)
self._build_args(optionlist=ARGS_BUILD_CUSTOM_HYPEROPT, parser=build_custom_hyperopt_cmd)
# add new-strategy subcommand
build_strategy_cmd = subparsers.add_parser('new-strategy',
help="Create new strategy")

View File

@ -0,0 +1,177 @@
import ast
import logging
from pathlib import Path
from typing import Any, Dict
from freqtrade.constants import USERPATH_HYPEROPTS
from freqtrade.exceptions import OperationalException
from freqtrade.state import RunMode
from freqtrade.configuration import setup_utils_configuration
from freqtrade.misc import render_template
logger = logging.getLogger(__name__)
'''
TODO
-make the code below more dynamic with a large list of indicators and aims
-buy_space integer values variation based on aim(later deep learning)
-add --mode , see notes
-when making the strategy reading tool, make sure that the populate indicators gets copied to here
'''
POSSIBLE_GUARDS = ["rsi", "mfi", "fastd"]
POSSIBLE_TRIGGERS = ["bb_lowerband", "bb_upperband"]
POSSIBLE_VALUES = {"above": ">", "below": "<"}
def build_hyperopt_buyelements(buy_indicators: Dict[str, str]):
"""
Build the arguments with the placefillers for the buygenerator
"""
buy_guards = ""
buy_triggers = ""
buy_space = ""
for indicator in buy_indicators:
# Error handling
if not indicator in POSSIBLE_GUARDS and not indicator in POSSIBLE_TRIGGERS:
raise OperationalException(
f"`{indicator}` is not part of the available indicators. The current options are {POSSIBLE_GUARDS + POSSIBLE_TRIGGERS}.")
elif not buy_indicators[indicator] in POSSIBLE_VALUES:
raise OperationalException(
f"`{buy_indicators[indicator]}` is not part of the available indicator options. The current options are {POSSIBLE_VALUES}.")
# If the indicator is a guard
elif indicator in POSSIBLE_GUARDS:
# get the symbol corrosponding to the value
aim = POSSIBLE_VALUES[buy_indicators[indicator]]
# add the guard to its argument
buy_guards += f"if '{indicator}-enabled' in params and params['{indicator}-enabled']: conditions.append(dataframe['{indicator}'] {aim} params['{indicator}-value'])"
# add the space to its argument
buy_space += f"Integer(10, 90, name='{indicator}-value'), Categorical([True, False], name='{indicator}-enabled'),"
# If the indicator is a trigger
elif indicator in POSSIBLE_TRIGGERS:
# get the symbol corrosponding to the value
aim = POSSIBLE_VALUES[buy_indicators[indicator]]
# add the trigger to its argument
buy_triggers += f"if params['trigger'] == '{indicator}': conditions.append(dataframe['{indicator}'] {aim} dataframe['close'])"
# Final line of indicator space makes all triggers
buy_space += "Categorical(["
# adding all triggers to the list
for indicator in buy_indicators:
if indicator in POSSIBLE_TRIGGERS:
buy_space += f"'{indicator}', "
# Deleting the last ", "
buy_space = buy_space[:-2]
buy_space += "], name='trigger')"
return {"buy_guards": buy_guards, "buy_triggers": buy_triggers, "buy_space": buy_space}
def build_hyperopt_sellelements(sell_indicators: Dict[str, str]):
"""
Build the arguments with the placefillers for the sellgenerator
"""
sell_guards = ""
sell_triggers = ""
sell_space = ""
for indicator in sell_indicators:
# Error handling
if not indicator in POSSIBLE_GUARDS and not indicator in POSSIBLE_TRIGGERS:
raise OperationalException(
f"`{indicator}` is not part of the available indicators. The current options are {POSSIBLE_GUARDS + POSSIBLE_TRIGGERS}.")
elif not sell_indicators[indicator] in POSSIBLE_VALUES:
raise OperationalException(
f"`{sell_indicators[indicator]}` is not part of the available indicator options. The current options are {POSSIBLE_VALUES}.")
# If indicator is a guard
elif indicator in POSSIBLE_GUARDS:
# get the symbol corrosponding to the value
aim = POSSIBLE_VALUES[sell_indicators[indicator]]
# add the guard to its argument
sell_guards += f"if '{indicator}-enabled' in params and params['sell-{indicator}-enabled']: conditions.append(dataframe['{indicator}'] {aim} params['sell-{indicator}-value'])"
# add the space to its argument
sell_space += f"Integer(10, 90, name='sell-{indicator}-value'), Categorical([True, False], name='sell-{indicator}-enabled'),"
# If the indicator is a trigger
elif indicator in POSSIBLE_TRIGGERS:
# get the symbol corrosponding to the value
aim = POSSIBLE_VALUES[sell_indicators[indicator]]
# add the trigger to its argument
sell_triggers += f"if params['sell-trigger'] == 'sell-{indicator}': conditions.append(dataframe['{indicator}'] {aim} dataframe['close'])"
# Final line of indicator space makes all triggers
sell_space += "Categorical(["
# Adding all triggers to the list
for indicator in sell_indicators:
if indicator in POSSIBLE_TRIGGERS:
sell_space += f"'sell-{indicator}', "
# Deleting the last ", "
sell_space = sell_space[:-2]
sell_space += "], name='trigger')"
return {"sell_guards": sell_guards, "sell_triggers": sell_triggers, "sell_space": sell_space}
def deploy_custom_hyperopt(hyperopt_name: str, hyperopt_path: Path, buy_indicators: Dict[str, str], sell_indicators: Dict[str, str]) -> None:
"""
Deploys a custom hyperopt template to hyperopt_path
"""
# Build the arguments for the buy and sell generators
buy_args = build_hyperopt_buyelements(buy_indicators)
sell_args = build_hyperopt_sellelements(sell_indicators)
# Build the final template
strategy_text = render_template(templatefile='base_hyperopt.py.j2',
arguments={"hyperopt": hyperopt_name,
"buy_guards": buy_args["buy_guards"],
"buy_triggers": buy_args["buy_triggers"],
"buy_space": buy_args["buy_space"],
"sell_guards": sell_args["sell_guards"],
"sell_triggers": sell_args["sell_triggers"],
"sell_space": sell_args["sell_space"],
})
logger.info(f"Writing custom hyperopt to `{hyperopt_path}`.")
hyperopt_path.write_text(strategy_text)
def start_build_hyperopt(args: Dict[str, Any]) -> None:
"""
Check if the right subcommands where passed and start building the hyperopt
"""
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
# check what the name of the hyperopt should
if not 'hyperopt' in args or not args['hyperopt']:
raise OperationalException("`build-hyperopt` requires --hyperopt to be set.")
elif not 'buy_indicators' in args or not args['buy_indicators']:
raise OperationalException("`build-hyperopt` requires --buy-indicators to be set.")
elif not 'sell_indicators' in args or not args['sell_indicators']:
raise OperationalException("`build-hyperopt` requires --sell-indicators to be set.")
else:
if args['hyperopt'] == 'DefaultHyperopt':
raise OperationalException("DefaultHyperopt is not allowed as name.")
new_path = config['user_data_dir'] / USERPATH_HYPEROPTS / (args['hyperopt'] + '.py')
if new_path.exists():
raise OperationalException(f"`{new_path}` already exists. "
"Please choose another Hyperopt Name.")
buy_indicators = ast.literal_eval(args['buy_indicators'])
sell_indicators = ast.literal_eval(args['sell_indicators'])
deploy_custom_hyperopt(args['hyperopt'], new_path,
buy_indicators, sell_indicators)

View File

@ -275,6 +275,19 @@ AVAILABLE_CLI_OPTIONS = {
'Example: `--hyperopt-filename=hyperopt_results_2020-09-27_16-20-48.pickle`',
metavar='FILENAME',
),
# Automation
"buy_indicators": Arg(
'-b', '--buy-indicators',
help='Specify the buy indicators the hyperopt should build. '
'Example: --buy-indicators `{"rsi":"above","bb_lowerband":"below"}`',
metavar='DICT',
),
"sell_indicators": Arg(
'-s', '--sell-indicators',
help='Specify the sell indicators the hyperopt should build. '
'Example: --sell-indicators `{"rsi":"above","bb_lowerband":"below"}`',
metavar='DICT',
),
# List exchanges
"print_one_column": Arg(
'-1', '--one-column',

View File

@ -55,16 +55,7 @@ class {{ hyperopt }}(IHyperOpt):
# TRIGGERS
if 'trigger' in params:
if params['trigger'] == 'bb_lower':
conditions.append(dataframe['close'] < dataframe['bb_lowerband'])
if params['trigger'] == 'macd_cross_signal':
conditions.append(qtpylib.crossed_above(
dataframe['macd'], dataframe['macdsignal']
))
if params['trigger'] == 'sar_reversal':
conditions.append(qtpylib.crossed_above(
dataframe['close'], dataframe['sar']
))
{{ buy_triggers | indent(16) }}
# Check that the candle had volume
conditions.append(dataframe['volume'] > 0)
@ -103,16 +94,7 @@ class {{ hyperopt }}(IHyperOpt):
# TRIGGERS
if 'sell-trigger' in params:
if params['sell-trigger'] == 'sell-bb_upper':
conditions.append(dataframe['close'] > dataframe['bb_upperband'])
if params['sell-trigger'] == 'sell-macd_cross_signal':
conditions.append(qtpylib.crossed_above(
dataframe['macdsignal'], dataframe['macd']
))
if params['sell-trigger'] == 'sell-sar_reversal':
conditions.append(qtpylib.crossed_above(
dataframe['sar'], dataframe['close']
))
{{ sell_triggers | indent(16) }}
# Check that the candle had volume
conditions.append(dataframe['volume'] > 0)