From 83bac3cf788dc22b818ecbd3916e758cd5034912 Mon Sep 17 00:00:00 2001 From: jack Date: Fri, 14 Nov 2025 18:34:24 +0800 Subject: [PATCH] update --- .gitignore | 4 +- alpha.txt | 6 +- core/api_client.py | 130 ++---- core/models.py | 7 +- main_bak.py | 496 -------------------- managers/simulation_manager.py | 83 ++-- result/simulation_results-1763048082.json | 139 ------ result/simulation_results-1763109446.json | 289 ++++++++++++ result/simulation_results-1763110929.json | 524 ++++++++++++++++++++++ result/simulation_results-1763112962.json | 82 ++++ result/simulation_results-1763113286.json | 12 + success.txt | 4 + utils/file_utils.py | 45 +- 13 files changed, 1041 insertions(+), 780 deletions(-) delete mode 100644 main_bak.py delete mode 100644 result/simulation_results-1763048082.json create mode 100644 result/simulation_results-1763109446.json create mode 100644 result/simulation_results-1763110929.json create mode 100644 result/simulation_results-1763112962.json create mode 100644 result/simulation_results-1763113286.json create mode 100644 success.txt diff --git a/.gitignore b/.gitignore index c9d0dc3..9a4d920 100644 --- a/.gitignore +++ b/.gitignore @@ -64,4 +64,6 @@ other/split_clash_config/split_config ai_news/save_data daily/*.txt -./result \ No newline at end of file +./result +./alpha.txt +./account.txt \ No newline at end of file diff --git a/alpha.txt b/alpha.txt index c16b885..5c6253b 100644 --- a/alpha.txt +++ b/alpha.txt @@ -1,5 +1 @@ -ts_rank(ts_returns(close, 15), 30) --ts_corr(ts_rank(close, 30), ts_rank(volume, 30), 10) -(close - ts_min(close, 20)) / (ts_max(close, 20) - ts_min(close, 20)) -ts_rank(ts_delta(close, 3) / ts_delay(close, 3), 15) --ts_std(ts_returns(close, 1), 30) \ No newline at end of file +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/core/api_client.py b/core/api_client.py index 7d5220f..d8961bc 100644 --- a/core/api_client.py +++ b/core/api_client.py @@ -5,8 +5,6 @@ import time from httpx import BasicAuth from typing import Dict, Any, Optional, Tuple -from .models import AlphaMetrics, TrainMetrics, TestMetrics, AlphaInfo - class WorldQuantBrainSimulate: def __init__(self, credentials_file='account.txt'): @@ -37,6 +35,7 @@ class WorldQuantBrainSimulate: if response.status_code == 201: print("登录成功!") + print(f"账户信息: {response.json()}") return True else: print(f"登录失败: {response.json()}") @@ -83,7 +82,12 @@ class WorldQuantBrainSimulate: if retry_after_sec == 0: break - print(sim_progress_resp.json()) + 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) @@ -103,7 +107,7 @@ class WorldQuantBrainSimulate: return {"status": "success", "alpha_id": alpha_id, "metrics": metrics} """获取Alpha因子的详细指标""" - def get_alpha_metrics(self, alpha_id: str) -> AlphaMetrics: + def get_alpha_metrics(self, alpha_id: str) -> Dict[str, Any]: if self.client is None: raise Exception("请先登录") @@ -114,99 +118,39 @@ class WorldQuantBrainSimulate: if alpha_resp.status_code in [200, 201]: alpha_data = alpha_resp.json() - return self._parse_alpha_metrics(alpha_data) + + # 以后可能需要获取其他参数 + if alpha_data.get('metrics'): + alpha_data = alpha_data.get('metrics') + + return alpha_data or {} else: - return AlphaMetrics( - train_metrics=TrainMetrics(), - is_metrics=TestMetrics(), - test_metrics=TestMetrics(), - alpha_info=AlphaInfo() - ) + 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 AlphaMetrics( - train_metrics=TrainMetrics(), - is_metrics=TestMetrics(), - test_metrics=TestMetrics(), - alpha_info=AlphaInfo() - ) - - """解析Alpha数据,提取关键指标""" - def _parse_alpha_metrics(self, alpha_data: Dict[str, Any]) -> AlphaMetrics: - # 解析训练集数据 - train_metrics = TrainMetrics() - if 'train' in alpha_data and alpha_data['train']: - train_data = alpha_data['train'] - train_metrics = TrainMetrics( - sharpe_ratio=train_data.get('sharpe'), - annual_return=train_data.get('returns'), - max_drawdown=train_data.get('drawdown'), - turnover=train_data.get('turnover'), - fitness=train_data.get('fitness'), - pnl=train_data.get('pnl'), - book_size=train_data.get('bookSize'), - long_count=train_data.get('longCount'), - short_count=train_data.get('shortCount'), - margin=train_data.get('margin'), - ) - - # 解析样本内测试数据 - is_metrics = TestMetrics() - if 'is' in alpha_data and alpha_data['is']: - is_data = alpha_data['is'] - is_metrics = TestMetrics( - sharpe_ratio=is_data.get('sharpe'), - annual_return=is_data.get('returns'), - max_drawdown=is_data.get('drawdown'), - turnover=is_data.get('turnover'), - fitness=is_data.get('fitness'), - pnl=is_data.get('pnl'), - ) - - # 解析样本外测试数据 - test_metrics = TestMetrics() - if 'test' in alpha_data and alpha_data['test']: - test_data = alpha_data['test'] - test_metrics = TestMetrics( - sharpe_ratio=test_data.get('sharpe'), - annual_return=test_data.get('returns'), - max_drawdown=test_data.get('drawdown'), - turnover=test_data.get('turnover'), - fitness=test_data.get('fitness'), - pnl=test_data.get('pnl'), - ) - - # 解析Alpha基本信息 - alpha_info = AlphaInfo( - grade=alpha_data.get('grade'), - stage=alpha_data.get('stage'), - status=alpha_data.get('status'), - date_created=alpha_data.get('dateCreated'), - ) - - # 解析检查结果 - if 'is' in alpha_data and 'checks' in alpha_data['is']: - checks = alpha_data['is']['checks'] - check_results = {} - for check in checks: - check_name = check.get('name', '') - result = check.get('result', '') - value = check.get('value', None) - check_results[check_name.lower()] = { - 'result': result, - 'value': value, - 'limit': check.get('limit', None) - } - alpha_info.checks = check_results - - return AlphaMetrics( - train_metrics=train_metrics, - is_metrics=is_metrics, - test_metrics=test_metrics, - alpha_info=alpha_info, - alpha_id=alpha_data.get('id') - ) + # 返回一个空的字典结构 + return { + "train": {}, + "is": {}, + "test": {}, + "grade": None, + "stage": None, + "status": None, + "dateCreated": None, + "id": alpha_id + } def close(self): """关闭连接""" diff --git a/core/models.py b/core/models.py index 664b20c..5872fd7 100644 --- a/core/models.py +++ b/core/models.py @@ -56,10 +56,7 @@ class SimulationResult: time_consuming: float formatted_time: str alpha_id: str - status: str # success, error, failed + status: str description: str simulation_timestamp: str - train_metrics: Optional[TrainMetrics] = None - is_metrics: Optional[TestMetrics] = None - test_metrics: Optional[TestMetrics] = None - alpha_info: Optional[AlphaInfo] = None \ No newline at end of file + metrics: Optional[Dict[str, Any]] = None \ No newline at end of file diff --git a/main_bak.py b/main_bak.py deleted file mode 100644 index 854f4a6..0000000 --- a/main_bak.py +++ /dev/null @@ -1,496 +0,0 @@ -import os.path -import httpx -import json -from httpx import BasicAuth -import time -from random import uniform -import threading -from concurrent.futures import ThreadPoolExecutor, as_completed - - -class WorldQuantBrainSimulate: - def __init__(self, credentials_file='account.txt'): - self.credentials_file = credentials_file - self.client = None - self.brain_api_url = 'https://api.worldquantbrain.com' - - """读取本地账号密码""" - def load_credentials(self): - if not os.path.exists(self.credentials_file): - print("未找到 account.txt 文件") - with open(self.credentials_file, 'w') as f: f.write("") - print("account.txt 文件已创建,请填写账号密码, 格式: ['username', 'password]") - exit(1) - - with open(self.credentials_file) as f: - credentials = eval(f.read()) - return credentials[0], credentials[1] - - """登录认证""" - def login(self): - username, password = self.load_credentials() - self.client = httpx.Client(auth=BasicAuth(username, password)) - - response = self.client.post(f'{self.brain_api_url}/authentication') - print(f"登录状态: {response.status_code}") - - if response.status_code == 201: - print("登录成功!") - return True - else: - print(f"登录失败: {response.json()}") - return False - - """模拟Alpha因子""" - def simulate_alpha(self, expression, settings=None): - if self.client is None: - raise Exception("请先登录") - - default_settings = { - 'instrumentType': 'EQUITY', - 'region': 'USA', - 'universe': 'TOP3000', - 'delay': 1, - 'decay': 0, - 'neutralization': 'INDUSTRY', - 'truncation': 0.08, - 'pasteurization': 'ON', - 'unitHandling': 'VERIFY', - 'nanHandling': 'OFF', - 'language': 'FASTEXPR', - 'visualization': False, - } - - if settings: - default_settings.update(settings) - - simulation_data = { - 'type': 'REGULAR', - 'settings': default_settings, - 'regular': expression - } - - sim_resp = self.client.post(f'{self.brain_api_url}/simulations', json=simulation_data) - print(f"模拟提交状态: {sim_resp.status_code}") - - sim_progress_url = sim_resp.headers['location'] - print(f"进度URL: {sim_progress_url}") - - while True: - sim_progress_resp = self.client.get(sim_progress_url) - retry_after_sec = float(sim_progress_resp.headers.get("Retry-After", 0)) - - if retry_after_sec == 0: - break - print(sim_progress_resp.json()) - print(f"等待 {retry_after_sec} 秒...") - time.sleep(retry_after_sec) - - # 如果因子模拟不通过, 获取一下失败信息 - if sim_progress_resp.json()["status"] == "ERROR": - result = sim_progress_resp.json()["message"] - print(f"因子模拟失败: {result}") - # 返回一个特殊标识,表示模拟失败 - return {"status": "error", "message": result} - - 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): - 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() - return self._parse_alpha_metrics(alpha_data) - else: - return {"error": f"无法获取Alpha信息: {alpha_resp.status_code}"} - - except Exception as e: - return {"error": f"获取指标时出错: {str(e)}"} - - """解析Alpha数据,提取关键指标""" - def _parse_alpha_metrics(self, alpha_data): - metrics = {} - - try: - # 从train字段获取指标数据 - if 'train' in alpha_data and alpha_data['train']: - train_data = alpha_data['train'] - metrics.update({ - 'sharpe_ratio': train_data.get('sharpe', None), - 'annual_return': train_data.get('returns', None), - 'max_drawdown': train_data.get('drawdown', None), - 'turnover': train_data.get('turnover', None), - 'fitness': train_data.get('fitness', None), - 'pnl': train_data.get('pnl', None), - 'book_size': train_data.get('bookSize', None), - 'long_count': train_data.get('longCount', None), - 'short_count': train_data.get('shortCount', None), - 'margin': train_data.get('margin', None), - 'start_date': train_data.get('startDate', None), - }) - - # 从is字段获取样本内测试数据 - if 'is' in alpha_data and alpha_data['is']: - is_data = alpha_data['is'] - metrics.update({ - 'is_sharpe': is_data.get('sharpe', None), - 'is_returns': is_data.get('returns', None), - 'is_drawdown': is_data.get('drawdown', None), - 'is_turnover': is_data.get('turnover', None), - 'is_fitness': is_data.get('fitness', None), - 'is_pnl': is_data.get('pnl', None), - }) - - # 从test字段获取样本外测试数据 - if 'test' in alpha_data and alpha_data['test']: - test_data = alpha_data['test'] - metrics.update({ - 'test_sharpe': test_data.get('sharpe', None), - 'test_returns': test_data.get('returns', None), - 'test_drawdown': test_data.get('drawdown', None), - 'test_turnover': test_data.get('turnover', None), - 'test_fitness': test_data.get('fitness', None), - 'test_pnl': test_data.get('pnl', None), - }) - - # 其他重要信息 - metrics.update({ - 'alpha_id': alpha_data.get('id', None), - 'grade': alpha_data.get('grade', None), - 'stage': alpha_data.get('stage', None), - 'status': alpha_data.get('status', None), - 'date_created': alpha_data.get('dateCreated', None), - }) - - # 解析检查结果 - if 'is' in alpha_data and 'checks' in alpha_data['is']: - checks = alpha_data['is']['checks'] - check_results = {} - for check in checks: - check_name = check.get('name', '') - result = check.get('result', '') - value = check.get('value', None) - check_results[check_name.lower()] = { - 'result': result, - 'value': value, - 'limit': check.get('limit', None) - } - metrics['checks'] = check_results - - except Exception as e: - metrics['error'] = f"解析指标时出错: {str(e)}" - - return metrics - - def close(self): - """关闭连接""" - if self.client: - self.client.close() - - -class AlphaSimulationManager: - def __init__(self, credentials_file='account.txt'): - self.credentials_file = credentials_file - self.results = [] - - """将秒数格式化为 xx分xx秒 格式""" - def format_time(self, seconds): - if seconds < 60: - return f"{seconds:.2f}秒" - else: - minutes = int(seconds // 60) - remaining_seconds = seconds % 60 - return f"{minutes}分{remaining_seconds:.2f}秒" - - """模拟单个Alpha因子(线程安全)""" - def simulate_single_alpha(self, api, expression, settings=None): - alpha_start_time = time.time() - - try: - # 模拟Alpha因子 - simulation_result = api.simulate_alpha(expression, settings) - alpha_end_time = time.time() - time_consuming = alpha_end_time - alpha_start_time - - # 根据模拟结果类型处理 - if simulation_result["status"] == "success": - # 模拟成功的结果 - result = { - "expression": expression, - "time_consuming": time_consuming, - "formatted_time": self.format_time(time_consuming), - "alpha_id": simulation_result["alpha_id"], - "status": "success", - "description": "/", - "simulation_timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), - # 训练集指标 - "train_metrics": { - "sharpe_ratio": simulation_result["metrics"].get('sharpe_ratio'), - "annual_return": simulation_result["metrics"].get('annual_return'), - "max_drawdown": simulation_result["metrics"].get('max_drawdown'), - "turnover": simulation_result["metrics"].get('turnover'), - "fitness": simulation_result["metrics"].get('fitness'), - "pnl": simulation_result["metrics"].get('pnl'), - "book_size": simulation_result["metrics"].get('book_size'), - "long_count": simulation_result["metrics"].get('long_count'), - "short_count": simulation_result["metrics"].get('short_count'), - "margin": simulation_result["metrics"].get('margin'), - }, - # 样本内测试指标 - "is_metrics": { - "sharpe_ratio": simulation_result["metrics"].get('is_sharpe'), - "annual_return": simulation_result["metrics"].get('is_returns'), - "max_drawdown": simulation_result["metrics"].get('is_drawdown'), - "turnover": simulation_result["metrics"].get('is_turnover'), - "fitness": simulation_result["metrics"].get('is_fitness'), - "pnl": simulation_result["metrics"].get('is_pnl'), - }, - # 样本外测试指标 - "test_metrics": { - "sharpe_ratio": simulation_result["metrics"].get('test_sharpe'), - "annual_return": simulation_result["metrics"].get('test_returns'), - "max_drawdown": simulation_result["metrics"].get('test_drawdown'), - "turnover": simulation_result["metrics"].get('test_turnover'), - "fitness": simulation_result["metrics"].get('test_fitness'), - "pnl": simulation_result["metrics"].get('test_pnl'), - }, - # 其他信息 - "alpha_info": { - "grade": simulation_result["metrics"].get('grade'), - "stage": simulation_result["metrics"].get('stage'), - "status": simulation_result["metrics"].get('status'), - "date_created": simulation_result["metrics"].get('date_created'), - "checks": simulation_result["metrics"].get('checks', {}) - } - } - print(f"✓ 因子模拟成功: {expression}") - print(f" 耗时: {self.format_time(time_consuming)},Alpha ID: {simulation_result['alpha_id']}") - - # 打印关键指标 - self._print_success_metrics(simulation_result["metrics"]) - - else: - # 模拟失败的结果(API返回的错误) - result = { - "expression": expression, - "time_consuming": time_consuming, - "formatted_time": self.format_time(time_consuming), - "alpha_id": "/", - "status": "error", - "description": simulation_result["message"], - "simulation_timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), - "performance_metrics": {}, - "risk_metrics": {}, - "quantile_metrics": {}, - "other_metrics": {} - } - print(f"✗ 因子模拟失败: {expression}") - print(f" 耗时: {self.format_time(time_consuming)},错误: {simulation_result['message']}") - - except Exception as e: - # 其他异常情况 - alpha_end_time = time.time() - time_consuming = alpha_end_time - alpha_start_time - - result = { - "expression": expression, - "time_consuming": time_consuming, - "formatted_time": self.format_time(time_consuming), - "alpha_id": "/", - "status": "failed", - "description": str(e), - "simulation_timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), - "performance_metrics": {}, - "risk_metrics": {}, - "quantile_metrics": {}, - "other_metrics": {} - } - print(f"✗ 因子模拟异常: {expression}") - print(f" 耗时: {self.format_time(time_consuming)},异常: {str(e)}") - - return result - - """打印成功因子的关键指标""" - - def _print_success_metrics(self, metrics): - if 'error' in metrics: - print(f" 指标获取错误: {metrics['error']}") - return - - print(" 关键指标 (训练集):") - key_metrics = [ - ('夏普比率', 'sharpe_ratio'), - ('年化收益', 'annual_return'), - ('最大回撤', 'max_drawdown'), - ('换手率', 'turnover'), - ('适应度', 'fitness'), - ('PNL', 'pnl'), - ] - - for chinese_name, metric_key in key_metrics: - value = metrics.get(metric_key) - if value is not None: - if isinstance(value, float): - value = f"{value:.4f}" - print(f" {chinese_name}: {value}") - - # 显示样本外测试的夏普比率(如果存在) - test_sharpe = metrics.get('test_sharpe') - if test_sharpe is not None: - print(f" 样本外夏普比率: {test_sharpe:.4f}") - - """模拟一批Alpha因子(3个一组)""" - def simulate_alpha_batch(self, alpha_batch, batch_number): - print(f"\n{'=' * 60}") - print(f"开始第 {batch_number} 批因子模拟 (共 {len(alpha_batch)} 个因子)") - print(f"因子列表: {alpha_batch}") - print(f"{'=' * 60}") - - batch_start_time = time.time() - batch_results = [] - - # 创建API客户端实例(每个线程独立的客户端) - api = WorldQuantBrainSimulate(self.credentials_file) - - try: - if api.login(): - # 使用线程池执行3个因子的模拟 - with ThreadPoolExecutor(max_workers=3) as executor: - # 提交所有任务 - future_to_alpha = {executor.submit(self.simulate_single_alpha, api, alpha): alpha for alpha in alpha_batch} - - # 等待所有任务完成 - for future in as_completed(future_to_alpha): - alpha = future_to_alpha[future] - try: - result = future.result() - batch_results.append(result) - except Exception as e: - print(f"因子 {alpha} 执行异常: {e}") - except Exception as e: - print(f"第 {batch_number} 批模拟过程中出错: {e}") - finally: - api.close() - - batch_end_time = time.time() - batch_total_time = batch_end_time - batch_start_time - - print(f"\n第 {batch_number} 批模拟完成!") - print(f"本批总耗时: {self.format_time(batch_total_time)}") - print(f"{'=' * 60}") - - return batch_results - - """运行批量模拟""" - def run_simulation(self, alpha_list, batch_size=3): - print("开始Alpha因子批量模拟...") - total_start_time = time.time() - - # 将因子列表分成每批3个 - batches = [alpha_list[i:i + batch_size] for i in range(0, len(alpha_list), batch_size)] - - all_results = [] - - for i, batch in enumerate(batches, 1): - # 模拟当前批次 - batch_results = self.simulate_alpha_batch(batch, i) - all_results.extend(batch_results) - - # 如果不是最后一批,则等待3-5秒 - if i < len(batches): - sleep_time = uniform(3, 5) - print(f"\n等待 {sleep_time:.2f} 秒后开始下一批...") - time.sleep(sleep_time) - - total_end_time = time.time() - total_time = total_end_time - total_start_time - - # 输出最终结果汇总 - self.print_summary(all_results, total_time) - - # 保存结果到文件 - self.save_results(all_results) - - return all_results - - """打印结果汇总""" - def print_summary(self, results, total_time): - print(f"\n{'=' * 60}") - print("模拟结果汇总") - print(f"{'=' * 60}") - - success_count = sum(1 for r in results if r['status'] == 'success') - error_count = sum(1 for r in results if r['status'] == 'error') - failed_count = sum(1 for r in results if r['status'] == 'failed') - - print(f"总模拟因子数: {len(results)}") - print(f"成功: {success_count} 个") - print(f"模拟错误: {error_count} 个") - print(f"执行异常: {failed_count} 个") - print(f"总耗时: {self.format_time(total_time)}") - print(f"{'=' * 60}") - - for i, result in enumerate(results, 1): - status_icon = "✓" if result['status'] == 'success' else "✗" - print(f"{i}. {status_icon} {result['expression']}") - print(f" 状态: {result['status']}") - print(f" 耗时: {result['formatted_time']}") - print(f" Alpha ID: {result['alpha_id']}") - if result['status'] != 'success': - print(f" 原因: {result['description']}") - print() - - """保存结果到文件""" - def save_results(self, results): - # 转换为可序列化的格式 - serializable_results = [] - for result in results: - serializable_result = result.copy() - serializable_result['time_consuming'] = round(serializable_result['time_consuming'], 2) - - # 处理metrics中的浮点数,保留6位小数 - for metric_category in ['performance_metrics', 'risk_metrics', 'quantile_metrics', 'other_metrics']: - if metric_category in serializable_result: - for key, value in serializable_result[metric_category].items(): - if isinstance(value, float): - serializable_result[metric_category][key] = round(value, 6) - - serializable_results.append(serializable_result) - - # 将日志文件, 保存到当前目录下, result 文件夹中 - if not os.path.exists('./result'): - os.makedirs('./result') - - result_name = f"result/simulation_results-{str(int(time.time()))}.json" - with open(result_name, 'w', encoding='utf-8') as f: - json.dump(serializable_results, f, ensure_ascii=False, indent=2) - print(f"结果已保存到 {result_name}") - - -if __name__ == "__main__": - # 待模拟因子列表 - with open('alpha.txt', 'r', encoding='utf-8') as file: - alpha_list = [line.strip() for line in file] - - if not alpha_list: - print("alpha.txt 文件不存在") - with open('alpha.txt', 'w', encoding='utf-8') as file: file.write("") - print("已创建 alpha.txt 文件, 请添加因子后重新运行, 一行一个因子") - exit(1) - - # 创建模拟管理器并运行 - manager = AlphaSimulationManager() - results = manager.run_simulation(alpha_list, batch_size=3) \ No newline at end of file diff --git a/managers/simulation_manager.py b/managers/simulation_manager.py index 582fd88..5137df8 100644 --- a/managers/simulation_manager.py +++ b/managers/simulation_manager.py @@ -7,9 +7,9 @@ from random import uniform from typing import List, Dict, Any from core.api_client import WorldQuantBrainSimulate -from core.models import SimulationResult, TrainMetrics, TestMetrics, AlphaInfo +from core.models import SimulationResult from utils.time_utils import format_time -from utils.file_utils import save_results_to_file +from utils.file_utils import save_results_to_file, save_success_alpha class AlphaSimulationManager: @@ -19,7 +19,7 @@ class AlphaSimulationManager: """模拟单个Alpha因子(线程安全)""" def simulate_single_alpha(self, api: WorldQuantBrainSimulate, expression: str, - settings: Dict[str, Any] = None) -> SimulationResult: + settings: Dict[str, Any] = None) -> SimulationResult: alpha_start_time = time.time() try: @@ -30,7 +30,7 @@ class AlphaSimulationManager: # 根据模拟结果类型处理 if simulation_result["status"] == "success": - # 模拟成功的结果 + # 模拟成功的结果 - 直接使用原始metrics数据 metrics = simulation_result["metrics"] result = SimulationResult( expression=expression, @@ -40,10 +40,7 @@ class AlphaSimulationManager: status="success", description="/", simulation_timestamp=time.strftime("%Y-%m-%d %H:%M:%S"), - train_metrics=metrics.train_metrics, - is_metrics=metrics.is_metrics, - test_metrics=metrics.test_metrics, - alpha_info=metrics.alpha_info + metrics=metrics # 直接存储原始metrics数据 ) print(f"✓ 因子模拟成功: {expression}") print(f" 耗时: {format_time(time_consuming)},Alpha ID: {simulation_result['alpha_id']}") @@ -60,7 +57,8 @@ class AlphaSimulationManager: alpha_id="/", status="error", description=simulation_result["message"], - simulation_timestamp=time.strftime("%Y-%m-%d %H:%M:%S") + simulation_timestamp=time.strftime("%Y-%m-%d %H:%M:%S"), + metrics=None ) print(f"✗ 因子模拟失败: {expression}") print(f" 耗时: {format_time(time_consuming)},错误: {simulation_result['message']}") @@ -77,7 +75,8 @@ class AlphaSimulationManager: alpha_id="/", status="failed", description=str(e), - simulation_timestamp=time.strftime("%Y-%m-%d %H:%M:%S") + simulation_timestamp=time.strftime("%Y-%m-%d %H:%M:%S"), + metrics=None ) print(f"✗ 因子模拟异常: {expression}") print(f" 耗时: {format_time(time_consuming)},异常: {str(e)}") @@ -85,37 +84,45 @@ class AlphaSimulationManager: return result """打印成功因子的关键指标""" - def _print_success_metrics(self, metrics): + def _print_success_metrics(self, metrics: Dict[str, Any]): + # 添加空值检查 + if not metrics: + print(" 无指标数据") + return + print(" 关键指标 (训练集):") + # 从原始metrics数据中提取训练集指标 + train_data = metrics.get('train', {}) or {} key_metrics = [ - ('夏普比率', metrics.train_metrics.sharpe_ratio), - ('年化收益', metrics.train_metrics.annual_return), - ('最大回撤', metrics.train_metrics.max_drawdown), - ('换手率', metrics.train_metrics.turnover), - ('适应度', metrics.train_metrics.fitness), - ('PNL', metrics.train_metrics.pnl), + ('夏普比率', 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}") - - # 显示样本外测试的夏普比率(如果存在) - if metrics.test_metrics.sharpe_ratio is not None: - print(f" 样本外夏普比率: {metrics.test_metrics.sharpe_ratio:.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"\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) @@ -200,12 +207,26 @@ class AlphaSimulationManager: 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 "✗" - print(f"{i}. {status_icon} {result.expression}") - print(f" 状态: {result.status}") - print(f" 耗时: {result.formatted_time}") - print(f" Alpha ID: {result.alpha_id}") + + if result.status == 'success': + 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': - print(f" 原因: {result.description}") - print() \ No newline at end of file + 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/result/simulation_results-1763048082.json b/result/simulation_results-1763048082.json deleted file mode 100644 index cabc4b9..0000000 --- a/result/simulation_results-1763048082.json +++ /dev/null @@ -1,139 +0,0 @@ -[ - { - "expression": "ts_rank(ts_returns(close, 15), 30)", - "time_consuming": 5.89, - "formatted_time": "5.89秒", - "alpha_id": "/", - "status": "error", - "description": "Attempted to use inaccessible or unknown operator \"ts_returns\"", - "simulation_timestamp": "2025-11-13 23:30:43", - "train_metrics": null, - "is_metrics": null, - "test_metrics": null, - "alpha_info": null - }, - { - "expression": "(close - ts_min(close, 20)) / (ts_max(close, 20) - ts_min(close, 20))", - "time_consuming": 7.89, - "formatted_time": "7.89秒", - "alpha_id": "/", - "status": "error", - "description": "Attempted to use inaccessible or unknown operator \"ts_min\"", - "simulation_timestamp": "2025-11-13 23:30:45", - "train_metrics": null, - "is_metrics": null, - "test_metrics": null, - "alpha_info": null - }, - { - "expression": "-ts_corr(ts_rank(close, 30), ts_rank(volume, 30), 10)", - "time_consuming": 88.99, - "formatted_time": "1分28.99秒", - "alpha_id": "JjEVlkNx", - "status": "success", - "description": "/", - "simulation_timestamp": "2025-11-13 23:32:06", - "train_metrics": { - "sharpe_ratio": null, - "annual_return": null, - "max_drawdown": null, - "turnover": null, - "fitness": null, - "pnl": null, - "book_size": null, - "long_count": null, - "short_count": null, - "margin": null - }, - "is_metrics": { - "sharpe_ratio": 1.15, - "annual_return": 0.0604, - "max_drawdown": 0.1311, - "turnover": 0.4517, - "fitness": 0.42, - "pnl": 2988907 - }, - "test_metrics": { - "sharpe_ratio": null, - "annual_return": null, - "max_drawdown": null, - "turnover": null, - "fitness": null, - "pnl": null - }, - "alpha_info": { - "grade": "INFERIOR", - "stage": "IS", - "status": "UNSUBMITTED", - "date_created": "2025-11-13T10:32:05-05:00", - "checks": { - "low_sharpe": { - "result": "FAIL", - "value": 1.15, - "limit": 1.25 - }, - "low_fitness": { - "result": "FAIL", - "value": 0.42, - "limit": 1.0 - }, - "low_turnover": { - "result": "PASS", - "value": 0.4517, - "limit": 0.01 - }, - "high_turnover": { - "result": "PASS", - "value": 0.4517, - "limit": 0.7 - }, - "concentrated_weight": { - "result": "PASS", - "value": null, - "limit": null - }, - "low_sub_universe_sharpe": { - "result": "PASS", - "value": 0.76, - "limit": 0.5 - }, - "self_correlation": { - "result": "PENDING", - "value": null, - "limit": null - }, - "matches_competition": { - "result": "PASS", - "value": null, - "limit": null - } - } - } - }, - { - "expression": "-ts_std(ts_returns(close, 1), 30)", - "time_consuming": 1.42, - "formatted_time": "1.42秒", - "alpha_id": "/", - "status": "error", - "description": "Attempted to use inaccessible or unknown operator \"ts_std\"", - "simulation_timestamp": "2025-11-13 23:32:15", - "train_metrics": null, - "is_metrics": null, - "test_metrics": null, - "alpha_info": null - }, - { - "expression": "ts_rank(ts_delta(close, 3) / ts_delay(close, 3), 15)", - "time_consuming": 148.04, - "formatted_time": "2分28.04秒", - "alpha_id": "/", - "status": "failed", - "description": "_ssl.c:980: The handshake operation timed out", - "simulation_timestamp": "2025-11-13 23:34:42", - "train_metrics": null, - "is_metrics": null, - "test_metrics": null, - "alpha_info": null - } -] \ No newline at end of file diff --git a/result/simulation_results-1763109446.json b/result/simulation_results-1763109446.json new file mode 100644 index 0000000..868c87a --- /dev/null +++ b/result/simulation_results-1763109446.json @@ -0,0 +1,289 @@ +[ + { + "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 new file mode 100644 index 0000000..24a94b1 --- /dev/null +++ b/result/simulation_results-1763110929.json @@ -0,0 +1,524 @@ +[ + { + "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 new file mode 100644 index 0000000..5d0d7d0 --- /dev/null +++ b/result/simulation_results-1763112962.json @@ -0,0 +1,82 @@ +[ + { + "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 new file mode 100644 index 0000000..8bf45fb --- /dev/null +++ b/result/simulation_results-1763113286.json @@ -0,0 +1,12 @@ +[ + { + "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/success.txt b/success.txt new file mode 100644 index 0000000..4fff374 --- /dev/null +++ b/success.txt @@ -0,0 +1,4 @@ +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/file_utils.py b/utils/file_utils.py index 813dd88..b9fec04 100644 --- a/utils/file_utils.py +++ b/utils/file_utils.py @@ -26,7 +26,7 @@ def save_results_to_file(results: List[Any], result_dir: str = 'result') -> str: serializable_results = [] for result in results: if hasattr(result, '__dict__'): - # 如果是dataclass对象 + # 如果是dataclass对象,转换为字典 result_dict = result.__dict__.copy() else: # 如果是字典 @@ -36,14 +36,19 @@ def save_results_to_file(results: List[Any], result_dir: str = 'result') -> str: if 'time_consuming' in result_dict: result_dict['time_consuming'] = round(result_dict['time_consuming'], 2) - # 处理metrics对象 - for key in list(result_dict.keys()): - if hasattr(result_dict[key], '__dict__'): - result_dict[key] = result_dict[key].__dict__ - # 处理浮点数精度 - for metric_key, value in result_dict[key].items(): - if isinstance(value, float): - result_dict[key][metric_key] = round(value, 6) + # 只需要确保所有浮点数精度一致 + 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) @@ -56,4 +61,24 @@ def save_results_to_file(results: List[Any], result_dir: str = 'result') -> str: json.dump(serializable_results, f, ensure_ascii=False, indent=2) print(f"结果已保存到 {result_name}") - return result_name \ No newline at end of file + 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}")