main.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. package main
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "log"
  8. "net/http"
  9. "os"
  10. "path/filepath"
  11. "strconv"
  12. "strings"
  13. "sync"
  14. "time"
  15. "golang.org/x/sync/semaphore"
  16. )
  17. // AlphaConfig Alpha因子配置
  18. type AlphaConfig struct {
  19. Name string `json:"name"`
  20. Expression string `json:"expression"`
  21. Settings map[string]interface{} `json:"settings,omitempty"`
  22. }
  23. // SimulationResult 模拟结果
  24. type SimulationResult struct {
  25. Name string `json:"name"`
  26. Expression string `json:"expression"`
  27. AlphaID string `json:"alpha_id,omitempty"`
  28. Success bool `json:"success"`
  29. Error string `json:"error,omitempty"`
  30. ResultData interface{} `json:"result_data,omitempty"`
  31. }
  32. // WorldQuantBrainAPI WorldQuant Brain API客户端
  33. type WorldQuantBrainAPI struct {
  34. BaseURL string
  35. CredentialsFile string
  36. HTTPClient *http.Client
  37. Username string
  38. Password string
  39. }
  40. // NewWorldQuantBrainAPI 创建新的API客户端
  41. func NewWorldQuantBrainAPI(credentialsFile string) *WorldQuantBrainAPI {
  42. if credentialsFile == "" {
  43. credentialsFile = "brain_credentials.txt"
  44. }
  45. // 扩展文件路径
  46. fullPath := expandUser(credentialsFile)
  47. return &WorldQuantBrainAPI{
  48. BaseURL: "https://api.worldquantbrain.com",
  49. CredentialsFile: fullPath,
  50. HTTPClient: &http.Client{Timeout: 30 * time.Second},
  51. }
  52. }
  53. // expandUser 扩展用户主目录路径
  54. func expandUser(path string) string {
  55. if strings.HasPrefix(path, "~/") {
  56. home, err := os.UserHomeDir()
  57. if err != nil {
  58. return path
  59. }
  60. return filepath.Join(home, path[2:])
  61. }
  62. return path
  63. }
  64. // Login 登录认证
  65. func (wq *WorldQuantBrainAPI) Login() error {
  66. // 读取凭证文件
  67. data, err := os.ReadFile(wq.CredentialsFile)
  68. if err != nil {
  69. return fmt.Errorf("读取凭证文件失败: %v", err)
  70. }
  71. content := strings.TrimSpace(string(data))
  72. var credentials []string
  73. // 方法1: 尝试标准JSON解析
  74. if err := json.Unmarshal([]byte(content), &credentials); err == nil && len(credentials) >= 2 {
  75. // JSON解析成功
  76. } else {
  77. // 方法2: 尝试处理单引号JSON
  78. singleQuoteContent := strings.ReplaceAll(content, "'", "\"")
  79. if err := json.Unmarshal([]byte(singleQuoteContent), &credentials); err == nil && len(credentials) >= 2 {
  80. // 单引号JSON解析成功
  81. } else {
  82. // 方法3: 尝试其他文本格式
  83. credentials = wq.parseTextCredentials(content)
  84. }
  85. }
  86. if len(credentials) < 2 {
  87. return fmt.Errorf("无法解析凭证文件,需要用户名和密码。当前内容: %s", content)
  88. }
  89. wq.Username = strings.TrimSpace(credentials[0])
  90. wq.Password = strings.TrimSpace(credentials[1])
  91. // 创建认证请求
  92. req, err := http.NewRequest("POST", wq.BaseURL+"/authentication", nil)
  93. if err != nil {
  94. return fmt.Errorf("创建请求失败: %v", err)
  95. }
  96. req.SetBasicAuth(wq.Username, wq.Password)
  97. // 发送认证请求
  98. resp, err := wq.HTTPClient.Do(req)
  99. if err != nil {
  100. return fmt.Errorf("认证请求失败: %v", err)
  101. }
  102. defer resp.Body.Close()
  103. fmt.Printf("认证状态: %d\n", resp.StatusCode)
  104. // 修复:检查 200 和 201 状态码都表示成功
  105. if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
  106. body, _ := io.ReadAll(resp.Body)
  107. return fmt.Errorf("登录失败,状态码: %d, 响应: %s", resp.StatusCode, string(body))
  108. }
  109. // 读取成功响应
  110. body, _ := io.ReadAll(resp.Body)
  111. fmt.Printf("登录成功响应: %s\n", string(body))
  112. fmt.Println("登录成功!")
  113. return nil
  114. }
  115. func (wq *WorldQuantBrainAPI) parseTextCredentials(content string) []string {
  116. panic("unimplemented")
  117. }
  118. // SimulateAlpha 模拟单个Alpha因子
  119. func (wq *WorldQuantBrainAPI) SimulateAlpha(config AlphaConfig) SimulationResult {
  120. if wq.HTTPClient == nil {
  121. return SimulationResult{
  122. Name: config.Name,
  123. Success: false,
  124. Error: "请先调用Login方法登录",
  125. }
  126. }
  127. name := config.Name
  128. if name == "" {
  129. name = "未命名Alpha"
  130. }
  131. fmt.Printf("\n🚀 开始模拟: %s\n", name)
  132. fmt.Printf("📝 表达式: %s\n", config.Expression)
  133. // 构建模拟数据
  134. simulationData := map[string]interface{}{
  135. "type": "REGULAR",
  136. "settings": config.Settings,
  137. "regular": config.Expression,
  138. }
  139. jsonData, err := json.Marshal(simulationData)
  140. if err != nil {
  141. return SimulationResult{
  142. Name: name,
  143. Success: false,
  144. Error: fmt.Sprintf("序列化模拟数据失败: %v", err),
  145. }
  146. }
  147. // 发送模拟请求
  148. req, err := http.NewRequest("POST", wq.BaseURL+"/simulations", strings.NewReader(string(jsonData)))
  149. if err != nil {
  150. return SimulationResult{
  151. Name: name,
  152. Success: false,
  153. Error: fmt.Sprintf("创建请求失败: %v", err),
  154. }
  155. }
  156. req.SetBasicAuth(wq.Username, wq.Password)
  157. req.Header.Set("Content-Type", "application/json")
  158. resp, err := wq.HTTPClient.Do(req)
  159. if err != nil {
  160. return SimulationResult{
  161. Name: name,
  162. Success: false,
  163. Error: fmt.Sprintf("模拟请求失败: %v", err),
  164. }
  165. }
  166. defer resp.Body.Close()
  167. fmt.Printf("📤 模拟提交状态: %d\n", resp.StatusCode)
  168. if resp.StatusCode != http.StatusOK {
  169. // 尝试读取错误响应体
  170. body, _ := io.ReadAll(resp.Body)
  171. return SimulationResult{
  172. Name: name,
  173. Success: false,
  174. Error: fmt.Sprintf("提交失败: %d, 响应: %s", resp.StatusCode, string(body)),
  175. }
  176. }
  177. // 获取进度URL
  178. progressURL := resp.Header.Get("Location")
  179. if progressURL == "" {
  180. return SimulationResult{
  181. Name: name,
  182. Success: false,
  183. Error: "未找到进度URL",
  184. }
  185. }
  186. fmt.Printf("⏳ %s 进度URL获取成功\n", name)
  187. // 轮询进度
  188. var resultData map[string]interface{}
  189. for {
  190. progressReq, err := http.NewRequest("GET", progressURL, nil)
  191. if err != nil {
  192. return SimulationResult{
  193. Name: name,
  194. Success: false,
  195. Error: fmt.Sprintf("创建进度请求失败: %v", err),
  196. }
  197. }
  198. progressReq.SetBasicAuth(wq.Username, wq.Password)
  199. progressResp, err := wq.HTTPClient.Do(progressReq)
  200. if err != nil {
  201. return SimulationResult{
  202. Name: name,
  203. Success: false,
  204. Error: fmt.Sprintf("进度请求失败: %v", err),
  205. }
  206. }
  207. retryAfter := progressResp.Header.Get("Retry-After")
  208. if retryAfter == "" || retryAfter == "0" {
  209. // 模拟完成
  210. if err := json.NewDecoder(progressResp.Body).Decode(&resultData); err != nil {
  211. progressResp.Body.Close()
  212. return SimulationResult{
  213. Name: name,
  214. Success: false,
  215. Error: fmt.Sprintf("解析结果失败: %v", err),
  216. }
  217. }
  218. progressResp.Body.Close()
  219. break
  220. }
  221. progressResp.Body.Close()
  222. // 等待指定时间
  223. waitSeconds, _ := strconv.ParseFloat(retryAfter, 64)
  224. fmt.Printf("⏰ %s 等待 %.1f 秒...\n", name, waitSeconds)
  225. time.Sleep(time.Duration(waitSeconds * float64(time.Second)))
  226. }
  227. // 提取Alpha ID
  228. alphaID, ok := resultData["alpha"].(string)
  229. if !ok {
  230. return SimulationResult{
  231. Name: name,
  232. Success: false,
  233. Error: "未找到Alpha ID",
  234. }
  235. }
  236. fmt.Printf("✅ %s 模拟完成! Alpha ID: %s\n", name, alphaID)
  237. return SimulationResult{
  238. Name: name,
  239. Expression: config.Expression,
  240. AlphaID: alphaID,
  241. Success: true,
  242. ResultData: resultData,
  243. }
  244. }
  245. // BatchSimulateAlphas 批量测试多个Alpha因子
  246. func (wq *WorldQuantBrainAPI) BatchSimulateAlphas(configFile string, maxWorkers int) []SimulationResult {
  247. // 读取Alpha配置
  248. configs, err := wq.loadAlphaConfigs(configFile)
  249. if err != nil {
  250. log.Printf("❌ 读取配置文件失败: %v", err)
  251. return nil
  252. }
  253. fmt.Printf("📁 读取到 %d 个Alpha配置\n", len(configs))
  254. // 登录
  255. if err := wq.Login(); err != nil {
  256. log.Printf("❌ 登录失败: %v", err)
  257. return nil
  258. }
  259. var results []SimulationResult
  260. var mu sync.Mutex
  261. var wg sync.WaitGroup
  262. // 使用信号量控制并发数量
  263. sem := semaphore.NewWeighted(int64(maxWorkers))
  264. ctx := context.Background()
  265. for _, config := range configs {
  266. wg.Add(1)
  267. go func(c AlphaConfig) {
  268. defer wg.Done()
  269. // 获取信号量
  270. if err := sem.Acquire(ctx, 1); err != nil {
  271. log.Printf("❌ 获取信号量失败: %v", err)
  272. return
  273. }
  274. defer sem.Release(1)
  275. // 执行模拟
  276. result := wq.SimulateAlpha(c)
  277. // 安全地添加结果
  278. mu.Lock()
  279. results = append(results, result)
  280. mu.Unlock()
  281. }(config)
  282. }
  283. wg.Wait()
  284. // 打印汇总结果
  285. wq.printSummary(results)
  286. return results
  287. }
  288. // loadAlphaConfigs 加载Alpha配置
  289. func (wq *WorldQuantBrainAPI) loadAlphaConfigs(configFile string) ([]AlphaConfig, error) {
  290. data, err := os.ReadFile(configFile)
  291. if err != nil {
  292. return nil, fmt.Errorf("读取配置文件 %s 失败: %v", configFile, err)
  293. }
  294. var configs []AlphaConfig
  295. if err := json.Unmarshal(data, &configs); err != nil {
  296. return nil, fmt.Errorf("解析配置文件 %s 失败: %v", configFile, err)
  297. }
  298. return configs, nil
  299. }
  300. // printSummary 打印测试结果汇总
  301. func (wq *WorldQuantBrainAPI) printSummary(results []SimulationResult) {
  302. fmt.Println("\n" + strings.Repeat("=", 50))
  303. fmt.Println("📊 Alpha测试结果汇总")
  304. fmt.Println(strings.Repeat("=", 50))
  305. successCount := 0
  306. for _, result := range results {
  307. if result.Success {
  308. successCount++
  309. }
  310. }
  311. failedCount := len(results) - successCount
  312. fmt.Printf("✅ 成功: %d 个\n", successCount)
  313. fmt.Printf("❌ 失败: %d 个\n", failedCount)
  314. fmt.Println("\n成功详情:")
  315. for _, result := range results {
  316. if result.Success {
  317. fmt.Printf(" ✓ %s: %s\n", result.Name, result.AlphaID)
  318. }
  319. }
  320. if failedCount > 0 {
  321. fmt.Println("\n失败详情:")
  322. for _, result := range results {
  323. if !result.Success {
  324. fmt.Printf(" ✗ %s: %s\n", result.Name, result.Error)
  325. }
  326. }
  327. }
  328. }
  329. // Close 关闭客户端
  330. func (wq *WorldQuantBrainAPI) Close() {
  331. // 如果有需要关闭的资源,可以在这里处理
  332. }
  333. // 保存结果到文件
  334. func saveResultsToFile(results []SimulationResult, filename string) error {
  335. data, err := json.MarshalIndent(results, "", " ")
  336. if err != nil {
  337. return err
  338. }
  339. return os.WriteFile(filename, data, 0644)
  340. }
  341. func main() {
  342. // 创建API实例
  343. wqAPI := NewWorldQuantBrainAPI("")
  344. // 确保在程序结束时关闭连接
  345. defer wqAPI.Close()
  346. fmt.Println("🎯 开始批量测试Alpha因子...")
  347. startTime := time.Now()
  348. // 批量测试所有Alpha
  349. results := wqAPI.BatchSimulateAlphas("alphas.json", 2) // 并发数量,根据API限制调整
  350. duration := time.Since(startTime)
  351. fmt.Printf("\n⏱️ 总耗时: %.2f 秒\n", duration.Seconds())
  352. fmt.Println(results)
  353. }