fix(portfolio): 前端回测MVP链路5处bug(VPS同步/routes命令/max-pool/filter/日期)
Layer1-3 链路验证发现并修复: 1. VPS runner_backtest旧版(tar同步,修 from bullet_trade.core import BacktestEngine ImportError) 2. routes shlex.quote对Windows路径产POSIX单引号cmd不认 -> 手动拼远端命令 3. routes 'set X=Y &&' 尾空格进value致bullet_trade provider名匹配失败 -> 删set(runner自带setdefault) 4. 加 --max-pool 参数(默认前端30)避免HS300+中小综指1258只基本面下载超时 5. filter_st/filter_new对全成分逐只 -> max_pool slice提前到filter前; _coerce_datetime加YYYYMMDD解析(原fromisoformat不认miniQMT日期格式)
This commit is contained in:
@@ -212,7 +212,14 @@ def _coerce_datetime(value: Any) -> Optional[datetime]:
|
||||
try:
|
||||
return datetime.fromisoformat(value[:10])
|
||||
except ValueError:
|
||||
return None
|
||||
pass
|
||||
# YYYYMMDD 8位纯数字(provider/miniQMT 用此格式,fromisoformat 不认)
|
||||
if len(value) >= 8 and value[:8].isdigit():
|
||||
try:
|
||||
return datetime.strptime(value[:8], "%Y%m%d")
|
||||
except ValueError:
|
||||
pass
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ def parse_args() -> argparse.Namespace:
|
||||
p.add_argument("--end", default="2024-12-31", help="回测结束日期 YYYY-MM-DD")
|
||||
p.add_argument("--cash", type=float, default=1_000_000.0, help="初始资金(元)")
|
||||
p.add_argument("--benchmark", default="000300.XSHG", help="基准代码")
|
||||
p.add_argument("--max-pool", type=int, default=0, help="限制选股池前N只(0=不限,MVP验证用)")
|
||||
p.add_argument("--frequency", default="day", help="回测频率 day/minute")
|
||||
p.add_argument(
|
||||
"--provider-config", default="{}",
|
||||
@@ -103,7 +104,7 @@ def run_backtest(args: argparse.Namespace) -> Dict[str, Any]:
|
||||
from bullet_trade import BacktestEngine # type: ignore
|
||||
from bullet_trade.data.api import set_data_provider # type: ignore
|
||||
|
||||
from .strategies import AllWeatherStrategy
|
||||
from .strategies import AllWeatherStrategy, AllWeatherConfig
|
||||
|
||||
provider = build_provider(args.provider_config)
|
||||
set_data_provider(provider)
|
||||
@@ -112,7 +113,10 @@ def run_backtest(args: argparse.Namespace) -> Dict[str, Any]:
|
||||
holder: Dict[str, Any] = {}
|
||||
|
||||
def initialize(context):
|
||||
strategy = AllWeatherStrategy(provider=provider)
|
||||
strategy = AllWeatherStrategy(
|
||||
provider=provider,
|
||||
config=AllWeatherConfig(max_pool=args.max_pool),
|
||||
)
|
||||
holder["strategy"] = strategy
|
||||
|
||||
# bullet-trade 的 run_daily/run_monthly 接受全局函数;把 method 暴露为模块级
|
||||
@@ -144,6 +148,7 @@ def run_backtest(args: argparse.Namespace) -> Dict[str, Any]:
|
||||
order_value=lambda c, v: bt_ov(c, v),
|
||||
)
|
||||
|
||||
print("[runner] ENGINE_BUILD_PRE", flush=True)
|
||||
engine = BacktestEngine(
|
||||
initialize=initialize,
|
||||
start_date=args.start,
|
||||
@@ -152,7 +157,9 @@ def run_backtest(args: argparse.Namespace) -> Dict[str, Any]:
|
||||
initial_cash=args.cash,
|
||||
benchmark=args.benchmark,
|
||||
)
|
||||
print("[runner] RUN_START", flush=True)
|
||||
result = engine.run()
|
||||
print("[runner] RUN_DONE type=%s" % type(result).__name__, flush=True)
|
||||
|
||||
# 输出结果摘要到 markdown(JSON 模式时 result_file="" 跳过)
|
||||
if getattr(args, "result_file", ""):
|
||||
@@ -220,6 +227,7 @@ def run_backtest_json(params: Dict[str, Any]) -> Dict[str, Any]:
|
||||
frequency="day",
|
||||
provider_config="{}",
|
||||
result_file="", # JSON 模式不写 md
|
||||
max_pool=int(params.get("max_pool", 0)),
|
||||
)
|
||||
raw = run_backtest(args)
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ class AllWeatherConfig:
|
||||
benchmark: str = "000300.XSHG"
|
||||
roic_threshold: float = 0.08 # filter_roic 的 ROIC > 阈值
|
||||
new_stock_days: int = 375
|
||||
max_pool: int = 0 # 0=不限;MVP/验证用,限制 _stock_pool 返回前 N 只(避免全成分基本面下载过慢)
|
||||
|
||||
|
||||
class AllWeatherStrategy:
|
||||
@@ -397,6 +398,10 @@ class AllWeatherStrategy:
|
||||
logger.warning("get_index_stocks(%s) 失败: %s", index_symbol, exc)
|
||||
return []
|
||||
stocks = filters.filter_kcbj_stock(stocks)
|
||||
# max_pool 提前到 filter_st/filter_new 前:这俩对全成分(HS300+中小综指 1200+只)逐只
|
||||
# get_security_info 极慢,先 slice 到 N 只再过滤(验证用子集,语义略变但提速百倍)
|
||||
if self.config.max_pool > 0:
|
||||
stocks = stocks[: self.config.max_pool]
|
||||
stocks = filters.filter_st_stock(stocks, self.provider)
|
||||
stocks = filters.filter_new_stock(stocks, self.provider, previous_date, self.config.new_stock_days)
|
||||
return stocks
|
||||
|
||||
Reference in New Issue
Block a user