refactor(scheduler): 重构每日任务调度逻辑并优化配置路径
- 将等待目标时间逻辑改为基于schedule库的定时任务调度 - 支持后台守护进程模式持续执行定时任务 - 优化命令行参数说明,默认执行时间改为15:30 - 简化立即执行和循环运行的逻辑 - 修改SSH私钥路径为相对于项目根目录 - 更新rotation.yaml配置中指数及加密货币标签说明 - 回测开始日期由2022-01-01调整为2020-01-01 refactor(report): 优化轮动策略绩效报告图表与指标展示 - 新增策略与基准绩效指标对比表格,展示累计收益、年化收益等关键指标 - 调整绩效表布局,增加绩效指标面板高度,保持与信号表格一致视觉 - 丰富绘图函数参数,支持传入绩效指标字典避免重复计算 - 规范调仓信号表操作列索引及样式,保持统一字体大小和行高 - 净值曲线、回撤及持仓分布面板分离,调整图表索引和标题名称 - 优化持仓分布图显示,提升整体报告信息完整性与易读性
This commit is contained in:
@@ -18,7 +18,7 @@ import os
|
||||
import time
|
||||
import argparse
|
||||
import subprocess
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到路径
|
||||
@@ -29,6 +29,7 @@ from dotenv import load_dotenv
|
||||
load_dotenv(project_root / ".env")
|
||||
|
||||
from loguru import logger
|
||||
import schedule
|
||||
import tushare as ts
|
||||
from core.common.notify import DingTalkBot
|
||||
from core.common.oss_utils import upload_image_to_oss
|
||||
@@ -182,36 +183,23 @@ def send_report_to_dingtalk(chart_path: str, summary_text: str = "") -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def wait_until_target_time(target_time: str = "15:00"):
|
||||
def setup_schedule(target_time: str = "15:30", config_path: str = "config/strategies/rotation.yaml"):
|
||||
"""
|
||||
等待直到目标时间
|
||||
设置定时任务
|
||||
|
||||
Args:
|
||||
target_time: 目标时间 (HH:MM)
|
||||
target_time: 执行时间 (HH:MM)
|
||||
config_path: 配置文件路径
|
||||
"""
|
||||
while True:
|
||||
now = datetime.now()
|
||||
target = now.replace(
|
||||
hour=int(target_time.split(":")[0]),
|
||||
minute=int(target_time.split(":")[1]),
|
||||
second=0,
|
||||
microsecond=0
|
||||
)
|
||||
logger.info(f"设置定时任务: 每天 {target_time} 执行")
|
||||
|
||||
# 如果目标时间已过,等到明天
|
||||
if target < now:
|
||||
target += timedelta(days=1)
|
||||
logger.info(f"目标时间已过,等到明天 {target_time}")
|
||||
# 清除所有现有任务
|
||||
schedule.clear()
|
||||
|
||||
wait_seconds = (target - now).total_seconds()
|
||||
# 添加每日任务
|
||||
schedule.every().day.at(target_time).do(daily_task, config_path)
|
||||
|
||||
if wait_seconds > 60:
|
||||
logger.info(f"等待 {target_time},还需 {wait_seconds/60:.0f} 分钟...")
|
||||
time.sleep(60) # 每分钟检查一次
|
||||
else:
|
||||
logger.info(f"即将到达目标时间,等待 {wait_seconds:.0f} 秒...")
|
||||
time.sleep(wait_seconds)
|
||||
break
|
||||
logger.info("定时任务设置完成,等待执行...")
|
||||
|
||||
|
||||
def daily_task(config_path: str = "config/strategies/rotation.yaml"):
|
||||
@@ -252,13 +240,24 @@ def daily_task(config_path: str = "config/strategies/rotation.yaml"):
|
||||
logger.info("每日任务完成")
|
||||
|
||||
|
||||
def run_scheduler_loop():
|
||||
"""运行调度器循环"""
|
||||
logger.info("启动调度器循环,按 Ctrl+C 停止")
|
||||
try:
|
||||
while True:
|
||||
schedule.run_pending()
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
logger.info("调度器已停止")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="ETF策略每日定时任务")
|
||||
parser.add_argument(
|
||||
"--time",
|
||||
type=str,
|
||||
default="15:30",
|
||||
help="执行时间 (HH:MM),默认15:00",
|
||||
help="执行时间 (HH:MM),默认15:30",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--config",
|
||||
@@ -269,12 +268,12 @@ def main():
|
||||
parser.add_argument(
|
||||
"--run-now",
|
||||
action="store_true",
|
||||
help="立即执行一次(不等待指定时间)",
|
||||
help="立即执行一次(不启动定时任务)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--loop",
|
||||
"--daemon",
|
||||
action="store_true",
|
||||
help="循环运行(每天执行)",
|
||||
help="后台运行(持续执行定时任务)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -284,18 +283,17 @@ def main():
|
||||
if args.run_now:
|
||||
# 立即执行一次
|
||||
daily_task(args.config)
|
||||
elif args.loop:
|
||||
# 循环运行
|
||||
logger.info(f"启动定时任务,每天 {args.time} 执行")
|
||||
while True:
|
||||
wait_until_target_time(args.time)
|
||||
daily_task(args.config)
|
||||
# 等待一段时间避免重复执行
|
||||
time.sleep(60)
|
||||
elif args.daemon:
|
||||
# 后台运行模式
|
||||
setup_schedule(args.time, args.config)
|
||||
run_scheduler_loop()
|
||||
else:
|
||||
# 等待到目标时间执行一次
|
||||
wait_until_target_time(args.time)
|
||||
# 默认:设置定时任务并执行一次(用于测试)
|
||||
setup_schedule(args.time, args.config)
|
||||
logger.info("执行一次任务用于测试...")
|
||||
daily_task(args.config)
|
||||
logger.info("测试完成,启动定时任务循环(按 Ctrl+C 停止)...")
|
||||
run_scheduler_loop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user