提议卡重构:卡面留结论、详情进弹窗、加技术面

用户反馈提议卡信息过载(选股/决策/公司深度整句全平铺、公司深度那句占大半屏)。
改成卡面只留结论、点开弹窗看详情。
- 卡面四行来源结论标签(选股系统/决策系统/公司深度/技术面)加数量档位,采纳驳回
- 弹窗 teleport 到 body(层级 3000/2995 同信号抽屉)从底部滑出,四类详情各自排版
- 公司深度弹窗用质地档/十视角/买方论点/失效条件/反方/催化分段带彩色小标题
- 技术面弹窗三指标块(布林/多空布林线/SAR),点开时调 /api/research 取完整读数
- 颜色按页面口径红涨绿跌:技术面看多红看空绿,评价类用中性蓝黄
- /api/proposals 每条挂技术面立场;两道页面守卫 ALL OK;本地注入假提议冒烟验证渲染

未真机判收。合议行与候选芯片仍属工作包二。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
zlt 2026-09-11 13:44:30 +08:00
parent d89580240b
commit 83022e1347
3 changed files with 205 additions and 28 deletions

View File

@ -1345,6 +1345,28 @@ PMS_JUDGE_ONLINE_EFFORT=low / PMS_JUDGE_ONLINE_MAX_TOKENS=16384 / PMS_JUDGE_REPA
2. 工作包二三源合议与仓位矩阵、工作包三离场纪律、工作包四选股打分另一仓库单独审批、工作包五README 调度总表补 13 位等文档)未做。 2. 工作包二三源合议与仓位矩阵、工作包三离场纪律、工作包四选股打分另一仓库单独审批、工作包五README 调度总表补 13 位等文档)未做。
3. BIONIC_PMS_INTERFACE.md 需补技术面只读接口一节(本次接口探活的字段事实源)。 3. BIONIC_PMS_INTERFACE.md 需补技术面只读接口一节(本次接口探活的字段事实源)。
---
## 2026-09-12 · 提议卡重构:卡面留结论、详情进弹窗、加技术面(未真机判收)
**做了什么**
用户反馈新建仓提议卡信息过载 —— 选股系统、决策系统、公司深度三段整句全平铺在卡面,公司深度那句最长,占了大半屏。重构成卡面只留结论、点开弹窗看详情。
- 卡面留数量、建议档位、四行来源结论(选股系统、决策系统、公司深度、技术面),采纳驳回。每行一个彩色结论标签加一句副文案,点开弹窗。
- 弹窗 teleport 到 body层级 3000/2995 同单票信号抽屉从底部滑出。四类详情各自排版选股系统给判决整句、理由、榜单决策系统给结论、把握度、原话、择时层两期限公司深度给质地档、估值、十视角计数、买方论点、失效条件、反方、催化、报告链接分段带彩色小标题技术面给立场加布林、多空布林线、SAR 三个指标块,点开时调 /api/research 取完整读数。
- 颜色按页面口径红涨绿跌:技术面看多用红、看空用绿;评价类(候选、质地好)用中性蓝,交你定用黄。
**动了哪些文件**
app/web/static/index.htmlCSS 加来源行与弹窗样式、模板卡面重构加弹窗、JS 加四个结论标签函数与弹窗开关、return 导出app/web/main.py/api/proposals 每条挂技术面立场 tech
**部署方式**
改了 Python 与页面make deploy 重建镜像加 force-recreate收盘后 155先经审批。
**真机判收**
未判收。两道页面守卫 ALL OK。开发机注入一条华测导航假提议起本地服务截图确认卡面四行结论、技术面弹窗三指标块、公司深度弹窗四段分组都渲染正确、不白屏。待 155 有真实提议时判收。
**还欠着什么**
合议行、候选芯片仍是工作包二。提议卡的技术面立场依赖工作包一的技术面映射(已上,未真机判收)。
--- ---
<!-- <!--
下一条节点从这里往下写,格式照抄上面: 下一条节点从这里往下写,格式照抄上面:

View File

