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"]
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",
"targeted_trade_amount"]
"targeted_trade_amount",
"overwrite_existing_exportfilename_content"]
# + ["target_trades", "minimum_trades",
@@ -458,13 +459,14 @@ class Arguments:
'files to the current version',
parents=[_common_parser])
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
backtest_lookahead_bias_checker_cmd = \
subparsers.add_parser('backtest-lookahead-bias-checker',
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)
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,
metavar='INT',
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 typing import Any, Dict
import pandas as pd
from tabulate import tabulate
from freqtrade.configuration import setup_utils_configuration
from freqtrade.enums import RunMode
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))
bias_checker_instances = []
filtered_strategy_objs = []
if 'strategy_list' in args and args['strategy_list'] is not None:
for args_strategy in args['strategy_list']:
@@ -87,28 +89,82 @@ def start_backtest_lookahead_bias_checker(args: Dict[str, Any]) -> None:
break
for filtered_strategy_obj in filtered_strategy_objs:
bias_checker_instances = initialize_single_lookahead_bias_checker(
filtered_strategy_obj, config, args)
bias_checker_instances.append(
initialize_single_lookahead_bias_checker(filtered_strategy_obj, config, args))
else:
processed_locations = set()
for strategy_obj in strategy_objs:
if strategy_obj['location'] not in processed_locations:
processed_locations.add(strategy_obj['location'])
bias_checker_instances = initialize_single_lookahead_bias_checker(
strategy_obj, config, args)
create_result_list(bias_checker_instances)
bias_checker_instances.append(
initialize_single_lookahead_bias_checker(strategy_obj, config, args))
text_table_bias_checker_instances(bias_checker_instances)
export_to_csv(args, bias_checker_instances)
def create_result_list(bias_checker_instances):
pass
def text_table_bias_checker_instances(bias_checker_instances):
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):
# try:
print(f"Bias test of {Path(strategy_obj['location']).name} started.")
instance_backtest_lookahead_bias_checker = BacktestLookaheadBiasChecker()
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
print(f"checking look ahead bias via backtests of {Path(strategy_obj['location']).name} "
f"took {elapsed:.1f} seconds.")

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

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