feat: 重评冷却期+LLM信号产出+reassessed_at追踪
This commit is contained in:
@@ -6,6 +6,31 @@ per_stock_reassess.py — 按个股触发重评
|
||||
DB holding_strategies 表(纯DB模式,已移除JSON依赖)。
|
||||
"""
|
||||
import sys, json, os, re
|
||||
from datetime import datetime
|
||||
|
||||
COOLDOWN_HOURS_TRADING = 4 # 交易时段冷却(9:00-15:00)
|
||||
COOLDOWN_HOURS_NONTRADING = 24 # 非交易时段冷却
|
||||
|
||||
def _in_cooldown(code):
|
||||
"""检查个股是否在重评冷却期内"""
|
||||
try:
|
||||
import sqlite3
|
||||
conn = sqlite3.connect("/home/hmo/MoFin/data/mofin.db")
|
||||
r = conn.execute("SELECT reassessed_at FROM holding_strategies WHERE code=? AND status='active' ORDER BY id DESC LIMIT 1", (code,)).fetchone()
|
||||
conn.close()
|
||||
if not r or not r[0]:
|
||||
return False # 从未重评,立即执行
|
||||
last = datetime.fromisoformat(r[0])
|
||||
now = datetime.now()
|
||||
# 交易时段 vs 非交易时段
|
||||
if 9 <= now.hour < 15:
|
||||
hours = COOLDOWN_HOURS_TRADING
|
||||
else:
|
||||
hours = COOLDOWN_HOURS_NONTRADING
|
||||
diff = (now - last).total_seconds() / 3600
|
||||
return diff < hours
|
||||
except:
|
||||
return False
|
||||
|
||||
sys.path.insert(0, "/home/hmo/web-dashboard")
|
||||
sys.path.insert(0, "/home/hmo/MoFin")
|
||||
@@ -197,6 +222,11 @@ def main():
|
||||
errors = 0
|
||||
skipped = 0
|
||||
for code in codes:
|
||||
# 冷却期检查
|
||||
if _in_cooldown(code):
|
||||
print(f" ⏭ {code}: 冷却期内跳过")
|
||||
skipped += 1
|
||||
continue
|
||||
entry = decisions_map.get(code)
|
||||
if not entry:
|
||||
# 不在 decisions 中的自选股 → 从 DB watchlist_stocks 构建entry
|
||||
@@ -368,7 +398,14 @@ def main():
|
||||
当前数据:大盘={_macro_desc or "震荡"} | PE/市值={_pe_val} {_pb_val} | 价格={price} 区间={entry.get("entry_low",0)}~{entry.get("entry_high",0)} 止损={entry.get("stop_loss",0)} 止盈={entry.get("take_profit",0)} RR={result.get("rr_ratio",entry.get("rr_ratio",0))} | 信号={result.get("timing_signal") or entry.get("timing_signal","")} | 行业={(result.get("sector_context") or entry.get("sector_context",""))[:50]}
|
||||
策略={(result.get("action") or entry.get("action",""))[:200]}
|
||||
技术={(result.get("tech_snapshot") or entry.get("tech_snapshot",""))[:200]}
|
||||
格式:①大盘×基本面 ②大盘×消息面 ③大盘×技术面 ④大盘×资金流 ⑤行业×基本面 ⑥行业×消息面 ⑦行业×技术面 ⑧个股×基本面 ⑨个股×消息面 然后综合结论和操作建议。"""
|
||||
|
||||
格式:①大盘×基本面 ②大盘×消息面 ③大盘×技术面 ④大盘×资金流 ⑤行业×基本面 ⑥行业×消息面 ⑦行业×技术面 ⑧个股×基本面 ⑨个股×消息面
|
||||
|
||||
最后必须输出:
|
||||
【综合结论】(买入/关注/观望/卖出)
|
||||
【操作建议】
|
||||
【建议止损】
|
||||
【建议止盈】"""
|
||||
try:
|
||||
_ur = __import__('urllib.request', fromlist=['Request'])
|
||||
_req = _ur.Request("http://127.0.0.1:8643/v1/chat/completions",
|
||||
@@ -384,11 +421,23 @@ def main():
|
||||
|
||||
# 保存到DB
|
||||
_fa_conn = __import__('sqlite3').connect("/home/hmo/MoFin/data/mofin.db")
|
||||
_fa_conn.execute("UPDATE holding_strategies SET full_analysis=? WHERE code=? AND status='active'", (_full_analysis_text, code))
|
||||
_fa_conn.execute("UPDATE holding_strategies SET full_analysis=?, reassessed_at=? WHERE code=? AND status='active'", (_full_analysis_text, __import__('datetime').datetime.now().isoformat(), code))
|
||||
_fa_conn.commit()
|
||||
_fa_conn.close()
|
||||
print(f" ✅ 完整九维分析已保存({len(_full_analysis_text)}字)")
|
||||
print(f" [DB] holding_strategies 已更新: {code}")
|
||||
# 从LLM输出提取信号
|
||||
if _full_analysis_text and '【综合结论】' in _full_analysis_text:
|
||||
try:
|
||||
_sig_line = [l for l in _full_analysis_text.split('\n') if '综合结论' in l]
|
||||
if _sig_line:
|
||||
_sig = '买入' if '买入' in _sig_line[0] else '关注' if '关注' in _sig_line[0] else '观望' if '观望' in _sig_line[0] else '卖出' if '卖出' in _sig_line[0] else ''
|
||||
if _sig:
|
||||
__import__('sqlite3').connect('/home/hmo/MoFin/data/mofin.db').execute(
|
||||
"UPDATE holding_strategies SET timing_signal=? WHERE code=? AND status='active'", (_sig, code)).connection.commit()
|
||||
print(f" ✅ LLM信号={_sig} 已写入")
|
||||
except: pass
|
||||
# 冷却期已更新(reassessed_at写入)
|
||||
except Exception as _dbe:
|
||||
print(f" [DB FAIL] holding_strategies 写入失败: {_dbe}", file=sys.stderr)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user