v1.9.0: ①自动化开关即时保存(切换即生效不回落) ②持仓跟踪推送方式(聚合一起发默认/分散发) ③行情数据增量延续到最新交易日(每小时自动+手动按钮)

This commit is contained in:
2026-09-09 15:59:35 +08:00
parent 9340577f7b
commit f20fe2f4f0
9 changed files with 317 additions and 32 deletions
+118 -1
View File
@@ -21,7 +21,7 @@ import random
import sys
from config import (CHROMA_NEWS_COLLECTION, CHROMA_PROFILE_COLLECTION, LOG_DIR)
from database import init_db, executemany, query_one, wipe_all
from database import init_db, executemany, query_one, query, execute, wipe_all
from rag import vector_store as vs
random.seed(42)
@@ -427,6 +427,123 @@ def build_vectors(news, stocks):
print(f" 概况索引条数: {vs.collection_count(CHROMA_PROFILE_COLLECTION)}")
# ===================================================================== 增量更新
def extend_daily(target_date=None):
"""把模拟行情/指数/新闻从库里最新日期增量扩展到最新交易日(幂等,可重复调用)。
返回 (起始日期, 结束日期, 新增交易日数, 新增新闻数) 或 (None, None, 0, 0)。
"""
init_db()
last = query_one("SELECT MAX(date) d FROM stock_daily")
last_date = last["d"] if last else None
today = dt.date.today()
if target_date is None:
target_date = today
if isinstance(target_date, str):
target_date = dt.date.fromisoformat(target_date)
# 最新交易日:今天若是周末则回退到周五
while target_date.weekday() >= 5:
target_date -= dt.timedelta(days=1)
if last_date:
start = dt.date.fromisoformat(last_date) + dt.timedelta(days=1)
else:
start = target_date - dt.timedelta(days=179)
dates = []
d = start
while d <= target_date:
if d.weekday() < 5:
dates.append(d.isoformat())
d += dt.timedelta(days=1)
if not dates:
return None, None, 0, 0
print(f">>> 增量扩展行情 {dates[0]} ~ {dates[-1]}{len(dates)} 个交易日)...")
stocks = query("SELECT code,name,industry,board,total_shares,float_shares FROM stocks")
daily = []
index = {}
price = {}
for s in stocks:
rows = query("SELECT date,close,volume FROM stock_daily WHERE code=? ORDER BY date DESC LIMIT 30",
(s["code"],))
if not rows:
continue
last_close = rows[0]["close"]
base_vol = rows[0]["volume"] or 1
closes = [r["close"] for r in reversed(rows)]
rets = [(closes[i + 1] - closes[i]) / closes[i] for i in range(len(closes) - 1)]
mean_r = sum(rets) / max(len(rets), 1)
vol = (sum((r - mean_r) ** 2 for r in rets) / max(len(rets), 1)) ** 0.5 if rets else 0.02
vol = max(0.005, min(0.05, vol))
drift = 0.0002
p = prev = last_close
for dd in dates:
r = random.gauss(drift, vol)
if random.random() < 0.02:
r += random.gauss(0, vol * 1.6)
prev = p
p = max(0.5, p * (1 + r))
open_p = prev * (1 + random.gauss(0, vol * 0.5))
high = max(open_p, p) * (1 + abs(random.gauss(0, vol * 0.35)))
low = min(open_p, p) * (1 - abs(random.gauss(0, vol * 0.35)))
volume = base_vol * (1 + 1.5 * abs(r) / max(vol, 1e-6)) * random.uniform(0.6, 1.4)
amount = volume * (open_p + p) / 2
chg = (p - prev) / prev * 100
daily.append((s["code"], dd, round(open_p, 2), round(high, 2), round(low, 2),
round(p, 2), round(volume, 0), round(amount, 0), round(chg, 2)))
price[s["code"]] = p
# 指数延续
idx_rows = query("SELECT date,sh,sz,cy FROM market_index ORDER BY date DESC LIMIT 1")
if idx_rows:
sh, sz, cy = idx_rows[0]["sh"], idx_rows[0]["sz"], idx_rows[0]["cy"]
else:
sh, sz, cy = 3245.0, 10580.0, 2120.0
for dd in dates:
sh_r = sum(random.gauss(0.0004, 0.008) for _ in range(6)) / 6
sz_r = sh_r + random.gauss(0, 0.004)
cy_r = sh_r + random.gauss(0, 0.006)
sh *= (1 + sh_r); sz *= (1 + sz_r); cy *= (1 + cy_r)
index[dd] = {"sh": round(sh, 2), "sz": round(sz, 2), "cy": round(cy, 2)}
executemany(
"INSERT OR REPLACE INTO stock_daily(code,date,open,high,low,close,volume,amount,change_pct) "
"VALUES(?,?,?,?,?,?,?,?,?)", daily)
executemany("INSERT OR REPLACE INTO market_index(date,sh,sz,cy) VALUES(?,?,?,?)",
[(d, v["sh"], v["sz"], v["cy"]) for d, v in index.items()])
gm = _gen_global(dates)
executemany("INSERT OR REPLACE INTO global_markets(date,data) VALUES(?,?)",
[(d, json.dumps(v, ensure_ascii=False)) for d, v in gm.items()])
# 回填市值
for code in price:
execute("UPDATE stocks SET market_cap=ROUND((SELECT close FROM stock_daily WHERE code=? ORDER BY date DESC LIMIT 1)*total_shares,2) WHERE code=?",
(code, code))
# 新增新闻(仅保留落在新增日期内的)
news = gen_news(dates, price)
date_set = set(dates)
new_news = [n for n in news if n["publish_date"] in date_set]
if new_news:
executemany(
"INSERT INTO news(title,content,source,category,publish_date,related_stocks,sentiment,is_positive) "
"VALUES(?,?,?,?,?,?,?,?)",
[(n["title"], n["content"], n["source"], n["category"], n["publish_date"],
n["related"], n["sentiment"], n["is_positive"]) for n in new_news])
# 向量追加
ids, docs, metas = [], [], []
for n in new_news:
for code in n["related"].split(","):
ids.append(f"news-{n['publish_date']}-{n['title']}-{code}")
docs.append(f"{n['title']}\n{n['content']}")
metas.append({"code": code, "title": n["title"], "date": n["publish_date"],
"category": n["category"], "sentiment": n["sentiment"], "news_id": 0})
try:
for i in range(0, len(ids), 16):
vs.add_documents(ids[i:i + 16], docs[i:i + 16], metas[i:i + 16], CHROMA_NEWS_COLLECTION)
except Exception as e:
print("新闻向量追加失败(可忽略,下次重灌会重建):", e)
print(f">>> 新增新闻 {len(new_news)} 条(向量已追加)")
return dates[0], dates[-1], len(dates), len(new_news)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--skip-vector", action="store_true", help="跳过向量索引重建")