partial progress commit:

added terminal tabulate-output
added yet non-working csv output using pandas
This commit is contained in:
hippocritical
2023-04-12 21:03:59 +02:00
parent 0fb155d6ee
commit a9ef4c3ab0
4 changed files with 116 additions and 45 deletions

10
freqtrade/commands/arguments.py Normal file → Executable file
View File

@@ -116,10 +116,11 @@ NO_CONF_REQURIED = ["convert-data", "convert-trade-data", "download-data", "list
NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-strategy"] NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-strategy"]
ARGS_STRATEGY_UPDATER = ["strategy_list"] ARGS_STRATEGY_UPDATER = ["strategy_list", "strategy_path", "recursive_strategy_search"]
ARGS_BACKTEST_LOOKAHEAD_BIAS_CHECKER = ARGS_BACKTEST + ["minimum_trade_amount", ARGS_BACKTEST_LOOKAHEAD_BIAS_CHECKER = ARGS_BACKTEST + ["minimum_trade_amount",
"targeted_trade_amount"] "targeted_trade_amount",
"overwrite_existing_exportfilename_content"]
# + ["target_trades", "minimum_trades", # + ["target_trades", "minimum_trades",
@@ -458,13 +459,14 @@ class Arguments:
'files to the current version', 'files to the current version',
parents=[_common_parser]) parents=[_common_parser])
strategy_updater_cmd.set_defaults(func=start_strategy_update) strategy_updater_cmd.set_defaults(func=start_strategy_update)
self._build_args(optionlist=ARGS_STRATEGY_UPDATER, parser=strategy_updater_cmd) self._build_args(optionlist=ARGS_STRATEGY_UPDATER,
parser=strategy_updater_cmd)
# Add backtest lookahead bias checker subcommand # Add backtest lookahead bias checker subcommand
backtest_lookahead_bias_checker_cmd = \ backtest_lookahead_bias_checker_cmd = \
subparsers.add_parser('backtest-lookahead-bias-checker', subparsers.add_parser('backtest-lookahead-bias-checker',
help="checks for potential look ahead bias", help="checks for potential look ahead bias",
parents=[_common_parser]) parents=[_common_parser, _strategy_parser])
backtest_lookahead_bias_checker_cmd.set_defaults(func=start_backtest_lookahead_bias_checker) backtest_lookahead_bias_checker_cmd.set_defaults(func=start_backtest_lookahead_bias_checker)
self._build_args(optionlist=ARGS_BACKTEST_LOOKAHEAD_BIAS_CHECKER, self._build_args(optionlist=ARGS_BACKTEST_LOOKAHEAD_BIAS_CHECKER,

5
freqtrade/commands/cli_options.py Normal file → Executable file
View File

@@ -688,5 +688,10 @@ AVAILABLE_CLI_OPTIONS = {
type=check_int_positive, type=check_int_positive,
metavar='INT', metavar='INT',
default=20, default=20,
),
"overwrite_existing_exportfilename_content": Arg(
'--overwrite-existing-exportfilename-content',
help='overwrites existing contents if existent with exportfilename given',
action='store_true'
) )
} }

78
freqtrade/commands/strategy_utils_commands.py Normal file → Executable file
View File

