首页 > web前端 > js教程 > 正文

使用 Node.js、Express 和 OpenAI API 构建高质量股票报告生成器

Barbara Streisand
发布: 2024-11-04 14:54:43
原创
621 人浏览过

Building a High-Quality Stock Report Generator with Node.js, Express, and OpenAI API

在本文中,我们将深入研究使用 Node.js、Express 和 OpenAI API 创建专业级股票报告生成器。我们的重点是编写高质量、可维护的代码,同时保留 OpenAI API 交互中使用的提示消息的完整性。该应用程序将获取股票数据,执行情绪和行业分析,并生成全面的投资报告。

目录

  1. 项目概况
  2. 设置环境
  3. 创建 Express 服务器
  4. 获取和处理数据
  5. 与 OpenAI API 集成
  6. 生成最终报告
  7. 测试应用程序
  8. 结论

项目概况

我们的目标是构建一个 API 端点,为给定的股票代码生成详细的投资报告。该报告将包括:

  • 公司概况
  • 财务表现
  • 管理层讨论与分析(MDA)
  • 情感分析
  • 行业分析
  • 风险与机遇
  • 投资建议

我们将从外部API获取股票数据,并使用OpenAI API进行高级分析,确保提示信息被准确保留。

设置环境

先决条件

  • Node.js 安装在您的计算机上
  • OpenAI API 密钥(如果您没有,请在 OpenAI 注册)

初始化项目

创建一个新目录并初始化 Node.js 项目:

mkdir stock-report-generator
cd stock-report-generator
npm init -y
登录后复制
登录后复制
登录后复制

安装必要的依赖项:

npm install express axios
登录后复制
登录后复制

设置项目结构:

mkdir routes utils data
touch app.js routes/report.js utils/helpers.js
登录后复制
登录后复制

创建 Express 服务器

设置 app.js

// app.js
const express = require('express');
const reportRouter = require('./routes/report');

const app = express();

app.use(express.json());
app.use('/api', reportRouter);

const PORT = process.env.PORT || 3000;

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
登录后复制
登录后复制
  • Express 初始化:导入 Express 并初始化应用程序。
  • 中间件:使用express.json()解析JSON请求体。
  • 路由:将报告路由器挂载到/api路径上。
  • 服务器监听:在指定端口启动服务器。

获取和处理数据

创建辅助函数

在 utils/helpers.js 中,我们将定义用于数据获取和处理的实用函数。

mkdir stock-report-generator
cd stock-report-generator
npm init -y
登录后复制
登录后复制
登录后复制
  • getLastYearDates:计算上一年的开始和结束日期。
  • objectToString:将对象转换为可读字符串,不包括指定的键。
  • fetchData:处理对外部 API 的 GET 请求,返回数据或默认值。
  • readLocalJson:从本地 JSON 文件读取数据。

实施股票数据获取

在routes/report.js中,定义获取股票数据的函数。

npm install express axios
登录后复制
登录后复制
  • fetchStockData:并发获取多个数据点并处理结果。
  • 数据处理:格式化和转换数据以供后续使用。
  • 错误处理:记录错误并重新抛出它们以进行更高级别的处理。

与 OpenAI API 集成

OpenAI API交互功能

mkdir routes utils data
touch app.js routes/report.js utils/helpers.js
登录后复制
登录后复制
  • analyzeWithOpenAI:处理与 OpenAI API 的通信。
  • API配置:设置模型、温度等参数。
  • 错误处理:记录并抛出上游处理的错误。

执行情感分析

// app.js
const express = require('express');
const reportRouter = require('./routes/report');

const app = express();

app.use(express.json());
app.use('/api', reportRouter);

const PORT = process.env.PORT || 3000;

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
登录后复制
登录后复制
  • performSentimentAnalysis:构造提示消息并调用OpenAI API进行分析。
  • 提示设计:确保提示信息清晰并包含必要的上下文。

行业分析

// utils/helpers.js
const axios = require('axios');
const fs = require('fs');
const path = require('path');

const BASE_URL = 'https://your-data-api.com'; // Replace with your actual data API

