From d4e55e9aca39c5d6124a72fde309da1cdd0977c9 Mon Sep 17 00:00:00 2001 From: jack Date: Wed, 26 Nov 2025 18:06:29 +0800 Subject: [PATCH] update --- Readme.md | 25 - __init__.py | 13 - alpha.txt | 1 - config/__init__.py | 8 - config/settings.py | 70 ++- core/__init__.py | 9 - core/api_client.py | 158 ----- core/database.py | 176 ++++++ core/models.py | 62 -- core/simulator.py | 126 ++++ main.py | 46 +- managers/__init__.py | 8 - managers/simulation_manager.py | 232 ------- models/__init__.py | 0 models/entities.py | 32 + reference/yearly-stats.json | 168 ----- reference/指标.json | 137 ----- reference/进度.json | 22 - requirements.txt | 714 ++++++++++++++++++++++ result/simulation_results-1763109446.json | 289 --------- result/simulation_results-1763110929.json | 524 ---------------- result/simulation_results-1763112962.json | 82 --- result/simulation_results-1763113286.json | 12 - services/__init__.py | 0 services/alpha_service.py | 60 ++ services/auth_service.py | 57 ++ services/notification_service.py | 27 + success.txt | 4 - utils/__init__.py | 9 - utils/file_utils.py | 84 --- utils/helpers.py | 23 + utils/time_utils.py | 9 - 32 files changed, 1302 insertions(+), 1885 deletions(-) delete mode 100644 Readme.md delete mode 100644 alpha.txt delete mode 100644 core/api_client.py create mode 100644 core/database.py delete mode 100644 core/models.py create mode 100644 core/simulator.py delete mode 100644 managers/__init__.py delete mode 100644 managers/simulation_manager.py create mode 100644 models/__init__.py create mode 100644 models/entities.py delete mode 100644 reference/yearly-stats.json delete mode 100644 reference/指标.json delete mode 100644 reference/进度.json create mode 100644 requirements.txt delete mode 100644 result/simulation_results-1763109446.json delete mode 100644 result/simulation_results-1763110929.json delete mode 100644 result/simulation_results-1763112962.json delete mode 100644 result/simulation_results-1763113286.json create mode 100644 services/__init__.py create mode 100644 services/alpha_service.py create mode 100644 services/auth_service.py create mode 100644 services/notification_service.py delete mode 100644 success.txt delete mode 100644 utils/file_utils.py create mode 100644 utils/helpers.py delete mode 100644 utils/time_utils.py diff --git a/Readme.md b/Readme.md deleted file mode 100644 index 09e93a3..0000000 --- a/Readme.md +++ /dev/null @@ -1,25 +0,0 @@ - -### 依赖 -pip install httpx - -### 目录结构 - -```text -FactorSimulator/ -├── __init__.py # 包初始化文件,定义包级别的导入和元数据 -├── main.py # 程序主入口,负责启动批量模拟流程 -├── core/ # 核心业务逻辑模块 -│ ├── __init__.py # 核心模块初始化,定义模块级别的导入 -│ ├── api_client.py # WorldQuant Brain API客户端封装,处理HTTP请求和认证 -│ └── models.py # 数据模型定义,使用dataclass定义各种指标和结果的数据结构 -├── managers/ # 管理器模块,负责业务流程协调 -│ ├── __init__.py # 管理器模块初始化 -│ └── simulation_manager.py # 模拟管理器,负责批量模拟的调度、线程池管理和结果汇总 -├── utils/ # 工具函数模块 -│ ├── __init__.py # 工具模块初始化 -│ ├── file_utils.py # 文件操作工具,处理因子列表加载和结果保存 -│ └── time_utils.py # 时间格式化工具,将秒数转换为可读格式 -└── config/ # 配置模块 - ├── __init__.py # 配置模块初始化 - └── settings.py # 模拟参数配置,定义默认的模拟设置常量 -``` \ No newline at end of file diff --git a/__init__.py b/__init__.py index aa27a57..e69de29 100644 --- a/__init__.py +++ b/__init__.py @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -""" -WorldQuant Brain 因子模拟器 -用于批量模拟Alpha因子的工具 -""" - -__version__ = "0.0.1" -__author__ = "Jack" - -from .core.api_client import WorldQuantBrainSimulate -from .managers.simulation_manager import AlphaSimulationManager - -__all__ = ['WorldQuantBrainSimulate', 'AlphaSimulationManager'] \ No newline at end of file diff --git a/alpha.txt b/alpha.txt deleted file mode 100644 index 5c6253b..0000000 --- a/alpha.txt +++ /dev/null @@ -1 +0,0 @@ -ts_delta(ts_mean(close, 5), 10) / ts_std(close, 20) * (ts_mean(close, 20) / ts_mean(close, 100) > 1.02) \ No newline at end of file diff --git a/config/__init__.py b/config/__init__.py index 0494c40..e69de29 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- -""" -配置模块 - 包含配置常量 -""" - -from .settings import DEFAULT_SIMULATION_SETTINGS - -__all__ = ['DEFAULT_SIMULATION_SETTINGS'] \ No newline at end of file diff --git a/config/settings.py b/config/settings.py index ab5471f..230d830 100644 --- a/config/settings.py +++ b/config/settings.py @@ -1,19 +1,53 @@ # -*- coding: utf-8 -*- -""" -模拟配置常量 -""" - -DEFAULT_SIMULATION_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, -} \ No newline at end of file +"""配置文件""" + +import os +from typing import Dict, Any + + +class Settings: + """应用配置""" + + # 数据库配置 + DATABASE_CONFIG = { + "host": "localhost", + "port": "5432", + "user": "jack", + "password": "aaaAAA111", + "database": "alpha" + } + + # API配置 + BRAIN_API_URL = "https://api.worldquantbrain.com" + + # 模拟配置 + SIMULATION_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, + } + + # 批处理配置 + BATCH_SIZE = 3 + CHECK_INTERVAL = 300 # 5分钟 + TOKEN_REFRESH_THRESHOLD = 1800 # 30分钟 + + # 通知配置 + GOTIFY_URL = "https://gotify.erhe.top/message?token=AvKJCJwQKU6yLP8" + + @property + def credentials_file(self) -> str: + """获取凭证文件路径""" + return os.path.join(os.path.dirname(os.path.dirname(__file__)), 'account.txt') + + +settings = Settings() \ No newline at end of file diff --git a/core/__init__.py b/core/__init__.py index a58446f..e69de29 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- -""" -核心模块 - 包含API客户端和数据模型 -""" - -from .api_client import WorldQuantBrainSimulate -from .models import AlphaMetrics, SimulationResult - -__all__ = ['WorldQuantBrainSimulate', 'AlphaMetrics', 'SimulationResult'] \ No newline at end of file diff --git a/core/api_client.py b/core/api_client.py deleted file mode 100644 index d8961bc..0000000 --- a/core/api_client.py +++ /dev/null @@ -1,158 +0,0 @@ -# -*- coding: utf-8 -*- -import os.path -import httpx -import time -from httpx import BasicAuth -from typing import Dict, Any, Optional, Tuple - - -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) -> Tuple[str, str]: - 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) -> bool: - 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("登录成功!") - print(f"账户信息: {response.json()}") - return True - else: - print(f"登录失败: {response.json()}") - return False - - """模拟Alpha因子""" - def simulate_alpha(self, expression: str, settings: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: - 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 - if sim_progress_resp.json(): - result = sim_progress_resp.json() - progress = result['progress'] - if progress: - print(f"模拟进度: {float(progress)*100}%") - - 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} - - alpha_id = sim_progress_resp.json()["alpha"] - print(f"生成的Alpha ID: {alpha_id}") - - # 获取详细的性能指标 - metrics = self.get_alpha_metrics(alpha_id) - - return {"status": "success", "alpha_id": alpha_id, "metrics": metrics} - - """获取Alpha因子的详细指标""" - def get_alpha_metrics(self, alpha_id: str) -> Dict[str, Any]: - if self.client is None: - raise Exception("请先登录") - - try: - # 获取Alpha的基本信息和指标 - alpha_url = f'{self.brain_api_url}/alphas/{alpha_id}' - alpha_resp = self.client.get(alpha_url) - - if alpha_resp.status_code in [200, 201]: - alpha_data = alpha_resp.json() - - # 以后可能需要获取其他参数 - if alpha_data.get('metrics'): - alpha_data = alpha_data.get('metrics') - - return alpha_data or {} - else: - print(f"获取Alpha指标失败: {alpha_resp.status_code}") - # 返回一个空的字典结构 - return { - "train": {}, - "is": {}, - "test": {}, - "grade": None, - "stage": None, - "status": None, - "dateCreated": None, - "id": alpha_id - } - - except Exception as e: - print(f"获取指标时出错: {str(e)}") - # 返回一个空的字典结构 - return { - "train": {}, - "is": {}, - "test": {}, - "grade": None, - "stage": None, - "status": None, - "dateCreated": None, - "id": alpha_id - } - - def close(self): - """关闭连接""" - if self.client: - self.client.close() \ No newline at end of file diff --git a/core/database.py b/core/database.py new file mode 100644 index 0000000..83b49b2 --- /dev/null +++ b/core/database.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +"""数据库管理层""" + +import psycopg2 +from typing import List, Optional +from config.settings import settings +from models.entities import SimulationResult, AlphaExpression + + +class DatabaseManager: + """数据库管理类""" + + def __init__(self): + self.connection = None + self.init_database() + + def create_database(self) -> None: + """创建数据库(如果不存在)""" + try: + # 先连接到默认的postgres数据库来创建alpha数据库 + conn = psycopg2.connect( + host=settings.DATABASE_CONFIG["host"], + port=settings.DATABASE_CONFIG["port"], + database="postgres", + user=settings.DATABASE_CONFIG["user"], + password=settings.DATABASE_CONFIG["password"] + ) + conn.autocommit = True + cursor = conn.cursor() + + # 检查数据库是否存在 + cursor.execute("SELECT 1 FROM pg_catalog.pg_database WHERE datname = %s", + (settings.DATABASE_CONFIG["database"],)) + exists = cursor.fetchone() + + if not exists: + cursor.execute(f"CREATE DATABASE {settings.DATABASE_CONFIG['database']}") + print(f"数据库 {settings.DATABASE_CONFIG['database']} 创建成功") + else: + print(f"数据库 {settings.DATABASE_CONFIG['database']} 已存在") + + cursor.close() + conn.close() + + except Exception as e: + print(f"创建数据库时出错: {e}") + raise + + def get_connection(self) -> psycopg2.extensions.connection: + """获取数据库连接""" + if self.connection is None or self.connection.closed: + self.connection = psycopg2.connect( + host=settings.DATABASE_CONFIG["host"], + port=settings.DATABASE_CONFIG["port"], + database=settings.DATABASE_CONFIG["database"], + user=settings.DATABASE_CONFIG["user"], + password=settings.DATABASE_CONFIG["password"] + ) + return self.connection + + def init_database(self) -> None: + """初始化数据库和表结构""" + # 先创建数据库 + self.create_database() + + # 然后连接到此数据库创建表 + conn = self.get_connection() + cursor = conn.cursor() + + # 创建 alpha_prepare 表 + cursor.execute(''' + CREATE TABLE IF NOT EXISTS alpha_prepare + ( + id + SERIAL + PRIMARY + KEY, + alpha + TEXT + NOT + NULL + UNIQUE, + unused + BOOLEAN + NOT + NULL + DEFAULT + TRUE, + created_time + TIMESTAMP + DEFAULT + CURRENT_TIMESTAMP + ) + ''') + + # 创建 simulation 表 + cursor.execute(''' + CREATE TABLE IF NOT EXISTS simulation + ( + id + SERIAL + PRIMARY + KEY, + expression + TEXT + NOT + NULL, + time_consuming + REAL + NOT + NULL, + status + TEXT + NOT + NULL, + timestamp + TEXT + NOT + NULL, + alpha_id + TEXT, + message + TEXT, + created_time + TIMESTAMP + DEFAULT + CURRENT_TIMESTAMP + ) + ''') + + conn.commit() + print(f"数据库 {settings.DATABASE_CONFIG['database']} 表结构初始化完成") + + def get_unused_alpha(self) -> List[str]: + """获取所有未使用的alpha表达式""" + conn = self.get_connection() + cursor = conn.cursor() + + cursor.execute("SELECT alpha FROM alpha_prepare WHERE unused = TRUE") + results = cursor.fetchall() + + alpha_list = [result[0] for result in results] + return alpha_list + + def mark_alpha_used(self, alpha: str) -> None: + """将alpha标记为已使用""" + conn = self.get_connection() + cursor = conn.cursor() + + cursor.execute("UPDATE alpha_prepare SET unused = FALSE WHERE alpha = %s", (alpha,)) + conn.commit() + + def insert_simulation_result(self, result: SimulationResult) -> None: + """插入模拟结果到simulation表""" + conn = self.get_connection() + cursor = conn.cursor() + + cursor.execute(''' + INSERT INTO simulation + (expression, time_consuming, status, timestamp, alpha_id, message) + VALUES (%s, %s, %s, %s, %s, %s) + ''', ( + result.expression, + result.time_consuming, + result.status, + result.timestamp, + result.alpha_id, + result.message or "" + )) + + conn.commit() + + def close_connection(self) -> None: + """关闭数据库连接""" + if self.connection and not self.connection.closed: + self.connection.close() \ No newline at end of file diff --git a/core/models.py b/core/models.py deleted file mode 100644 index 5872fd7..0000000 --- a/core/models.py +++ /dev/null @@ -1,62 +0,0 @@ -# -*- coding: utf-8 -*- -from dataclasses import dataclass -from typing import Dict, Any, Optional - - -@dataclass -class TrainMetrics: - """训练集指标""" - sharpe_ratio: Optional[float] = None - annual_return: Optional[float] = None - max_drawdown: Optional[float] = None - turnover: Optional[float] = None - fitness: Optional[float] = None - pnl: Optional[float] = None - book_size: Optional[float] = None - long_count: Optional[float] = None - short_count: Optional[float] = None - margin: Optional[float] = None - - -@dataclass -class TestMetrics: - """测试集指标""" - sharpe_ratio: Optional[float] = None - annual_return: Optional[float] = None - max_drawdown: Optional[float] = None - turnover: Optional[float] = None - fitness: Optional[float] = None - pnl: Optional[float] = None - - -@dataclass -class AlphaInfo: - """Alpha基本信息""" - grade: Optional[str] = None - stage: Optional[str] = None - status: Optional[str] = None - date_created: Optional[str] = None - checks: Optional[Dict[str, Any]] = None - - -@dataclass -class AlphaMetrics: - """Alpha因子完整指标""" - train_metrics: TrainMetrics - is_metrics: TestMetrics - test_metrics: TestMetrics - alpha_info: AlphaInfo - alpha_id: Optional[str] = None - - -@dataclass -class SimulationResult: - """模拟结果""" - expression: str - time_consuming: float - formatted_time: str - alpha_id: str - status: str - description: str - simulation_timestamp: str - metrics: Optional[Dict[str, Any]] = None \ No newline at end of file diff --git a/core/simulator.py b/core/simulator.py new file mode 100644 index 0000000..b683d2b --- /dev/null +++ b/core/simulator.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- +"""Alpha模拟器核心类""" + +import time +import httpx +from datetime import datetime +from typing import List, Tuple +from config.settings import settings +from models.entities import SimulationResult, TokenInfo +from core.database import DatabaseManager +from services.auth_service import AuthService +from services.alpha_service import AlphaService +from services.notification_service import NotificationService + + +class AlphaSimulator: + """Alpha模拟器主类""" + + def __init__(self): + self.db_manager = DatabaseManager() + self.auth_service = AuthService() + self.client = None + self.token_info = None + self.alpha_service = None + + def __del__(self): + """析构函数,确保数据库连接被关闭""" + if hasattr(self, 'db_manager'): + self.db_manager.close_connection() + + def initialize(self) -> None: + """初始化模拟器""" + self.client = httpx.Client() + self.login() + + def login(self) -> TokenInfo: + """登录并初始化alpha服务""" + self.token_info = self.auth_service.login(self.client) + self.alpha_service = AlphaService(self.client) + return self.token_info + + def needs_token_refresh(self) -> bool: + """检查是否需要刷新token""" + if not self.token_info: + return True + return self.token_info.expiry < settings.TOKEN_REFRESH_THRESHOLD + + def load_alpha_list(self) -> List[str]: + """从数据库加载未使用的alpha表达式""" + return self.db_manager.get_unused_alpha() + + def run_batch_simulation(self, alpha_list: List[str]) -> Tuple[int, int]: + """运行批量模拟""" + success_count = 0 + fail_count = 0 + + for i in range(0, len(alpha_list), settings.BATCH_SIZE): + batch = alpha_list[i:i + settings.BATCH_SIZE] + print(f"\n开始处理第 {i // settings.BATCH_SIZE + 1} 批因子,共 {len(batch)} 个") + + for expression in batch: + result = self._simulate_single_alpha(expression) + if result.status == "ok": + success_count += 1 + else: + fail_count += 1 + + print(f"第 {i // settings.BATCH_SIZE + 1} 批处理完成") + + self._print_summary(success_count, fail_count) + return success_count, fail_count + + def _simulate_single_alpha(self, expression: str) -> SimulationResult: + """模拟单个Alpha表达式""" + print(f"\n模拟因子: {expression}") + start_time = time.time() + + try: + result = self.alpha_service.simulate_alpha(expression) + end_time = time.time() + time_consuming = round(end_time - start_time, 2) + + simulation_result = SimulationResult( + expression=expression, + time_consuming=time_consuming, + status=result["status"], + timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + alpha_id=result.get("alpha_id"), + message=result.get("message", "") + ) + + if result["status"] == "ok": + print(f"✅ 模拟成功 - Alpha ID: {result['alpha_id']} - 耗时: {time_consuming}秒") + else: + print(f"❌ 模拟失败 - {result.get('message', '未知错误')}") + + except Exception as e: + end_time = time.time() + time_consuming = round(end_time - start_time, 2) + simulation_result = SimulationResult( + expression=expression, + time_consuming=time_consuming, + status="err", + message=str(e), + timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ) + print(f"❌ 模拟异常 - {str(e)}") + + # 保存结果并标记为已使用 + self._save_simulation_result(simulation_result) + return simulation_result + + def _save_simulation_result(self, result: SimulationResult) -> None: + """保存模拟结果到数据库""" + self.db_manager.mark_alpha_used(result.expression) + self.db_manager.insert_simulation_result(result) + + def _print_summary(self, success_count: int, fail_count: int) -> None: + """打印总结信息""" + now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + print(f"\n总计: 成功 {success_count} 个, 失败 {fail_count} 个") + print(f"完成时间: {now}") + print(f"所有结果已保存到 PostgreSQL 数据库 {settings.DATABASE_CONFIG['database']} 的 simulation 表中") + + # 发送通知 + NotificationService.send_to_gotify(success_count, fail_count) \ No newline at end of file diff --git a/main.py b/main.py index a83fbdf..2065f1e 100644 --- a/main.py +++ b/main.py @@ -1,21 +1,45 @@ # -*- coding: utf-8 -*- -import os -from managers.simulation_manager import AlphaSimulationManager -from utils.file_utils import load_alpha_list +"""主程序入口""" +import time +from core.simulator import AlphaSimulator +from config.settings import settings +from utils.helpers import retry_on_exception -def main(): - """主程序入口""" - # 待模拟因子列表 - alpha_list = load_alpha_list('alpha.txt') +@retry_on_exception(max_retries=3, delay=5.0) +def main_loop(simulator: AlphaSimulator) -> None: + """主循环""" + if simulator.needs_token_refresh(): + print("Token需要刷新,重新登录...") + simulator.login() + + alpha_list = simulator.load_alpha_list() if not alpha_list: - print("未找到有效的因子表达式,请检查 alpha.txt 文件") + print("暂无待处理的alpha表达式,10分钟后重新检查...") + time.sleep(600) return - # 创建模拟管理器并运行 - manager = AlphaSimulationManager() - results = manager.run_simulation(alpha_list, batch_size=3) + print(f"共加载 {len(alpha_list)} 个需要模拟的因子表达式") + success_count, fail_count = simulator.run_batch_simulation(alpha_list) + print(f"本轮处理完成: 成功 {success_count} 个, 失败 {fail_count} 个") + + +def main(): + """主函数""" + simulator = AlphaSimulator() + simulator.initialize() + + try: + while True: + main_loop(simulator) + print(f"等待{settings.CHECK_INTERVAL // 60}分钟后继续检查...") + time.sleep(settings.CHECK_INTERVAL) + except KeyboardInterrupt: + print("\n程序被用户中断") + except Exception as e: + print(f"程序执行异常: {e}") + raise if __name__ == "__main__": diff --git a/managers/__init__.py b/managers/__init__.py deleted file mode 100644 index 992426b..0000000 --- a/managers/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- -""" -管理模块 - 包含各种管理器类 -""" - -from .simulation_manager import AlphaSimulationManager - -__all__ = ['AlphaSimulationManager'] \ No newline at end of file diff --git a/managers/simulation_manager.py b/managers/simulation_manager.py deleted file mode 100644 index 5137df8..0000000 --- a/managers/simulation_manager.py +++ /dev/null @@ -1,232 +0,0 @@ -# -*- coding: utf-8 -*- -import time -import json -import os -from concurrent.futures import ThreadPoolExecutor, as_completed -from random import uniform -from typing import List, Dict, Any - -from core.api_client import WorldQuantBrainSimulate -from core.models import SimulationResult -from utils.time_utils import format_time -from utils.file_utils import save_results_to_file, save_success_alpha - - -class AlphaSimulationManager: - def __init__(self, credentials_file='account.txt'): - self.credentials_file = credentials_file - self.results = [] - - """模拟单个Alpha因子(线程安全)""" - def simulate_single_alpha(self, api: WorldQuantBrainSimulate, expression: str, - settings: Dict[str, Any] = None) -> SimulationResult: - 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": - # 模拟成功的结果 - 直接使用原始metrics数据 - metrics = simulation_result["metrics"] - result = SimulationResult( - expression=expression, - time_consuming=time_consuming, - formatted_time=format_time(time_consuming), - alpha_id=simulation_result["alpha_id"], - status="success", - description="/", - simulation_timestamp=time.strftime("%Y-%m-%d %H:%M:%S"), - metrics=metrics # 直接存储原始metrics数据 - ) - print(f"✓ 因子模拟成功: {expression}") - print(f" 耗时: {format_time(time_consuming)},Alpha ID: {simulation_result['alpha_id']}") - - # 打印关键指标 - self._print_success_metrics(metrics) - - else: - # 模拟失败的结果(API返回的错误) - result = SimulationResult( - expression=expression, - time_consuming=time_consuming, - formatted_time=format_time(time_consuming), - alpha_id="/", - status="error", - description=simulation_result["message"], - simulation_timestamp=time.strftime("%Y-%m-%d %H:%M:%S"), - metrics=None - ) - print(f"✗ 因子模拟失败: {expression}") - print(f" 耗时: {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 = SimulationResult( - expression=expression, - time_consuming=time_consuming, - formatted_time=format_time(time_consuming), - alpha_id="/", - status="failed", - description=str(e), - simulation_timestamp=time.strftime("%Y-%m-%d %H:%M:%S"), - metrics=None - ) - print(f"✗ 因子模拟异常: {expression}") - print(f" 耗时: {format_time(time_consuming)},异常: {str(e)}") - - return result - - """打印成功因子的关键指标""" - def _print_success_metrics(self, metrics: Dict[str, Any]): - # 添加空值检查 - if not metrics: - print(" 无指标数据") - return - - print(" 关键指标 (训练集):") - # 从原始metrics数据中提取训练集指标 - train_data = metrics.get('train', {}) or {} - key_metrics = [ - ('夏普比率', train_data.get('sharpe')), - ('年化收益', train_data.get('returns')), - ('最大回撤', train_data.get('drawdown')), - ('换手率', train_data.get('turnover')), - ('适应度', train_data.get('fitness')), - ('PNL', train_data.get('pnl')), - ] - - for chinese_name, value in key_metrics: - if value is not None: - if isinstance(value, float): - value = f"{value:.4f}" - print(f" {chinese_name}: {value}") # 衡量风险调整后的收益 - # 年化收益率 - # 显示样本外测试的夏普比率(如果存在) # 最大亏损幅度 - test_data = metrics.get('test', {}) or {} # 交易频率 - if test_data.get('sharpe') is not None: # 策略适应度得分 - print(f" 样本外夏普比率: {test_data.get('sharpe'):.4f}") # 净盈亏 - - """模拟一批Alpha因子(3个一组)""" - def simulate_alpha_batch(self, alpha_batch: List[str], batch_number: int) -> List[SimulationResult]: - 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"本批总耗时: {format_time(batch_total_time)}") - print(f"{'=' * 60}") - - return batch_results - - """运行批量模拟""" - def run_simulation(self, alpha_list: List[str], batch_size: int = 3) -> List[SimulationResult]: - 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) - - # 保存结果到文件 - save_results_to_file(all_results) - - return all_results - - """打印结果汇总""" - def print_summary(self, results: List[SimulationResult], total_time: float): - 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"总耗时: {format_time(total_time)}") - print(f"{'=' * 60}") - - success_expression_list = [] - - for i, result in enumerate(results, 1): - status_icon = "✓" if result.status == 'success' else "✗" - - if result.status == 'success': - success_expression_list.append(result.expression) - - line_parts = [ - f"{i}. {status_icon} {result.expression}", - f"状态: {result.status}", - f"耗时: {result.formatted_time}" - ] - - if result.alpha_id != '/': - line_parts.append(f"Alpha ID: {result.alpha_id}") - - if result.status != 'success': - line_parts.append(f"原因: {result.description}") - - print("\t".join(line_parts)) - - save_success_alpha(success_expression_list) \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/entities.py b/models/entities.py new file mode 100644 index 0000000..2e6fea0 --- /dev/null +++ b/models/entities.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +"""数据实体模型""" + +from dataclasses import dataclass +from datetime import datetime +from typing import Optional + + +@dataclass +class SimulationResult: + """模拟结果实体""" + expression: str + time_consuming: float + status: str # 'ok' or 'err' + timestamp: str + alpha_id: Optional[str] = None + message: Optional[str] = None + + +@dataclass +class AlphaExpression: + """Alpha表达式实体""" + expression: str + unused: bool = True + created_time: Optional[datetime] = None + + +@dataclass +class TokenInfo: + """认证令牌信息""" + token: str + expiry: int \ No newline at end of file diff --git a/reference/yearly-stats.json b/reference/yearly-stats.json deleted file mode 100644 index 6e2a7ff..0000000 --- a/reference/yearly-stats.json +++ /dev/null @@ -1,168 +0,0 @@ -{ - "schema": { - "name": "yearly-stats", - "title": "Yearly Stats", - "properties": [ - { - "name": "year", - "title": "Year", - "type": "year" - }, - { - "name": "pnl", - "title": "PnL", - "type": "amount" - }, - { - "name": "bookSize", - "title": "Book Size", - "type": "amount" - }, - { - "name": "longCount", - "title": "Long Count", - "type": "integer" - }, - { - "name": "shortCount", - "title": "Short Count", - "type": "integer" - }, - { - "name": "turnover", - "title": "Turnover", - "type": "percent" - }, - { - "name": "sharpe", - "title": "Sharpe", - "type": "decimal" - }, - { - "name": "returns", - "title": "Returns", - "type": "percent" - }, - { - "name": "drawdown", - "title": "Drawdown", - "type": "percent" - }, - { - "name": "margin", - "title": "Margin", - "type": "permyriad" - }, - { - "name": "fitness", - "title": "Fitness", - "type": "decimal" - }, - { - "name": "stage", - "title": "Stage", - "type": "string" - } - ] - }, - "records": [ - [ - "2018", - 347052.0, - 20000000, - 1081, - 1083, - 0.3727, - 1.54, - 0.0365, - 0.0156, - 0.000196, - 0.48, - "TRAIN" - ], - [ - "2019", - 190205.0, - 20000000, - 1364, - 1359, - 0.3659, - 0.83, - 0.0189, - 0.0353, - 0.000103, - 0.19, - "TRAIN" - ], - [ - "2020", - 1554201.0, - 20000000, - 1348, - 1340, - 0.3639, - 4.49, - 0.1682, - 0.0145, - 0.000925, - 3.05, - "TRAIN" - ], - [ - "2021", - 584087.0, - 20000000, - 1435, - 1424, - 0.3652, - 1.41, - 0.0579, - 0.0253, - 0.000317, - 0.56, - "TRAIN" - ], - [ - "2022", - 31117.0, - 20000000, - 1441, - 1434, - 0.3446, - 2.08, - 0.0648, - 0.004, - 0.000376, - 0.9, - "TRAIN" - ], - [ - "2022", - 443804.0, - 20000000, - 1415, - 1417, - 0.3623, - 1.19, - 0.0464, - 0.0349, - 0.000256, - 0.43, - "TEST" - ], - [ - "2023", - 68779.0, - 20000000, - 1405, - 1394, - 0.3554, - 6.22, - 0.1323, - 0.0019, - 0.000744, - 3.79, - "TEST" - ] - ] -} \ No newline at end of file diff --git a/reference/指标.json b/reference/指标.json deleted file mode 100644 index c562c7a..0000000 --- a/reference/指标.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "id": "KP0WWZ6l", - "type": "REGULAR", - "author": "YC93384", - "settings": { - "instrumentType": "EQUITY", - "region": "USA", - "universe": "TOP3000", - "delay": 1, - "decay": 0, - "neutralization": "SUBINDUSTRY", - "truncation": 0.08, - "pasteurization": "ON", - "unitHandling": "VERIFY", - "nanHandling": "OFF", - "maxTrade": "OFF", - "language": "FASTEXPR", - "visualization": false, - "startDate": "2018-01-20", - "endDate": "2023-01-20", - "testPeriod": "P1Y" - }, - "regular": { - "code": "rank(ts_sum(vec_avg(nws12_afterhsz_sl), 60)) * 0.7 + rank(-ts_delta(close, 2)) * 0.3", - "description": null, - "operatorCount": 9 - }, - "dateCreated": "2025-11-13T09:22:47-05:00", - "dateSubmitted": null, - "dateModified": "2025-11-13T09:22:47-05:00", - "name": null, - "favorite": false, - "hidden": false, - "color": null, - "category": null, - "tags": [], - "classifications": [], - "grade": "INFERIOR", - "stage": "IS", - "status": "UNSUBMITTED", - "is": { - "pnl": 3219244, - "bookSize": 20000000, - "longCount": 1332, - "shortCount": 1328, - "turnover": 0.3657, - "returns": 0.0651, - "drawdown": 0.0353, - "margin": 0.000356, - "sharpe": 1.93, - "fitness": 0.81, - "startDate": "2018-01-20", - "checks": [ - { - "name": "LOW_SHARPE", - "result": "PASS", - "limit": 1.25, - "value": 1.93 - }, - { - "name": "LOW_FITNESS", - "result": "FAIL", - "limit": 1.0, - "value": 0.81 - }, - { - "name": "LOW_TURNOVER", - "result": "PASS", - "limit": 0.01, - "value": 0.3657 - }, - { - "name": "HIGH_TURNOVER", - "result": "PASS", - "limit": 0.7, - "value": 0.3657 - }, - { - "name": "CONCENTRATED_WEIGHT", - "result": "PASS" - }, - { - "name": "LOW_SUB_UNIVERSE_SHARPE", - "result": "PASS", - "limit": 0.84, - "value": 1.7 - }, - { - "name": "SELF_CORRELATION", - "result": "PENDING" - }, - { - "name": "MATCHES_COMPETITION", - "result": "PASS", - "competitions": [ - { - "id": "challenge", - "name": "Challenge" - } - ] - } - ] - }, - "os": null, - "train": { - "pnl": 2718449, - "bookSize": 20000000, - "longCount": 1311, - "shortCount": 1306, - "turnover": 0.3665, - "returns": 0.0689, - "drawdown": 0.0353, - "margin": 0.000376, - "fitness": 0.92, - "sharpe": 2.13, - "startDate": "2018-01-20" - }, - "test": { - "pnl": 512583, - "bookSize": 20000000, - "longCount": 1415, - "shortCount": 1416, - "turnover": 0.362, - "returns": 0.0509, - "drawdown": 0.0349, - "margin": 0.000281, - "fitness": 0.5, - "sharpe": 1.33, - "startDate": "2022-01-20" - }, - "prod": null, - "competitions": null, - "themes": null, - "pyramids": null, - "pyramidThemes": null, - "team": null -} \ No newline at end of file diff --git a/reference/进度.json b/reference/进度.json deleted file mode 100644 index e2f3cd1..0000000 --- a/reference/进度.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "id": "3q4OCMgw4MFa8k16tdsYLml", - "type": "REGULAR", - "settings": { - "instrumentType": "EQUITY", - "region": "USA", - "universe": "TOP3000", - "delay": 1, - "decay": 0, - "neutralization": "SUBINDUSTRY", - "truncation": 0.08, - "pasteurization": "ON", - "unitHandling": "VERIFY", - "nanHandling": "OFF", - "maxTrade": "OFF", - "language": "FASTEXPR", - "visualization": false - }, - "regular": "rank(ts_sum(vec_avg(nws12_afterhsz_sl), 60)) * 0.7 + rank(-ts_delta(close, 2)) * 0.3", - "status": "COMPLETE", - "alpha": "KP0WWZ6l" -} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..cd81da3 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,714 @@ +aiobotocore @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_b3e4znaej2/croot/aiobotocore_1680004294920/work +aiofiles==24.1.0 +aiohappyeyeballs==2.6.1 +aiohttp==3.11.14 +aioitertools @ file:///tmp/build/80754af9/aioitertools_1607109665762/work +aiosignal @ file:///tmp/build/80754af9/aiosignal_1637843061372/work +aiosqlite @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_13w97vul7u/croot/aiosqlite_1683773912122/work +alabaster @ file:///home/ktietz/src/ci/alabaster_1611921544520/work +altgraph==0.17.4 +anaconda-catalogs @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_737qok84ed/croot/anaconda-catalogs_1685727302903/work +anaconda-client==1.11.3 +anaconda-navigator==2.4.2 +anaconda-project @ file:///Users/ec2-user/ci_py311/anaconda-project_1678395481364/work +annotated-types==0.7.0 +anyio==3.7.1 +appdirs==1.4.4 +applaunchservices @ file:///Users/ec2-user/ci_py311/applaunchservices_1678390181983/work +appnope @ file:///Users/ec2-user/ci_py311/appnope_1678317440516/work +appscript @ file:///Users/ec2-user/ci_py311/appscript_1678390203562/work +argon2-cffi @ file:///opt/conda/conda-bld/argon2-cffi_1645000214183/work +argon2-cffi-bindings @ file:///Users/ec2-user/ci_py311/argon2-cffi-bindings_1678317076583/work +arrow @ file:///Users/ec2-user/ci_py311/arrow_1678325845580/work +art==6.4 +astroid @ file:///Users/ec2-user/ci_py311/astroid_1678323235198/work +astropy @ file:///Users/ec2-user/ci_py311_2/astropy_1679335070862/work +asttokens @ file:///opt/conda/conda-bld/asttokens_1646925590279/work +astunparse==1.6.3 +async-timeout @ file:///Users/ec2-user/ci_py311/async-timeout_1678320112070/work +atomicwrites==1.4.0 +attrs==25.3.0 +Automat @ file:///tmp/build/80754af9/automat_1600298431173/work +autopep8 @ file:///opt/conda/conda-bld/autopep8_1650463822033/work +Babel @ file:///Users/ec2-user/ci_py311/babel_1678318085010/work +backcall @ file:///home/ktietz/src/ci/backcall_1611930011877/work +backoff==2.2.1 +backports.functools-lru-cache @ file:///tmp/build/80754af9/backports.functools_lru_cache_1618170165463/work +backports.tempfile @ file:///home/linux1/recipes/ci/backports.tempfile_1610991236607/work +backports.weakref==1.0.post1 +base58==2.1.1 +bcrypt @ file:///Users/ec2-user/ci_py311/bcrypt_1678325873219/work +beautifulsoup4==4.14.0 +better-proxy==1.1.5 +betterproto2==0.5.1 +bidict==0.23.1 +binaryornot @ file:///tmp/build/80754af9/binaryornot_1617751525010/work +bip39==0.0.2 +bitarray==2.9.2 +black @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_1f1pcodmuo/croot/black_1680737253550/work +bleach @ file:///opt/conda/conda-bld/bleach_1641577558959/work +blinker==1.7.0 +bokeh @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_f0dguf6k4i/croot/bokeh_1684534020835/work +boltons @ file:///Users/ec2-user/ci_py311/boltons_1678396074811/work +boto3 @ file:///Users/ec2-user/ci_py311_2/boto3_1679335351182/work +botocore @ file:///Users/ec2-user/ci_py311/botocore_1678320133200/work +Bottleneck @ file:///Users/ec2-user/ci_py311/bottleneck_1678322312632/work +Brotli==1.1.0 +brotlipy==0.7.0 +bs4==0.0.2 +build==1.2.2.post1 +buildozer==1.5.0 +cairocffi==1.6.1 +CairoSVG==2.7.1 +candlelite==1.0.17 +canoser==0.8.2 +captchatools==1.5.0 +certifi==2025.8.3 +cffi @ file:///Users/ec2-user/ci_py311/cffi_1678315312775/work +chardet @ file:///Users/ec2-user/ci_py311/chardet_1678326102860/work +charset-normalizer @ file:///tmp/build/80754af9/charset-normalizer_1630003229654/work +ci-info==0.3.0 +ckzg==1.0.1 +click @ file:///Users/ec2-user/ci_py311/click_1678318124837/work +cloudpickle @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_c57ujq_pgm/croot/cloudpickle_1683040025620/work +clyent==1.2.2 +colorama @ file:///Users/ec2-user/ci_py311/colorama_1678320227414/work +colorcet @ file:///Users/ec2-user/ci_py311/colorcet_1678380145131/work +comm @ file:///Users/ec2-user/ci_py311/comm_1678317779525/work +conda @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_a9qfrev42r/croot/conda_1685025193092/work +conda-build @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_e5ocr04fqn/croot/conda-build_1685026138121/work +conda-content-trust @ file:///Users/ec2-user/ci_py311/conda-content-trust_1678409283937/work +conda-libmamba-solver @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_b7jucd8kvj/croot/conda-libmamba-solver_1685032330942/work/src +conda-pack @ file:///tmp/build/80754af9/conda-pack_1611163042455/work +conda-package-handling @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_f61sssj1s3/croot/conda-package-handling_1685024797154/work +conda-repo-cli==1.0.41 +conda-token @ file:///Users/paulyim/miniconda3/envs/c3i/conda-bld/conda-token_1662660369760/work +conda-verify==3.4.2 +conda_index @ file:///Users/ec2-user/ci_py311/conda-index_1678409305677/work +conda_package_streaming @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_78ue32m8ay/croot/conda-package-streaming_1685019689310/work +configobj==5.0.8 +configparser==6.0.0 +constantly==15.1.0 +contourpy @ file:///Users/ec2-user/ci_py311/contourpy_1678322361099/work +cookiecutter @ file:///opt/conda/conda-bld/cookiecutter_1649151442564/work +cryptography @ file:///Users/ec2-user/ci_py311_2/cryptography_1679335457662/work +cssselect==1.1.0 +cssselect2==0.7.0 +cycler @ file:///tmp/build/80754af9/cycler_1637851556182/work +Cython==3.0.11 +cytoolz @ file:///Users/ec2-user/ci_py311/cytoolz_1678326214370/work +dask @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_ccdxmmxbox/croot/dask-core_1686782920612/work +dataclasses-json==0.6.7 +DataRecorder==3.6.2 +datashader @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_93l60mzvvb/croot/datashader_1685542975569/work +datashape==0.5.4 +debugpy @ file:///Users/ec2-user/ci_py311/debugpy_1678317801115/work +decorator @ file:///opt/conda/conda-bld/decorator_1643638310831/work +defusedxml @ file:///tmp/build/80754af9/defusedxml_1615228127516/work +Deprecated==1.2.18 +diff-match-patch @ file:///Users/ktietz/demo/mc3/conda-bld/diff-match-patch_1630511840874/work +dill @ file:///Users/ec2-user/ci_py311/dill_1678323290904/work +distlib==0.3.9 +distributed @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_95i88jlqtp/croot/distributed_1686866052419/work +distro==1.9.0 +dnspython==2.4.1 +docstring-to-markdown @ file:///Users/ec2-user/ci_py311/docstring-to-markdown_1678326320721/work +docutils==0.22.1 +docx==0.2.4 +DownloadKit==2.0.7 +DrissionPage==4.1.0.17 +dukpy==0.4.0 +easygui==0.98.3 +email-validator==2.0.0.post2 +entrypoints @ file:///Users/ec2-user/ci_py311/entrypoints_1678316169365/work +epcpy==0.1.7 +et-xmlfile==1.1.0 +etelemetry==0.3.1 +eth-account==0.11.2 +eth-hash==0.7.0 +eth-keyfile==0.8.1 +eth-keys==0.5.1 +eth-rlp==1.0.1 +eth-typing==4.2.1 +eth-utils==4.1.0 +eth_abi==5.1.0 +executing @ file:///opt/conda/conda-bld/executing_1646925071911/work +fake-useragent==1.5.1 +fastapi==0.104.1 +fastjsonschema @ file:///Users/ec2-user/ci_py311_2/python-fastjsonschema_1679338737111/work +filelock==3.16.1 +filetype==1.2.0 +fitz==0.0.1.dev2 +flake8 @ file:///Users/ec2-user/ci_py311/flake8_1678326404511/work +Flask @ file:///Users/ec2-user/ci_py311/flask_1678380788105/work +fonttools==4.25.0 +fqdn==1.5.1 +frozenlist @ file:///Users/ec2-user/ci_py311/frozenlist_1678318685088/work +fsspec @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_d9zxascxg8/croot/fsspec_1679418997424/work +future @ file:///Users/ec2-user/ci_py311_2/future_1679335881731/work +genshinhelper==2.1.3 +gensim @ file:///Users/ec2-user/ci_py311/gensim_1678412014794/work +glob2 @ file:///home/linux1/recipes/ci/glob2_1610991677669/work +gmpy2 @ file:///Users/ec2-user/ci_py311/gmpy2_1678380831980/work +gql==3.5.3 +graphql-core==3.2.6 +greenlet==3.1.1 +grpclib==0.4.8 +h11==0.16.0 +h2==4.1.0 +h5py @ file:///Users/ec2-user/ci_py311/h5py_1678381005308/work +HeapDict @ file:///Users/ktietz/demo/mc3/conda-bld/heapdict_1630598515714/work +hexbytes==0.3.1 +holoviews @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_7a7dicrx1g/croot/holoviews_1686339350598/work +hpack==4.0.0 +httpcore==1.0.9 +httplib2==0.22.0 +httptools==0.6.0 +httpx==0.25.2 +httpx-sse==0.4.0 +hvplot @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_4dben4g_th/croot/hvplot_1685998195310/work +hyperframe==6.0.1 +hyperlink @ file:///tmp/build/80754af9/hyperlink_1610130746837/work +idna @ file:///Users/ec2-user/ci_py311/idna_1678315819133/work +ifaddr==0.2.0 +imagecodecs @ file:///Users/ec2-user/ci_py311_2/imagecodecs_1679336141260/work +imageio @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_47v6nbs3l9/croot/imageio_1687264946242/work +imagesize @ file:///Users/ec2-user/ci_py311/imagesize_1678377399148/work +imap-tools==1.10.0 +imbalanced-learn @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_a1ju_tsc7c/croot/imbalanced-learn_1685020900729/work +importlib-metadata @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_81_20mq0d8/croot/importlib-metadata_1678997090664/work +incremental @ file:///tmp/build/80754af9/incremental_1636629750599/work +inflection==0.5.1 +iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work +intake @ file:///Users/ec2-user/ci_py311_2/intake_1679336302724/work +intervaltree @ file:///Users/ktietz/demo/mc3/conda-bld/intervaltree_1630511889664/work +ipykernel @ file:///Users/ec2-user/ci_py311/ipykernel_1678318220696/work +ipython @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_cd4ia5pr17/croot/ipython_1680701875298/work +ipython-genutils @ file:///tmp/build/80754af9/ipython_genutils_1606773439826/work +ipywidgets @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_f5atknkudc/croot/ipywidgets_1679394812720/work +isodate==0.6.1 +isoduration==20.11.0 +isort @ file:///tmp/build/80754af9/isort_1628603791788/work +itemadapter @ file:///tmp/build/80754af9/itemadapter_1626442940632/work +itemloaders @ file:///opt/conda/conda-bld/itemloaders_1646805235997/work +itsdangerous==2.2.0 +jaraco.classes @ file:///tmp/build/80754af9/jaraco.classes_1620983179379/work +jedi @ file:///Users/ec2-user/ci_py311_2/jedi_1679336327335/work +jellyfish @ file:///Users/ec2-user/ci_py311/jellyfish_1678392216202/work +Jinja2==3.1.6 +jinja2-time @ file:///opt/conda/conda-bld/jinja2-time_1649251842261/work +jiter==0.8.2 +jittor==1.3.8.5 +jmespath @ file:///Users/ktietz/demo/mc3/conda-bld/jmespath_1630583964805/work +joblib @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_dfriz0qjwt/croot/joblib_1685113297703/work +Js2Py_3.13==0.74.1 +json5 @ file:///tmp/build/80754af9/json5_1624432770122/work +jsonalias==0.1.1 +jsonpatch==1.33 +jsonpointer==2.1 +jsonschema==4.23.0 +jsonschema-specifications==2025.4.1 +jtorch==0.1.7 +jupyter @ file:///Users/ec2-user/ci_py311/jupyter_1678377791770/work +jupyter-console @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_e8qcp1kdie/croot/jupyter_console_1679999714867/work +jupyter-events @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_f0yqwpn5u1/croot/jupyter_events_1684268046871/work +jupyter-ydoc @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_79b9hk1z3q/croot/jupyter_ydoc_1683747244664/work +jupyter_client @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_5e4bbpqn9e/croot/jupyter_client_1680171866753/work +jupyter_core @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_b82fz_h369/croot/jupyter_core_1679906581737/work +jupyter_server @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_b7pddqvxoj/croot/jupyter_server_1686059483368/work +jupyter_server_fileid @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_b0lffopjbp/croot/jupyter_server_fileid_1684273621123/work +jupyter_server_terminals @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_257krljdmq/croot/jupyter_server_terminals_1686870730796/work +jupyter_server_ydoc @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_6848h2a_93/croot/jupyter_server_ydoc_1686768718335/work +jupyterlab @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_502x016ek3/croot/jupyterlab_1686179671461/work +jupyterlab-pygments @ file:///tmp/build/80754af9/jupyterlab_pygments_1601490720602/work +jupyterlab-widgets @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_9btffb27id/croot/jupyterlab_widgets_1679055288818/work +jupyterlab_server @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_9039tf6tpv/croot/jupyterlab_server_1680792539169/work +kaitaistruct==0.10 +keyring @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_2ds0bchnl2/croot/keyring_1678999223818/work +Kivy==2.3.0 +Kivy-Garden==0.1.5 +kiwisolver @ file:///Users/ec2-user/ci_py311/kiwisolver_1678322457192/work +langchain==0.3.14 +langchain-community==0.3.14 +langchain-core==0.3.29 +langchain-text-splitters==0.3.4 +langsmith==0.2.10 +lazy-object-proxy @ file:///Users/ec2-user/ci_py311/lazy-object-proxy_1678322504286/work +lazy_loader @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_02apt1h75k/croot/lazy_loader_1687264081288/work +libarchive-c @ file:///tmp/build/80754af9/python-libarchive-c_1617780486945/work +libmambapy @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_65seb320zf/croot/mamba-split_1680093070986/work/libmambapy +linkify-it-py @ file:///Users/ec2-user/ci_py311/linkify-it-py_1678413480728/work +llvmlite @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_39tj5og13d/croot/llvmlite_1683555130919/work +lmdb @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_88zbo0vn66/croot/python-lmdb_1682522360781/work +locket @ file:///Users/ec2-user/ci_py311/locket_1678322541585/work +loguru==0.7.2 +looseversion==1.3.0 +lru-dict==1.2.0 +lxml==6.0.2 +lz4 @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_7fl2lzb5wh/croot/lz4_1686057901919/work +m3u8==6.0.0 +macholib==1.16.3 +Markdown @ file:///Users/ec2-user/ci_py311/markdown_1678377870854/work +markdown-it-py @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_43xjt6hz0x/croot/markdown-it-py_1684279911135/work +markdown2==2.5.4 +MarkupSafe==3.0.3 +marshmallow==3.24.1 +matplotlib @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_41fhwn4tj9/croot/matplotlib-suite_1679593479845/work +matplotlib-inline @ file:///Users/ec2-user/ci_py311/matplotlib-inline_1678317544666/work +mccabe @ file:///opt/conda/conda-bld/mccabe_1644221741721/work +mdit-py-plugins @ file:///Users/ec2-user/ci_py311/mdit-py-plugins_1678417716573/work +mdurl @ file:///Users/ec2-user/ci_py311/mdurl_1678381820326/work +mistune @ file:///Users/ec2-user/ci_py311/mistune_1678317272228/work +mmh3==4.1.0 +mnemonic==0.21 +modulegraph==0.19.6 +more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work +mpmath==1.2.1 +msgpack @ file:///Users/ec2-user/ci_py311/msgpack-python_1678318264150/work +multidict @ file:///Users/ec2-user/ci_py311/multidict_1678318765630/work +multipledispatch @ file:///Users/ec2-user/ci_py311/multipledispatch_1678392901644/work +munkres==1.1.4 +mutf8==1.0.6 +mypy==1.18.2 +mypy_extensions==1.1.0 +names==0.3.0 +navigator-updater==0.4.0 +nbclassic @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_e9be4uk5qu/croot/nbclassic_1681756175065/work +nbclient @ file:///Users/ec2-user/ci_py311/nbclient_1678317300721/work +nbconvert @ file:///Users/ec2-user/ci_py311/nbconvert_1678317567483/work +nbformat @ file:///Users/ec2-user/ci_py311/nbformat_1678317010390/work +nest-asyncio @ file:///Users/ec2-user/ci_py311/nest-asyncio_1678316288195/work +networkx @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_1a72gktq2s/croot/networkx_1678964337061/work +nibabel==5.1.0 +nicegui==2.24.1 +nipype==1.8.6 +nltk @ file:///opt/conda/conda-bld/nltk_1645628263994/work +nodeenv==1.9.1 +notebook @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_be_2kh1kd4/croot/notebook_1681756176866/work +notebook_shim @ file:///Users/ec2-user/ci_py311/notebook-shim_1678318293647/work +numba @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_93pzbiav_e/croot/numba_1684245526578/work +numexpr @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_1b50c1js9s/croot/numexpr_1683227065029/work +numpy==1.26.3 +numpydoc @ file:///Users/ec2-user/ci_py311/numpydoc_1678393034712/work +OdooRPC==0.10.1 +okx==2.1.1 +okx-api==1.0.5 +ollama==0.4.5 +openai==1.59.6 +opencv-python==4.8.1.78 +openpyxl==3.0.10 +orjson==3.10.13 +outcome==1.3.0.post0 +packaging==24.2 +pandas==1.5.3 +pandocfilters @ file:///opt/conda/conda-bld/pandocfilters_1643405455980/work +panel @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_dfq8ysliwf/croot/panel_1686058338704/work +param @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_eb_xu6rgzh/croot/param_1684915322419/work +parsel @ file:///Users/ec2-user/ci_py311/parsel_1678382184303/work +parsimonious==0.10.0 +parso @ file:///opt/conda/conda-bld/parso_1641458642106/work +partd @ file:///opt/conda/conda-bld/partd_1647245470509/work +pathlib @ file:///Users/ktietz/demo/mc3/conda-bld/pathlib_1629713961906/work +pathspec @ file:///Users/ec2-user/ci_py311_2/pathspec_1679337122817/work +patsy==0.5.3 +paux==1.0.14 +pdfminer==20191125 +pdfminer3k==1.3.4 +pdfparanoia==0.0.17 +pendulum==3.0.0 +pep8==1.7.1 +pexpect @ file:///tmp/build/80754af9/pexpect_1605563209008/work +pickleshare @ file:///tmp/build/80754af9/pickleshare_1606932040724/work +pika==1.3.2 +Pillow==9.4.0 +pkginfo @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_d976p03z5u/croot/pkginfo_1679431175529/work +platformdirs==4.3.6 +playwright==1.50.0 +plotly @ file:///Users/ec2-user/ci_py311/plotly_1678382250051/work +pluggy @ file:///Users/ec2-user/ci_py311/pluggy_1678315400971/work +ply==3.11 +pooch @ file:///tmp/build/80754af9/pooch_1623324770023/work +potracer==0.0.4 +poyo @ file:///tmp/build/80754af9/poyo_1617751526755/work +prometheus-client @ file:///Users/ec2-user/ci_py311_2/prometheus_client_1679340232148/work +prompt-toolkit @ file:///Users/ec2-user/ci_py311/prompt-toolkit_1678317707969/work +propcache==0.3.0 +Protego @ file:///tmp/build/80754af9/protego_1598657180827/work +protobuf==5.26.1 +prov==2.0.0 +pscript==0.7.7 +psutil @ file:///Users/ec2-user/ci_py311_2/psutil_1679337242143/work +psycopg2==2.9.9 +ptyprocess @ file:///tmp/build/80754af9/ptyprocess_1609355006118/work/dist/ptyprocess-0.7.0-py2.py3-none-any.whl +pure-eval @ file:///opt/conda/conda-bld/pure_eval_1646925070566/work +py-cpuinfo @ file:///Users/ktietz/demo/mc3/conda-bld/py-cpuinfo_1629480366017/work +py2app==0.28.6 +pyarrow==11.0.0 +pyasn1 @ file:///Users/ktietz/demo/mc3/conda-bld/pyasn1_1629708007385/work +pyasn1-modules==0.2.8 +pycodestyle @ file:///Users/ec2-user/ci_py311/pycodestyle_1678324331308/work +pycosat @ file:///Users/ec2-user/ci_py311/pycosat_1678378899646/work +pycparser @ file:///tmp/build/80754af9/pycparser_1636541352034/work +pycryptodome==3.19.0 +pyct @ file:///Users/ec2-user/ci_py311/pyct_1678378955854/work +pycurl==7.45.2 +pydantic==2.11.9 +pydantic-extra-types==2.0.0 +pydantic-settings==2.7.1 +pydantic_core==2.33.2 +PyDispatcher==2.0.5 +pydocstyle @ file:///Users/ec2-user/ci_py311/pydocstyle_1678378981125/work +pydoll-python==1.3.3 +pydot==1.4.2 +pyee==12.0.0 +pyerfa @ file:///Users/ec2-user/ci_py311/pyerfa_1678379006730/work +pyflakes @ file:///Users/ec2-user/ci_py311/pyflakes_1678324357242/work +Pygments @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_cflayrfqsi/croot/pygments_1684279981084/work +PyICU==2.12 +pyinstaller==6.2.0 +pyinstaller-hooks-contrib==2023.10 +pyjsparser==2.7.1 +PyJWT @ file:///Users/ec2-user/ci_py311/pyjwt_1678379090382/work +pylint @ file:///Users/ec2-user/ci_py311/pylint_1678379112162/work +pylint-venv @ file:///Users/ec2-user/ci_py311/pylint-venv_1678393629931/work +pyls-spyder==0.4.0 +PyMuPDF==1.23.6 +PyMuPDFb==1.23.6 +pynanosvg==0.3.1 +pyobjc==10.3.1 +pyobjc-core==10.3.1 +pyobjc-framework-Accessibility==10.3.1 +pyobjc-framework-Accounts==10.3.1 +pyobjc-framework-AddressBook==10.3.1 +pyobjc-framework-AdServices==10.3.1 +pyobjc-framework-AdSupport==10.3.1 +pyobjc-framework-AppleScriptKit==10.3.1 +pyobjc-framework-AppleScriptObjC==10.3.1 +pyobjc-framework-ApplicationServices==10.3.1 +pyobjc-framework-AppTrackingTransparency==10.3.1 +pyobjc-framework-AudioVideoBridging==10.3.1 +pyobjc-framework-AuthenticationServices==10.3.1 +pyobjc-framework-AutomaticAssessmentConfiguration==10.3.1 +pyobjc-framework-Automator==10.3.1 +pyobjc-framework-AVFoundation==10.3.1 +pyobjc-framework-AVKit==10.3.1 +pyobjc-framework-AVRouting==10.3.1 +pyobjc-framework-BackgroundAssets==10.3.1 +pyobjc-framework-BusinessChat==10.3.1 +pyobjc-framework-CalendarStore==10.3.1 +pyobjc-framework-CallKit==10.3.1 +pyobjc-framework-CFNetwork==10.3.1 +pyobjc-framework-ClassKit==10.3.1 +pyobjc-framework-CloudKit==10.3.1 +pyobjc-framework-Cocoa==10.3.1 +pyobjc-framework-Collaboration==10.3.1 +pyobjc-framework-ColorSync==10.3.1 +pyobjc-framework-Contacts==10.3.1 +pyobjc-framework-ContactsUI==10.3.1 +pyobjc-framework-CoreAudio==10.3.1 +pyobjc-framework-CoreAudioKit==10.3.1 +pyobjc-framework-CoreBluetooth==10.3.1 +pyobjc-framework-CoreData==10.3.1 +pyobjc-framework-CoreHaptics==10.3.1 +pyobjc-framework-CoreLocation==10.3.1 +pyobjc-framework-CoreMedia==10.3.1 +pyobjc-framework-CoreMediaIO==10.3.1 +pyobjc-framework-CoreMIDI==10.3.1 +pyobjc-framework-CoreML==10.3.1 +pyobjc-framework-CoreMotion==10.3.1 +pyobjc-framework-CoreServices==10.3.1 +pyobjc-framework-CoreSpotlight==10.3.1 +pyobjc-framework-CoreText==10.3.1 +pyobjc-framework-CoreWLAN==10.3.1 +pyobjc-framework-CryptoTokenKit==10.3.1 +pyobjc-framework-DataDetection==10.3.1 +pyobjc-framework-DeviceCheck==10.3.1 +pyobjc-framework-DictionaryServices==10.3.1 +pyobjc-framework-DiscRecording==10.3.1 +pyobjc-framework-DiscRecordingUI==10.3.1 +pyobjc-framework-DiskArbitration==10.3.1 +pyobjc-framework-DVDPlayback==10.3.1 +pyobjc-framework-EventKit==10.3.1 +pyobjc-framework-ExceptionHandling==10.3.1 +pyobjc-framework-ExecutionPolicy==10.3.1 +pyobjc-framework-ExtensionKit==10.3.1 +pyobjc-framework-ExternalAccessory==10.3.1 +pyobjc-framework-FileProvider==10.3.1 +pyobjc-framework-FileProviderUI==10.3.1 +pyobjc-framework-FinderSync==10.3.1 +pyobjc-framework-FSEvents==10.3.1 +pyobjc-framework-GameCenter==10.3.1 +pyobjc-framework-GameController==10.3.1 +pyobjc-framework-GameKit==10.3.1 +pyobjc-framework-GameplayKit==10.3.1 +pyobjc-framework-HealthKit==10.3.1 +pyobjc-framework-ImageCaptureCore==10.3.1 +pyobjc-framework-InputMethodKit==10.3.1 +pyobjc-framework-InstallerPlugins==10.3.1 +pyobjc-framework-InstantMessage==10.3.1 +pyobjc-framework-Intents==10.3.1 +pyobjc-framework-IntentsUI==10.3.1 +pyobjc-framework-IOBluetooth==10.3.1 +pyobjc-framework-IOBluetoothUI==10.3.1 +pyobjc-framework-IOSurface==10.3.1 +pyobjc-framework-iTunesLibrary==10.3.1 +pyobjc-framework-KernelManagement==10.3.1 +pyobjc-framework-LatentSemanticMapping==10.3.1 +pyobjc-framework-LaunchServices==10.3.1 +pyobjc-framework-libdispatch==10.3.1 +pyobjc-framework-libxpc==10.3.1 +pyobjc-framework-LinkPresentation==10.3.1 +pyobjc-framework-LocalAuthentication==10.3.1 +pyobjc-framework-LocalAuthenticationEmbeddedUI==10.3.1 +pyobjc-framework-MailKit==10.3.1 +pyobjc-framework-MapKit==10.3.1 +pyobjc-framework-MediaAccessibility==10.3.1 +pyobjc-framework-MediaLibrary==10.3.1 +pyobjc-framework-MediaPlayer==10.3.1 +pyobjc-framework-MediaToolbox==10.3.1 +pyobjc-framework-Metal==10.3.1 +pyobjc-framework-MetalFX==10.3.1 +pyobjc-framework-MetalKit==10.3.1 +pyobjc-framework-MetalPerformanceShaders==10.3.1 +pyobjc-framework-MetalPerformanceShadersGraph==10.3.1 +pyobjc-framework-MetricKit==10.3.1 +pyobjc-framework-MLCompute==10.3.1 +pyobjc-framework-ModelIO==10.3.1 +pyobjc-framework-MultipeerConnectivity==10.3.1 +pyobjc-framework-NaturalLanguage==10.3.1 +pyobjc-framework-NetFS==10.3.1 +pyobjc-framework-Network==10.3.1 +pyobjc-framework-NetworkExtension==10.3.1 +pyobjc-framework-NotificationCenter==10.3.1 +pyobjc-framework-OpenDirectory==10.3.1 +pyobjc-framework-OSAKit==10.3.1 +pyobjc-framework-OSLog==10.3.1 +pyobjc-framework-PassKit==10.3.1 +pyobjc-framework-PencilKit==10.3.1 +pyobjc-framework-PHASE==10.3.1 +pyobjc-framework-Photos==10.3.1 +pyobjc-framework-PhotosUI==10.3.1 +pyobjc-framework-PreferencePanes==10.3.1 +pyobjc-framework-PushKit==10.3.1 +pyobjc-framework-Quartz==10.3.1 +pyobjc-framework-QuickLookThumbnailing==10.3.1 +pyobjc-framework-ReplayKit==10.3.1 +pyobjc-framework-SafariServices==10.3.1 +pyobjc-framework-SafetyKit==10.3.1 +pyobjc-framework-SceneKit==10.3.1 +pyobjc-framework-ScreenCaptureKit==10.3.1 +pyobjc-framework-ScreenSaver==10.3.1 +pyobjc-framework-ScreenTime==10.3.1 +pyobjc-framework-ScriptingBridge==10.3.1 +pyobjc-framework-SearchKit==10.3.1 +pyobjc-framework-Security==10.3.1 +pyobjc-framework-SecurityFoundation==10.3.1 +pyobjc-framework-SecurityInterface==10.3.1 +pyobjc-framework-ServiceManagement==10.3.1 +pyobjc-framework-SharedWithYou==10.3.1 +pyobjc-framework-SharedWithYouCore==10.3.1 +pyobjc-framework-ShazamKit==10.3.1 +pyobjc-framework-Social==10.3.1 +pyobjc-framework-SoundAnalysis==10.3.1 +pyobjc-framework-Speech==10.3.1 +pyobjc-framework-SpriteKit==10.3.1 +pyobjc-framework-StoreKit==10.3.1 +pyobjc-framework-SyncServices==10.3.1 +pyobjc-framework-SystemConfiguration==10.3.1 +pyobjc-framework-SystemExtensions==10.3.1 +pyobjc-framework-ThreadNetwork==10.3.1 +pyobjc-framework-UniformTypeIdentifiers==10.3.1 +pyobjc-framework-UserNotifications==10.3.1 +pyobjc-framework-UserNotificationsUI==10.3.1 +pyobjc-framework-VideoSubscriberAccount==10.3.1 +pyobjc-framework-VideoToolbox==10.3.1 +pyobjc-framework-Virtualization==10.3.1 +pyobjc-framework-Vision==10.3.1 +pyobjc-framework-WebKit==10.3.1 +pyodbc @ file:///Users/ec2-user/ci_py311/pyodbc_1678420730897/work +pyOpenSSL @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_19gfn1ib_u/croot/pyopenssl_1678965300171/work +pyotp==2.9.0 +pyparsing @ file:///Users/ec2-user/ci_py311/pyparsing_1678322828710/work +PyPDF2==1.26.0 +PyPDF4==1.27.0 +pyproject_hooks==1.2.0 +PyQt5-sip==12.11.0 +PyQt6==6.4.2 +pyqt6-plugins==6.4.2.2.3 +PyQt6-Qt6==6.4.3 +PyQt6-sip==13.6.0 +pyqt6-tools==6.4.2.3.3 +PyQt6-WebEngine==6.6.0 +PyQt6-WebEngine-Qt6==6.6.0 +pyright==1.1.405 +pyrsistent @ file:///Users/ec2-user/ci_py311/pyrsistent_1678316053523/work +PySocks @ file:///Users/ec2-user/ci_py311/pysocks_1678315868424/work +pysui==0.85.0 +pysui-fastcrypto==0.7.0 +pytest @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_bcnsxh_kaf/croot/pytest_1684171610545/work +python-barcode==0.15.1 +python-dateutil @ file:///tmp/build/80754af9/python-dateutil_1626374649649/work +python-dotenv==1.0.0 +python-engineio==4.12.2 +python-json-logger @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_49p5x2bjzn/croot/python-json-logger_1683823814668/work +python-lsp-black @ file:///Users/ec2-user/ci_py311/python-lsp-black_1678393907972/work +python-lsp-jsonrpc==1.0.0 +python-lsp-server @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_1fkjjx0yhx/croot/python-lsp-server_1681930400163/work +python-multipart==0.0.6 +python-slugify @ file:///tmp/build/80754af9/python-slugify_1620405669636/work +python-snappy @ file:///Users/ec2-user/ci_py311/python-snappy_1678388765172/work +python-socketio==5.13.0 +python-socks==2.7.1 +pytoolconfig @ file:///Users/ec2-user/ci_py311/pytoolconfig_1678324412360/work +pytz @ file:///Users/ec2-user/ci_py311/pytz_1678318034461/work +pyunormalize==15.1.0 +pyviz-comms @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_13yvjs0iia/croot/pyviz_comms_1685030724461/work +PyWavelets @ file:///Users/ec2-user/ci_py311/pywavelets_1678379192795/work +pyxnat==1.6 +PyYAML==6.0.2 +pyzmq @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_e6wiufh44w/croot/pyzmq_1686601381524/work +QDarkStyle @ file:///tmp/build/80754af9/qdarkstyle_1617386714626/work +qstylizer @ file:///Users/ec2-user/ci_py311/qstylizer_1678393978214/work/dist/qstylizer-0.2.2-py2.py3-none-any.whl +qt6-applications==6.4.3.2.3 +qt6-tools==6.4.3.1.3 +QtAwesome @ file:///Users/ec2-user/ci_py311/qtawesome_1678393953815/work +qtconsole @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_dexq2hi2gu/croot/qtconsole_1681394223590/work +QtPy @ file:///Users/ec2-user/ci_py311/qtpy_1678322936834/work +queuelib==1.5.0 +RandomWords==0.4.0 +rdflib==7.0.0 +redis==5.2.0 +referencing==0.36.2 +regex @ file:///Users/ec2-user/ci_py311/regex_1678394018661/work +reportlab==4.0.6 +requests==2.32.3 +requests-file @ file:///Users/ktietz/demo/mc3/conda-bld/requests-file_1629455781986/work +requests-toolbelt==1.0.0 +rfc3339-validator @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_07a1ken4gz/croot/rfc3339-validator_1683077050666/work +rfc3986-validator @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_6dzvsqx89i/croot/rfc3986-validator_1683059150431/work +rich==13.5.2 +rlp==4.0.1 +rope @ file:///Users/ec2-user/ci_py311/rope_1678379265159/work +rpds-py==0.25.1 +rstream==0.30.0 +Rtree @ file:///Users/ec2-user/ci_py311/rtree_1678394103092/work +ruamel-yaml-conda @ file:///Users/ec2-user/ci_py311/ruamel_yaml_1678394125333/work +ruamel.yaml @ file:///Users/ec2-user/ci_py311/ruamel.yaml_1678379291841/work +rumps==0.4.0 +s3fs @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_3dy0qlp99f/croot/s3fs_1680018510125/work +s3transfer @ file:///Users/ec2-user/ci_py311/s3transfer_1678324574832/work +sacremoses @ file:///tmp/build/80754af9/sacremoses_1633107328213/work +scapy==2.5.0 +scikit-image @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_3fh5dyitqb/croot/scikit-image_1682530834592/work +scikit-learn @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_506rr4lm7n/croot/scikit-learn_1684954727383/work +scipy==1.10.1 +Scrapy @ file:///Users/ec2-user/ci_py311/scrapy_1678422467931/work +seaborn @ file:///Users/ec2-user/ci_py311/seaborn_1678394152964/work +selenium==4.18.1 +selenium-wire==5.1.0 +Send2Trash @ file:///tmp/build/80754af9/send2trash_1632406701022/work +service-identity @ file:///Users/ktietz/demo/mc3/conda-bld/service_identity_1629460757137/work +sh==1.14.3 +simple-websocket==1.1.0 +simplejson==3.19.2 +sip @ file:///Users/ec2-user/ci_py311/sip_1678318421743/work +six @ file:///tmp/build/80754af9/six_1644875935023/work +smart-open @ file:///Users/ec2-user/ci_py311/smart_open_1678389689993/work +sniffio==1.3.0 +snowballstemmer @ file:///tmp/build/80754af9/snowballstemmer_1637937080595/work +socksio==1.0.0 +solders==0.25.0 +sortedcontainers @ file:///tmp/build/80754af9/sortedcontainers_1623949099177/work +soupsieve==2.8 +Sphinx @ file:///Users/ec2-user/ci_py311/sphinx_1678389735529/work +sphinxcontrib-applehelp @ file:///home/ktietz/src/ci/sphinxcontrib-applehelp_1611920841464/work +sphinxcontrib-devhelp @ file:///home/ktietz/src/ci/sphinxcontrib-devhelp_1611920923094/work +sphinxcontrib-htmlhelp @ file:///tmp/build/80754af9/sphinxcontrib-htmlhelp_1623945626792/work +sphinxcontrib-jsmath @ file:///home/ktietz/src/ci/sphinxcontrib-jsmath_1611920942228/work +sphinxcontrib-qthelp @ file:///home/ktietz/src/ci/sphinxcontrib-qthelp_1611921055322/work +sphinxcontrib-serializinghtml @ file:///tmp/build/80754af9/sphinxcontrib-serializinghtml_1624451540180/work +spyder @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_b93e0at0ak/croot/spyder_1681934083239/work +spyder-kernels @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_30ws3nleek/croot/spyder-kernels_1681307303955/work +SQLAlchemy @ file:///Users/ec2-user/ci_py311/sqlalchemy_1678379321154/work +stack-data @ file:///opt/conda/conda-bld/stack_data_1646927590127/work +starlette==0.27.0 +statsmodels @ file:///Users/ec2-user/ci_py311/statsmodels_1678394299954/work +supervisor==4.2.5 +svgwrite==1.4.3 +sympy @ file:///Users/ec2-user/ci_py311_2/sympy_1679338337660/work +tables @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_ccbzs9wm7e/croot/pytables_1685123223946/work +tabulate @ file:///Users/ec2-user/ci_py311/tabulate_1678423547338/work +TBB==0.2 +tblib @ file:///Users/ktietz/demo/mc3/conda-bld/tblib_1629402031467/work +tenacity==8.2.3 +termcolor==2.5.0 +terminado @ file:///Users/ec2-user/ci_py311/terminado_1678317735543/work +text-unidecode @ file:///Users/ktietz/demo/mc3/conda-bld/text-unidecode_1629401354553/work +textdistance @ file:///tmp/build/80754af9/textdistance_1612461398012/work +threadpoolctl @ file:///Users/ktietz/demo/mc3/conda-bld/threadpoolctl_1629802263681/work +three-merge @ file:///tmp/build/80754af9/three-merge_1607553261110/work +tifffile @ file:///tmp/build/80754af9/tifffile_1627275862826/work +time-machine==2.16.0 +tinycss2 @ file:///Users/ec2-user/ci_py311/tinycss2_1678317366076/work +tinydb==4.8.2 +tldextract==5.1.3 +toml @ file:///tmp/build/80754af9/toml_1616166611790/work +tomli @ file:///Users/ec2-user/ci_py311/tomli_1678315243316/work +tomlkit @ file:///Users/ec2-user/ci_py311/tomlkit_1678317388739/work +toolz @ file:///Users/ec2-user/ci_py311/toolz_1678323009669/work +torch==2.0.0 +torchvision==0.15 +tornado @ file:///Users/ec2-user/ci_py311/tornado_1678316833426/work +tqdm==4.67.1 +traitlets @ file:///Users/ec2-user/ci_py311/traitlets_1678316097905/work +traits==6.3.2 +transformers @ file:///tmp/build/80754af9/transformers_1633098115425/work +trio==0.24.0 +trio-websocket==0.11.1 +tushare==1.2.89 +twikit==2.3.3 +Twisted @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_3cf4rqmb9n/croot/twisted_1683796897214/work +typing-inspect==0.9.0 +typing-inspection==0.4.1 +typing-utils==0.1.0 +typing_extensions==4.12.2 +tzdata==2024.2 +tzlocal==5.3.1 +uc-micro-py @ file:///Users/ec2-user/ci_py311/uc-micro-py_1678394873457/work +ujson @ file:///Users/ec2-user/ci_py311/ujson_1678325349324/work +Unidecode @ file:///tmp/build/80754af9/unidecode_1614712377438/work +uri-template==1.3.0 +urllib3==1.26.18 +uvicorn==0.24.0 +uvloop==0.17.0 +vbuild==0.8.2 +virtualenv==20.27.0 +w3lib @ file:///Users/ktietz/demo/mc3/conda-bld/w3lib_1629359764703/work +watchdog @ file:///Users/ec2-user/ci_py311/watchdog_1678395046770/work +watchfiles==0.19.0 +wcwidth @ file:///Users/ktietz/demo/mc3/conda-bld/wcwidth_1629357192024/work +web3==6.17.2 +webcolors==24.11.1 +webencodings==0.5.1 +websocket-client==0.57.0 +websockets==12.0 +webvtt-py==0.5.1 +Werkzeug @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_fegt3z60s3/croot/werkzeug_1679489762631/work +whatthepatch @ file:///Users/ec2-user/ci_py311/whatthepatch_1678379478749/work +widgetsnbextension @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_8feupjw2ld/croot/widgetsnbextension_1679313870305/work +wrapt @ file:///Users/ec2-user/ci_py311/wrapt_1678323111850/work +wsproto==1.2.0 +wurlitzer @ file:///Users/ec2-user/ci_py311/wurlitzer_1678390004531/work +xarray @ file:///Users/ec2-user/ci_py311/xarray_1678395099624/work +XlsxWriter==3.1.2 +xlwings @ file:///Users/ec2-user/ci_py311_2/xlwings_1679338568565/work +xyzservices @ file:///Users/ec2-user/ci_py311/xyzservices_1678325391212/work +y-py @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_7dvnlnkj12/croot/y-py_1683555629284/work +yapf @ file:///tmp/build/80754af9/yapf_1615749224965/work +yarl==1.18.3 +you-get==0.4.1730 +ypy-websocket @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_75t89iv95n/croot/ypy-websocket_1684171758794/work +zict @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_59schmcson/croot/zict_1682698747213/work +zipp @ file:///Users/ec2-user/ci_py311/zipp_1678318061389/work +zope.interface @ file:///Users/ec2-user/ci_py311/zope.interface_1678379536727/work +zstandard @ file:///Users/ec2-user/ci_py311_2/zstandard_1679338593428/work diff --git a/result/simulation_results-1763109446.json b/result/simulation_results-1763109446.json deleted file mode 100644 index 868c87a..0000000 --- a/result/simulation_results-1763109446.json +++ /dev/null @@ -1,289 +0,0 @@ -[ - { - "expression": "-ts_correlation(open, close, 20)", - "time_consuming": 21.09, - "formatted_time": "21.09秒", - "alpha_id": "/", - "status": "error", - "description": "Attempted to use inaccessible or unknown operator \"ts_correlation\"", - "simulation_timestamp": "2025-11-14 16:35:40", - "metrics": null - }, - { - "expression": "ts_mean(volume / ts_mean(volume, 20), 30)", - "time_consuming": 90.94, - "formatted_time": "1分30.94秒", - "alpha_id": "akZ63dm1", - "status": "success", - "description": "/", - "simulation_timestamp": "2025-11-14 16:36:50", - "metrics": { - "id": "akZ63dm1", - "type": "REGULAR", - "author": "YC93384", - "settings": { - "instrumentType": "EQUITY", - "region": "USA", - "universe": "TOP3000", - "delay": 1, - "decay": 0, - "neutralization": "INDUSTRY", - "truncation": 0.08, - "pasteurization": "ON", - "unitHandling": "VERIFY", - "nanHandling": "OFF", - "maxTrade": "OFF", - "language": "FASTEXPR", - "visualization": false, - "startDate": "2018-01-20", - "endDate": "2023-01-20" - }, - "regular": { - "code": "ts_mean(volume / ts_mean(volume, 20), 30)", - "description": null, - "operatorCount": 3 - }, - "dateCreated": "2025-11-14T03:36:48-05:00", - "dateSubmitted": null, - "dateModified": "2025-11-14T03:36:48-05:00", - "name": null, - "favorite": false, - "hidden": false, - "color": null, - "category": null, - "tags": [], - "classifications": [ - { - "id": "DATA_USAGE:SINGLE_DATA_SET", - "name": "Single Data Set Alpha" - } - ], - "grade": "INFERIOR", - "stage": "IS", - "status": "UNSUBMITTED", - "is": { - "pnl": 752501, - "bookSize": 20000000, - "longCount": 1462, - "shortCount": 1666, - "turnover": 0.1707, - "returns": 0.0152, - "drawdown": 0.1146, - "margin": 0.000178, - "sharpe": 0.25, - "fitness": 0.07, - "startDate": "2018-01-20", - "checks": [ - { - "name": "LOW_SHARPE", - "result": "FAIL", - "limit": 1.25, - "value": 0.25 - }, - { - "name": "LOW_FITNESS", - "result": "FAIL", - "limit": 1.0, - "value": 0.07 - }, - { - "name": "LOW_TURNOVER", - "result": "PASS", - "limit": 0.01, - "value": 0.1707 - }, - { - "name": "HIGH_TURNOVER", - "result": "PASS", - "limit": 0.7, - "value": 0.1707 - }, - { - "name": "CONCENTRATED_WEIGHT", - "result": "PASS" - }, - { - "name": "LOW_SUB_UNIVERSE_SHARPE", - "result": "PASS", - "limit": 0.11, - "value": 0.83 - }, - { - "name": "SELF_CORRELATION", - "result": "PENDING" - }, - { - "name": "MATCHES_COMPETITION", - "result": "PASS", - "competitions": [ - { - "id": "challenge", - "name": "Challenge" - } - ] - } - ] - }, - "os": null, - "train": null, - "test": null, - "prod": null, - "competitions": null, - "themes": null, - "pyramids": null, - "pyramidThemes": null, - "team": null - } - }, - { - "expression": "ts_rank((high - low) / (close - open + 0.001), 15)", - "time_consuming": 116.93, - "formatted_time": "1分56.93秒", - "alpha_id": "VkjVoGGb", - "status": "success", - "description": "/", - "simulation_timestamp": "2025-11-14 16:37:16", - "metrics": { - "id": "VkjVoGGb", - "type": "REGULAR", - "author": "YC93384", - "settings": { - "instrumentType": "EQUITY", - "region": "USA", - "universe": "TOP3000", - "delay": 1, - "decay": 0, - "neutralization": "INDUSTRY", - "truncation": 0.08, - "pasteurization": "ON", - "unitHandling": "VERIFY", - "nanHandling": "OFF", - "maxTrade": "OFF", - "language": "FASTEXPR", - "visualization": false, - "startDate": "2018-01-20", - "endDate": "2023-01-20" - }, - "regular": { - "code": "ts_rank((high - low) / (close - open + 0.001), 15)", - "description": null, - "operatorCount": 5 - }, - "dateCreated": "2025-11-14T03:37:13-05:00", - "dateSubmitted": null, - "dateModified": "2025-11-14T03:37:14-05:00", - "name": null, - "favorite": false, - "hidden": false, - "color": null, - "category": null, - "tags": [], - "classifications": [ - { - "id": "DATA_USAGE:SINGLE_DATA_SET", - "name": "Single Data Set Alpha" - } - ], - "grade": "INFERIOR", - "stage": "IS", - "status": "UNSUBMITTED", - "is": { - "pnl": -1933631, - "bookSize": 20000000, - "longCount": 1562, - "shortCount": 1565, - "turnover": 1.3813, - "returns": -0.0391, - "drawdown": 0.2222, - "margin": -5.7e-05, - "sharpe": -1.15, - "fitness": -0.19, - "startDate": "2018-01-20", - "checks": [ - { - "name": "LOW_SHARPE", - "result": "FAIL", - "limit": 1.25, - "value": -1.15 - }, - { - "name": "LOW_FITNESS", - "result": "FAIL", - "limit": 1.0, - "value": -0.19 - }, - { - "name": "LOW_TURNOVER", - "result": "PASS", - "limit": 0.01, - "value": 1.3813 - }, - { - "name": "HIGH_TURNOVER", - "result": "FAIL", - "limit": 0.7, - "value": 1.3813 - }, - { - "name": "CONCENTRATED_WEIGHT", - "result": "PASS" - }, - { - "name": "LOW_SUB_UNIVERSE_SHARPE", - "result": "FAIL", - "limit": -0.5, - "value": -0.73 - }, - { - "name": "UNITS", - "result": "WARNING", - "message": "Incompatible unit for input of \"add\" at index 1, expected \"Unit[CSPrice:1]\", found \"Unit[]\"" - }, - { - "name": "SELF_CORRELATION", - "result": "PENDING" - }, - { - "name": "MATCHES_COMPETITION", - "result": "PASS", - "competitions": [ - { - "id": "challenge", - "name": "Challenge" - } - ] - } - ] - }, - "os": null, - "train": null, - "test": null, - "prod": null, - "competitions": null, - "themes": null, - "pyramids": null, - "pyramidThemes": null, - "team": null - } - }, - { - "expression": "-ts_rank(ts_skewness(returns(close), 10), 25)", - "time_consuming": 1.44, - "formatted_time": "1.44秒", - "alpha_id": "/", - "status": "error", - "description": "Attempted to use inaccessible or unknown operator \"ts_skewness\"", - "simulation_timestamp": "2025-11-14 16:37:22", - "metrics": null - }, - { - "expression": "(close - ts_min(low, 50)) / (ts_max(high, 50) - ts_min(low, 50) + 0.001)", - "time_consuming": 5.76, - "formatted_time": "5.76秒", - "alpha_id": "/", - "status": "error", - "description": "Attempted to use inaccessible or unknown operator \"ts_min\"", - "simulation_timestamp": "2025-11-14 16:37:26", - "metrics": null - } -] \ No newline at end of file diff --git a/result/simulation_results-1763110929.json b/result/simulation_results-1763110929.json deleted file mode 100644 index 24a94b1..0000000 --- a/result/simulation_results-1763110929.json +++ /dev/null @@ -1,524 +0,0 @@ -[ - { - "expression": "ts_rank(close - vwap, 12)", - "time_consuming": 15.79, - "formatted_time": "15.79秒", - "alpha_id": "/", - "status": "failed", - "description": "The read operation timed out", - "simulation_timestamp": "2025-11-14 16:58:48", - "metrics": null - }, - { - "expression": "ts_mean((close - open) / (high - low + 0.001), 20)", - "time_consuming": 109.71, - "formatted_time": "1分49.71秒", - "alpha_id": "VkjVwxrG", - "status": "success", - "description": "/", - "simulation_timestamp": "2025-11-14 17:00:22", - "metrics": { - "id": "VkjVwxrG", - "type": "REGULAR", - "author": "YC93384", - "settings": { - "instrumentType": "EQUITY", - "region": "USA", - "universe": "TOP3000", - "delay": 1, - "decay": 0, - "neutralization": "INDUSTRY", - "truncation": 0.08, - "pasteurization": "ON", - "unitHandling": "VERIFY", - "nanHandling": "OFF", - "maxTrade": "OFF", - "language": "FASTEXPR", - "visualization": false, - "startDate": "2018-01-20", - "endDate": "2023-01-20" - }, - "regular": { - "code": "ts_mean((close - open) / (high - low + 0.001), 20)", - "description": null, - "operatorCount": 5 - }, - "dateCreated": "2025-11-14T04:00:17-05:00", - "dateSubmitted": null, - "dateModified": "2025-11-14T04:00:18-05:00", - "name": null, - "favorite": false, - "hidden": false, - "color": null, - "category": null, - "tags": [], - "classifications": [ - { - "id": "DATA_USAGE:SINGLE_DATA_SET", - "name": "Single Data Set Alpha" - } - ], - "grade": "INFERIOR", - "stage": "IS", - "status": "UNSUBMITTED", - "is": { - "pnl": -4252305, - "bookSize": 20000000, - "longCount": 1570, - "shortCount": 1558, - "turnover": 0.3075, - "returns": -0.0859, - "drawdown": 0.5096, - "margin": -0.000559, - "sharpe": -0.83, - "fitness": -0.44, - "startDate": "2018-01-20", - "checks": [ - { - "name": "LOW_SHARPE", - "result": "FAIL", - "limit": 1.25, - "value": -0.83 - }, - { - "name": "LOW_FITNESS", - "result": "FAIL", - "limit": 1.0, - "value": -0.44 - }, - { - "name": "LOW_TURNOVER", - "result": "PASS", - "limit": 0.01, - "value": 0.3075 - }, - { - "name": "HIGH_TURNOVER", - "result": "PASS", - "limit": 0.7, - "value": 0.3075 - }, - { - "name": "CONCENTRATED_WEIGHT", - "result": "PASS" - }, - { - "name": "LOW_SUB_UNIVERSE_SHARPE", - "result": "FAIL", - "limit": -0.36, - "value": -0.7 - }, - { - "name": "UNITS", - "result": "WARNING", - "message": "Incompatible unit for input of \"add\" at index 1, expected \"Unit[CSPrice:1]\", found \"Unit[]\"" - }, - { - "name": "SELF_CORRELATION", - "result": "PENDING" - }, - { - "name": "MATCHES_COMPETITION", - "result": "PASS", - "competitions": [ - { - "id": "challenge", - "name": "Challenge" - } - ] - } - ] - }, - "os": null, - "train": null, - "test": null, - "prod": null, - "competitions": null, - "themes": null, - "pyramids": null, - "pyramidThemes": null, - "team": null - } - }, - { - "expression": "-ts_rank(ts_decay_linear(volume, 10), 30)", - "time_consuming": 124.43, - "formatted_time": "2分4.43秒", - "alpha_id": "E5W10mem", - "status": "success", - "description": "/", - "simulation_timestamp": "2025-11-14 17:00:37", - "metrics": { - "id": "E5W10mem", - "type": "REGULAR", - "author": "YC93384", - "settings": { - "instrumentType": "EQUITY", - "region": "USA", - "universe": "TOP3000", - "delay": 1, - "decay": 0, - "neutralization": "INDUSTRY", - "truncation": 0.08, - "pasteurization": "ON", - "unitHandling": "VERIFY", - "nanHandling": "OFF", - "maxTrade": "OFF", - "language": "FASTEXPR", - "visualization": false, - "startDate": "2018-01-20", - "endDate": "2023-01-20" - }, - "regular": { - "code": "-ts_rank(ts_decay_linear(volume, 10), 30)", - "description": null, - "operatorCount": 3 - }, - "dateCreated": "2025-11-14T04:00:33-05:00", - "dateSubmitted": null, - "dateModified": "2025-11-14T04:00:34-05:00", - "name": null, - "favorite": false, - "hidden": false, - "color": null, - "category": null, - "tags": [], - "classifications": [ - { - "id": "DATA_USAGE:SINGLE_DATA_SET", - "name": "Single Data Set Alpha" - } - ], - "grade": "INFERIOR", - "stage": "IS", - "status": "UNSUBMITTED", - "is": { - "pnl": -1352690, - "bookSize": 20000000, - "longCount": 1610, - "shortCount": 1517, - "turnover": 0.3383, - "returns": -0.0273, - "drawdown": 0.2176, - "margin": -0.000162, - "sharpe": -0.58, - "fitness": -0.16, - "startDate": "2018-01-20", - "checks": [ - { - "name": "LOW_SHARPE", - "result": "FAIL", - "limit": 1.25, - "value": -0.58 - }, - { - "name": "LOW_FITNESS", - "result": "FAIL", - "limit": 1.0, - "value": -0.16 - }, - { - "name": "LOW_TURNOVER", - "result": "PASS", - "limit": 0.01, - "value": 0.3383 - }, - { - "name": "HIGH_TURNOVER", - "result": "PASS", - "limit": 0.7, - "value": 0.3383 - }, - { - "name": "CONCENTRATED_WEIGHT", - "result": "PASS" - }, - { - "name": "LOW_SUB_UNIVERSE_SHARPE", - "result": "FAIL", - "limit": -0.25, - "value": -0.66 - }, - { - "name": "SELF_CORRELATION", - "result": "PENDING" - }, - { - "name": "MATCHES_COMPETITION", - "result": "PASS", - "competitions": [ - { - "id": "challenge", - "name": "Challenge" - } - ] - } - ] - }, - "os": null, - "train": null, - "test": null, - "prod": null, - "competitions": null, - "themes": null, - "pyramids": null, - "pyramidThemes": null, - "team": null - } - }, - { - "expression": "ts_rank(adv20 - volume, 15) + ts_rank(volume, 15)", - "time_consuming": 85.26, - "formatted_time": "1分25.26秒", - "alpha_id": "0m3qkZN1", - "status": "success", - "description": "/", - "simulation_timestamp": "2025-11-14 17:02:08", - "metrics": { - "id": "0m3qkZN1", - "type": "REGULAR", - "author": "YC93384", - "settings": { - "instrumentType": "EQUITY", - "region": "USA", - "universe": "TOP3000", - "delay": 1, - "decay": 0, - "neutralization": "INDUSTRY", - "truncation": 0.08, - "pasteurization": "ON", - "unitHandling": "VERIFY", - "nanHandling": "OFF", - "maxTrade": "OFF", - "language": "FASTEXPR", - "visualization": false, - "startDate": "2018-01-20", - "endDate": "2023-01-20" - }, - "regular": { - "code": "ts_rank(adv20 - volume, 15) + ts_rank(volume, 15)", - "description": null, - "operatorCount": 4 - }, - "dateCreated": "2025-11-14T04:02:06-05:00", - "dateSubmitted": null, - "dateModified": "2025-11-14T04:02:07-05:00", - "name": null, - "favorite": false, - "hidden": false, - "color": null, - "category": null, - "tags": [], - "classifications": [ - { - "id": "DATA_USAGE:SINGLE_DATA_SET", - "name": "Single Data Set Alpha" - } - ], - "grade": "INFERIOR", - "stage": "IS", - "status": "UNSUBMITTED", - "is": { - "pnl": 1607957, - "bookSize": 20000000, - "longCount": 1749, - "shortCount": 1375, - "turnover": 0.8699, - "returns": 0.0325, - "drawdown": 0.0811, - "margin": 7.5e-05, - "sharpe": 0.71, - "fitness": 0.14, - "startDate": "2018-01-20", - "checks": [ - { - "name": "LOW_SHARPE", - "result": "FAIL", - "limit": 1.25, - "value": 0.71 - }, - { - "name": "LOW_FITNESS", - "result": "FAIL", - "limit": 1.0, - "value": 0.14 - }, - { - "name": "LOW_TURNOVER", - "result": "PASS", - "limit": 0.01, - "value": 0.8699 - }, - { - "name": "HIGH_TURNOVER", - "result": "FAIL", - "limit": 0.7, - "value": 0.8699 - }, - { - "name": "CONCENTRATED_WEIGHT", - "result": "PASS" - }, - { - "name": "LOW_SUB_UNIVERSE_SHARPE", - "result": "PASS", - "limit": 0.31, - "value": 0.84 - }, - { - "name": "SELF_CORRELATION", - "result": "PENDING" - }, - { - "name": "MATCHES_COMPETITION", - "result": "PASS", - "competitions": [ - { - "id": "challenge", - "name": "Challenge" - } - ] - } - ] - }, - "os": null, - "train": null, - "test": null, - "prod": null, - "competitions": null, - "themes": null, - "pyramids": null, - "pyramidThemes": null, - "team": null - } - }, - { - "expression": "ts_mean(high - ts_delay(high, 1), 60) - ts_mean(low - ts_delay(low, 1), 60)", - "time_consuming": 86.01, - "formatted_time": "1分26.01秒", - "alpha_id": "wpxqw73d", - "status": "success", - "description": "/", - "simulation_timestamp": "2025-11-14 17:02:09", - "metrics": { - "id": "wpxqw73d", - "type": "REGULAR", - "author": "YC93384", - "settings": { - "instrumentType": "EQUITY", - "region": "USA", - "universe": "TOP3000", - "delay": 1, - "decay": 0, - "neutralization": "INDUSTRY", - "truncation": 0.08, - "pasteurization": "ON", - "unitHandling": "VERIFY", - "nanHandling": "OFF", - "maxTrade": "OFF", - "language": "FASTEXPR", - "visualization": false, - "startDate": "2018-01-20", - "endDate": "2023-01-20" - }, - "regular": { - "code": "ts_mean(high - ts_delay(high, 1), 60) - ts_mean(low - ts_delay(low, 1), 60)", - "description": null, - "operatorCount": 7 - }, - "dateCreated": "2025-11-14T04:02:06-05:00", - "dateSubmitted": null, - "dateModified": "2025-11-14T04:02:06-05:00", - "name": null, - "favorite": false, - "hidden": false, - "color": null, - "category": null, - "tags": [], - "classifications": [ - { - "id": "DATA_USAGE:SINGLE_DATA_SET", - "name": "Single Data Set Alpha" - } - ], - "grade": "INFERIOR", - "stage": "IS", - "status": "UNSUBMITTED", - "is": { - "pnl": -1454336, - "bookSize": 20000000, - "longCount": 1602, - "shortCount": 1526, - "turnover": 1.0897, - "returns": -0.0294, - "drawdown": 0.5598, - "margin": -5.4e-05, - "sharpe": -0.19, - "fitness": -0.03, - "startDate": "2018-01-20", - "checks": [ - { - "name": "LOW_SHARPE", - "result": "FAIL", - "limit": 1.25, - "value": -0.19 - }, - { - "name": "LOW_FITNESS", - "result": "FAIL", - "limit": 1.0, - "value": -0.03 - }, - { - "name": "LOW_TURNOVER", - "result": "PASS", - "limit": 0.01, - "value": 1.0897 - }, - { - "name": "HIGH_TURNOVER", - "result": "FAIL", - "limit": 0.7, - "value": 1.0897 - }, - { - "name": "CONCENTRATED_WEIGHT", - "result": "FAIL", - "date": "2022-10-27", - "limit": 0.1, - "value": 0.298634 - }, - { - "name": "LOW_SUB_UNIVERSE_SHARPE", - "result": "FAIL", - "limit": -0.08, - "value": -0.37 - }, - { - "name": "SELF_CORRELATION", - "result": "PENDING" - }, - { - "name": "MATCHES_COMPETITION", - "result": "PASS", - "competitions": [ - { - "id": "challenge", - "name": "Challenge" - } - ] - } - ] - }, - "os": null, - "train": null, - "test": null, - "prod": null, - "competitions": null, - "themes": null, - "pyramids": null, - "pyramidThemes": null, - "team": null - } - } -] \ No newline at end of file diff --git a/result/simulation_results-1763112962.json b/result/simulation_results-1763112962.json deleted file mode 100644 index 5d0d7d0..0000000 --- a/result/simulation_results-1763112962.json +++ /dev/null @@ -1,82 +0,0 @@ -[ - { - "expression": "ts_mean(close, 20) / ts_mean(close, 100) > 1.05 and ts_std(close / ts_mean(close, 10), 20) / ts_mean(close, 20) < 0.15", - "time_consuming": 5.94, - "formatted_time": "5.94秒", - "alpha_id": "/", - "status": "error", - "description": "Unexpected character 'a' near \"0) > 1.05 and ts_std\"", - "simulation_timestamp": "2025-11-14 17:35:28", - "metrics": null - }, - { - "expression": "ts_min(low, 20) / ts_max(high, 60) > 0.8 and market_cap > ts_mean(market_cap, all)", - "time_consuming": 6.53, - "formatted_time": "6.53秒", - "alpha_id": "/", - "status": "error", - "description": "Unexpected character 'a' near \"60) > 0.8 and market\"", - "simulation_timestamp": "2025-11-14 17:35:29", - "metrics": null - }, - { - "expression": "close > ts_mean(close, 50) and ts_delta(ts_rank(volume, 10), 5) > 0", - "time_consuming": 7.6, - "formatted_time": "7.60秒", - "alpha_id": "/", - "status": "error", - "description": "Unexpected character 'a' near \"lose, 50) and ts_del\"", - "simulation_timestamp": "2025-11-14 17:35:30", - "metrics": null - }, - { - "expression": "ts_corr(close, vwap, 30) < 0.3 and ts_mean(volume, 5) / ts_mean(volume, 60) < 0.8", - "time_consuming": 5.8, - "formatted_time": "5.80秒", - "alpha_id": "/", - "status": "error", - "description": "Unexpected character 'a' near \"30) < 0.3 and ts_mea\"", - "simulation_timestamp": "2025-11-14 17:35:40", - "metrics": null - }, - { - "expression": "abs(close - vwap) / vwap < 0.02 and volume < ts_mean(volume, 20)", - "time_consuming": 6.62, - "formatted_time": "6.62秒", - "alpha_id": "/", - "status": "error", - "description": "Unexpected character 'a' near \"ap < 0.02 and volume\"", - "simulation_timestamp": "2025-11-14 17:35:41", - "metrics": null - }, - { - "expression": "(close - ts_mean(close, 30)) / ts_std(close, 30) > 1.5 and ts_delta(close, 5) < 0", - "time_consuming": 15.51, - "formatted_time": "15.51秒", - "alpha_id": "/", - "status": "error", - "description": "Unexpected character 'a' near \"30) > 1.5 and ts_del\"", - "simulation_timestamp": "2025-11-14 17:35:50", - "metrics": null - }, - { - "expression": "ts_std(log(close / close[1]), 20) > ts_mean(ts_std(log(close / close[1]), 20), 60)", - "time_consuming": 1.2, - "formatted_time": "1.20秒", - "alpha_id": "/", - "status": "error", - "description": "Unexpected character '[' near \"se / close[1]), 20)\"", - "simulation_timestamp": "2025-11-14 17:35:56", - "metrics": null - }, - { - "expression": "ts_range(high - low, 5) / ts_mean(high - low, 60) > 1.5 and ts_delta(ts_std(close, 10), 5) > 0", - "time_consuming": 7.38, - "formatted_time": "7.38秒", - "alpha_id": "/", - "status": "error", - "description": "Unexpected character 'a' near \"60) > 1.5 and ts_del\"", - "simulation_timestamp": "2025-11-14 17:36:02", - "metrics": null - } -] \ No newline at end of file diff --git a/result/simulation_results-1763113286.json b/result/simulation_results-1763113286.json deleted file mode 100644 index 8bf45fb..0000000 --- a/result/simulation_results-1763113286.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "expression": "ts_delta(ts_mean(close, 5), 10) / ts_std(close, 20) * (ts_mean(close, 20) / ts_mean(close, 100) > 1.02)", - "time_consuming": 6.62, - "formatted_time": "6.62秒", - "alpha_id": "/", - "status": "error", - "description": "Attempted to use inaccessible or unknown operator \"ts_std\"", - "simulation_timestamp": "2025-11-14 17:41:26", - "metrics": null - } -] \ No newline at end of file diff --git a/services/__init__.py b/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services/alpha_service.py b/services/alpha_service.py new file mode 100644 index 0000000..0b44529 --- /dev/null +++ b/services/alpha_service.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +"""Alpha表达式服务""" + +import time +import httpx +from typing import List, Dict, Any +from config.settings import settings + + +class AlphaService: + """Alpha表达式服务类""" + + def __init__(self, client: httpx.Client): + self.client = client + + def simulate_alpha(self, expression: str) -> Dict[str, Any]: + """模拟单个Alpha表达式""" + simulation_data = { + 'type': 'REGULAR', + 'settings': settings.SIMULATION_SETTINGS, + 'regular': expression + } + + sim_resp = self.client.post(f'{settings.BRAIN_API_URL}/simulations', json=simulation_data) + print(f"模拟提交状态: {sim_resp.status_code}") + + if 'location' not in sim_resp.headers: + return {"status": "err", "message": "No location header in response"} + + sim_progress_url = sim_resp.headers['location'] + return self._wait_for_simulation_result(sim_progress_url) + + def _wait_for_simulation_result(self, progress_url: str) -> Dict[str, Any]: + """等待模拟结果""" + while True: + sim_progress_resp = self.client.get(progress_url) + retry_after_sec = float(sim_progress_resp.headers.get("Retry-After", 0)) + + if retry_after_sec == 0: + break + + if sim_progress_resp.json(): + result = sim_progress_resp.json() + progress = result.get('progress', 0) + print(f"模拟进度: {float(progress) * 100}%") + + print(f"等待 {retry_after_sec} 秒...") + time.sleep(retry_after_sec) + + result_data = sim_progress_resp.json() + + if result_data.get("status") == "ERROR": + error_message = result_data.get("message", "未知错误") + print(f"因子模拟失败: {error_message}") + return {"status": "err", "message": error_message} + + alpha_id = result_data.get("alpha") + print(f"生成的Alpha ID: {alpha_id}") + + return {"status": "ok", "alpha_id": alpha_id} \ No newline at end of file diff --git a/services/auth_service.py b/services/auth_service.py new file mode 100644 index 0000000..c2bcca9 --- /dev/null +++ b/services/auth_service.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +"""认证服务""" + +import os +import httpx +from httpx import BasicAuth +from typing import Tuple, Dict, Any +from config.settings import settings +from models.entities import TokenInfo + + +class AuthService: + """认证服务类""" + + def __init__(self): + self.credentials_file = settings.credentials_file + + def load_credentials(self) -> Tuple[str, str]: + """加载凭证""" + if not os.path.exists(self.credentials_file): + self._create_credentials_file() + + with open(self.credentials_file, 'r', encoding='utf-8') as f: + credentials = eval(f.read()) + return credentials[0], credentials[1] + + def _create_credentials_file(self) -> None: + """创建凭证文件""" + print("未找到 account.txt 文件") + with open(self.credentials_file, 'w', encoding='utf-8') as f: + f.write("") + print("account.txt 文件已创建,请填写账号密码, 格式: ['username', 'password']") + exit(1) + + def login(self, client: httpx.Client) -> TokenInfo: + """登录并获取token""" + username, password = self.load_credentials() + + # 设置 BasicAuth + client.auth = BasicAuth(username, password) + + response = client.post(f'{settings.BRAIN_API_URL}/authentication') + print(f"登录状态: {response.status_code}") + + if response.status_code == 201: + login_data = response.json() + print(f"登录成功!: {login_data}") + return TokenInfo( + token=login_data['token'], + expiry=int(login_data['token']['expiry']) + ) + elif response.status_code == 429: + print("API rate limit exceeded") + exit(1) + else: + print(f"登录失败: {response.json()}") + raise Exception(f"登录失败: {response.json()}") \ No newline at end of file diff --git a/services/notification_service.py b/services/notification_service.py new file mode 100644 index 0000000..3109818 --- /dev/null +++ b/services/notification_service.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +"""通知服务""" + +import httpx +from datetime import datetime +from config.settings import settings + + +class NotificationService: + """通知服务类""" + + @staticmethod + def send_to_gotify(success_count: int, fail_count: int) -> None: + """发送结果到Gotify""" + now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + text = f"总计: 成功 {success_count} 个, 失败 {fail_count} 个\n\n完成时间: {now}" + title = f"alpha模拟结果 时间: {now}" + + try: + resp = httpx.post( + settings.GOTIFY_URL, + json={'title': title, 'message': text}, + timeout=10 + ) + print("通知发送成功") + except Exception as e: + print(f"通知发送失败: {e}") \ No newline at end of file diff --git a/success.txt b/success.txt deleted file mode 100644 index 4fff374..0000000 --- a/success.txt +++ /dev/null @@ -1,4 +0,0 @@ -ts_mean((close - open) / (high - low + 0.001), 20) --ts_rank(ts_decay_linear(volume, 10), 30) -ts_rank(adv20 - volume, 15) + ts_rank(volume, 15) -ts_mean(high - ts_delay(high, 1), 60) - ts_mean(low - ts_delay(low, 1), 60) diff --git a/utils/__init__.py b/utils/__init__.py index 27d207b..e69de29 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- -""" -工具模块 - 包含各种工具函数 -""" - -from .file_utils import load_alpha_list, save_results_to_file -from .time_utils import format_time - -__all__ = ['load_alpha_list', 'save_results_to_file', 'format_time'] \ No newline at end of file diff --git a/utils/file_utils.py b/utils/file_utils.py deleted file mode 100644 index b9fec04..0000000 --- a/utils/file_utils.py +++ /dev/null @@ -1,84 +0,0 @@ -# -*- coding: utf-8 -*- -import os -import json -import time -from typing import List, Any - - -def load_alpha_list(file_path: str) -> List[str]: - """从文件加载Alpha因子列表""" - if not os.path.exists(file_path): - print(f"{file_path} 文件不存在") - with open(file_path, 'w', encoding='utf-8') as file: - file.write("") - print(f"已创建 {file_path} 文件, 请添加因子后重新运行, 一行一个因子") - return [] - - with open(file_path, 'r', encoding='utf-8') as file: - alpha_list = [line.strip() for line in file if line.strip()] - - return alpha_list - - -def save_results_to_file(results: List[Any], result_dir: str = 'result') -> str: - """保存结果到文件""" - # 转换为可序列化的格式 - serializable_results = [] - for result in results: - if hasattr(result, '__dict__'): - # 如果是dataclass对象,转换为字典 - result_dict = result.__dict__.copy() - else: - # 如果是字典 - result_dict = result.copy() - - # 处理时间消耗 - if 'time_consuming' in result_dict: - result_dict['time_consuming'] = round(result_dict['time_consuming'], 2) - - # 只需要确保所有浮点数精度一致 - def round_floats(obj): - if isinstance(obj, float): - return round(obj, 6) - elif isinstance(obj, dict): - return {k: round_floats(v) for k, v in obj.items()} - elif isinstance(obj, list): - return [round_floats(item) for item in obj] - else: - return obj - - # 对结果中的所有浮点数进行精度处理 - result_dict = round_floats(result_dict) - - serializable_results.append(result_dict) - - # 确保结果目录存在 - if not os.path.exists(result_dir): - os.makedirs(result_dir) - - result_name = f"{result_dir}/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}") - return result_name - - -def save_success_alpha(success_expression_list): - """ - 将成功的表达式列表保存到 success.txt 文件中 - 如果文件不存在则创建,如果存在则追加写入 - """ - if not success_expression_list: - print("没有成功的表达式需要保存") - return - - try: - with open('success.txt', 'a', encoding='utf-8') as f: - for expression in success_expression_list: - f.write(expression + '\n') - - print(f"成功保存 {len(success_expression_list)} 个表达式到 success.txt") - - except Exception as e: - print(f"保存文件时出错: {e}") diff --git a/utils/helpers.py b/utils/helpers.py new file mode 100644 index 0000000..14159b0 --- /dev/null +++ b/utils/helpers.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +"""工具函数""" + +import time +from typing import Callable, Any + + +def retry_on_exception(max_retries: int = 3, delay: float = 5.0) -> Callable: + """异常重试装饰器""" + def decorator(func: Callable) -> Callable: + def wrapper(*args, **kwargs) -> Any: + for attempt in range(max_retries): + try: + return func(*args, **kwargs) + except Exception as e: + if attempt == max_retries - 1: + raise e + print(f"尝试 {attempt + 1}/{max_retries} 失败: {e}") + print(f"{delay}秒后重试...") + time.sleep(delay) + return None + return wrapper + return decorator \ No newline at end of file diff --git a/utils/time_utils.py b/utils/time_utils.py deleted file mode 100644 index 37b7bc2..0000000 --- a/utils/time_utils.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- -def format_time(seconds: float) -> str: - """将秒数格式化为 xx分xx秒 格式""" - if seconds < 60: - return f"{seconds:.2f}秒" - else: - minutes = int(seconds // 60) - remaining_seconds = seconds % 60 - return f"{minutes}分{remaining_seconds:.2f}秒" \ No newline at end of file