docs: 更新CHANGELOG + 开发日志记录07/13全部变更
- XMPP Bot非阻塞/本地XMPP/MUC修复 - price_monitor推送冷却持久化 - 深套股规则重构+换股规划swap-plan - 周月线技术分析增强 - 健康Tab写无读清零 - stale_push_wlin/per_stock_reassess/promote_candidates更新 - static/index.html + mofin_health.json + server.py同步
This commit is contained in:
@@ -229,11 +229,11 @@ def main():
|
||||
continue
|
||||
entry = decisions_map.get(code)
|
||||
if not entry:
|
||||
# 不在 decisions 中的自选股 → 从 DB watchlist_stocks 构建entry
|
||||
# 不在 decisions 中的自选股 → 从 holding_strategies 构建entry
|
||||
import sqlite3
|
||||
_db = sqlite3.connect('/home/hmo/web-dashboard/data/mofin.db')
|
||||
_db = sqlite3.connect('/home/hmo/MoFin/data/mofin.db')
|
||||
_db.row_factory = sqlite3.Row
|
||||
_wl = _db.execute("SELECT * FROM watchlist_stocks WHERE code=? AND is_active=1", (code,)).fetchone()
|
||||
_wl = _db.execute("SELECT * FROM holding_strategies WHERE code=? AND status='active' AND decision_type='自选策略'", (code,)).fetchone()
|
||||
_db.close()
|
||||
if _wl:
|
||||
entry = {
|
||||
@@ -299,7 +299,13 @@ def main():
|
||||
skipped += 1
|
||||
continue
|
||||
# 打印参数调试
|
||||
print(f" DEBUG: code={code} name={entry.get('name','')} price={price} cost={entry.get('cost')} shares={entry.get('shares')} action={entry.get('action','')[:30]} is_wl={entry.get('type','') in ('自选策略','watchlist')}", flush=True)
|
||||
if entry is None:
|
||||
print(f" DEBUG: code={code} ENTRY=NONE 跳过")
|
||||
print(f" [SKIP] {code} 策略数据不存在")
|
||||
skipped += 1
|
||||
continue
|
||||
entry_action = str(entry.get('action') or '')
|
||||
print(f" DEBUG: code={code} name={entry.get('name','')} price={price} cost={entry.get('cost')} shares={entry.get('shares')} action={entry_action[:30]} is_wl={entry.get('type','') in ('自选策略','watchlist')}", flush=True)
|
||||
result = reassess_strategy(
|
||||
code=code,
|
||||
name=entry.get("name", ""),
|
||||
|
||||
@@ -56,6 +56,19 @@ def main():
|
||||
print(f" ⏭ {code} {name} 已在自选中,标记promoted")
|
||||
continue
|
||||
|
||||
# 验证实时价格:无有效价格的候选股不入自选(防假数据污染)
|
||||
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)
|
||||
if float(_q.get("price", 0)) <= 0:
|
||||
print(f" ⏭ {code} {name} 无实时价格,跳过")
|
||||
continue
|
||||
except Exception as _e:
|
||||
print(f" ⏭ {code} {name} 价格获取失败({_e}),跳过")
|
||||
continue
|
||||
|
||||
# 构建策略
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
timing_signal = "买入" if score >= 7 else "关注"
|
||||
|
||||
@@ -136,7 +136,9 @@ def main():
|
||||
if not cur_price or cur_price <= 0:
|
||||
continue
|
||||
drift = (cur_price / center - 1) * 100
|
||||
if abs(drift) > 15:
|
||||
# 触发条件:价格偏离>15% 或 买入区明确错误(价格完全在区间外且偏离>50%)
|
||||
price_outside = cur_price < wl_el or cur_price > wl_eh
|
||||
if abs(drift) > 15 or (price_outside and abs(drift) > 50):
|
||||
reassess_scripts.append(code)
|
||||
print(f"[AUTO_REASSESS] {name}({code}) 价{cur_price:.2f}偏离买入区中心{center:.2f} {drift:+.0f}% → 触发重评")
|
||||
if reassess_scripts:
|
||||
|
||||
@@ -363,39 +363,6 @@ def main():
|
||||
dec = read_decisions()
|
||||
for e in dec.get("decisions", []):
|
||||
code_data[e["code"]] = e
|
||||
# 补充watchlist_stocks中不在holding_strategies的自选股
|
||||
import sqlite3 as _sq3
|
||||
_wl_db = _sq3.connect('/home/hmo/MoFin/data/mofin.db')
|
||||
_wl_db.row_factory = _sq3.Row
|
||||
_wl_rows = _wl_db.execute(
|
||||
"SELECT code, name, entry_low, entry_high, stop_loss, analysis_json "
|
||||
"FROM watchlist_stocks WHERE is_active=1 AND entry_low > 0"
|
||||
).fetchall()
|
||||
_wl_db.close()
|
||||
for _w in _wl_rows:
|
||||
_c = str(_w["code"])
|
||||
if _c in code_data:
|
||||
continue
|
||||
_aj = json.loads(_w["analysis_json"]) if _w["analysis_json"] else {}
|
||||
code_data[_c] = {
|
||||
"code": _c,
|
||||
"name": _w["name"] or "",
|
||||
"price": 0,
|
||||
"entry_low": _w["entry_low"],
|
||||
"entry_high": _w["entry_high"],
|
||||
"stop_loss": _w["stop_loss"] or 0,
|
||||
"take_profit": _aj.get("take_profit", 0),
|
||||
"rr_ratio": _aj.get("rr", 0),
|
||||
"tech_snapshot": _aj.get("tech_snapshot", ""),
|
||||
"timing_signal": _aj.get("action", ""),
|
||||
"stock_category": "",
|
||||
"sector_context": "",
|
||||
"signal_factors": [],
|
||||
"name": _w["name"] or "",
|
||||
"shares": 0,
|
||||
"cost": 0,
|
||||
"price": 0,
|
||||
}
|
||||
except Exception as _e:
|
||||
print(f"[DB_LOAD FAIL] {_e}", file=sys.stderr)
|
||||
|
||||
@@ -498,8 +465,8 @@ def main():
|
||||
eh = row[5] or 0
|
||||
if price <= 0 or el <= 0 or eh <= 0:
|
||||
continue
|
||||
# 价格必须在买入区内或略高于(不超过5%)
|
||||
if price > eh * 1.05:
|
||||
# 价格必须在买入区内(硬检查,拒绝偏离仍推买入)
|
||||
if price < el or price > eh:
|
||||
continue
|
||||
name = row[1] or code
|
||||
all_candidates.append((name, code, price, el, eh, "", False))
|
||||
@@ -573,6 +540,12 @@ def main():
|
||||
# 信号必须含买入/加仓才推荐——其他非操作信号跳过
|
||||
if not any(kw in sig for kw in ["买入", "加仓"]):
|
||||
continue
|
||||
# 价格必须在买入区内(硬检查,拒绝价格偏离仍推买入)
|
||||
buy_low, buy_high = s[3], s[4]
|
||||
price_check = s[2]
|
||||
if buy_low and buy_high and buy_low > 0:
|
||||
if not (buy_low <= price_check <= buy_high):
|
||||
continue
|
||||
# RR完整性检查:买入/加仓信号必须RR>0(策略数据要完整)
|
||||
cd = code_data.get(s[1], {})
|
||||
rr = cd.get("rr_ratio", 0) or 0
|
||||
|
||||
Reference in New Issue
Block a user