fix(live): max_pool 全链默认 30→0(0=不限)——08-24巡检定罪MVP限流泄漏生产,选股池=代码序前30只失真 [vps]
根因(策略session 08-24 午休探针实证):runtime/live_strategy.py SANGUO_LIVE_MAX_POOL 默认30经env注入全部实盘+影子+paper实例,_stock_pool截断成份池为「代码序前30只」: small_cap「全市场最小市值」实际在000001平安银行等30只固定代码里选(平安银行≈3800亿 出现在小市值买入=market_cap开盘NaN排序失效叠bug);momentum每行业RPS只在代码序前 30里排;value 0/30+零委托史同源。注释自曝「MVP验证用」=限流遗留泄漏生产,上线 首日起全部选股失真。 改动(9处默认位一致30→0;语义0=不限,与策略层max_pool>0才截断一致): - sanguo_portfolio/live_strategy.py 适配器env默认+docstring - sanguo_live/runner.py _portfolio_env_for(存量DB显式值不篡改,缺列/0→"0") - sanguo_trader/shadow/supervisor.py 影子env默认 - sanguo_portfolio/runner_live.py live_env默认 - sanguo_trader/portfolio_paper.py + sanguo_api/routes_paper.py paper默认 - sanguo_api/routes_live.py create setdefault - frontend live/paper New.vue 表单默认 测试:env mapping三态断言(缺列/0→"0",显式30不篡改)+live_env默认"0" (RED→GREEN);CI范围642绿。存量实例DB仍存显式30,激活需配套DB迁移,必须与数据 session的get_security_info_batch SQL治本(101s→亚秒)同车部署——池放大×慢SQL=更糟。
This commit is contained in:
@@ -19,7 +19,7 @@ const isPortfolio = computed(() => strategyType.value === 'portfolio')
|
||||
|
||||
const portfolioOptions = ref<string[]>([])
|
||||
const portfolioStrategy = ref('all_weather')
|
||||
const poolForm = ref({ pool: 'all', max_pool: 30, benchmark: '000300.XSHG' })
|
||||
const poolForm = ref({ pool: 'all', max_pool: 0, benchmark: '000300.XSHG' })
|
||||
const POOL_OPTIONS = [
|
||||
{ label: 'HS300 子集(小范围验证)', value: 'hs300_subset' },
|
||||
{ label: '全市场', value: 'all' },
|
||||
|
||||
@@ -27,7 +27,7 @@ const instancesOfCta = computed(() => {
|
||||
const file = classToFile.value[form.value.strategies[0]?.name]
|
||||
return file ? instanceOptions.value.filter((i) => i.code_file === file) : instanceOptions.value
|
||||
})
|
||||
const poolForm = reactive({ pool: 'all', max_pool: 30, benchmark: '000300.XSHG' })
|
||||
const poolForm = reactive({ pool: 'all', max_pool: 0, benchmark: '000300.XSHG' })
|
||||
const POOL_OPTIONS = [
|
||||
{ label: 'HS300 子集(小范围验证)', value: 'hs300_subset' },
|
||||
{ label: '全市场', value: 'all' },
|
||||
|
||||
@@ -139,7 +139,7 @@ def create_live(req: LiveCreateRequest):
|
||||
if not payload.get("strategy_class"):
|
||||
raise HTTPException(400, "组合实盘需选择策略(strategy_class)")
|
||||
payload.setdefault("pool", "all")
|
||||
payload.setdefault("max_pool", 30)
|
||||
payload.setdefault("max_pool", 0)
|
||||
payload.setdefault("benchmark", "000300.XSHG")
|
||||
payload["vt_symbol"] = payload["pool"]
|
||||
# 周期由前端下拉传(miniQMT 成品K线档位);空=默认日线
|
||||
|
||||
@@ -62,7 +62,7 @@ class PaperCreateRequest(BaseModel):
|
||||
# 撮合引擎(影子柜台 P1):eod_replay=日终回放(NAS 20:30) / shadow=影子柜台(VPS 盘中实时)
|
||||
engine: str = "eod_replay"
|
||||
pool: str = "all"
|
||||
max_pool: int = 30
|
||||
max_pool: int = 0
|
||||
benchmark: str = "000300.XSHG"
|
||||
# §12.6 实例做实:账户绑档案;空=发起即建档(自动创建实例再发起)
|
||||
instance_id: int | None = None
|
||||
|
||||
@@ -349,7 +349,7 @@ def _portfolio_env_for(account_row: dict[str, Any], db_path: str) -> dict[str, s
|
||||
"SANGUO_QMT_ACCOUNT": account_row.get("account", ""),
|
||||
"SANGUO_QMT_PATH": account_row.get("mini_path", ""),
|
||||
"SANGUO_LIVE_STRATEGY": account_row.get("strategy_class", "all_weather"),
|
||||
"SANGUO_LIVE_MAX_POOL": str(account_row.get("max_pool", 30) or 30),
|
||||
"SANGUO_LIVE_MAX_POOL": str(account_row.get("max_pool", 0) or 0),
|
||||
"SANGUO_LIVE_BENCHMARK": account_row.get("benchmark", "000300.XSHG"),
|
||||
"SANGUO_LIVE_CASH": str(account_row.get("initial_capital", 1_000_000)),
|
||||
"SANGUO_LIVE_DB": db_path,
|
||||
|
||||
@@ -7,7 +7,7 @@ sanguo_portfolio 的 StrategyTemplate 策略挂到 run_daily/run_monthly 定时
|
||||
配置从 env 读(supervisor 注入,或手动 set 后直跑 ``python -m sanguo_portfolio.runner_live``):
|
||||
|
||||
SANGUO_LIVE_STRATEGY all_weather / momentum_timing / value_selection / small_cap
|
||||
SANGUO_LIVE_MAX_POOL 选股池上限(默认 30)
|
||||
SANGUO_LIVE_MAX_POOL 选股池上限(默认 0=不限;>0 截断成份池前 N 只)
|
||||
|
||||
数据 provider 由 runner_live ``set_data_provider`` 先行注入(miniQMT live 模式)。
|
||||
|
||||
@@ -45,7 +45,7 @@ def _build_live_strategy(provider):
|
||||
)
|
||||
|
||||
name = os.environ.get("SANGUO_LIVE_STRATEGY", "all_weather")
|
||||
max_pool = int(os.environ.get("SANGUO_LIVE_MAX_POOL", "30") or 30)
|
||||
max_pool = int(os.environ.get("SANGUO_LIVE_MAX_POOL", "0") or 0)
|
||||
factories = {
|
||||
"all_weather": lambda: AllWeatherStrategy(
|
||||
provider=provider, config=AllWeatherConfig(max_pool=max_pool)),
|
||||
|
||||
@@ -49,7 +49,7 @@ def live_env() -> Dict[str, str]:
|
||||
"""解析 env 实盘配置(带默认值)。独立出来便于单测。"""
|
||||
return {
|
||||
"strategy": os.environ.get("SANGUO_LIVE_STRATEGY", "all_weather"),
|
||||
"max_pool": os.environ.get("SANGUO_LIVE_MAX_POOL", "30"),
|
||||
"max_pool": os.environ.get("SANGUO_LIVE_MAX_POOL", "0"),
|
||||
"benchmark": os.environ.get("SANGUO_LIVE_BENCHMARK", "000300.XSHG"),
|
||||
"cash": os.environ.get("SANGUO_LIVE_CASH", "1000000"),
|
||||
"account": os.environ.get("SANGUO_QMT_ACCOUNT", ""),
|
||||
|
||||
@@ -72,7 +72,7 @@ def run_portfolio_live_step(db_path: str, account_id: int, today: str | None = N
|
||||
raise ValueError("portfolio 账户缺少策略配置")
|
||||
strategy_name = strategies[0].get("name", "all_weather")
|
||||
pool = (json.loads(acc["symbols"] or "[]") or ["hs300_subset"])[0]
|
||||
max_pool = int(strategies[0].get("params", {}).get("max_pool", 30))
|
||||
max_pool = int(strategies[0].get("params", {}).get("max_pool", 0))
|
||||
benchmark = strategies[0].get("params", {}).get("benchmark", "000300.XSHG")
|
||||
|
||||
from sanguo_portfolio.runner_backtest import run_backtest_json
|
||||
|
||||
@@ -51,7 +51,7 @@ def account_env(acc: dict[str, Any], db_path: str) -> Dict[str, str]:
|
||||
env = dict(os.environ)
|
||||
env.update({
|
||||
"SANGUO_LIVE_STRATEGY": name,
|
||||
"SANGUO_LIVE_MAX_POOL": _num(params.get("max_pool", 30)),
|
||||
"SANGUO_LIVE_MAX_POOL": _num(params.get("max_pool", 0)),
|
||||
"SANGUO_LIVE_BENCHMARK": str(params.get("benchmark", "000300.XSHG")),
|
||||
"SANGUO_LIVE_CASH": _num(acc.get("initial_capital") or 1_000_000),
|
||||
"SANGUO_SHADOW_DB": db_path,
|
||||
|
||||
@@ -118,6 +118,13 @@ def test_portfolio_env_mapping():
|
||||
assert env["SANGUO_LIVE_CASH"] == "2000000"
|
||||
assert env["SANGUO_LIVE_DB"] == "live.db"
|
||||
assert env["SANGUO_LIVE_ACCOUNT_ID"] == "7"
|
||||
# 行缺 max_pool / 存 0 → env "0"(0=不限);显式 30 等存量值不篡改
|
||||
no_pool = {k: v for k, v in acc.items() if k != "max_pool"}
|
||||
assert live_runner._portfolio_env_for(no_pool, "live.db")["SANGUO_LIVE_MAX_POOL"] == "0"
|
||||
zero_pool = dict(acc, max_pool=0)
|
||||
assert live_runner._portfolio_env_for(zero_pool, "live.db")["SANGUO_LIVE_MAX_POOL"] == "0"
|
||||
kept = dict(acc, max_pool=30)
|
||||
assert live_runner._portfolio_env_for(kept, "live.db")["SANGUO_LIVE_MAX_POOL"] == "30"
|
||||
|
||||
|
||||
def test_runner_live_env_defaults(monkeypatch):
|
||||
@@ -129,7 +136,7 @@ def test_runner_live_env_defaults(monkeypatch):
|
||||
monkeypatch.delenv(k, raising=False)
|
||||
cfg = runner_live.live_env()
|
||||
assert cfg["strategy"] == "all_weather"
|
||||
assert cfg["max_pool"] == "30"
|
||||
assert cfg["max_pool"] == "0" # 0=不限(2026-08-24 巡检:MVP 默认 30 泄漏生产致选股池失真)
|
||||
assert cfg["benchmark"] == "000300.XSHG"
|
||||
assert cfg["account"] == "" # 空 → run_live 拒绝启动(防误下单)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user