@@ -4,6 +4,9 @@ import time
from pathlib import Path from pathlib import Path
from typing import Any, Dict from typing import Any, Dict
import pandas as pd
from tabulate import tabulate
from freqtrade.configuration import setup_utils_configuration from freqtrade.configuration import setup_utils_configuration
from freqtrade.enums import RunMode from freqtrade.enums import RunMode
from freqtrade.resolvers import StrategyResolver from freqtrade.resolvers import StrategyResolver
@@ -76,7 +79,6 @@ def start_backtest_lookahead_bias_checker(args: Dict[str, Any]) -> None:
config, enum_failed=False, recursive=config.get('recursive_strategy_search', False)) config, enum_failed=False, recursive=config.get('recursive_strategy_search', False))
bias_checker_instances = [] bias_checker_instances = []
filtered_strategy_objs = [] filtered_strategy_objs = []
if 'strategy_list' in args and args['strategy_list'] is not None: if 'strategy_list' in args and args['strategy_list'] is not None:
for args_strategy in args['strategy_list']: for args_strategy in args['strategy_list']:
@@ -87,28 +89,82 @@ def start_backtest_lookahead_bias_checker(args: Dict[str, Any]) -> None:
break break
for filtered_strategy_obj in filtered_strategy_objs: for filtered_strategy_obj in filtered_strategy_objs:
bias_checker_instances = initialize_single_lookahead_bias_checker( bias_checker_instances.append(
filtered_strategy_obj, config, args) initialize_single_lookahead_bias_checker(filtered_strategy_obj, config, args))
else: else:
processed_locations = set() processed_locations = set()
for strategy_obj in strategy_objs: for strategy_obj in strategy_objs:
if strategy_obj['location'] not in processed_locations: if strategy_obj['location'] not in processed_locations:
processed_locations.add(strategy_obj['location']) processed_locations.add(strategy_obj['location'])
bias_checker_instances = initialize_single_lookahead_bias_checker( bias_checker_instances.append(
strategy_obj, config, args) initialize_single_lookahead_bias_checker(strategy_obj, config, args))
create_result_list(bias_checker_instances) text_table_bias_checker_instances(bias_checker_instances)
export_to_csv(args, bias_checker_instances)
def create_result_list(bias_checker_instances): def text_table_bias_checker_instances(bias_checker_instances):
pass headers = ['strategy', 'has_bias',
'total_signals', 'biased_entry_signals', 'biased_exit_signals', 'biased_indicators']
data = []
for current_instance in bias_checker_instances:
data.append(
[current_instance.strategy_obj['name'],
current_instance.current_analysis.has_bias,
current_instance.current_analysis.total_signals,
current_instance.current_analysis.false_entry_signals,
current_instance.current_analysis.false_exit_signals,
", ".join(current_instance.current_analysis.false_indicators)]
)
table = tabulate(data, headers=headers, tablefmt="orgtbl")
print(table)
def export_to_csv(args, bias_checker_instances):
def add_or_update_row(df, row_data):
strategy_col_name = 'strategy'
if row_data[strategy_col_name] in df[strategy_col_name].values:
# create temporary dataframe with a single row
# and use that to replace the previous data in there.
index = (df.index[df[strategy_col_name] ==
row_data[strategy_col_name]][0])
df.loc[index] = pd.Series(row_data, index='strategy')
else:
df = df.concat(row_data, ignore_index=True)
return df
csv_df = None
if not Path.exists(args['exportfilename']):
# If the file doesn't exist, create a new DataFrame from scratch
csv_df = pd.DataFrame(columns=['filename', 'strategy', 'has_bias',
'total_signals',
'biased_entry_signals', 'biased_exit_signals',
'biased_indicators'],
index='filename')
else:
# Read CSV file into a pandas dataframe
csv_df = pd.read_csv(args['exportfilename'])
for inst in bias_checker_instances:
new_row_data = {'filename': inst.strategy_obj['location'].parts[-1],
'strategy': inst.strategy_obj['name'],
'has_bias': inst.current_analysis.has_bias,
'total_signals': inst.current_analysis.total_signals,
'biased_entry_signals': inst.current_analysis.false_entry_signals,
'biased_exit_signals': inst.current_analysis.false_exit_signals,
'biased_indicators': ", ".join(inst.current_analysis.false_indicators)}
csv_df = add_or_update_row(csv_df, new_row_data)
if len(bias_checker_instances) > 0:
print(f"saving {args['exportfilename']}")
csv_df.to_csv(args['exportfilename'])
def initialize_single_lookahead_bias_checker(strategy_obj, config, args): def initialize_single_lookahead_bias_checker(strategy_obj, config, args):
# try:
print(f"Bias test of {Path(strategy_obj['location']).name} started.") print(f"Bias test of {Path(strategy_obj['location']).name} started.")
instance_backtest_lookahead_bias_checker = BacktestLookaheadBiasChecker()
start = time.perf_counter() start = time.perf_counter()
current_instance = instance_backtest_lookahead_bias_checker.start(config, strategy_obj, args) current_instance = BacktestLookaheadBiasChecker()
current_instance.start(config, strategy_obj, args)
elapsed = time.perf_counter() - start elapsed = time.perf_counter() - start
print(f"checking look ahead bias via backtests of {Path(strategy_obj['location']).name} " print(f"checking look ahead bias via backtests of {Path(strategy_obj['location']).name} "
f"took {elapsed:.1f} seconds.") f"took {elapsed:.1f} seconds.")

30
freqtrade/strategy/backtest_lookahead_bias_checker.py Normal file → Executable file
View File

