mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-11-29 08:33:07 +00:00
partial progress commit:
added terminal tabulate-output added yet non-working csv output using pandas
This commit is contained in:
10
freqtrade/commands/arguments.py
Normal file → Executable file
10
freqtrade/commands/arguments.py
Normal file → Executable 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
5
freqtrade/commands/cli_options.py
Normal file → Executable 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
78
freqtrade/commands/strategy_utils_commands.py
Normal file → Executable 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
68
freqtrade/strategy/backtest_lookahead_bias_checker.py
Normal file → Executable 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']
|
||||
|
||||
Reference in New Issue
Block a user