一个基于Python的AI行情分析工具

本来是写给家里长辈炒股用的小玩具,既然有佬友想要就开源出来吧。
随手搓的,代码质量比较糟糕,不要嘲笑我 :tieba_025:
项目链接ashare-llm-analyst
效果预览图

132 Likes

顺便求个star :tieba_087:

2 Likes

所以有帮助吗,赚了多少 :money_mouth_face:

2 Likes

拿去给家里长辈用的,我不了解 :tieba_022:

2 Likes

star了

2 Likes

感谢佬友支持 :tieba_025:

1 Like

刚看了眼main.py里,很多. ,中文标点,是敲错了吗?还是上传的时候出了问题?

4 Likes

有用吗,有没有大佬实践一些的,真是亏麻了已经 :sob:

3 Likes

佬友,我克隆到 VSCode 里边,发现 main.py 里面不少小问题。

2 Likes

嘶,我看看

1 Like

已star,感谢佬友分享
代码里确实不少中文字符

3 Likes

推荐你用cline,把代码都过一遍

4 Likes

找到原因了,我在网页端编辑了一次main的内容,然后代码就被浏览器插件污染了 :tieba_087:

4 Likes

感谢佬友分享~

3 Likes

感谢佬友的分享~

3 Likes

学习了大佬

3 Likes

修改几个地方可以兼容openai格式:

main.py class StockAnalyzer 的初始化函数:

def __init__(
        self,
        _stock_info,
        deepseek_api_key,
        deepseek_base_url,
        model,
        count=120,
    ):
        """
        初始化股票分析器

        Args:
            _stock_info: 股票信息字典
            count: 获取的数据条数
            deepseek_api_key: Deepseek API密钥
            deepseek_base_url: Deepseek API基础URL
        """
        self.stock_codes = list(_stock_info.values())
        self.stock_names = _stock_info
        self.count = count
        self.data = {}
        plt.rcParams["font.sans-serif"] = ["SimHei"]
        plt.rcParams["axes.unicode_minus"] = False

        # 初始化Deepseek分析器
        self.deepseek = (
            DeepseekAnalyzer(deepseek_api_key, deepseek_base_url, model=model)
            if deepseek_api_key
            else None
        )

Deepseek.py 的 class DeepseekAnalyzer