@ -440,8 +440,18 @@ def api_export_publish_xlsx():
def api_proposals(status: str = Query("WAIT_USER"), limit: int = 100, def api_proposals(status: str = Query("WAIT_USER"), limit: int = 100,
include_archived: bool = Query(False)): include_archived: bool = Query(False)):
statuses = tuple(s for s in (status or "").split(",") if s) or ("WAIT_USER",) statuses = tuple(s for s in (status or "").split(",") if s) or ("WAIT_USER",)
return ok(pms_repo.list_proposals, statuses=statuses, limit=limit,
def _list():
rows = pms_repo.list_proposals(statuses=statuses, limit=limit,
include_archived=include_archived) include_archived=include_archived)
# 技术面立场 (2026-09-11): 每条提议挂 tech提议卡第四行结论用取不到不塌。
try:
from app.services import tech_service
tech_service.attach(rows)
except Exception as e: # noqa: BLE001
logger.warning("[提议] 技术面立场挂不上: %s", e)
return rows
return ok(_list)
@app.post("/api/proposals/{proposal_id}/decide") @app.post("/api/proposals/{proposal_id}/decide")

View File

@ -406,6 +406,26 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
padding:11px;font-size:14px;font-weight:600;cursor:pointer;} padding:11px;font-size:14px;font-weight:600;cursor:pointer;}
.login-btn:disabled{opacity:.6;cursor:default;} .login-btn:disabled{opacity:.6;cursor:default;}
.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 重构) ── */
.prop-src{margin:9px 0 4px;border-top:1px solid var(--hair);}
.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:hover{background:var(--surface-2);}
.prop-src-row .lbl{font-size:12px;color:var(--muted);width:52px;flex:none;}
.prop-src-row .sub{font-size:12px;color:var(--ink-2);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.prop-src-row .car{font-style:normal;color:var(--muted);flex:none;font-size:15px;}
.ptag{font-size:11.5px;padding:1px 8px;border-radius:6px;flex:none;font-weight:500;}
.ptag.up{background:var(--up-weak);color:var(--up);}
.ptag.down{background:var(--down-weak);color:var(--down);}
.ptag.accent{background:var(--accent-weak);color:var(--accent);}
.ptag.warn{background:var(--warn-weak);color:var(--warn);}
.ptag.neutral{background:var(--surface-2);color:var(--muted);}
.prop-sheet{position:fixed;left:0;right:0;bottom:0;z-index:3000;max-height:86vh;overflow:auto;
background:var(--surface);border-radius:16px 16px 0 0;box-shadow:0 -12px 40px rgba(17,17,16,.22);}
.prop-metric{display:flex;flex-direction:column;gap:7px;margin:8px 0;}
.prop-metric .m{background:var(--surface-2);border-radius:9px;padding:9px 11px;display:flex;align-items:center;gap:10px;}
.prop-metric .m .nm{font-size:12.5px;flex:1;} .prop-metric .m .dt{font-size:11px;color:var(--muted);}
.prop-badge{font-size:11px;font-family:ui-monospace,Menlo,monospace;background:var(--surface-2);padding:2px 7px;border-radius:5px;}
</style> </style>
</head> </head>
<body> <body>
@ -1103,33 +1123,30 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
<div class="st" v-if="propSizeMismatch(p)" style="color:#d8574a">{{ propSizeMismatch(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> <div class="st muted" v-if="propResized(p)">{{ propResized(p) }}</div>
<div class="st" v-if="(p.hard_numbers||{}).verdict"><span class="muted">选股系统:</span>{{ propVerdictLine(p) }}</div> <!-- 四行来源结论, 点开看完整依据 (2026-09-12 重构): 原先每类一整句平铺在卡面,
<div class="st" v-if="p.judge_reason"><span class="muted">决策系统:</span>{{ propJudgeShort(p) }}<span class="muted" v-if="(p.hard_numbers||{}).judge_conf!=null">(把握度 {{ (p.hard_numbers||{}).judge_conf }}</span></div> 公司深度那句最长 —— 现在卡面只留结论标签, 整句进弹窗。判定用的键一个没动。 -->
<div class="prop-src">
<!-- 公司深度只在真有报告时才占一行。判据用结构化的 company_review 而不是 <div class="prop-src-row" v-if="propPlanTag(p)" @click="openPropDetail(p,'plan')">
那句整句 —— 没报告时上游下发的是一句 21 字的占位, 实测 77 只票里 64 只 <span class="lbl">选股系统</span>
都是它, 占着位置又不说事。 --> <span class="ptag" :class="propPlanTag(p).cls">{{ propPlanTag(p).text }}</span>
<div class="st" v-if="(p.hard_numbers||{}).company_review"><span class="muted">公司深度:</span>{{ propText(p, 'company_review_text') }}</div> <span class="sub">{{ propPlanSub(p) }}</span><i class="car"></i>
</div>
<!-- 为什么选它。这三条一直算好放在 hard_numbers.reasons 里, 但模板从来没用过。 --> <div class="prop-src-row" v-if="propJudgeTag(p)" @click="openPropDetail(p,'judge')">
<div class="st muted" v-for="(x, ri) in propReasons(p)" :key="'r'+ri">{{ x }}</div> <span class="lbl">决策系统</span>
<span class="ptag" :class="propJudgeTag(p).cls">{{ propJudgeTag(p).text }}</span>
<div class="st muted clickable" @click="togglePropMore(p.proposal_id)"> <span class="sub" v-if="(p.hard_numbers||{}).judge_conf!=null">把握 {{ (p.hard_numbers||{}).judge_conf }}</span><i class="car"></i>
{{ propMoreOpen.has(p.proposal_id) ? '▾ 收起证据' : '▸ 展开证据(研报、安全边际、催化、定价、快讯、行业、择时层)' }} </div>
<div class="prop-src-row" v-if="propCompanyTag(p)" @click="openPropDetail(p,'company')">
<span class="lbl">公司深度</span>
<span class="ptag" :class="propCompanyTag(p).cls">{{ propCompanyTag(p).text }}</span>
<span class="sub" v-if="cr(p) && cr(p).valuation">估值{{ cr(p).valuation }}</span><i class="car"></i>
</div>
<div class="prop-src-row" v-if="propTechTag(p)" @click="openPropDetail(p,'tech')">
<span class="lbl">技术面</span>
<span class="ptag" :class="propTechTag(p).cls">{{ propTechTag(p).text }}</span>
<span class="sub">{{ propTechSub(p) }}</span><i class="car"></i>
</div>
</div> </div>
<template v-if="propMoreOpen.has(p.proposal_id)">
<div class="st" v-if="propLogic(p)"><span class="muted">研报说:</span>{{ propLogic(p) }}<span class="muted" v-if="propLogicMore(p)">(另有 {{ propLogicMore(p) }} 条)</span></div>
<div class="st muted" v-if="propText(p, 'valuation_text')">{{ propText(p, 'valuation_text') }}</div>
<div class="st muted" v-if="propText(p, 'events_text')">{{ propText(p, 'events_text') }}</div>
<div class="st muted" v-if="propText(p, 'pricing_text')">{{ propText(p, 'pricing_text') }}</div>
<div class="st muted" v-if="propText(p, 'news_text')">{{ propText(p, 'news_text') }}</div>
<div class="st muted" v-if="propText(p, 'industry_catalyst_text')">{{ propText(p, 'industry_catalyst_text') }}</div>
<div class="st" v-if="propText(p, 'company_gate')"><span class="muted">判决收敛:</span>{{ propText(p, 'company_gate') }}</div>
<div class="st" v-if="propText(p, 'advice_text')">{{ propText(p, 'advice_text') }}</div>
<div class="st" v-if="propHeads(p)"><span class="muted">择时层两个期限:</span>{{ propHeads(p) }}</div>
<div class="st muted">{{ propWhy(p) }}</div>
<div class="st muted" v-if="p.judge_reason && propJudgeShort(p) !== p.judge_reason">决策系统原话:{{ p.judge_reason }}</div>
</template>
<div style="margin-top:6px"> <div style="margin-top:6px">
<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>
@ -1925,6 +1942,77 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
</div> </div>
<!-- ═══ 提议来源详情弹窗 (2026-09-12): 点提议卡某个来源看它的完整依据。teleport 到 body,
层级 3000/2995 同单票信号抽屉, 盖住右栏浮层 ═══ -->
<teleport to="body">
<div v-if="propDetailOpen">
<div class="cs-scrim" @click="closePropDetail"></div>
<aside class="prop-sheet">
<div class="cs-h">
<span class="ttl">{{ nm((propDetailP||{}).ts_code) }}</span>
<span class="muted mono" style="font-size:11px">{{ (propDetailP||{}).ts_code }}</span>
<span class="ptag neutral">{{ PROP_SRC_LABEL[propDetailKind] }}</span>
<span class="spacer"></span>
<button class="iconbtn" @click="closePropDetail" title="关闭"></button>
</div>
<div class="cs-b" v-if="propDetailP">
<template v-if="propDetailKind==='plan'">
<div class="st" style="margin-bottom:8px">{{ propVerdictLine(propDetailP) }}</div>
<div class="cs-sect" v-if="propReasons(propDetailP).length">
<div class="h">为什么选它</div>
<div class="st" v-for="(x,ri) in propReasons(propDetailP)" :key="ri">· {{ x }}</div>
</div>
<div class="cs-sect"><div class="h">榜单</div><div class="st muted">{{ propWhy(propDetailP) }}</div></div>
</template>
<template v-else-if="propDetailKind==='judge'">
<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="muted" v-if="(propDetailP.hard_numbers||{}).judge_conf!=null">把握度 {{ (propDetailP.hard_numbers||{}).judge_conf }}</span>
</div>
<div class="st">{{ propDetailP.judge_reason || '决策系统没有留下原话。' }}</div>
<div class="cs-sect" v-if="propHeads(propDetailP)"><div class="h">择时层两个期限</div><div class="st muted">{{ propHeads(propDetailP) }}</div></div>
</template>
<template v-else-if="propDetailKind==='company'">
<div style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px" v-if="cr(propDetailP)">
<span class="ptag" :class="propCompanyTag(propDetailP).cls">质地{{ cr(propDetailP).overall }}</span>
<span class="ptag neutral" v-if="cr(propDetailP).valuation">估值{{ cr(propDetailP).valuation }}</span>
<span class="prop-badge" v-if="cr(propDetailP).grade_counts">十视角 好 {{ cr(propDetailP).grade_counts['好'] }} / 中 {{ cr(propDetailP).grade_counts['中'] }} / 差 {{ cr(propDetailP).grade_counts['差'] }}</span>
<span class="prop-badge" v-if="cr(propDetailP).confidence">置信 {{ cr(propDetailP).confidence }}</span>
</div>
<div class="cs-sect" v-if="cr(propDetailP) && cr(propDetailP).thesis"><div class="h" style="color:var(--down)">买方论点</div><div class="st">{{ cr(propDetailP).thesis }}</div></div>
<div class="cs-sect" v-if="cr(propDetailP) && cr(propDetailP).invalidation"><div class="h" style="color:var(--warn)">失效条件</div><div class="st">{{ cr(propDetailP).invalidation }}</div></div>
<div class="cs-sect" v-if="cr(propDetailP) && cr(propDetailP).antithesis"><div class="h" style="color:var(--up)">反方</div><div class="st">{{ cr(propDetailP).antithesis }}</div></div>
<div class="cs-sect" v-if="cr(propDetailP) && cr(propDetailP).catalyst"><div class="h">催化</div><div class="st muted">{{ cr(propDetailP).catalyst }}</div></div>
<div class="st" v-if="cr(propDetailP) && cr(propDetailP).report_url" style="margin-top:8px"><a :href="cr(propDetailP).report_url" target="_blank">查看完整报告 ↗</a></div>
<div class="st muted" v-if="!cr(propDetailP)">这只票没有公司深度评析。</div>
</template>
<template v-else-if="propDetailKind==='tech'">
<div class="muted" v-if="propTechErr">{{ propTechErr }}</div>
<div class="muted" v-else-if="!propTechFull">正在读技术面…</div>
<template v-else>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap">
<span class="ptag" :class="propTechTag(propDetailP).cls">{{ propTechFull.stance }}<template v-if="propTechFull.strength"> · {{ propTechFull.strength }}</template></span>
<span class="muted" v-if="propTechFull.phase">{{ propTechFull.phase }}</span>
<span class="ptag warn" v-if="propTechFull.choppy">震荡市</span>
<span class="spacer"></span>
<span class="muted" style="font-size:11px" v-if="propTechFull.data_date">读数日 {{ propTechFull.data_date }}</span>
</div>
<div class="prop-metric" v-if="propTechFull.latest">
<div class="m"><span class="nm">布林线<template v-if="(propTechFull.latest.boll||{}).bw_pct!=null"><span class="dt"> · 带宽 {{ (propTechFull.latest.boll||{}).bw_pct }}%</span></template><template v-if="(propTechFull.latest.boll||{}).squeeze"><span class="dt"> · 收口</span></template></span><span class="ptag accent" v-if="(propTechFull.latest.boll||{}).state">{{ (propTechFull.latest.boll||{}).state }}</span></div>
<div class="m"><span class="nm">多空布林线</span><span class="ptag accent" v-if="(propTechFull.latest.bbi||{}).state">{{ (propTechFull.latest.bbi||{}).state }}</span></div>
<div class="m"><span class="nm">SAR 跟踪止损<template v-if="(propTechFull.latest.sar||{}).value!=null"><span class="dt"> · 止损位 {{ (propTechFull.latest.sar||{}).value }}</span></template><template v-if="propTechFull.sar_flip_days!=null"><span class="dt"> · 翻向 {{ propTechFull.sar_flip_days }} 天</span></template></span><span class="ptag" :class="propTechTag(propDetailP).cls" v-if="(propTechFull.latest.sar||{}).side">{{ (propTechFull.latest.sar||{}).side }}</span></div>
</div>
<div class="st muted" v-if="propTechFull.reason">{{ propTechFull.reason }}</div>
</template>
</template>
</div>
</aside>
</div>
</teleport>
<!-- 运维抽屉 --> <!-- 运维抽屉 -->
<!-- ═══ 单票信号抽屉 (2026-09-09): 点一只票看它今天的全部信号 ═══ <!-- ═══ 单票信号抽屉 (2026-09-09): 点一只票看它今天的全部信号 ═══
不用 el-drawer 是因为弹层库的层级从 2000 起自增, 信号栏浮层写死 2010、遮罩 2005, 不用 el-drawer 是因为弹层库的层级从 2000 起自增, 信号栏浮层写死 2010、遮罩 2005,
@ -2891,6 +2979,59 @@ createApp({
const r = String(p.judge_reason || '').trim(); const r = String(p.judge_reason || '').trim();
return r.length > 60 ? r.slice(0, 60) + '…' : r; return r.length > 60 ? r.slice(0, 60) + '…' : r;
} }
// ── 2026-09-12 提议卡重构: 卡面每个来源只留结论标签, 整句进弹窗 ──
// 标签返回 {text, cls}; cls 走 .ptag 取色 —— 技术面看多看空按页面口径红涨绿跌 (up/down),
// 其余评价类用中性色 (accent 蓝 / warn 黄 / neutral 灰), 不掺进涨跌红绿。
const cr = (p) => (p && p.hard_numbers && p.hard_numbers.company_review) || null;
const propPlanTag = (p) => {
const v = String((p.hard_numbers || {}).verdict || '').trim();
if (!v) return null;
return { text: v, cls: v === '候选' ? 'accent' : (v === '关注' ? 'warn' : 'neutral') };
};
const propPlanSub = (p) => {
const h = p.hard_numbers || {};
if (h.upside != null) return '空间 ' + (h.upside > 0 ? '+' : '') + (h.upside * 100).toFixed(0) + '%';
return propReasons(p)[0] || '';
};
const propJudgeTag = (p) => {
if (!p.judge_reason && !p.judge_verdict) return null;
return String(p.judge_verdict || '').trim() === 'PASS'
? { text: '通过', cls: 'accent' } : { text: '交你定', cls: 'warn' };
};
const propCompanyTag = (p) => {
const c = cr(p);
if (!c || !c.overall) return null;
return { text: '质地' + c.overall, cls: c.overall === '好' ? 'accent' : (c.overall === '差' ? 'warn' : 'neutral') };
};
const propTechTag = (p) => {
const t = p.tech;
if (!t || !t.stance) return null;
return { text: t.stance, cls: t.stance === '看多' ? 'up' : (t.stance === '看空' ? 'down' : 'neutral') };
};
const propTechSub = (p) => {
const t = p.tech || {}, bits = [];
if (t.phase) bits.push(t.phase);
if (t.sar_side) bits.push('SAR ' + t.sar_side);
return bits.join(' · ');
};
// 弹窗: 点某来源看它的完整依据。teleport 到 body, 层级同单票信号抽屉 (3000/2995) 盖住右栏。
const propDetailOpen = ref(false), propDetailKind = ref(''), propDetailP = ref(null);
const propTechFull = ref(null), propTechErr = ref('');
const PROP_SRC_LABEL = { plan: '选股系统', judge: '决策系统', company: '公司深度', tech: '技术面' };
function openPropDetail(p, kind) {
propDetailP.value = p; propDetailKind.value = kind; propDetailOpen.value = true;
propTechFull.value = null; propTechErr.value = '';
if (kind === 'tech') loadPropTech(p.ts_code);
}
function closePropDetail() { propDetailOpen.value = false; }
async function loadPropTech(code) {
const d = await call('get', '/api/research/' + encodeURIComponent(code));
if (!propDetailOpen.value || propDetailKind.value !== 'tech') return;
if (d && d.ok === false) { propTechErr.value = d.error || '读不到技术面'; return; }
const data = (d && d.data) ? d.data : d;
propTechFull.value = (data && data.tech) || null;
if (!propTechFull.value) propTechErr.value = (data && data.tech_note) || '这只票没有技术面读数';
}
// 硬数字里由上游写好的整句 (安全边际 / 催化事件 / 定价状态): 原样显示, 不是字符串就不显示。 // 硬数字里由上游写好的整句 (安全边际 / 催化事件 / 定价状态): 原样显示, 不是字符串就不显示。
function propText(p, key) { function propText(p, key) {
const v = (p.hard_numbers || {})[key]; const v = (p.hard_numbers || {})[key];
@ -4085,6 +4226,10 @@ createApp({
// 2026-09-10 提议卡重排新增 // 2026-09-10 提议卡重排新增
propScale, propSizePct, propAdviceTier, propSizeMismatch, propResized, propJudgeShort, propScale, propSizePct, propAdviceTier, propSizeMismatch, propResized, propJudgeShort,
propMoreOpen, togglePropMore, propMoreOpen, togglePropMore,
// 2026-09-12 提议卡重构: 来源结论标签 + 详情弹窗
cr, propPlanTag, propPlanSub, propJudgeTag, propCompanyTag, propTechTag, propTechSub,
propDetailOpen, propDetailKind, propDetailP, propTechFull, propTechErr, PROP_SRC_LABEL,
openPropDetail, closePropDetail,
pHaltBuy, pResumeBuy, pHaltAll, pResumeAll, pReduce, pIncrease, pLiquidate, pSectorExit, pHaltBuy, pResumeBuy, pHaltAll, pResumeAll, pReduce, pIncrease, pLiquidate, pSectorExit,
pctOf, tgtPos, posMoveValid, posMovePreview, doPosMove, heldSectors, secSel, doSectorExit, pctOf, tgtPos, posMoveValid, posMovePreview, doPosMove, heldSectors, secSel, doSectorExit,
pmap, pval, dcaOn, sumPosition, sumDca, sumAutonomy, pmap, pval, dcaOn, sumPosition, sumDca, sumAutonomy,