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"]
|
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
5
freqtrade/commands/cli_options.py
Normal file → Executable 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
78
freqtrade/commands/strategy_utils_commands.py
Normal file → Executable 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
30
freqtrade/strategy/backtest_lookahead_bias_checker.py
Normal file → Executable 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']
|
||||||
|
|||||||
Reference in New Issue
Block a user