From 2bccfa5087ab36ab11e5ef5814892456aca04113 Mon Sep 17 00:00:00 2001 From: Jack Date: Thu, 13 Nov 2025 22:18:42 +0800 Subject: [PATCH] update --- wqb-simulate/alpha.txt | 2 +- .../result/simulation_results-1763042291.json | 24 +++ .../result/simulation_results-1763042641.json | 41 ++++ .../result/simulation_results-1763042892.json | 41 ++++ wqb-simulate/simulate.py | 187 ++++++++++++++++-- 5 files changed, 278 insertions(+), 17 deletions(-) create mode 100644 wqb-simulate/result/simulation_results-1763042291.json create mode 100644 wqb-simulate/result/simulation_results-1763042641.json create mode 100644 wqb-simulate/result/simulation_results-1763042892.json diff --git a/wqb-simulate/alpha.txt b/wqb-simulate/alpha.txt index 23c943e..b7a62e5 100644 --- a/wqb-simulate/alpha.txt +++ b/wqb-simulate/alpha.txt @@ -1 +1 @@ -ts_Mean(ts_mean(close - delay(close,1),5) / atr(15), 30) \ No newline at end of file +rank(ts_sum(vec_avg(nws12_afterhsz_sl), 60)) * 0.7 + rank(-ts_delta(close, 2)) * 0.3 \ No newline at end of file diff --git a/wqb-simulate/result/simulation_results-1763042291.json b/wqb-simulate/result/simulation_results-1763042291.json new file mode 100644 index 0000000..113e2db --- /dev/null +++ b/wqb-simulate/result/simulation_results-1763042291.json @@ -0,0 +1,24 @@ +{ + "simulation_info": { + "total_count": 1, + "success_count": 0, + "failed_count": 1, + "completion_time": "2025-11-13 21:58:11", + "total_time_seconds": 10.505059003829956 + }, + "results": [ + { + "expression": "ts_mean(ts_mean(close - delay(close,1),5) / atr(15), 30)", + "time_consuming_seconds": 10.505059003829956, + "formatted_time": "10.51秒", + "alpha_id": "/", + "status": "failed", + "description": "'alpha'", + "simulation_timestamp": "2025-11-13 21:58:11", + "performance_metrics": {}, + "risk_metrics": {}, + "quantile_metrics": {}, + "other_metrics": {} + } + ] +} \ No newline at end of file diff --git a/wqb-simulate/result/simulation_results-1763042641.json b/wqb-simulate/result/simulation_results-1763042641.json new file mode 100644 index 0000000..030c815 --- /dev/null +++ b/wqb-simulate/result/simulation_results-1763042641.json @@ -0,0 +1,41 @@ +[ + { + "expression": "rank(ts_sum(vec_avg(nws12_afterhsz_sl), 60)) * 0.7 + rank(-ts_delta(close, 2)) * 0.3", + "time_consuming": 75.8, + "formatted_time": "1分15.80秒", + "alpha_id": "WjqvZQLO", + "status": "success", + "description": "/", + "simulation_timestamp": "2025-11-13 22:04:01", + "performance_metrics": { + "sharpe_ratio": null, + "annual_return": null, + "annual_volatility": null, + "max_drawdown": null, + "information_ratio": null, + "total_return": null + }, + "risk_metrics": { + "turnover": null, + "score": null, + "specific_return": null, + "specific_risk": null, + "tail_ratio": null, + "common_ratio": null + }, + "quantile_metrics": { + "top_minus_bottom": null, + "top_decile_return": null, + "bottom_decile_return": null, + "ic": null, + "ic_decay": null + }, + "other_metrics": { + "capacity": null, + "fitness": null, + "instrument_count": null, + "start_date": null, + "end_date": null + } + } +] \ No newline at end of file diff --git a/wqb-simulate/result/simulation_results-1763042892.json b/wqb-simulate/result/simulation_results-1763042892.json new file mode 100644 index 0000000..c62eb5f --- /dev/null +++ b/wqb-simulate/result/simulation_results-1763042892.json @@ -0,0 +1,41 @@ +[ + { + "expression": "rank(ts_sum(vec_avg(nws12_afterhsz_sl), 60)) * 0.7 + rank(-ts_delta(close, 2)) * 0.3", + "time_consuming": 78.3, + "formatted_time": "1分18.30秒", + "alpha_id": "WjqvZQLO", + "status": "success", + "description": "/", + "simulation_timestamp": "2025-11-13 22:08:12", + "performance_metrics": { + "sharpe_ratio": null, + "annual_return": null, + "annual_volatility": null, + "max_drawdown": null, + "information_ratio": null, + "total_return": null + }, + "risk_metrics": { + "turnover": null, + "score": null, + "specific_return": null, + "specific_risk": null, + "tail_ratio": null, + "common_ratio": null + }, + "quantile_metrics": { + "top_minus_bottom": null, + "top_decile_return": null, + "bottom_decile_return": null, + "ic": null, + "ic_decay": null + }, + "other_metrics": { + "capacity": null, + "fitness": null, + "instrument_count": null, + "start_date": null, + "end_date": null + } + } +] \ No newline at end of file diff --git a/wqb-simulate/simulate.py b/wqb-simulate/simulate.py index 00d1663..e16b454 100644 --- a/wqb-simulate/simulate.py +++ b/wqb-simulate/simulate.py @@ -1,5 +1,4 @@ import os.path - import httpx import json from httpx import BasicAuth @@ -15,8 +14,8 @@ class WorldQuantBrainSimulate: 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("") @@ -27,8 +26,8 @@ class WorldQuantBrainSimulate: 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)) @@ -42,8 +41,8 @@ class WorldQuantBrainSimulate: print(f"登录失败: {response.json()}") return False + """模拟Alpha因子""" def simulate_alpha(self, expression, settings=None): - """模拟Alpha因子""" if self.client is None: raise Exception("请先登录") @@ -94,9 +93,87 @@ class WorldQuantBrainSimulate: # 返回一个特殊标识,表示模拟失败 return {"status": "error", "message": result} - result = sim_progress_resp.json()["alpha"] - print(f"生成的Alpha ID: {result}") - return {"status": "success", "alpha_id": 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 == 200: + 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: + # 基本统计信息 + if 'returns' in alpha_data: + returns = alpha_data['returns'] + metrics.update({ + 'sharpe_ratio': returns.get('sharpe', None), + 'annual_return': returns.get('annualReturn', None), + 'annual_volatility': returns.get('annualVolatility', None), + 'max_drawdown': returns.get('maxDrawdown', None), + 'information_ratio': returns.get('informationRatio', None), + 'tail_ratio': returns.get('tailRatio', None), + 'common_ratio': returns.get('commonRatio', None), + }) + + # 风险调整后收益 + if 'riskAdjustment' in alpha_data: + risk_adj = alpha_data['riskAdjustment'] + metrics.update({ + 'risk_adjusted_return': risk_adj.get('return', None), + 'turnover': risk_adj.get('turnover', None), + 'score': risk_adj.get('score', None), + 'specific_return': risk_adj.get('specificReturn', None), + 'specific_risk': risk_adj.get('specificRisk', None), + }) + + # 分位数表现 + if 'quantiles' in alpha_data: + quantiles = alpha_data['quantiles'] + metrics.update({ + 'top_minus_bottom': quantiles.get('topMinusBottom', None), + 'top_decile_return': quantiles.get('topDecileReturn', None), + 'bottom_decile_return': quantiles.get('bottomDecileReturn', None), + 'ic': quantiles.get('ic', None), + 'ic_decay': quantiles.get('icDecay', None), + }) + + # 其他重要指标 + metrics.update({ + 'total_return': alpha_data.get('totalReturn', None), + 'capacity': alpha_data.get('capacity', None), + 'fitness': alpha_data.get('fitness', None), + 'instrument_count': alpha_data.get('instrumentCount', None), + 'start_date': alpha_data.get('startDate', None), + 'end_date': alpha_data.get('endDate', None), + }) + + except Exception as e: + metrics['error'] = f"解析指标时出错: {str(e)}" + + return metrics def close(self): """关闭连接""" @@ -109,8 +186,8 @@ class AlphaSimulationManager: self.credentials_file = credentials_file self.results = [] + """将秒数格式化为 xx分xx秒 格式""" def format_time(self, seconds): - """将秒数格式化为 xx分xx秒 格式""" if seconds < 60: return f"{seconds:.2f}秒" else: @@ -118,8 +195,8 @@ class AlphaSimulationManager: remaining_seconds = seconds % 60 return f"{minutes}分{remaining_seconds:.2f}秒" + """模拟单个Alpha因子(线程安全)""" def simulate_single_alpha(self, api, expression, settings=None): - """模拟单个Alpha因子(线程安全)""" alpha_start_time = time.time() try: @@ -137,11 +214,49 @@ class AlphaSimulationManager: "formatted_time": self.format_time(time_consuming), "alpha_id": simulation_result["alpha_id"], "status": "success", - "description": "/" + "description": "/", + "simulation_timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), + # 性能指标 + "performance_metrics": { + "sharpe_ratio": simulation_result["metrics"].get('sharpe_ratio'), + "annual_return": simulation_result["metrics"].get('annual_return'), + "annual_volatility": simulation_result["metrics"].get('annual_volatility'), + "max_drawdown": simulation_result["metrics"].get('max_drawdown'), + "information_ratio": simulation_result["metrics"].get('information_ratio'), + "total_return": simulation_result["metrics"].get('total_return'), + }, + # 风险指标 + "risk_metrics": { + "turnover": simulation_result["metrics"].get('turnover'), + "score": simulation_result["metrics"].get('score'), + "specific_return": simulation_result["metrics"].get('specific_return'), + "specific_risk": simulation_result["metrics"].get('specific_risk'), + "tail_ratio": simulation_result["metrics"].get('tail_ratio'), + "common_ratio": simulation_result["metrics"].get('common_ratio'), + }, + # 分位数指标 + "quantile_metrics": { + "top_minus_bottom": simulation_result["metrics"].get('top_minus_bottom'), + "top_decile_return": simulation_result["metrics"].get('top_decile_return'), + "bottom_decile_return": simulation_result["metrics"].get('bottom_decile_return'), + "ic": simulation_result["metrics"].get('ic'), + "ic_decay": simulation_result["metrics"].get('ic_decay'), + }, + # 其他指标 + "other_metrics": { + "capacity": simulation_result["metrics"].get('capacity'), + "fitness": simulation_result["metrics"].get('fitness'), + "instrument_count": simulation_result["metrics"].get('instrument_count'), + "start_date": simulation_result["metrics"].get('start_date'), + "end_date": simulation_result["metrics"].get('end_date'), + } } 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 = { @@ -150,7 +265,12 @@ class AlphaSimulationManager: "formatted_time": self.format_time(time_consuming), "alpha_id": "/", "status": "error", - "description": simulation_result["message"] + "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']}") @@ -166,15 +286,42 @@ class AlphaSimulationManager: "formatted_time": self.format_time(time_consuming), "alpha_id": "/", "status": "failed", - "description": str(e) + "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 + + key_metrics = [ + ('夏普比率', 'sharpe_ratio'), + ('年化收益', 'annual_return'), + ('最大回撤', 'max_drawdown'), + ('信息比率', 'information_ratio'), + ('总分', 'score'), + ] + + print(" 关键指标:") + 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}") + + """模拟一批Alpha因子(3个一组)""" def simulate_alpha_batch(self, alpha_batch, batch_number): - """模拟一批Alpha因子(3个一组)""" print(f"\n{'=' * 60}") print(f"开始第 {batch_number} 批因子模拟 (共 {len(alpha_batch)} 个因子)") print(f"因子列表: {alpha_batch}") @@ -215,8 +362,8 @@ class AlphaSimulationManager: return batch_results + """运行批量模拟""" def run_simulation(self, alpha_list, batch_size=3): - """运行批量模拟""" print("开始Alpha因子批量模拟...") total_start_time = time.time() @@ -247,8 +394,8 @@ class AlphaSimulationManager: return all_results + """打印结果汇总""" def print_summary(self, results, total_time): - """打印结果汇总""" print(f"\n{'=' * 60}") print("模拟结果汇总") print(f"{'=' * 60}") @@ -274,13 +421,21 @@ class AlphaSimulationManager: 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 文件夹中