工作交接 2026-09-20:换开发机前的现状、四件要紧事、155 实况与开工步骤;补记 155 源码挂载的例外

交接文档 docs/交接_2026-09-20.md。成文前只读核对 155 与 188,核出四件此前没记录的事:
155 上源码经服务器本地的 docker-compose.override.yml 挂载进容器(make stale 在 155 恒为一致);
工作包乙 f5cc1b8 已随 09-18 的部署提前生效,09-22 早上上游切质地轴时会自动切序,待用户拍板是否先关开关;
系统自决首周自动采纳为零,四只候选都在 09:31 前后因研判不可用交人;
单测的假仓储没替换读数表仓储,155 上每跑一次 make test 往 pms_consensus_stat 写一行假数据。
另把不部署看页面的假后端整理进 docs/dev_preview/(不在代码指纹范围内)。
CLAUDE.md 与 DEVLOG 开头关于「源码打进镜像」的说法补了 155 的例外。只动文档,代码指纹不变。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
zlt 2026-09-20 09:12:52 +08:00
parent cb73f74717
commit 9171bd8bd8
5 changed files with 711 additions and 0 deletions

View File

@ -49,6 +49,8 @@ docker compose run --rm --no-deps pms-web python scripts/test_batch3_units.py
**改了 Python 代码必须重建镜像加 force-recreate绝不能用 `docker compose restart`。** 源码是打进镜像的,`restart` 只是把老容器停了再起,跑的还是旧镜像里的旧代码,而且一点报错都没有。`make deploy` 和 `make up` 走的都是正确路径。这是本项目头号坑,`Makefile` 顶部、`scripts/deploy.sh`、`run_tests.py` 里都反复写了这一条。
**模拟仓 155 是个例外要另记2026-09-20 实测)。** 155 的项目目录里有一个服务器本地文件 `docker-compose.override.yml`,内容同仓库里的 `docker-compose.dev.yml`,它把整个仓库目录绑定挂载到四个容器的 `/app`。这个文件被 `.gitignore` 忽略,仓库里看不到;真实仓 188 没有它。后果有三条。一,在 155 上 `git pull` 之后,页面静态文件立刻就是新的,后端四个进程仍是旧代码,直到重启或 `make deploy`。二,`make test` 与 `make stale` 起的一次性容器读的也是工作树,所以在 155 上「指纹一致」恒成立,查不出「拉了代码没重启」。三,往 155 拉代码等于把页面先上线了,拉之前先看 `git log` 里有没有别的会话提交的、还没到上线日的改动。`make deploy` 在两台机器上都仍是对的做法。
**下发模式由参数 `PMS_DISPATCH_MODE` 决定,实际以页面顶部横幅为准,不要拿这段文字当现状。** 设置文件的初值是 `shadow`(影子模式:指令照常生成、过规则闸、记账、置为已下发,但不写下游,需人工在 QMT 侧执行,成交由回放任务按先进先出认领回账本)。但模拟仓 155 运行时已切到 `ws`,指令经 `pms-ws` 真向模拟 QMT 发出、自主动作按档位可自动执行,不再需要人工下单。真实仓 188 的 PMS 尚未追平到最新代码,上线等 QMT 侧,追平部署时按真实仓准备包的初始参数清单先进影子期。切换只是 `PMS_DISPATCH_MODE``PMS_QMT_WS_ENABLED` 两个开关加一次联调,不是再写代码。改动涉及下单链路时,务必先确认自己面对的是哪一套实例、此刻是哪个模式。
**业务参数一律走 ParamStore 读,不要直接读 settings。** `config/settings.py` 里的业务参数(`PMS_TOTAL_SCALE`、各种档位阈值等)只是「初值」。运行时以数据库表 `pms_runtime_param` 的值为准,页面改参数即时生效。取业务参数统一经 `app/services/param_store.py`,直接读 settings 会让页面调参失效。基础设施类配置(连接串、密钥)才从 settings 与 `.env` 读。

View File

@ -8,6 +8,9 @@
> - **tradingSystemPMS** 跑在 `factor@factorevaluation`,源码打进镜像,
> 改码必须 `make deploy`,跑完 `make test` 见 ALL SUITES PASS盯盘 `make watch`
> 容器时钟是北京时间。
> **2026-09-20 查实的例外**155 上有服务器本地文件 `docker-compose.override.yml`,把仓库目录绑定挂载到四个容器的 `/app`
> 所以在 155 上 `git pull` 后页面静态文件立刻是新的、后端进程仍旧,`make stale` 在 155 上恒为一致、查不出「拉了没重启」。
> 188 没有这个文件,代码确实在镜像里。`make deploy` 在两台机器上都是对的做法。详见 `docs/交接_2026-09-20.md` 第七节。
> - **bionic_trader决策系统** 跑在 `192.168.16.188:38000`,挂载卷,
> `git pull``docker compose restart backend-api worker-brain` 即生效;
> **新增 env 必须 `docker compose up -d --force-recreate`**restart 不重读 env。
@ -1715,6 +1718,25 @@ app/services/company_report.py、app/web/main.py新接口、app/w
---
## 2026-09-20 · 换开发机前的工作交接与 155 实况核对(只写文档,未动代码)
**做了什么**
用户要换开发机,先把本项目的交接汇总成文并入库:`docs/交接_2026-09-20.md`。成文前只读核对了 155 与 188 的实况核出四件此前没有记录的事。一155 上源码是挂载进容器的:项目目录里有服务器本地文件 docker-compose.override.yml内容同仓库的 docker-compose.dev.yml被 .gitignore 忽略),把仓库目录绑定挂载到四个容器的 /app188 没有这个文件。后果是在 155 上 git pull 之后页面静态文件立刻是新的、后端进程仍旧make test 与 make stale 的一次性容器读的也是工作树,所以 155 上「指纹一致」恒成立、查不出「拉了没重启」。二09-17 的工作包乙候选按上游名次排f5cc1b8台账 016另一个会话做的当时没写节点已随 09-18 11:52 那次 make deploy 提前生效,开关 PMS_PLAN_RANK_BY_UPSTREAM 读回 True它靠上游计划的 rank_axis 自门控,选股系统机器仍在 3d3f317到今天行为未变09-22 早上上游切质地轴时会自动切到上游序,比方案定的 09-23 收盘后早一天且与上游切轴同一个早上要用户拍板是否先关开关。三系统自决上线一周自动采纳为零四只弱表态候选09-15 的 300308、09-17 的 688778、09-18 的 002050 与 300073全部在 09:31 到 09:33 因「研判不可用,降级人工确认」交人,三只被人工采纳、一只过期。四,读数表 pms_consensus_stat 混进单测写的假行test_wiring 的 install_fakes 没有替换 consensus_stat_repo用例跑整轮扫描时读数落表走真仓储开发机无库被软失败吞掉155 上每跑一次 make test 写一行「600000.SH auto_accept 目标价到价自动清仓」,四行的写入时刻与四次 make test 逐一对上它撑大了复核第六张表的分母研判不可用占比被低估09-15 显示 50% 实为 100%09-18 显示 66.7% 实为 100%)。另外把历次用来不部署看页面的假后端整理进 docs/dev_preview/不在代码指纹范围内CLAUDE.md 与本文件开头关于「源码打进镜像」的说法按实测补了 155 的例外。
**动了哪些文件**
docs/交接_2026-09-20.md、docs/dev_preview/mock_preview.py 与 sample_review.md、CLAUDE.md头号坑一段补 155 挂载的例外、DEVLOG.md开头部署方式补一条、本节点。没有动 app/、scripts/、config/,代码指纹仍是 26510ade120c。
**部署方式**
无。文档提交155 拉代码即可,不需要重启。
**真机判收**
真机见过155 与 1882026-09-20 09:03 到 09:15 北京时间全部只读。155仓库 cb73f74、指纹一致 26510ade120c、四容器已运行 45 小时、持仓两只300627.SZ、688778.SH各挂一条运行中的网格、待确认提议 0docker inspect 见 /home/factor/project/tradingSystem 绑定挂载到 /app参数读回 PMS_SELF_DECIDE full、PMS_SELF_DECIDE_TRIAL True、PMS_TECH_INTRADAY_CONFIRM False、PMS_PLAN_RANK_BY_UPSTREAM True、PMS_PLAN_TIERS 强传导、PMS_PLAN_TOP_N 50最近三份名册快照的 rank_mode 为空旧代码写的make consensus-review DAYS=6 六张表读数已摘进交接文档。188仓库 51bd6dc落后主干 112 个提交)、只跑 pms-web 与 pms-ws、只挂载了 logs。本机docs/dev_preview/mock_preview.py 起服务后页面 200、评析样例与假持仓可读。
**还欠着什么**
全部写在 docs/交接_2026-09-20.md 第二节与第五节。最要紧四件:旧开发机上选股系统 9acd3fa 与决策系统六个副本提交未推送09-21 收盘后选股系统机器要拉代码PMS_PLAN_RANK_BY_UPSTREAM 要不要在 09-22 07:10 前先关;研判在 09:31 前后不可用的根因待查;单测污染读数表待修、四行假数据待清(删库行先经用户同意)。
---
<!--
下一条节点从这里往下写,格式照抄上面:
## YYYY-MM-DD · 一句话标题

