Files
MoFin/deploy/profile-scripts/pool_news_collector.py
T

104 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""pool_news_collector.py — 池子新闻采集(自选+持仓,盘中高频)
2026-08-13 新增(老莫架构):盘前全量跑(选股),盘中只跑池子里的(自选+持仓),增加频率。
- 盘前:news_collector_full b0-b7(全市场 4272 只,8批)
- 盘中:pool_news_collector(池子 82 只,*/30 盘中)
池子 = 持仓(holdings shares>0) + 自选(holding_strategies 自选策略)
"""
import sys, os, json, time, sqlite3
from datetime import datetime, timedelta
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from news_collector import fetch_stock_news, init_table
DB_PATH = Path("/home/hmo/MoFin/data/mofin.db")
SLEEP = 0.4 # 请求间隔(防东财封)
RECENT_DAYS = 3 # 只存近 3 天新闻(news3 因子口径)
def _singleton_guard(tag):
lock_dir = Path("/tmp/mofin_locks")
lock_dir.mkdir(exist_ok=True)
try:
import fcntl
fd = os.open(str(lock_dir / f"{tag}.lock"), os.O_CREAT | os.O_RDWR)
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
return fd
except OSError:
return None
def get_pool_codes(conn):
"""池子:持仓 + 自选(去重)"""
codes = set()
# 持仓
for r in conn.execute("SELECT DISTINCT code FROM holdings WHERE is_active=1 AND shares>0"):
codes.add(r[0])
# 自选
for r in conn.execute("SELECT DISTINCT code FROM holding_strategies WHERE status='active' AND decision_type='自选策略'"):
codes.add(r[0])
return sorted(codes)
def main():
tag = "pool_news_collector"
guard = _singleton_guard(tag)
if guard is None:
print(f"[{tag}] 已有实例在运行,跳过", file=sys.stderr)
return
conn = sqlite3.connect(DB_PATH)
init_table(conn)
cur = conn.cursor()
codes = get_pool_codes(conn)
print(f"[{tag}] {datetime.now().strftime('%H:%M:%S')} 池子新闻采集 开始", flush=True)
print(f" 池子: {len(codes)} 只(持仓+自选)", flush=True)
since = (datetime.now() - timedelta(days=RECENT_DAYS)).strftime("%Y-%m-%d")
t0 = time.time()
ok = empty = fail = new = 0
for idx, code in enumerate(codes, 1):
try:
arts = fetch_stock_news(code, max_pages=1)
if not arts:
empty += 1
continue
n = 0
for a in arts:
dstr = (a.get("date") or "")[:10]
if dstr and dstr >= since:
try:
cur.execute(
"INSERT OR IGNORE INTO stock_news (code, title, content, source, date, url) "
"VALUES (?,?,?,?,?,?)",
(code, a.get("title", ""), a.get("content", ""),
a.get("source", "eastmoney"), a.get("date", ""), a.get("url", "")))
if cur.rowcount:
n += 1
except Exception:
pass
new += n
ok += 1
except Exception as e:
fail += 1
if fail <= 5:
print(f" FAIL {code}: {str(e)[:60]}", flush=True)
if idx % 20 == 0:
conn.commit()
print(f" [{idx}/{len(codes)}] ok={ok} empty={empty} fail={fail} 新增={new} | {time.time()-t0:.0f}s", flush=True)
time.sleep(SLEEP)
conn.commit()
conn.close()
dt = time.time() - t0
print(f"[{tag}] 完成: {ok}/{len(codes)} 成功, {empty} 无新闻, {fail} 失败, 新增 {new} 条, 耗时 {dt:.0f}s", flush=True)
if __name__ == "__main__":
main()