From d81f00ab85d03686239d9d449ddc13da7205a723 Mon Sep 17 00:00:00 2001 From: jack Date: Thu, 13 Nov 2025 17:48:09 +0800 Subject: [PATCH] update --- requirements.txt | 20 +- wqb-demo/brain_credentials.txt | 2 +- wqb-demo/requirements.txt | 1 - wqb-login/brain_credentials.txt | 1 + wqb-login/login.py | 33 ++ wqb-simulate/account.txt | 1 + wqb-simulate/alpha.txt | 1 + .../result/simulation_results-1763024938.json | 10 + wqb-simulate/simulate.py | 309 ++++++++++++++++++ 9 files changed, 365 insertions(+), 13 deletions(-) delete mode 100644 wqb-demo/requirements.txt create mode 100644 wqb-login/brain_credentials.txt create mode 100644 wqb-login/login.py create mode 100644 wqb-simulate/account.txt create mode 100644 wqb-simulate/alpha.txt create mode 100644 wqb-simulate/result/simulation_results-1763024938.json create mode 100644 wqb-simulate/simulate.py diff --git a/requirements.txt b/requirements.txt index 943271e..365e6d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,24 +9,22 @@ seaborn>=0.12.0 # 量化交易 backtrader>=1.9.78 yfinance>=0.2.18 -## brew install ta-lib -ta-lib>=0.4.25 # 机器学习 -scikit-learn>=1.3.0 -xgboost>=1.7.0 +# scikit-learn>=1.3.0 +# xgboost>=1.7.0 # 工具库 httpx>=0.28.1 tqdm>=4.65.0 # PyTorch 生态系统 -torch>=2.0.0 -torchvision>=0.15.0 -torchaudio>=2.0.0 -pytorch-lightning>=2.0.0 -torchinfo>=1.7.0 +# torch>=2.0.0 +# torchvision>=0.15.0 +# torchaudio>=2.0.0 +# pytorch-lightning>=2.0.0 +# torchinfo>=1.7.0 # 时间序列深度学习 -pytorch-forecasting>=1.0.0 -pytorch-tabular>=1.0.0 +# pytorch-forecasting>=1.0.0 +# pytorch-tabular>=1.0.0 diff --git a/wqb-demo/brain_credentials.txt b/wqb-demo/brain_credentials.txt index 7716cbb..5cfaa62 100644 --- a/wqb-demo/brain_credentials.txt +++ b/wqb-demo/brain_credentials.txt @@ -1 +1 @@ -['uname', 'pw'] \ No newline at end of file +['jack0210_@hotmail.com', '!QAZ2wsx+0913'] \ No newline at end of file diff --git a/wqb-demo/requirements.txt b/wqb-demo/requirements.txt deleted file mode 100644 index aebd9d2..0000000 --- a/wqb-demo/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -httpx==0.28.1 \ No newline at end of file diff --git a/wqb-login/brain_credentials.txt b/wqb-login/brain_credentials.txt new file mode 100644 index 0000000..5cfaa62 --- /dev/null +++ b/wqb-login/brain_credentials.txt @@ -0,0 +1 @@ +['jack0210_@hotmail.com', '!QAZ2wsx+0913'] \ No newline at end of file diff --git a/wqb-login/login.py b/wqb-login/login.py new file mode 100644 index 0000000..b30249a --- /dev/null +++ b/wqb-login/login.py @@ -0,0 +1,33 @@ +import httpx +from httpx import BasicAuth + +def login(credentials_file='brain_credentials.txt'): + """登录WorldQuant Brain API""" + # 读取本地账号密码 + with open(credentials_file) as f: + credentials = eval(f.read()) + username, password = credentials[0], credentials[1] + + print(f"正在登录账户: {username}") + + # 创建客户端并认证 + client = httpx.Client(auth=BasicAuth(username, password)) + + # 发送登录请求 + response = client.post('https://api.worldquantbrain.com/authentication') + print(f"登录状态: {response.status_code}") + + if response.status_code == 201: + print("登录成功!") + print(response.json()) + return client + else: + print(f"登录失败: {response.json()}") + client.close() + return None + +# 使用示例 +if __name__ == "__main__": + client = login() + if client: + client.close() \ No newline at end of file diff --git a/wqb-simulate/account.txt b/wqb-simulate/account.txt new file mode 100644 index 0000000..5cfaa62 --- /dev/null +++ b/wqb-simulate/account.txt @@ -0,0 +1 @@ +['jack0210_@hotmail.com', '!QAZ2wsx+0913'] \ No newline at end of file diff --git a/wqb-simulate/alpha.txt b/wqb-simulate/alpha.txt new file mode 100644 index 0000000..23c943e --- /dev/null +++ b/wqb-simulate/alpha.txt @@ -0,0 +1 @@ +ts_Mean(ts_mean(close - delay(close,1),5) / atr(15), 30) \ No newline at end of file diff --git a/wqb-simulate/result/simulation_results-1763024938.json b/wqb-simulate/result/simulation_results-1763024938.json new file mode 100644 index 0000000..4fd0dcd --- /dev/null +++ b/wqb-simulate/result/simulation_results-1763024938.json @@ -0,0 +1,10 @@ +[ + { + "expression": "ts_Mean(ts_mean(close - delay(close,1),5) / atr(15), 30)", + "time_consuming": 6.7, + "formatted_time": "6.70秒", + "alpha_id": "/", + "status": "error", + "description": "Attempted to use inaccessible or unknown operator \"delay\"" + } +] \ No newline at end of file diff --git a/wqb-simulate/simulate.py b/wqb-simulate/simulate.py new file mode 100644 index 0000000..00d1663 --- /dev/null +++ b/wqb-simulate/simulate.py @@ -0,0 +1,309 @@ +import os.path + +import httpx +import json +from httpx import BasicAuth +import time +from random import uniform +import threading +from concurrent.futures import ThreadPoolExecutor, as_completed + + +class WorldQuantBrainSimulate: + def __init__(self, credentials_file='account.txt'): + self.credentials_file = credentials_file + self.client = None + self.brain_api_url = 'https://api.worldquantbrain.com' + + def load_credentials(self): + """读取本地账号密码""" + if not os.path.exists(self.credentials_file): + print("未找到 account.txt 文件") + with open(self.credentials_file, 'w') as f: f.write("") + print("account.txt 文件已创建,请填写账号密码, 格式: ['username', 'password]") + exit(1) + + with open(self.credentials_file) as f: + credentials = eval(f.read()) + return credentials[0], credentials[1] + + def login(self): + """登录认证""" + username, password = self.load_credentials() + self.client = httpx.Client(auth=BasicAuth(username, password)) + + 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.json()}") + return False + + def simulate_alpha(self, expression, settings=None): + """模拟Alpha因子""" + if self.client is None: + raise Exception("请先登录") + + default_settings = { + 'instrumentType': 'EQUITY', + 'region': 'USA', + 'universe': 'TOP3000', + 'delay': 1, + 'decay': 0, + 'neutralization': 'INDUSTRY', + 'truncation': 0.08, + 'pasteurization': 'ON', + 'unitHandling': 'VERIFY', + 'nanHandling': 'OFF', + 'language': 'FASTEXPR', + 'visualization': False, + } + + if settings: + default_settings.update(settings) + + simulation_data = { + 'type': 'REGULAR', + 'settings': default_settings, + 'regular': expression + } + + sim_resp = self.client.post(f'{self.brain_api_url}/simulations', json=simulation_data) + print(f"模拟提交状态: {sim_resp.status_code}") + + sim_progress_url = sim_resp.headers['location'] + print(f"进度URL: {sim_progress_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: + break + print(sim_progress_resp.json()) + print(f"等待 {retry_after_sec} 秒...") + time.sleep(retry_after_sec) + + # 如果因子模拟不通过, 获取一下失败信息 + if sim_progress_resp.json()["status"] == "ERROR": + result = sim_progress_resp.json()["message"] + print(f"因子模拟失败: {result}") + # 返回一个特殊标识,表示模拟失败 + return {"status": "error", "message": result} + + result = sim_progress_resp.json()["alpha"] + print(f"生成的Alpha ID: {result}") + return {"status": "success", "alpha_id": result} + + def close(self): + """关闭连接""" + if self.client: + self.client.close() + + +class AlphaSimulationManager: + def __init__(self, credentials_file='account.txt'): + self.credentials_file = credentials_file + self.results = [] + + def format_time(self, seconds): + """将秒数格式化为 xx分xx秒 格式""" + if seconds < 60: + return f"{seconds:.2f}秒" + else: + minutes = int(seconds // 60) + remaining_seconds = seconds % 60 + return f"{minutes}分{remaining_seconds:.2f}秒" + + def simulate_single_alpha(self, api, expression, settings=None): + """模拟单个Alpha因子(线程安全)""" + alpha_start_time = time.time() + + try: + # 模拟Alpha因子 + simulation_result = api.simulate_alpha(expression, settings) + alpha_end_time = time.time() + time_consuming = alpha_end_time - alpha_start_time + + # 根据模拟结果类型处理 + if simulation_result["status"] == "success": + # 模拟成功的结果 + result = { + "expression": expression, + "time_consuming": time_consuming, + "formatted_time": self.format_time(time_consuming), + "alpha_id": simulation_result["alpha_id"], + "status": "success", + "description": "/" + } + print(f"✓ 因子模拟成功: {expression}") + print(f" 耗时: {self.format_time(time_consuming)},Alpha ID: {simulation_result['alpha_id']}") + + else: + # 模拟失败的结果(API返回的错误) + result = { + "expression": expression, + "time_consuming": time_consuming, + "formatted_time": self.format_time(time_consuming), + "alpha_id": "/", + "status": "error", + "description": simulation_result["message"] + } + print(f"✗ 因子模拟失败: {expression}") + print(f" 耗时: {self.format_time(time_consuming)},错误: {simulation_result['message']}") + + except Exception as e: + # 其他异常情况 + alpha_end_time = time.time() + time_consuming = alpha_end_time - alpha_start_time + + result = { + "expression": expression, + "time_consuming": time_consuming, + "formatted_time": self.format_time(time_consuming), + "alpha_id": "/", + "status": "failed", + "description": str(e) + } + print(f"✗ 因子模拟异常: {expression}") + print(f" 耗时: {self.format_time(time_consuming)},异常: {str(e)}") + + return result + + def simulate_alpha_batch(self, alpha_batch, batch_number): + """模拟一批Alpha因子(3个一组)""" + print(f"\n{'=' * 60}") + print(f"开始第 {batch_number} 批因子模拟 (共 {len(alpha_batch)} 个因子)") + print(f"因子列表: {alpha_batch}") + print(f"{'=' * 60}") + + batch_start_time = time.time() + batch_results = [] + + # 创建API客户端实例(每个线程独立的客户端) + api = WorldQuantBrainSimulate(self.credentials_file) + + try: + if api.login(): + # 使用线程池执行3个因子的模拟 + with ThreadPoolExecutor(max_workers=3) as executor: + # 提交所有任务 + future_to_alpha = {executor.submit(self.simulate_single_alpha, api, alpha): alpha for alpha in alpha_batch} + + # 等待所有任务完成 + for future in as_completed(future_to_alpha): + alpha = future_to_alpha[future] + try: + result = future.result() + batch_results.append(result) + except Exception as e: + print(f"因子 {alpha} 执行异常: {e}") + except Exception as e: + print(f"第 {batch_number} 批模拟过程中出错: {e}") + finally: + api.close() + + batch_end_time = time.time() + batch_total_time = batch_end_time - batch_start_time + + print(f"\n第 {batch_number} 批模拟完成!") + print(f"本批总耗时: {self.format_time(batch_total_time)}") + print(f"{'=' * 60}") + + return batch_results + + def run_simulation(self, alpha_list, batch_size=3): + """运行批量模拟""" + print("开始Alpha因子批量模拟...") + total_start_time = time.time() + + # 将因子列表分成每批3个 + batches = [alpha_list[i:i + batch_size] for i in range(0, len(alpha_list), batch_size)] + + all_results = [] + + for i, batch in enumerate(batches, 1): + # 模拟当前批次 + batch_results = self.simulate_alpha_batch(batch, i) + all_results.extend(batch_results) + + # 如果不是最后一批,则等待3-5秒 + if i < len(batches): + sleep_time = uniform(3, 5) + print(f"\n等待 {sleep_time:.2f} 秒后开始下一批...") + time.sleep(sleep_time) + + total_end_time = time.time() + total_time = total_end_time - total_start_time + + # 输出最终结果汇总 + self.print_summary(all_results, total_time) + + # 保存结果到文件 + self.save_results(all_results) + + return all_results + + def print_summary(self, results, total_time): + """打印结果汇总""" + print(f"\n{'=' * 60}") + print("模拟结果汇总") + print(f"{'=' * 60}") + + success_count = sum(1 for r in results if r['status'] == 'success') + error_count = sum(1 for r in results if r['status'] == 'error') + failed_count = sum(1 for r in results if r['status'] == 'failed') + + print(f"总模拟因子数: {len(results)}") + print(f"成功: {success_count} 个") + print(f"模拟错误: {error_count} 个") + print(f"执行异常: {failed_count} 个") + print(f"总耗时: {self.format_time(total_time)}") + print(f"{'=' * 60}") + + for i, result in enumerate(results, 1): + status_icon = "✓" if result['status'] == 'success' else "✗" + print(f"{i}. {status_icon} {result['expression']}") + print(f" 状态: {result['status']}") + print(f" 耗时: {result['formatted_time']}") + print(f" Alpha ID: {result['alpha_id']}") + if result['status'] != 'success': + print(f" 原因: {result['description']}") + print() + + def save_results(self, results): + """保存结果到文件""" + # 转换为可序列化的格式 + serializable_results = [] + for result in results: + serializable_result = result.copy() + serializable_result['time_consuming'] = round(serializable_result['time_consuming'], 2) + serializable_results.append(serializable_result) + + # 将日志文件, 保存到当前目录下, result 文件夹中 + if not os.path.exists('./result'): + os.makedirs('./result') + + result_name = f"result/simulation_results-{str(int(time.time()))}.json" + with open(result_name, 'w', encoding='utf-8') as f: + json.dump(serializable_results, f, ensure_ascii=False, indent=2) + print(f"结果已保存到 {result_name}") + + +if __name__ == "__main__": + # 待模拟因子列表 + with open('alpha.txt', 'r', encoding='utf-8') as file: + alpha_list = [line.strip() for line in file] + + if not alpha_list: + print("alpha.txt 文件不存在") + with open('alpha.txt', 'w', encoding='utf-8') as file: file.write("") + print("已创建 alpha.txt 文件, 请添加因子后重新运行, 一行一个因子") + exit(1) + + # 创建模拟管理器并运行 + manager = AlphaSimulationManager() + results = manager.run_simulation(alpha_list, batch_size=3) \ No newline at end of file