/**
 * Get the start and end dates for the last year.
 * @returns {object} - An object containing `start` and `end` dates.
 */
function getLastYearDates() {
  const now = new Date();
  const end = now.toISOString().split('T')[0];
  now.setFullYear(now.getFullYear() - 1);
  const start = now.toISOString().split('T')[0];
  return { start, end };
}

/**
 * Convert an object to a string, excluding specified keys.
 * @param {object} obj - The object to convert.
 * @param {string[]} excludeKeys - Keys to exclude.
 * @returns {string} - The resulting string.
 */
function objectToString(obj, excludeKeys = []) {
  return Object.entries(obj)
    .filter(([key]) => !excludeKeys.includes(key))
    .map(([key, value]) => `${key}: ${value}`)
    .join('\n');
}

/**
 * Fetch data from a specified endpoint with given parameters.
 * @param {string} endpoint - API endpoint.
 * @param {object} params - Query parameters.
 * @param {any} defaultValue - Default value if the request fails.
 * @returns {Promise<any>} - The fetched data or default value.
 */
async function fetchData(endpoint, params = {}, defaultValue = null) {
  try {
    const response = await axios.get(`${BASE_URL}${endpoint}`, { params });
    return response.data || defaultValue;
  } catch (error) {
    console.error(`Error fetching data from ${endpoint}:`, error.message);
    return defaultValue;
  }
}

/**
 * Read data from a local JSON file.
 * @param {string} fileName - Name of the JSON file.
 * @returns {any} - The parsed data.
 */
function readLocalJson(fileName) {
  const filePath = path.join(__dirname, '../data', fileName);
  const data = fs.readFileSync(filePath, 'utf-8');
  return JSON.parse(data);
}

module.exports = {
  fetchData,
  objectToString,
  getLastYearDates,
  readLocalJson,
};
登录后复制
  • analyzeIndustry:与情绪分析类似,但专注于更广泛的行业背景。
  • 提示保留:保持原始提示信息的完整性。

生成最终报告

编译所有数据

// routes/report.js
const express = require('express');
const {
  fetchData,
  objectToString,
  getLastYearDates,
  readLocalJson,
} = require('../utils/helpers');

const router = express.Router();

/**
 * Fetches stock data including historical prices, financials, MDA, and main business info.
 * @param {string} ticker - Stock ticker symbol.
 * @returns {Promise<object>} - An object containing all fetched data.
 */
async function fetchStockData(ticker) {
  try {
    const dates = getLastYearDates();
    const [historicalData, financialData, mdaData, businessData] = await Promise.all([
      fetchData('/stock_zh_a_hist', {
        symbol: ticker,
        period: 'weekly',
        start_date: dates.start,
        end_date: dates.end,
      }, []),

      fetchData('/stock_financial_benefit_ths', {
        code: ticker,
        indicator: '按年度',
      }, [{}]),

      fetchData('/stock_mda', { code: ticker }, []),

      fetchData('/stock_main_business', { code: ticker }, []),
    ]);

    const hist = historicalData[historicalData.length - 1];
    const currentPrice = (hist ? hist['开盘'] : 'N/A') + ' CNY';
    const historical = historicalData
      .map((item) => objectToString(item, ['股票代码']))
      .join('\n----------\n');

    const zsfzJson = readLocalJson('zcfz.json');
    const balanceSheet = objectToString(zsfzJson.find((item) => item['股票代码'] === ticker));

    const financial = objectToString(financialData[0]);

    const mda = mdaData.map(item => `${item['报告期']}\n${item['内容']}`).join('\n-----------\n');

    const mainBusiness = businessData.map(item =>
      `主营业务: ${item['主营业务']}\n产品名称: ${item['产品名称']}\n产品类型: ${item['产品类型']}\n经营范围: ${item['经营范围']}`
    ).join('\n-----------\n');

    return { currentPrice, historical, balanceSheet, mda, mainBusiness, financial };
  } catch (error) {
    console.error('Error fetching stock data:', error.message);
    throw error;
  }
}
登录后复制
  • 提供最终分析:精心制作提示消息,合并所有收集的数据。
  • 提示完整性:确保原始提示消息不被更改或损坏。

