@@ -381,6 +381,10 @@ def initialize_data_sources(
381381 None
382382 """
383383 logger .info ("Initializing data sources" )
384+
385+ if data_sources is None or len (data_sources ) == 0 :
386+ return
387+
384388 data_provider_service = self .container .data_provider_service ()
385389 data_provider_service .reset ()
386390
@@ -415,6 +419,10 @@ def initialize_data_sources_backtest(
415419 None
416420 """
417421 logger .info ("Initializing data sources for backtest" )
422+
423+ if data_sources is None or len (data_sources ) == 0 :
424+ return
425+
418426 data_provider_service = self .container .data_provider_service ()
419427 data_provider_service .reset ()
420428
@@ -900,18 +908,90 @@ def run_backtest(
900908
901909 return backtest
902910
911+ def run_vector_backtests (
912+ self ,
913+ backtest_date_range : BacktestDateRange ,
914+ initial_amount ,
915+ strategies : List [TradingStrategy ],
916+ snapshot_interval : SnapshotInterval = SnapshotInterval .DAILY ,
917+ risk_free_rate : Optional [float ] = None ,
918+ skip_data_sources_initialization : bool = False
919+ ):
920+ """
921+ Run vectorized backtests for a set of strategies. The provided
922+ set of strategies need to have their 'buy_signal_vectorized' and
923+ 'sell_signal_vectorized' methods implemented to support vectorized
924+ backtesting.
925+
926+ Args:
927+ backtest_date_range: The date range to run the backtest for
928+ (instance of BacktestDateRange)
929+ initial_amount: The initial amount to start the backtest with.
930+ This will be the amount of trading currency that the backtest
931+ portfolio will start with.
932+ strategies (List[TradingStrategy]): List of strategy objects
933+ that need to be backtested. Each strategy should implement
934+ the 'buy_signal_vectorized' and 'sell_signal_vectorized'
935+ methods to support vectorized backtesting.
936+ snapshot_interval (SnapshotInterval): The snapshot
937+ interval to use for the backtest. This is used to determine
938+ how often the portfolio snapshot should be taken during the
939+ backtest. The default is TRADE_CLOSE, which means that the
940+ portfolio snapshot will be taken at the end of each trade.
941+ risk_free_rate (Optional[float]): The risk-free rate to use for
942+ the backtest. This is used to calculate the Sharpe ratio
943+ and other performance metrics. If not provided, the default
944+ risk-free rate will be tried to be fetched from the
945+ US Treasury website.
946+ skip_data_sources_initialization (bool): Whether to skip the
947+ initialization of data sources. This is useful when the data
948+ sources are already initialized, and you want to skip the
949+ initialization step. This will speed up the backtesting
950+ process, but make sure that the data sources are already
951+ initialized before calling this method.
952+
953+ Returns:
954+ List[Backtest]: List of Backtest instances for each strategy
955+ that was backtested.
956+ """
957+ backtests = []
958+ data_sources = []
959+
960+ for strategy in strategies :
961+ data_sources .extend (strategy .data_sources )
962+
963+ if not skip_data_sources_initialization :
964+ self .initialize_data_sources_backtest (
965+ data_sources , backtest_date_range
966+ )
967+
968+ for strategy in tqdm (strategies ):
969+ backtests .append (
970+ self .run_vector_backtest (
971+ backtest_date_range = backtest_date_range ,
972+ initial_amount = initial_amount ,
973+ strategy = strategy ,
974+ snapshot_interval = snapshot_interval ,
975+ risk_free_rate = risk_free_rate ,
976+ skip_data_sources_initialization = True
977+ )
978+ )
979+
980+ return backtests
981+
903982 def run_vector_backtest (
904983 self ,
905984 backtest_date_range : BacktestDateRange ,
906985 initial_amount ,
907- strategy ,
986+ strategy : TradingStrategy ,
908987 snapshot_interval : SnapshotInterval = SnapshotInterval .DAILY ,
909988 metadata : Optional [Dict [str , str ]] = None ,
910989 risk_free_rate : Optional [float ] = None ,
990+ skip_data_sources_initialization : bool = False
911991 ) -> Backtest :
912992 """
913- Run a vectorized backtest for an algorithm . The provided algorithm
914- or set of strategies need to have their 'buy_signal_vectorized' and
993+ Run vectorized backtests for a strategy . The provided
994+ strategy needs to have its 'buy_signal_vectorized' and
915995 'sell_signal_vectorized' methods implemented to support vectorized
916996 backtesting.
917997
@@ -937,6 +1017,12 @@ def run_vector_backtest(
9371017 backtest report. This can be used to store additional
9381018 information about the backtest, such as the author, version,
9391019 parameters or any other relevant information.
1020+ skip_data_sources_initialization (bool): Whether to skip the
1021+ initialization of data sources. This is useful when the data
1022+ sources are already initialized, and you want to skip the
1023+ initialization step. This will speed up the backtesting
1024+ process, but make sure that the data sources are already
1025+ initialized before calling this method.
9401026
9411027 Returns:
9421028 Backtest: Instance of Backtest
@@ -947,9 +1033,11 @@ def run_vector_backtest(
9471033 snapshot_interval = snapshot_interval ,
9481034 initial_amount = initial_amount
9491035 )
950- self .initialize_data_sources_backtest (
951- strategy .data_sources , backtest_date_range
952- )
1036+
1037+ if not skip_data_sources_initialization :
1038+ self .initialize_data_sources_backtest (
1039+ strategy .data_sources , backtest_date_range
1040+ )
9531041
9541042 if risk_free_rate is None :
9551043 logger .info ("No risk free rate provided, retrieving it..." )
@@ -971,7 +1059,17 @@ def run_vector_backtest(
9711059 initial_amount = initial_amount ,
9721060 risk_free_rate = risk_free_rate
9731061 )
974- backtest .metadata = metadata if metadata is not None else {}
1062+
1063+ # Add the metadata to the backtest
1064+ if metadata is None :
1065+
1066+ if strategy .metadata is not None :
1067+ backtest .metadata = {}
1068+ else :
1069+ backtest .metadata = strategy .metadata
1070+ else :
1071+ backtest .metadata = metadata
1072+
9751073 return backtest
9761074
9771075 def run_backtests (
0 commit comments