freqtrade_origin/freqtrade/optimize/recursive_analysis.py

151 lines
5.8 KiB
Python
Raw Normal View History

2023-09-04 01:53:04 +00:00
import logging
import shutil
from copy import deepcopy
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any, Dict, List, Optional
from pandas import DataFrame
from freqtrade.configuration import TimeRange
from freqtrade.exchange import timeframe_to_minutes
from freqtrade.loggers.set_log_levels import (reduce_verbosity_for_bias_tester,
restore_verbosity_for_bias_tester)
from freqtrade.optimize.backtesting import Backtesting
2023-09-12 06:42:32 +00:00
from freqtrade.optimize.base_analysis import BaseAnalysis, VarHolder
2023-09-04 01:53:04 +00:00
logger = logging.getLogger(__name__)
2023-09-12 06:42:32 +00:00
class RecursiveAnalysis(BaseAnalysis):
2023-09-04 01:53:04 +00:00
def __init__(self, config: Dict[str, Any], strategy_obj: Dict):
2023-09-12 06:42:32 +00:00
super().__init__(config, strategy_obj)
2023-09-04 02:45:25 +00:00
self.partial_varHolder_array: List[VarHolder] = []
self.partial_varHolder_lookahead_array: List[VarHolder] = []
2023-09-04 01:53:04 +00:00
self._startup_candle = config.get('startup_candle', [199, 399, 499, 999, 1999])
2023-09-04 02:52:09 +00:00
self.dict_recursive: Dict[str, Any] = dict()
2023-09-04 01:53:04 +00:00
# For recursive bias check
# analyzes two data frames with processed indicators and shows differences between them.
def analyze_indicators(self):
2023-09-04 02:35:44 +00:00
2023-09-04 01:53:04 +00:00
pair_to_check = self.local_config['pairs'][0]
2023-09-04 02:35:44 +00:00
logger.info("Start checking for recursive bias")
2023-09-04 01:53:04 +00:00
# check and report signals
base_last_row = self.full_varHolder.indicators[pair_to_check].iloc[-1]
2023-09-04 02:38:13 +00:00
2023-09-04 01:53:04 +00:00
for part in self.partial_varHolder_array:
part_last_row = part.indicators[pair_to_check].iloc[-1]
compare_df = base_last_row.compare(part_last_row)
if compare_df.shape[0] > 0:
# print(compare_df)
for col_name, values in compare_df.items():
# print(col_name)
if 'other' == col_name:
continue
indicators = values.index
for indicator in indicators:
2023-09-04 02:45:25 +00:00
if (indicator not in self.dict_recursive):
2023-09-04 01:53:04 +00:00
self.dict_recursive[indicator] = {}
values_diff = compare_df.loc[indicator]
values_diff_self = values_diff.loc['self']
values_diff_other = values_diff.loc['other']
2023-09-04 02:35:44 +00:00
diff = (values_diff_other - values_diff_self) / values_diff_self * 100
2023-09-04 01:53:04 +00:00
2023-09-04 02:41:24 +00:00
self.dict_recursive[indicator][part.startup_candle] = f"{diff:.3f}%"
2023-09-04 01:53:04 +00:00
else:
logger.info("No difference found. Stop the process.")
break
# For lookahead bias check
# analyzes two data frames with processed indicators and shows differences between them.
def analyze_indicators_lookahead(self):
2023-09-04 02:35:44 +00:00
2023-09-04 01:53:04 +00:00
pair_to_check = self.local_config['pairs'][0]
2023-09-04 02:35:44 +00:00
logger.info("Start checking for lookahead bias on indicators only")
2023-09-04 01:53:04 +00:00
part = self.partial_varHolder_lookahead_array[0]
part_last_row = part.indicators[pair_to_check].iloc[-1]
date_to_check = part_last_row['date']
2023-09-04 02:35:44 +00:00
index_to_get = (self.full_varHolder.indicators[pair_to_check]['date'] == date_to_check)
base_row_check = self.full_varHolder.indicators[pair_to_check].loc[index_to_get].iloc[-1]
2023-09-04 01:53:04 +00:00
check_time = part.to_dt.strftime('%Y-%m-%dT%H:%M:%S')
logger.info(f"Check indicators at {check_time}")
# logger.info(f"vs {part_timerange} with {part.startup_candle} startup candle")
2023-09-04 02:35:44 +00:00
compare_df = base_row_check.compare(part_last_row)
2023-09-04 01:53:04 +00:00
if compare_df.shape[0] > 0:
# print(compare_df)
for col_name, values in compare_df.items():
# print(col_name)
if 'other' == col_name:
continue
indicators = values.index
for indicator in indicators:
logger.info(f"=> found lookahead in indicator {indicator}")
# logger.info("base value {:.5f}".format(values_diff_self))
# logger.info("part value {:.5f}".format(values_diff_other))
else:
2023-09-04 02:35:44 +00:00
logger.info("No lookahead bias on indicators found. Stop the process.")
2023-09-04 01:53:04 +00:00
def fill_partial_varholder(self, start_date, startup_candle):
partial_varHolder = VarHolder()
partial_varHolder.from_dt = start_date
partial_varHolder.to_dt = self.full_varHolder.to_dt
partial_varHolder.startup_candle = startup_candle
self.local_config['startup_candle_count'] = startup_candle
self.prepare_data(partial_varHolder, self.local_config['pairs'])
self.partial_varHolder_array.append(partial_varHolder)
def fill_partial_varholder_lookahead(self, end_date):
partial_varHolder = VarHolder()
partial_varHolder.from_dt = self.full_varHolder.from_dt
partial_varHolder.to_dt = end_date
self.prepare_data(partial_varHolder, self.local_config['pairs'])
self.partial_varHolder_lookahead_array.append(partial_varHolder)
def start(self) -> None:
2023-09-12 06:42:32 +00:00
super().start()
2023-09-04 01:53:04 +00:00
start_date_full = self.full_varHolder.from_dt
end_date_full = self.full_varHolder.to_dt
timeframe_minutes = timeframe_to_minutes(self.full_varHolder.timeframe)
end_date_partial = start_date_full + timedelta(minutes=int(timeframe_minutes * 10))
self.fill_partial_varholder_lookahead(end_date_partial)
# restore_verbosity_for_bias_tester()
start_date_partial = end_date_full - timedelta(minutes=int(timeframe_minutes))
for startup_candle in self._startup_candle:
self.fill_partial_varholder(start_date_partial, int(startup_candle))
# Restore verbosity, so it's not too quiet for the next strategy
restore_verbosity_for_bias_tester()
self.analyze_indicators()
2023-09-04 02:35:44 +00:00
self.analyze_indicators_lookahead()