Merge branch 'develop' of https://github.com/Bovhasselt/Tradingbotv1.1 into develop
This commit is contained in:
commit
535e7a41f2
@ -12,7 +12,7 @@ from freqtrade.commands.data_commands import (start_convert_data, start_download
|
|||||||
start_list_data)
|
start_list_data)
|
||||||
from freqtrade.commands.deploy_commands import (start_create_userdir, start_install_ui,
|
from freqtrade.commands.deploy_commands import (start_create_userdir, start_install_ui,
|
||||||
start_new_hyperopt, start_new_strategy)
|
start_new_hyperopt, start_new_strategy)
|
||||||
from freqtrade.commands.automation_commands import start_build_hyperopt
|
from freqtrade.commands.automation_commands import (start_build_hyperopt, start_custom_hyperopt, start_extract_strategy)
|
||||||
from freqtrade.commands.hyperopt_commands import start_hyperopt_list, start_hyperopt_show
|
from freqtrade.commands.hyperopt_commands import start_hyperopt_list, start_hyperopt_show
|
||||||
from freqtrade.commands.list_commands import (start_list_exchanges, start_list_hyperopts,
|
from freqtrade.commands.list_commands import (start_list_exchanges, start_list_hyperopts,
|
||||||
start_list_markets, start_list_strategies,
|
start_list_markets, start_list_strategies,
|
||||||
|
@ -55,8 +55,13 @@ ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"]
|
|||||||
|
|
||||||
ARGS_BUILD_HYPEROPT = ["user_data_dir", "hyperopt", "template"]
|
ARGS_BUILD_HYPEROPT = ["user_data_dir", "hyperopt", "template"]
|
||||||
|
|
||||||
|
# Automation
|
||||||
ARGS_BUILD_CUSTOM_HYPEROPT = ["buy_indicators", "sell_indicators", "hyperopt"]
|
ARGS_BUILD_CUSTOM_HYPEROPT = ["buy_indicators", "sell_indicators", "hyperopt"]
|
||||||
|
|
||||||
|
ARGS_EXTRACT_STRATEGY = ["strategy", "extract_name"]
|
||||||
|
|
||||||
|
ARGS_BUILD_BUILD_HYPEROPT = ["strategy", "hyperopt"]
|
||||||
|
|
||||||
ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"]
|
ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"]
|
||||||
ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes"]
|
ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes"]
|
||||||
|
|
||||||
@ -175,7 +180,7 @@ class Arguments:
|
|||||||
start_list_data, start_list_exchanges, start_list_hyperopts,
|
start_list_data, start_list_exchanges, start_list_hyperopts,
|
||||||
start_list_markets, start_list_strategies,
|
start_list_markets, start_list_strategies,
|
||||||
start_list_timeframes, start_new_config, start_new_hyperopt,
|
start_list_timeframes, start_new_config, start_new_hyperopt,
|
||||||
start_build_hyperopt,
|
start_build_hyperopt, start_custom_hyperopt, start_extract_strategy,
|
||||||
start_new_strategy, start_plot_dataframe, start_plot_profit,
|
start_new_strategy, start_plot_dataframe, start_plot_profit,
|
||||||
start_show_trades, start_test_pairlist, start_trading)
|
start_show_trades, start_test_pairlist, start_trading)
|
||||||
|
|
||||||
@ -210,12 +215,24 @@ class Arguments:
|
|||||||
build_hyperopt_cmd.set_defaults(func=start_new_hyperopt)
|
build_hyperopt_cmd.set_defaults(func=start_new_hyperopt)
|
||||||
self._build_args(optionlist=ARGS_BUILD_HYPEROPT, parser=build_hyperopt_cmd)
|
self._build_args(optionlist=ARGS_BUILD_HYPEROPT, parser=build_hyperopt_cmd)
|
||||||
|
|
||||||
# add build-hyperopt subcommand
|
# add custom-hyperopt subcommand
|
||||||
build_custom_hyperopt_cmd = subparsers.add_parser('build-hyperopt',
|
build_custom_hyperopt_cmd = subparsers.add_parser('custom-hyperopt',
|
||||||
help="Build a custom hyperopt")
|
help="Build a custom hyperopt")
|
||||||
build_custom_hyperopt_cmd.set_defaults(func=start_build_hyperopt)
|
build_custom_hyperopt_cmd.set_defaults(func=start_custom_hyperopt)
|
||||||
self._build_args(optionlist=ARGS_BUILD_CUSTOM_HYPEROPT, parser=build_custom_hyperopt_cmd)
|
self._build_args(optionlist=ARGS_BUILD_CUSTOM_HYPEROPT, parser=build_custom_hyperopt_cmd)
|
||||||
|
|
||||||
|
# add extract-strategy subcommand
|
||||||
|
extract_strategy_cmd = subparsers.add_parser('extract-strategy',
|
||||||
|
help="Extract data dictionaries for custom-hyperopt from strategy")
|
||||||
|
extract_strategy_cmd.set_defaults(func=start_extract_strategy)
|
||||||
|
self._build_args(optionlist=ARGS_EXTRACT_STRATEGY, parser=extract_strategy_cmd)
|
||||||
|
|
||||||
|
# add build-hyperopt subcommand
|
||||||
|
build_extracted_hyperopt_cmd = subparsers.add_parser('build-hyperopt',
|
||||||
|
help="Create a hyperopt for a strategy")
|
||||||
|
build_extracted_hyperopt_cmd.set_defaults(func=start_build_hyperopt)
|
||||||
|
self._build_args(optionlist=ARGS_BUILD_BUILD_HYPEROPT, parser=build_extracted_hyperopt_cmd)
|
||||||
|
|
||||||
# add new-strategy subcommand
|
# add new-strategy subcommand
|
||||||
build_strategy_cmd = subparsers.add_parser('new-strategy',
|
build_strategy_cmd = subparsers.add_parser('new-strategy',
|
||||||
help="Create new strategy")
|
help="Create new strategy")
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import ast
|
import ast
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from freqtrade.constants import USERPATH_HYPEROPTS
|
from freqtrade.constants import (USERPATH_HYPEROPTS,
|
||||||
|
USERPATH_STRATEGIES)
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.state import RunMode
|
from freqtrade.state import RunMode
|
||||||
from freqtrade.configuration import setup_utils_configuration
|
from freqtrade.configuration import setup_utils_configuration
|
||||||
@ -11,20 +12,139 @@ from freqtrade.misc import render_template
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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"]
|
# ---------------------------------------------------extract-strategy------------------------------------------------------
|
||||||
POSSIBLE_TRIGGERS = ["bb_lowerband", "bb_upperband"]
|
|
||||||
POSSIBLE_VALUES = {"above": ">", "below": "<"}
|
def get_indicator_info(file: List, indicators: Dict) -> None:
|
||||||
|
"""
|
||||||
|
Get all necessary information to build a custom hyperopt space using
|
||||||
|
the file and a dictionary filled with the indicators and their corropsonding line numbers.
|
||||||
|
"""
|
||||||
|
info_list = []
|
||||||
|
for indicator in indicators:
|
||||||
|
indicator_info = []
|
||||||
|
|
||||||
|
# find the corrosponding aim
|
||||||
|
for position, line in enumerate(file):
|
||||||
|
if position == indicators[indicator]:
|
||||||
|
# use split twice to remove the context around the indicator
|
||||||
|
back_of_line = line.split(f"(dataframe['{indicator}'] ", 1)[1]
|
||||||
|
aim = back_of_line.split()[0]
|
||||||
|
|
||||||
|
# add the indicator and aim to the info
|
||||||
|
indicator_info.append(indicator)
|
||||||
|
indicator_info.append(aim)
|
||||||
|
|
||||||
|
# check if first character after aim is a d in which case the indicator is a trigger
|
||||||
|
if back_of_line.split()[1][0] == "d":
|
||||||
|
indicator_info.append("trigger")
|
||||||
|
|
||||||
|
# add the second indicator of the guard to the info list
|
||||||
|
back_of_line = back_of_line.split("dataframe['")[1]
|
||||||
|
second_indicator = back_of_line.split("'])")[0]
|
||||||
|
indicator_info.append(second_indicator)
|
||||||
|
|
||||||
|
# elif indicator[0:3] == "CDL":
|
||||||
|
# indicator_info.append("guard")
|
||||||
|
|
||||||
|
# else it is a regular guard
|
||||||
|
else:
|
||||||
|
indicator_info.append("guard")
|
||||||
|
|
||||||
|
value = back_of_line.split()[1]
|
||||||
|
value = value[:-1]
|
||||||
|
value = float(value)
|
||||||
|
|
||||||
|
indicator_info.append(value)
|
||||||
|
info_list.append(indicator_info)
|
||||||
|
|
||||||
|
return info_list
|
||||||
|
|
||||||
|
|
||||||
def build_hyperopt_buyelements(buy_indicators: Dict[str, str]):
|
def extract_lists(strategypath: Path) -> None:
|
||||||
|
"""
|
||||||
|
Get the indicators, their aims and the stoploss and format them into lists
|
||||||
|
"""
|
||||||
|
|
||||||
|
# store the file in a list for reference
|
||||||
|
stored_file = []
|
||||||
|
with open(strategypath) as file:
|
||||||
|
for line in file:
|
||||||
|
stored_file.append(line)
|
||||||
|
|
||||||
|
# find the start and end of buy trend
|
||||||
|
for position, line in enumerate(stored_file):
|
||||||
|
if "populate_buy_trend(" in line:
|
||||||
|
start_buy_number = position
|
||||||
|
elif "populate_sell_trend(" in line:
|
||||||
|
end_buy_number = position
|
||||||
|
|
||||||
|
# list the numbers between the start and end of buy trend
|
||||||
|
buy_lines = []
|
||||||
|
for i in range(start_buy_number, end_buy_number):
|
||||||
|
buy_lines.append(i)
|
||||||
|
|
||||||
|
# populate the indicators dictionaries with indicators attached to the line they are on
|
||||||
|
buyindicators = {}
|
||||||
|
sellindicators = {}
|
||||||
|
|
||||||
|
for position, line in enumerate(stored_file):
|
||||||
|
# check the lines in buy trend for indicator and add them
|
||||||
|
if position in buy_lines and "(dataframe['" in line:
|
||||||
|
# use split twice to remove the context around the indicator
|
||||||
|
back_of_line = line.split("(dataframe['", 1)[1]
|
||||||
|
buyindicator = back_of_line.split("'] ", 1)[0]
|
||||||
|
buyindicators[buyindicator] = position
|
||||||
|
|
||||||
|
# check the lines in sell trend for indicator and add them
|
||||||
|
elif position > end_buy_number and "(dataframe['" in line:
|
||||||
|
# use split twice to remove the context around the indicator
|
||||||
|
back_of_line = line.split("(dataframe['", 1)[1]
|
||||||
|
sellindicator = back_of_line.split("'] ", 1)[0]
|
||||||
|
sellindicators[sellindicator] = position
|
||||||
|
|
||||||
|
# build the final lists
|
||||||
|
buy_info_list = get_indicator_info(stored_file, buyindicators)
|
||||||
|
sell_info_list = get_indicator_info(stored_file, sellindicators)
|
||||||
|
|
||||||
|
# put the final lists into a tuple
|
||||||
|
final_lists = (buy_info_list, sell_info_list)
|
||||||
|
|
||||||
|
return final_lists
|
||||||
|
|
||||||
|
|
||||||
|
def start_extract_strategy(args: Dict) -> None:
|
||||||
|
"""
|
||||||
|
Check if the right subcommands where passed and start extracting the strategy data
|
||||||
|
"""
|
||||||
|
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
|
||||||
|
|
||||||
|
# check if all required options are filled in
|
||||||
|
if not 'strategy' in args or not args['strategy']:
|
||||||
|
raise OperationalException("`extract-strategy` requires --strategy to be set.")
|
||||||
|
else:
|
||||||
|
# if the name is not specified use (strategy)_extract
|
||||||
|
if not 'extract_name' in args or not args['extract_name']:
|
||||||
|
args['extract_name'] = args['strategy'] + "_extract"
|
||||||
|
|
||||||
|
new_path = config['user_data_dir'] / USERPATH_STRATEGIES / (args['extract_name'] + '.txt')
|
||||||
|
if new_path.exists():
|
||||||
|
raise OperationalException(f"`{new_path}` already exists. "
|
||||||
|
"Please choose another name.")
|
||||||
|
# the path of the chosen strategy
|
||||||
|
strategy_path = config['user_data_dir'] / USERPATH_STRATEGIES / (args['strategy'] + '.py')
|
||||||
|
|
||||||
|
# extract the buy and sell indicators as dicts
|
||||||
|
extracted_lists = str(extract_lists(strategy_path))
|
||||||
|
|
||||||
|
# save the dicts in a file
|
||||||
|
logger.info(f"Writing custom hyperopt to `{new_path}`.")
|
||||||
|
new_path.write_text(extracted_lists)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------custom-hyperopt------------------------------------------------------
|
||||||
|
|
||||||
|
def custom_hyperopt_buyelements(buy_indicators: List):
|
||||||
"""
|
"""
|
||||||
Build the arguments with the placefillers for the buygenerator
|
Build the arguments with the placefillers for the buygenerator
|
||||||
"""
|
"""
|
||||||
@ -32,39 +152,44 @@ def build_hyperopt_buyelements(buy_indicators: Dict[str, str]):
|
|||||||
buy_triggers = ""
|
buy_triggers = ""
|
||||||
buy_space = ""
|
buy_space = ""
|
||||||
|
|
||||||
for indicator in buy_indicators:
|
for indicator_info in buy_indicators:
|
||||||
# Error handling
|
indicator = indicator_info[0]
|
||||||
if not indicator in POSSIBLE_GUARDS and not indicator in POSSIBLE_TRIGGERS:
|
aim = indicator_info[1]
|
||||||
raise OperationalException(
|
usage = indicator_info[2]
|
||||||
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
|
# If the indicator is a guard
|
||||||
elif indicator in POSSIBLE_GUARDS:
|
if usage == "guard":
|
||||||
# get the symbol corrosponding to the value
|
value = indicator_info[3]
|
||||||
aim = POSSIBLE_VALUES[buy_indicators[indicator]]
|
|
||||||
|
if value >= -1.0 and value <= 1.0:
|
||||||
|
lower_bound = value - 0.3
|
||||||
|
upper_bound = value + 0.3
|
||||||
|
else:
|
||||||
|
lower_bound = value - 30.0
|
||||||
|
upper_bound = value + 30.0
|
||||||
|
|
||||||
# add the guard to its argument
|
# 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'])"
|
buy_guards += f"if params.get('{indicator}-enabled'):\n conditions.append(dataframe['{indicator}'] {aim} params['{indicator}-value'])\n"
|
||||||
|
|
||||||
# add the space to its argument
|
# add the space to its argument
|
||||||
buy_space += f"Integer(10, 90, name='{indicator}-value'), Categorical([True, False], name='{indicator}-enabled'),"
|
buy_space += f"Integer({lower_bound}, {upper_bound}, name='{indicator}-value'),\nCategorical([True, False], name='{indicator}-enabled'),\n"
|
||||||
# If the indicator is a trigger
|
|
||||||
elif indicator in POSSIBLE_TRIGGERS:
|
|
||||||
# get the symbol corrosponding to the value
|
|
||||||
aim = POSSIBLE_VALUES[buy_indicators[indicator]]
|
|
||||||
|
|
||||||
|
# If the indicator is a trigger
|
||||||
|
elif usage == "trigger":
|
||||||
|
secondindicator = indicator_info[3]
|
||||||
# add the trigger to its argument
|
# add the trigger to its argument
|
||||||
buy_triggers += f"if params['trigger'] == '{indicator}': conditions.append(dataframe['{indicator}'] {aim} dataframe['close'])"
|
buy_triggers += f"if params['trigger'] == '{indicator}':\n conditions.append(dataframe['{indicator}'] {aim} dataframe['{secondindicator}'])\n"
|
||||||
|
|
||||||
# Final line of indicator space makes all triggers
|
# Final line of indicator space makes all triggers
|
||||||
|
|
||||||
buy_space += "Categorical(["
|
buy_space += "Categorical(["
|
||||||
|
|
||||||
# adding all triggers to the list
|
# adding all triggers to the list
|
||||||
for indicator in buy_indicators:
|
for indicator_info in buy_indicators:
|
||||||
if indicator in POSSIBLE_TRIGGERS:
|
indicator = indicator_info[0]
|
||||||
|
usage = indicator_info[2]
|
||||||
|
|
||||||
|
if usage == "trigger":
|
||||||
buy_space += f"'{indicator}', "
|
buy_space += f"'{indicator}', "
|
||||||
|
|
||||||
# Deleting the last ", "
|
# Deleting the last ", "
|
||||||
@ -74,7 +199,7 @@ def build_hyperopt_buyelements(buy_indicators: Dict[str, str]):
|
|||||||
return {"buy_guards": buy_guards, "buy_triggers": buy_triggers, "buy_space": buy_space}
|
return {"buy_guards": buy_guards, "buy_triggers": buy_triggers, "buy_space": buy_space}
|
||||||
|
|
||||||
|
|
||||||
def build_hyperopt_sellelements(sell_indicators: Dict[str, str]):
|
def custom_hyperopt_sellelements(sell_indicators: Dict[str, str]):
|
||||||
"""
|
"""
|
||||||
Build the arguments with the placefillers for the sellgenerator
|
Build the arguments with the placefillers for the sellgenerator
|
||||||
"""
|
"""
|
||||||
@ -82,44 +207,50 @@ def build_hyperopt_sellelements(sell_indicators: Dict[str, str]):
|
|||||||
sell_triggers = ""
|
sell_triggers = ""
|
||||||
sell_space = ""
|
sell_space = ""
|
||||||
|
|
||||||
for indicator in sell_indicators:
|
for indicator_info in sell_indicators:
|
||||||
# Error handling
|
indicator = indicator_info[0]
|
||||||
if not indicator in POSSIBLE_GUARDS and not indicator in POSSIBLE_TRIGGERS:
|
aim = indicator_info[1]
|
||||||
raise OperationalException(
|
usage = indicator_info[2]
|
||||||
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:
|
# If the indicator is a guard
|
||||||
raise OperationalException(
|
if usage == "guard":
|
||||||
f"`{sell_indicators[indicator]}` is not part of the available indicator options. The current options are {POSSIBLE_VALUES}.")
|
value = indicator_info[3]
|
||||||
# If indicator is a guard
|
|
||||||
elif indicator in POSSIBLE_GUARDS:
|
if value >= -1 and value <= 1:
|
||||||
# get the symbol corrosponding to the value
|
lower_bound = value - 0.3
|
||||||
aim = POSSIBLE_VALUES[sell_indicators[indicator]]
|
upper_bound = value + 0.3
|
||||||
|
else:
|
||||||
|
lower_bound = value - 30
|
||||||
|
upper_bound = value + 30
|
||||||
|
|
||||||
# add the guard to its argument
|
# 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'])"
|
sell_guards += f"if params.get('sell-{indicator}-enabled'):\n conditions.append(dataframe['{indicator}'] {aim} params['sell-{indicator}-value'])\n"
|
||||||
|
|
||||||
# add the space to its argument
|
# add the space to its argument
|
||||||
sell_space += f"Integer(10, 90, name='sell-{indicator}-value'), Categorical([True, False], name='sell-{indicator}-enabled'),"
|
sell_space += f"Integer({lower_bound}, {upper_bound}, name='sell-{indicator}-value'),\nCategorical([True, False], name='sell-{indicator}-enabled'),\n"
|
||||||
|
|
||||||
# If the indicator is a trigger
|
# If the indicator is a trigger
|
||||||
elif indicator in POSSIBLE_TRIGGERS:
|
elif usage == "trigger":
|
||||||
# get the symbol corrosponding to the value
|
secondindicator = indicator_info[3]
|
||||||
aim = POSSIBLE_VALUES[sell_indicators[indicator]]
|
|
||||||
|
|
||||||
# add the trigger to its argument
|
# add the trigger to its argument
|
||||||
sell_triggers += f"if params['sell-trigger'] == 'sell-{indicator}': conditions.append(dataframe['{indicator}'] {aim} dataframe['close'])"
|
sell_triggers += f"if params['sell-trigger'] == 'sell-{indicator}':\n conditions.append(dataframe['{indicator}'] {aim} dataframe['{secondindicator}'])\n"
|
||||||
|
|
||||||
# Final line of indicator space makes all triggers
|
# Final line of indicator space makes all triggers
|
||||||
|
|
||||||
sell_space += "Categorical(["
|
sell_space += "Categorical(["
|
||||||
|
|
||||||
# Adding all triggers to the list
|
# adding all triggers to the list
|
||||||
for indicator in sell_indicators:
|
for indicator_info in sell_indicators:
|
||||||
if indicator in POSSIBLE_TRIGGERS:
|
indicator = indicator_info[0]
|
||||||
|
usage = indicator_info[2]
|
||||||
|
|
||||||
|
if usage == "trigger":
|
||||||
sell_space += f"'sell-{indicator}', "
|
sell_space += f"'sell-{indicator}', "
|
||||||
|
|
||||||
# Deleting the last ", "
|
# Deleting the last ", "
|
||||||
sell_space = sell_space[:-2]
|
sell_space = sell_space[:-2]
|
||||||
sell_space += "], name='trigger')"
|
sell_space += "], name='sell-trigger')"
|
||||||
|
|
||||||
return {"sell_guards": sell_guards, "sell_triggers": sell_triggers, "sell_space": sell_space}
|
return {"sell_guards": sell_guards, "sell_triggers": sell_triggers, "sell_space": sell_space}
|
||||||
|
|
||||||
@ -130,11 +261,11 @@ def deploy_custom_hyperopt(hyperopt_name: str, hyperopt_path: Path, buy_indicato
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Build the arguments for the buy and sell generators
|
# Build the arguments for the buy and sell generators
|
||||||
buy_args = build_hyperopt_buyelements(buy_indicators)
|
buy_args = custom_hyperopt_buyelements(buy_indicators)
|
||||||
sell_args = build_hyperopt_sellelements(sell_indicators)
|
sell_args = custom_hyperopt_sellelements(sell_indicators)
|
||||||
|
|
||||||
# Build the final template
|
# Build the final template
|
||||||
strategy_text = render_template(templatefile='base_hyperopt.py.j2',
|
strategy_text = render_template(templatefile='base_custom_hyperopt.py.j2',
|
||||||
arguments={"hyperopt": hyperopt_name,
|
arguments={"hyperopt": hyperopt_name,
|
||||||
"buy_guards": buy_args["buy_guards"],
|
"buy_guards": buy_args["buy_guards"],
|
||||||
"buy_triggers": buy_args["buy_triggers"],
|
"buy_triggers": buy_args["buy_triggers"],
|
||||||
@ -148,19 +279,18 @@ def deploy_custom_hyperopt(hyperopt_name: str, hyperopt_path: Path, buy_indicato
|
|||||||
hyperopt_path.write_text(strategy_text)
|
hyperopt_path.write_text(strategy_text)
|
||||||
|
|
||||||
|
|
||||||
def start_build_hyperopt(args: Dict[str, Any]) -> None:
|
def start_custom_hyperopt(args: Dict[str, Any]) -> None:
|
||||||
"""
|
"""
|
||||||
Check if the right subcommands where passed and start building the hyperopt
|
Check if the right subcommands where passed and start building the hyperopt
|
||||||
"""
|
"""
|
||||||
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
|
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']:
|
if not 'hyperopt' in args or not args['hyperopt']:
|
||||||
raise OperationalException("`build-hyperopt` requires --hyperopt to be set.")
|
raise OperationalException("`custom-hyperopt` requires --hyperopt to be set.")
|
||||||
elif not 'buy_indicators' in args or not args['buy_indicators']:
|
elif not 'buy_indicators' in args or not args['buy_indicators']:
|
||||||
raise OperationalException("`build-hyperopt` requires --buy-indicators to be set.")
|
raise OperationalException("`custom-hyperopt` requires --buy-indicators to be set.")
|
||||||
elif not 'sell_indicators' in args or not args['sell_indicators']:
|
elif not 'sell_indicators' in args or not args['sell_indicators']:
|
||||||
raise OperationalException("`build-hyperopt` requires --sell-indicators to be set.")
|
raise OperationalException("`custom-hyperopt` requires --sell-indicators to be set.")
|
||||||
else:
|
else:
|
||||||
if args['hyperopt'] == 'DefaultHyperopt':
|
if args['hyperopt'] == 'DefaultHyperopt':
|
||||||
raise OperationalException("DefaultHyperopt is not allowed as name.")
|
raise OperationalException("DefaultHyperopt is not allowed as name.")
|
||||||
@ -175,3 +305,40 @@ def start_build_hyperopt(args: Dict[str, Any]) -> None:
|
|||||||
|
|
||||||
deploy_custom_hyperopt(args['hyperopt'], new_path,
|
deploy_custom_hyperopt(args['hyperopt'], new_path,
|
||||||
buy_indicators, sell_indicators)
|
buy_indicators, sell_indicators)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------build-hyperopt------------------------------------------------------
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# strategy and hyperopt need to be defined
|
||||||
|
if not 'strategy' in args or not args['strategy']:
|
||||||
|
raise OperationalException("`build-hyperopt` requires --strategy to be set.")
|
||||||
|
if not 'hyperopt' in args or not args['hyperopt']:
|
||||||
|
args['hyperopt'] = args['strategy'] + "opt"
|
||||||
|
else:
|
||||||
|
if args['hyperopt'] == 'DefaultHyperopt':
|
||||||
|
raise OperationalException("DefaultHyperopt is not allowed as name.")
|
||||||
|
|
||||||
|
# the path of the chosen strategy
|
||||||
|
strategy_path = config['user_data_dir'] / USERPATH_STRATEGIES / (args['strategy'] + '.py')
|
||||||
|
|
||||||
|
# the path where the hyperopt should be written
|
||||||
|
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.")
|
||||||
|
|
||||||
|
# extract the buy and sell indicators as dicts
|
||||||
|
extracted_lists = extract_lists(strategy_path)
|
||||||
|
|
||||||
|
buy_indicators = extracted_lists[0]
|
||||||
|
sell_indicators = extracted_lists[1]
|
||||||
|
|
||||||
|
# use the dicts to write the hyperopt
|
||||||
|
deploy_custom_hyperopt(args['hyperopt'], new_path,
|
||||||
|
buy_indicators, sell_indicators)
|
||||||
|
@ -279,14 +279,21 @@ AVAILABLE_CLI_OPTIONS = {
|
|||||||
"buy_indicators": Arg(
|
"buy_indicators": Arg(
|
||||||
'-b', '--buy-indicators',
|
'-b', '--buy-indicators',
|
||||||
help='Specify the buy indicators the hyperopt should build. '
|
help='Specify the buy indicators the hyperopt should build. '
|
||||||
'Example: --buy-indicators `{"rsi":"above","bb_lowerband":"below"}`',
|
'Example: --buy-indicators `[["rsi","<","trigger",30.0],["bb_lowerband",">","guard","close"]]`'
|
||||||
metavar='DICT',
|
'Check the documentation for specific requirements for the lists.',
|
||||||
|
metavar='LIST',
|
||||||
),
|
),
|
||||||
"sell_indicators": Arg(
|
"sell_indicators": Arg(
|
||||||
'-s', '--sell-indicators',
|
'-s', '--sell-indicators',
|
||||||
help='Specify the sell indicators the hyperopt should build. '
|
help='Specify the sell indicators the hyperopt should build. '
|
||||||
'Example: --sell-indicators `{"rsi":"above","bb_lowerband":"below"}`',
|
'Example: --sell-indicators [["rsi",">","trigger",70.0],["bb_lowerband","<","guard","close"]]'
|
||||||
metavar='DICT',
|
'Check the documentation for specific requirements for the lists.',
|
||||||
|
metavar='LIST',
|
||||||
|
),
|
||||||
|
"extract_name": Arg(
|
||||||
|
'--extract-name',
|
||||||
|
help='Specify the name of the file to which the data should be extracted. ',
|
||||||
|
metavar='FILENAME',
|
||||||
),
|
),
|
||||||
# List exchanges
|
# List exchanges
|
||||||
"print_one_column": Arg(
|
"print_one_column": Arg(
|
||||||
|
@ -392,4 +392,4 @@ PairWithTimeframe = Tuple[str, str]
|
|||||||
ListPairsWithTimeframes = List[PairWithTimeframe]
|
ListPairsWithTimeframes = List[PairWithTimeframe]
|
||||||
|
|
||||||
# Type for trades list
|
# Type for trades list
|
||||||
TradeList = List[List]
|
TradeList = List[List]
|
164
freqtrade/templates/base_custom_hyperopt.py.j2
Normal file
164
freqtrade/templates/base_custom_hyperopt.py.j2
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
||||||
|
|
||||||
|
# --- Do not remove these libs ---
|
||||||
|
from functools import reduce
|
||||||
|
from typing import Any, Callable, Dict, List
|
||||||
|
|
||||||
|
import numpy as np # noqa
|
||||||
|
import pandas as pd # noqa
|
||||||
|
from pandas import DataFrame
|
||||||
|
from skopt.space import Categorical, Dimension, Integer, Real # noqa
|
||||||
|
|
||||||
|
from freqtrade.optimize.hyperopt_interface import IHyperOpt
|
||||||
|
|
||||||
|
# --------------------------------
|
||||||
|
# Add your lib to import here
|
||||||
|
import talib.abstract as ta # noqa
|
||||||
|
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||||
|
|
||||||
|
|
||||||
|
class {{ hyperopt }}(IHyperOpt):
|
||||||
|
"""
|
||||||
|
This is a Hyperopt template to get you started.
|
||||||
|
|
||||||
|
More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/
|
||||||
|
|
||||||
|
You should:
|
||||||
|
- Add any lib you need to build your hyperopt.
|
||||||
|
|
||||||
|
You must keep:
|
||||||
|
- The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator.
|
||||||
|
|
||||||
|
The methods roi_space, generate_roi_table and stoploss_space are not required
|
||||||
|
and are provided by default.
|
||||||
|
However, you may override them if you need 'roi' and 'stoploss' spaces that
|
||||||
|
differ from the defaults offered by Freqtrade.
|
||||||
|
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
|
||||||
|
def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
|
||||||
|
"""
|
||||||
|
Define the buy strategy parameters to be used by Hyperopt.
|
||||||
|
"""
|
||||||
|
def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
|
"""
|
||||||
|
Buy strategy Hyperopt will build and use.
|
||||||
|
"""
|
||||||
|
conditions = []
|
||||||
|
|
||||||
|
# GUARDS AND TRENDS
|
||||||
|
{{ buy_guards | indent(12) }}
|
||||||
|
|
||||||
|
# TRIGGERS
|
||||||
|
if 'trigger' in params:
|
||||||
|
{{ buy_triggers | indent(16) }}
|
||||||
|
|
||||||
|
# Check that the candle had volume
|
||||||
|
conditions.append(dataframe['volume'] > 0)
|
||||||
|
|
||||||
|
if conditions:
|
||||||
|
dataframe.loc[
|
||||||
|
reduce(lambda x, y: x & y, conditions),
|
||||||
|
'buy'] = 1
|
||||||
|
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
return populate_buy_trend
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def indicator_space() -> List[Dimension]:
|
||||||
|
"""
|
||||||
|
Define your Hyperopt space for searching buy strategy parameters.
|
||||||
|
"""
|
||||||
|
return [
|
||||||
|
{{ buy_space | indent(12) }}
|
||||||
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def sell_strategy_generator(params: Dict[str, Any]) -> Callable:
|
||||||
|
"""
|
||||||
|
Define the sell strategy parameters to be used by Hyperopt.
|
||||||
|
"""
|
||||||
|
def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
|
"""
|
||||||
|
Sell strategy Hyperopt will build and use.
|
||||||
|
"""
|
||||||
|
conditions = []
|
||||||
|
|
||||||
|
# GUARDS AND TRENDS
|
||||||
|
{{ sell_guards | indent(12) }}
|
||||||
|
|
||||||
|
# TRIGGERS
|
||||||
|
if 'sell-trigger' in params:
|
||||||
|
{{ sell_triggers | indent(16) }}
|
||||||
|
|
||||||
|
# Check that the candle had volume
|
||||||
|
conditions.append(dataframe['volume'] > 0)
|
||||||
|
|
||||||
|
if conditions:
|
||||||
|
dataframe.loc[
|
||||||
|
reduce(lambda x, y: x & y, conditions),
|
||||||
|
'sell'] = 1
|
||||||
|
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
return populate_sell_trend
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def sell_indicator_space() -> List[Dimension]:
|
||||||
|
"""
|
||||||
|
Define your Hyperopt space for searching sell strategy parameters.
|
||||||
|
"""
|
||||||
|
return [
|
||||||
|
{{ sell_space | indent(12) }}
|
||||||
|
]
|
||||||
|
@ staticmethod
|
||||||
|
def generate_roi_table(params: Dict) -> Dict[int, float]:
|
||||||
|
"""
|
||||||
|
Generate the ROI table that will be used by Hyperopt
|
||||||
|
This implementation generates the default legacy Freqtrade ROI tables.
|
||||||
|
Change it if you need different number of steps in the generated
|
||||||
|
ROI tables or other structure of the ROI tables.
|
||||||
|
Please keep it aligned with parameters in the 'roi' optimization
|
||||||
|
hyperspace defined by the roi_space method.
|
||||||
|
"""
|
||||||
|
roi_table = {}
|
||||||
|
roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3']
|
||||||
|
roi_table[params['roi_t3']] = params['roi_p1'] + params['roi_p2']
|
||||||
|
roi_table[params['roi_t3'] + params['roi_t2']] = params['roi_p1']
|
||||||
|
roi_table[params['roi_t3'] + params['roi_t2'] + params['roi_t1']] = 0
|
||||||
|
|
||||||
|
return roi_table
|
||||||
|
|
||||||
|
@ staticmethod
|
||||||
|
def roi_space() -> List[Dimension]:
|
||||||
|
"""
|
||||||
|
Values to search for each ROI steps
|
||||||
|
Override it if you need some different ranges for the parameters in the
|
||||||
|
'roi' optimization hyperspace.
|
||||||
|
Please keep it aligned with the implementation of the
|
||||||
|
generate_roi_table method.
|
||||||
|
"""
|
||||||
|
return [
|
||||||
|
Integer(10, 120, name='roi_t1'),
|
||||||
|
Integer(10, 60, name='roi_t2'),
|
||||||
|
Integer(10, 40, name='roi_t3'),
|
||||||
|
Real(0.01, 0.04, name='roi_p1'),
|
||||||
|
Real(0.01, 0.07, name='roi_p2'),
|
||||||
|
Real(0.01, 0.20, name='roi_p3'),
|
||||||
|
]
|
||||||
|
|
||||||
|
@ staticmethod
|
||||||
|
def stoploss_space() -> List[Dimension]:
|
||||||
|
"""
|
||||||
|
Stoploss Value to search
|
||||||
|
Override it if you need some different range for the parameter in the
|
||||||
|
'stoploss' optimization hyperspace.
|
||||||
|
"""
|
||||||
|
return [
|
||||||
|
Real(-0.35, -0.02, name='stoploss'),
|
||||||
|
]
|
@ -55,7 +55,16 @@ class {{ hyperopt }}(IHyperOpt):
|
|||||||
|
|
||||||
# TRIGGERS
|
# TRIGGERS
|
||||||
if 'trigger' in params:
|
if 'trigger' in params:
|
||||||
{{ buy_triggers | indent(16) }}
|
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']
|
||||||
|
))
|
||||||
|
|
||||||
# Check that the candle had volume
|
# Check that the candle had volume
|
||||||
conditions.append(dataframe['volume'] > 0)
|
conditions.append(dataframe['volume'] > 0)
|
||||||
@ -94,7 +103,16 @@ class {{ hyperopt }}(IHyperOpt):
|
|||||||
|
|
||||||
# TRIGGERS
|
# TRIGGERS
|
||||||
if 'sell-trigger' in params:
|
if 'sell-trigger' in params:
|
||||||
{{ sell_triggers | indent(16) }}
|
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']
|
||||||
|
))
|
||||||
|
|
||||||
# Check that the candle had volume
|
# Check that the candle had volume
|
||||||
conditions.append(dataframe['volume'] > 0)
|
conditions.append(dataframe['volume'] > 0)
|
||||||
@ -115,4 +133,4 @@ class {{ hyperopt }}(IHyperOpt):
|
|||||||
"""
|
"""
|
||||||
return [
|
return [
|
||||||
{{ sell_space | indent(12) }}
|
{{ sell_space | indent(12) }}
|
||||||
]
|
]
|
Loading…
Reference in New Issue
Block a user