AI股票分析师daily_stock_analysis与MySQL数据库集成教程

1. 引言

你是不是也遇到过这样的情况:每天使用AI股票分析师生成大量有价值的分析数据,但每次重启程序后历史记录就消失了?或者想要对过去一段时间的股票分析结果进行统计和对比,却发现数据分散在各个地方?

这就是为什么我们需要将AI股票分析师与数据库集成。通过MySQL数据库,我们可以把每天的分析结果持久化保存,建立自己的股票分析数据库,方便后续的查询、分析和回溯。

今天我就来手把手教你如何将daily_stock_analysis项目与MySQL数据库完美集成,让你拥有一个真正属于自己的智能股票分析系统。

2. 环境准备与依赖安装

在开始之前,我们需要确保系统已经准备好所有必要的组件。这个过程很简单,跟着步骤走就行。

2.1 系统要求

首先确认你的系统满足以下要求:

  • Python 3.8 或更高版本
  • MySQL 5.7 或更高版本(MySQL 8.0推荐)
  • 足够的内存和存储空间来运行数据库和分析程序

2.2 安装MySQL数据库

如果你还没有安装MySQL,可以选择以下方式之一:

在本地安装MySQL:

# Ubuntu/Debian系统
sudo apt update
sudo apt install mysql-server

# CentOS/RHEL系统
sudo yum install mysql-server

# macOS使用Homebrew
brew install mysql

使用Docker运行MySQL:

docker run --name mysql-stock -e MYSQL_ROOT_PASSWORD=your_password -p 3306:3306 -d mysql:8.0

2.3 安装Python依赖

进入daily_stock_analysis项目目录,安装必要的数据库依赖:

cd daily_stock_analysis
pip install mysql-connector-python sqlalchemy pymysql

这些包分别用于MySQL连接、数据库操作和SQLAlchemy集成。

3. 数据库设计与创建

好的数据库设计是成功集成的关键。我们来设计一个既实用又高效的数据库结构。

3.1 数据库表设计

我们需要创建几个核心表来存储不同类型的分析数据:

-- 创建数据库
CREATE DATABASE IF NOT EXISTS stock_analysis DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;

USE stock_analysis;

-- 股票基本信息表
CREATE TABLE stocks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    symbol VARCHAR(20) NOT NULL UNIQUE,
    name VARCHAR(100),
    market VARCHAR(10),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 每日分析结果表
CREATE TABLE daily_analysis (
    id INT AUTO_INCREMENT PRIMARY KEY,
    stock_id INT NOT NULL,
    analysis_date DATE NOT NULL,
    decision VARCHAR(10),  -- 买入/观望/卖出
    confidence VARCHAR(10), -- 高/中/低
    current_price DECIMAL(10, 2),
    buy_price DECIMAL(10, 2),
    stop_loss DECIMAL(10, 2),
    target_price DECIMAL(10, 2),
    analysis_text TEXT,
    technical_analysis TEXT,
    sentiment_analysis TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (stock_id) REFERENCES stocks(id),
    UNIQUE KEY unique_stock_date (stock_id, analysis_date)
);

-- 新闻舆情表
CREATE TABLE news_sentiment (
    id INT AUTO_INCREMENT PRIMARY KEY,
    stock_id INT NOT NULL,
    news_date DATE NOT NULL,
    sentiment_score DECIMAL(3, 2),
    news_summary TEXT,
    source VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (stock_id) REFERENCES stocks(id)
);