测试应用程序

定义路由处理程序

在routes/report.js中添加路由处理程序:

const axios = require('axios');

const OPENAI_API_KEY = 'your-openai-api-key'; // Replace with your OpenAI API key

/**
 * Interacts with the OpenAI API to get completion results.
 * @param {array} messages - Array of messages, including system prompts and user messages.
 * @returns {Promise<string>} - The AI's response.
 */
async function analyzeWithOpenAI(messages) {
  try {
    const headers = {
      'Authorization': `Bearer ${OPENAI_API_KEY}`,
      'Content-Type': 'application/json',
    };
    const requestData = {
      model: 'gpt-4',
      temperature: 0.3,
      messages: messages,
    };

    const response = await axios.post(
      'https://api.openai.com/v1/chat/completions',
      requestData,
      { headers }
    );
    return response.data.choices[0].message.content.trim();
  } catch (error) {
    console.error('Error fetching analysis from OpenAI:', error.message);
    throw error;
  }
}
登录后复制
  • 输入验证:检查是否提供了股票代码。
  • 数据收集:同时获取股票数据并执行分析。
  • 错误处理:记录错误并在失败时发送 500 响应。

启动服务器

确保你的app.js和routes/report.js设置正确,然后启动服务器:

/**
 * Performs sentiment analysis on news articles using the OpenAI API.
 * @param {string} ticker - Stock ticker symbol.
 * @returns {Promise<string>} - Sentiment analysis summary.
 */
async function performSentimentAnalysis(ticker) {
  const systemPrompt = `You are a sentiment analysis assistant. Analyze the sentiment of the given news articles for ${ticker} and provide a summary of the overall sentiment and any notable changes over time. Be measured and discerning. You are a skeptical investor.`;

  const tickerNewsResponse = await fetchData('/stock_news_specific', { code: ticker }, []);

  const newsText = tickerNewsResponse
    .map(item => `${item['文章来源']} Date: ${item['发布时间']}\n${item['新闻内容']}`)
    .join('\n----------\n');

  const messages = [
    { role: 'system', content: systemPrompt },
    {
      role: 'user',
      content: `News articles for ${ticker}:\n${newsText || 'N/A'}\n----\nProvide a summary of the overall sentiment and any notable changes over time.`,
    },
  ];

  return await analyzeWithOpenAI(messages);
}
登录后复制

发送测试请求

使用curl或Postman发送POST请求:

mkdir stock-report-generator
cd stock-report-generator
npm init -y
登录后复制
登录后复制
登录后复制
  • 响应:服务器应返回一个包含生成报告的 JSON 对象。

结论

我们构建了一个高质量的库存报告生成器,具有以下功能:

  • 从外部 API 获取和处理股票数据
  • 使用 OpenAI API 执行高级分析
  • 生成全面的投资报告,同时确保提示信息的完整性。

在整个开发过程中,我们专注于编写专业、可维护的代码,并提供了详细的解释和注释。

实施的最佳实践

  • 模块化代码结构:函数被模块化以实现可重用性和清晰度。
  • 异步操作:使用async/await和Promise.all进行高效的异步编程。
  • 错误处理:全面的 try-catch 块和错误消息。
  • API 抽象:分离的 API 交互逻辑,以提高可维护性。
  • 提示工程:为OpenAI API精心设计提示消息,以达到期望的输出。
  • 输入验证:检查所需的输入参数,以防止不必要的错误。
  • 代码文档:添加了JSDoc注释,以便更好地理解和维护。

下一步

  • 缓存:实现缓存机制以减少冗余的API调用。
  • 身份验证:通过身份验证和速率限制来保护 API 端点。
  • 前端开发:构建用于与应用程序交互的用户界面。
  • 其他分析:纳入技术分析或其他财务模型。

参考

  • Node.js 文档
  • Express.js 文档
  • Axios 文档
  • OpenAI API 参考

免责声明:此应用程序仅用于教育目的。确保遵守所有 API 服务条款并适当处理敏感数据。

以上是使用 Node.js、Express 和 OpenAI API 构建高质量股票报告生成器的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板