View File

@ -0,0 +1,272 @@
# -*- coding: utf-8 -*-
"""页面的本机预览服务 (只给开发机看样式用, 不进容器、不连库、不进代码指纹)。
用途: app/web/static/index.html 之后, 不部署就能在浏览器里看效果它只做几件事:
静态托管页面; 假登录 (交易员加系统管理员两个角色); 给顶栏持仓表建仓候选栏个股详情抽屉
评析报告抽屉运维视图组合操作页喂固定的假数据其余 /api/* 一律回预览无后端,
页面照常挂载, 顶部会有一条错误提示, 不影响看样式
怎么跑 (开发机, 需要一个装了 fastapi uvicorn Python, 用项目 requirements.txt 建的虚拟环境即可):
~/venvs/pms/bin/python docs/dev_preview/mock_preview.py
浏览器开 http://127.0.0.1:38199/ 看主页面
浏览器开 http://127.0.0.1:38199/#detail=300627.SZ 直达个股详情抽屉
两个坑:
, 仓库若放在 macOS 文稿目录下, 由别的程序 (例如 Claude 桌面版的预览面板) 拉起的进程可能没有
读这个目录的权限, 表现为页面 404 或读不到文件办法是把 app/web/static 整个拷到一个临时目录,
再用环境变量 PMS_PREVIEW_STATIC 指过去:
cp -R app/web/static /tmp/pms_static && PMS_PREVIEW_STATIC=/tmp/pms_static python docs/dev_preview/mock_preview.py
改了页面要重新拷一次
, 假数据的键要跟着接口走交易员视图的持仓表只显示 total_qty 大于零的行; 顶栏读 /health/api/overview
/api/tech/status/api/macro/status/api/dispatch-mode; 运维视图组合操作页读 /api/params
页面新读了哪个接口, 就在这里补一个同形状的假应答
本文件放在 docs/ 下是有意的: 代码指纹只覆盖 app/scripts/config/ 三个目录的 .py, 放这里不会让
服务器上的 make stale 镜像与工作树不一致
"""
import os
from fastapi import FastAPI
from fastapi.responses import FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.environ.get("PMS_PREVIEW_STATIC") or os.path.abspath(os.path.join(HERE, "..", "..", "app", "web", "static"))
SAMPLE_REVIEW = os.path.join(HERE, "sample_review.md")
app = FastAPI()
app.mount("/static", StaticFiles(directory=ROOT), name="static")
@app.get("/")
def index():
return FileResponse(os.path.join(ROOT, "index.html"))
@app.get("/api/me")
def me():
return {"ok": True, "user": {"phone": "", "username": "预览"}, "roles": ["交易员", "系统管理员"]}
RESEARCH = {
"ts_code": "300750.SZ",
"company": {"overall": "", "valuation": "便宜", "period": "2026Q2",
"line": "质地好(回报好、盈余中、成长好),估值便宜,置信中",
"invalidation": "下一期财报看M24净利率、M26经营现金流对净利润、M63收入同比、M64净利润同比",
"doubt_hard": False, "report_url": "https://example.invalid/report/300750", "text": ""},
"fund": {"stance": "看多", "fact": "质地好", "mark": None, "priced_in": False, "valuation": "便宜",
"overall": "", "no_read_why": None},
"tech": {"stance": "看空", "strength": "", "phase": "趋势空", "confirm": None, "sar_side": "",
"sar_value": 344.446, "sar_flip_days": 18, "choppy": False,
"reason": "SAR 空且多空布林线未在多头区:下行", "data_date": 20260911,
"latest": {"data_date": 20260911,
"boll": {"upper": 352.1, "mid": 338.0, "lower": 323.9, "bw_pct": 8.3, "pos": 0.12,
"state": "贴下轨", "squeeze": False},
"bbi": {"bbi": 341.2, "pos": -0.6, "state": "空头区", "dist_pct": -1.2},
"sar": {"value": 344.446, "side": "", "flip_days": 18, "dist_pct": 2.1},
"quality": "OK", "reanchored": True},
"flips": 1, "bars": 25},
"timing": {"stance": "中性", "nightly": "WATCH", "trade_date": "20260911", "flip_at": None},
"consensus": {"direction": "中性", "votes": {"fund": "看多", "tech": "看空", "timing": "中性"},
"strength": None, "reason": "基本面看多、技术面看空、择时中性 → 中性",
"route": "观察", "route_reason": "等技术面转向"},
"sources": {"company": "ok", "fund": "ok", "tech": "ok", "timing": "ok", "consensus": "ok"},
}
@app.get("/api/research/{code}")
def research(code: str):
return {"ok": True, "data": {**RESEARCH, "ts_code": code}}
@app.get("/api/upstream/signals/{code}")
def signals(code: str):
return {"ok": True, "data": {
"ok": True, "ts_code": code,
"timeline": [
{"time": "10:53", "src": "alert", "src_label": "盘中告警", "cat": "money_flow",
"cat_label": "资金流强度", "direction": "看涨", "level": "INFO",
"reason": "money_flow_in_intensity", "stale": True, "dup": 1, "ts_from": "trigger"},
],
"snapshot": {"metrics": [{"direction": "看涨", "window_net": 432000000.0, "z_dd": 6.143}], "mr": {}},
"sources": {"intraday": {"ok": True}, "alerts": {"ok": True}, "sell_actions": {"ok": True},
"metrics": {"ok": True}, "mr": {"ok": True}},
"truncated": {},
}}
@app.get("/api/ledger")
def ledger(ts_code: str = "", limit: int = 40):
import datetime
today = datetime.date.today().isoformat()
return {"ok": True, "data": [
{"created_at": f"{today} 09:41:12", "action": "OPEN", "verdict": "REJECT", "arbiter": "system",
"reason": "系统放弃:研究证据走弱(逻辑存疑),自动放弃;明日重新评估"},
]}
@app.get("/api/proposals")
def proposals(status: str = "WAIT_USER", limit: int = 100):
return {"ok": True, "data": []}
# ---- 持仓表 (综合分析列三枚标签), 照 155 上 10:53 读回的三票造几行 ----
def _pos(code, name, qty, cost, price, dims, cushion="POSITIVE", cpct=0.052, frozen="NONE", target=None):
mv = qty * price
return {
"ts_code": code, "name": name, "qty": qty, "total_qty": qty, "sellable_qty": qty, "avg_cost": cost, "price": price,
"status": "HOLDING", "frozen_reason": frozen, "user_target_price": target,
"price_ok": True, "market_value": mv, "pct_of_scale": mv / 2000000.0,
"cushion_state": cushion, "cushion_pct": cpct, "neg_cushion_days": 0,
"user_stop_price": None,
"entry": {"why": "光模块需求上行"}, "logic_now": {"state": "INTACT", "text": "证据仍在"},
"tech": {"stance": dims["tech"] or "无读数", "phase": dims["tech_phase"]},
"dims": dims, "lots": 1,
}
POSITIONS = [
_pos("300627.SZ", "华测导航", 1200, 41.2, 43.5,
{"fund": "看多", "fund_fact": "质地好", "tech": "看空", "tech_phase": "转空", "timing": "中性", "nightly": "WATCH"}),
_pos("688281.SH", "华秦科技", 300, 158.0, 151.2,
{"fund": "中性", "fund_fact": "质地中", "tech": "看空", "tech_phase": "转空", "timing": "中性", "nightly": "WATCH"},
cushion="NEGATIVE", cpct=-0.043),
_pos("002463.SZ", "沪电股份", 200, 115.25, 118.9,
{"fund": None, "fund_fact": "没有买方评析", "tech": "中性", "tech_phase": "收口等待", "timing": "中性", "nightly": "WATCH"}),
_pos("002179.SZ", "中航光电", 500, 52.3, 55.1,
{"fund": None, "fund_fact": "没有买方评析", "tech": None, "tech_phase": None, "timing": "看多", "nightly": "BUY"},
frozen="MANUAL", target=60.0),
_pos("002709.SZ", "天赐材料", 800, 24.1, 22.7,
{"fund": None, "fund_fact": "没有买方评析", "tech": "看空", "tech_phase": "趋势空", "timing": None, "nightly": "DROPPED"},
cushion="NEGATIVE", cpct=-0.058),
_pos("600279.SH", "重庆港", 2000, 6.1, 6.2,
{"fund": None, "fund_fact": "没有买方评析", "tech": None, "tech_phase": None, "timing": None, "nightly": None}),
]
@app.get("/api/strategies")
def strategies(include_archived: bool = False):
return {"ok": True, "strategies": [
{"strategy_id": "STR_20260908_300627SZ_1", "ts_code": "300627.SZ", "type": "GRID", "status": "ACTIVE",
"autonomy": "auto", "params": {"lower": 38.0, "upper": 46.0, "center": 42.0},
"state": {"filled_levels": {"1": 200, "2": 200}, "invested": 16800.0}, "archived_at": None,
"buy_paused": None, "created_at": "2026-09-08 09:40:00"},
{"strategy_id": "STR_20260910_002463SZ_1", "ts_code": "002463.SZ", "type": "TRAIL", "status": "PAUSED",
"autonomy": "auto", "params": {"trail_pct": 0.05}, "state": {"high_water": 121.4, "armed": False},
"archived_at": None, "buy_paused": None, "created_at": "2026-09-10 09:40:00"},
], "enabled": True, "auto": {"enabled": True, "ran_today": True, "last_scan": {"at": "2026-09-15 09:40:12"}}}
@app.get("/api/positions")
def positions():
return {"ok": True, "positions": POSITIONS, "stock_params": {},
"summary": {"total_scale": 2000000.0, "market_value": sum(p["market_value"] for p in POSITIONS)}}
# ---- 建仓候选栏: 计划行 + 只读处置快照 ----
PLAN_ROWS = [
{"rank": 1, "ts_code": "300308.SZ", "name": "中际旭创", "theme": "光模块与算力互联", "tier": "白名单"},
{"rank": 2, "ts_code": "300750.SZ", "name": "宁德时代", "theme": "动力电池", "tier": "白名单"},
{"rank": 3, "ts_code": "688981.SH", "name": "中芯国际", "theme": "晶圆代工与先进制程国产替代", "tier": "白名单"},
{"rank": 4, "ts_code": "002050.SZ", "name": "三花智控", "theme": "热管理与机器人执行器", "tier": "候补"},
{"rank": 5, "ts_code": "300660.SZ", "name": "江苏雷利", "theme": "微特电机", "tier": "候补"},
{"rank": 6, "ts_code": "603986.SH", "name": "兆易创新", "theme": "存储芯片", "tier": "候补"},
]
@app.get("/api/upstream/plan")
def plan(limit: int = 50):
return {"ok": True, "status": {"ok": True, "date": "2026-09-15", "plan_source": "snapshot", "gate_on": True, "age_tdays": 1, "fetched_at": __import__("time").time() - 3000, "heat_date": "2026-09-15"},
"rows": PLAN_ROWS, "candidates_raw": None}
@app.get("/api/upstream/plan-changes")
def plan_changes(limit: int = 50):
return {"ok": True, "changes": {"ok": True, "added": [], "removed": []}, "log": {}}
def _con(fund, overall, tech, phase, timing, direction, route):
return {"fund": fund, "overall": overall, "tech": tech, "phase": phase, "timing": timing,
"direction": direction, "route": route}
@app.get("/api/open-scan")
def open_scan():
return {"ok": True, "notes": ["名额 3/8", "可用资金 61.2 万"], "by_code": {
"300308.SZ": {"disp": "would", "why": "名额与资金充足,本轮将建底仓",
"consensus": _con("看多", "", "看多", "趋势多", "看多", "看多", "放行")},
"300750.SZ": {"disp": "wait_tech", "why": "布林带仍在收口,等开口向上再评估",
"consensus": _con("看多", "", "中性", "收口等待", "中性", "看多", "交人")},
"688981.SH": {"disp": "wait_timing", "why": "三票看多但择时看空,等择时转多",
"consensus": _con("看多", "", "看多", "趋势多", "看空", "看多", "观察")},
"002050.SZ": {"disp": "wait_confirm", "why": "技术面已开口,等盘中站上均价且量比达标",
"consensus": _con("中性", "", "看多", "开口向上", "中性", "看多", "放行")},
"300660.SZ": {"disp": "sys_decline", "why": "系统放弃:研究证据走弱(逻辑存疑),明日重新评估",
"consensus": _con("看空", "", "中性", "收口等待", None, "中性", "跳过")},
}}
# ---- 2026-09-18: 顶栏标签、运维视图组合操作页、评析报告抽屉要用的几路 ----
@app.get("/health")
def health():
import datetime
return {"ok": True, "version": "preview", "now": datetime.datetime.now().strftime("%H:%M:%S"), "trade_day": True}
@app.get("/api/overview")
def overview():
mv = sum(p["market_value"] for p in POSITIONS)
return {"ok": True, "data": {"buy_halt": False, "exec_halt": False, "autonomy": "full", "scale": 2000000.0,
"portfolio_mv": mv, "portfolio_pct": mv / 2000000.0, "portfolio_cap": 0.8, "cap_room": 2000000.0 * 0.8 - mv,
"cash_source": "ws", "cash_avail": 612000.0, "cash_est": 600000.0, "names_count": len(POSITIONS), "max_names": 8,
"float_pnl": 12345.0, "solid_names": 2, "neg_names": 2, "sector_ready": True}}
@app.get("/api/params")
def params():
def p(k, v, t="float", d=""):
return {"key": k, "value": v, "type": t, "file_default": v, "source": "table", "desc": d, "updated_at": "2026-09-17 15:20:00"}
return {"ok": True, "data": {"params": [
p("PMS_TOTAL_SCALE", 2000000, "int", "总操作规模"), p("PMS_PORTFOLIO_CAP", 0.8, "float", "总仓上限"),
p("PMS_STOCK_CAP", 0.15, "float", "单股上限"), p("PMS_MAX_NAMES", 8, "int", "最多持仓"),
p("PMS_CASH_RESERVE", 0.05, "float", "预留现金"), p("PMS_DCA_MAX_RATIO", 0.5, "float", "最多补到首批的"),
p("PMS_DCA_TRIGGERS", "-0.08,-0.15", "str", "亏到多少开始评估"), p("PMS_DCA_DEEP_CONFIRM", True, "bool", "更深那档要确认"),
p("PMS_FILL_MAX_LOSS", -0.05, "float", "回落补足的止损"), p("PMS_AUTONOMY", "full", "str", "自主档位"),
]}}
@app.get("/api/tech/status")
def tech_status():
return {"ok": True, "data": {"enabled": True, "data_date": 20260917, "map": {"fresh": True}}}
@app.get("/api/macro/status")
def macro_status():
return {"ok": True, "enabled": True, "autonomy": "propose_only", "params": {"hot_th": 1.5, "cold_th": -1.5, "exit_band": 0.3},
"signals": [{"key": "MZ_20260918", "label": "大盘冷热", "value": 0.4, "zone": "NEUTRAL", "ymd": "2026-09-18",
"e_now": 0, "streak": 3, "history": [], "advice": None}]}
@app.get("/api/dispatch-mode")
def dispatch_mode():
return {"ok": True, "data": {"mode": "ws", "shadow": False}}
@app.get("/api/research/{code}/report")
def research_report(code: str):
md = open(SAMPLE_REVIEW, encoding="utf-8").read()
if code == "600279.SH":
return {"ok": False, "ts_code": code, "url": None, "source": "none", "markdown": "", "cached": False,
"fetched_at": None, "error": "这只票没有个股深度评析报告的链接(不在持仓也不在当日选股计划里)"}
return {"ok": True, "ts_code": code, "url": "http://192.168.16.178:8000/company/%s/review" % code, "source": "held",
"markdown": md, "cached": False, "fetched_at": "2026-09-18 11:50:00", "error": None}
@app.api_route("/api/{path:path}", methods=["GET", "POST"])
def catchall(path: str):
return JSONResponse({"ok": False, "error": "预览无后端"})
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=38199)

