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

107 lines
5.4 KiB
Python

#!/usr/bin/env python3
"""实盘数据累积系统(MoFin):每日收盘后累积重点行业/个股的实盘数据。
目标:从当前开始累积实盘数据(资金流、行业热度、龙虎榜代理等),定时回测调整策略。
数据源(MoFin已有):stock_daily(日线)、stock_capital_flow(资金流)、stock_news(新闻)、sector_index_daily(行业指数)
增量:每日收盘后计算并累积:
- 重点行业(近期热点)的资金流/新闻热度/行业趋势
- 重点个股的龙虎榜代理(大单/主力资金)
- 市场状态(大盘RSI/ADX/行业强弱分布)
输出:/home/hmo/MoFin/data/live_data/ 目录下按日期累积
"""
import sqlite3, json, os, pandas as pd, numpy as np
from datetime import datetime, timedelta
DB = "/home/hmo/MoFin/data/mofin.db"
LIVE_DIR = "/home/hmo/MoFin/data/live_data"
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 query_only=ON")
if date_str is None:
date_str = datetime.now().strftime('%Y-%m-%d')
print(f"Collecting live data for {date_str}")
# 1. 大盘状态(上证)
mkt = pd.read_sql("SELECT date, close FROM sector_index_daily WHERE sector='上证指数' ORDER BY date DESC LIMIT 60", conn)
mkt['date'] = mkt['date'].astype(str)
mkt = mkt.sort_values('date').reset_index(drop=True)
mkt['ret'] = mkt['close'].pct_change()
mkt['gain'] = mkt['ret'].clip(lower=0)
mkt['loss'] = (-mkt['ret']).clip(lower=0)
mkt['ag'] = mkt['gain'].rolling(14).mean()
mkt['al'] = mkt['loss'].rolling(14).mean()
mkt['rsi'] = 100 - 100 / (1 + mkt['ag'] / mkt['al'].replace(0, np.nan))
mkt['rsi'] = mkt['rsi'].fillna(50)
mkt['ma20'] = mkt['close'].rolling(20).mean()
latest = mkt.iloc[-1] if len(mkt) else None
# 2. 行业热度(当日新闻数)
news = pd.read_sql("SELECT code, date FROM stock_news WHERE date >= ? AND date <= ?", conn,
params=[(pd.Timestamp(date_str) - pd.Timedelta(days=5)).strftime('%Y-%m-%d'), date_str])
news['date'] = news['date'].astype(str).str[:10]
secmap = dict(conn.execute("SELECT code, sector FROM stock_sectors_em").fetchall())
news['sector'] = news['code'].map(secmap)
sector_news = news.groupby('sector').size().reset_index(name='news_count').sort_values('news_count', ascending=False)
# 3. 行业资金流(当日主力资金净流入)
flow = pd.read_sql("SELECT code, date, main_net FROM stock_capital_flow WHERE date = ?", conn, params=[date_str])
flow['sector'] = flow['code'].map(secmap)
sector_flow = flow.groupby('sector')['main_net'].sum().reset_index().sort_values('main_net', ascending=False)
# 4. 行业指数(当日涨跌)
sec_idx = pd.read_sql("SELECT sector, date, close, change_pct FROM sector_index_daily WHERE date = ?", conn, params=[date_str])
sec_idx['sector'] = sec_idx['sector'].astype(str)
# 5. 重点行业识别(新闻热度+资金流+行业指数强势)
merged = sector_news.merge(sector_flow, on='sector', how='outer').merge(
sec_idx[['sector', 'change_pct']], on='sector', how='outer').fillna(0)
merged['heat_score'] = merged['news_count'] * 0.5 + merged['main_net'].clip(lower=0) / 1e8 * 0.3 + merged['change_pct'] * 10
hot_sectors = merged.sort_values('heat_score', ascending=False).head(20)
# 6. 重点个股(热点行业内的强势股)
# 用当日涨幅 + 资金流筛选
stocks = pd.read_sql(f"""
SELECT sd.code, sd.date, sd.close, sd.open, sd.high, sd.low, sd.volume,
scf.main_net, scf.main_pct
FROM stock_daily sd
LEFT JOIN stock_capital_flow scf ON sd.code = scf.code AND sd.date = scf.date
WHERE sd.date = ?
""", conn, params=[date_str])
stocks['sector'] = stocks['code'].map(secmap)
# 只保留热点行业的股票
hot_sector_names = set(hot_sectors['sector'].head(10))
hot_stocks = stocks[stocks['sector'].isin(hot_sector_names)].copy()
hot_stocks['ret'] = hot_stocks.groupby('code')['close'].transform(lambda x: x.pct_change())
hot_stocks = hot_stocks.sort_values(['sector', 'ret'], ascending=[True, False])
top_stocks = hot_stocks.groupby('sector').head(5)
snapshot = {
"date": date_str,
"market": {
"close": float(latest['close']) if latest is not None else None,
"rsi": float(latest['rsi']) if latest is not None and not np.isnan(latest['rsi']) else None,
"ma20": float(latest['ma20']) if latest is not None and not np.isnan(latest['ma20']) else None,
},
"hot_sectors": hot_sectors.to_dict('records'),
"top_stocks": top_stocks.to_dict('records'),
"sector_news": sector_news.head(20).to_dict('records'),
"sector_flow": sector_flow.head(20).to_dict('records'),
}
# 保存
out_path = os.path.join(LIVE_DIR, f"{date_str}.json")
with open(out_path, 'w', encoding='utf-8') as f:
json.dump(snapshot, f, ensure_ascii=False, indent=1, default=str)
print(f" Saved: {out_path} ({os.path.getsize(out_path)//1024}KB)")
print(f" Hot sectors: {hot_sectors['sector'].head(5).tolist()}")
conn.close()
return snapshot
if __name__ == '__main__':
import sys
date_str = sys.argv[1] if len(sys.argv) > 1 else None
collect_daily_snapshot(date_str)