7 Sprints 全部完成: Sprint 0: 基础设施 (DataManager + MariaDB) Sprint 1: 因子引擎 (34因子/12分类) Sprint 2: VectorBT 回测 (5策略+截面) Sprint 3: Optuna 优化 (+Walk-Forward) Sprint 4: ML 模型 (LightGBM+CatBoost) Sprint 5: Qwen 情绪因子 (三源新闻+日期对齐) Sprint 6: Agent 系统 (4Agent+日报.md/.html) 生产加固 (15项): Tushare双源fallback, SSH自动恢复, pool_pre_ping, save_daily先删后插, load_dotenv绝对路径, 日报5d/20d修复, RiskAgent改上证指数, 昨日对比+数据截止, mac_report utf8mb4, CLAUDE-*.md 9条已知Bug, demo全参数化, djapi数据源归一化, indexDatas API修正 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
113 lines
3.5 KiB
Python
113 lines
3.5 KiB
Python
"""
|
|
newsRedo — 手动重新执行新闻 AI 分割流程。
|
|
|
|
用法:
|
|
python newsRedo.py # 默认当天日期
|
|
python newsRedo.py 20250601 # yyyymmdd 格式
|
|
python newsRedo.py 2025-06-01 # yyyy-mm-dd 格式
|
|
|
|
流程:
|
|
1. 检查 xwlb_daily_ext 是否已有 >5 条 → 已处理过,正常跳过
|
|
2. 检查 xwlb_daily 是否有当天记录 → 无记录则先跑 getVideo5 全流程
|
|
3. 有记录但未处理 → 直接执行 news_to_db() AI 分割
|
|
"""
|
|
import sys
|
|
import re
|
|
import logging
|
|
from datetime import datetime
|
|
from mysqlHandle import MySQLDB
|
|
from newsProcess import news_to_db
|
|
from getVideo5 import process_videos
|
|
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def _parse_date(date_str):
|
|
"""解析日期,返回 (yyyymmdd_str, yyyy_mm_dd_str),或报错退出"""
|
|
if not date_str:
|
|
today = datetime.now()
|
|
d8 = today.strftime('%Y%m%d')
|
|
d10 = today.strftime('%Y-%m-%d')
|
|
logger.info(f"未指定日期,使用当天: {d10}")
|
|
return d8, d10
|
|
|
|
if re.match(r'^\d{4}-\d{2}-\d{2}$', date_str):
|
|
try:
|
|
datetime.strptime(date_str, '%Y-%m-%d')
|
|
except ValueError:
|
|
print(f"无效日期: {date_str}")
|
|
sys.exit(1)
|
|
return date_str.replace('-', ''), date_str
|
|
|
|
if re.match(r'^\d{8}$', date_str):
|
|
try:
|
|
datetime.strptime(date_str, '%Y%m%d')
|
|
except ValueError:
|
|
print(f"无效日期: {date_str}")
|
|
sys.exit(1)
|
|
return date_str, f"{date_str[:4]}-{date_str[4:6]}-{date_str[6:8]}"
|
|
|
|
print("日期格式错误,请使用 yyyymmdd 或 yyyy-mm-dd 格式")
|
|
sys.exit(1)
|
|
|
|
|
|
def main():
|
|
date_str = sys.argv[1] if len(sys.argv) > 1 else None
|
|
date_d8, date_d10 = _parse_date(date_str)
|
|
|
|
db = MySQLDB()
|
|
|
|
# 1. 检查 xwlb_daily_ext 是否已处理过
|
|
try:
|
|
ext_count = db.query_data(
|
|
table="xwlb_daily_ext",
|
|
columns="COUNT(*) as count",
|
|
where="news_date = %s",
|
|
params=(date_d10,)
|
|
)
|
|
if ext_count and ext_count[0]['count'] > 5:
|
|
logger.info(f"日期 {date_d10} 已有 {ext_count[0]['count']} 条精编记录,无需重新处理。")
|
|
return
|
|
except Exception as e:
|
|
logger.error(f"查询 xwlb_daily_ext 失败: {e}")
|
|
finally:
|
|
db.close()
|
|
|
|
db = MySQLDB()
|
|
|
|
# 2. 检查 xwlb_daily 是否有当天数据
|
|
try:
|
|
daily_count = db.query_data(
|
|
table="xwlb_daily",
|
|
columns="COUNT(*) as count",
|
|
where="news_days = %s",
|
|
params=(date_d10,)
|
|
)
|
|
has_daily = daily_count and daily_count[0]['count'] > 0
|
|
except Exception as e:
|
|
logger.error(f"查询 xwlb_daily 失败: {e}")
|
|
has_daily = False
|
|
finally:
|
|
db.close()
|
|
|
|
# 3. 分支处理
|
|
if has_daily:
|
|
logger.info(f"日期 {date_d10} 在 xwlb_daily 中有记录,直接执行 AI 分割。")
|
|
try:
|
|
news_to_db(date_d10)
|
|
except Exception as e:
|
|
logger.error(f"news_to_db 执行出错: {e}")
|
|
sys.exit(1)
|
|
else:
|
|
logger.info(f"日期 {date_d10} 在 xwlb_daily 中无记录,重新执行视频下载全流程。")
|
|
try:
|
|
process_videos(date_d8, date_d8)
|
|
except Exception as e:
|
|
logger.error(f"process_videos 执行出错: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|