交易页面:精简提议与候选信息,突出建议和风险
一、让交易员先看建议与风险。数量、金额和仓位差异置于卡面,重复操作说明合并,数量计算过程收进详情。候选显示等待条件,来源与参数折叠保留。 二、仅调整呈现。三项页面守卫通过,缺价、时区和仓位显示检查通过。采纳、驳回、策略动作及候选筛选函数与改前一致。交易规则和运行参数未改。 三、部署采用 155 静态绑定挂载。先备份并断网验证,再拉取;不重启交易进程。线上判收另行记录。
This commit is contained in:
parent
f79ddffd1b
commit
16d8fd76b2
|
|
@ -1939,3 +1939,11 @@ PMS 页面需要重新登录;基座页面版本与研究字段已实机核对
|
||||||
真实浏览器侧栏 295 像素、来源区 694 像素均无横向溢出,默认折叠与基本面详情展开通过。
|
真实浏览器侧栏 295 像素、来源区 694 像素均无横向溢出,默认折叠与基本面详情展开通过。
|
||||||
裁决按钮回到提议摘要之后,重载后无新脚本错误。未触发裁决操作。
|
裁决按钮回到提议摘要之后,重载后无新脚本错误。未触发裁决操作。
|
||||||
备份指纹、生效方式、回退与截图见四仓同步交接最新一节。
|
备份指纹、生效方式、回退与截图见四仓同步交接最新一节。
|
||||||
|
|
||||||
|
## 2026-10-16 交易员界面整体精简(本地完成,部署待判收)
|
||||||
|
|
||||||
|
用户要求围绕建议、依据和风险整理整个交易员界面,重点为右侧提议和候选。仅修改静态页面。提议突出数量、预计金额和建议仓位;仓位不一致仍显著提示。重算过程移入数量说明,通用操作说明只保留一次,有效期转为北京时间短格式。清仓使用中性的预计金额,缺价显示缺失。候选保留原列表与排序,直接显示等待条件;占用提示去除内部代码数组,未知失败提示原样保留。交易方案显示当前可见数量,暂停买入的原因可展开。持仓、信号、顶栏和计划详情同步简化,技术来源折叠保留。
|
||||||
|
|
||||||
|
本地验证:三项页面守卫全部通过;脚本语法、金额缺失、北京时间跨日、仓位差异、原值不变的临时断言通过。采纳、驳回、策略动作、计划读取刷新函数及候选筛选表达式与改前逐字一致。未修改 Python、运行参数或交易规则。
|
||||||
|
|
||||||
|
部署预判:155 当前为 f79ddff,pms-web 通过仓库绑定挂载提供静态页。先备份页面,以匹配的运行镜像断网运行三项页面守卫,再仅 git pull。预期四容器镜像和启动时间不变。浏览器应显示精简提议、可读的候选等待状态,数量说明与基本面入口正常,关键风险仍可见,右栏无横向溢出。不得采纳提议或制造交易用于验收。188 不操作。
|
||||||
|
|
|
||||||
|
|
@ -492,6 +492,17 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
.chip.hd-user{background:var(--accent-weak);color:var(--accent);} .chip.hd-user .dot{background:var(--accent);}
|
.chip.hd-user{background:var(--accent-weak);color:var(--accent);} .chip.hd-user .dot{background:var(--accent);}
|
||||||
/* ── 提议卡: 来源结论行 + 详情弹窗 (2026-09-12 重构) ── */
|
/* ── 提议卡: 来源结论行 + 详情弹窗 (2026-09-12 重构) ── */
|
||||||
.prop-src{margin:9px 0 4px;border-top:1px solid var(--hair);}
|
.prop-src{margin:9px 0 4px;border-top:1px solid var(--hair);}
|
||||||
|
.prop-order{margin:7px 0 4px;display:flex;align-items:baseline;justify-content:space-between;gap:8px;flex-wrap:wrap;}
|
||||||
|
.prop-order b{font-size:15px;font-variant-numeric:tabular-nums;}
|
||||||
|
.prop-meta{display:flex;align-items:center;gap:6px 12px;flex-wrap:wrap;color:var(--muted);font-size:12px;}
|
||||||
|
.prop-actions{display:flex;align-items:center;gap:8px;margin-top:8px;}
|
||||||
|
.prop-actions .el-button+.el-button{margin-left:0;}
|
||||||
|
.prop-actions .research-link{margin-left:auto;font-size:12px;}
|
||||||
|
.rail-note{font-size:12px;color:var(--muted);margin:0 0 8px;}
|
||||||
|
.rail-detail{font-size:12px;color:var(--ink-2);margin-top:5px;overflow-wrap:anywhere;}
|
||||||
|
.rail-detail summary{cursor:pointer;color:var(--muted);}
|
||||||
|
.sigrow2.clk .cand-status{flex-basis:100%;font-size:11.5px;color:var(--muted);padding-left:35px;}
|
||||||
|
.sigrow2.clk .cand-status.would{color:var(--accent);}
|
||||||
.prop-src-row{display:flex;align-items:center;gap:8px;padding:8px 2px;border-bottom:1px solid rgba(0,0,0,.04);cursor:pointer;}
|
.prop-src-row{display:flex;align-items:center;gap:8px;padding:8px 2px;border-bottom:1px solid rgba(0,0,0,.04);cursor:pointer;}
|
||||||
.prop-src-row:last-child{border-bottom:none;}
|
.prop-src-row:last-child{border-bottom:none;}
|
||||||
.prop-src-row:hover{background:var(--surface-2);}
|
.prop-src-row:hover{background:var(--surface-2);}
|
||||||
|
|
@ -551,7 +562,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<!-- 顶栏 (2026-09-18 改成单行): 原来七枚胶囊各带长文字与说明气泡, 右侧再排六个按钮, 窗口不到 1950 就折成两行。
|
<!-- 顶栏 (2026-09-18 改成单行): 原来七枚胶囊各带长文字与说明气泡, 右侧再排六个按钮, 窗口不到 1950 就折成两行。
|
||||||
现在状态只留一个短词做成标签 (与综合分析列同款), 说明进悬停; 按钮收进右侧一个菜单。整行不折行, 放不下时按重要性从低到高收起。 -->
|
现在状态只留一个短词做成标签 (与综合分析列同款), 说明进悬停; 按钮收进右侧一个菜单。整行不折行, 放不下时按重要性从低到高收起。 -->
|
||||||
<div class="hd">
|
<div class="hd">
|
||||||
<h1>PMS 持仓管理<span class="ver">v{{ health.version || '-' }} · {{ health.now || '' }}</span></h1>
|
<h1>PMS 持仓管理<span class="ver"><template v-if="mode==='ops'">v{{ health.version || '-' }} · </template>{{ health.now || '' }}</span></h1>
|
||||||
<div class="hd-tags">
|
<div class="hd-tags">
|
||||||
<span class="hst" :class="marketOpen?'ok':'idle'" :title="'交易时段:' + marketState + (health.now ? '。服务器时间 ' + health.now : '')">{{ marketState }}</span>
|
<span class="hst" :class="marketOpen?'ok':'idle'" :title="'交易时段:' + marketState + (health.now ? '。服务器时间 ' + health.now : '')">{{ marketState }}</span>
|
||||||
<span class="hst hd-buy" :class="ov.buy_halt?'warn':'ok'" :title="ov.buy_halt ? '买入已暂停:系统不再发起新买入,已挂出未成交的买单已撤;卖出与止损照常。恢复买入在运维视图的「组合操作」。' : '买入正常:系统按规则自动买入。暂停买入在运维视图的「组合操作」。'">买入{{ ov.buy_halt ? '已暂停' : '正常' }}</span>
|
<span class="hst hd-buy" :class="ov.buy_halt?'warn':'ok'" :title="ov.buy_halt ? '买入已暂停:系统不再发起新买入,已挂出未成交的买单已撤;卖出与止损照常。恢复买入在运维视图的「组合操作」。' : '买入正常:系统按规则自动买入。暂停买入在运维视图的「组合操作」。'">买入{{ ov.buy_halt ? '已暂停' : '正常' }}</span>
|
||||||
|
|
@ -564,7 +575,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<el-tooltip placement="bottom-start" effect="light" :show-after="120">
|
<el-tooltip placement="bottom-start" effect="light" :show-after="120">
|
||||||
<template #content>
|
<template #content>
|
||||||
<div style="max-width:360px;font-size:12px;line-height:1.75">
|
<div style="max-width:360px;font-size:12px;line-height:1.75">
|
||||||
<template v-if="macro.enabled===false"><b>大盘冷热这一层没开</b><div class="muted">在「参数设置」里把 PMS_MACRO_ENABLED 打开</div></template>
|
<template v-if="macro.enabled===false"><b>大盘冷热这一层没开</b><div class="muted">可在运维视图启用大盘冷热分析</div></template>
|
||||||
<template v-else-if="!mzSig"><b>大盘冷热</b>:还没有数据(今天还没扫描过,交易日 09:35 自动跑)</template>
|
<template v-else-if="!mzSig"><b>大盘冷热</b>:还没有数据(今天还没扫描过,交易日 09:35 自动跑)</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div><b>{{ mzSig.label }}</b>
|
<div><b>{{ mzSig.label }}</b>
|
||||||
|
|
@ -781,7 +792,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<span class="disp" :class="lvClass(g.level)" style="padding:0 6px">{{ lvLabel(g.level) }}</span>
|
<span class="disp" :class="lvClass(g.level)" style="padding:0 6px">{{ lvLabel(g.level) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="muted" style="margin-top:9px;font-size:11.5px">
|
<div class="muted" style="margin-top:9px;font-size:11.5px">
|
||||||
灰掉的是已经过时的:超过设定的时效还没有新的同类信号盖过它。不隐藏,只是提醒你别再当新消息看。</div>
|
淡色信号已过时,仅供回顾。</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="muted" v-if="sigOld.length" @click="showOldSig=!showOldSig"
|
<div class="muted" v-if="sigOld.length" @click="showOldSig=!showOldSig"
|
||||||
style="margin:10px 0 5px;font-weight:600;font-size:11.5px;border-top:1px dashed var(--hair);padding-top:8px;cursor:pointer">
|
style="margin:10px 0 5px;font-weight:600;font-size:11.5px;border-top:1px dashed var(--hair);padding-top:8px;cursor:pointer">
|
||||||
|
|
@ -997,11 +1008,8 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<!-- 持仓: 操作折叠成一枚「⋯」, 表格在中栏放得下; 比中栏还宽时横向滚动 -->
|
<!-- 持仓: 操作折叠成一枚「⋯」, 表格在中栏放得下; 比中栏还宽时横向滚动 -->
|
||||||
<div class="panel tblk postbl" id="t-pos">
|
<div class="panel tblk postbl" id="t-pos">
|
||||||
<h3>持仓 <span class="muted" style="font-weight:400">· {{ heldPositions.length }} 只</span></h3>
|
<h3>持仓 <span class="muted" style="font-weight:400">· {{ heldPositions.length }} 只</span></h3>
|
||||||
<div class="sub2">安全垫<span class="help" data-tip="现在的浮盈够不够厚,够不够扛一次回撤。厚垫=赚得多,可以考虑加仓;薄垫=刚开始赚;浮亏无垫=还在亏,不加仓。">?</span>
|
<div class="sub2">占比按总操作规模计算。
|
||||||
看的是浮盈厚不厚:厚垫可以考虑加仓,薄垫先放着,浮亏无垫不加仓。
|
<span class="help" data-tip="成本为各批买入价的加权平均。安全垫反映浮盈承受回撤的余量。人工设定价仅随人工命令变更,系统参考位另见管理视图。">?</span></div>
|
||||||
成本是各批买入价的加权平均,占比是占总操作规模的百分比。
|
|
||||||
「人工设定价」这一列是人工通过命令设定的止损价与目标价,一直在,人工改动才变;
|
|
||||||
系统每天早上自己算的参考位是另一回事,在管理视图的持仓总览里看。</div>
|
|
||||||
<el-table :data="heldPositions" size="small" border max-height="480" @expand-change="onPosExpand">
|
<el-table :data="heldPositions" size="small" border max-height="480" @expand-change="onPosExpand">
|
||||||
<el-table-column type="expand" width="40">
|
<el-table-column type="expand" width="40">
|
||||||
<template #default="s">
|
<template #default="s">
|
||||||
|
|
@ -1084,7 +1092,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</template>
|
</template>
|
||||||
<span v-else class="muted" style="cursor:pointer;text-decoration:underline dotted"
|
<span v-else class="muted" style="cursor:pointer;text-decoration:underline dotted"
|
||||||
@click.stop="openCodeSignals(s.row.ts_code)"
|
@click.stop="openCodeSignals(s.row.ts_code)"
|
||||||
title="速览只留了每类最新的若干条,这里的「没有」可能是假的。点一下按代码回扫今天全部。">今天没有</span>
|
title="速览未收录信号,点击查看今日完整记录。">暂无速览</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!-- 状态 · 下一步 (2026-09-15 重排): 第一行是芯片 (持仓状态 / 挂着的交易方案 / 只卖不买), 第二行一句短语,
|
<!-- 状态 · 下一步 (2026-09-15 重排): 第一行是芯片 (持仓状态 / 挂着的交易方案 / 只卖不买), 第二行一句短语,
|
||||||
|
|
@ -1184,7 +1192,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<span class="muted" style="font-weight:400">· {{ opLog.length }} 条</span>
|
<span class="muted" style="font-weight:400">· {{ opLog.length }} 条</span>
|
||||||
<span class="muted" style="font-size:12px;font-weight:normal">{{ showOpLog?'▲ 收起':'▼ 展开' }}</span></h3>
|
<span class="muted" style="font-size:12px;font-weight:normal">{{ showOpLog?'▲ 收起':'▼ 展开' }}</span></h3>
|
||||||
<template v-if="showOpLog">
|
<template v-if="showOpLog">
|
||||||
<div class="muted" style="margin-bottom:6px">你每做一次操作留一行;被规则挡下的也记下原因。</div>
|
<div class="muted" style="margin-bottom:6px">记录操作结果及未执行原因。</div>
|
||||||
<div class="muted" v-if="!opLog.length">还没有操作记录。</div>
|
<div class="muted" v-if="!opLog.length">还没有操作记录。</div>
|
||||||
<div v-for="(o,i) in opLog" :key="i" class="feed-row">
|
<div v-for="(o,i) in opLog" :key="i" class="feed-row">
|
||||||
<span class="muted mono">{{ (o.at||'').slice(11,16) }}</span>
|
<span class="muted mono">{{ (o.at||'').slice(11,16) }}</span>
|
||||||
|
|
@ -1219,27 +1227,19 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<div class="secth" @click="toggleSect('prop')">待确认提议
|
<div class="secth" @click="toggleSect('prop')">待确认提议
|
||||||
<span class="n">· {{ proposals.length }} 条</span><span class="car">{{ sectClosed.prop?'▸':'▾' }}</span></div>
|
<span class="n">· {{ proposals.length }} 条</span><span class="car">{{ sectClosed.prop?'▸':'▾' }}</span></div>
|
||||||
<div class="sectb">
|
<div class="sectb">
|
||||||
<div class="muted" v-if="!proposals.length">当前没有需人工确认的事。</div>
|
<div class="muted" v-if="!proposals.length">暂无待确认提议。</div>
|
||||||
|
<div v-else class="rail-note">采纳后生成待执行指令;驳回后今日不再提议。</div>
|
||||||
<div v-for="p in proposals" :key="p.proposal_id" class="mini">
|
<div v-for="p in proposals" :key="p.proposal_id" class="mini">
|
||||||
<div class="top"><b class="lk" title="查看个股详情" @click="openCodeSignals(p.ts_code)">{{ nm(p.ts_code) }}</b>
|
<div class="top"><b class="lk" title="查看个股详情" @click="openCodeSignals(p.ts_code)">{{ nm(p.ts_code) }}</b>
|
||||||
<span class="muted mono lk" style="font-size:11px" title="查看个股详情" @click="openCodeSignals(p.ts_code)">{{ p.ts_code }}</span>
|
<span class="muted mono lk" style="font-size:11px" title="查看个股详情" @click="openCodeSignals(p.ts_code)">{{ p.ts_code }}</span>
|
||||||
<el-tag size="small" effect="dark" type="primary">{{ tx('action', p.action) }}</el-tag>
|
<el-tag size="small" effect="dark" type="primary">{{ tx('action', p.action) }}</el-tag>
|
||||||
<span class="spacer"></span><span class="muted" style="font-size:11px">{{ p.expire_at }} 前有效</span></div>
|
<span class="spacer"></span></div>
|
||||||
<!-- 2026-09-10 重排。原先这里一口气铺十六行、实测一张卡二十七八个可视行,
|
<!-- 只调整呈现:数量、金额和仓位差异在卡面,重算过程保留在数量说明。 -->
|
||||||
其中大半是「暂无」「近三天没点名」这类占位句 —— 要人在一屏读不完的
|
<div class="prop-order"><b>{{ p.qty }} 股</b><span>预计金额 <b>{{ propAmount(p) }}</b></span></div>
|
||||||
文字里自己找哪一条是要紧的。现在按「决定要不要点采纳所必需」分两层:
|
<div class="prop-meta"><span>参考价 {{ (p.hard_numbers||{}).price==null ? '—' : (p.hard_numbers||{}).price }}</span>
|
||||||
必需的留在卡面, 证据类的收进展开。判定用的键一个都没动, 只动 DOM。 -->
|
<span v-if="propSizePct(p)">占总规模 {{ propSizePct(p) }}</span></div>
|
||||||
|
<div class="st" v-if="propAdviceTier(p)"><span class="muted">建议 </span>{{ propAdviceTier(p) }}</div>
|
||||||
<!-- 第一行就是这一笔到底买多少。原先它排在第十四行, 而它才是采纳按钮真正
|
<div class="st" v-if="propSizeMismatch(p)" style="color:var(--warn)">{{ propSizeMismatch(p) }}</div>
|
||||||
会执行的东西。 -->
|
|
||||||
<div class="st"><b>{{ p.qty }} 股</b> · 参考价 {{ (p.hard_numbers||{}).price==null ? '—' : (p.hard_numbers||{}).price }}
|
|
||||||
· 约需 <b>{{ money(((p.hard_numbers||{}).price||0)*(p.qty||0)) }}</b>
|
|
||||||
<span class="muted" v-if="propSizePct(p)">(占总规模 {{ propSizePct(p) }})</span></div>
|
|
||||||
|
|
||||||
<!-- 建议档位与实际下单量对不对得上。对不上就直接说, 别让人自己去算。 -->
|
|
||||||
<div class="st" v-if="propAdviceTier(p)"><span class="muted">建议档位:</span>{{ propAdviceTier(p) }}</div>
|
|
||||||
<div class="st" v-if="propSizeMismatch(p)" style="color:#d8574a">{{ propSizeMismatch(p) }}</div>
|
|
||||||
<div class="st muted" v-if="propResized(p)">{{ propResized(p) }}</div>
|
|
||||||
|
|
||||||
<!-- 四行来源结论, 点开看完整依据 (2026-09-12 重构): 原先每类一整句平铺在卡面,
|
<!-- 四行来源结论, 点开看完整依据 (2026-09-12 重构): 原先每类一整句平铺在卡面,
|
||||||
公司深度那句最长 —— 现在卡面只留结论标签, 整句进弹窗。判定用的键一个没动。 -->
|
公司深度那句最长 —— 现在卡面只留结论标签, 整句进弹窗。判定用的键一个没动。 -->
|
||||||
|
|
@ -1272,11 +1272,12 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<span class="sub">{{ propTimingSub(p) }}</span><i class="car">›</i>
|
<span class="sub">{{ propTimingSub(p) }}</span><i class="car">›</i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top:6px">
|
<div class="prop-actions">
|
||||||
<el-button type="success" size="small" @click="decide(p,'ACCEPTED')">采纳</el-button>
|
<el-button type="success" size="small" @click="decide(p,'ACCEPTED')">采纳</el-button>
|
||||||
<el-button type="danger" size="small" @click="decide(p,'DECLINED')">驳回</el-button>
|
<el-button type="danger" size="small" @click="decide(p,'DECLINED')">驳回</el-button>
|
||||||
|
<button class="research-link" v-if="propResized(p) || propSizeMismatch(p)" @click="openPropDetail(p,'size')">数量说明</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="muted" style="font-size:11px;margin-top:4px">采纳=生成一笔待执行的指令,系统会在允许下单的时间段里挑时机下出去;驳回=今天不再提这只。</div>
|
<div class="muted" style="font-size:11px;margin-top:5px" :title="p.expire_at">{{ propExpiry(p.expire_at) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1284,17 +1285,17 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<!-- 交易方案 -->
|
<!-- 交易方案 -->
|
||||||
<div class="sect" :class="{closed:sectClosed.strat}" id="t-strat">
|
<div class="sect" :class="{closed:sectClosed.strat}" id="t-strat">
|
||||||
<div class="secth" @click="toggleSect('strat')">交易方案
|
<div class="secth" @click="toggleSect('strat')">交易方案
|
||||||
<span class="n">· {{ strategies.length }} 个</span><span class="car">{{ sectClosed.strat?'▸':'▾' }}</span></div>
|
<span class="n">· {{ strategiesShown.length }} 个</span><span class="car">{{ sectClosed.strat?'▸':'▾' }}</span></div>
|
||||||
<div class="sectb">
|
<div class="sectb">
|
||||||
<div class="muted" style="margin-bottom:6px">
|
<div class="muted" style="margin-bottom:6px">
|
||||||
{{ stratEnabled ? '策略功能已开启' : '策略功能没开(参数 PMS_STRATEGY_ENABLED)' }}<template v-if="autoInfo"> ·
|
{{ stratEnabled ? '执行已开启' : '执行已关闭' }}<template v-if="autoInfo"> ·
|
||||||
{{ autoInfo.enabled ? '自动挂方案已开启' : '自动挂方案没开' }}</template>
|
{{ autoInfo.enabled ? '自动安排' : '手动安排' }}</template>
|
||||||
<template v-if="autoInfo && autoInfo.ran_today && autoInfo.last_scan"> · 今天
|
<template v-if="autoInfo && autoInfo.ran_today && autoInfo.last_scan"> · 今天
|
||||||
{{ (autoInfo.last_scan.at||'').slice(11,16) }} 跑过一轮</template>
|
{{ (autoInfo.last_scan.at||'').slice(11,16) }} 已评估</template>
|
||||||
<template v-else-if="autoInfo"> · 今天还没跑(交易日 09:40 自动跑)</template>
|
<template v-else-if="autoInfo"> · 今日尚未评估</template>
|
||||||
</div>
|
</div>
|
||||||
<el-alert v-if="!stratEnabled" type="info" effect="dark" :closable="false" show-icon style="margin-bottom:8px"
|
<el-alert v-if="!stratEnabled" type="info" effect="dark" :closable="false" show-icon style="margin-bottom:8px"
|
||||||
title="策略挂上了也不会下单:要在运维视图的「参数设置」里把 PMS_STRATEGY_ENABLED 打开。至于是真下单还是只记录,由 PMS_DISPATCH_MODE 决定。"></el-alert>
|
title="交易方案执行已关闭。启用方式与下单模式见运维视图。"></el-alert>
|
||||||
|
|
||||||
<div class="row" style="margin-bottom:8px">
|
<div class="row" style="margin-bottom:8px">
|
||||||
<el-checkbox v-model="showAllStrategies" @change="loadStrategies" size="small" style="font-weight:normal">显示已撤下和已完成</el-checkbox>
|
<el-checkbox v-model="showAllStrategies" @change="loadStrategies" size="small" style="font-weight:normal">显示已撤下和已完成</el-checkbox>
|
||||||
|
|
@ -1348,9 +1349,10 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<span class="spacer"></span>
|
<span class="spacer"></span>
|
||||||
<el-tag size="small" :type="s.status==='ACTIVE'?'primary':'info'">{{ tx('stratStatus', s.status) }}</el-tag></div>
|
<el-tag size="small" :type="s.status==='ACTIVE'?'primary':'info'">{{ tx('stratStatus', s.status) }}</el-tag></div>
|
||||||
<div class="st">{{ stratStateText(s) }}</div>
|
<div class="st">{{ stratStateText(s) }}</div>
|
||||||
<div class="st" style="color:var(--warn)" v-if="s.buy_paused">
|
<template v-if="s.buy_paused">
|
||||||
这只票现在只卖不买。决策系统提示{{ s.buy_paused.reason || '资金异动' }},已经停掉买入;
|
<div class="st" style="color:var(--warn)">买入已暂停,卖出照常。</div>
|
||||||
卖出、以及把当天做T买的那部分卖回去,都不受影响。</div>
|
<details class="rail-detail"><summary>暂停原因</summary>{{ s.buy_paused.reason || '未记录具体原因' }}</details>
|
||||||
|
</template>
|
||||||
<div style="margin-top:6px">
|
<div style="margin-top:6px">
|
||||||
<el-button v-if="s.status==='ACTIVE'" size="small" @click="setStrategyStatus(s.strategy_id,'PAUSED')">暂停</el-button>
|
<el-button v-if="s.status==='ACTIVE'" size="small" @click="setStrategyStatus(s.strategy_id,'PAUSED')">暂停</el-button>
|
||||||
<el-button v-else-if="s.status==='PAUSED'" size="small" type="success" @click="setStrategyStatus(s.strategy_id,'ACTIVE')">恢复</el-button>
|
<el-button v-else-if="s.status==='PAUSED'" size="small" type="success" @click="setStrategyStatus(s.strategy_id,'ACTIVE')">恢复</el-button>
|
||||||
|
|
@ -1365,17 +1367,15 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 建仓候选 -->
|
<!-- 建仓候选 -->
|
||||||
<div class="sect" :class="{closed:sectClosed.pool}">
|
<div class="sect" :class="{closed:sectClosed.pool}" id="t-pool">
|
||||||
<div class="secth" @click="toggleSect('pool')">建仓候选
|
<div class="secth" @click="toggleSect('pool')">建仓候选
|
||||||
<span class="n">· {{ watchCandidates.length }} 只</span><span class="car">{{ sectClosed.pool?'▸':'▾' }}</span></div>
|
<span class="n">· {{ watchCandidates.length }} 只</span><span class="car">{{ sectClosed.pool?'▸':'▾' }}</span></div>
|
||||||
<div class="sectb">
|
<div class="sectb">
|
||||||
<div class="muted" style="margin-bottom:5px">今天选股计划里、系统仍在评估、可能建仓的票。计划数据至 {{ planStatus.date || '—' }}<template
|
<div class="rail-note">行情截至 {{ planStatus.date || '—' }}<template v-if="planFetchedText"> · {{ planFetchedText.replace('拉取','更新') }}</template></div>
|
||||||
v-if="planFetchedText">,{{ planFetchedText }}</template>。
|
<div class="pool-note" v-if="scanNotes.length"><div v-for="n in scanNotes" :key="n">{{ n }}</div></div>
|
||||||
已下单、待确认、未建仓的,分别在「当日指令」和「待确认提议」。</div>
|
<div class="pool-note" v-if="openScan.ok===false">候选评估暂不可用,以下仅供观察。</div>
|
||||||
<div class="pool-note" v-if="(openScan.notes||[]).length">
|
<div class="muted" v-if="planStatus.ok===false">选股计划暂不可用。</div>
|
||||||
<span v-for="(n,i) in openScan.notes" :key="i">{{ n }}<span v-if="i < openScan.notes.length-1">;</span></span></div>
|
<div class="muted" v-else-if="!watchCandidates.length">暂无待评估候选。</div>
|
||||||
<div class="muted" v-if="planStatus.ok===false">上游的选股计划现在读不到。</div>
|
|
||||||
<div class="muted" v-else-if="!watchCandidates.length">当前没有待建仓的候选,都已处理。</div>
|
|
||||||
<div v-else v-for="r in watchCandidates" :key="r.ts_code" class="sigrow2 clk"
|
<div v-else v-for="r in watchCandidates" :key="r.ts_code" class="sigrow2 clk"
|
||||||
:title="'查看个股详情' + (dispOf(r.ts_code).label ? ' · ' + dispOf(r.ts_code).label : '') + (dispOf(r.ts_code).why ? ':' + dispOf(r.ts_code).why : '')"
|
:title="'查看个股详情' + (dispOf(r.ts_code).label ? ' · ' + dispOf(r.ts_code).label : '') + (dispOf(r.ts_code).why ? ':' + dispOf(r.ts_code).why : '')"
|
||||||
@click="openCodeSignals(r.ts_code)">
|
@click="openCodeSignals(r.ts_code)">
|
||||||
|
|
@ -1390,14 +1390,10 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<span class="dim" :class="dimCls(conOf(r.ts_code).tech)" :title="'技术面 ' + (conOf(r.ts_code).tech||'无读数') + (conOf(r.ts_code).phase ? ' · ' + conOf(r.ts_code).phase : '')">技术面</span>
|
<span class="dim" :class="dimCls(conOf(r.ts_code).tech)" :title="'技术面 ' + (conOf(r.ts_code).tech||'无读数') + (conOf(r.ts_code).phase ? ' · ' + conOf(r.ts_code).phase : '')">技术面</span>
|
||||||
<span class="dim" :class="dimCls(conOf(r.ts_code).timing)" :title="'择时 ' + (conOf(r.ts_code).timing||'无读数')">择时</span>
|
<span class="dim" :class="dimCls(conOf(r.ts_code).timing)" :title="'择时 ' + (conOf(r.ts_code).timing||'无读数')">择时</span>
|
||||||
</template>
|
</template>
|
||||||
<!-- 等待态 (候选观察/技术面待开口/盘中确认中/择时待转多) 不占行内位置: 这一栏本来就是
|
|
||||||
还在评估的票, 原因进悬停; 只有拟建仓、系统已放弃这类有动作意义的才显示芯片。 -->
|
|
||||||
<span class="disp" v-if="dispOf(r.ts_code).cls !== 'wait'" :class="dispOf(r.ts_code).cls" style="padding:0 6px">{{ dispOf(r.ts_code).label }}</span>
|
|
||||||
</span>
|
</span>
|
||||||
|
<span class="cand-status" :class="dispOf(r.ts_code).cls">{{ dispOf(r.ts_code).label }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="muted" style="margin-top:6px;font-size:11.5px">
|
<button class="research-link" style="margin-top:8px" @click="openPlan">查看完整选股计划</button>
|
||||||
受益层级<span class="help" data-tip="这只票离主题多近:直接受益还是间接受益。上游的选股计划给的。">?</span>
|
|
||||||
和完整明细,在右上角菜单的「选股计划」里。</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -2141,6 +2137,15 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<div class="cs-sect"><div class="h">榜单</div><div class="st muted">{{ propWhy(propDetailP) }}</div></div>
|
<div class="cs-sect"><div class="h">榜单</div><div class="st muted">{{ propWhy(propDetailP) }}</div></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template v-else-if="propDetailKind==='size'">
|
||||||
|
<div class="st"><b>{{ propDetailP.qty }} 股 · {{ propAmount(propDetailP) }}</b></div>
|
||||||
|
<div class="st" v-if="propAdviceTier(propDetailP)">建议 {{ propAdviceTier(propDetailP) }}</div>
|
||||||
|
<div class="st" v-if="propSizeMismatch(propDetailP)" style="color:var(--warn)">{{ propSizeMismatch(propDetailP) }}</div>
|
||||||
|
<div class="cs-sect" v-if="propResized(propDetailP)"><div class="h">数量调整</div><div class="st">{{ propResized(propDetailP) }}</div></div>
|
||||||
|
<div class="st" v-if="(propDetailP.hard_numbers||{}).advice_resize_note">{{ propDetailP.hard_numbers.advice_resize_note }}</div>
|
||||||
|
<div class="st muted">采纳后按上述股数生成指令,实际成交金额以成交回报为准。</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template v-else-if="propDetailKind==='judge'">
|
<template v-else-if="propDetailKind==='judge'">
|
||||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
||||||
<span class="ptag" :class="propJudgeTag(propDetailP).cls">{{ propJudgeTag(propDetailP).text }}</span>
|
<span class="ptag" :class="propJudgeTag(propDetailP).cls">{{ propJudgeTag(propDetailP).text }}</span>
|
||||||
|
|
@ -2429,21 +2434,22 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
|
|
||||||
<!-- 上游选股计划抽屉 -->
|
<!-- 上游选股计划抽屉 -->
|
||||||
<el-drawer v-model="planDrawer" title="上游选股计划 (候选池的事实源)" size="56%">
|
<el-drawer v-model="planDrawer" title="选股计划" size="56%">
|
||||||
<div class="row" style="margin-bottom:8px">
|
<div class="row" style="margin-bottom:8px">
|
||||||
<el-button v-if="isAdmin" type="primary" :loading="planLoading" @click="refreshPlan">
|
<el-button v-if="isAdmin" type="primary" :loading="planLoading" @click="refreshPlan">
|
||||||
强刷 + 灌行业映射</el-button>
|
更新计划与行业映射</el-button>
|
||||||
<el-button :loading="planLoading" @click="loadPlan">只重读</el-button>
|
<el-button :loading="planLoading" @click="loadPlan">刷新显示</el-button>
|
||||||
<span class="muted" style="margin-left:8px">
|
<span class="muted" style="margin-left:8px">
|
||||||
计划不带价格与金额 —— 买多少/什么价一律 PMS 自己算; 行情取不到价的票会被剔除。
|
候选需结合持仓、行情与风险检查后确定买入数量。
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="muted" v-if="planLoading" style="margin-bottom:8px">
|
<div class="muted" v-if="planLoading" style="margin-bottom:8px">
|
||||||
正在读上游选股计划…… 正在读取已发布的完整版本。
|
正在读取选股计划…</div>
|
||||||
这段时间抽屉是空的,属正常;真读不到会在这里显示原因。</div>
|
|
||||||
<el-alert v-if="planStatus.ok===false" type="error" effect="dark" show-icon :closable="false"
|
<el-alert v-if="planStatus.ok===false" type="error" effect="dark" show-icon :closable="false"
|
||||||
:title="planStatus.hint || '不可用'" style="margin-bottom:8px"></el-alert>
|
:title="planStatus.hint || '不可用'" style="margin-bottom:8px"></el-alert>
|
||||||
<div class="muted" v-else style="margin-bottom:8px">
|
<div class="muted" v-else style="margin-bottom:8px">行情截至 {{ planStatus.date || '—' }}<template v-if="planFetchedText"> · {{ planFetchedText.replace('拉取','更新') }}</template></div>
|
||||||
|
<details class="rail-detail" style="margin-bottom:10px"><summary>数据来源与筛选记录</summary>
|
||||||
|
<div class="muted" style="margin-bottom:8px">
|
||||||
数据日 <b>{{ planStatus.date }}</b>(上游每个交易日早上用上一交易日收盘后的证据出计划,日龄 {{ planStatus.age_tdays }} 交易日<template
|
数据日 <b>{{ planStatus.date }}</b>(上游每个交易日早上用上一交易日收盘后的证据出计划,日龄 {{ planStatus.age_tdays }} 交易日<template
|
||||||
v-if="planStatus.age_tdays===1">,属正常</template>)<template v-if="planFetchedText"> · {{ planFetchedText }}</template> ·
|
v-if="planStatus.age_tdays===1">,属正常</template>)<template v-if="planFetchedText"> · {{ planFetchedText }}</template> ·
|
||||||
热度日 {{ planStatus.heat_date || '-' }} · 快照 {{ (planStatus.market_snapshot_days||[]).join(',') }} ·
|
热度日 {{ planStatus.heat_date || '-' }} · 快照 {{ (planStatus.market_snapshot_days||[]).join(',') }} ·
|
||||||
|
|
@ -2453,10 +2459,11 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
→(券商预期 + 目标价≥现价 + 主题限额 + top)→ 实收
|
→(券商预期 + 目标价≥现价 + 主题限额 + top)→ 实收
|
||||||
主榜 {{ (planStatus.funnel||{}).returned_main }} / 观察 {{ (planStatus.funnel||{}).returned_observe }}
|
主榜 {{ (planStatus.funnel||{}).returned_main }} / 观察 {{ (planStatus.funnel||{}).returned_observe }}
|
||||||
</div>
|
</div>
|
||||||
|
</details>
|
||||||
<el-alert v-if="planCand && planCand.tier_complete===false" type="warning" effect="dark"
|
<el-alert v-if="planCand && planCand.tier_complete===false" type="warning" effect="dark"
|
||||||
show-icon :closable="false" style="margin-bottom:8px"
|
show-icon :closable="false" style="margin-bottom:8px"
|
||||||
:title="'返回的全是白名单档位 (top=' + ((planStatus.requested||{}).top)
|
:title="'候选档位覆盖不完整(读取数量 ' + ((planStatus.requested||{}).top)
|
||||||
+ ' 吃满了) —— 白名单档位可能没取全, 调大 PMS_PLAN_TOP'">
|
+ ' 已达返回上限),候选可能不完整,请检查计划读取范围。'">
|
||||||
</el-alert>
|
</el-alert>
|
||||||
<div class="muted" v-if="planCand" style="margin-bottom:8px">
|
<div class="muted" v-if="planCand" style="margin-bottom:8px">
|
||||||
按当前参数筛选: 排序池 {{ planCand.considered }} → 合格 {{ planCand.eligible }} →
|
按当前参数筛选: 排序池 {{ planCand.considered }} → 合格 {{ planCand.eligible }} →
|
||||||
|
|
@ -2465,7 +2472,6 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
落选 {{ dropCn(planCand.dropped) }}
|
落选 {{ dropCn(planCand.dropped) }}
|
||||||
<span v-if="(planCand.st_unknown||[]).length" style="color:#E6A23C">·
|
<span v-if="(planCand.st_unknown||[]).length" style="color:#E6A23C">·
|
||||||
{{ planCand.st_unknown.length }} 只无名称, ST 判不了已放行</span>
|
{{ planCand.st_unknown.length }} 只无名称, ST 判不了已放行</span>
|
||||||
<br><span class="mono">{{ (planCand.items||[]).map(x=>x.ts_code).join(' ') }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- 榜单变化 (PMS 自算; 上游 changes 恒为 null) -->
|
<!-- 榜单变化 (PMS 自算; 上游 changes 恒为 null) -->
|
||||||
<el-divider content-position="left">榜单变化</el-divider>
|
<el-divider content-position="left">榜单变化</el-divider>
|
||||||
|
|
@ -2475,16 +2481,15 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</div>
|
</div>
|
||||||
<el-alert v-if="chgSt.in_sync === false" type="error" effect="dark" show-icon
|
<el-alert v-if="chgSt.in_sync === false" type="error" effect="dark" show-icon
|
||||||
:closable="false" style="margin-bottom:8px"
|
:closable="false" style="margin-bottom:8px"
|
||||||
title="手上这份计划没落进快照 —— 下面比的是两个旧版本, 与当前候选池不是同一份">
|
title="当前计划的比较记录尚未就绪,下方变化暂不能代表当前候选。">
|
||||||
</el-alert>
|
</el-alert>
|
||||||
<el-alert v-else-if="chg && chg.kind === 'same_date_revision'" type="warning" effect="dark"
|
<el-alert v-else-if="chg && chg.kind === 'same_date_revision'" type="warning" effect="dark"
|
||||||
show-icon :closable="false" style="margin-bottom:8px"
|
show-icon :closable="false" style="margin-bottom:8px"
|
||||||
:title="'同一计划日 ' + chg.curr_date + ' 的完整修订版本:'
|
:title="'今日选股依据已更新,请查看下方名单与研究变化。'">
|
||||||
+ '此前收到的计划与当前版本不同,请查看版本及研究变化'">
|
|
||||||
</el-alert>
|
</el-alert>
|
||||||
<el-alert v-if="chg && chg.prev_broken" type="error" effect="dark" show-icon
|
<el-alert v-if="chg && chg.prev_broken" type="error" effect="dark" show-icon
|
||||||
:closable="false" style="margin-bottom:8px"
|
:closable="false" style="margin-bottom:8px"
|
||||||
title="上一版名册读不出来 (快照损坏) —— 这次比不了, 下面的空白不代表没有变化">
|
title="上一份名单读取失败,暂时无法比较变化。">
|
||||||
</el-alert>
|
</el-alert>
|
||||||
<el-alert v-if="chgHeld.n_watch" type="error" effect="dark" show-icon :closable="false"
|
<el-alert v-if="chgHeld.n_watch" type="error" effect="dark" show-icon :closable="false"
|
||||||
style="margin-bottom:8px"
|
style="margin-bottom:8px"
|
||||||
|
|
@ -2492,7 +2497,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</el-alert>
|
</el-alert>
|
||||||
<el-alert v-if="chg && chg.date_regressed" type="warning" effect="dark" show-icon
|
<el-alert v-if="chg && chg.date_regressed" type="warning" effect="dark" show-icon
|
||||||
:closable="false" style="margin-bottom:8px"
|
:closable="false" style="margin-bottom:8px"
|
||||||
title="计划日往回走了 (拉过一份旧计划?) —— 新进/掉榜的方向是反的">
|
title="当前计划日期早于此前记录,新进与退出方向需反向理解。">
|
||||||
</el-alert>
|
</el-alert>
|
||||||
<div class="muted" v-if="chg" style="margin-bottom:8px">
|
<div class="muted" v-if="chg" style="margin-bottom:8px">
|
||||||
{{ chg.kind === 'first' ? '首次落快照' : (chg.prev_date + ' → ' + chg.curr_date) }} ·
|
{{ chg.kind === 'first' ? '首次落快照' : (chg.prev_date + ' → ' + chg.curr_date) }} ·
|
||||||
|
|
@ -2542,17 +2547,17 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane name="in" :label="'新进榜 (' + chg.counts.entered + ')'">
|
<el-tab-pane name="in" :label="'新进榜 (' + chg.counts.entered + ')'">
|
||||||
<el-table :data="chg.entered" size="small" border max-height="220" empty-text="无">
|
<el-table :data="chg.entered" size="small" border max-height="220" empty-text="无">
|
||||||
<el-table-column prop="rank" label="rank" width="62"></el-table-column>
|
<el-table-column prop="rank" label="名次" width="62"></el-table-column>
|
||||||
<el-table-column prop="ts_code" label="代码" width="104"></el-table-column>
|
<el-table-column prop="ts_code" label="代码" width="104"></el-table-column>
|
||||||
<el-table-column prop="name" label="名称" width="92"></el-table-column>
|
<el-table-column prop="name" label="名称" width="92"></el-table-column>
|
||||||
<el-table-column prop="tier" label="传导档" width="82"></el-table-column>
|
<el-table-column prop="tier" label="传导档" width="82"></el-table-column>
|
||||||
<el-table-column prop="theme" label="主题" width="120"></el-table-column>
|
<el-table-column prop="theme" label="主题" width="120"></el-table-column>
|
||||||
<el-table-column prop="score" label="score" width="82"></el-table-column>
|
<el-table-column prop="score" label="评分" width="82"></el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane name="up" :label="'升档 (' + chg.counts.tier_up + ')'">
|
<el-tab-pane name="up" :label="'升档 (' + chg.counts.tier_up + ')'">
|
||||||
<el-table :data="chg.tier_up" size="small" border max-height="220" empty-text="无">
|
<el-table :data="chg.tier_up" size="small" border max-height="220" empty-text="无">
|
||||||
<el-table-column prop="rank" label="rank" width="62"></el-table-column>
|
<el-table-column prop="rank" label="名次" width="62"></el-table-column>
|
||||||
<el-table-column prop="ts_code" label="代码" width="104"></el-table-column>
|
<el-table-column prop="ts_code" label="代码" width="104"></el-table-column>
|
||||||
<el-table-column prop="name" label="名称" width="92"></el-table-column>
|
<el-table-column prop="name" label="名称" width="92"></el-table-column>
|
||||||
<el-table-column label="档位" min-width="150">
|
<el-table-column label="档位" min-width="150">
|
||||||
|
|
@ -2600,13 +2605,13 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<el-table-column label="研究依据" width="110"><template #default="s">
|
<el-table-column label="研究依据" width="110"><template #default="s">
|
||||||
<button class="research-link" @click="openResearch(s.row, s.row.ts_code)">查看详情</button>
|
<button class="research-link" @click="openResearch(s.row, s.row.ts_code)">查看详情</button>
|
||||||
</template></el-table-column>
|
</template></el-table-column>
|
||||||
<el-table-column prop="rank" label="rank" width="62"></el-table-column>
|
<el-table-column prop="rank" label="名次" width="62"></el-table-column>
|
||||||
<el-table-column prop="ts_code" label="代码" width="104"></el-table-column>
|
<el-table-column prop="ts_code" label="代码" width="104"></el-table-column>
|
||||||
<el-table-column prop="name" label="名称" width="92"></el-table-column>
|
<el-table-column prop="name" label="名称" width="92"></el-table-column>
|
||||||
<el-table-column prop="score" label="score" width="82"></el-table-column>
|
<el-table-column prop="score" label="评分" width="82"></el-table-column>
|
||||||
<el-table-column prop="tier" label="传导档" width="82"></el-table-column>
|
<el-table-column prop="tier" label="传导档" width="82"></el-table-column>
|
||||||
<el-table-column prop="theme" label="主题" width="120"></el-table-column>
|
<el-table-column prop="theme" label="主题" width="120"></el-table-column>
|
||||||
<el-table-column prop="heat" label="heat" width="78"></el-table-column>
|
<el-table-column prop="heat" label="热度" width="78"></el-table-column>
|
||||||
<el-table-column label="预期空间" width="96">
|
<el-table-column label="预期空间" width="96">
|
||||||
<template #default="s">
|
<template #default="s">
|
||||||
<span class="muted">{{ s.row.upside==null ? '-' :
|
<span class="muted">{{ s.row.upside==null ? '-' :
|
||||||
|
|
@ -2615,6 +2620,8 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="n_sources" label="源数" width="66"></el-table-column>
|
<el-table-column prop="n_sources" label="源数" width="66"></el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
<div class="muted" style="margin-top:6px">预期空间为券商目标价相对现价的幅度,不参与排序。</div>
|
||||||
|
<details class="rail-detail"><summary>筛选口径与参数说明</summary>
|
||||||
<div class="muted" style="margin-top:6px">
|
<div class="muted" style="margin-top:6px">
|
||||||
「预期空间」= 券商目标价相对现价的空间 (上游 upside 字段, 2.12 → +212%)。噪音大,
|
「预期空间」= 券商目标价相对现价的空间 (上游 upside 字段, 2.12 → +212%)。噪音大,
|
||||||
所以<b>永不参与排序</b> —— 排序按当前有效模式与稳定次级键; 只在 PMS_PLAN_MIN_UPSIDE 上做下限过滤。
|
所以<b>永不参与排序</b> —— 排序按当前有效模式与稳定次级键; 只在 PMS_PLAN_MIN_UPSIDE 上做下限过滤。
|
||||||
|
|
@ -2627,6 +2634,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
「主题」是<b>传导主题</b>(事件驱动、天天变、覆盖约三成), <b>不是行业</b> —— 行业硬拦截
|
「主题」是<b>传导主题</b>(事件驱动、天天变、覆盖约三成), <b>不是行业</b> —— 行业硬拦截
|
||||||
请把 PMS_SECTOR_SOURCE 设成 gp_stock_category, 别用 PMS_PLAN_THEME_SYNC。
|
请把 PMS_SECTOR_SOURCE 设成 gp_stock_category, 别用 PMS_PLAN_THEME_SYNC。
|
||||||
</div>
|
</div>
|
||||||
|
</details>
|
||||||
<pre class="json" v-if="planResult">{{ planResult }}</pre>
|
<pre class="json" v-if="planResult">{{ planResult }}</pre>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
|
|
||||||
|
|
@ -3124,19 +3132,29 @@ createApp({
|
||||||
if (d.disp === 'would') return { cls: 'would', label: '拟建仓', why: d.why };
|
if (d.disp === 'would') return { cls: 'would', label: '拟建仓', why: d.why };
|
||||||
// 三源合议判观察 (2026-09-11): 等技术面开口, 是「在盯」不是「被否决」——
|
// 三源合议判观察 (2026-09-11): 等技术面开口, 是「在盯」不是「被否决」——
|
||||||
// 归到 watch 那一档而不是今天没建仓那一档, 因为它还会回来。
|
// 归到 watch 那一档而不是今天没建仓那一档, 因为它还会回来。
|
||||||
if (d.disp === 'wait_tech') return { cls: 'wait', label: '等技术面开口', why: d.why };
|
if (d.disp === 'wait_tech') return { cls: 'wait', label: '等待技术面转强', why: d.why };
|
||||||
// 盘中确认包 (2026-09-14): 开口未确认/时段外, 等盘中放量确认, 同样是「在盯」不是「被否决」。
|
// 盘中确认包 (2026-09-14): 开口未确认/时段外, 等盘中放量确认, 同样是「在盯」不是「被否决」。
|
||||||
if (d.disp === 'wait_confirm') return { cls: 'wait', label: '等盘中确认', why: d.why };
|
if (d.disp === 'wait_confirm') return { cls: 'wait', label: '等盘中确认', why: d.why };
|
||||||
// 系统自决 (2026-09-14): 方向看多但择时看空, 进观察等择时转多, 也是「在盯」不是「被否决」。
|
// 系统自决 (2026-09-14): 方向看多但择时看空, 进观察等择时转多, 也是「在盯」不是「被否决」。
|
||||||
if (d.disp === 'wait_timing') return { cls: 'wait', label: '择时待转多', why: d.why };
|
if (d.disp === 'wait_timing') return { cls: 'wait', label: '等待择时转多', why: d.why };
|
||||||
// 系统自决今日已放弃 (2026-09-14): 明日重评, 与「未建仓」分开显示。
|
// 系统自决今日已放弃 (2026-09-14): 明日重评, 与「未建仓」分开显示。
|
||||||
if (d.disp === 'sys_decline') return { cls: 'deny', label: '系统已放弃', why: d.why };
|
if (d.disp === 'sys_decline') return { cls: 'deny', label: '系统已放弃', why: d.why };
|
||||||
return { cls: 'deny', label: '未建仓', why: d.why };
|
return { cls: 'deny', label: '未建仓', why: d.why };
|
||||||
}
|
}
|
||||||
return { cls: 'wait', label: '候选观察', why: '' };
|
return { cls: 'wait', label: '待评估', why: '' };
|
||||||
}
|
}
|
||||||
// 候选行的三源合议摘要 (2026-09-14 页面收尾包): 后端候选处置快照 by_code 里挂的 consensus 小块
|
// 候选行的三源合议摘要 (2026-09-14 页面收尾包): 后端候选处置快照 by_code 里挂的 consensus 小块
|
||||||
// (fund/overall/tech/phase/direction/route)。没有就返回 null, 芯片整段不显示。
|
// (fund/overall/tech/phase/direction/route)。没有就返回 null, 芯片整段不显示。
|
||||||
|
function scanNoteText(note) {
|
||||||
|
const text = String(note || '');
|
||||||
|
const m = text.match(/^在途建仓已占 (\d+) 个名额、约 ([\d,]+) 元 \(\[.*\]\), 本轮按余量扫描$/);
|
||||||
|
if (m) return '待确认与未成交建仓占用 ' + m[1] + ' 个名额,约 ' + m[2] + ' 元。';
|
||||||
|
const labels = {'自主档位 off —— 本轮不扫描候选池 (off 是总闸)':'自主操作已暂停,候选暂不评估。',
|
||||||
|
'新建仓档位 off —— 不扫描候选池':'新建仓已暂停,候选暂不评估。',
|
||||||
|
'全局暂停执行 —— 本轮不扫描候选池':'执行已暂停,候选暂不评估。'};
|
||||||
|
return labels[text] || text;
|
||||||
|
}
|
||||||
|
const scanNotes = computed(() => [...new Set((openScan.value.notes || []).map(scanNoteText).filter(Boolean))]);
|
||||||
const conOf = (code) => ((openScan.value.by_code || {})[code] || {}).consensus || null;
|
const conOf = (code) => ((openScan.value.by_code || {})[code] || {}).consensus || null;
|
||||||
const denyToday = computed(() => (planRows.value || []).map(r => {
|
const denyToday = computed(() => (planRows.value || []).map(r => {
|
||||||
const d = dispOf(r.ts_code);
|
const d = dispOf(r.ts_code);
|
||||||
|
|
@ -3201,10 +3219,25 @@ createApp({
|
||||||
const amt = Number(h.target_amount), pct = Number(h.target_pct);
|
const amt = Number(h.target_amount), pct = Number(h.target_pct);
|
||||||
return (isFinite(amt) && isFinite(pct) && pct > 0) ? amt / pct : null;
|
return (isFinite(amt) && isFinite(pct) && pct > 0) ? amt / pct : null;
|
||||||
}
|
}
|
||||||
|
function propAmount(p) {
|
||||||
|
const raw = (p.hard_numbers || {}).price, price = Number(raw), qty = Number(p.qty);
|
||||||
|
return raw != null && raw !== '' && Number.isFinite(price) && price > 0
|
||||||
|
&& Number.isFinite(qty) && qty > 0 ? money(price * qty) + ' 元' : '—';
|
||||||
|
}
|
||||||
|
// PMS 的无偏移业务时刻约定为北京时间;带偏移的时间显式转换。
|
||||||
|
function propExpiry(value) {
|
||||||
|
if (!value) return '有效期未记录';
|
||||||
|
const raw = String(value), date = new Date(/(?:Z|[+-]\d{2}:?\d{2})$/i.test(raw) ? raw : raw.replace(' ','T') + '+08:00');
|
||||||
|
if (Number.isNaN(date.getTime())) return '有效期未记录';
|
||||||
|
const parts = Object.fromEntries(new Intl.DateTimeFormat('en-CA', {timeZone:'Asia/Shanghai',
|
||||||
|
year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',hourCycle:'h23'}).formatToParts(date).map(x=>[x.type,x.value]));
|
||||||
|
const day = parts.year + '-' + parts.month + '-' + parts.day;
|
||||||
|
return (day === todayDateStr.value ? '今日' : day) + ' ' + parts.hour + ':' + parts.minute + ' 前有效';
|
||||||
|
}
|
||||||
function propSizePct(p) {
|
function propSizePct(p) {
|
||||||
const h = p.hard_numbers || {};
|
const h = p.hard_numbers || {};
|
||||||
const scale = propScale(p), price = Number(h.price), qty = Number(p.qty);
|
const scale = propScale(p), price = Number(h.price), qty = Number(p.qty);
|
||||||
if (!scale || !isFinite(price) || !isFinite(qty) || qty <= 0) return '';
|
if (!scale || !isFinite(price) || price <= 0 || !isFinite(qty) || qty <= 0) return '';
|
||||||
return (price * qty / scale * 100).toFixed(2) + '%';
|
return (price * qty / scale * 100).toFixed(2) + '%';
|
||||||
}
|
}
|
||||||
// 建议档位的短版。原先卡上摆的是 advice_text 那一整句 (实测 90 多个字, 把档位、
|
// 建议档位的短版。原先卡上摆的是 advice_text 那一整句 (实测 90 多个字, 把档位、
|
||||||
|
|
@ -3234,15 +3267,14 @@ createApp({
|
||||||
// 后端算不出新数量时会留一句原因(例如「一手就超过机械档,这只票对当前规模太贵」)。
|
// 后端算不出新数量时会留一句原因(例如「一手就超过机械档,这只票对当前规模太贵」)。
|
||||||
// 有这句就直接说它 —— 比让人自己去对两个百分比清楚。
|
// 有这句就直接说它 —— 比让人自己去对两个百分比清楚。
|
||||||
if (h.advice_resize_note) {
|
if (h.advice_resize_note) {
|
||||||
return '注意:数量没能按建议档位重算(' + h.advice_resize_note + ')。采纳后下的就是上面那个数。';
|
return '数量未按建议仓位调整,采纳将按 ' + p.qty + ' 股执行。请查看数量说明。';
|
||||||
}
|
}
|
||||||
const a = h.advice;
|
const a = h.advice;
|
||||||
if (!a || typeof a !== 'object') return '';
|
if (!a || typeof a !== 'object') return '';
|
||||||
const advPct = Number(a.target_pct), usedPct = Number(h.target_pct);
|
const advPct = Number(a.target_pct), usedPct = Number(h.target_pct);
|
||||||
if (!isFinite(advPct) || !isFinite(usedPct) || usedPct <= 0) return '';
|
if (!isFinite(advPct) || !isFinite(usedPct) || usedPct <= 0) return '';
|
||||||
if (Math.abs(advPct - usedPct) < 1e-9) return '';
|
if (Math.abs(advPct - usedPct) < 1e-9) return '';
|
||||||
return '注意:上面那个股数是按 ' + (usedPct * 100).toFixed(1) + '% 那一档算的,'
|
return '数量按 ' + (usedPct * 100).toFixed(1) + '% 测算,建议为 ' + (advPct * 100).toFixed(1) + '%。';
|
||||||
+ '不是按建议档位的 ' + (advPct * 100).toFixed(1) + '%。采纳后下的就是上面那个数。';
|
|
||||||
}
|
}
|
||||||
// 数量按建议档位重算过就说一声。这是正面反馈:让人知道卡上写的档位与真下的股数
|
// 数量按建议档位重算过就说一声。这是正面反馈:让人知道卡上写的档位与真下的股数
|
||||||
// 是对上的,不用自己再算一遍。抬到一手那种情形另有一句 note,一并带出来。
|
// 是对上的,不用自己再算一遍。抬到一手那种情形另有一句 note,一并带出来。
|
||||||
|
|
@ -3424,7 +3456,7 @@ createApp({
|
||||||
const propDetailOpen = ref(false), propDetailKind = ref(''), propDetailP = ref(null);
|
const propDetailOpen = ref(false), propDetailKind = ref(''), propDetailP = ref(null);
|
||||||
const propTechFull = ref(null), propTechErr = ref('');
|
const propTechFull = ref(null), propTechErr = ref('');
|
||||||
const PROP_SRC_LABEL = { plan: '选股系统', judge: '决策系统', company: '基本面',
|
const PROP_SRC_LABEL = { plan: '选股系统', judge: '决策系统', company: '基本面',
|
||||||
tech: '技术面', consensus: '择时' };
|
tech: '技术面', consensus: '择时', size: '数量说明' };
|
||||||
function openPropDetail(p, kind) {
|
function openPropDetail(p, kind) {
|
||||||
propDetailP.value = p; propDetailKind.value = kind; propDetailOpen.value = true;
|
propDetailP.value = p; propDetailKind.value = kind; propDetailOpen.value = true;
|
||||||
propTechFull.value = null; propTechErr.value = '';
|
propTechFull.value = null; propTechErr.value = '';
|
||||||
|
|
@ -4852,7 +4884,7 @@ createApp({
|
||||||
propVerdictLine, propLogic, propLogicMore, propText, propHeads,
|
propVerdictLine, propLogic, propLogicMore, propText, propHeads,
|
||||||
hardCn, opCn, paramsCn, dropCn, plainCheck,
|
hardCn, opCn, paramsCn, dropCn, plainCheck,
|
||||||
// 2026-09-10 提议卡重排新增
|
// 2026-09-10 提议卡重排新增
|
||||||
propScale, propSizePct, propAdviceTier, propSizeMismatch, propResized, propJudgeShort,
|
propScale, propAmount, propExpiry, propSizePct, propAdviceTier, propSizeMismatch, propResized, propJudgeShort,
|
||||||
propMoreOpen, togglePropMore,
|
propMoreOpen, togglePropMore,
|
||||||
// 2026-09-12 提议卡重构: 来源结论标签 + 详情弹窗
|
// 2026-09-12 提议卡重构: 来源结论标签 + 详情弹窗
|
||||||
cr, propPlanTag, propPlanSub, propJudgeTag, propCompanyTag, propTechTag, propTechSub,
|
cr, propPlanTag, propPlanSub, propJudgeTag, propCompanyTag, propTechTag, propTechSub,
|
||||||
|
|
@ -4870,7 +4902,7 @@ createApp({
|
||||||
openScan, insTab, todayYmd, insLive, insDone, insEnd, dispOf, conOf, loadOpenScan, denyToday,
|
openScan, insTab, todayYmd, insLive, insDone, insEnd, dispOf, conOf, loadOpenScan, denyToday,
|
||||||
commandsShown, strategiesShown, instrLive, showAllCommands, showAllStrategies, showAllInstr,
|
commandsShown, strategiesShown, instrLive, showAllCommands, showAllStrategies, showAllInstr,
|
||||||
cmdArchivable, stratArchivable, archiveRow,
|
cmdArchivable, stratArchivable, archiveRow,
|
||||||
showOpLog, showSettings, watchCandidates, marketOpen, marketState, lastRefresh, autoRefresh,
|
showOpLog, showSettings, watchCandidates, scanNotes, marketOpen, marketState, lastRefresh, autoRefresh,
|
||||||
upstream, showUpstream, usrc, loadUpstream, srcFail,
|
upstream, showUpstream, usrc, loadUpstream, srcFail,
|
||||||
csOpen, csCode, csLoading, csErr, csData, csFromSnap, csTimeline, csSrcFails,
|
csOpen, csCode, csLoading, csErr, csData, csFromSnap, csTimeline, csSrcFails,
|
||||||
csTruncated, csSrcLabel, csLedger, csLedgerErr, csHeld, csInPlan,
|
csTruncated, csSrcLabel, csLedger, csLedgerErr, csHeld, csInPlan,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue