docs: 部署方案补充(管道修复实践+deploy_guard机制+系统彻查报告)

This commit is contained in:
hmo
2026-08-10 14:37:21 +08:00
parent b9c68a83a7
commit 7cdaa41f6f
4 changed files with 859 additions and 0 deletions
@@ -0,0 +1,412 @@
{
"meta": {
"version": "1.0",
"last_full_scan": "2026-08-10T08:00:45.922036",
"auto_discovery_enabled": true,
"description": "MoFin 系统常规体检清单。新增组件会自动加入,修改/修复后手动或自动调整。"
},
"categories": [
{
"id": "infrastructure",
"name": "基础设施层",
"order": 1,
"items": [
{
"id": "infra-xmpp-zhiwei",
"description": "知微 XMPP Bot 运行中",
"check": "systemctl:xmpp-zhiwei.service",
"expected": "active",
"severity": "critical"
},
{
"id": "infra-xmpp-xiaoguo",
"description": "小果 XMPP Bot 运行中",
"check": "systemctl:xmpp-xiaoguo.service",
"expected": "active",
"severity": "critical"
},
{
"id": "infra-gateway-zhiwei",
"description": "知微 Gateway :8643 监听中",
"check": "port:8643",
"expected": "listening",
"severity": "critical"
},
{
"id": "infra-gateway-xiaoguo",
"description": "小果 Gateway :8645 监听中",
"check": "port:8645",
"expected": "listening",
"severity": "critical"
},
{
"id": "infra-gateway-default",
"description": "默认 Gateway :8642 监听中",
"check": "port:8642",
"expected": "listening",
"severity": "high"
},
{
"id": "infra-dashboard",
"description": "Dashboard :8899 运行中",
"check": "systemctl:mofin-dashboard.service",
"expected": "active",
"severity": "high"
},
{
"id": "infra-xiaoguo-tunnel",
"description": "小果 LLM SSH隧道 运行中",
"check": "proc:autossh.*18003",
"expected": "running",
"severity": "critical"
},
{
"id": "infra-xiaoguo-api",
"description": "小果 LLM API 可访问",
"check": "http:http://node122:18003/v1/models",
"expected": "200",
"severity": "high"
},
{
"id": "infra-ejabberd",
"description": "ejabberd XMPP :5222 监听中",
"check": "port:5222",
"expected": "listening",
"severity": "critical"
},
{
"id": "infra-disk-space",
"description": "磁盘空间充足(>10%可用)",
"check": "disk:/",
"expected": ">10%",
"severity": "high"
}
]
},
{
"id": "sense",
"name": "SENSE 感受层(数据采集)",
"order": 2,
"items": [
{
"id": "sense-price-monitor",
"description": "价格监控 cron 已调度",
"check": "cron:3a9fb3300a6a",
"expected": "enabled+ok",
"severity": "critical"
},
{
"id": "sense-xiaoguo-scanner",
"description": "小果扫描 cron 已调度",
"check": "cron:0851c7838ca3",
"expected": "enabled+ok",
"severity": "high"
},
{
"id": "sense-xiaoguo-scanner-data",
"description": "小果扫描 今日有数据产出",
"check": "db:xiaoguo_scan_tracker:last_scanned_at::today:1",
"expected": ">0",
"severity": "high"
},
{
"id": "sense-macro-morning",
"description": "宏观采集-早盘 cron 已调度",
"check": "cron:da16db7b2ce7",
"expected": "enabled+ok",
"severity": "high"
},
{
"id": "sense-macro-noon",
"description": "宏观采集-午间 cron 已调度",
"check": "cron:f22c2cf8c86b",
"expected": "enabled+ok",
"severity": "medium"
},
{
"id": "sense-hk-rate",
"description": "港股汇率缓存 今日更新",
"check": "filefresh:/home/hmo/.hermes/profiles/position-analyst/home/.cache/hk_exchange_rate.json:24h",
"expected": "fresh",
"severity": "medium"
},
{
"id": "sense-trend-detector",
"description": "板块异动检测 今日有信号",
"check": "db:sector_signals:detected_at::today:1",
"expected": ">0",
"severity": "medium"
},
{
"id": "sense-mtf-cache",
"description": "多周期缓存 cron 已调度",
"check": "cron:45cf771dc4f2",
"expected": "enabled+ok",
"severity": "medium"
}
]
},
{
"id": "respond",
"name": "RESPOND 响应层(决策推送)",
"order": 3,
"items": [
{
"id": "resp-stale-push",
"description": "自选提醒 cron 已调度",
"check": "cron:e13323928f3a",
"expected": "enabled+ok",
"severity": "high"
},
{
"id": "resp-branch-evaluator",
"description": "分支评估 cron 已调度",
"check": "cron:b809fcabfa5b",
"expected": "enabled+ok",
"severity": "high"
},
{
"id": "resp-opening-brief",
"description": "开盘简报 cron 已调度(9:35)",
"check": "cron:5c354ad84085",
"expected": "enabled+ok",
"severity": "high"
},
{
"id": "resp-closing-brief",
"description": "收盘简报 cron 已调度(16:10)",
"check": "cron:85517c739d0b",
"expected": "enabled+ok",
"severity": "high"
},
{
"id": "resp-price-monitor-out",
"description": "价格监控 今天有事件产出",
"check": "db:price_events:date::today:1",
"expected": ">0",
"severity": "medium"
},
{
"id": "resp-signal-unprocessed",
"description": "信号管道 未处理信号堆积",
"check": "db:signal_news::unprocessed:30",
"expected": "<30",
"severity": "high"
}
]
},
{
"id": "adapt",
"name": "ADAPT 适应层(策略调整)",
"order": 4,
"items": [
{
"id": "adapt-per-stock-reassess",
"description": "策略重评 cron 已调度",
"check": "cron:00b8681e9cec",
"expected": "enabled+ok",
"severity": "high"
},
{
"id": "adapt-strategy-tree",
"description": "策略树活跃分支数正常",
"check": "db:strategy:branches:count",
"expected": ">0",
"severity": "medium"
},
{
"id": "adapt-strategy-staleness",
"description": "策略时效性检查 cron 已调度",
"check": "cron:dbe2ccaffc06",
"expected": "enabled+ok",
"severity": "medium"
}
]
},
{
"id": "improve",
"name": "IMPROVE 进化层(自我成长)",
"order": 5,
"items": [
{
"id": "improve-knowledge-extract",
"description": "知识萃取 cron 已调度(16:30)",
"check": "cron:e27e2e92ed80",
"expected": "enabled+ok",
"severity": "medium"
},
{
"id": "improve-hardcode-scanner",
"description": "硬编码扫描 cron 已调度",
"check": "cron:6d7ad7d20a39",
"expected": "enabled+ok",
"severity": "medium"
},
{
"id": "improve-system-audit",
"description": "系统审计 cron 已调度",
"check": "cron:c6a2c814a1c4",
"expected": "enabled+ok",
"severity": "medium"
},
{
"id": "improve-prune-branches",
"description": "分支剪枝 cron 已调度",
"check": "cron:a3697c108c7b",
"expected": "enabled+ok",
"severity": "low"
},
{
"id": "improve-meta-growth",
"description": "元自成长 cron 已调度",
"check": "cron:81696a7c0a9b",
"expected": "enabled+ok",
"severity": "medium"
},
{
"id": "improve-strategy-eval-daily",
"description": "日策略评估 cron 已调度",
"check": "cron:9d1236d8a07f",
"expected": "enabled+ok",
"severity": "medium"
}
]
},
{
"id": "data",
"name": "数据库健康度",
"order": 6,
"items": [
{
"id": "data-holdings",
"description": "DB holdings 表有持仓记录",
"check": "db:holdings:code::active:1",
"expected": ">0",
"severity": "critical"
},
{
"id": "data-strategies",
"description": "DB holding_strategies 表有活跃策略",
"check": "db:holding_strategies:code::active:1",
"expected": ">0",
"severity": "high"
},
{
"id": "data-watchlist",
"description": "DB watchlist_stocks 表有自选股",
"check": "db:watchlist_stocks:code::active:1",
"expected": ">0",
"severity": "high"
},
{
"id": "data-mofin-db",
"description": "mofin.db 数据库存在",
"check": "fileexists:/home/hmo/MoFin/data/mofin.db",
"expected": "exists",
"severity": "critical"
}
]
},
{
"id": "pipeline",
"name": "管道完整性",
"order": 7,
"items": [
{
"id": "pipe-xiaoguo-to-knowledge",
"description": "小果→知微 信号桥通畅",
"check": "pipeline:xiaoguo_signal_flow",
"expected": "recent_signals_consumed",
"severity": "high"
},
{
"id": "pipe-cron-errors",
"description": "无异常cron状态(最近24h)",
"check": "cron_errors:last24h",
"expected": "0",
"severity": "critical"
},
{
"id": "pipe-cron-paused-check",
"description": "无不应暂停cron",
"check": "cron_paused:check",
"expected": "all_expected_running",
"severity": "medium"
},
{
"id": "pipe-delivery-targets",
"description": "所有deliver=origin有目标",
"check": "delivery:origin_targets",
"expected": "all_resolved",
"severity": "high"
},
{
"id": "pipe-cron-audit",
"description": "全部cron任务最近24h有运行",
"check": "cron_audit:all",
"expected": "all_ran_in_24h",
"severity": "high"
},
{
"id": "pipe-registry-audit",
"description": "数据管道注册表完整性审计",
"check": "pipeline:registry_audit",
"expected": "all_flows_intact",
"severity": "high"
}
]
},
{
"id": "meta",
"name": "元自检(自检自身)",
"order": 8,
"items": [
{
"id": "meta-health-check-ran",
"description": "本体检昨天正常完成",
"check": "meta:health_check_yesterday",
"expected": "completed",
"severity": "critical"
},
{
"id": "meta-checklist-completeness",
"description": "检查清单覆盖无遗漏新增组件",
"check": "meta:checklist_completeness",
"expected": "all_components_covered",
"severity": "medium"
},
{
"id": "meta-intraday-check",
"description": "盘中自检 cron 已调度",
"check": "cron:intraday_check",
"expected": "enabled+ok",
"severity": "medium"
},
{
"id": "meta-self-todo-executor",
"description": "TODO执行器 cron 已调度",
"check": "cron:self_todo_executor",
"expected": "enabled+ok",
"severity": "high"
}
]
}
],
"auto_discovery": {
"last_scan": null,
"patterns": [
{
"type": "cron_job",
"check": "发现新增cron任务时自动加入checklist"
},
{
"type": "new_script",
"check": "发现新增脚本时检查是否需要加入checklist"
},
{
"type": "modified_script",
"check": "脚本修改时重新评估checklist项是否需要调整"
}
]
}
}
@@ -0,0 +1,192 @@
#!/usr/bin/env python3
"""promote_candidates.py — 自动提拔候选股入自选
从 candidates 表读未提拔的候选,评估后自动加入 holding_strategies。
"""
import sys, json, sqlite3
from pathlib import Path
from datetime import datetime
DB_PATH = Path("/home/hmo/MoFin/data/mofin.db")
def main():
conn = sqlite3.connect(str(DB_PATH), timeout=30)
conn.execute("PRAGMA busy_timeout=30000")
conn.row_factory = sqlite3.Row
# 读未提拔候选(按评分降序)
# 2026-07-24 老爸"优中选优"score>=7 才可入候选评估(原 4 = 91%通过率等于没门槛)
rows = conn.execute("""
SELECT c.code, c.name, c.score_final, c.entry_range, c.stop_loss, c.target
FROM candidates c
WHERE (c.promoted IS NULL OR c.promoted = 0)
AND (c.dropped IS NULL OR c.dropped = 0)
AND c.score_final >= 7
ORDER BY c.score_final DESC
""").fetchall()
if not rows:
print("[PROMOTE] 无待提拔候选")
conn.close()
return
promoted = 0
for r in rows:
code = str(r[0])
name = r[1] or code
score = r[2] or 0
entry_range = r[3] or ""
sl = r[4] or 0
tp = r[5] or 0
# 解析 entry_range
el, eh = 0, 0
if "~" in entry_range:
parts = entry_range.split("~")
try:
el = float(parts[0])
eh = float(parts[1])
except: pass
# 查是否已在 holding_strategies
exists = conn.execute(
"SELECT id FROM holding_strategies WHERE code=? AND status='active'",
(code,)
).fetchone()
if exists:
conn.execute("UPDATE candidates SET promoted=1 WHERE code=?", (code,))
print(f"{code} {name} 已在自选中,标记promoted")
continue
# 验证实时价格:无有效价格的候选股不入自选(防假数据污染)
_price = 0.0
try:
import subprocess, json as _jj
_r = subprocess.run(["python3", "/home/hmo/.hermes/profiles/position-analyst/scripts/stock_quote.py", code],
capture_output=True, text=True, timeout=10)
_q = _jj.loads(_r.stdout)
_price = float(_q.get("price", 0))
if _price <= 0:
print(f"{code} {name} 无实时价格,跳过")
continue
except Exception as _e:
print(f"{code} {name} 价格获取失败({_e}),跳过")
continue
# ── 优中选优闸(2026-07-24 老爸):ST排除 + 技术位锚定参数 + RR>=2.0 ──
if "ST" in (name or "").upper():
print(f"{code} {name} ST股,不入自选")
continue
# 技术位锚定:不信扫描器拍的 entry/sl/tp,用 ta.full_analysis 的确定性技术位重定
try:
import sys as _s
if '/home/hmo/MoFin/deploy/profile-scripts' not in _s.path:
_s.path.insert(0, '/home/hmo/MoFin/deploy/profile-scripts')
import technical_analysis as _ta
_ta_r = _ta.full_analysis(code)
_sr = (_ta_r or {}).get("support_resistance", {}) or {}
_ws, _ss = _sr.get("weak_support"), _sr.get("strong_support")
_wr, _sr2 = _sr.get("weak_resist"), _sr.get("strong_resist")
if _ws and _wr and _price > 0:
el = round(_ws * 0.995, 2) # 区下沿贴弱撑
eh = round(min(_wr, _price * 1.05), 2) # 区上沿取弱压(且不超现价5%
sl = round((_ss or _ws) * 0.985, 2) # 止损=强撑下1.5%(无强撑用弱撑)
tp = round(_sr2 or _wr * 1.15, 2) # 止盈=强压(无强压则弱压+15%
except Exception as _te:
print(f" ⚠️ {code} 技术位锚定失败({_te}),用扫描器参数", flush=True)
if el > 0 and eh > el and sl > 0 and tp > 0:
_mid = (el + eh) / 2
_rr = (tp - _mid) / (_mid - sl) if (_mid - sl) > 0 else 0
if _rr < 2.0:
print(f"{code} {name} RR={_rr:.2f}<2.0,不入自选")
continue
else:
print(f"{code} {name} 锚定后参数无效(区{el}~{eh}{sl}{tp}),跳过")
continue
# 构建策略(2026-07-24 老爸:提拔不直接给"买入"——先入观察,12维确认后再升)
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
timing_signal = "关注"
price_est = (el + eh) / 2 if el > 0 and eh > 0 else 0
reason_text = []
if el > 0: reason_text.append(f"{el}~{eh}")
if sl > 0: reason_text.append(f"{sl}")
if tp > 0: reason_text.append(f"{tp}")
if sl > 0 and tp > 0 and price_est > 0:
rr = (tp - price_est) / (price_est - sl) if (price_est - sl) > 0 else 0
reason_text.append(f"RR{rr:.1f}")
reason_text.append(f"评分{score}")
action = " | ".join(reason_text) if reason_text else f"市场扫描发现(评分{score})"
# ── 容量闸:自选上限60只,超出时删综合分最弱的(RR低优先)──
MAX_WATCH = 60
wl_count = conn.execute(
"SELECT COUNT(*) FROM holding_strategies WHERE status='active' AND decision_type='自选策略'").fetchone()[0]
if wl_count >= MAX_WATCH:
weakest = conn.execute("""
SELECT code, name, COALESCE(rr_ratio,0) as rr FROM holding_strategies
WHERE status='active' AND decision_type='自选策略'
ORDER BY COALESCE(rr_ratio,0) ASC, updated_at ASC LIMIT 1""").fetchone()
if weakest and (weakest[2] or 0) < 2.0:
conn.execute(
"INSERT INTO watchlist_log (code, name, event, reason, old_signal, new_signal, price) "
"VALUES (?,?,?,?,?,?,?)",
(weakest[0], weakest[1] or "", "exit", f"容量{MAX_WATCH}淘汰为新标的{code}腾位", "", "已删除", 0))
conn.execute(
"DELETE FROM holding_strategies WHERE code=? AND status='active' AND decision_type='自选策略'",
(weakest[0],))
print(f" 🔴 容量淘汰: {weakest[0]} {weakest[1]} (RR={weakest[2]})", flush=True)
else:
print(f" ⏭ 自选已满{MAX_WATCH}且现有标的均RR>=2.0{code}暂缓提拔", flush=True)
continue
cur = conn.execute("""
INSERT OR IGNORE INTO holding_strategies
(code, name, price, entry_low, entry_high, stop_loss, take_profit,
timing_signal, action, decision_type, strategy_type, status,
rr_ratio, stock_category, created_at, updated_at,
sector_context, quality_check)
VALUES (?,?,?,?,?,?,?,?,?,'自选策略','scan',
'active',0,'关注',?,?,'', 'pending')
""", (code, name, 0, el, eh, sl, tp, timing_signal, action, now, now))
newly_added = cur.rowcount > 0
conn.execute("UPDATE candidates SET promoted=1 WHERE code=?", (code,))
if newly_added:
promoted += 1
print(f"{code} {name} 评分{score} → 已加入自选({timing_signal})", flush=True)
else:
print(f"{code} {name} 已在自选策略中,标记promoted", flush=True)
# 触发全量重评(生成完整9维策略)——仅新插入的股票需要
if newly_added:
try:
import subprocess as _sp
r = _sp.run(["python3", "/home/hmo/MoFin/deploy/profile-scripts/per_stock_reassess.py", code],
capture_output=True, text=True, timeout=480)
if r.returncode == 0:
print(f" 重评完成", flush=True)
else:
print(f" 重评失败: {r.stderr.strip()[:100]}", flush=True)
except Exception as e:
print(f" 重评异常: {e}", flush=True)
conn.commit()
print(f"\n[PROMOTE] 本次提拔{promoted}", flush=True)
# 推XMPP
if promoted > 0:
try:
import urllib.request
msg = f"📈 自动提拔{promoted}只候选入自选"
payload = json.dumps({"to": "hmo@yoin.fun", "body": msg, "type": "chat"}).encode()
req = urllib.request.Request("http://127.0.0.1:5805/", data=payload,
headers={"Content-Type": "application/json"})
urllib.request.urlopen(req, timeout=5)
except Exception:
pass
conn.close()
if __name__ == "__main__":
main()