package main import ( "encoding/json" "fmt" "io" "log" "net/http" "strconv" "strings" "time" ) const ( totalRetryCount = 100000 nacosURL = "http://192.168.31.41:30848/nacos/v1/cs/configs?dataId=wq_account&group=quantify" ) // NacosConfig 对应 nacos 返回的账号配置 type NacosConfig struct { UserName string `json:"user_name"` Password string `json:"password"` } // basicAuthTransport 为每个请求自动添加 Basic Auth 头 type basicAuthTransport struct { username string password string base http.RoundTripper } func (t *basicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) { req.SetBasicAuth(t.username, t.password) return t.base.RoundTrip(req) } // Login 登录 WorldQuant Brain API,返回带 BasicAuth 的 HTTP Client func Login() (*http.Client, error) { // 1. 从 nacos 获取账号密码 resp, err := http.Get(nacosURL) if err != nil { log.Printf("获取账号配置失败: %v", err) return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("nacos 返回非 200 状态码: %d", resp.StatusCode) } var config NacosConfig if err := json.NewDecoder(resp.Body).Decode(&config); err != nil { return nil, fmt.Errorf("解析 nacos 配置失败: %w", err) } log.Printf("正在登录账户: %s", config.UserName) // 2. 创建 HTTP Client,后续所有请求都会使用 BasicAuth client := &http.Client{ Timeout: 30 * time.Second, Transport: &basicAuthTransport{ username: config.UserName, password: config.Password, base: http.DefaultTransport, }, } // 3. 发送登录请求 loginReq, _ := http.NewRequest("POST", "https://api.worldquantbrain.com/authentication", nil) loginResp, err := client.Do(loginReq) if err != nil { return nil, fmt.Errorf("登录请求失败: %w", err) } defer loginResp.Body.Close() log.Printf("登录状态: %d", loginResp.StatusCode) if loginResp.StatusCode == http.StatusCreated { log.Println("登录成功!") return client, nil } body, _ := io.ReadAll(loginResp.Body) return nil, fmt.Errorf("登录失败: %s", string(body)) } // SubmitAlpha 提交 alpha,自动重试直到成功或达到最大重试次数 // 返回 nil 表示提交成功,否则返回错误 func SubmitAlpha(alphaID string) error { retryCount := 0 var client *http.Client for retryCount < totalRetryCount { // 如果没有有效 client,则重新登录 if client == nil { var err error client, err = Login() if err != nil { log.Printf("登录失败: %v, 10秒后重试 (重试次数: %d)", err, retryCount) time.Sleep(10 * time.Second) retryCount++ continue } } url := fmt.Sprintf("https://api.worldquantbrain.com/alphas/%s/submit", alphaID) log.Printf("请求 URL: %s", url) // 发送提交请求 resp, err := client.Post(url, "application/json", nil) if err != nil { log.Printf("网络请求异常 (alpha=%s, retry=%d): %v", alphaID, retryCount, err) retryCount++ time.Sleep(10 * time.Second) continue } // 处理 400 特殊情形:已提交,进入轮询 if resp.StatusCode == http.StatusBadRequest { bodyBytes, _ := io.ReadAll(resp.Body) resp.Body.Close() bodyStr := string(bodyBytes) if strings.Contains(bodyStr, "The plain HTTP request was sent to HTTPS port") { log.Println("Alpha 已提交,正在轮询状态...") pollInterval := 1.0 // 秒 for { time.Sleep(time.Duration(pollInterval) * time.Second) fmt.Print(".") // 重新 GET 查询状态 pollResp, err := client.Get(url) if err != nil { log.Printf("轮询请求失败: %v", err) break } // 检查 Retry-After 头 if retryAfter := pollResp.Header.Get("Retry-After"); retryAfter != "" { if f, err := strconv.ParseFloat(retryAfter, 64); err == nil && f > 0 { pollInterval = max(f, 3.0) } else { pollInterval = 3.0 } } else { pollInterval = 3.0 } // 当状态不再是 400 且不包含该特定消息时,退出轮询 if pollResp.StatusCode != http.StatusBadRequest { resp = pollResp break } bodyBytes2, _ := io.ReadAll(pollResp.Body) pollResp.Body.Close() if !strings.Contains(string(bodyBytes2), "The plain HTTP request was sent to HTTPS port") { resp = pollResp break } pollResp.Body.Close() } log.Printf("轮询结束,最终状态码: %d", resp.StatusCode) } else { // 非特殊 400,按普通错误处理 resp.Body.Close() } } // 确保 resp 不为 nil(如果上面分支没有设置 resp,则跳过本次循环) if resp == nil { retryCount++ continue } // 根据状态码处理 switch resp.StatusCode { case http.StatusTooManyRequests: // 429 log.Println("触发限流 (429),休眠 60 秒后重试") resp.Body.Close() time.Sleep(60 * time.Second) retryCount++ continue case http.StatusUnauthorized: // 401 log.Println("认证失效,重新登录") resp.Body.Close() client = nil retryCount++ continue case http.StatusNotFound: // 404 log.Printf("Alpha %s 不存在或超时,重试 (%d/%d)", alphaID, retryCount+1, totalRetryCount) resp.Body.Close() retryCount++ continue case http.StatusForbidden: // 403 log.Printf("%s 提交失败 (403)", alphaID) var failChecks []map[string]interface{} var bodyMap map[string]interface{} if err := json.NewDecoder(resp.Body).Decode(&bodyMap); err == nil { if isObj, ok := bodyMap["is"].(map[string]interface{}); ok { if checks, ok := isObj["checks"].([]interface{}); ok { for _, c := range checks { if ch, ok := c.(map[string]interface{}); ok { if result, ok := ch["result"]; ok && result == "FAIL" { failChecks = append(failChecks, ch) } } } } } } resp.Body.Close() log.Printf("失败的检查项: %v", failChecks) // 如果因为提交次数超限而失败,放弃重试 for _, ch := range failChecks { if name, ok := ch["name"]; ok { if name == "REGULAR_SUBMISSION" || name == "SUPER_SUBMISSION" { return fmt.Errorf("提交次数超过限制: %v", failChecks) } } } return fmt.Errorf("提交失败,HTTP 403") case http.StatusOK: // 200 log.Printf("%s 提交成功", alphaID) resp.Body.Close() return nil default: // 处理 5xx 错误 if resp.StatusCode >= 500 && resp.StatusCode < 600 { log.Printf("服务器错误 %d,5 秒后重试", resp.StatusCode) resp.Body.Close() time.Sleep(5 * time.Second) retryCount++ continue } // 其他非 2xx 状态码视为失败 bodyBytes, _ := io.ReadAll(resp.Body) resp.Body.Close() return fmt.Errorf("未处理的响应状态码 %d: %s", resp.StatusCode, string(bodyBytes)) } } return fmt.Errorf("达到最大重试次数 %d,提交失败", totalRetryCount) } // max 返回两个 float64 中的较大值 func max(a, b float64) float64 { if a > b { return a } return b } func main() { // 使用示例 alphaID := "your_alpha_id_here" if err := SubmitAlpha(alphaID); err != nil { log.Fatalf("提交失败: %v", err) } log.Println("提交完成") }