技术面接入工作包一(页面):顶栏新鲜度芯片、持仓研究面列、单票抽屉技术面节
- 顶栏加技术面新鲜度芯片(调 /api/tech/status,绿=映射新鲜/黄=读不到或过期/灰=没开) - 我的持仓加「研究面」列显示技术面立场,点击进单票抽屉 - 单票抽屉顶部加「研究面·技术面」节(调 /api/research),展示立场/相位/理由/三指标读数 - /api/positions 每行挂 tech 立场;设置页自动列出 11 个 PMS_TECH_* 参数(无需另做设置卡) - 两道页面守卫 ALL OK;本地起服务冒烟确认不白屏、顶栏芯片与研究面列渲染正确 候选技术芯片、管理视图修显示错误并入工作包二一起铺。未真机判收。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
0d3984582c
commit
d89580240b
|
|
@ -1341,7 +1341,7 @@ PMS_JUDGE_ONLINE_EFFORT=low / PMS_JUDGE_ONLINE_MAX_TOKENS=16384 / PMS_JUDGE_REPA
|
||||||
未判收。开发机临时 venv 跑全量单测见 ALL SUITES PASS(含新增 batch27 二十七例)。接口字段已只读探活 188 坐实。待 155 部署后按方案第七节判收:make test、pull_and_map 行数五千上下、映射只数等于持仓加候选、status、第一个交易日 08:4x 映射写入时刻与读数日。
|
未判收。开发机临时 venv 跑全量单测见 ALL SUITES PASS(含新增 batch27 二十七例)。接口字段已只读探活 188 坐实。待 155 部署后按方案第七节判收:make test、pull_and_map 行数五千上下、映射只数等于持仓加候选、status、第一个交易日 08:4x 映射写入时刻与读数日。
|
||||||
|
|
||||||
**还欠着什么**
|
**还欠着什么**
|
||||||
1. 页面七处建议并入工作包二,与三源合议的提议卡芯片、基本面质地芯片一次铺齐,避免先做技术面芯片再返工重排提议卡。
|
1. 页面已补技术面这一路的核心三项:顶栏新鲜度芯片、我的持仓「研究面」列、单票抽屉「研究面·技术面」节;设置页自动列出十一个 PMS_TECH_* 参数(无需另做设置卡)。持仓接口每行挂 tech 立场。开发机起本地服务冒烟确认不白屏、芯片与列渲染正确。剩下与三源合议耦合的两项——候选栏技术芯片、管理视图持仓总览修显示错误——并入工作包二,与提议卡三芯片、基本面质地芯片一次铺齐,避免返工。
|
||||||
2. 工作包二(三源合议与仓位矩阵)、工作包三(离场纪律)、工作包四(选股打分,另一仓库单独审批)、工作包五(README 调度总表补 13 位等文档)未做。
|
2. 工作包二(三源合议与仓位矩阵)、工作包三(离场纪律)、工作包四(选股打分,另一仓库单独审批)、工作包五(README 调度总表补 13 位等文档)未做。
|
||||||
3. BIONIC_PMS_INTERFACE.md 需补技术面只读接口一节(本次接口探活的字段事实源)。
|
3. BIONIC_PMS_INTERFACE.md 需补技术面只读接口一节(本次接口探活的字段事实源)。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -381,6 +381,12 @@ def api_positions():
|
||||||
logic_state_service.decorate_positions(v["positions"])
|
logic_state_service.decorate_positions(v["positions"])
|
||||||
except Exception as e: # noqa: BLE001
|
except Exception as e: # noqa: BLE001
|
||||||
logger.warning("[持仓] 入场论点与逻辑状态两栏取不到: %s", e)
|
logger.warning("[持仓] 入场论点与逻辑状态两栏取不到: %s", e)
|
||||||
|
# 技术面立场 (2026-09-11): 每行挂 tech(看多/看空/中性/无读数),只是显示,取不到不塌。
|
||||||
|
try:
|
||||||
|
from app.services import tech_service
|
||||||
|
tech_service.attach(v["positions"])
|
||||||
|
except Exception as e: # noqa: BLE001
|
||||||
|
logger.warning("[持仓] 技术面立场挂不上: %s", e)
|
||||||
return {"ok": True, **v, "stock_params": sp}
|
return {"ok": True, **v, "stock_params": sp}
|
||||||
return ok(_view)
|
return ok(_view)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -432,6 +432,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<span class="chip hd-exec" :class="ov.exec_halt?'warn':'ok'"><span class="dot"></span>执行 {{ ov.exec_halt ? '已暂停':'正常' }}</span>
|
<span class="chip hd-exec" :class="ov.exec_halt?'warn':'ok'"><span class="dot"></span>执行 {{ ov.exec_halt ? '已暂停':'正常' }}</span>
|
||||||
<span class="chip" :class="ov.autonomy==='off'?'warn':'ok'"><span class="dot"></span>自主 {{ tx('autonomy', ov.autonomy) }}<span class="help" data-tip="系统能自己做主到什么程度。全自动=各项检查通过就直接下单;只提议=先在「等我拍板」问过你;暂停=系统不自己动。">?</span></span>
|
<span class="chip" :class="ov.autonomy==='off'?'warn':'ok'"><span class="dot"></span>自主 {{ tx('autonomy', ov.autonomy) }}<span class="help" data-tip="系统能自己做主到什么程度。全自动=各项检查通过就直接下单;只提议=先在「等我拍板」问过你;暂停=系统不自己动。">?</span></span>
|
||||||
<span class="chip hd-plan" :class="planStatus.ok===false?'warn':'ok'"><span class="dot"></span>选股计划<span class="hd-plan-d"> {{ planStatus.ok===false ? '读不到' : (planStatus.date || '-') }}</span></span>
|
<span class="chip hd-plan" :class="planStatus.ok===false?'warn':'ok'"><span class="dot"></span>选股计划<span class="hd-plan-d"> {{ planStatus.ok===false ? '读不到' : (planStatus.date || '-') }}</span></span>
|
||||||
|
<span class="chip hd-tech" :class="techChipCls"><span class="dot"></span>技术面<span class="hd-plan-d"> {{ techChipText }}</span><span class="help" data-tip="每晚算的技术面读数(布林线、多空布林线、SAR)是哪天的。绿=今天的映射新鲜;黄=读不到或已过期;灰=技术面这一层没开。">?</span></span>
|
||||||
<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">
|
||||||
|
|
@ -926,6 +927,17 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<div class="muted">{{ pct(s.row.cushion_pct) }}</div>
|
<div class="muted">{{ pct(s.row.cushion_pct) }}</div>
|
||||||
<div class="muted" v-if="s.row.neg_cushion_days>0">连亏 {{ s.row.neg_cushion_days }} 日</div></template>
|
<div class="muted" v-if="s.row.neg_cushion_days>0">连亏 {{ s.row.neg_cushion_days }} 日</div></template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="研究面" width="92">
|
||||||
|
<template #default="s">
|
||||||
|
<template v-if="s.row.tech">
|
||||||
|
<b :class="techStanceCls(s.row.tech.stance)" style="cursor:pointer;text-decoration:underline dotted"
|
||||||
|
@click.stop="openCodeSignals(s.row.ts_code)"
|
||||||
|
:title="'技术面:' + s.row.tech.stance + '(' + (s.row.tech.phase||'') + ')\n' + (s.row.tech.reason||'') + '\n点一下看研究面'">{{ s.row.tech.stance }}</b>
|
||||||
|
<div class="muted" style="font-size:11px" v-if="s.row.tech.phase">{{ s.row.tech.phase }}</div>
|
||||||
|
</template>
|
||||||
|
<span class="muted" v-else>无读数</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="今天的信号" width="108">
|
<el-table-column label="今天的信号" width="108">
|
||||||
<template #default="s">
|
<template #default="s">
|
||||||
<template v-if="heldSigOf(s.row.ts_code).length">
|
<template v-if="heldSigOf(s.row.ts_code).length">
|
||||||
|
|
@ -1947,6 +1959,30 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
下面列出的不是这只票的全部,是能读到的那部分。
|
下面列出的不是这只票的全部,是能读到的那部分。
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="cs-sect" v-if="techFeed || techFeedErr">
|
||||||
|
<div class="h">研究面 · 技术面<span class="muted" style="font-weight:400" v-if="techFeed && techFeed.tech"> · 读数日 {{ techFeed.tech.data_date }}</span></div>
|
||||||
|
<div class="muted" v-if="techFeedErr">读不到技术面:{{ techFeedErr }}</div>
|
||||||
|
<div class="muted" v-else-if="!techFeed || !techFeed.tech">{{ (techFeed && techFeed.tech_note) || '这只票没有技术面读数' }}</div>
|
||||||
|
<template v-else>
|
||||||
|
<div class="cs-row">
|
||||||
|
<span class="s">立场</span>
|
||||||
|
<b :class="techStanceCls(techFeed.tech.stance)">{{ techFeed.tech.stance }}<span v-if="techFeed.tech.strength"> · {{ techFeed.tech.strength }}</span></b>
|
||||||
|
<span v-if="techFeed.tech.phase">{{ techFeed.tech.phase }}</span>
|
||||||
|
<span class="ab-stale" v-if="techFeed.tech.choppy">震荡市</span>
|
||||||
|
<div style="flex-basis:100%;font-size:12px;line-height:1.45" class="muted" v-if="techFeed.tech.reason">{{ techFeed.tech.reason }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="cs-row" v-if="techFeed.tech.latest">
|
||||||
|
<span class="s">布林</span><span>{{ (techFeed.tech.latest.boll||{}).state || '—' }}</span>
|
||||||
|
<span class="muted" v-if="(techFeed.tech.latest.boll||{}).squeeze">收口</span>
|
||||||
|
<span class="s">多空布林线</span><span>{{ (techFeed.tech.latest.bbi||{}).state || '—' }}</span>
|
||||||
|
<span class="s">SAR</span>
|
||||||
|
<b :class="techFeed.tech.sar_side==='多'?'clr-up':'clr-down'">{{ techFeed.tech.sar_side || '—' }}</b>
|
||||||
|
<span class="muted" v-if="techFeed.tech.sar_value!=null">止损位 <span class="mono">{{ techFeed.tech.sar_value }}</span></span>
|
||||||
|
<span class="muted" v-if="techFeed.tech.sar_flip_days!=null">翻向 {{ techFeed.tech.sar_flip_days }} 天</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="cs-sect">
|
<div class="cs-sect">
|
||||||
<div class="h">时间线 · {{ csTimeline.length }} 条</div>
|
<div class="h">时间线 · {{ csTimeline.length }} 条</div>
|
||||||
<div class="muted" v-if="!csTimeline.length && !csLoading">
|
<div class="muted" v-if="!csTimeline.length && !csLoading">
|
||||||
|
|
@ -2326,6 +2362,26 @@ createApp({
|
||||||
const planStatus = computed(() => planRaw.value.status || {});
|
const planStatus = computed(() => planRaw.value.status || {});
|
||||||
const planRows = computed(() => planRaw.value.rows || []);
|
const planRows = computed(() => planRaw.value.rows || []);
|
||||||
const planCand = computed(() => planRaw.value.candidates_raw || null);
|
const planCand = computed(() => planRaw.value.candidates_raw || null);
|
||||||
|
// 技术面模块状态 (2026-09-11): 顶栏新鲜度芯片。只读 /api/tech/status, 交易员也看得到。
|
||||||
|
const techStatus = ref({});
|
||||||
|
const techChipText = computed(() => {
|
||||||
|
const t = techStatus.value || {};
|
||||||
|
if (t.enabled === false) return '关';
|
||||||
|
const dd = t.data_date;
|
||||||
|
if (!dd) return '无';
|
||||||
|
const s = String(dd);
|
||||||
|
return s.length === 8 ? s.slice(4, 6) + '-' + s.slice(6, 8) : s;
|
||||||
|
});
|
||||||
|
const techChipCls = computed(() => {
|
||||||
|
const t = techStatus.value || {};
|
||||||
|
if (t.enabled === false) return 'idle';
|
||||||
|
if (t.error || !t.data_date) return 'warn';
|
||||||
|
return (t.map || {}).fresh ? 'ok' : 'warn';
|
||||||
|
});
|
||||||
|
const loadTech = async () => {
|
||||||
|
const d = await call('get', '/api/tech/status');
|
||||||
|
techStatus.value = (d && d.ok) ? (d.data || {}) : { error: (d || {}).error || '读不到' };
|
||||||
|
};
|
||||||
// 榜单变化 (PMS 自算 —— 上游 changes 字段恒为 null)。另起一个接口, 不塞进 /api/upstream/plan:
|
// 榜单变化 (PMS 自算 —— 上游 changes 字段恒为 null)。另起一个接口, 不塞进 /api/upstream/plan:
|
||||||
// 它要读库, 而计划预览要读上游, 两者的故障域不同, 混在一起会互相拖垮。
|
// 它要读库, 而计划预览要读上游, 两者的故障域不同, 混在一起会互相拖垮。
|
||||||
const chgRaw = ref({}), chgLoading = ref(false), chgTab = ref('held');
|
const chgRaw = ref({}), chgLoading = ref(false), chgTab = ref('held');
|
||||||
|
|
@ -3286,6 +3342,17 @@ createApp({
|
||||||
// 补全失败就保留快照并挂一条黄条, 不清空 —— 有一半总比没有强。
|
// 补全失败就保留快照并挂一条黄条, 不清空 —— 有一半总比没有强。
|
||||||
const csOpen = ref(false), csCode = ref(''), csLoading = ref(false),
|
const csOpen = ref(false), csCode = ref(''), csLoading = ref(false),
|
||||||
csErr = ref(''), csData = ref(null), csFromSnap = ref(false);
|
csErr = ref(''), csData = ref(null), csFromSnap = ref(false);
|
||||||
|
// 研究面·技术面 (2026-09-11): 单票抽屉顶部一节, 点票时顺带调 /api/research 拉回。
|
||||||
|
const techFeed = ref(null), techFeedErr = ref('');
|
||||||
|
const techStanceCls = (s) => s === '看多' ? 'clr-up' : (s === '看空' ? 'clr-down' : 'muted');
|
||||||
|
async function csLoadResearch() {
|
||||||
|
const code = csCode.value;
|
||||||
|
techFeed.value = null; techFeedErr.value = '';
|
||||||
|
const d = await call('get', '/api/research/' + encodeURIComponent(code));
|
||||||
|
if (csCode.value !== code) return; // 期间换了票, 丢弃这份回包
|
||||||
|
if (d && d.ok === false) { techFeedErr.value = d.error || '读不到研究面'; return; }
|
||||||
|
techFeed.value = (d && d.data) ? d.data : d;
|
||||||
|
}
|
||||||
function _csSnapRows(code) {
|
function _csSnapRows(code) {
|
||||||
const k = code6(code), out = [];
|
const k = code6(code), out = [];
|
||||||
const push = (src, label, r, extra) => out.push(Object.assign(
|
const push = (src, label, r, extra) => out.push(Object.assign(
|
||||||
|
|
@ -3319,6 +3386,7 @@ createApp({
|
||||||
csOpen.value = true;
|
csOpen.value = true;
|
||||||
csLoadFull();
|
csLoadFull();
|
||||||
csLoadLedger();
|
csLoadLedger();
|
||||||
|
csLoadResearch();
|
||||||
}
|
}
|
||||||
function closeCodeSignals() { csOpen.value = false; }
|
function closeCodeSignals() { csOpen.value = false; }
|
||||||
async function csLoadFull() {
|
async function csLoadFull() {
|
||||||
|
|
@ -3500,7 +3568,7 @@ createApp({
|
||||||
// (通道告警对交易员改由 dispatch-mode 里的 channel 兜底, 见 wsc 的注释)
|
// (通道告警对交易员改由 dispatch-mode 里的 channel 兜底, 见 wsc 的注释)
|
||||||
(isAdmin.value ? loadWs() : Promise.resolve()),
|
(isAdmin.value ? loadWs() : Promise.resolve()),
|
||||||
loadStrategies(), loadOpLog(), loadOpenScan(), loadUpstream(),
|
loadStrategies(), loadOpLog(), loadOpenScan(), loadUpstream(),
|
||||||
loadMacro()]);
|
loadMacro(), loadTech()]);
|
||||||
await loadNames();
|
await loadNames();
|
||||||
lastRefresh.value = new Date().toTimeString().slice(0, 8);
|
lastRefresh.value = new Date().toTimeString().slice(0, 8);
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|
@ -4003,6 +4071,7 @@ createApp({
|
||||||
cancelCmd, replan, decide, ops, loadSchema, openReport, cancelIns,
|
cancelCmd, replan, decide, ops, loadSchema, openReport, cancelIns,
|
||||||
planDrawer, planLoading, planStatus, planRows, planCand, planResult,
|
planDrawer, planLoading, planStatus, planRows, planCand, planResult,
|
||||||
loadPlan, openPlan, refreshPlan,
|
loadPlan, openPlan, refreshPlan,
|
||||||
|
techStatus, techChipText, techChipCls,
|
||||||
chgRaw, chgSt, chgLoading, chgTab, chg, chgHeld, chgLog, chgRevised, chgWatch,
|
chgRaw, chgSt, chgLoading, chgTab, chg, chgHeld, chgLog, chgRevised, chgWatch,
|
||||||
loadChanges,
|
loadChanges,
|
||||||
mode, tx, nm, fullName, nameMap, attention, heldPositions, traderParams,
|
mode, tx, nm, fullName, nameMap, attention, heldPositions, traderParams,
|
||||||
|
|
@ -4031,6 +4100,7 @@ createApp({
|
||||||
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,
|
||||||
openCodeSignals, closeCodeSignals, csLoadFull,
|
openCodeSignals, closeCodeSignals, csLoadFull,
|
||||||
|
techFeed, techFeedErr, techStanceCls,
|
||||||
macro, macroGateOn, macroUnavailable, mzLabel, mzColor, mzBarStyle,
|
macro, macroGateOn, macroUnavailable, mzLabel, mzColor, mzBarStyle,
|
||||||
loadMacro, macroScan, macroAdopt,
|
loadMacro, macroScan, macroAdopt,
|
||||||
mzSig, mzAdvicePending, mzChipCls, mzChipText, macroChipClick,
|
mzSig, mzAdvicePending, mzChipCls, mzChipText, macroChipClick,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue