v1.9.0: ①自动化开关即时保存(切换即生效不回落) ②持仓跟踪推送方式(聚合一起发默认/分散发) ③行情数据增量延续到最新交易日(每小时自动+手动按钮)
This commit is contained in:
+118
-1
@@ -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="跳过向量索引重建")
|
||||
|
||||
Reference in New Issue
Block a user