feat: 添加热点股追踪数据导出/导入脚本
This commit is contained in:
Executable
+213
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env python3
|
||||
"""热点股追踪数据导出/导入脚本
|
||||
|
||||
用法:
|
||||
导出8月20日数据:
|
||||
python scripts/export_hotspot_data.py export --date 2026-08-20
|
||||
|
||||
导入数据:
|
||||
python scripts/export_hotspot_data.py import --file hotspot_2026-08-20.json
|
||||
|
||||
查看数据库中有哪些日期的数据:
|
||||
python scripts/export_hotspot_data.py list
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
# 添加项目路径
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "backend"))
|
||||
|
||||
from database import get_connection, dict_from_row
|
||||
|
||||
|
||||
def export_data(date: str, output_file: Optional[str] = None) -> dict:
|
||||
"""导出指定日期的热点股追踪数据"""
|
||||
conn = get_connection()
|
||||
try:
|
||||
# 1. 导出每日题材前20
|
||||
themes = conn.execute(
|
||||
"SELECT * FROM daily_top_themes WHERE trade_date = ? ORDER BY rank ASC",
|
||||
(date,),
|
||||
).fetchall()
|
||||
|
||||
# 2. 导出每日核心股票
|
||||
core_stocks = conn.execute(
|
||||
"SELECT * FROM daily_core_stocks WHERE trade_date = ? ORDER BY rank ASC",
|
||||
(date,),
|
||||
).fetchall()
|
||||
|
||||
# 3. 导出每日核心股票与题材的关联
|
||||
stock_themes = conn.execute(
|
||||
"SELECT * FROM daily_core_stock_themes WHERE trade_date = ?",
|
||||
(date,),
|
||||
).fetchall()
|
||||
|
||||
data = {
|
||||
"version": "1.0",
|
||||
"export_time": datetime.now().isoformat(),
|
||||
"trade_date": date,
|
||||
"themes": [dict_from_row(r) for r in themes],
|
||||
"core_stocks": [dict_from_row(r) for r in core_stocks],
|
||||
"stock_themes": [dict_from_row(r) for r in stock_themes],
|
||||
}
|
||||
|
||||
# 统计信息
|
||||
data["stats"] = {
|
||||
"theme_count": len(data["themes"]),
|
||||
"core_stock_count": len(data["core_stocks"]),
|
||||
"stock_theme_count": len(data["stock_themes"]),
|
||||
}
|
||||
|
||||
# 保存到文件
|
||||
if output_file is None:
|
||||
output_file = f"hotspot_{date}.json"
|
||||
|
||||
with open(output_file, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"✅ 导出成功:{output_file}")
|
||||
print(f" 题材数量:{data['stats']['theme_count']}")
|
||||
print(f" 核心股票:{data['stats']['core_stock_count']}")
|
||||
print(f" 股票-题材关联:{data['stats']['stock_theme_count']}")
|
||||
|
||||
return data
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def import_data(input_file: str, dry_run: bool = False) -> bool:
|
||||
"""导入热点股追踪数据"""
|
||||
if not os.path.exists(input_file):
|
||||
print(f"❌ 文件不存在:{input_file}")
|
||||
return False
|
||||
|
||||
with open(input_file, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
|
||||
# 验证数据格式
|
||||
required_keys = ["version", "trade_date", "themes", "core_stocks", "stock_themes"]
|
||||
if not all(k in data for k in required_keys):
|
||||
print("❌ 数据格式错误:缺少必要字段")
|
||||
return False
|
||||
|
||||
date = data["trade_date"]
|
||||
print(f"📅 导入日期:{date}")
|
||||
print(f" 题材数量:{len(data['themes'])}")
|
||||
print(f" 核心股票:{len(data['core_stocks'])}")
|
||||
print(f" 股票-题材关联:{len(data['stock_themes'])}")
|
||||
|
||||
if dry_run:
|
||||
print("\n🔍 试运行模式,不写入数据库")
|
||||
return True
|
||||
|
||||
# 检查是否已存在
|
||||
conn = get_connection()
|
||||
try:
|
||||
existing = conn.execute(
|
||||
"SELECT 1 FROM daily_core_stocks WHERE trade_date = ? LIMIT 1",
|
||||
(date,),
|
||||
).fetchone()
|
||||
|
||||
if existing:
|
||||
print(f"\n⚠️ {date} 的数据已存在,将跳过重复数据(使用 INSERT OR IGNORE)")
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
# 写入数据库
|
||||
conn = get_connection()
|
||||
try:
|
||||
# 导入每日题材前20
|
||||
for item in data["themes"]:
|
||||
# 移除自动生成的id和created_at
|
||||
item.pop("id", None)
|
||||
item.pop("created_at", None)
|
||||
conn.execute(
|
||||
"INSERT OR IGNORE INTO daily_top_themes (trade_date, theme_code, theme_name, bf3, hot_rank, rank) VALUES (?,?,?,?,?,?)",
|
||||
(item["trade_date"], item["theme_code"], item["theme_name"], item["bf3"], item["hot_rank"], item["rank"]),
|
||||
)
|
||||
|
||||
# 导入每日核心股票
|
||||
for item in data["core_stocks"]:
|
||||
# 移除自动生成的id和created_at
|
||||
item.pop("id", None)
|
||||
item.pop("created_at", None)
|
||||
conn.execute(
|
||||
"INSERT OR IGNORE INTO daily_core_stocks (trade_date, stock_code, stock_name, f3, cover_count, rank) VALUES (?,?,?,?,?,?)",
|
||||
(item["trade_date"], item["stock_code"], item["stock_name"], item["f3"], item["cover_count"], item["rank"]),
|
||||
)
|
||||
|
||||
# 导入每日核心股票与题材的关联
|
||||
for item in data["stock_themes"]:
|
||||
# 移除自动生成的id和created_at
|
||||
item.pop("id", None)
|
||||
item.pop("created_at", None)
|
||||
conn.execute(
|
||||
"INSERT OR IGNORE INTO daily_core_stock_themes (trade_date, stock_code, theme_code, theme_name) VALUES (?,?,?,?)",
|
||||
(item["trade_date"], item["stock_code"], item["theme_code"], item["theme_name"]),
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
print(f"\n✅ 导入成功:{date}")
|
||||
return True
|
||||
except Exception as e:
|
||||
conn.rollback()
|
||||
print(f"\n❌ 导入失败:{e}")
|
||||
return False
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def list_dates() -> list[str]:
|
||||
"""列出数据库中所有有数据的日期"""
|
||||
conn = get_connection()
|
||||
try:
|
||||
rows = conn.execute(
|
||||
"SELECT DISTINCT trade_date FROM daily_core_stocks ORDER BY trade_date DESC"
|
||||
).fetchall()
|
||||
return [row["trade_date"] for row in rows]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="热点股追踪数据导出/导入工具")
|
||||
subparsers = parser.add_subparsers(dest="command", help="可用命令")
|
||||
|
||||
# 导出命令
|
||||
export_parser = subparsers.add_parser("export", help="导出数据")
|
||||
export_parser.add_argument("--date", required=True, help="交易日期 (YYYY-MM-DD)")
|
||||
export_parser.add_argument("--output", "-o", help="输出文件路径")
|
||||
|
||||
# 导入命令
|
||||
import_parser = subparsers.add_parser("import", help="导入数据")
|
||||
import_parser.add_argument("--file", "-f", required=True, help="输入文件路径")
|
||||
import_parser.add_argument("--dry-run", action="store_true", help="试运行,不写入数据库")
|
||||
|
||||
# 列表命令
|
||||
subparsers.add_parser("list", help="列出所有有数据的日期")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "export":
|
||||
export_data(args.date, args.output)
|
||||
elif args.command == "import":
|
||||
import_data(args.file, args.dry_run)
|
||||
elif args.command == "list":
|
||||
dates = list_dates()
|
||||
if dates:
|
||||
print("📅 数据库中的日期:")
|
||||
for d in dates:
|
||||
print(f" {d}")
|
||||
else:
|
||||
print("📭 数据库中暂无数据")
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user