convert_joinquant_to_ptrade
Convert JoinQuant quantitative trading strategies to Ptrade platform format by automatically mapping APIs and generating executable Python files with detailed conversion reports.
Instructions
将聚宽(JoinQuant)策略代码转换为Ptrade格式。可以直接输入代码字符串,或提供文件路径(支持.py/.txt等格式)。自动识别并转换所有API调用,生成Ptrade可用的.py文件和详细转换报告。
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| code | No | 聚宽策略的完整Python代码(可选,与filepath二选一) | |
| filepath | No | 策略文件的路径(支持.py、.txt等格式,可选,与code二选一) | |
| output_dir | No | 输出目录路径(可选,默认为当前目录或原文件所在目录) |
Implementation Reference
- src/tools/convertStrategy.ts:187-490 (handler)The core implementation of the JoinQuant to Ptrade conversion logic. It takes a code string, performs a series of regex-based transformations, and returns the converted Ptrade-compatible Python code along with a report of changes and warnings.
function convertJoinQuantToPtrade(code: string): ConversionResult { const lines = code.split('\n'); const convertedLines: string[] = []; const changes: ConversionResult['changes'] = []; const warnings: string[] = []; // API映射规则 - 按优先级排序,特定模式优先匹配 const apiMappings = [ // ========== 导入模块 ========== { pattern: /from\s+jqdata\s+import\s+\*/g, replacement: 'from ptrade.api import *', reason: '更新导入模块为Ptrade' }, { pattern: /import\s+jqdata/g, replacement: 'import ptrade', reason: '更新导入模块为Ptrade' }, // ========== 日志函数(关键差异)========== // 必须在其他模式之前匹配,确保正确转换 { pattern: /\blog\.info\s*\(/g, replacement: 'log(', reason: 'JoinQuant使用log.info(),Ptrade使用log()' }, { pattern: /\blog\.debug\s*\(/g, replacement: 'log(', reason: 'JoinQuant使用log.debug(),Ptrade使用log()' }, { pattern: /\blog\.warn\s*\(/g, replacement: 'log(', reason: 'JoinQuant使用log.warn(),Ptrade使用log()' }, { pattern: /\blog\.error\s*\(/g, replacement: 'log(', reason: 'JoinQuant使用log.error(),Ptrade使用log()' }, // ========== Context对象持仓访问(关键差异)========== { pattern: /context\.portfolio\.positions\[([^\]]+)\]/g, replacement: 'get_position($1)', reason: 'JoinQuant使用context.portfolio.positions[stock],Ptrade使用get_position(stock)' }, { pattern: /context\.portfolio\.positions/g, replacement: 'get_positions()', reason: 'JoinQuant使用context.portfolio.positions,Ptrade使用get_positions()' }, // ========== 历史数据获取 ========== { pattern: /\battribute_history\s*\(/g, replacement: 'get_history(', reason: 'JoinQuant使用attribute_history(),Ptrade使用get_history()' }, { pattern: /\bhistory\s*\(/g, replacement: 'get_history(', reason: 'JoinQuant使用history(),Ptrade使用get_history()' }, // ========== 股票池和指数成分股 ========== { pattern: /\bget_index_stocks\s*\(/g, replacement: 'get_index_stocks(', reason: '获取指数成分股(Ptrade兼容,需确认参数)' }, { pattern: /\bget_all_securities\s*\(/g, replacement: 'get_market_list(', reason: 'JoinQuant使用get_all_securities(),Ptrade使用get_market_list()' }, // ========== 交易相关函数 ========== { pattern: /\border_target_percent\s*\(/g, replacement: 'order_target_value(', reason: 'JoinQuant的order_target_percent在Ptrade中使用order_target_value实现' }, { pattern: /\border\s*\(/g, replacement: 'order(', reason: '按数量下单(Ptrade兼容)' }, { pattern: /\border_target\s*\(/g, replacement: 'order_target(', reason: '目标持仓下单(Ptrade兼容)' }, { pattern: /\border_value\s*\(/g, replacement: 'order_value(', reason: '按金额下单(Ptrade兼容)' }, { pattern: /\border_target_value\s*\(/g, replacement: 'order_target_value(', reason: '目标金额下单(Ptrade兼容)' }, // ========== 回测设置 ========== { pattern: /\bset_benchmark\s*\(/g, replacement: 'set_benchmark(', reason: '设置基准(Ptrade兼容)' }, { pattern: /\bset_option\s*\(/g, replacement: 'set_option(', reason: '设置选项(Ptrade兼容)' }, { pattern: /\bset_commission\s*\(/g, replacement: 'set_commission(', reason: '设置手续费(Ptrade兼容)' }, { pattern: /\bset_slippage\s*\(/g, replacement: 'set_slippage(', reason: '设置滑点(Ptrade兼容)' }, { pattern: /\bset_order_cost\s*\(/g, replacement: 'set_commission(', reason: 'JoinQuant使用set_order_cost(),Ptrade使用set_commission()' }, // ========== 数据获取函数 ========== { pattern: /\bget_price\s*\(/g, replacement: 'get_price(', reason: '获取历史价格数据(Ptrade兼容)' }, { pattern: /\bget_fundamentals\s*\(/g, replacement: 'get_fundamentals(', reason: '获取财务数据(Ptrade兼容)' }, { pattern: /\bget_current_data\s*\(/g, replacement: 'get_snapshot(', reason: 'JoinQuant使用get_current_data(),Ptrade使用get_snapshot()' }, // ========== 定时任务 ========== { pattern: /\brun_daily\s*\(/g, replacement: 'run_daily(', reason: '按日运行(Ptrade兼容)' }, { pattern: /\brun_weekly\s*\(/g, replacement: 'run_daily(', reason: 'JoinQuant的run_weekly在Ptrade中用run_daily配合判断实现' }, { pattern: /\brun_monthly\s*\(/g, replacement: 'run_daily(', reason: 'JoinQuant的run_monthly在Ptrade中用run_daily配合判断实现' }, // ========== Context对象其他属性 ========== { pattern: /\bcontext\.current_dt\b/g, replacement: 'context.current_dt', reason: '当前时间(Ptrade兼容)' }, { pattern: /\bcontext\.portfolio\.total_value\b/g, replacement: 'context.portfolio.total_value', reason: '总资产(Ptrade兼容)' }, { pattern: /\bcontext\.portfolio\.available_cash\b/g, replacement: 'context.portfolio.available_cash', reason: '可用资金(Ptrade兼容)' }, { pattern: /\bcontext\.portfolio\.market_value\b/g, replacement: 'context.portfolio.market_value', reason: '持仓市值(Ptrade兼容)' }, { pattern: /\bcontext\.portfolio\b/g, replacement: 'context.portfolio', reason: '账户信息(Ptrade兼容)' }, { pattern: /\bcontext\.run_info\b/g, replacement: 'context.run_info', reason: '运行信息(Ptrade兼容)' } ]; // 逐行转换 lines.forEach((line, index) => { let convertedLine = line; let lineChanged = false; apiMappings.forEach((mapping) => { if (mapping.pattern.test(convertedLine)) { const original = convertedLine; convertedLine = convertedLine.replace(mapping.pattern, mapping.replacement); if (original !== convertedLine) { lineChanged = true; changes.push({ line: index + 1, original: original.trim(), converted: convertedLine.trim(), reason: mapping.reason }); } } }); convertedLines.push(convertedLine); }); // 检测潜在的兼容性问题和需要注意的部分 const codeText = code.toLowerCase(); const convertedText = convertedLines.join('\n').toLowerCase(); // 检查run_weekly和run_monthly的转换 if (codeText.includes('run_weekly') || codeText.includes('run_monthly')) { warnings.push('⚠️ 定时任务:run_weekly/run_monthly已转换为run_daily,需要在函数内添加日期判断逻辑'); } // 检查持仓访问的转换 if (codeText.includes('context.portfolio.positions')) { warnings.push('✅ 持仓访问:已将context.portfolio.positions转换为get_position()/get_positions()函数调用'); } // 检查日志函数的转换 if (codeText.includes('log.info') || codeText.includes('log.warn') || codeText.includes('log.error') || codeText.includes('log.debug')) { warnings.push('✅ 日志函数:已将log.info()/warn()/error()/debug()统一转换为log()'); } // 检查历史数据函数 if (codeText.includes('attribute_history') || codeText.includes('history(')) { warnings.push('✅ 历史数据:已将attribute_history()/history()转换为get_history()'); } // 检查get_current_data if (codeText.includes('get_current_data')) { warnings.push('✅ 行情快照:已将get_current_data()转换为get_snapshot()'); } // 检查get_all_securities if (codeText.includes('get_all_securities')) { warnings.push('✅ 证券列表:已将get_all_securities()转换为get_market_list(),请确认参数格式'); } // 检查order_target_percent if (codeText.includes('order_target_percent')) { warnings.push('⚠️ 按比例下单:order_target_percent已转换为order_target_value,需要手动计算目标金额'); } // 检查set_order_cost if (codeText.includes('set_order_cost')) { warnings.push('✅ 手续费设置:已将set_order_cost()转换为set_commission()'); } // 检查get_fundamentals的使用 if (codeText.includes('get_fundamentals')) { warnings.push('📊 财务数据:get_fundamentals()在Ptrade中支持,但查询语法可能需要调整'); } // 检查get_index_stocks if (codeText.includes('get_index_stocks')) { warnings.push('📋 指数成分股:get_index_stocks()在Ptrade中支持,请确认参数格式'); } // 检查时间参数格式 if (codeText.includes('time=') || codeText.includes('date=')) { warnings.push('⏰ 时间参数:请确认日期时间参数格式符合Ptrade要求(建议使用字符串格式)'); } // 检查数据频率参数 if (codeText.includes("'1d'") || codeText.includes('"1d"') || codeText.includes("'daily'") || codeText.includes('"daily"')) { warnings.push('📈 数据频率:Ptrade支持多种频率(1d/1w/1m/5m/15m/30m/60m等),请确认参数正确'); } // 总结性提示 if (changes.length === 0) { warnings.push('ℹ️ 未检测到需要转换的API调用,代码可能已经是Ptrade格式或使用了自定义函数'); } else { warnings.push(`🎯 转换完成:共修改了${changes.length}处代码,请仔细检查转换结果`); warnings.push('🧪 建议:在Ptrade平台上进行回测验证,确保策略逻辑正确'); } return { convertedCode: convertedLines.join('\n'), changes, warnings, success: true }; } - src/tools/convertStrategy.ts:20-182 (registration)The tool registration object 'convertStrategy' containing the metadata (name, description, parameters) and the 'run' method that orchestrates file handling and calls the conversion function.
export const convertStrategy = { name: "convert_joinquant_to_ptrade", description: "将聚宽(JoinQuant)策略代码转换为Ptrade格式。可以直接输入代码字符串,或提供文件路径(支持.py/.txt等格式)。自动识别并转换所有API调用,生成Ptrade可用的.py文件和详细转换报告。", parameters: { type: "object", properties: { code: { type: "string", description: "聚宽策略的完整Python代码(可选,与filepath二选一)" }, filepath: { type: "string", description: "策略文件的路径(支持.py、.txt等格式,可选,与code二选一)" }, output_dir: { type: "string", description: "输出目录路径(可选,默认为当前目录或原文件所在目录)" } }, required: [] }, async run(args: { code?: string; filepath?: string; output_dir?: string }) { try { let codeContent: string; let sourceInfo: string; let originalFilePath: string | undefined; let originalFileName: string; // 优先使用filepath,其次使用code if (args.filepath && args.filepath.trim().length > 0) { // 从文件读取代码 const filepath = args.filepath.trim(); originalFilePath = filepath; // 验证文件是否存在 if (!fs.existsSync(filepath)) { throw new Error(`文件不存在: ${filepath}`); } // 验证文件格式 const ext = path.extname(filepath).toLowerCase(); const supportedExts = ['.py', '.txt', '.text', '.code', '.strategy']; if (!supportedExts.includes(ext)) { throw new Error(`不支持的文件格式: ${ext}。支持的格式: ${supportedExts.join(', ')}`); } // 读取文件内容 try { codeContent = fs.readFileSync(filepath, 'utf-8'); originalFileName = path.basename(filepath, path.extname(filepath)); sourceInfo = `文件: ${path.basename(filepath)}`; } catch (error: any) { throw new Error(`读取文件失败: ${error.message}`); } } else if (args.code && args.code.trim().length > 0) { // 直接使用提供的代码 codeContent = args.code; originalFileName = 'strategy'; sourceInfo = "直接输入的代码"; } else { throw new Error("请提供策略代码(code参数)或文件路径(filepath参数)"); } // 验证代码不为空 if (!codeContent || codeContent.trim().length === 0) { throw new Error("策略代码不能为空"); } const result = convertJoinQuantToPtrade(codeContent); // 确定输出目录和文件名 let outputDir: string; if (args.output_dir && args.output_dir.trim().length > 0) { outputDir = args.output_dir.trim(); } else if (originalFilePath) { // 使用原文件所在目录 outputDir = path.dirname(originalFilePath); } else { // 使用当前工作目录 outputDir = process.cwd(); } // 生成输出文件名 const outputFileName = `${originalFileName}_ptrade.py`; const outputFilePath = path.join(outputDir, outputFileName); // 保存转换后的代码到文件 try { fs.writeFileSync(outputFilePath, result.convertedCode, 'utf-8'); } catch (error: any) { throw new Error(`保存文件失败: ${error.message}`); } // 生成转换报告 let report = `# 🔄 聚宽 → Ptrade 策略转换报告\n\n`; report += `## 📄 源文件信息\n\n${sourceInfo}\n\n`; report += `## ✅ 转换状态:${result.success ? "成功" : "失败"}\n\n`; report += `## 💾 输出文件\n\n`; report += `**文件路径**: \`${outputFilePath}\`\n`; report += `**文件名**: \`${outputFileName}\`\n`; report += `**文件大小**: ${Buffer.byteLength(result.convertedCode, 'utf-8')} 字节\n\n`; if (result.changes.length > 0) { report += `## 📝 代码修改详情(共 ${result.changes.length} 处)\n\n`; result.changes.forEach((change, idx) => { report += `### 修改 ${idx + 1}:第 ${change.line} 行\n`; report += `- **原代码**:\`${change.original}\`\n`; report += `- **新代码**:\`${change.converted}\`\n`; report += `- **原因**:${change.reason}\n\n`; }); } if (result.warnings.length > 0) { report += `## ⚠️ 注意事项(${result.warnings.length} 项)\n\n`; result.warnings.forEach((warning, idx) => { report += `${idx + 1}. ${warning}\n`; }); report += `\n`; } report += `## 🎯 转换后的Ptrade代码(已保存)\n\n`; report += `代码已成功保存到文件,可直接使用!\n\n`; report += `如需查看代码内容,打开文件:\`${outputFilePath}\`\n\n`; // 可选:显示前20行预览 const previewLines = result.convertedCode.split('\n').slice(0, 20); if (previewLines.length < result.convertedCode.split('\n').length) { report += `<details>\n<summary>📝 点击查看代码预览(前20行)</summary>\n\n`; report += `\`\`\`python\n${previewLines.join('\n')}\n... (更多内容请查看文件)\n\`\`\`\n\n`; report += `</details>\n\n`; } else { report += `\`\`\`python\n${result.convertedCode}\n\`\`\`\n\n`; } report += `---\n\n`; report += `### 📋 下一步操作\n\n`; report += `1. 🎯 **直接使用文件**: 将 \`${outputFileName}\` 上传到Ptrade平台\n`; report += `2. ⚠️ **检查注意事项**: 查看上方提到的可能需要手动调整的部分\n`; report += `3. 🧪 **运行回测**: 在Ptrade上验证策略逻辑\n`; report += `4. 🔧 **调整参数**: 根据回测结果优化参数\n\n`; report += `💡 提示:转换后的文件已保存在 \`${outputDir}\` 目录下!\n`; return { content: [{ type: "text" as const, text: report }] }; } catch (error: any) { return { content: [{ type: "text" as const, text: `❌ 转换失败: ${error.message}\n\n请检查输入的代码格式是否正确。` }], isError: true }; } } };