Skip to main content
Glama

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

TableJSON Schema
NameRequiredDescriptionDefault
codeNo聚宽策略的完整Python代码(可选,与filepath二选一)
filepathNo策略文件的路径(支持.py、.txt等格式,可选,与code二选一)
output_dirNo输出目录路径(可选,默认为当前目录或原文件所在目录)

Implementation Reference

  • 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
      };
    }
  • 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
          };
        }
      }
    };
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full disclosure burden. It adequately explains the processing behavior ('自动识别并转换所有API调用') and output artifacts ('生成Ptrade可用的.py文件和详细转换报告'), but omits critical operational details like file overwrite behavior, error handling, or whether the conversion is destructive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The single-sentence structure is densely packed with zero waste: purpose clause → input options → processing logic → output specification. Information is front-loaded with the conversion action immediately stated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given three parameters with complete schema coverage and no output schema, the description appropriately compensates by detailing the generated artifacts (conversion report and .py file). It could be improved by mentioning error conditions or prerequisites, but adequately covers the core workflow.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Despite 100% schema coverage, the description adds valuable context: it clarifies the XOR relationship between code and filepath, specifies supported extensions (.py/.txt), and explains the default behavior for output_dir. This goes beyond mere schema repetition.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action (转换/convert), source format (聚宽/JoinQuant), and target format (Ptrade), with no ambiguity. It effectively communicates the tool's singular purpose without needing sibling differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clarifies the input mutual exclusivity ('可以直接输入代码字符串,或提供文件路径') and supported file formats ('支持.py/.txt等格式'). While it lacks explicit 'when not to use' guidance, this is acceptable given no sibling tools exist for alternative conversion paths.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/guangxiangdebizi/Quant2Ptrader-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server