View File

@ -0,0 +1,78 @@
# 华测导航300627.SZ个股深度评析
> 生成 2026-09-18 00:05数据截至 2026-09-18价格日 2026-09-17视角版本 2026.09-2。本报告只给人看不是系统判决带价格的判据只输出通过与否不产出目标价。
**公司质地:好(回报好、盈余好、成长好);估值:便宜**(代码合成,接入方案第三节;估值只展示)
- 回报类好buffett、terry_smith、penman、dupont 评为好且无差
- 盈余质量类好盈余质量三分为好、F 分数 7
- 成长类好:收入五年复合 21.3%、现金转化 0.90
- 估值便宜:市盈率十年分位 3%、隐含市盈率 26.4 对一致预期中位 28.5
## 一、头部:数据与材料
## 一、头部:数据与材料
- 财务指标表 33 期(('2018Q1', '2026Q1')),重构报表 41 期(('2015Q4', '2026Q2')),其中重构校验失败被剔除 0 期。
- 主营构成年度 11 年,分红记录 9 年,每日基础指标 2308 天,近 90 天研报 21 篇。
- 材料:第一批只用财务表与研报明细,年报文本与研报论断在第二批接入。
- 口径:收入取主营构成表的表头行(营业收入合计),没有表头行时按产品各项相加;经营现金流与企业自由现金流取财务指标表口径;重构报表只用金额结构与回报率,它的现金流列与原始金额对不上,标为供应商口径仅参考;比率类「最新期」是年初至今,回报率类判据用最新年度。
- 数据说明:供应商质量表红旗:利润质量低(CFO/NOPAT<0.5)
- 数据说明fin_quality 的现金转化 0.36 与自算 0.95 不符,派生表只作参考
- 数据说明:最新期 2026Q1 不是年报期:比率类指标是年初至今口径,现金流类与回报率类判据一律用最新年度
## 二、十年画像
| 年份 | 收入 | 经营利润后税 | 经营现金流 | 企业自由现金流 | 净资产收益率 | 毛利率 | 净利率 | 资产负债率 | 每股收益 | 每股分红 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 2015 | 3.62 亿元 | 缺 | 缺 | 缺 | 缺 | 缺 | 缺 | 缺 | 缺 | 缺 |
| 2016 | 4.82 亿元 | 缺 | 缺 | 缺 | 缺 | 缺 | 缺 | 缺 | 缺 | 缺 |
| 2017 | 6.78 亿元 | 缺 | 缺 | 缺 | 缺 | 缺 | 缺 | 缺 | 缺 | 0.27 元 |
| 2018 | 9.52 亿元 | 1.12 亿元 | -2608.6 万元 | -5658.8 万元 | 12.8% | 54.3% | 11.3% | 36.7% | 0.44 元 | 0.20 元 |
| 2019 | 11.46 亿元 | 1.56 亿元 | 1.64 亿元 | -259.5 万元 | 14.9% | 56.0% | 12.4% | 36.3% | 0.58 元 | 0.30 元 |
| 2020 | 14.10 亿元 | 1.96 亿元 | 2.31 亿元 | 2.86 亿元 | 19.0% | 54.3% | 14.1% | 43.3% | 0.58 元 | 0.25 元 |
| 2021 | 19.03 亿元 | 2.97 亿元 | 2.71 亿元 | -3.16 亿元 | 18.0% | 54.6% | 15.2% | 34.2% | 0.82 元 | 0.30 元 |
| 2022 | 22.36 亿元 | 3.59 亿元 | 3.54 亿元 | 3744.4 万元 | 15.3% | 56.6% | 16.1% | 35.8% | 0.68 元 | 0.27 元 |
## 四、市场预期一侧
| 指标 | 读数 | 期 | 公式 |
| --- | --- | --- | --- |
| 现价 | 26.35 元 | 2026-09-17 | |
| 总市值 | 209.21 亿元 | 2026-09-17 | 万元×1e4 |
| 总股本 | 7.94 亿股 | 2026-09-17 | 万股×1e4 |
| 市盈率(滚动) | 30.23 倍 | 2026-09-17 | |
| 市净率 | 4.79 倍 | 2026-09-17 | |
| 股息率(滚动) | 1.7% | 2026-09-17 | |
## 五、买方论点合议(云端评议 deepseek-v4-pro · 评议版本 2026.09-1 · 输出 10170 token · 数字回查 51/52 命中)
### 5.1 逐视角复核
| 视角 | 失真判据 | 方向 | 为什么 | 还剩什么有效信息 |
| --- | --- | --- | --- | --- |
| xue_siwei | C3 | 不适用 | 最新期2026Q1年初至今仅一季度毛利率60.4%不可直接与年度判据比较 | 收入复合增速、周转、财务杠杆、现金转化与分红等年度指标仍有效 |
| xue_siwei | C6 | 不适用 | 由C3毛利率减C7净利率得出44.6%,受一季度期间费用确认不均影响 | 收入复合增速、周转、财务杠杆、现金转化与分红等年度指标仍有效 |
| xue_siwei | C7 | 不适用 | 净利率15.8%为年初至今一季度口径,与年度判据不可比 | 收入复合增速、周转、财务杠杆、现金转化与分红等年度指标仍有效 |
| xue_siwei | C5 | 偏松 | 毛利率十年标准差实际仅近8年数据样本不足十年标准差可能偏小 | 收入复合增速、周转、财务杠杆、现金转化与分红等年度指标仍有效 |
### 5.2 综合三句
- 回报来源回报主要来自均衡的净利率18.5%与周转0.75倍M37-M38/C24-C27收入五年复合21.3%C1
- 利润含金量利润含金量中等经营现金流对净利润0.90倍、应计0.0%M31/M36/C37-C38但企业自由现金流对净利润仅0.35倍M35/C21
- 财务安全:财务安全强:净金融负债-13.56亿元M11/C20/C54、流动比率3.25倍M27/C13/C53、资产负债率27.3%M25/C12
### 5.3 买方论点七字段
| 字段 | 内容 |
| --- | --- |
| 论点 | 公司质地好且估值分位极低一致预期可能低估其盈利稳定性2026Q4隐含市盈率26.35倍低于一致预期中位28.50倍。 |
| 证据 | M49、M50、M52、M81、M86、M89 |
| 催化剂与时间窗 | 无法判断 |
| 预期 | 现价隐含市盈率26.35倍M81低于一致预期市盈率中位28.50倍M89EPS中位1.00元M86中性上行8.2%M82。 |
| 失效条件 | 下一期财报若M63收入同比<0或M64净利润同比进一步下降M31<0.8M36>10%,则失效。 |
| 置信度 | 中多数视角好且估值分位低但2026Q1净利润同比-12.2%、企业自由现金流对净利润0.35倍,存在确认风险 |
> 本文件是给本机预览用的节选样例,不是完整报告。

