@@ -589,7 +589,7 @@ async def test_get_price_and_predicted_order_book_with_invalid_formula(profile_d
589589
590590
591591async def test_get_price_and_predicted_order_book_with_error (profile_data_with_full_mm_config , mm_data_by_exchange ):
592- """Test _get_price_and_predicted_order_book with error handling ."""
592+ """Empty reference price sources yield an unsupported-pair error ."""
593593 profile_data_with_full_mm_config .tentacles [0 ].config [
594594 simple_market_making_trading .SimpleMarketMakingTradingMode .CONFIG_PAIR_SETTINGS
595595 ][0 ][simple_market_making_trading .SimpleMarketMakingTradingMode .REFERENCE_PRICE ] = []
@@ -602,7 +602,9 @@ async def test_get_price_and_predicted_order_book_with_error(profile_data_with_f
602602
603603 assert result == {
604604 "BTC/USDT" : {
605- market_making_constants .ERROR_KEY : "BTC/USDT reference price on binance can't be computed from the following price sources: {}"
605+ market_making_constants .ERROR_KEY : market_making_core ._get_unsupported_pair_message (
606+ "BTC/USDT" , "binance"
607+ ),
606608 }
607609 }
608610
@@ -1059,21 +1061,42 @@ async def _mock_exchange_manager_context(exchange_manager):
10591061 yield exchange_manager
10601062
10611063
1064+ def _configure_lazy_load_markets (exchange_manager , lazy_load_markets : bool ):
1065+ exchange_manager .exchange .get_option_value = mock .Mock (
1066+ side_effect = lambda option , ** kwargs : (
1067+ lazy_load_markets
1068+ if option == trading_enums .ExchangeClientOptions .LAZY_LOAD_MARKETS
1069+ else False
1070+ )
1071+ )
1072+
1073+
1074+ def _get_symbols_passed_to_fetch_tickers (fetch_tickers_mock ) -> set :
1075+ return {
1076+ symbol
1077+ for call_args in fetch_tickers_mock .call_args_list
1078+ for symbol in call_args .args [2 ]
1079+ }
1080+
1081+
10621082async def _call_fill_market_making_data_by_symbol (
10631083 price_sources ,
10641084 available_symbols ,
10651085 formula_init_patches = None ,
1086+ lazy_load_markets = True ,
10661087):
10671088 profile_data = _profile_data_for_market_making_fill ()
10681089 mm_data_by_symbol_by_exchange = {}
10691090 exchange_manager = mock .Mock ()
10701091 exchange_manager .exchange .get_all_available_symbols = mock .Mock (
10711092 return_value = available_symbols
10721093 )
1094+ _configure_lazy_load_markets (exchange_manager , lazy_load_markets )
10731095 ticker_updater = mock .Mock ()
10741096 ticker_updater .fetch_all_tickers = mock .AsyncMock (return_value = {})
10751097 ticker_cache = mock .Mock ()
10761098 ticker_cache .get_all_tickers = mock .Mock (return_value = {})
1099+ fetch_tickers_mock = mock .AsyncMock (return_value = ({}, ticker_updater ))
10771100
10781101 patches = [
10791102 mock .patch .object (
@@ -1099,7 +1122,7 @@ async def _call_fill_market_making_data_by_symbol(
10991122 mock .patch .object (
11001123 market_making_core ,
11011124 "_fetch_tickers" ,
1102- mock . AsyncMock ( return_value = ({}, ticker_updater )) ,
1125+ fetch_tickers_mock ,
11031126 ),
11041127 ]
11051128 if formula_init_patches :
@@ -1118,7 +1141,10 @@ async def _call_fill_market_making_data_by_symbol(
11181141 auth = None ,
11191142 )
11201143
1121- return mm_data_by_symbol_by_exchange , ticker_updater .fetch_all_tickers
1144+ return mm_data_by_symbol_by_exchange , {
1145+ "fetch_tickers" : fetch_tickers_mock ,
1146+ "fetch_all_tickers" : ticker_updater .fetch_all_tickers ,
1147+ }
11221148
11231149
11241150class TestFillMarketMakingDataBySymbol :
@@ -1141,12 +1167,13 @@ async def test_skips_ticker_fetch_for_unsupported_ref_price_symbol_with_formula(
11411167 mock .patch .object (exchange_operators , "create_ohlcv_operators" , return_value = []),
11421168 mock .patch .object (exchange_operators , "create_price_operators" , return_value = []),
11431169 ]
1144- mm_data_by_symbol_by_exchange , fetch_all_tickers_mock = await _call_fill_market_making_data_by_symbol (
1170+ mm_data_by_symbol_by_exchange , fetch_mocks = await _call_fill_market_making_data_by_symbol (
11451171 price_sources ,
11461172 available_symbols = {"BTC/ETH" , "ETH/USDT" },
11471173 formula_init_patches = formula_init_patches ,
11481174 )
11491175
1176+ fetch_all_tickers_mock = fetch_mocks ["fetch_all_tickers" ]
11501177 assert fetch_all_tickers_mock .call_count == 0 or all (
11511178 "BTC/USDT" not in call_args .args [0 ]
11521179 for call_args in fetch_all_tickers_mock .call_args_list
@@ -1189,12 +1216,13 @@ async def test_still_fetches_formula_dependency_symbols(self):
11891216 return_value = exchange_operators .create_price_operators (exchange_manager , "BTC/USDT" ),
11901217 ),
11911218 ]
1192- mm_data_by_symbol_by_exchange , fetch_all_tickers_mock = await _call_fill_market_making_data_by_symbol (
1219+ mm_data_by_symbol_by_exchange , fetch_mocks = await _call_fill_market_making_data_by_symbol (
11931220 price_sources ,
11941221 available_symbols = {"BTC/ETH" , "ETH/USDT" },
11951222 formula_init_patches = formula_init_patches ,
11961223 )
11971224
1225+ fetch_all_tickers_mock = fetch_mocks ["fetch_all_tickers" ]
11981226 assert fetch_all_tickers_mock .call_count == 1
11991227 fetched_symbols = fetch_all_tickers_mock .call_args .args [0 ]
12001228 assert "BTC/USDT" not in fetched_symbols
@@ -1203,6 +1231,7 @@ async def test_still_fetches_formula_dependency_symbols(self):
12031231 assert set (mm_data_by_symbol_by_exchange ["binance" ]) == {"BTC/USDT" , "BTC/ETH" , "ETH/USDT" }
12041232
12051233 async def test_fetches_ticker_for_unsupported_symbol_without_formula (self ):
1234+ """Lazy-load exchanges still fetch direct pairs not listed in available_symbols."""
12061235 price_sources = [
12071236 advanced_reference_price_import .AdvancedPriceSource (
12081237 exchange = "binance" ,
@@ -1212,13 +1241,55 @@ async def test_fetches_ticker_for_unsupported_symbol_without_formula(self):
12121241 formula = "" ,
12131242 )
12141243 ]
1215- mm_data_by_symbol_by_exchange , fetch_all_tickers_mock = await _call_fill_market_making_data_by_symbol (
1244+ mm_data_by_symbol_by_exchange , fetch_mocks = await _call_fill_market_making_data_by_symbol (
12161245 price_sources ,
12171246 available_symbols = {"BTC/ETH" , "ETH/USDT" },
1247+ lazy_load_markets = True ,
12181248 )
12191249
1220- assert fetch_all_tickers_mock .call_count == 1
1221- assert fetch_all_tickers_mock .call_args .args [0 ] == ["BTC/USDT" ]
1250+ fetch_tickers_mock = fetch_mocks ["fetch_tickers" ]
1251+ assert fetch_tickers_mock .call_count == 1
1252+ assert fetch_tickers_mock .call_args .args [2 ] == ["BTC/USDT" ]
1253+
1254+ async def test_skips_unavailable_direct_pair_on_cex (self ):
1255+ price_sources = [
1256+ advanced_reference_price_import .AdvancedPriceSource (
1257+ exchange = "binance" ,
1258+ pair = "BTC/USDT" ,
1259+ time_frame = advanced_reference_price_import .DEFAULT_TIME_FRAME ,
1260+ weight = decimal .Decimal ("1.0" ),
1261+ formula = "" ,
1262+ )
1263+ ]
1264+ mm_data_by_symbol_by_exchange , fetch_mocks = await _call_fill_market_making_data_by_symbol (
1265+ price_sources ,
1266+ available_symbols = {"BTC/ETH" , "ETH/USDT" },
1267+ lazy_load_markets = False ,
1268+ )
1269+
1270+ assert "BTC/USDT" not in _get_symbols_passed_to_fetch_tickers (fetch_mocks ["fetch_tickers" ])
1271+ btc_usdt_data = mm_data_by_symbol_by_exchange ["binance" ]["BTC/USDT" ]
1272+ assert btc_usdt_data .price .is_nan ()
1273+
1274+ async def test_still_fetches_unavailable_direct_pair_on_lazy_load_exchange (self ):
1275+ price_sources = [
1276+ advanced_reference_price_import .AdvancedPriceSource (
1277+ exchange = "binance" ,
1278+ pair = "BTC/USDT" ,
1279+ time_frame = advanced_reference_price_import .DEFAULT_TIME_FRAME ,
1280+ weight = decimal .Decimal ("1.0" ),
1281+ formula = "" ,
1282+ )
1283+ ]
1284+ _ , fetch_mocks = await _call_fill_market_making_data_by_symbol (
1285+ price_sources ,
1286+ available_symbols = {"BTC/ETH" , "ETH/USDT" },
1287+ lazy_load_markets = True ,
1288+ )
1289+
1290+ fetch_tickers_mock = fetch_mocks ["fetch_tickers" ]
1291+ assert fetch_tickers_mock .call_count == 1
1292+ assert fetch_tickers_mock .call_args .args [2 ] == ["BTC/USDT" ]
12221293
12231294
12241295def _mock_create_price_operators (prices_by_symbol : dict [str , float ]):
0 commit comments