@@ -2,7 +2,7 @@ import copy
from copy import deepcopy from copy import deepcopy
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
import pandas from pandas import DataFrame
from freqtrade.configuration import TimeRange from freqtrade.configuration import TimeRange
from freqtrade.data.history import get_timerange from freqtrade.data.history import get_timerange
@@ -10,17 +10,17 @@ from freqtrade.exchange import timeframe_to_minutes
from freqtrade.optimize.backtesting import Backtesting from freqtrade.optimize.backtesting import Backtesting
class BacktestLookaheadBiasChecker:
class VarHolder: class VarHolder:
timerange: TimeRange timerange: TimeRange
data: pandas.DataFrame data: DataFrame
indicators: pandas.DataFrame indicators: DataFrame
result: pandas.DataFrame result: DataFrame
compared: pandas.DataFrame compared: DataFrame
from_dt: datetime from_dt: datetime
to_dt: datetime to_dt: datetime
compared_dt: datetime compared_dt: datetime
class Analysis: class Analysis:
def __init__(self): def __init__(self):
self.total_signals = 0 self.total_signals = 0
@@ -36,7 +36,11 @@ class BacktestLookaheadBiasChecker:
false_indicators: list false_indicators: list
has_bias: bool has_bias: bool
class BacktestLookaheadBiasChecker:
def __init__(self): def __init__(self):
self.exportfilename = None
self.strategy_obj = None self.strategy_obj = None
self.current_analysis = None self.current_analysis = None
self.local_config = None self.local_config = None
@@ -44,7 +48,6 @@ class BacktestLookaheadBiasChecker:
self.entry_varHolder = None self.entry_varHolder = None
self.exit_varHolder = None self.exit_varHolder = None
self.backtesting = None self.backtesting = None
self.current_analysis = None
self.minimum_trade_amount = None self.minimum_trade_amount = None
self.targeted_trade_amount = None self.targeted_trade_amount = None
@@ -124,9 +127,12 @@ class BacktestLookaheadBiasChecker:
prepare_data_config['timerange'] = (str(self.dt_to_timestamp(varHolder.from_dt)) + "-" + prepare_data_config['timerange'] = (str(self.dt_to_timestamp(varHolder.from_dt)) + "-" +
str(self.dt_to_timestamp(varHolder.to_dt))) str(self.dt_to_timestamp(varHolder.to_dt)))
prepare_data_config['pairs'] = pairs_to_load prepare_data_config['pairs'] = pairs_to_load
self.backtesting = Backtesting(prepare_data_config) self.backtesting = Backtesting(prepare_data_config)
self.backtesting._set_strategy(self.backtesting.strategylist[0]) self.backtesting._set_strategy(self.backtesting.strategylist[0])
varHolder.data, varHolder.timerange = self.backtesting.load_bt_data() varHolder.data, varHolder.timerange = self.backtesting.load_bt_data()
self.backtesting.load_bt_data_detail()
varHolder.indicators = self.backtesting.strategy.advise_all_indicators(varHolder.data) varHolder.indicators = self.backtesting.strategy.advise_all_indicators(varHolder.data)
varHolder.result = self.get_result(self.backtesting, varHolder.indicators) varHolder.result = self.get_result(self.backtesting, varHolder.indicators)
@@ -139,12 +145,14 @@ class BacktestLookaheadBiasChecker:
# and not worry about another strategy to check after. # and not worry about another strategy to check after.
self.local_config = deepcopy(config) self.local_config = deepcopy(config)
self.local_config['strategy_list'] = [strategy_obj['name']] self.local_config['strategy_list'] = [strategy_obj['name']]
self.current_analysis = BacktestLookaheadBiasChecker.Analysis() self.current_analysis = Analysis()
self.minimum_trade_amount = args['minimum_trade_amount'] self.minimum_trade_amount = args['minimum_trade_amount']
self.targeted_trade_amount = args['targeted_trade_amount'] self.targeted_trade_amount = args['targeted_trade_amount']
self.exportfilename = args['exportfilename']
self.strategy_obj = strategy_obj
# first make a single backtest # first make a single backtest
self.full_varHolder = BacktestLookaheadBiasChecker.VarHolder() self.full_varHolder = VarHolder()
# define datetime in human-readable format # define datetime in human-readable format
parsed_timerange = TimeRange.parse_timerange(config['timerange']) parsed_timerange = TimeRange.parse_timerange(config['timerange'])
@@ -182,8 +190,8 @@ class BacktestLookaheadBiasChecker:
self.current_analysis.total_signals += 1 self.current_analysis.total_signals += 1
self.entry_varHolder = BacktestLookaheadBiasChecker.VarHolder() self.entry_varHolder = VarHolder()
self.exit_varHolder = BacktestLookaheadBiasChecker.VarHolder() self.exit_varHolder = VarHolder()
self.entry_varHolder.from_dt = self.full_varHolder.from_dt self.entry_varHolder.from_dt = self.full_varHolder.from_dt
self.entry_varHolder.compared_dt = result_row['open_date'] self.entry_varHolder.compared_dt = result_row['open_date']