|
1 | | -import gc |
2 | | -import shutil |
3 | 1 | import inspect |
4 | 2 | import logging |
5 | 3 | import os |
|
20 | 18 | BACKTESTING_START_DATE, BACKTESTING_END_DATE, APP_MODE, MarketCredential, \ |
21 | 19 | AppMode, BacktestDateRange, DATABASE_DIRECTORY_NAME, DataSource, \ |
22 | 20 | BACKTESTING_INITIAL_AMOUNT, SNAPSHOT_INTERVAL, Backtest, DataError, \ |
23 | | - PortfolioConfiguration, SnapshotInterval, DataType, combine_backtests, \ |
| 21 | + PortfolioConfiguration, SnapshotInterval, DataType, \ |
24 | 22 | PortfolioProvider, OrderExecutor, ImproperlyConfigured, TimeFrame, \ |
25 | 23 | DataProvider, INDEX_DATETIME, tqdm, BacktestPermutationTest, \ |
26 | | - LAST_SNAPSHOT_DATETIME, BACKTESTING_FLAG, \ |
27 | | - generate_backtest_summary_metrics, DATA_DIRECTORY |
| 24 | + LAST_SNAPSHOT_DATETIME, BACKTESTING_FLAG, DATA_DIRECTORY, \ |
| 25 | + generate_backtest_summary_metrics |
28 | 26 | from investing_algorithm_framework.infrastructure import setup_sqlalchemy, \ |
29 | 27 | create_all_tables, CCXTOrderExecutor, CCXTPortfolioProvider, \ |
30 | 28 | BacktestOrderExecutor, CCXTOHLCVDataProvider, clear_db, \ |
|
34 | 32 | DefaultTradeOrderEvaluator, get_risk_free_rate_us |
35 | 33 | from .app_hook import AppHook |
36 | 34 | from .eventloop import EventLoopService |
37 | | -from .analysis import create_ohlcv_permutation, save_backtests_to_directory, \ |
38 | | - load_backtests_from_directory |
39 | | - |
| 35 | +from .analysis import create_ohlcv_permutation |
40 | 36 |
|
41 | 37 | logger = logging.getLogger("investing_algorithm_framework") |
42 | 38 | COLOR_RESET = '\033[0m' |
@@ -1021,211 +1017,68 @@ def filter_function( |
1021 | 1017 | List[Backtest]: List of Backtest instances for each strategy |
1022 | 1018 | that was backtested. |
1023 | 1019 | """ |
1024 | | - backtests = [] |
1025 | | - algorithm_ids = [strategy.algorithm_id for strategy in strategies] |
1026 | | - algorithm_ids_selection = algorithm_ids.copy() |
1027 | | - data_sources = [] |
| 1020 | + backtest_service = self.container.backtest_service() |
1028 | 1021 |
|
1029 | | - # Only used when backtest_storage_directory is None and |
1030 | | - # backtest_date_ranges is provided |
1031 | | - backtests_ordered_by_algorithm = {} |
| 1022 | + if use_checkpoints and backtest_storage_directory is None: |
| 1023 | + raise OperationalException( |
| 1024 | + "backtest_storage_directory must be provided when " |
| 1025 | + "use_checkpoints is set to True" |
| 1026 | + ) |
1032 | 1027 |
|
1033 | 1028 | if backtest_date_range is None and backtest_date_ranges is None: |
1034 | 1029 | raise OperationalException( |
1035 | 1030 | "Either backtest_date_range or backtest_date_ranges must be " |
1036 | 1031 | "provided" |
1037 | 1032 | ) |
1038 | 1033 |
|
1039 | | - for strategy in strategies: |
1040 | | - data_sources.extend(strategy.data_sources) |
1041 | | - |
1042 | | - if risk_free_rate is None: |
1043 | | - logger.info("No risk free rate provided, retrieving it...") |
1044 | | - risk_free_rate = get_risk_free_rate_us() |
1045 | | - |
1046 | | - if risk_free_rate is None: |
1047 | | - raise OperationalException( |
1048 | | - "Could not retrieve risk free rate for backtest metrics." |
1049 | | - "Please provide a risk free as an argument when running " |
1050 | | - "your backtest or make sure you have an internet " |
1051 | | - "connection" |
1052 | | - ) |
1053 | | - |
1054 | | - if backtest_date_range is not None: |
1055 | | - if not skip_data_sources_initialization: |
1056 | | - self.initialize_data_sources_backtest( |
1057 | | - data_sources, |
1058 | | - backtest_date_range, |
1059 | | - show_progress=show_progress |
1060 | | - ) |
1061 | | - |
1062 | | - for strategy in tqdm( |
1063 | | - strategies, colour="green", desc="Running backtests" |
1064 | | - ): |
1065 | | - backtests.append( |
1066 | | - self.run_vector_backtest( |
1067 | | - backtest_date_range=backtest_date_range, |
1068 | | - initial_amount=initial_amount, |
1069 | | - strategy=strategy, |
1070 | | - snapshot_interval=snapshot_interval, |
1071 | | - risk_free_rate=risk_free_rate, |
1072 | | - skip_data_sources_initialization=True, |
1073 | | - market=market, |
1074 | | - trading_symbol=trading_symbol, |
1075 | | - continue_on_error=continue_on_error, |
1076 | | - use_checkpoints=use_checkpoints, |
1077 | | - backtest_storage_directory=backtest_storage_directory, |
1078 | | - show_progress=False, |
1079 | | - ) |
1080 | | - ) |
1081 | | - |
1082 | | - # Apply filter function if set |
1083 | | - if filter_function is not None: |
1084 | | - backtests = filter_function(backtests, backtest_date_range) |
| 1034 | + if not skip_data_sources_initialization: |
| 1035 | + print("adding default data providers") |
| 1036 | + data_provider_service = self.container.data_provider_service() |
| 1037 | + data_provider_service.reset() |
1085 | 1038 |
|
1086 | | - # Save to storage directory if provided |
1087 | | - if backtest_storage_directory is not None: |
1088 | | - save_backtests_to_directory( |
1089 | | - backtests=backtests, |
1090 | | - directory_path=backtest_storage_directory, |
| 1039 | + for data_provider_tuple in self._data_providers: |
| 1040 | + data_provider_service.add_data_provider( |
| 1041 | + data_provider_tuple[0], priority=data_provider_tuple[1] |
1091 | 1042 | ) |
1092 | 1043 |
|
1093 | | - return backtests |
1094 | | - else: |
1095 | | - active_strategies = strategies.copy() |
1096 | | - storage_directories = {} |
1097 | | - |
1098 | | - # Make sure that the backtest date ranges are sorted by start date |
1099 | | - # and unique |
1100 | | - unique_date_ranges = set(backtest_date_ranges) |
1101 | | - backtest_date_ranges = sorted( |
1102 | | - unique_date_ranges, key=lambda x: x.start_date |
| 1044 | + # Add the default data providers |
| 1045 | + data_provider_service.add_data_provider( |
| 1046 | + CCXTOHLCVDataProvider() |
1103 | 1047 | ) |
1104 | 1048 |
|
1105 | | - for backtest_date_range in tqdm( |
1106 | | - backtest_date_ranges, |
1107 | | - colour="green", |
1108 | | - desc="Running backtests for all date ranges" |
1109 | | - ): |
1110 | | - backtest_results = [] |
1111 | | - if not skip_data_sources_initialization: |
1112 | | - self.initialize_data_sources_backtest( |
1113 | | - data_sources, |
1114 | | - backtest_date_range, |
1115 | | - show_progress=show_progress |
1116 | | - ) |
1117 | | - |
1118 | | - start_date = backtest_date_range\ |
1119 | | - .start_date.strftime('%Y-%m-%d') |
1120 | | - end_date = backtest_date_range.end_date.strftime('%Y-%m-%d') |
1121 | | - |
1122 | | - if backtest_storage_directory is not None: |
1123 | | - storage_directories[backtest_date_range] = os.path.join( |
1124 | | - backtest_storage_directory, |
1125 | | - f"{start_date}_to_{end_date}" |
1126 | | - ) |
1127 | | - |
1128 | | - # Run backtests for active strategies only |
1129 | | - for strategy in tqdm( |
1130 | | - active_strategies, |
1131 | | - colour="green", |
1132 | | - desc=f"Running backtests for " |
1133 | | - f"{start_date} to {end_date}" |
1134 | | - ): |
1135 | | - backtest_results.append( |
1136 | | - self.run_vector_backtest( |
1137 | | - backtest_date_range=backtest_date_range, |
1138 | | - initial_amount=initial_amount, |
1139 | | - strategy=strategy, |
1140 | | - snapshot_interval=snapshot_interval, |
1141 | | - risk_free_rate=risk_free_rate, |
1142 | | - skip_data_sources_initialization=True, |
1143 | | - market=market, |
1144 | | - trading_symbol=trading_symbol, |
1145 | | - use_checkpoints=use_checkpoints, |
1146 | | - backtest_storage_directory=( |
1147 | | - backtest_storage_directory |
1148 | | - ), |
1149 | | - show_progress=False |
1150 | | - ) |
1151 | | - ) |
1152 | | - |
1153 | | - # Apply filter function after each date range to determine |
1154 | | - # which strategies continue to the next period |
1155 | | - if filter_function is not None: |
1156 | | - backtest_results = filter_function( |
1157 | | - backtest_results, backtest_date_range |
1158 | | - ) |
1159 | | - algorithm_ids_selection = [ |
1160 | | - backtest.algorithm_id for backtest in backtest_results |
1161 | | - ] |
1162 | | - active_strategies = [ |
1163 | | - strategy for strategy in active_strategies |
1164 | | - if strategy.algorithm_id in algorithm_ids_selection |
1165 | | - ] |
1166 | | - |
1167 | | - # Save the intermediate backtests to a temp storage location |
1168 | | - # if backtest_storage_directory is provided and clean up memory |
1169 | | - if backtest_storage_directory is not None: |
1170 | | - path = storage_directories[backtest_date_range] |
1171 | | - save_backtests_to_directory( |
1172 | | - backtests=backtest_results, directory_path=path, |
1173 | | - ) |
1174 | | - |
1175 | | - else: |
1176 | | - for backtest in backtest_results: |
1177 | | - backtests_ordered_by_algorithm.setdefault( |
1178 | | - backtest.algorithm_id, [] |
1179 | | - ).append(backtest) |
1180 | | - |
1181 | | - del backtest_results |
1182 | | - |
1183 | | - # Free up memory |
1184 | | - gc.collect() |
1185 | | - |
1186 | | - def load_backtest_filter_fn(bt: Backtest) -> bool: |
1187 | | - return bt.algorithm_id in algorithm_ids_selection |
1188 | | - |
1189 | | - # load all backtests from storage directories and combine them |
1190 | | - if backtest_storage_directory is not None: |
1191 | | - for backtest_range in storage_directories: |
1192 | | - path = storage_directories[backtest_range] |
1193 | | - loaded_backtests = load_backtests_from_directory( |
1194 | | - directory_path=path, |
1195 | | - filter_function=load_backtest_filter_fn |
1196 | | - ) |
1197 | | - |
1198 | | - for backtest in loaded_backtests: |
1199 | | - backtests_ordered_by_algorithm.setdefault( |
1200 | | - backtest.algorithm_id, [] |
1201 | | - ).append(backtest) |
1202 | | - |
1203 | | - # Remove all temp storage directories |
1204 | | - shutil.rmtree(path) |
1205 | | - else: |
1206 | | - # Remove all strategies that are not in the final selection |
1207 | | - backtests_ordered_by_algorithm = { |
1208 | | - algorithm_id: backtests |
1209 | | - for algorithm_id, backtests in |
1210 | | - backtests_ordered_by_algorithm.items() |
1211 | | - if algorithm_id in algorithm_ids_selection |
1212 | | - } |
1213 | | - |
1214 | | - for algorith_id in backtests_ordered_by_algorithm.keys(): |
1215 | | - backtests.append( |
1216 | | - combine_backtests( |
1217 | | - backtests_ordered_by_algorithm[algorith_id] |
1218 | | - ) |
1219 | | - ) |
1220 | | - |
1221 | | - if backtest_storage_directory is not None: |
1222 | | - # Save final combined backtests to storage directory |
1223 | | - save_backtests_to_directory( |
1224 | | - backtests=backtests, |
1225 | | - directory_path=backtest_storage_directory, |
1226 | | - ) |
1227 | | - |
1228 | | - return backtests |
| 1049 | + if use_checkpoints: |
| 1050 | + sdsi = skip_data_sources_initialization |
| 1051 | + return backtest_service.run_vector_backtests_with_checkpoints( |
| 1052 | + strategies=strategies, |
| 1053 | + backtest_date_range=backtest_date_range, |
| 1054 | + backtest_date_ranges=backtest_date_ranges, |
| 1055 | + snapshot_interval=snapshot_interval, |
| 1056 | + risk_free_rate=risk_free_rate, |
| 1057 | + initial_amount=initial_amount, |
| 1058 | + skip_data_sources_initialization=sdsi, |
| 1059 | + show_progress=show_progress, |
| 1060 | + market=market, |
| 1061 | + trading_symbol=trading_symbol, |
| 1062 | + continue_on_error=continue_on_error, |
| 1063 | + backtest_storage_directory=backtest_storage_directory, |
| 1064 | + filter_function=filter_function |
| 1065 | + ) |
| 1066 | + sdsi = skip_data_sources_initialization |
| 1067 | + return backtest_service.run_vector_backtests( |
| 1068 | + strategies=strategies, |
| 1069 | + backtest_date_range=backtest_date_range, |
| 1070 | + backtest_date_ranges=backtest_date_ranges, |
| 1071 | + snapshot_interval=snapshot_interval, |
| 1072 | + risk_free_rate=risk_free_rate, |
| 1073 | + initial_amount=initial_amount, |
| 1074 | + skip_data_sources_initialization=sdsi, |
| 1075 | + show_progress=show_progress, |
| 1076 | + market=market, |
| 1077 | + trading_symbol=trading_symbol, |
| 1078 | + continue_on_error=continue_on_error, |
| 1079 | + filter_function=filter_function, |
| 1080 | + backtest_storage_directory=backtest_storage_directory, |
| 1081 | + ) |
1229 | 1082 |
|
1230 | 1083 | def run_vector_backtest( |
1231 | 1084 | self, |
|
0 commit comments