This commit is contained in:
2026-08-29 12:00:51 +08:00
parent 4e28182b3f
commit 9a43aaba23
11 changed files with 19 additions and 38 deletions

View File

@@ -1,11 +1,11 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging as log
import logging
from logging.handlers import TimedRotatingFileHandler
import os
import sys
import time
import schedule
import config
from dataclasses import dataclass
import yaml
@@ -16,6 +16,12 @@ GLOBAL_CONFIG_PATH = os.path.join(PROJECT_ROOT, "etc", "_global.yaml")
if PROJECT_ROOT not in sys.path:
sys.path.insert(0, PROJECT_ROOT)
logging.basicConfig(
level=logging.INFO,
format='[%(levelname)s] %(asctime)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
from sdk import APIError, Client
from strategy.trend.boot import StartTrend
from strategy.ipo import AutoBuyIpo
@@ -45,16 +51,16 @@ def check_single_instance(project_root: str) -> bool:
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
handle = kernel32.CreateMutexW(None, True, mutex_name)
if not handle or handle == invalid_handle_value:
log.error(f"无法创建互斥锁,错误代码:{ctypes.get_last_error()}")
logging.error(f"无法创建互斥锁,错误代码:{ctypes.get_last_error()}")
return False
if ctypes.get_last_error() == error_already_exists:
log.error("程序已在运行中,无法启动多个实例")
logging.error("程序已在运行中,无法启动多个实例")
kernel32.CloseHandle(handle)
return False
log.info(f"成功获取互斥锁:{mutex_name}")
logging.info(f"成功获取互斥锁:{mutex_name}")
return True
except Exception as exc:
log.error(f"单实例检测失败:{exc}", exc_info=True)
logging.error(f"单实例检测失败:{exc}", exc_info=True)
return False
@@ -66,11 +72,11 @@ def wait_for_qmt_api(retry_interval: float = 5.0) -> None:
while not retry_event.is_set():
try:
client.assets()
log.info(f"API 服务已连通:{config.global_config.qmt_base_url}")
logging.info(f"API 服务已连通:{config.global_config.qmt_base_url}")
client.close()
return
except (APIError, httpx.RequestError) as exc:
log.warning(
logging.warning(
"API 服务未就绪:%s%g 秒后重试:%s",
config.global_config.qmt_base_url,
retry_interval,
@@ -78,27 +84,6 @@ def wait_for_qmt_api(retry_interval: float = 5.0) -> None:
)
retry_event.wait(retry_interval)
def configure_logging(data_dir: str) -> None:
"""同时输出控制台日志和按天轮转的文本日志。"""
log_dir = os.path.join(data_dir, "logs")
os.makedirs(log_dir, exist_ok=True)
root = log.getLogger()
root.setLevel(log.INFO)
formatter = log.Formatter("%(asctime)s [%(levelname)s] %(message)s")
if not root.handlers:
console = log.StreamHandler()
console.setFormatter(formatter)
root.addHandler(console)
file_handler = TimedRotatingFileHandler(
os.path.join(log_dir, "py-client.log"),
when="midnight",
interval=1,
backupCount=30,
encoding="utf-8",
)
file_handler.setFormatter(formatter)
root.addHandler(file_handler)
def wait_for_any_key() -> None:
print("按任意键退出...", flush=True)
@@ -121,13 +106,15 @@ def main() -> int:
config.load()
if config.global_config is None or config.account_config is None:
raise RuntimeError("配置尚未加载,请先调用 config.load()")
configure_logging(config.global_config.qmt_data_dir)
wait_for_qmt_api()
# 自动打新与主策略隔离;申购服务失败不能阻止趋势策略启动。
AutoBuyIpo()
schedule.every().day.at("10:00").do(AutoBuyIpo)
schedule.run_pending()
logging.info("IPO 自动打新启动成功")
STRATEGIES[config.account_config.strategy].start_strategy()
logging.info("%s 策略启动成功",config.account_config.strateg)
return 0
except (OSError, yaml.YAMLError, ValueError, RuntimeError, KeyError) as exc:
print(f"启动失败: {exc}", file=sys.stderr, flush=True)