2323import octobot_commons .errors as errors
2424import octobot_commons .logging as commons_logging
2525import octobot_commons .pretty_printer as pretty_printer
26- import octobot_commons .symbol_util as symbol_util
26+ import octobot_commons .symbols . symbol_util as symbol_util
2727import octobot_commons .databases as databases
2828import octobot_commons .optimization_campaign as optimization_campaign
2929import octobot_commons .time_frame_manager as time_frame_manager
3737import octobot_evaluators .constants as evaluator_constants
3838
3939import octobot_trading .api as trading_api
40+ import octobot_trading .enums as trading_enums
4041
4142
4243class IndependentBacktesting :
@@ -165,7 +166,7 @@ async def _post_backtesting_end_callback(self):
165166 @staticmethod
166167 def _get_market_delta (symbol , exchange_manager , min_timeframe ):
167168 market_data = trading_api .get_symbol_historical_candles (
168- trading_api .get_symbol_data (exchange_manager , symbol ), min_timeframe )
169+ trading_api .get_symbol_data (exchange_manager , symbol . legacy_symbol () ), min_timeframe )
169170 market_begin = market_data [enums .PriceIndexes .IND_PRICE_CLOSE .value ][0 ]
170171 market_end = market_data [enums .PriceIndexes .IND_PRICE_CLOSE .value ][- 1 ]
171172
@@ -186,7 +187,7 @@ async def _register_available_data(self):
186187 if exchange_name not in self .symbols_to_create_exchange_classes :
187188 self .symbols_to_create_exchange_classes [exchange_name ] = []
188189 for symbol in description [backtesting_enums .DataFormatKeys .SYMBOLS .value ]:
189- self .symbols_to_create_exchange_classes [exchange_name ].append (symbol )
190+ self .symbols_to_create_exchange_classes [exchange_name ].append (symbol_util . parse_symbol ( symbol ) )
190191
191192 def _init_default_config_values (self ):
192193 self .risk = copy .deepcopy (self .octobot_origin_config [common_constants .CONFIG_TRADING ][
@@ -241,9 +242,9 @@ def _get_exchanges_report(self, reference_market, trading_mode):
241242 exchange_name = trading_api .get_exchange_name (exchange_manager )
242243 for symbol in self .symbols_to_create_exchange_classes [exchange_name ]:
243244 market_delta = self ._get_market_delta (symbol , exchange_manager , min_timeframe )
244- report [SYMBOL_REPORT ].append ({symbol : market_delta * 100 })
245+ report [SYMBOL_REPORT ].append ({symbol . symbol_str : market_delta * 100 })
245246 report [CHART_IDENTIFIERS ].append ({
246- "symbol" : symbol ,
247+ "symbol" : symbol . symbol_str ,
247248 "exchange_id" : exchange_id ,
248249 "exchange_name" : exchange_name ,
249250 "time_frame" : min_timeframe .value
@@ -286,7 +287,7 @@ def _log_trades_history(self, exchange_manager, exchange_name):
286287
287288 def _log_symbol_report (self , symbol , exchange_manager , min_time_frame ):
288289 market_delta = self ._get_market_delta (symbol , exchange_manager , min_time_frame )
289- self .logger .info (f"{ symbol } Profitability : { market_delta * 100 } %" )
290+ self .logger .info (f"{ symbol . symbol_str } Profitability : { market_delta * 100 } %" )
290291
291292 def _log_global_report (self , exchange_manager ):
292293 _ , profitability , _ , market_average_profitability , _ = trading_api .get_profitability_stats (exchange_manager )
@@ -310,6 +311,14 @@ def _log_global_report(self, exchange_manager):
310311 f"{ round (backtesting_api .get_backtesting_duration (self .octobot_backtesting .backtesting ), 3 )} sec" )
311312
312313 def _adapt_config (self ):
314+ self ._init_exchange_type ()
315+ self .backtesting_config [common_constants .CONFIG_EXCHANGES ] = copy .deepcopy (
316+ self .octobot_origin_config [common_constants .CONFIG_EXCHANGES ])
317+ for exchange_details in self .backtesting_config [common_constants .CONFIG_EXCHANGES ].values ():
318+ exchange_details .pop (common_constants .CONFIG_EXCHANGE_KEY , None )
319+ exchange_details .pop (common_constants .CONFIG_EXCHANGE_SECRET , None )
320+ exchange_details .pop (common_constants .CONFIG_EXCHANGE_PASSWORD , None )
321+ exchange_details .pop (common_constants .CONFIG_EXCHANGE_SANDBOXED , None )
313322 self .backtesting_config [common_constants .CONFIG_TRADING ][common_constants .CONFIG_TRADER_RISK ] = self .risk
314323 self .backtesting_config [common_constants .CONFIG_TRADING ][
315324 common_constants .CONFIG_TRADER_REFERENCE_MARKET ] = self ._find_reference_market ()
@@ -323,6 +332,17 @@ def _adapt_config(self):
323332 self .backtesting_config [evaluator_constants .CONFIG_FORCED_TIME_FRAME ] = self .forced_time_frames
324333 self ._add_config_default_backtesting_values ()
325334
335+ def _init_exchange_type (self ):
336+ try :
337+ for exchange_name in self .symbols_to_create_exchange_classes :
338+ # use current profile config to create a spot/future/margin backtesting exchange
339+ self .octobot_backtesting .exchange_type_by_exchange [exchange_name ] = \
340+ self .octobot_origin_config [common_constants .CONFIG_EXCHANGES ].get (exchange_name , {}).\
341+ get (common_constants .CONFIG_EXCHANGE_TYPE , common_constants .DEFAULT_EXCHANGE_TYPE )
342+ except StopIteration :
343+ # use default exchange type
344+ pass
345+
326346 async def _generate_backtesting_id_if_missing (self ):
327347 if self .backtesting_config [common_constants .CONFIG_BACKTESTING_ID ] is None :
328348 run_dbs_identifier = databases .RunDatabasesIdentifier (
@@ -337,25 +357,31 @@ async def _generate_backtesting_id_if_missing(self):
337357 def _find_reference_market (self ):
338358 ref_market_candidate = None
339359 ref_market_candidates = {}
340- for pairs in self .symbols_to_create_exchange_classes .values ():
341- if self .octobot_backtesting .is_future and \
342- trading_api .is_inverse_future_contract (self .octobot_backtesting .futures_contract_type ):
343- if len (pairs ) > 1 :
344- self .logger .error (f"Only one trading pair is supported in inverse contracts backtesting. "
345- f"Found: the following pairs: { pairs } " )
360+ for symbols in self .symbols_to_create_exchange_classes .values ():
361+ symbol = symbols [0 ]
362+ if next (iter (self .octobot_backtesting .exchange_type_by_exchange .values ())) \
363+ == common_constants .CONFIG_EXCHANGE_FUTURE :
364+ if symbol .is_inverse ():
365+ if not all ([symbol .is_inverse () for symbol in symbols ]):
366+ self .logger .error (f"Mixed inverse and linear contracts backtesting are not supported yet" )
367+ self .octobot_backtesting .futures_contract_type = trading_enums .FutureContractType .INVERSE_PERPETUAL
368+ else :
369+ if not all ([symbol .is_linear () for symbol in symbols ]):
370+ self .logger .error (f"Mixed inverse and linear contracts backtesting are not supported yet" )
371+ self .octobot_backtesting .futures_contract_type = trading_enums .FutureContractType .LINEAR_PERPETUAL
346372 # in inverse contracts, use BTC for BTC/USD trading as reference market
347- return symbol_util . split_symbol ( pairs [ 0 ])[ 0 ]
348- for pair in pairs :
349- base = symbol_util . split_symbol ( pair )[ 1 ]
373+ return symbol . settlement_asset
374+ for symbol in symbols :
375+ quote = symbol . quote
350376 if ref_market_candidate is None :
351- ref_market_candidate = base
352- if base in ref_market_candidates :
353- ref_market_candidates [base ] += 1
377+ ref_market_candidate = quote
378+ if quote in ref_market_candidates :
379+ ref_market_candidates [quote ] += 1
354380 else :
355- ref_market_candidates [base ] = 1
356- if ref_market_candidate != base and \
357- ref_market_candidates [ref_market_candidate ] < ref_market_candidates [base ]:
358- ref_market_candidate = base
381+ ref_market_candidates [quote ] = 1
382+ if ref_market_candidate != quote and \
383+ ref_market_candidates [ref_market_candidate ] < ref_market_candidates [quote ]:
384+ ref_market_candidate = quote
359385 return ref_market_candidate
360386
361387 def _add_config_default_backtesting_values (self ):
@@ -366,11 +392,12 @@ def _add_config_default_backtesting_values(self):
366392 self .backtesting_config [common_constants .CONFIG_SIMULATOR ][common_constants .CONFIG_ENABLED_OPTION ] = True
367393
368394 def _add_crypto_currencies_config (self ):
369- for pairs in self .symbols_to_create_exchange_classes .values ():
370- for pair in pairs :
371- if pair not in self .backtesting_config [common_constants .CONFIG_CRYPTO_CURRENCIES ]:
372- self .backtesting_config [common_constants .CONFIG_CRYPTO_CURRENCIES ][pair ] = {
395+ for symbols in self .symbols_to_create_exchange_classes .values ():
396+ for symbol in symbols :
397+ symbol_id = symbol .legacy_symbol ()
398+ if symbol_id not in self .backtesting_config [common_constants .CONFIG_CRYPTO_CURRENCIES ]:
399+ self .backtesting_config [common_constants .CONFIG_CRYPTO_CURRENCIES ][symbol_id ] = {
373400 common_constants .CONFIG_CRYPTO_PAIRS : []
374401 }
375- self .backtesting_config [common_constants .CONFIG_CRYPTO_CURRENCIES ][pair ][
376- common_constants .CONFIG_CRYPTO_PAIRS ] = [pair ]
402+ self .backtesting_config [common_constants .CONFIG_CRYPTO_CURRENCIES ][symbol_id ][
403+ common_constants .CONFIG_CRYPTO_PAIRS ] = [symbol_id ]
0 commit comments