refactor(portfolio): TET副本类名对齐策略库扫描约定—*StrategyEx→*ExStrategy(AllWeatherExStrategy等8类):strategy_registry._extract_class_names只认endswith('Strategy')的类名,Ex结尾类被判'无策略类'整文件跳过→策略库/代码编辑器看不到副本;改名后_scan_self_owned自动识别4文件(type=portfolio);CLI/API/前端选项值(all_weather_ex等)不变;328绿 [vps]
CI/CD / test (push) Successful in 12s
CI/CD / nas-deploy (push) Successful in 34s
CI/CD / nas-verify (push) Successful in 19s

This commit is contained in:
2026-08-15 13:35:23 +08:00
parent 5507058089
commit 01184c2b1f
6 changed files with 69 additions and 69 deletions
+20 -20
View File
@@ -185,28 +185,28 @@ def _build_strategy(args: argparse.Namespace, provider: Any) -> Any:
return ChannelTestStrategy(provider=provider, config=ChannelTestConfig())
# TET Phase 2 验证副本(issue #19):取数走 _ex strict 接口,逻辑与原策略同源 copy
if name == "all_weather_ex":
from .strategies import AllWeatherConfigEx, AllWeatherStrategyEx
return AllWeatherStrategyEx(
from .strategies import AllWeatherExConfig, AllWeatherExStrategy
return AllWeatherExStrategy(
provider=provider,
config=AllWeatherConfigEx(max_pool=args.max_pool),
config=AllWeatherExConfig(max_pool=args.max_pool),
)
if name == "momentum_timing_ex":
from .strategies import MomentumTimingConfigEx, MomentumTimingStrategyEx
return MomentumTimingStrategyEx(
from .strategies import MomentumTimingExConfig, MomentumTimingExStrategy
return MomentumTimingExStrategy(
provider=provider,
config=MomentumTimingConfigEx(max_pool=args.max_pool),
config=MomentumTimingExConfig(max_pool=args.max_pool),
)
if name == "value_selection_ex":
from .strategies import ValueSelectionConfigEx, ValueSelectionStrategyEx
return ValueSelectionStrategyEx(
from .strategies import ValueSelectionExConfig, ValueSelectionExStrategy
return ValueSelectionExStrategy(
provider=provider,
config=ValueSelectionConfigEx(max_pool=args.max_pool),
config=ValueSelectionExConfig(max_pool=args.max_pool),
)
if name == "small_cap_ex":
from .strategies import SmallCapConfigEx, SmallCapStrategyEx
return SmallCapStrategyEx(
from .strategies import SmallCapExConfig, SmallCapExStrategy
return SmallCapExStrategy(
provider=provider,
config=SmallCapConfigEx(max_pool=args.max_pool),
config=SmallCapExConfig(max_pool=args.max_pool),
)
raise ValueError(
f"未知 strategy: {name}(支持: all_weather / momentum_timing / value_selection / small_cap"
@@ -225,28 +225,28 @@ def _register_schedule(strategy: Any) -> None:
try:
from .strategies import (
AllWeatherStrategy,
AllWeatherStrategyEx,
AllWeatherExStrategy,
MomentumTimingStrategy,
MomentumTimingStrategyEx,
MomentumTimingExStrategy,
SmallCapStrategy,
SmallCapStrategyEx,
SmallCapExStrategy,
ValueSelectionStrategy,
ValueSelectionStrategyEx,
ValueSelectionExStrategy,
)
if isinstance(strategy, (AllWeatherStrategy, AllWeatherStrategyEx)):
if isinstance(strategy, (AllWeatherStrategy, AllWeatherExStrategy)):
run_daily(strategy.prepare_stock_list, "9:05")
run_monthly(strategy.monthly_adjustment, 1, "9:30")
run_daily(strategy.stop_loss, "14:00")
return
if isinstance(strategy, (MomentumTimingStrategy, MomentumTimingStrategyEx)):
if isinstance(strategy, (MomentumTimingStrategy, MomentumTimingExStrategy)):
# 原策略 handle_data 单位时间触发 → 每日 9:30
run_daily(strategy.handle_data, "9:30")
return
if isinstance(strategy, (ValueSelectionStrategy, ValueSelectionStrategyEx)):
if isinstance(strategy, (ValueSelectionStrategy, ValueSelectionExStrategy)):
# 原策略 run_monthly 第 5 个交易日(月度调仓)
run_monthly(strategy.monthly_adjustment, 5, "9:30")
return
if isinstance(strategy, (SmallCapStrategy, SmallCapStrategyEx)):
if isinstance(strategy, (SmallCapStrategy, SmallCapExStrategy)):
# 原策略 handle_data 单位时间触发 → 每日 9:30
# 5 日调仓周期由 handle_data 内部 day_count % tc == 0 控制(对齐 g.t % g.tc)
run_daily(strategy.handle_data, "9:30")
+12 -12
View File
@@ -1,13 +1,13 @@
"""sanguo_portfolio 策略层。"""
from .all_weather import AllWeatherConfig, AllWeatherStrategy, BrokerFacade
from .all_weather_ex import AllWeatherConfigEx, AllWeatherStrategyEx
from .all_weather_ex import AllWeatherExConfig, AllWeatherExStrategy
from .channel_test import ChannelTestConfig, ChannelTestStrategy
from .momentum_timing import MomentumTimingConfig, MomentumTimingStrategy
from .momentum_timing_ex import MomentumTimingConfigEx, MomentumTimingStrategyEx
from .momentum_timing_ex import MomentumTimingExConfig, MomentumTimingExStrategy
from .small_cap import SmallCapConfig, SmallCapStrategy
from .small_cap_ex import SmallCapConfigEx, SmallCapStrategyEx
from .small_cap_ex import SmallCapExConfig, SmallCapExStrategy
from .value_selection import ValueSelectionConfig, ValueSelectionStrategy
from .value_selection_ex import ValueSelectionConfigEx, ValueSelectionStrategyEx
from .value_selection_ex import ValueSelectionExConfig, ValueSelectionExStrategy
__all__ = [
"AllWeatherStrategy",
@@ -22,12 +22,12 @@ __all__ = [
"ValueSelectionStrategy",
"ValueSelectionConfig",
# TET Phase 2 验证副本(issue #19,验证后 Phase 3 删)
"AllWeatherStrategyEx",
"AllWeatherConfigEx",
"MomentumTimingStrategyEx",
"MomentumTimingConfigEx",
"SmallCapStrategyEx",
"SmallCapConfigEx",
"ValueSelectionStrategyEx",
"ValueSelectionConfigEx",
"AllWeatherExStrategy",
"AllWeatherExConfig",
"MomentumTimingExStrategy",
"MomentumTimingExConfig",
"SmallCapExStrategy",
"SmallCapExConfig",
"ValueSelectionExStrategy",
"ValueSelectionExConfig",
]
+10 -10
View File
@@ -1,11 +1,11 @@
"""聚宽"全天候轮动"策略(post48819)翻译到 BulletTrade 框架。
聚宽源码完整保留在原仓库 transcript,这里做**结构等价**翻译:
- ``initialize`` ``AllWeatherStrategyEx.initialize``
- ``prepare_stock_list`` ``AllWeatherStrategyEx.prepare_stock_list``
- ``stop_loss`` ``AllWeatherStrategyEx.stop_loss``
- ``monthly_adjustment`` ``AllWeatherStrategyEx.monthly_adjustment``
- ``SMALL/BIG/ROIC_BIG/BM`` ``AllWeatherStrategyEx.small/big/roic_big/bm``
- ``initialize`` ``AllWeatherExStrategy.initialize``
- ``prepare_stock_list`` ``AllWeatherExStrategy.prepare_stock_list``
- ``stop_loss`` ``AllWeatherExStrategy.stop_loss``
- ``monthly_adjustment`` ``AllWeatherExStrategy.monthly_adjustment``
- ``SMALL/BIG/ROIC_BIG/BM`` ``AllWeatherExStrategy.small/big/roic_big/bm``
- ``filter_*`` ``sanguo_portfolio.filters``
策略层不直接 import bullet-trade 顶层 API(避免 Mac dev 环境装不全崩),通过两个注入点接入:
@@ -57,7 +57,7 @@ class BrokerFacade:
# ------------------------ 策略 ------------------------
@dataclass
class AllWeatherConfigEx:
class AllWeatherExConfig:
"""全天候策略参数(聚宽 initialize 的硬编码抽出来便于调参)。"""
stock_num: int = 3 # g.stock_num
@@ -79,7 +79,7 @@ class AllWeatherConfigEx:
max_pool: int = 0 # 0=不限;MVP/验证用,限制 _stock_pool 返回前 N 只(避免全成分基本面下载过慢)
class AllWeatherStrategyEx:
class AllWeatherExStrategy:
"""聚宽"全天候轮动"策略(动态选股 + 大小盘轮动 + 海外 ETF 兜底 + 涨停盯盘)。
实例化时不连数据/不下单,所有 IO 走注入的 ``provider`` ``broker``
@@ -90,11 +90,11 @@ class AllWeatherStrategyEx:
self,
provider: Any,
broker: Optional[BrokerFacade] = None,
config: Optional[AllWeatherConfigEx] = None,
config: Optional[AllWeatherExConfig] = None,
) -> None:
self.provider = provider
self.broker = broker or BrokerFacade()
self.config = config or AllWeatherConfigEx()
self.config = config or AllWeatherExConfig()
# 聚宽 g.* 全局变量映射到实例属性
self.hold_list: List[str] = []
@@ -603,4 +603,4 @@ def _shift_date(date_str: str, days: int) -> str:
return (dt + datetime.timedelta(days=days)).strftime("%Y-%m-%d")
__all__ = ["AllWeatherStrategyEx", "AllWeatherConfigEx", "BrokerFacade"]
__all__ = ["AllWeatherExStrategy", "AllWeatherExConfig", "BrokerFacade"]
@@ -2,12 +2,12 @@
聚宽源码完整保留在 ``docs/research/joinquant_strategies/03_momentum_timing/source.py``,
这里做**结构等价 + bug 修复**翻译:
- ``initialize`` ``MomentumTimingStrategyEx.initialize``
- ``calRPS`` ``MomentumTimingStrategyEx._cal_rps`` (**修复取数区间**)
- ``findStockPool`` ``MomentumTimingStrategyEx._find_stock_pool``
- ``selectStocks`` ``MomentumTimingStrategyEx._select_stocks``
- ``calBuySign`` ``MomentumTimingStrategyEx._cal_buy_sign``
- ``handle_data`` ``MomentumTimingStrategyEx.handle_data`` (**修复 date.today()**)
- ``initialize`` ``MomentumTimingExStrategy.initialize``
- ``calRPS`` ``MomentumTimingExStrategy._cal_rps`` (**修复取数区间**)
- ``findStockPool`` ``MomentumTimingExStrategy._find_stock_pool``
- ``selectStocks`` ``MomentumTimingExStrategy._select_stocks``
- ``calBuySign`` ``MomentumTimingExStrategy._cal_buy_sign``
- ``handle_data`` ``MomentumTimingExStrategy.handle_data`` (**修复 date.today()**)
策略层不直接 import bullet-trade 顶层 API(避免 Mac dev 环境装不全崩),
通过两个注入点接入( all_weather 模式):
@@ -66,7 +66,7 @@ _DEFAULT_INDEX_LIST: List[str] = [
@dataclass
class MomentumTimingConfigEx:
class MomentumTimingExConfig:
"""牛熊分界+取强舍弱+均线动量 策略参数(聚宽 g.* 全局变量抽出便于调参)。"""
# 板块列表(默认 10 个中证行业指数 000928-000937,G1 补全后切回原版,见模块顶部说明)
@@ -82,7 +82,7 @@ class MomentumTimingConfigEx:
# ------------------------ 策略 ------------------------
class MomentumTimingStrategyEx:
class MomentumTimingExStrategy:
"""牛熊分界+取强舍弱+均线动量策略(纯量价,无基本面)。
实例化时不连数据/不下单,所有 IO 走注入的 ``provider`` ``broker``
@@ -93,11 +93,11 @@ class MomentumTimingStrategyEx:
self,
provider: Any,
broker: Optional[BrokerFacade] = None,
config: Optional[MomentumTimingConfigEx] = None,
config: Optional[MomentumTimingExConfig] = None,
) -> None:
self.provider = provider
self.broker = broker or BrokerFacade()
self.config = config or MomentumTimingConfigEx()
self.config = config or MomentumTimingExConfig()
# =================== initialize ===================
def initialize(self, context: Any) -> None:
@@ -440,4 +440,4 @@ def _shift_date(date_str: str, days: int) -> str:
return (dt + datetime.timedelta(days=days)).strftime("%Y-%m-%d")
__all__ = ["MomentumTimingStrategyEx", "MomentumTimingConfigEx"]
__all__ = ["MomentumTimingExStrategy", "MomentumTimingExConfig"]
+9 -9
View File
@@ -14,10 +14,10 @@
- statsmodels 回归 import(原代码 import 但未实际用)
翻译对照:
- ``initialize`` ``SmallCapStrategyEx.initialize``
- ``pick_stocks`` ``SmallCapStrategyEx._pick_stocks`` (**py2py3**: df.sortsort_values)
- ``compute_signals`` ``SmallCapStrategyEx.handle_data`` (**5 日计数器**替代 g.t)
- ``rebalance`` ``SmallCapStrategyEx._rebalance`` (**仅保留股票部分**,
- ``initialize`` ``SmallCapExStrategy.initialize``
- ``pick_stocks`` ``SmallCapExStrategy._pick_stocks`` (**py2py3**: df.sortsort_values)
- ``compute_signals`` ``SmallCapExStrategy.handle_data`` (**5 日计数器**替代 g.t)
- ``rebalance`` ``SmallCapExStrategy._rebalance`` (**仅保留股票部分**,
去掉期货/账户调配/保证金,等权调仓)
- ``compute_hedge_ratio`` / ``get_next_month_future`` / SubPortfolio **删除**
@@ -54,7 +54,7 @@ logger = logging.getLogger(__name__)
# ------------------------ Config ------------------------
@dataclass
class SmallCapConfigEx:
class SmallCapExConfig:
"""小市值 20 只轮动策略参数(聚宽 g.* 全局变量抽出便于调参)。
默认值严格对齐原策略 ``set_params`` (source.py 38-48 ):
@@ -87,7 +87,7 @@ class SmallCapConfigEx:
# ------------------------ 策略 ------------------------
class SmallCapStrategyEx:
class SmallCapExStrategy:
"""小市值 20 只轮动策略(纯选股,无对冲)。
实例化时不连数据/不下单,所有 IO 走注入的 ``provider`` ``broker``
@@ -103,11 +103,11 @@ class SmallCapStrategyEx:
self,
provider: Any,
broker: Optional[BrokerFacade] = None,
config: Optional[SmallCapConfigEx] = None,
config: Optional[SmallCapExConfig] = None,
) -> None:
self.provider = provider
self.broker = broker or BrokerFacade()
self.config = config or SmallCapConfigEx()
self.config = config or SmallCapExConfig()
# 聚宽 g.* 全局变量映射到实例属性
self.day_count: int = 0 # g.t:运行天数
@@ -446,4 +446,4 @@ def _shift_date(date_str: str, days: int) -> str:
return (dt + datetime.timedelta(days=days)).strftime("%Y-%m-%d")
__all__ = ["SmallCapStrategyEx", "SmallCapConfigEx"]
__all__ = ["SmallCapExStrategy", "SmallCapExConfig"]
@@ -2,8 +2,8 @@
聚宽源码完整保留在 ``docs/research/joinquant_strategies/01_value_selection/source.py``,
这里做**结构等价 + bug 修复 + py2py3** 翻译:
- ``initialize`` ``ValueSelectionStrategyEx.initialize``
- ``get_stock_list`` ``ValueSelectionStrategyEx._get_stock_list``
- ``initialize`` ``ValueSelectionExStrategy.initialize``
- ``get_stock_list`` ``ValueSelectionExStrategy._get_stock_list``
- ``get_check_stocks_sort`` **删除**(排序后不截断+全买的死代码,KISS)
- ``buy`` / ``sell`` 调仓逻辑合入 ``monthly_adjustment``
- ``get_data`` (pd.Panel) ``provider.get_value_metrics`` 接口替代
@@ -56,7 +56,7 @@ logger = logging.getLogger(__name__)
# ------------------------ Config ------------------------
@dataclass
class ValueSelectionConfigEx:
class ValueSelectionExConfig:
"""价值精选 6 条策略参数(聚宽 g.* 全局变量抽出便于调参)。
6 条过滤阈值严格对齐原策略 source.py 91-97 行注释 + 105-171 行代码
@@ -97,7 +97,7 @@ class ValueSelectionConfigEx:
# ------------------------ 策略 ------------------------
class ValueSelectionStrategyEx:
class ValueSelectionExStrategy:
"""价值精选 6 条策略(全市场横向比较 + 月度调仓)。
实例化时不连数据/不下单,所有 IO 走注入的 ``provider`` ``broker``
@@ -115,11 +115,11 @@ class ValueSelectionStrategyEx:
self,
provider: Any,
broker: Optional[BrokerFacade] = None,
config: Optional[ValueSelectionConfigEx] = None,
config: Optional[ValueSelectionExConfig] = None,
) -> None:
self.provider = provider
self.broker = broker or BrokerFacade()
self.config = config or ValueSelectionConfigEx()
self.config = config or ValueSelectionExConfig()
# =================== initialize ===================
def initialize(self, context: Any) -> None:
@@ -455,4 +455,4 @@ def _is_valid_number(v: Any) -> bool:
return not math.isnan(fv) and not math.isinf(fv)
__all__ = ["ValueSelectionStrategyEx", "ValueSelectionConfigEx"]
__all__ = ["ValueSelectionExStrategy", "ValueSelectionExConfig"]