337
docs/交接_2026-09-20.md Normal file
View File

@ -0,0 +1,337 @@
# 工作交接 · 2026-09-20持仓管理系统换开发机之前
写给下一个接手的会话,也写给换了新开发机的用户本人。读完这一份就能接着干,不需要回头翻聊天记录。
阅读顺序建议:先读仓库根目录的 `CLAUDE.md`,再读这一份,然后读 `DEVLOG.md` 的开头规则与最后三条节点。本文第二节是必须先处理的事,第三节是 2026-09-20 上午从服务器只读读回的实况,其余各节是背景与办法。附录标了「可不读」。
本文里反复出现三个名字先说清。「155」是模拟仓服务器 192.168.16.155持仓管理系统与选股系统都跑在上面。「188」是真实仓服务器 192.168.16.188,连着真实账户,决策系统也在上面。「选股系统」指仓库 akg-factor-bridge它每个交易日早上出一份选股计划持仓管理系统 08:40 拉取。
---
## 一、一句话现状
持仓管理系统在 155 上运行正常,代码到提交 f6e94cf2026-09-18 午间部署),系统自决全开,账户里两只持仓、两条网格方案、没有待确认提议。真实仓 188 仍停在 08-28 的旧版本,等 QMT 一侧,没有人动过。
从 09-21 起的主线是「选股计划排序轴换成公司质地」的上线协同。持仓管理系统这一侧的代码已经写完并复审通过,而且**已经提前生效**,这件事需要用户在 09-22 早上之前拍一次板,见第二节第 2 条。
---
## 二、最要紧的四件事
### 1. 旧开发机上有没推送的提交,换机器前必须推
2026-09-18 盘点时,旧开发机上这几处只存在于本机。
| 仓库 | 没推送或没提交的东西 | 为什么要紧 |
|---|---|---|
| 选股系统 akg-factor-bridge | 提交 9acd3fa「pms_roster 档位白名单修补」没推送 | 这是 09-17 复审退回的那一处修补。日程要求 09-21 收盘后选股系统机器拉代码,它必须先推送并过复审 |
| 决策系统 bionic_trader | 6 个「方案副本同步」提交没推送 | 四个仓库的方案文档要逐字同步,缺这几份副本就不同步了 |
| astock-kg | `CLAUDE.md` 有未提交修改 | 提交或放弃,二选一 |
| quant_factor_service | 「第二阶段收工记录_20260831.md」没提交 | 提交 |
在旧开发机上运行,推送前者:
```bash
cd /Users/baobao/Documents/work/project/akg-factor-bridge && git push origin main
```
在旧开发机上运行,推送后者:
```bash
cd /Users/baobao/Documents/work/project/bionic_trader && git push origin main
```
持仓管理系统仓库本身是干净的,全部已推送。
### 2. 工作包乙已经提前生效09-22 早上之前要拍板
**背景。** 《上线与协同阶段工作方案与规格书_2026-09-17》的工作包乙让持仓管理系统在上游计划带 `rank_axis` 为 quality 时按上游名次排候选,不再按自己的立场分桶重排。提交是 f5cc1b8台账第 016 条,复审通过。方案定的上线日是 **09-23 收盘后**,前提是 09-22 的六处读数干净,而且特意不与选股系统 09-21 拉代码放在同一天,理由是「两处同时变化说不清」。
**实际发生了什么。** 2026-09-18 11:52为了上线页面改动在 155 执行了一次 `make deploy`。f5cc1b8 是 09-17 提交的,就在这次部署里。四个容器从那一刻起跑的就是含工作包乙的代码。开关 `PMS_PLAN_RANK_BY_UPSTREAM` 读回为 True这是代码默认值。
**到今天为止有没有影响。** 没有。这项功能靠上游计划顶层的 `rank_axis` 自门控。选股系统机器现在停在 3d3f317出的计划不带这个键持仓管理系统走的还是旧的排法。
**什么时候会有影响。** 09-21 收盘后选股系统拉代码并重启。09-22 早上 07:10 出第一份质地序计划。持仓管理系统 08:40 拉取时会自动切到上游序。这比方案早一天,并且与上游切轴落在同一个早上。
**两个选择。**
- 选择甲,接受提前生效。代码已复审通过,旧计划下逐字回旧有单测钉住。代价是 09-22 两处同时变,读数说不清时要靠名册快照里的 `rank_mode` 区分。
- 选择乙恢复方案原意。09-22 07:10 之前在参数页把 `PMS_PLAN_RANK_BY_UPSTREAM` 关掉。09-23 收盘后再打开,同一天按拍板点六把 `PMS_PLAN_TIERS` 改成「强传导,弱传导」。不改代码,不重启。
**建议选乙。** 它只是一次页面改参数,却能让 09-22 的六处读数只反映上游一处变化。
选择乙的做法:登录 155 页面,切到运维视图,在「参数设置」里改。或者在 155 上运行下面这条,预期最后打印 False。
```bash
cd /home/factor/project/tradingSystem && docker exec pms-web python -c "from app.services import param_store as ps; ps.set_param('PMS_PLAN_RANK_BY_UPSTREAM', False, 'handoff'); print(ps.get('PMS_PLAN_RANK_BY_UPSTREAM', None))"
```
**怎么核对它到底走的哪种序。** 看名册快照元数据里的 `rank_mode`。09-18 及更早的快照里它是空的因为那时还是旧代码。09-21 08:40 那份应当是 quality_bucket。开关开着且上游带 quality 时是 upstream。在 155 上运行:
```bash
cd /home/factor/project/tradingSystem && docker exec pms-web python -c "
from app.services import plan_feed as pf
import json
for r in (pf.snapshot_log(limit=3).get('rows') or []):
m = r.get('meta') or {}
m = json.loads(m) if isinstance(m, str) else m
print(r.get('plan_date'), r.get('fetched_at'), 'rank_mode', m.get('rank_mode'))
" 2>&1 | grep -v INFO
```
顺带一条:台账 016 的「上线」一段写的是「重建镜像加 force-recreate」复审记录说「重启进程即可」。这两句现在都不用执行了代码已经在跑。
### 3. 系统自决上线一周,自动采纳为零,四只候选全部因「研判不可用」交给了人
系统自决是 09-14 傍晚全开的,设计目标是弱表态候选在研判通过后自动买试探仓。第一周的真实读数如下,来自读数表 `pms_consensus_stat` 与提议表。
| 日期 | 代码 | 自决的结论 | 原因原文 | 写入时刻 | 后来 |
|---|---|---|---|---|---|
| 09-15 | 300308.SZ | 交人 | 研判不可用,降级人工确认 | 09:31 | 提议过期,没人裁决 |
| 09-17 | 688778.SH | 交人 | 研判不可用,降级人工确认 | 09:31 | 人工采纳,已建仓并自动挂上网格 |
| 09-18 | 002050.SZ | 交人 | 研判不可用,降级人工确认 | 09:33 | 人工采纳,截至 09-20 未见持仓 |
| 09-18 | 300073.SZ | 交人 | 研判不可用,降级人工确认 | 09:31 | 人工采纳,截至 09-20 未见持仓 |
四只全部卡在同一处开盘后第一轮扫描向决策系统要研判对方不可用按铁律二交人。研判不可用的占比按真实行算是四比四台账线是两成以下。300073.SZ 在别的时刻被研判驳回过四次,说明研判接口平时是通的,问题集中在 09:31 前后。
**下个会话要查的方向。** 一决策系统研判接口在开盘头几分钟的超时。2026-09-01 查过一次根因:推理档的思考 token 挤爆预算,修法是 188 上决策系统 `.env` 的三行EFFORT 改 low、MAX_TOKENS 改 16384、REPARSE 设 1再重建 `trader_worker_pms` 容器,当时写的是「待用户执行」,是否已做没有核实。二,持仓管理系统一侧的研判超时参数够不够。三,首轮扫描要不要让开开盘头几分钟,或者对「研判不可用」的新建仓候选在几分钟后自动再问一次。动决策系统之前先答三题:为什么动、动哪里、怎么回退。
**同一周的其他读数**155 上 `make consensus-review DAYS=6`,只读)。技术面无读数占比每天低于百分之一,台账线是百分之三。「等开口」每天 1 到 9 只,台账线是 1 到 15 只。五个交易日里「放行」为零。转空离场零条。盘中确认开关 `PMS_TECH_INTRADAY_CONFIRM` 仍是关的,原定 09-17 复核后再定,仓库里没有见到那次复核的记录。
### 4. 读数表里混进了单测写的假行,会让复核读数失真
**现象。** 复核脚本第五张表里有一只 600000.SH每逢部署日就「采纳 · target_price」。账户里没有这只票评审账本里它一行都没有。
**机制。** 单测的假仓储装配函数 `install_fakes``scripts/test_wiring.py`)只替换了 `pms_repo` 等几个仓储,没有替换读数表仓储 `consensus_stat_repo`。用例调用整轮扫描 `scan_and_route`末尾的读数落表会走真仓储。开发机上没有数据库这一步失败后被软失败吞掉所以一直没人发现。155 上 `make test` 起的一次性容器带着真实的 `.env`,于是每跑一次全量单测,就往真表里写一行。
**证据。** 四行假数据的写入时刻,与四次在 155 跑 `make test` 的时刻逐一对上。
| 统计日 | 代码 | 类别 | 累计轮数 | 写入时刻 |
|---|---|---|---|---|
| 09-14 | 600000.SH | auto_accept | 2 | 09-14 16:45:13 |
| 09-15 | 600000.SH | auto_accept | 10 | 09-15 11:31:00 |
| 09-16 | 600000.SH | auto_accept | 2 | 09-16 09:42:12 |
| 09-18 | 600000.SH | auto_accept | 2 | 09-18 11:53:27 |
**影响。** 复核脚本第六张表「研判不可用占比」的分母被假行撑大占比被低估。09-15 显示 50%,实为 100%。09-18 显示 66.7%,实为 100%。09-24 的读数与四周复核都会读这张表。
**修法(下个会话做,属代码改动)。** `install_fakes``consensus_stat_repo``upsert_many``delete_day_kind` 也换成内存桩。另外两个调用整轮扫描的测试文件 `test_batch14_units.py``test_batch21_units.py` 同样检查。再加一例哨兵:装完假仓储之后,读数仓储的写函数不是真的那个。修完在 155 上跑一次 `make test`,确认读数表没有新增 600000.SH 的行。
**清理。** 只有上表四行。删除是数据库写操作,先向用户列出要删的行并拿到同意。本周 `auto_accept` 类别下没有任何真实行,所以可以按「统计日加类别」删,但下手前务必先用下面的只读命令再列一遍确认。在 155 上运行:
```bash
cd /home/factor/project/tradingSystem && docker exec pms-web python -c "
from app.repo import consensus_stat_repo as csr
for r in csr.list_range(20260914, 20260930, kinds=['auto_accept','auto_decline','auto_confirm','judge_unavail']):
print(r.get('stat_date'), r.get('ts_code'), r.get('kind'), r.get('rounds'), r.get('reason'), r.get('updated_at'))
" 2>&1 | grep -v INFO
```
---
## 三、服务器实况2026-09-20 09:03 北京时间,只读读回)
### 模拟仓 155
| 项目 | 读数 |
|---|---|
| 仓库版本 | cb73f74文档提交代码到 f6e94cf |
| 代码指纹 | 26510ade120c镜像与工作树一致 |
| 容器 | pms-web、pms-worker、pms-beat、pms-ws 四个都在,已运行 45 小时,即 09-18 11:52 部署以来没动过 |
| 持仓 | 两只300627.SZ 与 688778.SH |
| 交易方案 | 两条网格都在运行300627.SZ 与 688778.SH |
| 待确认提议 | 0 |
| 单测 | 904 例09-18 部署时 ALL SUITES PASS |
关键运行参数,全部读自参数中心。
| 参数 | 值 | 白话 |
|---|---|---|
| PMS_DISPATCH_MODE | ws | 指令真的发给模拟 QMT |
| PMS_AUTONOMY | full | 持仓上的四类动作全自动 |
| PMS_OPEN_AUTONOMY | propose_only | 新建仓名义上只提议,实际由系统自决那一层先决定 |
| PMS_SELF_DECIDE | full | 系统自决全开 |
| PMS_SELF_DECIDE_TRIAL | True | 五类试探仓情形允许自动采纳 |
| PMS_SELF_DECIDE_DAILY_MAX | 3 | 每天最多自动新建仓三只 |
| PMS_STRATEGY_ENABLED | True | 策略层开着,用户 09-14 确认是有意的 |
| PMS_TECH_EXIT_AUTONOMY | full | 技术面转空自动离场 |
| PMS_TECH_INTRADAY_CONFIRM | False | 盘中确认仍关,等复核后拍板 |
| PMS_PLAN_RANK_BY_UPSTREAM | True | 见第二节第 2 条 |
| PMS_PLAN_RANK_BY_QUALITY | True | 旧的按立场分桶排,上游序生效时它退场 |
| PMS_PLAN_TIERS | 强传导 | 拍板点六建议 09-23 改成「强传导,弱传导」 |
| PMS_PLAN_TOP_N | 50 | 与选股系统入池的五十对上 |
| PMS_PLAN_THEME_CAP | 0 | 不向上游传主题限额,用上游默认每主题五只 |
| PMS_CONSENSUS_STAT_TIMES | 0935,1030,1330,1445 | 观察读数的四个检查点 |
| PMS_PROPOSAL_TTL_HOURS | 24 | 但提议最晚当天 23:59:59 失效,不跨自然日 |
| PMS_MACRO_ENABLED 与 PMS_MACRO_AUTONOMY | True 与 full | 大盘冷热这一层开着 |
| PMS_TOTAL_SCALE | 2000000 | 总规模两百万 |
155 的工作树里有几样服务器本地的东西:`scripts/deploy.sh` 的权限位变化、`backups/`、`celerybeat-schedule`、`pms_public.pem`、`prof_open_scan.py`。它们不挡 `git pull`,不要提交,也不要删。
选股系统在 155 上的仓库版本是 3d3f317按日程 09-21 收盘后才拉新代码。
### 真实仓 188
仓库停在 51bd6dc08-28落后主干 112 个提交。只跑着 pms-web 与 pms-ws 两个容器,已运行三周。这台没有源码挂载文件,代码确实在镜像里。追平之前必须先过真实仓初始参数清单:下发模式 shadow、自主档 propose_only、新建仓关、策略层关、转空离场 propose_only、系统自决 off 与 False 与 1。任何写操作先经用户审批。
---
## 四、这一周做完了什么
按日期列,括号里是提交号。细节都在 `DEVLOG.md` 对应日期的节点里。
- **09-14。** 观察读数包f2d6b37新表 `pms_consensus_stat` 与复核脚本、加固包e21b73b、文档包b1ebb36。盘中确认包1ccd542部署时开关关着。页面收尾包2f64274。系统自决包44ec6d4评审修订 980235c台账 015。页面专业化包c94787a 与 77db6b8含文案守卫。选股打分包选股系统 3d3f317持仓管理系统 de06c77。傍晚系统自决全开。
- **09-15。** 个股详情抽屉重做e3fb0c6、9ef5e4c。三个维度统一成基本面、技术面、择时同一套颜色e14c9d9。建仓候选行不再溢出8ff4ea3、c3de056。持仓表操作列折叠成一枚菜单状态列短语加悬停说明78b6c69
- **09-16。** 待确认提议不跨自然日失效,盘前与每轮扫描开头清扫过期提议。管理视图持仓总览加「状态 · 下一步」列。选股计划标成数据日03e4dd5
- **09-17。** 工作包乙候选按上游名次排f5cc1b8台账 016。这是另一个会话做的当时没有写开发节点记录本次在 09-20 的节点里补了一笔。
- **09-18。** 个股深度评析报告改成页内排版。组合操作与纪律设置移到运维视图新页。顶栏改成单行标签加菜单f6e94cf
---
## 五、还欠着什么
第二节的四件事不再重复。其余按要紧程度排。
1. **用户页面验收。** 09-15、09-16、09-18 三批页面改动都已部署,等用户登录 155 逐项看:顶栏单行与右上角菜单、运维视图「组合操作」页、评析报告抽屉、持仓表「⋯」菜单与「状态 · 下一步」、建仓候选行、管理视图持仓总览。
2. **盘中确认开关。** `PMS_TECH_INTRADAY_CONFIRM` 仍关。原计划 09-17 复核观察读数无异常后由用户在页面打开。
3. **688778.SH 两天内两次对账修正**(负 1400 股、负 200 股,都是「以下游为准冲销批次」)。这只票挂着网格,值得查一下网格成交回放与账本之间为什么会差出来。
4. **真实仓准备包。** 初始参数自检脚本 `scripts/real_init_params.py`、台账 014、追平清单。准备件可以先做执行等 QMT 一侧。
5. **系统自决的两个不阻塞项。** 自动采纳的评审方应写 system 并按评审方计数。接口契约文档要补裁决请求体与预设理由接口。
6. **「下一步」短语的依据。** 持仓表与抽屉里的「下一步」只按三票立场与转空相位推断,没有读真实的增持门与离场纪律状态。
7. **小的文档与页面欠账。** `FRONTEND_TRADER_VIEW_PLAN.md` 第 5 节仍写着组合操作在交易员右栏。评析抽屉不能用 Esc 关闭。
8. **155 读数表里两行 09-11 的遗留种子数据**,要不要删等用户定。
9. **八月遗留,此后没有再核实过。** 公示导出的表格样式用户说「还是不对」但没有指认差异。三个口径待拍板。仓库根目录的公司原表 xlsx 是未跟踪状态。
---
## 六、本周日程里与持仓管理系统有关的几天
完整日程在《上线与协同阶段工作方案与规格书_2026-09-17》第四节这里只摘与本仓库有关的。
| 日期 | 事情 | 持仓管理系统这边要做什么 |
|---|---|---|
| 09-21 周一 | 收盘后选股系统机器拉代码并重启接口容器,经审批,避开 07:00 到 07:30 与 08:30 到 09:05 | 先确认 9acd3fa 已推送并过复审。早上 08:40 之后顺手看一眼名册快照的 `rank_mode` 是不是 quality_bucket |
| 09-22 周二 | 07:15 后核首份质地序计划的六处读数,再核「两条路同一份前二十」 | 07:10 之前按第二节第 2 条的拍板结果处理开关。名册主榜行数会从六十到一百行跳到接近三百行,属预期 |
| 09-23 周三 | 原定收盘后上线工作包乙 | 若选了乙:收盘后打开开关,并把 `PMS_PLAN_TIERS` 改成「强传导,弱传导」,台账补一行 |
| 09-24 周四 | 08:45 后核候选序 | 在一次性容器里调候选选择函数,核 `rank_mode` 为 upstream、名次递增。命令在工作方案第六节 |
| 09-25 周五 | 首份带两份对照名单的周报 | 无 |
| 10-16 前后 | 排序轴四周复核 | 读数表要先清干净,见第二节第 4 条 |
---
## 七、机器、部署,以及一个新核实的事实
**登录。** 模拟仓:`ssh -p 2280 factor@192.168.16.155`,项目在 `/home/factor/project/tradingSystem`,页面端口 38100。真实仓`ssh -p 2280 tlai@192.168.16.188`,项目在 `/mnt/work/project/tradingSystem`,页面端口 38200。代码服务器是 Gitea地址 `git@192.168.18.24`,本仓库是 `zlt/tradingSystem`
**部署。** 在开发机改代码并提交推送,然后在 155 上 `git pull --ff-only && make deploy && make test`。判收看四样ALL SUITES PASS、`make stale` 指纹一致、四个容器健康、通道 ONLINE。用户认可盘中部署通道会自动重连不必等收盘。
**新核实的事实155 上源码是挂载进容器的。** 155 的项目目录里有一个服务器本地文件 `docker-compose.override.yml`,内容与仓库里的 `docker-compose.dev.yml` 相同,它把整个仓库目录绑定挂载到四个容器的 `/app`。这个文件被 `.gitignore` 忽略所以仓库里看不到。188 没有这个文件。它带来四个后果。
1. 在 155 上 `git pull` 之后,页面静态文件立刻就是新的,因为每次请求都从磁盘读。后端四个进程仍是旧代码,直到重启或 `make deploy`
2. `make test``make stale` 起的一次性容器读的也是工作树。所以在 155 上「指纹一致」恒成立,它查不出「拉了代码没重启」。这道护栏只在 188 这种没有挂载的机器上才真的起作用。
3. 只改页面或文档时,拉代码即生效。改了 Python 必须让进程重启。`make deploy` 仍然是最稳的一条路,它重建镜像、重建容器、顺带建表。只重启不重建的命令是 `docker compose --profile sched --profile ws restart -t 30`,同样要先经审批。
4. 往 155 拉代码这件事本身就等于「上线了一半」。第二节第 2 条就是这么来的。以后往 155 拉代码之前,先看一眼 `git log` 里有没有别的会话提交的、还没到上线日的改动。
**四条服务器铁律**(用户 2026-08-31 立的不重启不关机容器的重启与重建先列命令与影响拿到审批再做数据库容器除审批外还要说明原因不进无关项目的目录。155 上别人的 `quant_*` 容器、188 上的 `bionic_trader` 系列容器都不要碰。
---
## 八、工作纪律与用户偏好
**纪律。**
- 输出遵守 `.claude/output-styles/readable-chinese.md`:完整句子,一句一事,不自造缩写,术语先用白话解释。
- 每次交付附用户能亲手运行的验证命令,写明预期读数,标明在哪台机器上运行,一个命令块只放一台机器。
- 重要工作往 `DEVLOG.md` 追加五段式节点:做了什么、动了哪些文件、部署方式、真机判收、还欠着什么。判收只写「真机见过」或「未判收」。
- 时间一律写北京时间。容器时钟是北京时间。155 宿主机的 `date` 是 UTC。数据库时间列是 UTC要加八小时。
- 动持仓管理系统以外的系统之前先答三题:为什么动、动哪里、怎么回退。
- 加测试文件必须同时登记进 `scripts/run_tests.py` 的清单与例数表。改结构性的东西先对一遍那份哨兵清单。
- 改页面后三道守卫一起跑:枚举、接线、文案。模板里不写自闭合的自定义标签。
**用户对页面的偏好**(多次返工换来的)。
- 措辞专业,像交易系统。不出现内部设计词,例如合议、路由、交人。不自造简写。
- 同一个概念在所有地方用同一套维度与同一套颜色:基本面、技术面、择时;看多红,看空绿,中性灰,无读数虚框。
- 表格必须在中栏放得下。说明文字进悬停,不占格。不常用的操作可以折叠。
- 改完就部署,盘中重建容器可以接受。
---
## 九、新开发机开工步骤
1. 旧机器上先做完第二节第 1 条的推送。
2. 把旧机器上的迁移包 `~/Documents/work/claude-migrate-20260918.tar.gz` 拷到新机器。里面是五个项目的 Claude 记忆、各仓库被忽略的 `.claude` 目录(输出风格与项目设置)、全局设置。注意持仓管理系统仓库的 `.gitignore` 第 19、20 行忽略了输出风格与项目设置,克隆下来没有它们,必须从迁移包里取。
3. 新机器生成 SSH 密钥公钥登记三处Gitea 账号 zlt网页上加、155 的 factor 用户、188 的 tlai 用户。后两处是服务器写操作,先经用户同意。
4. 克隆仓库,解包,必要时把记忆目录按新用户名改名。目录名的规则是项目绝对路径里的非字母数字字符都换成横线。
在新开发机上运行,克隆:
```bash
mkdir -p ~/Documents/work/project && cd ~/Documents/work/project && git clone git@192.168.18.24:zlt/tradingSystem.git && git clone git@192.168.18.24:zlt/akg-factor-bridge.git && git clone git@192.168.18.24:tlsj/bionic_trader.git
```
在新开发机上运行,解包:
```bash
cd ~ && tar -xzf ~/Documents/work/claude-migrate-20260918.tar.gz
```
在新开发机上运行,建测试环境并跑全量单测,预期最后一行是 ALL SUITES PASS共 904 例:
```bash
cd ~/Documents/work/project/tradingSystem && python3 -m venv ~/venvs/pms && ~/venvs/pms/bin/pip install -r requirements.txt -q && ~/venvs/pms/bin/python scripts/run_tests.py | tail -2
```
开发机上取不到代码指纹时单测会跳过新旧判断,不影响判定。第二十四批有一例在早上 09:36 之前跑必然失败,那是用例对时钟的假设,不是代码问题。
---
## 十、新窗口的开场白
可以直接把下面这段贴给新会话。
> 先读 CLAUDE.md、docs/交接_2026-09-20.md、DEVLOG.md 的开头规则与最后三条节点。读完后只读核对 155 的仓库版本、四个容器状态、PMS_PLAN_RANK_BY_UPSTREAM 的当前值,然后告诉我交接文档第二节四件事各自的现状,等我拍板再动手。
---
## 附录甲(可不读):不部署就能看页面效果的办法
`docs/dev_preview/mock_preview.py` 是一个只给开发机用的假后端。它托管 `app/web/static` 里的真页面,给顶栏、持仓表、建仓候选栏、个股详情抽屉、评析报告抽屉、运维视图「组合操作」页喂固定的假数据。其余接口一律回「预览无后端」,页面顶部会有一条错误提示,不影响看样式。
在开发机上运行:
```bash
cd ~/Documents/work/project/tradingSystem && ~/venvs/pms/bin/python docs/dev_preview/mock_preview.py
```
然后浏览器打开 `http://127.0.0.1:38199/`。地址后面加 `#detail=300627.SZ` 可以直达个股详情抽屉。
两个坑写在脚本开头的说明里:仓库放在 macOS「文稿」目录下时由别的程序拉起的进程可能读不到页面文件要先把静态目录拷出来再用环境变量指过去页面新读了哪个接口就得在脚本里补一个同形状的假应答。这个脚本放在 `docs/` 下是有意的,代码指纹只覆盖 `app/`、`scripts/`、`config/` 三个目录。
量页面布局时用浏览器的脚本控制台读元素的宽高最可靠。历次用过的宽度档位是 1920、1680、1440、1280、1180。
## 附录乙(可不读):本次核对用的只读命令
在 155 上运行,读版本、指纹与容器:
```bash
cd /home/factor/project/tradingSystem && git log --oneline -1 && make stale | tail -1 && docker compose ps --format "table {{.Name}}\t{{.Status}}"
```
在 155 上运行,读一周的观察读数,六张表:
```bash
cd /home/factor/project/tradingSystem && make consensus-review DAYS=6
```
在 155 上运行,确认源码是不是挂载的:
```bash
docker inspect pms-web --format "{{range .Mounts}}{{.Source}} -> {{.Destination}} ({{.Type}}){{println}}{{end}}"
```
在 155 上运行,读通道状态,预期「连接 ONLINE」
```bash
cd /home/factor/project/tradingSystem && docker compose run --rm --no-deps pms-web python scripts/ws_smoke.py status 2>&1 | head -12
```