"""sanguo_portfolio:聚宽全天候轮动策略搬到 BulletTrade 框架。 子模块: - ``factors`` 纯函数估值/ROIC 因子(无外部依赖) - ``filters`` ST/停牌/科创北交/次新/涨跌停 过滤(无外部依赖) - ``providers`` BaostockProvider(Mac/回测,历史成分股+TTM) / SanguoMiniQmtProvider(VPS 实盘,继承 bullet-trade MiniQMTProvider) - ``strategies`` AllWeatherStrategy(聚宽 post48819 翻译) ENV GUARD(bullet-trade 0.9.2 坑): - ``import bullet_trade`` 时 default provider 是 jqdata,会强制 ``import jqdatasdk``, 本地/服务器都没装(用户铁律不用 jqdata 付费)。 - 在任何 ``import bullet_trade`` **之前**设 ``DEFAULT_DATA_PROVIDER``, default provider 切到非 jqdata provider,跳过 jqdatasdk。 - 实际数据走 ``set_data_provider(...)`` 覆盖。 """ import os import sys as _sys from unittest.mock import MagicMock as _MagicMock # ENV + mock 必须早于任何 bullet_trade import(providers 子模块会触发 bullet_trade): # miniqmt → import xtquant(周六休市 miniQMT 客户端不响应→卡死) # jqdata → import jqdatasdk(用户铁律不装→ModuleNotFoundError) # 方案: ENV 设 jqdata + 预插 mock jqdatasdk(@jq.utils.assert_auth 装饰器 passthrough), # 让 bullet_trade import 走 jqdata 分支拿 mock 不崩不卡;真实 provider 由 # set_data_provider 运行时注入覆盖(local/baostock/miniqmt)。 # 必须在包 __init__ 顶部(providers import 触发 bullet_trade 之前),runner_backtest 顶部太晚。 os.environ.setdefault("DEFAULT_DATA_PROVIDER", "jqdata") if "jqdatasdk" not in _sys.modules: _m = _MagicMock() _m.utils.assert_auth = lambda func: func # jqdata.py 用 @jq.utils.assert_auth 装饰器 _sys.modules["jqdatasdk"] = _m from . import factors, filters from .providers import BaostockProvider, SanguoMiniQmtProvider from .strategies import ( AllWeatherConfig, AllWeatherStrategy, BrokerFacade, MomentumTimingConfig, MomentumTimingStrategy, ) __all__ = [ "factors", "filters", "BaostockProvider", "SanguoMiniQmtProvider", "AllWeatherStrategy", "AllWeatherConfig", "BrokerFacade", "MomentumTimingStrategy", "MomentumTimingConfig", ]