fix: 全脚本sqlite连接加busy_timeout防并发写锁(2026-08-27DB损坏教训,43文件75处)
This commit is contained in:
@@ -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 "
|
||||
|
||||
@@ -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()]
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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' "
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user