class DeepseekAnalyzer:
    """使用 OpenAI SDK 与 Deepseek API 交互的类"""

    def __init__(
        self,
        api_key: str,
        base_url: str ,
        model: str ,
    ):
        """
        初始化 Deepseek 分析器

        Args:
            api_key (str): Deepseek API 密钥
            base_url (str): Deepseek API 基础 URL
        """
        self.client = OpenAI(api_key=api_key, base_url=base_url)
        self.model = model

    def request_analysis(
        self, df: pd.DataFrame, technical_indicators: pd.DataFrame
    ) -> Optional[Dict[str, Any]]:
        """
        向 Deepseek API 发送分析请求

        Args:
            df (pd.DataFrame): 原始股票数据
            technical_indicators (pd.DataFrame): 技术指标数据

        Returns:
            Optional[Dict[str, Any]]: API 响应的分析结果
        """
        try:
            # 准备数据
            print("开始准备数据...")
            data_str = _format_data_for_prompt(df, technical_indicators)
            print(f"数据准备完成,数据长度: {len(data_str)}")

            # 构建消息
            print("构建API请求消息...")
            messages = [
                {"role": "system", "content": _create_system_prompt()},
                {
                    "role": "user",
                    "content": f"请分析以下股票数据并给出专业的分析意见:\n{data_str}",
                },
            ]
            print(f"消息构建完成,系统提示词长度: {len(messages[0]['content'])}")
            print(f"用户消息长度: {len(messages[1]['content'])}")

            # 发送请求
            print("开始发送API请求...")
            try:
                response = self.client.chat.completions.create(
                    model=self.model,
                    messages=messages,
                    temperature=1.0,
                    stream=False,
                    max_tokens=10000,
                )
                print("API请求发送成功")
            except Exception as api_e:
                # 检查是否是空响应导致的JSON解析错误
                if str(api_e).startswith("Expecting value: line 1 column 1 (char 0)"):
                    print("API返回空响应,服务器可能繁忙")
                    raise APIBusyError("API服务器繁忙,返回空响应") from api_e
                print(f"API请求发送失败: {str(api_e)}")
                raise  # 重新抛出其他类型的异常

            # 记录原始响应以便调试
            print("API 原始响应类型:", type(response))
            print("API 原始响应内容:", response)

            # 检查响应内容
            if not response:
                print("API返回空响应")
                return format_analysis_result({})

            if not hasattr(response, "choices"):
                print(f"API响应缺少choices属性,响应结构: {dir(response)}")
                return format_analysis_result({})

            if not response.choices:
                print("API响应的choices为空")
                return format_analysis_result({})

            # 解析响应
            try:
                analysis_text = response.choices[0].message.content
                print("成功获取分析文本内容")
                print("分析文本:", analysis_text)
            except Exception as text_e:
                print(f"获取分析文本失败: {str(text_e)}")
                raise

            # 将文本响应组织成结构化数据
            print("开始解析分析文本...")
            result = _parse_analysis_response(analysis_text)
            print("分析文本解析完成")
            return result

        except APIBusyError as be:  # 处理API繁忙异常
            print(f"=== API繁忙错误 ===")
            print(f"错误详情: {str(be)}")
            print(f"错误类型: {type(be)}")
            return format_analysis_result({})
        except json.JSONDecodeError as je:
            print(f"=== JSON解析错误 ===")
            print(f"错误详情: {str(je)}")
            print(f"错误类型: {type(je)}")
            print(f"错误位置: {je.pos}")
            print(f"错误行列: 行 {je.lineno}, 列 {je.colno}")
            print(f"错误的文档片段: {je.doc[:100] if je.doc else 'None'}")
            return format_analysis_result({})
        except openai.APITimeoutError as te:
            print(f"=== API超时错误 ===")
            print(f"错误详情: {str(te)}")
            print(f"错误类型: {type(te)}")
            return format_analysis_result({})
        except openai.APIConnectionError as ce:
            print(f"=== API连接错误 ===")
            print(f"错误详情: {str(ce)}")
            print(f"错误类型: {type(ce)}")
            return format_analysis_result({})
        except openai.APIError as ae:
            print(f"=== API错误 ===")
            print(f"错误详情: {str(ae)}")
            print(f"错误类型: {type(ae)}")
            return format_analysis_result({})
        except openai.RateLimitError as re:
            print(f"=== API频率限制错误 ===")
            print(f"错误详情: {str(re)}")
            print(f"错误类型: {type(re)}")
            return format_analysis_result({})
        except Exception as e:
            print(f"=== 未预期的错误 ===")
            print(f"错误详情: {str(e)}")
            print(f"错误类型: {type(e)}")
            print(f"错误追踪:")
            import traceback

            traceback.print_exc()
            return format_analysis_result({})

修改后的调用示例(在main.py的最下面)

if __name__ == "__main__":
    stock_info = {"上证指数": "sh000001"}
    base_url = "https://hq.sinajs.cn/v1" #一定要包含v1
    model = "gemini-2.0-flash"
    api_key = "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    analyzer = StockAnalyzer(stock_info, api_key, base_url, model)
    report_path = analyzer.run_analysis()
    print(f"分析报告已生成: {report_path}")
13 Likes

感谢大佬!

6 Likes

挺好的,一开始我就是用 openai的SDK去对接的,但是DS在繁忙时会持续返回空行来维持TCP连接,OAI的SDK捕获不到这种情况。折腾完catch error就懒得去调兼容了 :tieba_022:

3 Likes

建议改一个地方,数据不用直接生成html,而是存入到一个本地数据库里,然后再生成一个查询系统,可以用streamlit实现,去调用数据库的查询,数据的拖拽和查看更方便,而且可以设置多页,也可以多数据多维度对比。

9 Likes