From 50a7be1f590b1cedf2d34c139df1858aa7123717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=A5=E5=BE=AE?= Date: Thu, 9 Jul 2026 08:32:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8A=9F=E8=83=BD=E6=A0=91=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0"=E8=AF=B4=E6=98=8E"=E5=88=97(64=E9=A1=B9=E6=8F=8F?= =?UTF-8?q?=E8=BF=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/mofin_health.py | 95 +++++++++++++++++++++++++++++++++++++++- static/mofin_health.html | 29 ++++++------ 2 files changed, 110 insertions(+), 14 deletions(-) diff --git a/scripts/mofin_health.py b/scripts/mofin_health.py index d75b4189..21b943e3 100644 --- a/scripts/mofin_health.py +++ b/scripts/mofin_health.py @@ -157,6 +157,92 @@ def match_cron(cron_jobs, name_keywords): deduped.append(j) return deduped + +# ── 功能树描述 ── +NODE_DESC = { + "数据采集": "从腾讯/东财/小果采集原始行情、新闻、资金流数据", + "策略分析": "策略评估、新鲜度检查、重评和成长分析", + "推荐推送": "生成简报、推荐并推送到XMPP", + "风险监控": "宏观风险信号、跨市场背离检测", + "自检/审计": "系统健康检查、监控采集、审计", + "执行/修复": "自愈系统、门禁跟进、清理修复", + "持仓复查": "持仓基本面复查和策略复盘", + "信号消费": "消费小果情感分析和宏观风险信号", + "系统服务": "系统维护(如DB真空整理)", + "持仓监控": "特定持仓(300308/芯碁微装)盘中监控", + "市场快照": "每10分钟采集全市场板块和指数快照", + "宏观新闻": "采集宏观新闻和财经资讯", + "价格监控": "每2分钟刷新持仓/自选实时价格→写入live_prices", + "小果扫描": "小果独立扫描潜在机会", + "资金流采集": "盘中采集板块资金流向", + "宏观上下文刷新": "刷新大盘指数/市场情绪", + "策略重评": "价格偏离买入区或策略过期时自动重评", + "持仓自选新鲜度检查": "检查策略是否过期或价格严重偏离", + "自选买入区提醒": "自选进入买入区时推送提醒", + "策略评估": "每日/每周策略效果评估", + "分支自成长": "策略分支探索和剪枝", + "元自成长": "系统元层级自我进化", + "MoFin盘前中监控": "上午盘中实时监控+推送", + "MoFin午后监控": "下午盘中实时监控+推送", + "cron报告推XMPP": "cron报告通过XMPP推送到手机", + "开盘简报": "每日开盘前市场简报", + "收盘简报": "每日收盘后市场简报", + "市场精选推荐": "每日全市场潜力股精推", + "宏观风险扫描": "从新闻中检测系统性风险", + "宏观风险信号消费": "消费宏观风险信号并生成建议", + "跨市场背离检测": "检测A股/港股/美股指数背离", + "系统全局审计": "7维度系统全面审计", + "全局cron健康监控": "监控所有cron的运行状态", + "重评管道审计": "审计策略重评管道完整性", + "健康监控数据采集": "采集健康数据供Dashboard展示", + "自愈执行器": "每10分钟自动处理TODO列表", + "策略质量门禁": "新策略必须通过9维验证才能写入", + "自选自动清理": "开盘前清理过期自选数据", + "建议对账": "每周对账校验建议准确性", + "持仓基本面复查": "每周持仓基本面深度复查", + "策略复盘": "每日策略执行复盘", + "小果情感分析": "收盘后对持仓/自选做新闻情感分析", + "宏观风险信号消费-盘中": "盘中消费宏观风险信号", + "小果市场筛选": "全市场扫描值得关注的板块和个股", + "芯碁微装": "芯碁微装午后价格监控", + "300308": "300308午后紧盯+入场信号监控", + "硬编码扫描": "扫描脚本中的硬编码参数", + "系统体检": "开盘前系统全面体检", + "盘中自检": "盘中高频自检", + "记忆守卫": "每日记忆清理和优化", + "数据治理": "每周数据清理和归档", + "自选股自动重评": "周末自动重评自选股策略", + "多周期缓存": "刷新MA5/MA20/MA60等技术指标缓存", + "数据同步": "同步数据到Dashboard", + "盘前热点扫描": "盘前扫描市场热点", + "宏观新闻采集": "采集宏观新闻", + "宏观新闻采集-周末": "周末宏观新闻采集", + "state.db真空整理": "DB真空整理维护", + "分支剪枝-每日": "修剪已失效的策略分支", + "自选股自动重评-周末": "周末批量重评自选股策略", + "系统健康检查-开盘前": "开盘前检查所有核心组件是否正常", + "多周期缓存刷新-开盘前": "开盘前刷新技术指标缓存", + "MoFin 系统常规体检-开盘前": "开盘前8:00全面系统体检", + "开盘前钉对钉验证": "开盘前15项验证(脚本同步/DB完整性/资产公式)", + "cron-推XMPP中继": "将cron输出通过XMPP中继推送", + "小果信号消费-盘中": "盘中消费小果扫描信号", + "硬编码扫描-每日": "扫描脚本中的硬编码参数", + "盘中自检-高频": "每15分钟盘中自检", + "数据治理-每周": "每周数据治理", + "记忆守卫-每日": "每日记忆优化", + "300308入场信号紧盯": "300308入场信号(13:00-14:00)", + "300308午后紧盯": "300308午后监控(13:00-15:00含止损)", + "多周期缓存刷新-盘中": "盘中刷新技术指标缓存", + "知识萃取-盘后": "盘后从分析报告中萃取可复用知识", + "区间维护": "每30分钟维护买入区", + "知微洞察生成": "生成每日市场洞察(15:35)", + "小果市场筛选-全市场": "小果筛选全市场关注板块", + "数据同步-dashboard": "同步数据到Dashboard", + "state.db真空整理-每周": "每周DB真空整理", + "未分类": "未被规则匹配的cron自动归入此", +} + + def build_feature_tree(cron_jobs, db_stats): # 硬编码分类规则:标签→匹配关键词 rules = { @@ -228,6 +314,10 @@ def build_feature_tree(cron_jobs, db_stats): def attach_pipes(node, parent_cat=None): nonlocal matched_names label = node.get("label", "") + # 附加描述(自动带脚本名的节点去掉括号内容匹配) + desc_key = label.split(" (")[0] if " (" in label else label + if desc_key in NODE_DESC: + node["desc"] = NODE_DESC[desc_key] keywords = rules.get(label) pipes = [] if keywords: @@ -251,8 +341,11 @@ def build_feature_tree(cron_jobs, db_stats): attach_pipes(c, parent_cat or label) def make_cron_node(j): + name = j.get("name", "?") + desc_key = name.split(" (")[0] if " (" in name else name return { - "label": f"{j.get('name','?')} ({j.get('script','LLM')})", + "label": f"{name} ({j.get('script','LLM')})", + "desc": NODE_DESC.get(desc_key, ""), "status": j.get("last_status", "unknown"), "pipes": [{ "name": j.get("name", ""), diff --git a/static/mofin_health.html b/static/mofin_health.html index 4b00f466..912d88b6 100644 --- a/static/mofin_health.html +++ b/static/mofin_health.html @@ -68,7 +68,7 @@ function switchTab(idx) { function renderFeatureTree(tree) { let grp = (function(){let i=0;return function(){return 'ftg-'+(++i);}})(); - let html = ''; + let html = '
功能模块来源类型调度状态最后运行
'; function walk(node, depth, groupId) { const indent = 'padding-left:'+(depth*20+8)+'px'; @@ -80,8 +80,10 @@ function renderFeatureTree(tree) { if (isCat) { // 分类节点——生成一个可折叠组 const g = grp(); + const desc = node.desc || ''; html += ``; html += ``; + html += ``; html += ``; // 子节点都带上这个组的data-sub node.children.forEach(c => walk(c, depth+1, g)); @@ -89,18 +91,19 @@ function renderFeatureTree(tree) { } if (pipes.length > 0) { - pipes.forEach((p, i) => { - const indent2 = i===0 ? indent : 'padding-left:'+(depth*20+32)+'px'; - const tagCls = p.status==='ok'?'ok':p.status==='error'?'error':'warn'; - const pBadge = p.profile==='position-analyst' ? '📋' : '📦'; - // 子节点显式带data-group - const groupAttr = groupId ? `data-group="${groupId}"` : ''; - if (i===0) { - html += ``; - } else { - html += ``; - } - html += ``; + if (pipes.length > 0) { + pipes.forEach((p, i) => { + const indent2 = i===0 ? indent : 'padding-left:'+(depth*20+32)+'px'; + const tagCls = p.status==='ok'?'ok':p.status==='error'?'error':'warn'; + const pBadge = p.profile==='position-analyst' ? '📋' : '📦'; + const descText = i===0 ? (node.desc || '') : ''; + const groupAttr = groupId ? `data-group="${groupId}"` : ''; + if (i===0) { + html += ``; + } else { + html += ``; + } + html += ``; html += ``; html += ``; html += ``;
功能模块说明来源类型调度状态最后运行
${node.label}${desc}---${cls}-
${node.label}
${p.name||p.script||'LLM'}${pBadge} ${p.profile||'?'}
${node.label}
${p.name||p.script||'LLM'}${descText}${p.type||'LLM'}${p.schedule||'-'}${p.status}