add launcher
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -22,6 +22,7 @@ logs/
|
|||||||
# Go workspace file
|
# Go workspace file
|
||||||
go.work
|
go.work
|
||||||
go.work.sum
|
go.work.sum
|
||||||
|
setup.txt
|
||||||
|
|
||||||
# env file
|
# env file
|
||||||
.env
|
.env
|
||||||
|
|||||||
21
launcher/build.bat
Normal file
21
launcher/build.bat
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
where go.exe >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo Go was not found in PATH.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
pushd "%~dp0" || exit /b 1
|
||||||
|
go build -trimpath -ldflags "-s -w -H=windowsgui" -o start.exe start.go
|
||||||
|
set "BUILD_EXIT=%errorlevel%"
|
||||||
|
popd
|
||||||
|
|
||||||
|
if not "%BUILD_EXIT%"=="0" (
|
||||||
|
echo Failed to build start.exe.
|
||||||
|
exit /b %BUILD_EXIT%
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Built "%~dp0start.exe" successfully.
|
||||||
|
exit /b 0
|
||||||
99
launcher/qmt_auto_login.ahk
Normal file
99
launcher/qmt_auto_login.ahk
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
; ==============================================================================
|
||||||
|
; 国金证券 QMT 自动登录脚本 (精准识别版)
|
||||||
|
; 适用版本: 2.0.8.300 | 窗口类名: Qt5QWindowIcon
|
||||||
|
; ==============================================================================
|
||||||
|
#Requires AutoHotkey v2.0
|
||||||
|
#SingleInstance Force
|
||||||
|
#Warn All, Off
|
||||||
|
|
||||||
|
; ---------- 核心配置区 ----------
|
||||||
|
global QMT_PATH := "D:\gjqmt\bin.x64\XtItClient.exe" ; 程序路径自己填
|
||||||
|
global USERNAME := EnvGet("QMT_ACCOUNT_ID") ; 账号自己填
|
||||||
|
global PASSWORD := EnvGet("QMT_ACCOUNT_PWD") ; 密码自己填
|
||||||
|
; 精准窗口识别特征
|
||||||
|
global QMT_CLASS := "ahk_class Qt5QWindowIcon" ; 自己抓 国金不用管 理论所有程序都可以
|
||||||
|
global QMT_EXE := "ahk_exe XtItClient.exe" ; 自己抓 国金不用管 理论所有程序都可以
|
||||||
|
global QMT_TITLE := QMT_CLASS . " " . QMT_EXE ; 不用管
|
||||||
|
|
||||||
|
; 强制等待时间:60秒
|
||||||
|
global FORCE_WAIT_MS := 60000
|
||||||
|
; ------------------------------
|
||||||
|
|
||||||
|
if (USERNAME = "" or PASSWORD = "")
|
||||||
|
{
|
||||||
|
MsgBox("❌ QMT_AC,QMT_PWD 未配置")
|
||||||
|
ExitApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
; 1. 环境清理:确保没有残留进程
|
||||||
|
if ProcessExist("XtItClient.exe") {
|
||||||
|
ProcessClose("XtItClient.exe")
|
||||||
|
Sleep(2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
; 2. 以管理员权限启动 QMT
|
||||||
|
try {
|
||||||
|
Run(QMT_PATH, "", "runas")
|
||||||
|
} catch Error as e {
|
||||||
|
MsgBox("启动失败!请检查路径是否正确:`n" . QMT_PATH)
|
||||||
|
ExitApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
; 3. 等待窗口初步加载
|
||||||
|
; 只要检测到窗口类名 Qt5QWindowIcon 出现,立即启动 30 秒计时
|
||||||
|
if !WinWait(QMT_TITLE, , 30) {
|
||||||
|
MsgBox("❌ QMT 窗口在 30 秒内未启动,流程终止。")
|
||||||
|
ExitApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
; 4. 【强制等待】精准 30 秒倒计时
|
||||||
|
startTime := A_TickCount
|
||||||
|
while (A_TickCount - startTime < FORCE_WAIT_MS) {
|
||||||
|
remaining := Ceil((FORCE_WAIT_MS - (A_TickCount - startTime)) / 1000)
|
||||||
|
ToolTip("已识别 QMT 窗口,强制等待中... 剩余 " . remaining . " 秒开始输入")
|
||||||
|
Sleep(100)
|
||||||
|
}
|
||||||
|
ToolTip() ; 清除倒计时
|
||||||
|
|
||||||
|
; 5. 精准激活并执行登录
|
||||||
|
if WinExist(QMT_TITLE) {
|
||||||
|
; 强制置顶并激活
|
||||||
|
WinActivate(QMT_TITLE)
|
||||||
|
if !WinWaitActive(QMT_TITLE, , 5) {
|
||||||
|
ToolTip("无法激活 QMT 窗口,请手动点击窗口")
|
||||||
|
Sleep(2000)
|
||||||
|
ToolTip()
|
||||||
|
}
|
||||||
|
|
||||||
|
; 锁定键鼠,确保输入不被干扰
|
||||||
|
BlockInput(true)
|
||||||
|
|
||||||
|
; --- 填写账号 ---
|
||||||
|
; 针对 Qt 界面,全选后 Backspace 是最稳妥的清理方式
|
||||||
|
Send("^a{Backspace}")
|
||||||
|
Sleep(2000)
|
||||||
|
SendText(USERNAME)
|
||||||
|
Sleep(2000)
|
||||||
|
|
||||||
|
; --- 切换到密码框 ---
|
||||||
|
Send("{Tab}")
|
||||||
|
Sleep(2000)
|
||||||
|
|
||||||
|
; --- 填写密码 ---
|
||||||
|
Send("^a{Backspace}")
|
||||||
|
Sleep(2000)
|
||||||
|
SendText(PASSWORD)
|
||||||
|
Sleep(2000)
|
||||||
|
|
||||||
|
; --- 回车登录 ---
|
||||||
|
Send("{Enter}")
|
||||||
|
|
||||||
|
BlockInput(false) ; 解锁键鼠
|
||||||
|
}
|
||||||
|
|
||||||
|
; 6. 任务完成
|
||||||
|
ToolTip("✅ 30秒强制等待结束,登录流程已完成")
|
||||||
|
SetTimer () => ToolTip(), -5000
|
||||||
|
|
||||||
|
; 脚本保持运行。若需登录后自动退出,取消下方注释
|
||||||
|
ExitApp()
|
||||||
35
launcher/setup.bat
Normal file
35
launcher/setup.bat
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
set "TASK_NAME=qmt_launcher"
|
||||||
|
set "LAUNCHER_EXE=%~dp0start.exe"
|
||||||
|
if not exist "%LAUNCHER_EXE%" (
|
||||||
|
call "%~dp0build.bat"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo Failed to prepare start.exe.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command ^
|
||||||
|
"$ErrorActionPreference = 'Stop';" ^
|
||||||
|
"$taskName = '%TASK_NAME%';" ^
|
||||||
|
"$launcherDir = (Resolve-Path -LiteralPath '%~dp0').Path;" ^
|
||||||
|
"$workingDir = Split-Path -Parent $launcherDir;" ^
|
||||||
|
"$launcherExe = Join-Path $launcherDir 'start.exe';" ^
|
||||||
|
"$action = New-ScheduledTaskAction -Execute $launcherExe -WorkingDirectory $workingDir;" ^
|
||||||
|
"$trigger = New-ScheduledTaskTrigger -AtLogOn -User ([System.Security.Principal.WindowsIdentity]::GetCurrent().Name);" ^
|
||||||
|
"$principal = New-ScheduledTaskPrincipal -UserId ([System.Security.Principal.WindowsIdentity]::GetCurrent().Name) -LogonType Interactive -RunLevel Highest;" ^
|
||||||
|
"$settings = New-ScheduledTaskSettingsSet -Hidden:$false;" ^
|
||||||
|
"Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null"
|
||||||
|
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo Failed to create task "%TASK_NAME%".
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Task "%TASK_NAME%" created or updated successfully with a visible window.
|
||||||
|
pause
|
||||||
|
exit /b 0
|
||||||
300
launcher/start.go
Normal file
300
launcher/start.go
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
collectorURL = "http://139.224.247.176:13499/collector.exe"
|
||||||
|
statusURL = "http://127.0.0.1:5000/status"
|
||||||
|
qmtStartupDelay = 5 * time.Minute
|
||||||
|
statusInterval = 1 * time.Minute
|
||||||
|
restartCooldown = 15 * time.Minute
|
||||||
|
failureThreshold = 3
|
||||||
|
httpTimeout = 5 * time.Second
|
||||||
|
createNewProcessGroup = 0x00000200
|
||||||
|
errorAlreadyExists = syscall.Errno(183)
|
||||||
|
mutexName = `Local\qmt_launcher_single_instance`
|
||||||
|
)
|
||||||
|
|
||||||
|
func acquireSingleInstance() (syscall.Handle, error) {
|
||||||
|
kernel32 := syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
createMutex := kernel32.NewProc("CreateMutexW")
|
||||||
|
name, err := syscall.UTF16PtrFromString(mutexName)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
handle, _, callErr := createMutex.Call(0, 0, uintptr(unsafe.Pointer(name)))
|
||||||
|
if handle == 0 {
|
||||||
|
return 0, fmt.Errorf("CreateMutexW failed: %v", callErr)
|
||||||
|
}
|
||||||
|
if callErr == errorAlreadyExists {
|
||||||
|
_ = syscall.CloseHandle(syscall.Handle(handle))
|
||||||
|
return 0, fmt.Errorf("another launcher instance is already running")
|
||||||
|
}
|
||||||
|
return syscall.Handle(handle), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
launcherDir string
|
||||||
|
projectRoot string
|
||||||
|
collectorPath string
|
||||||
|
ahkScript string
|
||||||
|
runScript string
|
||||||
|
httpClient = &http.Client{Timeout: httpTimeout}
|
||||||
|
)
|
||||||
|
|
||||||
|
func initPaths() error {
|
||||||
|
executable, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("locate launcher executable: %w", err)
|
||||||
|
}
|
||||||
|
executable, err = filepath.EvalSymlinks(executable)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("resolve launcher executable: %w", err)
|
||||||
|
}
|
||||||
|
launcherDir = filepath.Dir(executable)
|
||||||
|
projectRoot = filepath.Dir(launcherDir)
|
||||||
|
collectorPath = filepath.Join(projectRoot, "bin", "collector.exe")
|
||||||
|
ahkScript = filepath.Join(launcherDir, "qmt_auto_login.ahk")
|
||||||
|
runScript = filepath.Join(projectRoot, "run.bat")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureLogging() (*os.File, error) {
|
||||||
|
logFile, err := os.OpenFile(filepath.Join(launcherDir, "start.log"), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.SetOutput(logFile)
|
||||||
|
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
|
||||||
|
return logFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadCollector() error {
|
||||||
|
if info, err := os.Stat(collectorPath); err == nil && !info.IsDir() {
|
||||||
|
log.Printf("INFO collector.exe already exists: %s", collectorPath)
|
||||||
|
return nil
|
||||||
|
} else if err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(filepath.Dir(collectorPath), 0o755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
temporaryPath := collectorPath + ".download"
|
||||||
|
log.Printf("INFO Downloading collector.exe from %s", collectorURL)
|
||||||
|
|
||||||
|
response, err := httpClient.Get(collectorURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
if response.StatusCode != http.StatusOK {
|
||||||
|
return fmt.Errorf("download returned HTTP %d", response.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := os.Create(temporaryPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, copyErr := io.Copy(output, response.Body)
|
||||||
|
closeErr := output.Close()
|
||||||
|
if copyErr != nil || closeErr != nil {
|
||||||
|
_ = os.Remove(temporaryPath)
|
||||||
|
if copyErr != nil {
|
||||||
|
return copyErr
|
||||||
|
}
|
||||||
|
return closeErr
|
||||||
|
}
|
||||||
|
info, err := os.Stat(temporaryPath)
|
||||||
|
if err != nil || info.Size() == 0 {
|
||||||
|
_ = os.Remove(temporaryPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fmt.Errorf("downloaded collector.exe is empty")
|
||||||
|
}
|
||||||
|
if err := os.Rename(temporaryPath, collectorPath); err != nil {
|
||||||
|
_ = os.Remove(temporaryPath)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("INFO collector.exe downloaded: %s", collectorPath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func launchQMTLogin() error {
|
||||||
|
if _, err := os.Stat(ahkScript); err != nil {
|
||||||
|
return fmt.Errorf("AHK script not found: %s", ahkScript)
|
||||||
|
}
|
||||||
|
log.Printf("INFO Launching %s", ahkScript)
|
||||||
|
|
||||||
|
shell32 := syscall.NewLazyDLL("shell32.dll")
|
||||||
|
shellExecute := shell32.NewProc("ShellExecuteW")
|
||||||
|
verb, _ := syscall.UTF16PtrFromString("open")
|
||||||
|
file, _ := syscall.UTF16PtrFromString(ahkScript)
|
||||||
|
result, _, callErr := shellExecute.Call(0, uintptr(unsafe.Pointer(verb)), uintptr(unsafe.Pointer(file)), 0, 0, 1)
|
||||||
|
if result <= 32 {
|
||||||
|
return fmt.Errorf("ShellExecuteW failed (%d): %v", result, callErr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func launchRunScript() error {
|
||||||
|
if _, err := os.Stat(runScript); err != nil {
|
||||||
|
return fmt.Errorf("run.bat not found: %s", runScript)
|
||||||
|
}
|
||||||
|
log.Printf("INFO Launching %s", runScript)
|
||||||
|
command := exec.Command("cmd.exe", "/c", runScript)
|
||||||
|
command.Dir = projectRoot
|
||||||
|
command.SysProcAttr = &syscall.SysProcAttr{CreationFlags: createNewProcessGroup}
|
||||||
|
return command.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
func restartSystem() error {
|
||||||
|
if err := launchQMTLogin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("INFO Waiting %d seconds for QMT login", int(qmtStartupDelay/time.Second))
|
||||||
|
time.Sleep(qmtStartupDelay)
|
||||||
|
if err := launchRunScript(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
time.Sleep(statusInterval)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func systemIsHealthy() bool {
|
||||||
|
response, err := httpClient.Get(statusURL)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("WARNING Status check failed: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
if response.StatusCode != http.StatusOK {
|
||||||
|
log.Printf("WARNING Status endpoint returned HTTP %d", response.StatusCode)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyString := strings.ReplaceAll(string(body), "Null", "\"\"")
|
||||||
|
bodyString = strings.ReplaceAll(bodyString, "NULL", "\"\"")
|
||||||
|
bodyString = strings.ReplaceAll(bodyString, "NONE", "\"\"")
|
||||||
|
bodyString = strings.ReplaceAll(bodyString, "None", "\"\"")
|
||||||
|
bodyString = strings.ReplaceAll(bodyString, "Nil", "\"\"")
|
||||||
|
|
||||||
|
var payload struct {
|
||||||
|
Status map[string]any `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal([]byte(bodyString), &payload); err != nil {
|
||||||
|
log.Printf("WARNING Status check failed: %v, %s", err, bodyString)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
healthy := payload.Status != nil && payload.Status["qmt_status"] == "connected"
|
||||||
|
if !healthy {
|
||||||
|
log.Printf("WARNING System is not fully started: %v", payload.Status)
|
||||||
|
}
|
||||||
|
return healthy
|
||||||
|
}
|
||||||
|
|
||||||
|
func isTradingTime(now time.Time) bool {
|
||||||
|
weekday := now.Weekday()
|
||||||
|
if weekday == time.Saturday || weekday == time.Sunday {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
hhmm := now.Hour()*100 + now.Minute()
|
||||||
|
return (hhmm >= 910 && hhmm <= 1130) || (hhmm >= 1240 && hhmm <= 1500)
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() error {
|
||||||
|
log.Print("INFO ==================================================")
|
||||||
|
log.Print("INFO QMT launcher supervisor started")
|
||||||
|
if err := downloadCollector(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
consecutiveFailures := 0
|
||||||
|
var lastRestart time.Time
|
||||||
|
wasOutsideTradingTime := false
|
||||||
|
for {
|
||||||
|
if !isTradingTime(time.Now()) {
|
||||||
|
if !wasOutsideTradingTime {
|
||||||
|
log.Print("INFO Outside trading hours; skipping health check and startup")
|
||||||
|
}
|
||||||
|
wasOutsideTradingTime = true
|
||||||
|
consecutiveFailures = 0
|
||||||
|
time.Sleep(statusInterval)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if wasOutsideTradingTime {
|
||||||
|
log.Print("INFO Trading hours started; resuming health checks")
|
||||||
|
wasOutsideTradingTime = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if systemIsHealthy() {
|
||||||
|
consecutiveFailures = 0
|
||||||
|
time.Sleep(statusInterval)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
consecutiveFailures++
|
||||||
|
if consecutiveFailures < failureThreshold {
|
||||||
|
log.Printf("WARNING Health check failed %d/%d; waiting before restart", consecutiveFailures, failureThreshold)
|
||||||
|
time.Sleep(statusInterval)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if remaining := restartCooldown - time.Since(lastRestart); !lastRestart.IsZero() && remaining > 0 {
|
||||||
|
log.Printf("WARNING Restart suppressed by cooldown; retrying in approximately %s", remaining.Round(time.Second))
|
||||||
|
time.Sleep(statusInterval)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("WARNING Health check failed %d consecutive times; restarting system", consecutiveFailures)
|
||||||
|
if err := restartSystem(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lastRestart = time.Now()
|
||||||
|
consecutiveFailures = 0
|
||||||
|
time.Sleep(statusInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
instanceMutex, err := acquireSingleInstance()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer syscall.CloseHandle(instanceMutex)
|
||||||
|
|
||||||
|
if err := initPaths(); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
logFile, err := configureLogging()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer logFile.Close()
|
||||||
|
if err := run(); err != nil {
|
||||||
|
log.Printf("ERROR Launcher failed: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
264
launcher/start.log
Normal file
264
launcher/start.log
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
2026-08-13 15:22:43,135 INFO QMT launcher supervisor started
|
||||||
|
2026-08-13 15:22:43,135 INFO Downloading collector.exe from http://139.224.247.176/collector.exe
|
||||||
|
2026-08-13 15:22:43,244 ERROR Launcher cycle failed
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "D:\work\quant\qmt-v2\launcher\start.py", line 107, in main
|
||||||
|
download_collector()
|
||||||
|
File "D:\work\quant\qmt-v2\launcher\start.py", line 47, in download_collector
|
||||||
|
with urlopen(COLLECTOR_URL, timeout=HTTP_TIMEOUT_SECONDS) as response:
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "D:\devapps\Python311\Lib\urllib\request.py", line 216, in urlopen
|
||||||
|
return opener.open(url, data, timeout)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "D:\devapps\Python311\Lib\urllib\request.py", line 525, in open
|
||||||
|
response = meth(req, response)
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "D:\devapps\Python311\Lib\urllib\request.py", line 634, in http_response
|
||||||
|
response = self.parent.error(
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
File "D:\devapps\Python311\Lib\urllib\request.py", line 563, in error
|
||||||
|
return self._call_chain(*args)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "D:\devapps\Python311\Lib\urllib\request.py", line 496, in _call_chain
|
||||||
|
result = func(*args)
|
||||||
|
^^^^^^^^^^^
|
||||||
|
File "D:\devapps\Python311\Lib\urllib\request.py", line 643, in http_error_default
|
||||||
|
raise HTTPError(req.full_url, code, msg, hdrs, fp)
|
||||||
|
urllib.error.HTTPError: HTTP Error 404: Not Found
|
||||||
|
2026-08-13 15:26:45,007 INFO QMT launcher supervisor started
|
||||||
|
2026-08-13 15:26:45,008 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026-08-13 15:26:45,008 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026-08-13 15:26:45,106 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026-08-13 21:53:04,684 INFO QMT launcher supervisor started
|
||||||
|
2026-08-13 21:53:04,686 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026-08-13 21:53:04,686 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026-08-13 21:53:06,267 INFO Waiting 180 seconds for QMT login
|
||||||
|
2026-08-13 22:01:06,389 INFO QMT launcher supervisor started
|
||||||
|
2026-08-13 22:01:06,389 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026-08-13 22:01:06,389 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026-08-13 22:01:06,463 INFO Waiting 180 seconds for QMT login
|
||||||
|
2026-08-13 22:04:06,464 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026-08-13 23:02:15,722 INFO ==================================================
|
||||||
|
2026-08-13 23:02:15,722 INFO QMT launcher supervisor started
|
||||||
|
2026-08-13 23:02:15,722 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026-08-13 23:02:17,756 WARNING Status check failed: <urlopen error [WinError 10061] 由于目标计算机积极拒绝,无法连接。>
|
||||||
|
2026-08-13 23:02:17,756 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026-08-13 23:02:17,837 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026-08-13 23:07:17,838 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026-08-13 23:27:05,964 INFO ==================================================
|
||||||
|
2026-08-13 23:27:05,965 INFO QMT launcher supervisor started
|
||||||
|
2026-08-13 23:27:05,965 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026-08-13 23:27:08,034 WARNING Status check failed: <urlopen error [WinError 10061] 由于目标计算机积极拒绝,无法连接。>
|
||||||
|
2026-08-13 23:27:08,035 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026-08-13 23:27:08,118 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/13 23:45:13.608527 INFO ==================================================
|
||||||
|
2026/08/13 23:45:13.622235 INFO QMT launcher supervisor started
|
||||||
|
2026/08/13 23:45:13.622235 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026/08/13 23:45:13.623496 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/13 23:45:13.624026 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/13 23:45:13.703711 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/13 23:50:13.704349 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/13 23:52:13.715253 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/13 23:52:13.715407 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/13 23:52:13.767285 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/14 08:48:36.427941 INFO ==================================================
|
||||||
|
2026/08/14 08:48:36.961109 INFO QMT launcher supervisor started
|
||||||
|
2026/08/14 08:48:37.692841 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026/08/14 08:48:37.769022 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/14 08:48:38.177308 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/14 08:48:39.449445 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/14 08:53:39.559317 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/14 08:55:39.569610 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/14 08:55:39.569610 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/14 08:55:39.622915 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/14 09:00:39.623224 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/14 09:02:39.640363 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/14 09:02:39.640363 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/14 09:02:39.687327 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/15 08:50:00.885570 INFO ==================================================
|
||||||
|
2026/08/15 08:50:01.657003 INFO QMT launcher supervisor started
|
||||||
|
2026/08/15 08:50:01.659591 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026/08/15 08:50:01.662166 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/15 08:50:01.894393 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/15 08:50:04.507973 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/16 08:52:26.025434 INFO ==================================================
|
||||||
|
2026/08/16 08:52:26.104316 INFO QMT launcher supervisor started
|
||||||
|
2026/08/16 08:52:26.125470 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026/08/16 08:52:26.127520 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/16 08:52:26.129067 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/16 08:52:27.204828 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/16 08:57:27.234137 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/17 07:48:38.513724 INFO ==================================================
|
||||||
|
2026/08/17 07:48:38.824692 INFO QMT launcher supervisor started
|
||||||
|
2026/08/17 07:48:38.856254 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026/08/17 07:48:38.875302 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/17 07:48:39.231263 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/17 07:48:40.845492 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/17 07:53:40.940886 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/17 09:25:42.472480 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/17 09:25:42.476066 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/17 09:25:42.698749 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/17 09:30:42.699596 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/17 09:32:42.711957 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/17 09:32:42.712191 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/17 09:32:42.762591 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/17 09:37:42.763160 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/17 09:40:42.778731 WARNING Status check failed: invalid character 'N' looking for beginning of value
|
||||||
|
2026/08/17 09:40:42.779241 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/17 09:40:42.832414 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/18 08:03:14.885643 INFO ==================================================
|
||||||
|
2026/08/18 08:03:15.353370 INFO QMT launcher supervisor started
|
||||||
|
2026/08/18 08:03:15.758480 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026/08/18 08:03:16.301320 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/18 08:03:16.385159 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/18 08:03:17.010169 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/18 08:08:17.204987 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/18 08:10:17.215378 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/18 08:10:17.215747 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/18 08:10:17.258153 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/18 08:15:17.258601 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/18 13:58:18.503095 WARNING Status check failed: invalid character 'N' looking for beginning of value
|
||||||
|
2026/08/18 13:58:18.528903 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/18 13:58:19.022561 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/18 13:59:24.123137 INFO ==================================================
|
||||||
|
2026/08/18 13:59:24.136062 INFO QMT launcher supervisor started
|
||||||
|
2026/08/18 13:59:24.136062 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026/08/18 13:59:24.139154 WARNING Status check failed: invalid character 'N' looking for beginning of value
|
||||||
|
2026/08/18 13:59:24.139154 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/18 13:59:24.222221 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/18 14:03:19.023203 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/18 14:04:24.222305 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/18 14:05:19.039743 WARNING Status check failed: invalid character 'N' looking for beginning of value
|
||||||
|
2026/08/18 14:05:19.039889 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/18 14:05:19.091765 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/18 16:50:14.766362 INFO ==================================================
|
||||||
|
2026/08/18 16:50:14.813222 INFO QMT launcher supervisor started
|
||||||
|
2026/08/18 16:50:14.829377 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026/08/18 16:50:15.043111 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/18 16:50:15.239254 WARNING Health check failed 1/3; waiting before restart
|
||||||
|
2026/08/18 16:51:15.239641 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/18 16:51:15.240155 WARNING Health check failed 2/3; waiting before restart
|
||||||
|
2026/08/18 16:52:15.241716 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/18 16:52:15.241716 WARNING Health check failed 3 consecutive times; restarting system
|
||||||
|
2026/08/18 16:52:15.242314 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/18 16:52:15.333484 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/18 16:57:15.334509 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/19 19:22:16.079187 INFO ==================================================
|
||||||
|
2026/08/19 19:22:16.773161 INFO QMT launcher supervisor started
|
||||||
|
2026/08/19 19:22:16.891082 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026/08/19 19:22:16.941701 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/19 19:22:17.669752 WARNING Health check failed 1/3; waiting before restart
|
||||||
|
2026/08/19 19:23:17.696305 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/19 19:23:17.696812 WARNING Health check failed 2/3; waiting before restart
|
||||||
|
2026/08/19 19:24:17.698348 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/19 19:24:17.698348 WARNING Health check failed 3 consecutive times; restarting system
|
||||||
|
2026/08/19 19:24:17.699493 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/19 19:24:17.778244 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/19 19:29:17.787076 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/20 00:46:19.533672 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/20 00:46:19.557647 WARNING Health check failed 1/3; waiting before restart
|
||||||
|
2026/08/20 00:47:19.558550 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/20 00:47:19.558550 WARNING Health check failed 2/3; waiting before restart
|
||||||
|
2026/08/20 00:48:19.561081 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/20 00:48:19.561171 WARNING Health check failed 3 consecutive times; restarting system
|
||||||
|
2026/08/20 00:48:19.562250 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/20 00:48:20.393672 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/20 00:53:20.394245 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/20 00:55:20.405666 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/20 00:55:20.405666 WARNING Health check failed 1/3; waiting before restart
|
||||||
|
2026/08/20 09:04:29.885549 INFO ==================================================
|
||||||
|
2026/08/20 09:04:30.104470 INFO QMT launcher supervisor started
|
||||||
|
2026/08/20 09:04:30.188385 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026/08/20 09:04:30.296629 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/20 09:04:30.904740 WARNING Health check failed 1/3; waiting before restart
|
||||||
|
2026/08/20 09:05:30.933442 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/20 09:05:30.933442 WARNING Health check failed 2/3; waiting before restart
|
||||||
|
2026/08/20 09:06:30.935437 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/20 09:06:30.935437 WARNING Health check failed 3 consecutive times; restarting system
|
||||||
|
2026/08/20 09:06:30.936235 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/20 09:06:31.015171 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/20 09:11:31.019034 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/20 18:38:33.643883 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/20 18:38:33.644433 WARNING Health check failed 1/3; waiting before restart
|
||||||
|
2026/08/20 18:39:33.645042 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/20 18:39:33.645042 WARNING Health check failed 2/3; waiting before restart
|
||||||
|
2026/08/20 18:40:33.645183 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/20 18:40:33.645183 WARNING Health check failed 3 consecutive times; restarting system
|
||||||
|
2026/08/20 18:40:33.645183 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/20 18:40:33.708064 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/21 07:43:26.860445 INFO ==================================================
|
||||||
|
2026/08/21 07:43:26.900291 INFO QMT launcher supervisor started
|
||||||
|
2026/08/21 07:43:26.900802 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026/08/21 07:43:26.900802 INFO Outside trading hours; skipping health check and startup
|
||||||
|
2026/08/21 07:56:42.689005 INFO ==================================================
|
||||||
|
2026/08/21 07:56:42.956200 INFO QMT launcher supervisor started
|
||||||
|
2026/08/21 07:56:43.003770 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026/08/21 07:56:43.098420 INFO Outside trading hours; skipping health check and startup
|
||||||
|
2026/08/21 09:10:43.146562 INFO Trading hours started; resuming health checks
|
||||||
|
2026/08/21 09:10:43.151689 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/21 09:10:43.152235 WARNING Health check failed 1/3; waiting before restart
|
||||||
|
2026/08/21 09:11:43.153756 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/21 09:11:43.153892 WARNING Health check failed 2/3; waiting before restart
|
||||||
|
2026/08/21 09:12:43.155121 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/21 09:12:43.155121 WARNING Health check failed 3 consecutive times; restarting system
|
||||||
|
2026/08/21 09:12:43.158139 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/21 09:12:43.602943 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/21 09:17:43.603673 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/21 09:41:43.700827 WARNING Status check failed: invalid character 'N' looking for beginning of value
|
||||||
|
2026/08/21 09:41:43.701040 WARNING Health check failed 1/3; waiting before restart
|
||||||
|
2026/08/21 09:42:43.705588 WARNING Status check failed: invalid character 'N' looking for beginning of value
|
||||||
|
2026/08/21 09:42:43.705588 WARNING Health check failed 2/3; waiting before restart
|
||||||
|
2026/08/21 09:43:43.708222 WARNING Status check failed: invalid character 'N' looking for beginning of value
|
||||||
|
2026/08/21 09:43:43.708222 WARNING Health check failed 3 consecutive times; restarting system
|
||||||
|
2026/08/21 09:43:43.708222 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/21 09:43:43.755418 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/24 07:34:46.696720 INFO ==================================================
|
||||||
|
2026/08/24 07:34:46.886535 INFO QMT launcher supervisor started
|
||||||
|
2026/08/24 07:34:47.102305 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026/08/24 07:34:47.117274 INFO Outside trading hours; skipping health check and startup
|
||||||
|
2026/08/25 07:26:35.556191 INFO ==================================================
|
||||||
|
2026/08/25 07:26:35.588196 INFO QMT launcher supervisor started
|
||||||
|
2026/08/25 07:26:35.597019 INFO collector.exe already exists: D:\work\quant\qmt-v2\bin\collector.exe
|
||||||
|
2026/08/25 07:26:35.598275 INFO Outside trading hours; skipping health check and startup
|
||||||
|
2026/08/25 09:10:35.669830 INFO Trading hours started; resuming health checks
|
||||||
|
2026/08/25 09:10:35.796782 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:10:35.798360 WARNING Health check failed 1/3; waiting before restart
|
||||||
|
2026/08/25 09:11:35.798720 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:11:35.798720 WARNING Health check failed 2/3; waiting before restart
|
||||||
|
2026/08/25 09:12:35.800607 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:12:35.800607 WARNING Health check failed 3 consecutive times; restarting system
|
||||||
|
2026/08/25 09:12:35.802484 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/25 09:12:36.274607 INFO Waiting 300 seconds for QMT login
|
||||||
|
2026/08/25 09:17:36.276883 INFO Launching D:\work\quant\qmt-v2\run.bat
|
||||||
|
2026/08/25 09:19:36.305826 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:19:36.305826 WARNING Health check failed 1/3; waiting before restart
|
||||||
|
2026/08/25 09:20:36.306951 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:20:36.306951 WARNING Health check failed 2/3; waiting before restart
|
||||||
|
2026/08/25 09:21:36.308254 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:21:36.308254 WARNING Restart suppressed by cooldown; retrying in approximately 12m0s
|
||||||
|
2026/08/25 09:22:36.312490 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:22:36.312490 WARNING Restart suppressed by cooldown; retrying in approximately 11m0s
|
||||||
|
2026/08/25 09:23:36.313957 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:23:36.313957 WARNING Restart suppressed by cooldown; retrying in approximately 10m0s
|
||||||
|
2026/08/25 09:24:36.315175 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:24:36.315175 WARNING Restart suppressed by cooldown; retrying in approximately 9m0s
|
||||||
|
2026/08/25 09:25:36.317724 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:25:36.317758 WARNING Restart suppressed by cooldown; retrying in approximately 8m0s
|
||||||
|
2026/08/25 09:26:36.318252 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:26:36.318252 WARNING Restart suppressed by cooldown; retrying in approximately 7m0s
|
||||||
|
2026/08/25 09:27:36.320154 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:27:36.320237 WARNING Restart suppressed by cooldown; retrying in approximately 6m0s
|
||||||
|
2026/08/25 09:28:36.322021 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:28:36.322021 WARNING Restart suppressed by cooldown; retrying in approximately 5m0s
|
||||||
|
2026/08/25 09:29:36.323197 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:29:36.323354 WARNING Restart suppressed by cooldown; retrying in approximately 4m0s
|
||||||
|
2026/08/25 09:30:36.323848 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:30:36.323848 WARNING Restart suppressed by cooldown; retrying in approximately 3m0s
|
||||||
|
2026/08/25 09:31:36.325264 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:31:36.325264 WARNING Restart suppressed by cooldown; retrying in approximately 2m0s
|
||||||
|
2026/08/25 09:32:36.326993 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:32:36.327058 WARNING Restart suppressed by cooldown; retrying in approximately 1m0s
|
||||||
|
2026/08/25 09:33:36.328252 WARNING Status check failed: Get "http://127.0.0.1:5000/status": dial tcp 127.0.0.1:5000: connectex: No connection could be made because the target machine actively refused it.
|
||||||
|
2026/08/25 09:33:36.328252 WARNING Health check failed 15 consecutive times; restarting system
|
||||||
|
2026/08/25 09:33:36.328757 INFO Launching D:\work\quant\qmt-v2\launcher\qmt_auto_login.ahk
|
||||||
|
2026/08/25 09:33:36.384905 INFO Waiting 300 seconds for QMT login
|
||||||
28
launcher/start_test.go
Normal file
28
launcher/start_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNormalizeJSONEmptyValues(t *testing.T) {
|
||||||
|
input := map[string]any{
|
||||||
|
"json_null": nil,
|
||||||
|
"nil_text": "nil",
|
||||||
|
"null_text": " NULL ",
|
||||||
|
"normal": "connected",
|
||||||
|
"nested": []any{nil, "NIL", 1.0},
|
||||||
|
}
|
||||||
|
want := map[string]any{
|
||||||
|
"json_null": "",
|
||||||
|
"nil_text": "",
|
||||||
|
"null_text": "",
|
||||||
|
"normal": "connected",
|
||||||
|
"nested": []any{"", "", 1.0},
|
||||||
|
}
|
||||||
|
|
||||||
|
got := normalizeJSONEmptyValues(input)
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Fatalf("normalizeJSONEmptyValues() = %#v, want %#v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
account_id: 8889399698
|
account_id: 8889399698
|
||||||
host_key: liao
|
host_key: liao
|
||||||
buy_value: 5000
|
buy_value: 10000
|
||||||
min_cash_ratio: 0.10
|
min_cash_ratio: 0.10
|
||||||
grid_step_pct: 1
|
grid_step_pct: 1
|
||||||
strategy: trend
|
strategy: trend
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
"""策略单次运行所需的公共上下文对象。"""
|
"""策略单次运行所需的公共上下文对象。"""
|
||||||
|
|
||||||
|
import logging
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
from config import AccountConfig, GlobalConfig
|
from config import AccountConfig, GlobalConfig, HTTP_TIMEOUT
|
||||||
from sdk import Client
|
from sdk import Client
|
||||||
from libs.grid_take_profit import GridTrailingTracker
|
from libs.grid_take_profit import GridTrailingTracker
|
||||||
|
from libs.http import get_json
|
||||||
from libs.order import OrderBook
|
from libs.order import OrderBook
|
||||||
from libs.watch import DipWatch
|
from libs.watch import DipWatch
|
||||||
|
|
||||||
@@ -22,3 +24,23 @@ class Runtime:
|
|||||||
add_watch: DipWatch
|
add_watch: DipWatch
|
||||||
profit_tracker: GridTrailingTracker
|
profit_tracker: GridTrailingTracker
|
||||||
executor: ThreadPoolExecutor | None = None
|
executor: ThreadPoolExecutor | None = None
|
||||||
|
server_inital: dict[str, list[str]] = field(init=False)
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
"""读取服务端初始化数据,失败时保留空证券列表。"""
|
||||||
|
self.server_inital = {"all_codes": [], "today_pass_codes": []}
|
||||||
|
try:
|
||||||
|
url = f"{self.global_cfg.api_host.rstrip('/')}/a/initial"
|
||||||
|
payload = get_json(url, HTTP_TIMEOUT)
|
||||||
|
if not isinstance(payload, dict) or str(payload.get("code")) != "0":
|
||||||
|
raise ValueError(f"服务端初始化响应失败: {payload!r}")
|
||||||
|
data = payload.get("data")
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise ValueError("服务端初始化 data 必须为对象")
|
||||||
|
for key in ("all_codes", "today_pass_codes"):
|
||||||
|
codes = data.get(key)
|
||||||
|
if not isinstance(codes, list) or not all(isinstance(code, str) for code in codes):
|
||||||
|
raise ValueError(f"服务端初始化 {key} 必须为字符串列表")
|
||||||
|
self.server_inital = data
|
||||||
|
except Exception:
|
||||||
|
logging.exception("[初始化] 获取服务端初始化数据失败")
|
||||||
|
|||||||
@@ -166,6 +166,11 @@ def handle_loss(
|
|||||||
return TradeDecision(False, f"次数无效:{add_num}")
|
return TradeDecision(False, f"次数无效:{add_num}")
|
||||||
if pnl_rate > LOSS_TIERS[add_num]:
|
if pnl_rate > LOSS_TIERS[add_num]:
|
||||||
return TradeDecision(False)
|
return TradeDecision(False)
|
||||||
|
|
||||||
|
all_codes = runtime.server_inital.get("all_codes") or []
|
||||||
|
if position.stock_code not in all_codes:
|
||||||
|
return TradeDecision(False, "不在服务端初始化股票池内,禁止补仓")
|
||||||
|
|
||||||
if tick.last_price > 200 or position.market_value >= 20_000:
|
if tick.last_price > 200 or position.market_value >= 20_000:
|
||||||
return TradeDecision(False, "价格或仓位市值超过补仓限制")
|
return TradeDecision(False, "价格或仓位市值超过补仓限制")
|
||||||
if not runtime.add_watch.triggered("补仓", position.stock_code, tick.last_price):
|
if not runtime.add_watch.triggered("补仓", position.stock_code, tick.last_price):
|
||||||
|
|||||||
Reference in New Issue
Block a user