Files
aiagents-stock/monitor_scheduler.py
2025-10-16 18:27:24 +08:00

236 lines
8.6 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
实时监测定时调度模块
支持交易日交易时间自动启动关闭监测服务
"""
import schedule
import time
import threading
from datetime import datetime, time as dtime
from typing import Dict, Optional
import json
import os
class TradingTimeScheduler:
"""交易时间调度器"""
def __init__(self, monitor_service):
self.monitor_service = monitor_service
self.running = False
self.thread = None
self.config = self._load_config()
def _load_config(self) -> Dict:
"""加载调度配置"""
config_file = "monitor_schedule_config.json"
default_config = {
"enabled": False, # 默认不启用自动调度
"market": "CN", # CN=中国A股, US=美股, HK=港股
"trading_hours": {
"CN": [
{"start": "09:30", "end": "11:30"}, # 上午
{"start": "13:00", "end": "15:00"} # 下午
],
"US": [
{"start": "21:30", "end": "04:00"} # 美股时间(北京时间)
],
"HK": [
{"start": "09:30", "end": "12:00"}, # 上午
{"start": "13:00", "end": "16:00"} # 下午
]
},
"trading_days": [1, 2, 3, 4, 5], # 周一到周五
"auto_stop": True, # 收盘后自动停止
"pre_market_minutes": 5, # 提前5分钟启动
"post_market_minutes": 5 # 延后5分钟停止
}
if os.path.exists(config_file):
try:
with open(config_file, 'r', encoding='utf-8') as f:
loaded_config = json.load(f)
# 合并配置,保留默认值
default_config.update(loaded_config)
except Exception as e:
print(f"加载调度配置失败,使用默认配置: {e}")
return default_config
def _save_config(self):
"""保存调度配置"""
config_file = "monitor_schedule_config.json"
try:
with open(config_file, 'w', encoding='utf-8') as f:
json.dump(self.config, f, indent=2, ensure_ascii=False)
print(f"✅ 调度配置已保存")
except Exception as e:
print(f"❌ 保存调度配置失败: {e}")
def update_config(self, **kwargs):
"""更新配置"""
self.config.update(kwargs)
self._save_config()
def is_trading_day(self) -> bool:
"""判断是否为交易日"""
now = datetime.now()
weekday = now.weekday() + 1 # 转换为1-7(周一到周日)
# 检查是否在交易日列表中
if weekday not in self.config['trading_days']:
return False
# TODO: 可以进一步检查是否为法定节假日
# 这里简单判断为工作日即交易日
return True
def is_trading_time(self) -> bool:
"""判断当前是否在交易时间内"""
if not self.is_trading_day():
return False
now = datetime.now()
current_time = now.time()
market = self.config.get('market', 'CN')
trading_hours = self.config['trading_hours'].get(market, [])
for period in trading_hours:
start_time = datetime.strptime(period['start'], '%H:%M').time()
end_time = datetime.strptime(period['end'], '%H:%M').time()
# 处理跨天的情况(如美股)
if start_time > end_time:
if current_time >= start_time or current_time <= end_time:
return True
else:
if start_time <= current_time <= end_time:
return True
return False
def get_next_trading_time(self) -> Optional[str]:
"""获取下一个交易时间"""
if not self.is_trading_day():
return "非交易日"
now = datetime.now()
current_time = now.time()
market = self.config.get('market', 'CN')
trading_hours = self.config['trading_hours'].get(market, [])
for period in trading_hours:
start_time = datetime.strptime(period['start'], '%H:%M').time()
if current_time < start_time:
return period['start']
return "交易时间已结束"
def start_scheduler(self):
"""启动调度器"""
if self.running:
print("⚠️ 调度器已在运行")
return
if not self.config.get('enabled', False):
print("⚠️ 调度器未启用")
return
self.running = True
self.thread = threading.Thread(target=self._schedule_loop, daemon=True)
self.thread.start()
print("✅ 调度器已启动")
def stop_scheduler(self):
"""停止调度器"""
self.running = False
schedule.clear()
if self.thread:
self.thread.join(timeout=5)
print("⏹️ 调度器已停止")
def _schedule_loop(self):
"""调度循环"""
# 清空之前的任务
schedule.clear()
# 根据市场设置定时任务
market = self.config.get('market', 'CN')
trading_hours = self.config['trading_hours'].get(market, [])
for period in trading_hours:
start_time = period['start']
end_time = period['end']
# 设置开盘启动任务
schedule.every().day.at(start_time).do(self._auto_start_monitoring)
print(f"📅 已设置开盘启动任务: {start_time}")
# 设置收盘停止任务
if self.config.get('auto_stop', True):
schedule.every().day.at(end_time).do(self._auto_stop_monitoring)
print(f"📅 已设置收盘停止任务: {end_time}")
# 每分钟检查一次是否在交易时间
print("🔄 调度器循环已启动")
while self.running:
try:
schedule.run_pending()
# 智能检测:如果当前在交易时间但服务未运行,则启动
if self.is_trading_time() and not self.monitor_service.running:
print("🔔 检测到交易时间,自动启动监测服务")
self.monitor_service.start_monitoring()
# 智能检测:如果当前不在交易时间但服务在运行,且auto_stop=True,则停止
if not self.is_trading_time() and self.monitor_service.running and self.config.get('auto_stop', True):
print("🔔 检测到非交易时间,自动停止监测服务")
self.monitor_service.stop_monitoring()
time.sleep(60) # 每分钟检查一次
except Exception as e:
print(f"❌ 调度器错误: {e}")
time.sleep(60)
def _auto_start_monitoring(self):
"""自动启动监测"""
if self.is_trading_day():
print(f"🔔 定时启动监测服务 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
if not self.monitor_service.running:
self.monitor_service.start_monitoring()
else:
print("⏸️ 非交易日,跳过启动")
def _auto_stop_monitoring(self):
"""自动停止监测"""
print(f"🔔 定时停止监测服务 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
if self.monitor_service.running:
self.monitor_service.stop_monitoring()
def get_status(self) -> Dict:
"""获取调度器状态"""
return {
'scheduler_running': self.running,
'scheduler_enabled': self.config.get('enabled', False),
'is_trading_day': self.is_trading_day(),
'is_trading_time': self.is_trading_time(),
'market': self.config.get('market', 'CN'),
'next_trading_time': self.get_next_trading_time(),
'monitor_service_running': self.monitor_service.running,
'auto_stop': self.config.get('auto_stop', True)
}
# 全局调度器实例(延迟初始化)
_scheduler_instance = None
def get_scheduler(monitor_service=None):
"""获取调度器实例"""
global _scheduler_instance
if _scheduler_instance is None and monitor_service is not None:
_scheduler_instance = TradingTimeScheduler(monitor_service)
return _scheduler_instance