我们利用上一篇构建AI工具(让AI给你写代码(9.1):引导AI根据输入的问题,并结合本地知识库的预存需求组成提示模板,生成代码并测试,保存),通过在本地知识库预制需求,然后通过提示引导的方式生成代码这种模式,构建稍微复杂一点的程序,即在上文的最后提到的,需要使用多个接口的代码。
我们的需求是计算给定股票及时间段,计算静态市盈率——按理说,可以用akshare的乐咕乐股stock_a_indicator_lg接口直接获取,但很不幸,这个接口暂时失效了…
所以我们来自行构建
我们使用比较简单的逻辑:即用当前的价格除以上一年年末的收益(EPS)
涉及两个akshare的接口stock_zh_a_hist获得历史行情,以及stock_yjbb_em业绩报表数据
主要说一下面临几个问题:
1 提示问题和本地库的内容匹配
我们使用的初始预设需求如下:
计算指定股票静态市盈率,可以使用akshare库,第一步.按股票代码(stock_code),使用stock_zh_a_hist(symbol=stock_code)接口,获取该股票历史行情; 第二步,遍历历史行情dataframe的日期,获取对应前一年的报表日期(report_date,按"YYYY1231"),再使用stock_yjbb_em(date=report_date)接口获得业绩报表数据,按股票代码筛选出每股收益,然后除以按该日期对应的股票收盘价(属性‘收盘‘),得到静态市盈率,在上述dataframe新增一列“静态市盈率”,并用print打印,代码格式为python...
提示问题如下:
计算指定股票静态市盈率,例如计算600150
但不幸的是,本地库匹配的结果确实匹配到需求
我们看一下AI的答复
raw_code:: 无关。提供的上下文没有直接涉及计算特定股票(如600150)的静态市盈率的具体实现代码,而是概述了计算静态市盈率的一般步骤和如何获取股票实时行情信息。
根据提示,我们换了一个提示问题:
请输入您的问题:请计算A股股票每个历史行情日期所对应的前一年度的静态市盈率,例如600276
raw_code:: 有关。
根据提供的上下文,可以设计如下Python代码片段来计算A股股票(以600276为例)每个历史行情日期所对应的前一年度的静态市盈率,使用akshare库:
‘’’ python…
总结:
我们采用了模板
CONTEXT_QA_TMPL = “”"
下面的信息({summary_prompt})是否有这个问题({message})有关,
如果你觉得无关请告诉我无法根据提供的上下文回答’{message}'这个问题,简要回答即可,
否则请根据{summary_prompt}对{message}的问题进行回答
“”"
相当于 从本地库匹配之后,再需要AI判断一次原始问题和本地匹配的内容是否相关,粗暴采取直接用分局的方式不可取,还是要提炼归纳主题作为问题才比较可靠。
2 具体执行逻辑合理化
我们继续看AI提供的代码:
import akshare as ak
import pandas as pd
from datetime import datetime, timedelta
# 设置股票代码和开始日期
stock_code = '600276'
start_date = '20230101'
# 获取股票历史行情数据
stock_hist_df = ak.stock_zh_a_hist(symbol=stock_code, adjust="qfq", start_date=start_date)
# 确保日期列是datetime类型以便处理
stock_hist_df['日期'] = pd.to_datetime(stock_hist_df['日期'])
# 初始化静态市盈率DataFrame
pe_ratio_df = pd.DataFrame(columns=['日期', '股价', '静态市盈率'])
# 遍历历史行情日期
for date in stock_hist_df['日期']:
# 计算前一年年末的报表日期(假设报表日期为每年12月31日)
report_date = (date - timedelta(days=365)).strftime('%Y1231')
# 获取报表数据
try:
report_df = ak.stock_yjbb_em(date=report_date)
# 筛选对应股票代码的每股收益
earning_per_share = report_df[report_df['股票代码'] == stock_code]['每股收益'].values[0]
except IndexError: # 如果没有找到对应日期的报告或者每股收益数据,跳过该日期
print(f"No report found for {report_date}, skipping...")
continue
# 计算静态市盈率
closing_price = stock_hist_df[stock_hist_df['日期'] == date]['收盘']
if not closing_price.empty:
pe_ratio = closing_price / earning_per_share
# 添加到结果DataFrame
pe_ratio_df = pd.concat([pe_ratio_df, pd.DataFrame({'日期': [date], '股价': [closing_price.values[0]], '静态市盈率': [pe_ratio.values[0]]})], ignore_index=True)
else:
print(f"No closing price found for {date}, skipping...")
# 打印结果
print(pe_ratio_df)
# 返回结果DataFrame
pe_ratio_df
这段代码首先获取指定股票从2023年1月1日开始的历史行情数据,然后对于每一天,计算前一年12月31日的每股收益,最后利用当天的收盘价计算静态市盈率并存储。请注意,实际运行时需要确保akshare库已正确安装且网络连接良好,同时,由于数据获取的限制,某些日期可能无法获取到完整的报表数据,代码中已经包含了基本的错误处理逻辑来应对这种情况。
83%|████████▎ | 20/24 [01:12<00:07, 1.80s/it]
实际上这个代码是不合理,即先通过stock_zh_a_hist获得历史行情,再遍历每天的收盘价,每次循环都要去获取一次stock_yjbb_em接口去查上一年的业绩报表,虽然最后可以算出来,但显然是不经济的。
论其源头,是预设逻辑有不合理的地方:第一步先用stock_zh_a_hist,第二步,再遍历去获得业绩报表。 我们调整一下预设逻辑,即先用stock_yjbb_em查每年年底的EPS,然后在去获得历史行情:
计算指定股票静态市盈率过N年的市盈率,可以使用akshare库,逻辑为先使用stock_yjbb_em(date=report_date)接口,按"YYYY1231"格式报表日期(report_date)获得前一年末业绩报表数据,按股票代码筛选出每股收益,再按股票代码(stock_code),使用stock_zh_a_hist接口获得该股票YYYY1231对应后一年的历史行情(使用‘不复权’),然后按当前日期(列名’日期’)的股票收盘价(列名’收盘’)除以前一年的每股收益,然后在历史行情dataframe增加新列,静态市盈率; 重复上述逻辑,计算下一年,直到完成N年静态市盈率计算,最后返回dataframe,并用print打印,日期格式是date,代码格式为python...
提示问题为 请计算A股股票每个历史行情日期所对应的前一年度的静态市盈率,例如600276,请从20230101开始计算
这次AI给出的代码如下:
raw_code:: 有关。根据提供的上下文,可以设计一个Python代码示例来计算A股股票(例如600276)从指定日期(20230101)开始,每个历史行情日期所对应的前一年度静态市盈率。这里是一个基于akshare库的实现逻辑:
```python
import akshare as ak
import pandas as pd
# 设置股票代码和起始日期
stock_code = '600276'
start_date = '20230101'
# 获取股票历史行情数据(不复权)
hist_data = ak.stock_zh_a_hist(stock_code, adjust='qfq', start_date=start_date)
# 确保日期列是datetime类型以便于处理
hist_data['日期'] = pd.to_datetime(hist_data['日期'])
# 初始化一个空的DataFrame用于存储每年的静态市盈率
pe_ratio_df = pd.DataFrame()
# 循环处理每一年,计算静态市盈率
current_year = hist_data['日期'].dt.year.min() # 开始年份
end_year = hist_data['日期'].dt.year.max() + 1 # 结束年份+1,因为需要包括最后一年的数据
while current_year < end_year:
# 构造报表日期字符串
report_date = f"{current_year - 1}1231"
# 使用akshare接口获取前一年末的每股收益数据
earnings_data = ak.stock_yjbb_em(date=report_date)
earnings_data = earnings_data[earnings_data['股票代码'] == stock_code]
eps = earnings_data['每股收益'].values[0] # 假设每股收益只有一条记录
# 计算当前年份的静态市盈率并添加到DataFrame
year_data = hist_data[hist_data['日期'].dt.year == current_year]
year_data['静态市盈率'] = year_data['收盘'] / eps
# 合并到总DataFrame中
pe_ratio_df = pd.concat([pe_ratio_df, year_data[['日期', '静态市盈率']]])
# 进入下一年
current_year += 1
# 打印结果
print(pe_ratio_df)
请注意,实际应用中需要检查`stock_yjbb_em`接口返回的每股收益数据是否有效,以及处理可能存在的数据缺失或异常情况。此外,由于akshare库的API可能会有变动,建议在实际操作前查看最新的库文档。
执行成功
#打印结果
print(pe_ratio_df)
所示代码起一个合适英语文件名
raw_file_name:: "China_Stock_Static_PE_Ratio_Calculation.py"
file_name:: China_Stock_Static_PE_Ratio_Calculation.py
文件已存在
文件保存成功
result:: {0: ' 日期 静态市盈率\n0 2023-01-03 62.754098\n1 2023-01-04 63.295082\n2 2023-01-05 64.573770\n3 2023-01-06 64.180328\n4 2023-01-09 64.573770\n.. ... ...\n332 2024-05-22 64.029412\n333 2024-05-23 63.308824\n334 2024-05-24 62.411765\n335 2024-05-27 62.794118\n336 2024-05-28 63.632353\n\n[337 rows x 2 columns]\n'}
这样就准确了,我们看一下保存代码的执行结果,为了清晰展示,最后直接打印的dataframe
3 避免一些接口类型和日期处理等方面的不稳定性
这个问题比较杂,我们简单描述一下,例如
执行失败 错误信息: TypeError(‘strptime() argument 1 must be str, not datetime.date’)
result:: {0: “TypeError(‘strptime() argument 1 must be str, not datetime.date’)”}
这个错误,就是AI生成代码时,把日期当成是字符串,再转了一次,但实际上,本身就是date类型,一转就报错了
另外,会产生列名不匹配,比如接口列名是[最高,收盘]但描述时,往往会是最高价,收盘价等等,给出的代码会报KeyError
也需要在预设代码中稍微处理一下 (以下加黑):
计算指定股票静态市盈率过N年的市盈率,可以使用akshare库,逻辑为先使用stock_yjbb_em(date=report_date)接口,按"YYYY1231"格式报表日期(report_date)获得前一年末业绩报表数据,按股票代码筛选出每股收益,再按股票代码(stock_code),使用stock_zh_a_hist接口获得该股票YYYY1231对应后一年的历史行情(使用‘不复权’),然后按当前日期(列名’日期’)的股票收盘价(列名’收盘’)除以前一年的每股收益,然后在历史行情dataframe增加新列,静态市盈率; 重复上述逻辑,计算下一年,直到完成N年静态市盈率计算,最后返回dataframe,并用print打印,日期格式是date,代码格式为python...
虽然不能完全避免,但生成代码准确率会提高很多
以上就是一个稍微复杂一点案例,计算静态市盈率,当然还有继续改进的余地,比如访问local数据,就要每次去访问互联网快很多,我们会继续尝试结合访问local数据。