fix: 全脚本sqlite连接加busy_timeout防并发写锁(2026-08-27DB损坏教训,43文件75处)

This commit is contained in:
xxm
2026-08-28 01:29:37 +08:00
parent ca51cff625
commit e72fc0bd63
44 changed files with 111 additions and 0 deletions
+6
View File
@@ -36,6 +36,7 @@ def calc_rsi(closes, n=14):
def main():
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
hsi = pd.read_sql("SELECT date, close FROM stock_daily WHERE code='hkHSI' ORDER BY date", conn)
codes = [r[0] for r in conn.execute(
"SELECT code FROM hk_connect_stocks WHERE is_active=1 ORDER BY code").fetchall()]
@@ -61,6 +62,7 @@ def main():
if not sec:
continue
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
df = pd.read_sql("SELECT date, close FROM stock_daily WHERE code=? ORDER BY date",
conn, params=(code,))
conn.close()
@@ -83,6 +85,7 @@ def main():
# 估值分位(每日截面)
print("加载历史估值...", flush=True)
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
hist = pd.read_sql(
"SELECT code, date, pe_ttm, pb FROM stock_fundamentals_history WHERE length(code)=5", conn)
conn.close()
@@ -97,6 +100,7 @@ def main():
# 资金流(当日主力净流入,万元)
print("加载资金流...", flush=True)
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
flow = pd.read_sql(
"SELECT code, date, flow_in, flow_out FROM hk_flow_daily", conn)
conn.close()
@@ -110,6 +114,7 @@ def main():
# 市值(当前市值×价格比反推历史,总股本短期不变)
print("加载市值...", flush=True)
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
fund_mc = pd.read_sql(
"SELECT code, mcap_total FROM stock_fundamentals WHERE length(code)=5", conn)
conn.close()
@@ -121,6 +126,7 @@ def main():
for idx, code in enumerate(codes):
sec = sector.get(code)
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
df = pd.read_sql(
"SELECT date, open, close, high, low, volume FROM stock_daily WHERE code=? ORDER BY date",
conn, params=(code,))
@@ -155,6 +155,7 @@ def main():
try:
import sqlite3
_db = sqlite3.connect("/home/hmo/MoFin/data/mofin.db")
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
for row in _db.execute("SELECT DISTINCT code FROM holdings WHERE is_active=1").fetchall():
if row[0]: codes.add(row[0])
for row in _db.execute("SELECT DISTINCT code FROM holding_strategies WHERE status='active' AND decision_type='自选策略'").fetchall():
@@ -58,6 +58,7 @@ def check_missing_strategies(conn, decisions_list):
def main():
conn = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
# 1. 清理 holding_strategies
archived = clean_holding_strategies(conn)
@@ -95,6 +95,7 @@ def load_history():
try:
import sqlite3
conn = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
rows = conn.execute(
"SELECT indices, created_at FROM macro_context_log "
"WHERE has_valid_data=1 ORDER BY created_at DESC LIMIT 5"
@@ -277,6 +278,7 @@ def write_to_signal_news(signals):
return
import sqlite3
conn = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
high_signals = [s for s in signals if s["level"] == "high"]
if high_signals:
@@ -22,6 +22,7 @@ def prefix(code):
def main():
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
codes = [r[0] for r in conn.execute(
"SELECT DISTINCT code FROM holding_strategies WHERE status='active'")]
print(f"刷新 {len(codes)} 只基本面...")
@@ -98,6 +98,7 @@ def main():
try:
import sqlite3
_conn = sqlite3.connect("/home/hmo/web-dashboard/data/mofin.db")
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
_actionable = _conn.execute(
"SELECT hs.code, lp.price, hs.entry_low, hs.entry_high FROM holding_strategies hs "
"LEFT JOIN live_prices lp ON hs.code = lp.code "
+2
View File
@@ -36,6 +36,7 @@ def load_regime_map():
global _REGIME_CACHE
if _REGIME_CACHE is None:
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
_REGIME_CACHE = dict(conn.execute(
"SELECT date, regime FROM market_regime WHERE market='hk'").fetchall())
conn.close()
@@ -156,6 +157,7 @@ def portfolio_nav(trades, capital=1000000, slots=8):
return {}, {}
# 用真实日历(stock_daily 港股日K日期)
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
cal = [r[0] for r in conn.execute(
"SELECT DISTINCT date FROM stock_daily WHERE date>=? AND date<=? AND length(code)=5 ORDER BY date",
(dates[0], dates[-1])).fetchall()]
+2
View File
@@ -26,6 +26,7 @@ def _get_rate_from_db():
try:
import sqlite3
conn = sqlite3.connect(DB_PATH)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
cursor = conn.execute(
"SELECT rate FROM fx_rate WHERE currency_pair='HKD_CNY' AND is_active=1 ORDER BY created_at DESC LIMIT 1"
)
@@ -42,6 +43,7 @@ def _set_rate_in_db(rate, source='api_refresh'):
try:
import sqlite3
conn = sqlite3.connect(DB_PATH)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
# 先将旧的设为 inactive
conn.execute("UPDATE fx_rate SET is_active=0 WHERE currency_pair='HKD_CNY'")
# 插入新汇率
@@ -40,6 +40,7 @@ def fetch_tencent_batch(codes):
def main():
import sqlite3
conn = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
# 获取已有代码
existing = set(r[0] for r in conn.execute("SELECT code FROM stocks").fetchall())
@@ -114,6 +114,7 @@ def main():
# Step 1: Update SQLite (regenerate_all reads from here)
print("\n→ 更新 SQLite holdings 表...")
conn = sqlite3.connect(DB_PATH)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
c = conn.cursor()
c.execute('DELETE FROM holdings')
c.execute('DELETE FROM portfolio_summary')
@@ -51,6 +51,7 @@ def main():
return
notified = _load_state()
db = sqlite3.connect(KANBAN_DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
rows = db.execute(
"SELECT id, title, assignee, created_by FROM tasks "
"WHERE status='ready' AND assignee IN (%s) ORDER BY created_at"
+1
View File
@@ -41,6 +41,7 @@ def scan_leaders():
return []
c = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
# 最新交易日
row = c.execute("SELECT MAX(date) FROM stock_indicators").fetchone()
if not row or not row[0]:
@@ -25,6 +25,7 @@ os.makedirs(LIVE_DIR, exist_ok=True)
def collect_daily_snapshot(date_str=None):
"""累积某日的实盘数据快照"""
conn = sqlite3.connect(f"file:{DB}?mode=ro", uri=True)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
conn.execute("PRAGMA query_only=ON")
if date_str is None:
@@ -217,6 +217,7 @@ def write_risk_signal(conn, level, matched, summary):
def main():
conn = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
ensure_tables(conn)
# 去重基础
@@ -59,6 +59,7 @@ def get_existing_codes():
try:
import sqlite3
db = sqlite3.connect(str(MOFIN_DATA / "mofin.db"))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
for row in db.execute("SELECT code FROM watchlist_stocks WHERE is_active=1"):
codes.add(str(row[0]).strip())
for row in db.execute("SELECT code FROM holdings"):
+2
View File
@@ -23,6 +23,7 @@ SCRIPT_DIR = Path('/home/hmo/MoFin/scripts')
def _get_db():
db = sqlite3.connect(DB_PATH)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
db.row_factory = sqlite3.Row
return db
@@ -287,6 +288,7 @@ def write_cash_log(cash_before, cash_after, frozen_before, frozen_after,
"""记录现金变更到 cash_log 表。"""
change_amount = round(cash_after - cash_before, 2) if cash_after is not None and cash_before is not None else 0
db = sqlite3.connect(DB_PATH)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
try:
cur = db.execute(
"""INSERT INTO cash_log
+2
View File
@@ -1119,6 +1119,7 @@ def get_price_from_db(code: str) -> tuple[float | None, float | None]:
"""
try:
db = sqlite3.connect('/home/hmo/web-dashboard/data/mofin.db')
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
db.row_factory = sqlite3.Row
row = db.execute(
"SELECT price, change_pct FROM holdings WHERE code=? AND is_active=1", (str(code),)
@@ -1142,6 +1143,7 @@ def get_prices_batch_from_db(codes: list[str]) -> dict:
return results
try:
db = sqlite3.connect('/home/hmo/web-dashboard/data/mofin.db')
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
db.row_factory = sqlite3.Row
for code in codes:
row = db.execute(
+1
View File
@@ -29,6 +29,7 @@ def clean_proxy():
def get_conn():
import sqlite3
conn = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
conn.row_factory = sqlite3.Row
return conn
@@ -141,6 +141,7 @@ def write_todos_for_issues():
try:
conn = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
todo_priority = {"critical": "high", "error": "medium", "warn": "low"}
new_count = 0
@@ -304,6 +305,7 @@ def check_db_table_count(table, field, value, op="today", threshold=0):
"""检查数据库中的记录数"""
try:
conn = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
cur = conn.cursor()
if op == "today":
today = ctx["started_at"].strftime("%Y-%m-%d")
@@ -478,6 +480,7 @@ def check_meta_health_check_yesterday():
"""元检:昨天体检是否正常完成"""
try:
conn = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
yesterday = (ctx["started_at"] - timedelta(days=1)).strftime("%Y-%m-%d")
row = conn.execute(
"SELECT ok_count, error_count, critical_count FROM health_check_log "
@@ -804,6 +807,7 @@ def main():
# 保存历史到DB
try:
conn_hist = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
details = json.dumps([e for e in ctx["report"] if e["level"] in ("critical", "error")])
conn_hist.execute(
"INSERT INTO health_check_log (ok_count, warn_count, error_count, critical_count, duration_s, details) "
@@ -834,6 +838,7 @@ def main():
# 检查是否有执行器升级来的TODO(通知失败挂起的)
try:
conn2 = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
needs_llm = conn2.execute(
"SELECT id, title, priority, created_at, note FROM todos "
"WHERE status='needs_llm' "
+1
View File
@@ -69,6 +69,7 @@ def init_table(conn):
if __name__ == '__main__':
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
init_table(conn)
# 取策略用到的 58 只股票
@@ -16,6 +16,7 @@ def _in_cooldown(code):
try:
import sqlite3
conn = sqlite3.connect("/home/hmo/MoFin/data/mofin.db")
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(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]:
@@ -272,6 +273,7 @@ def main():
# 不在 decisions 中的自选股 → 从 holding_strategies 构建entry
import sqlite3
_db = sqlite3.connect('/home/hmo/MoFin/data/mofin.db')
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
_db.row_factory = sqlite3.Row
_wl = _db.execute("SELECT * FROM holding_strategies WHERE code=? AND status='active' AND decision_type='自选策略'", (code,)).fetchone()
_db.close()
@@ -306,6 +308,7 @@ def main():
price = 0
import sqlite3
db = sqlite3.connect('/home/hmo/web-dashboard/data/mofin.db')
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
db.row_factory = sqlite3.Row
row = db.execute("SELECT price FROM holdings WHERE code=? AND is_active=1", (code_raw,)).fetchone()
if not row:
@@ -559,6 +562,7 @@ def main():
from datetime import datetime as _dt
import sqlite3
_db2 = sqlite3.connect('/home/hmo/web-dashboard/data/mofin.db')
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
for _code in codes:
_entry = decisions_map.get(_code)
if _entry and _entry.get("is_watchlist"):
@@ -622,6 +626,7 @@ def scan_watchlist_stocks():
DB = '/home/hmo/web-dashboard/data/mofin.db'
db = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
db.row_factory = sqlite3.Row
rows = db.execute(
@@ -58,6 +58,7 @@ def main():
return
conn = sqlite3.connect(DB_PATH)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
init_table(conn)
cur = conn.cursor()
@@ -67,6 +67,7 @@ def check_data_freshness():
try:
import sqlite3
c = sqlite3.connect(str(WEB_DATA / "mofin.db"))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
row = c.execute("SELECT MAX(updated_at) FROM holding_strategies WHERE status IN ('active','updated')").fetchone()
c.close()
if row and row[0]:
@@ -71,6 +71,7 @@ def check_db():
return
try:
conn = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
pragma = conn.execute("PRAGMA integrity_check").fetchone()[0]
check("DB 完整性", pragma == "ok", pragma)
@@ -245,6 +246,7 @@ if __name__ == "__main__":
# 尝试写mofin.db todos
try:
conn = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
conn.execute("""
INSERT OR REPLACE INTO todos (id, content, status, source, created_at, fix_action)
VALUES (?, ?, 'pending', 'preflight', datetime('now'), 'check preflight_result.json and fix issues')
@@ -23,6 +23,7 @@ SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
def get_stock_info(code):
"""从数据库获取股票信息"""
db = sqlite3.connect(DB_PATH)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
db.row_factory = sqlite3.Row
try:
# 检查持仓
@@ -92,6 +92,7 @@ def main():
ts = now.strftime("%Y-%m-%d %H:%M:%S")
try:
conn = sqlite3.connect(str(DB))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
conn.execute("""
INSERT INTO macro_context_log
(data_timestamp, session, has_valid_data, indices, structure, created_at)
+4
View File
@@ -25,6 +25,7 @@ def _flow_state(code):
"""资金维度: flow_5d / flow_delta → negative/neutral/positive/strong_positive"""
try:
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
rows = conn.execute("""
SELECT date, main_pct FROM stock_capital_flow
WHERE code=? ORDER BY date DESC LIMIT 10
@@ -58,6 +59,7 @@ _NEG_KW = ['大跌','跌停','回落','下跌','下挫','走弱','资金流出',
def sector_news_sentiment(sector, days=3):
import sqlite3
conn = sqlite3.connect('/home/hmo/MoFin/data/mofin.db')
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
try:
stocks = conn.execute("SELECT code FROM stock_sectors_em WHERE sector=?", (sector,)).fetchall()
if not stocks:
@@ -92,6 +94,7 @@ def _news_state(code, name=None, sector=None):
since = (datetime.now() - timedelta(days=3)).strftime('%Y-%m-%d')
try:
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
# 个股级优先(searched_stocks 或 summary 含代码/名称)
rows = conn.execute("""
SELECT overall_sentiment, summary, sector, created_at FROM signal_news
@@ -159,6 +162,7 @@ def evaluate_resonance(code, gate, name=None):
def log_resonance(code, name, price, gate, res, signal_before, signal_after):
"""完整记录三维状态 → signal_veto_log(后续回测验证的数据资产)"""
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
conn.execute("""
CREATE TABLE IF NOT EXISTS signal_veto_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
+2
View File
@@ -94,6 +94,7 @@ test("frozen_cash 已清零", pf.get('frozen_cash', 0) < 1, f"frozen_cash={pf.ge
print("\n--- 6. P&L ---")
import sqlite3
db = sqlite3.connect('/home/hmo/web-dashboard/data/mofin.db')
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
rows = db.execute("SELECT code, name, cost, price, shares, currency FROM holdings WHERE is_active=1 AND shares>0").fetchall()
pnl_issues = []
for r in rows:
@@ -111,6 +112,7 @@ db.close()
# ── 7. DB 完整性 ──
print("\n--- 7. DB 完整性 ---")
db = sqlite3.connect('/home/hmo/web-dashboard/data/mofin.db')
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
n_holds = db.execute("SELECT COUNT(*) FROM holdings WHERE is_active=1").fetchone()[0]
n_strat = db.execute("SELECT COUNT(*) FROM holding_strategies WHERE status IN ('active','updated')").fetchone()[0]
n_wl = db.execute("SELECT COUNT(*) FROM watchlist_stocks WHERE is_active=1").fetchone()[0]
@@ -0,0 +1,36 @@
import os
results = {}
for f in sorted(os.listdir(".")):
if not f.endswith(".py"):
continue
with open(f, errors="ignore") as fh:
content = fh.read()
hits = []
for i, line in enumerate(content.split("\n"), 1):
stripped = line.strip()
if stripped.startswith("#") or stripped.startswith('"""') or stripped.startswith("'''"):
continue
apis = []
if "qt.gtimg.cn" in line: apis.append("腾讯quote")
if "push2.eastmoney" in line or "eastmoney.com" in line: apis.append("东财")
if "ifzq.gtimg.cn" in line: apis.append("腾讯K线")
if "exchangerate-api" in line: apis.append("汇率API")
if "hq.sinajs" in line: apis.append("新浪行情")
if "web.sqt.gtimg.cn" in line: apis.append("腾讯sqt")
if "proxy.finance.qq.com" in line: apis.append("腾讯proxy")
if "stock.xueqiu.com" in line or "xueqiu.com" in line: apis.append("雪球")
if "finance.pae.baidu.com" in line or "baidu.com" in line: apis.append("百度")
if apis:
hits.append((i, "|".join(apis), stripped[:80]))
if hits:
results[f] = hits
print("=== 全部外部API调用点 ===")
for f, hits in sorted(results.items()):
print("\n%s (%d 处):" % (f, len(hits)))
for ln, api, code in hits:
print(" L%d [%s] %s" % (ln, api, code))
@@ -16,6 +16,7 @@ DB = "/home/hmo/MoFin/data/mofin.db"
def main():
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
codes = [r[0] for r in conn.execute(
"SELECT DISTINCT code FROM holding_strategies WHERE status='active'")]
have = {r[0] for r in conn.execute("SELECT code FROM stock_sectors")}
@@ -126,6 +126,7 @@ def scan():
return
conn = sqlite3.connect(str(STATE_DB))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
conn.row_factory = sqlite3.Row
cur = conn.cursor()
+3
View File
@@ -84,6 +84,7 @@ def main():
try:
import sqlite3
db = sqlite3.connect('/home/hmo/MoFin/data/mofin.db')
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
db.row_factory = sqlite3.Row
wl_rows = db.execute(
"SELECT code, name, entry_low, entry_high, stop_loss, take_profit, rr_ratio, timing_signal, action "
@@ -124,6 +125,7 @@ def main():
try:
import subprocess, sqlite3
db = sqlite3.connect('/home/hmo/MoFin/data/mofin.db')
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
db.row_factory = sqlite3.Row
wl_stocks = db.execute(
"SELECT code, name, entry_low, entry_high "
@@ -182,6 +184,7 @@ def main():
to_check = [d for d in decisions_list if (d.get("entry_low") is not None or d.get("entry_high") is not None) and d.get("status") not in EXCLUDED_STATUSES]
# 重新合并自选(从 holding_strategies 读)
db2 = sqlite3.connect('/home/hmo/MoFin/data/mofin.db')
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
db2.row_factory = sqlite3.Row
wl_rows2 = db2.execute(
"SELECT code, name, entry_low, entry_high, stop_loss, take_profit, rr_ratio, timing_signal, action "
+1
View File
@@ -58,6 +58,7 @@ def load_macro() -> dict:
try:
import sqlite3
conn = sqlite3.connect(os.path.join(DATA_DIR, "mofin.db"))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
row = conn.execute(
"SELECT indices, structure, key_sectors FROM macro_context_log "
"WHERE has_valid_data=1 ORDER BY created_at DESC LIMIT 1"
+1
View File
@@ -303,6 +303,7 @@ def _get_name_from_cache(code):
try:
import sqlite3
_db = sqlite3.connect(str(DATA_DIR / "mofin.db"))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
_db.row_factory = sqlite3.Row
row = _db.execute("SELECT name FROM watchlist_stocks WHERE code=? AND is_active=1", (code,)).fetchone()
_db.close()
@@ -205,6 +205,7 @@ def main():
stale_items = [s for s in flagged if any("[STRATEGY_STALE]" in f for f in s.get("flags", []))]
if stale_items:
conn = sqlite3.connect(DB_PATH)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
for s in stale_items:
code = s["code"]
existing = conn.execute("SELECT id FROM todos WHERE title LIKE ? AND status IN ('pending','in_progress')", (f"%{code}%",)).fetchone()
+1
View File
@@ -37,6 +37,7 @@ ORANGE_PNL_RATIO = 0.5 # 盈亏比 < 0.5(赚的越来越少亏的越来越
def rolling_stats(version, days=60):
"""从 strategy_research 提取该策略近期交易的滚动胜率/盈亏比"""
c = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
rows = c.execute(
"SELECT results_json FROM strategy_research WHERE version=? ORDER BY period_tag DESC LIMIT 1",
(version,)
@@ -707,6 +707,7 @@ def load_macro_context():
import sqlite3
from pathlib import Path
conn = sqlite3.connect(str(Path(__file__).parent.parent / "data" / "mofin.db"))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
row = conn.execute(
"SELECT indices, structure FROM macro_context_log "
"WHERE has_valid_data=1 ORDER BY created_at DESC LIMIT 1"
@@ -748,6 +749,7 @@ def batch_fetch_prices(codes):
try:
import sqlite3
db = sqlite3.connect('/home/hmo/web-dashboard/data/mofin.db')
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
db.row_factory = sqlite3.Row
for raw_code in codes:
raw_code = str(raw_code).split('_')[0]
@@ -817,6 +819,7 @@ def get_price_tencent(code):
try:
import sqlite3
db = sqlite3.connect('/home/hmo/web-dashboard/data/mofin.db')
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
db.row_factory = sqlite3.Row
row = db.execute("SELECT price FROM holdings WHERE code=? AND is_active=1", (raw_code,)).fetchone()
if not row:
@@ -208,6 +208,7 @@ def review():
strategies = decisions.get("decisions", [])
conn = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
stats = {"correct": 0, "wrong": 0, "mixed": 0, "pending": 0, "total": 0}
signal_fails = Counter()
+2
View File
@@ -175,6 +175,7 @@ def audit_pipeline():
"""遍历所有关键数据管道,检查生产者→存储→消费者链路是否完整"""
today = datetime.now().strftime("%Y-%m-%d")
conn = sqlite3.connect(str(DATA_DIR / "mofin.db"))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
pipelines = [
# 管道名, 生产者, 存储位置, 检查SQL/文件, 新鲜度阈值(天)
@@ -269,6 +270,7 @@ def audit_services():
def main():
start = time.time()
conn = sqlite3.connect(str(DATA_DIR / "mofin.db"))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
audit_signals(conn)
audit_stocks(conn)
@@ -497,6 +497,7 @@ def analyze_volume_deep(code):
DATA_DIR = Path(__file__).parent / "data"
try:
conn = sqlite3.connect(str(DATA_DIR / "mofin.db"))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
row = conn.execute("SELECT cache_json FROM mtf_cache WHERE code=?", (code,)).fetchone()
conn.close()
if not row:
@@ -623,6 +624,7 @@ def _calc_scores(code: str) -> dict:
try:
import sqlite3
conn = sqlite3.connect('/home/hmo/MoFin/data/mofin.db')
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
rows = conn.execute(
"SELECT close, high, low, volume FROM stock_daily WHERE code=? ORDER BY date DESC LIMIT 30",
(code,)).fetchall()
+3
View File
@@ -25,6 +25,7 @@ def get_market_id(code):
def get_codes():
"""从 v8.1 5y 策略交易取 58 只股票"""
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
codes = set()
for r in conn.execute("SELECT results_json FROM strategy_research WHERE version='v8.1' AND period_tag='5y'").fetchall():
for t in json.loads(r[0])['trades']:
@@ -98,6 +99,7 @@ def run_backfill():
codes = get_codes()
print(f"=== 同花顺新闻回填: {len(codes)} 只股票 ===", flush=True)
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
init_table(conn)
total_new = 0
for idx, code in enumerate(codes, 1):
@@ -123,6 +125,7 @@ def run_daily():
codes = get_codes()
print(f"=== 同花顺每日更新: {len(codes)} 只股票 ===", flush=True)
conn = sqlite3.connect(DB)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
init_table(conn)
total_new = 0
for idx, code in enumerate(codes, 1):
+1
View File
@@ -24,6 +24,7 @@ DB_PATH = DATA_DIR / "mofin.db"
def get_conn():
conn = sqlite3.connect(str(DB_PATH))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
conn.row_factory = sqlite3.Row
return conn
@@ -23,6 +23,7 @@ for db_path in DBS:
size_before = os.path.getsize(db_path) / 1024 / 1024
try:
c = sqlite3.connect(db_path)
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
c.execute("PRAGMA auto_vacuum=2")
# 只做incremental vacuum,不做full vacuum(耗时太长)
c.execute("PRAGMA incremental_vacuum(50000)")
@@ -81,6 +81,7 @@ def check_cron_jobs():
continue
try:
c = sqlite3.connect(str(db_path))
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
for row in c.execute("""
SELECT id, name, last_status, last_run_at, enabled
FROM cron_jobs WHERE enabled=1
@@ -112,6 +113,7 @@ def run():
for _attempt in range(2):
try:
conn = sqlite3.connect("/home/hmo/MoFin/data/mofin.db")
conn.execute("PRAGMA busy_timeout=30000") # 2026-08-28 防并发写锁(DB损坏教训)
conn.execute("SELECT 1 FROM live_prices LIMIT 1").fetchone()
break
except Exception as e: