import httpx import json from concurrent.futures import ThreadPoolExecutor, as_completed from os.path import expanduser, exists from httpx import BasicAuth from time import sleep from datetime import datetime class WorldQuantBrainAPI: def __init__(self, credentials_file='brain_credentials.txt'): """初始化API客户端""" self.credentials_file = expanduser(credentials_file) self.client = None self.brain_api_url = 'https://api.worldquantbrain.com' def login(self): """登录认证""" try: # 检查凭证文件是否存在 if not exists(self.credentials_file): print(f"❌ 凭证文件不存在: {self.credentials_file}") return False # Load credentials with open(self.credentials_file) as f: file_content = f.read().strip() with open(self.credentials_file) as f: credentials = eval(f.read()) # Extract username and password from the list if len(credentials) != 2: print(f"❌ 凭证格式错误,应该是 [username, password] 格式,但得到: {credentials}") return False username, password = credentials # Create a client with basic authentication self.client = httpx.Client(auth=BasicAuth(username, password)) # Send authentication request response = self.client.post(f'{self.brain_api_url}/authentication') print(f"认证状态: {response.status_code}") if response.status_code == 201: print("✅ 登录成功!") return True else: print(f"❌ 登录失败: {response.text}") return False except json.JSONDecodeError as e: print(f"❌ JSON解析错误: {e}") print(f"📄 文件内容: {file_content if 'file_content' in locals() else '无法读取'}") return False except Exception as e: print(f"❌ 登录过程中出错: {str(e)}") return False def batch_simulate_alphas(self, alphas_config_file='alphas.json', max_workers=3): """批量测试多个Alpha因子(使用线程池)""" # 检查配置文件是否存在 if not exists(alphas_config_file): print(f"❌ 配置文件 {alphas_config_file} 不存在") return [] # 读取Alpha配置 try: with open(alphas_config_file, 'r', encoding='utf-8') as f: file_content = f.read().strip() print(f"📄 Alpha配置文件内容预览: {file_content[:200]}...") # 只显示前200个字符 with open(alphas_config_file, 'r', encoding='utf-8') as f: alpha_configs = json.load(f) print(f"✅ 成功读取 {len(alpha_configs)} 个Alpha配置") for i, config in enumerate(alpha_configs): print(f" {i+1}. {config.get('name', '未命名')}") except json.JSONDecodeError as e: print(f"❌ 配置文件 JSON格式错误: {e}") print(f"📄 出错的文件内容: {file_content if 'file_content' in locals() else '无法读取'}") return [] except Exception as e: print(f"❌ 读取配置文件时出错: {str(e)}") return [] print(f"📁 读取到 {len(alpha_configs)} 个Alpha配置") # 登录 if not self.login(): print("❌ 登录失败,无法继续") return [] results = [] # 使用线程池并发执行 with ThreadPoolExecutor(max_workers=max_workers) as executor: # 提交所有任务 future_to_alpha = { executor.submit(self.simulate_alpha, config): config for config in alpha_configs } # 收集结果 for future in as_completed(future_to_alpha): config = future_to_alpha[future] try: result = future.result() results.append(result) except Exception as e: print(f"❌ {config.get('name', '未知Alpha')} 执行失败: {str(e)}") results.append({ 'name': config.get('name', '未知Alpha'), 'success': False, 'error': str(e) }) # 打印汇总结果 self._print_summary(results) return results def simulate_alpha(self, alpha_config): """模拟单个Alpha因子""" if self.client is None: raise Exception("请先调用 login() 方法登录") name = alpha_config.get('name', '未命名Alpha') expression = alpha_config['expression'] settings = alpha_config.get('settings', {}) print(f"\n🚀 开始模拟: {name}") print(f"📝 表达式: {expression}") simulation_data = { 'type': 'REGULAR', 'settings': settings, 'regular': expression } try: # 发送模拟数据 sim_resp = self.client.post( f'{self.brain_api_url}/simulations', json=simulation_data, ) print(f"📤 模拟提交状态: {sim_resp.status_code}") if sim_resp.status_code != 201: print(f"❌ {name} 提交失败: {sim_resp.text}") return {'name': name, 'success': False, 'error': f'提交失败: {sim_resp.status_code}'} # 获取进度URL并轮询结果 sim_progress_url = sim_resp.headers['location'] print(f"⏳ {name} 进度URL获取成功") while True: sim_progress_resp = self.client.get(sim_progress_url) retry_after_sec = float(sim_progress_resp.headers.get("Retry-After", 0)) if retry_after_sec == 0: # simulation done! break print(f"⏰ {name} 等待 {retry_after_sec} 秒...") sleep(retry_after_sec) # 获取最终的alpha ID result_data = sim_progress_resp.json() alpha_id = result_data["alpha"] print(f"✅ {name} 模拟完成! Alpha ID: {alpha_id}") return { 'name': name, 'expression': expression, 'alpha_id': alpha_id, 'success': True, 'result_data': result_data } except Exception as e: print(f"❌ {name} 模拟过程中出错: {str(e)}") return {'name': name, 'success': False, 'error': str(e)} def _print_summary(self, results): """打印测试结果汇总""" print("\n" + "=" * 50) print("📊 Alpha测试结果汇总") print("=" * 50) success_count = sum(1 for r in results if r.get('success', False)) failed_count = len(results) - success_count print(f"✅ 成功: {success_count} 个") print(f"❌ 失败: {failed_count} 个") print("\n成功详情:") for result in results: if result.get('success'): print(f" ✓ {result['name']}: {result['alpha_id']}") if failed_count > 0: print("\n失败详情:") for result in results: if not result.get('success'): print(f" ✗ {result['name']}: {result.get('error', '未知错误')}") def close(self): """关闭客户端连接""" if self.client: self.client.close() self.client = None # 使用示例 if __name__ == "__main__": # 创建API实例 wq_api = WorldQuantBrainAPI() try: # 1. 批量测试所有Alpha print("🎯 开始批量测试Alpha因子...") start_time = datetime.now() results = wq_api.batch_simulate_alphas( alphas_config_file='alphas.json', max_workers=2 # 并发数量,根据API限制调整 ) end_time = datetime.now() print(f"\n⏱️ 测试总耗时: {(end_time - start_time).total_seconds():.2f} 秒") # 2. 提取成功的Alpha ID successful_alphas = [r for r in results if r.get('success')] alpha_ids = [r['alpha_id'] for r in successful_alphas] print(f"\n📋 成功模拟的Alpha数量: {len(alpha_ids)}") for alpha in successful_alphas: print(f" - {alpha['name']}: {alpha['alpha_id']}") except Exception as e: print(f"❌ 程序执行出错: {str(e)}") import traceback traceback.print_exc() # 打印完整的错误堆栈 finally: # 确保连接关闭 wq_api.close()