|
25 | 25 | pass |
26 | 26 |
|
27 | 27 | from backtest.loaders.registry import ( |
| 28 | + FALLBACK_CHAINS, |
28 | 29 | LOADER_REGISTRY, |
29 | 30 | get_loader_cls_with_fallback, |
30 | 31 | resolve_loader, |
@@ -302,6 +303,25 @@ def main(run_dir: Path) -> None: |
302 | 303 | fields=config.get("extra_fields") or None, |
303 | 304 | interval=interval, |
304 | 305 | ) |
| 306 | + # Runtime fallback: try next sources in chain when primary returns empty |
| 307 | + if not data_map and codes: |
| 308 | + market = _detect_market(codes[0]) |
| 309 | + for fb_name in FALLBACK_CHAINS.get(market, []): |
| 310 | + if fb_name == source or fb_name not in LOADER_REGISTRY: |
| 311 | + continue |
| 312 | + fb_loader = LOADER_REGISTRY[fb_name]() |
| 313 | + if not fb_loader.is_available(): |
| 314 | + continue |
| 315 | + fb_codes = _normalize_codes(codes, fb_name) |
| 316 | + data_map = fb_loader.fetch( |
| 317 | + fb_codes, config.get("start_date", ""), |
| 318 | + config.get("end_date", ""), interval=interval, |
| 319 | + ) |
| 320 | + if data_map: |
| 321 | + logger.info("Runtime fallback: %s -> %s", source, fb_name) |
| 322 | + source = fb_name |
| 323 | + loader = fb_loader |
| 324 | + break |
305 | 325 | if not data_map: |
306 | 326 | print(json.dumps({"error": "No data fetched"})) |
307 | 327 | sys.exit(1) |
@@ -476,6 +496,21 @@ def _fetch_auto(codes: List[str], config: dict, interval: str = "1D") -> dict: |
476 | 496 | normalized_codes = _normalize_codes(market_codes, src_name) |
477 | 497 | fields = config.get("extra_fields") if src_name == "tushare" else None |
478 | 498 | result = loader.fetch(normalized_codes, start_date, end_date, fields=fields, interval=interval) |
| 499 | + |
| 500 | + # Runtime fallback: try remaining sources when primary returns empty |
| 501 | + if not result: |
| 502 | + for fb_name in FALLBACK_CHAINS.get(market, []): |
| 503 | + if fb_name == src_name or fb_name not in LOADER_REGISTRY: |
| 504 | + continue |
| 505 | + fb_loader = LOADER_REGISTRY[fb_name]() |
| 506 | + if not fb_loader.is_available(): |
| 507 | + continue |
| 508 | + fb_codes = _normalize_codes(market_codes, fb_name) |
| 509 | + result = fb_loader.fetch(fb_codes, start_date, end_date, interval=interval) |
| 510 | + if result: |
| 511 | + logger.info("Runtime fallback: %s -> %s for %s", src_name, fb_name, market) |
| 512 | + break |
| 513 | + |
479 | 514 | merged.update(result) |
480 | 515 |
|
481 | 516 | return merged |
|
0 commit comments