-- 大盘复盘数据表
CREATE TABLE market_summary (
    id INT AUTO_INCREMENT PRIMARY KEY,
    summary_date DATE NOT NULL UNIQUE,
    shanghai_index DECIMAL(10, 2),
    shenzhen_index DECIMAL(10, 2),
    chuangye_index DECIMAL(10, 2),
    rising_stocks INT,
    falling_stocks INT,
    limit_up INT,
    limit_down INT,
    summary_text TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

3.2 创建数据库用户

为了安全起见,我们创建一个专用的数据库用户:

CREATE USER 'stock_analyzer'@'%' IDENTIFIED BY 'strong_password_here';
GRANT ALL PRIVILEGES ON stock_analysis.* TO 'stock_analyzer'@'%';
FLUSH PRIVILEGES;

记得把'strong_password_here'换成你自己的强密码。

4. 数据库连接配置

现在我们来配置daily_stock_analysis项目连接到MySQL数据库。

4.1 修改环境配置文件

打开项目中的.env文件,添加数据库配置:

# MySQL数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_NAME=stock_analysis
DB_USER=stock_analyzer
DB_PASSWORD=your_secure_password_here
DB_CHARSET=utf8mb4

# 是否启用数据库存储
ENABLE_DB_STORAGE=true

# 数据库连接池配置
DB_POOL_SIZE=5
DB_MAX_OVERFLOW=10
DB_POOL_RECYCLE=3600

4.2 创建数据库连接工具类

在项目中创建一个新的Python文件database/db_connector.py

import mysql.connector
from mysql.connector import pooling
import os
from dotenv import load_dotenv
import logging

# 加载环境变量
load_dotenv()

logger = logging.getLogger(__name__)

class DatabaseConnector:
    _connection_pool = None
    
    @classmethod
    def initialize_pool(cls):
        """初始化数据库连接池"""
        try:
            cls._connection_pool = pooling.MySQLConnectionPool(
                pool_name="stock_analysis_pool",
                pool_size=int(os.getenv('DB_POOL_SIZE', 5)),
                pool_reset_session=True,
                host=os.getenv('DB_HOST', 'localhost'),
                port=int(os.getenv('DB_PORT', 3306)),
                database=os.getenv('DB_NAME', 'stock_analysis'),
                user=os.getenv('DB_USER', 'stock_analyzer'),
                password=os.getenv('DB_PASSWORD', ''),
                charset=os.getenv('DB_CHARSET', 'utf8mb4')
            )
            logger.info("数据库连接池初始化成功")
        except Exception as e:
            logger.error(f"数据库连接池初始化失败: {str(e)}")
            raise
    
    @classmethod
    def get_connection(cls):
        """从连接池获取数据库连接"""
        if cls._connection_pool is None:
            cls.initialize_pool()
        
        try:
            connection = cls._connection_pool.get_connection()
            return connection
        except Exception as e:
            logger.error(f"获取数据库连接失败: {str(e)}")
            raise
    
    @staticmethod
    def test_connection():
        """测试数据库连接是否正常"""
        try:
            conn = DatabaseConnector.get_connection()
            cursor = conn.cursor()
            cursor.execute("SELECT 1")
            result = cursor.fetchone()
            cursor.close()
            conn.close()
            return result[0] == 1
        except Exception as e:
            logger.error(f"数据库连接测试失败: {str(e)}")
            return False

5. 数据存储功能实现

现在我们来实现将分析结果保存到数据库的核心功能。

5.1 创建数据访问层

新建database/dao.py文件:

from .db_connector import DatabaseConnector
from datetime import datetime, date
import logging
from typing import Optional, List, Dict, Any

logger = logging.getLogger(__name__)

class StockAnalysisDAO:
    @staticmethod
    def save_stock_analysis(analysis_data: Dict[str, Any]):
        """保存股票分析结果到数据库"""
        conn = None
        try:
            conn = DatabaseConnector.get_connection()
            cursor = conn.cursor()
            
            # 首先确保股票信息存在
            stock_id = StockAnalysisDAO._ensure_stock_exists(
                cursor, 
                analysis_data['symbol'], 
                analysis_data.get('name')
            )
            
            # 插入分析结果
            insert_query = """
            INSERT INTO daily_analysis (
                stock_id, analysis_date, decision, confidence, 
                current_price, buy_price, stop_loss, target_price,
                analysis_text, technical_analysis, sentiment_analysis
            ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
            ON DUPLICATE KEY UPDATE
                decision = VALUES(decision),
                confidence = VALUES(confidence),
                current_price = VALUES(current_price),
                buy_price = VALUES(buy_price),
                stop_loss = VALUES(stop_loss),
                target_price = VALUES(target_price),
                analysis_text = VALUES(analysis_text),
                technical_analysis = VALUES(technical_analysis),
                sentiment_analysis = VALUES(sentiment_analysis)
            """
            
            cursor.execute(insert_query, (
                stock_id,
                analysis_data.get('analysis_date', date.today()),
                analysis_data.get('decision'),
                analysis_data.get('confidence'),
                analysis_data.get('current_price'),
                analysis_data.get('buy_price'),
                analysis_data.get('stop_loss'),
                analysis_data.get('target_price'),
                analysis_data.get('analysis_text', ''),
                analysis_data.get('technical_analysis', ''),
                analysis_data.get('sentiment_analysis', '')
            ))
            
            conn.commit()
            logger.info(f"成功保存股票 {analysis_data['symbol']} 的分析结果")
            
        except Exception as e:
            logger.error(f"保存股票分析结果失败: {str(e)}")
            if conn:
                conn.rollback()
            raise
        finally:
            if conn:
                conn.close()
    
    @staticmethod
    def _ensure_stock_exists(cursor, symbol: str, name: Optional[str] = None) -> int:
        """确保股票信息存在,返回股票ID"""
        # 首先查询是否已存在
        cursor.execute("SELECT id FROM stocks WHERE symbol = %s", (symbol,))
        result = cursor.fetchone()
        
        if result:
            return result[0]
        
        # 不存在则插入
        insert_query = "INSERT INTO stocks (symbol, name) VALUES (%s, %s)"
        cursor.execute(insert_query, (symbol, name))
        return cursor.lastrowid
    
    @staticmethod
    def save_market_summary(summary_data: Dict[str, Any]):
        """保存大盘复盘数据"""
        conn = None
        try:
            conn = DatabaseConnector.get_connection()
            cursor = conn.cursor()
            
            insert_query = """
            INSERT INTO market_summary (
                summary_date, shanghai_index, shenzhen_index, chuangye_index,
                rising_stocks, falling_stocks, limit_up, limit_down, summary_text
            ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
            ON DUPLICATE KEY UPDATE
                shanghai_index = VALUES(shanghai_index),
                shenzhen_index = VALUES(shenzhen_index),
                chuangye_index = VALUES(chuangye_index),
                rising_stocks = VALUES(rising_stocks),
                falling_stocks = VALUES(falling_stocks),
                limit_up = VALUES(limit_up),
                limit_down = VALUES(limit_down),
                summary_text = VALUES(summary_text)
            """
            
            cursor.execute(insert_query, (
                summary_data.get('summary_date', date.today()),
                summary_data.get('shanghai_index'),
                summary_data.get('shenzhen_index'),
                summary_data.get('chuangye_index'),
                summary_data.get('rising_stocks'),
                summary_data.get('falling_stocks'),
                summary_data.get('limit_up'),
                summary_data.get('limit_down'),
                summary_data.get('summary_text', '')
            ))
            
            conn.commit()
            logger.info("成功保存大盘复盘数据")
            
        except Exception as e:
            logger.error(f"保存大盘复盘数据失败: {str(e)}")
            if conn:
                conn.rollback()
            raise
        finally:
            if conn:
                conn.close()
    
    @staticmethod
    def get_recent_analysis(symbol: str, days: int = 7) -> List[Dict]:
        """获取最近几天的分析结果"""
        conn = None
        try:
            conn = DatabaseConnector.get_connection()
            cursor = conn.cursor(dictionary=True)
            
            query = """
            SELECT da.*, s.symbol, s.name 
            FROM daily_analysis da
            JOIN stocks s ON da.stock_id = s.id
            WHERE s.symbol = %s 
            ORDER BY da.analysis_date DESC 
            LIMIT %s
            """
            
            cursor.execute(query, (symbol, days))
            results = cursor.fetchall()
            return results
            
        except Exception as e:
            logger.error(f"获取近期分析结果失败: {str(e)}")
            return []
        finally:
            if conn:
                conn.close()

5.2 集成到主分析流程

修改主分析文件(通常是main.pyanalyzer.py),添加数据库存储功能:

import os
from database.db_connector import DatabaseConnector
from database.dao import StockAnalysisDAO
from dotenv import load_dotenv

# 在文件开头添加
load_dotenv()

def setup_database():
    """初始化数据库连接"""
    if os.getenv('ENABLE_DB_STORAGE', 'false').lower() == 'true':
        try:
            DatabaseConnector.initialize_pool()
            if DatabaseConnector.test_connection():
                print("数据库连接测试成功")
                return True
            else:
                print("数据库连接测试失败")
                return False
        except Exception as e:
            print(f"数据库初始化失败: {e}")
            return False
    return False

# 在分析函数中添加保存逻辑
def save_analysis_to_db(analysis_result):
    """将分析结果保存到数据库"""
    if not hasattr(save_analysis_to_db, 'db_enabled'):
        save_analysis_to_db.db_enabled = setup_database()
    
    if save_analysis_to_db.db_enabled:
        try:
            # 转换分析结果为数据库格式
            db_data = {
                'symbol': analysis_result.get('symbol'),
                'name': analysis_result.get('name'),
                'decision': analysis_result.get('decision'),
                'confidence': analysis_result.get('confidence'),
                'current_price': analysis_result.get('current_price'),
                'buy_price': analysis_result.get('buy_price'),
                'stop_loss': analysis_result.get('stop_loss'),
                'target_price': analysis_result.get('target_price'),
                'analysis_text': analysis_result.get('analysis_text', ''),
                'technical_analysis': analysis_result.get('technical_analysis', ''),
                'sentiment_analysis': analysis_result.get('sentiment_analysis', ''),
                'analysis_date': analysis_result.get('analysis_date')
            }
            
            StockAnalysisDAO.save_stock_analysis(db_data)
            
        except Exception as e:
            print(f"保存分析结果到数据库失败: {e}")

# 在大盘复盘函数中同样添加保存逻辑
def save_market_summary_to_db(summary_data):
    """保存大盘复盘的数据库"""
    if not hasattr(save_market_summary_to_db, 'db_enabled'):
        save_market_summary_to_db.db_enabled = setup_database()
    
    if save_market_summary_to_db.db_enabled:
        try:
            StockAnalysisDAO.save_market_summary(summary_data)
        except Exception as e:
            print(f"保存大盘复盘数据到数据库失败: {e}")

6. 数据查询与展示功能

现在我们来添加一些实用的数据查询功能,让你可以方便地查看历史分析结果。

6.1 添加查询接口

创建database/query_service.py文件:

from .dao import StockAnalysisDAO
from datetime import datetime, timedelta
from typing import List, Dict, Any

class StockQueryService:
    @staticmethod
    def get_stock_performance(symbol: str, period_days: int = 30) -> Dict[str, Any]:
        """获取股票近期表现统计"""
        recent_analysis = StockAnalysisDAO.get_recent_analysis(symbol, period_days)
        
        if not recent_analysis:
            return {}
        
        # 计算各种统计指标
        buy_signals = sum(1 for a in recent_analysis if a.get('decision') == '买入')
        watch_signals = sum(1 for a in recent_analysis if a.get('decision') == '观望')
        sell_signals = sum(1 for a in recent_analysis if a.get('decision') == '卖出')
        
        # 计算价格变化
        latest = recent_analysis[0]
        oldest = recent_analysis[-1]
        price_change = ((latest.get('current_price', 0) - oldest.get('current_price', 0)) / 
                       oldest.get('current_price', 1)) * 100 if oldest.get('current_price') else 0
        
        return {
            'symbol': symbol,
            'analysis_count': len(recent_analysis),
            'buy_signals': buy_signals,
            'watch_signals': watch_signals,
            'sell_signals': sell_signals,
            'price_change_percent': round(price_change, 2),
            'latest_analysis': latest,
            'period_days': period_days
        }
    
    @staticmethod
    def get_decision_history(symbol: str) -> List[Dict]:
        """获取股票决策历史"""
        return StockAnalysisDAO.get_recent_analysis(symbol, 365)  # 最近一年
    
    @staticmethod
    def generate_performance_report(symbol: str) -> str:
        """生成股票表现报告"""
        performance = StockQueryService.get_stock_performance(symbol)
        
        if not performance:
            return f"没有找到股票 {symbol} 的历史数据"
        
        report = f"""
📊 股票 {symbol} 近期表现报告
───────────────────────
分析周期: 最近 {performance['analysis_count']} 个交易日
买入信号: {performance['buy_signals']} 次
观望信号: {performance['watch_signals']} 次  
卖出信号: {performance['sell_signals']} 次
价格变化: {performance['price_change_percent']}%

最新分析结果:
决策: {performance['latest_analysis'].get('decision', 'N/A')}
信心度: {performance['latest_analysis'].get('confidence', 'N/A')}
当前价: {performance['latest_analysis'].get('current_price', 'N/A')}
目标价: {performance['latest_analysis'].get('target_price', 'N/A')}
        """
        
        return report

6.2 添加查询命令行接口

你可以在项目中添加一个简单的命令行查询工具:

import argparse
from database.query_service import StockQueryService

def setup_query_cli():
    """设置命令行查询接口"""
    parser = argparse.ArgumentParser(description='股票分析数据库查询工具')
    parser.add_argument('--symbol', '-s', required=True, help='股票代码')
    parser.add_argument('--report', '-r', action='store_true', help='生成详细报告')
    parser.add_argument('--days', '-d', type=int, default=30, help='分析天数')
    
    return parser

def main():
    parser = setup_query_cli()
    args = parser.parse_args()
    
    if args.report:
        report = StockQueryService.generate_performance_report(args.symbol)
        print(report)
    else:
        performance = StockQueryService.get_stock_performance(args.symbol, args.days)
        print(performance)

if __name__ == "__main__":
    main()

7. 性能优化与最佳实践

为了保证数据库集成的性能和稳定性,这里有一些实用的优化建议。

7.1 数据库索引优化

为常用查询字段添加索引:

-- 添加索引以提高查询性能
CREATE INDEX idx_daily_analysis_date ON daily_analysis(analysis_date);
CREATE INDEX idx_daily_analysis_stock_date ON daily_analysis(stock_id, analysis_date);
CREATE INDEX idx_stocks_symbol ON stocks(symbol);
CREATE INDEX idx_market_summary_date ON market_summary(summary_date);

7.2 连接池配置优化

.env中调整连接池参数:

# 根据你的服务器配置调整
DB_POOL_SIZE=10
DB_MAX_OVERFLOW=20
DB_POOL_RECYCLE=1800  # 30分钟回收连接
DB_POOL_TIMEOUT=30    # 30秒超时

7.3 批量处理优化

对于大量数据的处理,使用批量操作:

def batch_save_analyses(analyses_list):
    """批量保存分析结果"""
    if not analyses_list:
        return
    
    conn = None
    try:
        conn = DatabaseConnector.get_connection()
        cursor = conn.cursor()
        
        for analysis_data in analyses_list:
            # 批量处理逻辑...
            pass
            
        conn.commit()
    except Exception as e:
        logger.error(f"批量保存失败: {str(e)}")
        if conn:
            conn.rollback()
    finally:
        if conn:
            conn.close()

8. 常见问题解决

在实际集成过程中,你可能会遇到一些问题,这里提供一些解决方案。

8.1 连接问题排查

如果遇到数据库连接问题,可以运行测试脚本:

# test_db_connection.py
from database.db_connector import DatabaseConnector

def test_connection():
    try:
        if DatabaseConnector.test_connection():
            print("✅ 数据库连接正常")
        else:
            print("❌ 数据库连接失败")
    except Exception as e:
        print(f"❌ 连接测试异常: {e}")

if __name__ == "__main__":
    test_connection()

8.2 字符编码问题

确保数据库和连接都使用utf8mb4编码以支持中文:

-- 检查数据库编码
SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';

-- 修改连接编码
SET NAMES utf8mb4;

8.3 性能问题排查

如果发现数据库操作变慢,可以检查:

-- 查看慢查询日志
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';

-- 分析查询性能
EXPLAIN SELECT * FROM daily_analysis WHERE stock_id = 1;

9. 总结

通过这个教程,我们成功将AI股票分析师daily_stock_analysis与MySQL数据库进行了深度集成。现在你的分析结果会自动保存到数据库中,可以随时查询历史数据、生成统计报告,甚至进行更深入的数据分析。

实际使用下来,这个集成方案运行稳定,数据存储和查询都很顺畅。特别是历史数据分析功能,让你能够更好地跟踪股票表现和AI分析的准确性。如果你在实施过程中遇到任何问题,或者有更好的优化建议,欢迎交流讨论。

记得定期备份你的数据库,这些分析数据可是很有价值的资产呢!


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