import pandas as pd
import akshare as ak
import talib
from datetime import datetime, timedelta
def get_stock_basic_data(stock_code):
"""获取股票基本信息和日线数据,包含雪球实时数据"""
# 获取实时行情数据
realtime_df = ak.stock_zh_a_spot_em()
stock_name = realtime_df[realtime_df['代码'] == stock_code]['名称'].iloc[0]
# 获取个股基本信息
info_df = ak.stock_individual_info_em(symbol=stock_code)
info_dict = {row["item"]: str(row["value"]) for _, row in info_df.iterrows()}
# 获取日线数据(获取全部历史数据)
hist_daily = ak.stock_zh_a_hist(symbol=stock_code, period="daily", adjust="qfq")
# 检查并处理列名
if 'date' in hist_daily.columns:
hist_daily.columns = ['日期', '开盘', '最高', '最低', '收盘', '成交量', '成交额', '振幅', '涨跌幅', '涨跌额', '换手率']
# 选择需要的列并转换日期格式
hist_daily = hist_daily[['日期', '开盘', '最高', '最低', '收盘', '成交量']].copy()
hist_daily['日期'] = pd.to_datetime(hist_daily['日期'])
# 获取雪球实时数据
try:
prefix = "SH" if stock_code.startswith("6") else "SZ"
stock_individual_spot_xq_df = ak.stock_individual_spot_xq(symbol=f"{prefix}{stock_code}")
except Exception as e:
print(f"获取雪球实时数据失败: {str(e)}")
stock_individual_spot_xq_df = pd.DataFrame()
return stock_name, info_dict, hist_daily, stock_individual_spot_xq_df
def process_basic_data(hist_daily, info_dict):
"""处理基础数据(日/月/年线)"""
# 日线数据处理
daily_data = hist_daily[['日期', '开盘', '最高', '最低', '收盘', '成交量']].tail(30)
# 月线数据处理
temp_month = hist_daily.copy()
temp_month.set_index('日期', inplace=True)
monthly_df = temp_month.resample('ME').agg({
'开盘': 'first',
'最高': 'max',
'最低': 'min',
'收盘': 'last',
'成交量': 'sum'
}).reset_index()
monthly_df['日期'] = monthly_df['日期'].dt.strftime('%Y-%m')
monthly_data = monthly_df.tail(24)
# 年线数据处理
temp_year = hist_daily.copy()
temp_year.set_index('日期', inplace=True)
yearly_df = temp_year.resample('YE').agg({
'开盘': 'first',
'最高': 'max',
'最低': 'min',
'收盘': 'last',
'成交量': 'sum'
}).reset_index()
yearly_df['日期'] = yearly_df['日期'].dt.strftime('%Y')
# 处理时间范围
listed_year = int(info_dict.get("上市日期", "1900-01-01").split("-")[0])
latest_year = datetime.now().year
yearly_data = yearly_df[(yearly_df['日期'].astype(int) >= listed_year) &
(yearly_df['日期'].astype(int) <= latest_year)]
return daily_data, monthly_data, yearly_data
def calculate_technical_indicators(hist_daily):
"""计算技术指标"""
# 准备技术指标数据框架(英文列名)
tech_df = hist_daily.copy()
tech_df.columns = ['date', 'open', 'high', 'low', 'close', 'volume']
# 确保日期是datetime类型
tech_df['date'] = pd.to_datetime(tech_df['date'])
# 存储技术指标描述
indicator_descriptions = []
# 计算移动平均线
tech_df['MA5'] = talib.MA(tech_df['close'], timeperiod=5)
indicator_descriptions.append("MA5: 5日简单移动平均线")
tech_df['EMA5'] = talib.EMA(tech_df['close'], timeperiod=5)
indicator_descriptions.append("EMA5: 5日指数移动平均线")
# 计算MACD指标
tech_df['DIF'], tech_df['DEA'], tech_df['MACD'] = talib.MACD(
tech_df['close'], fastperiod=12, slowperiod=26, signalperiod=9
)
indicator_descriptions.append("MACD: 异同移动平均线(12/26/9)")
# 计算布林带
tech_df['BB_upper'], tech_df['BB_middle'], tech_df['BB_lower'] = talib.BBANDS(
tech_df['close'], timeperiod=20
)
indicator_descriptions.append("BBANDS: 布林带指标(20日中轨)")
# 计算RSI
tech_df['RSI14'] = talib.RSI(tech_df['close'], timeperiod=14)
indicator_descriptions.append("RSI14: 14日相对强弱指数")
# 计算抛物线转向指标
tech_df['SAR'] = talib.SAR(tech_df['high'], tech_df['low'], acceleration=0.02, maximum=0.2)
indicator_descriptions.append("SAR: 抛物线转向指标")
# 计算威廉指标
tech_df['WILLR14'] = talib.WILLR(
tech_df['high'], tech_df['low'], tech_df['close'], timeperiod=14
)
indicator_descriptions.append("WILLR14: 14日威廉指标")
# 计算动量指标
tech_df['MOM10'] = talib.MOM(tech_df['close'], timeperiod=10)
indicator_descriptions.append("MOM10: 10日动量指标")
# 计算能量潮指标
tech_df['OBV'] = talib.OBV(tech_df['close'], tech_df['volume'])
indicator_descriptions.append("OBV: 能量潮指标")
# 计算平均真实波幅
tech_df['ATR14'] = talib.ATR(
tech_df['high'], tech_df['low'], tech_df['close'], timeperiod=14
)
indicator_descriptions.append("ATR14: 14日平均真实波幅")
# K线形态识别
tech_df['CDLHAMMER'] = talib.CDLHAMMER(tech_df['open'], tech_df['high'], tech_df['low'], tech_df['close'])
indicator_descriptions.append("CDLHAMMER: 锤头线形态")
tech_df['CDLDOJI'] = talib.CDLDOJI(tech_df['open'], tech_df['high'], tech_df['low'], tech_df['close'])
indicator_descriptions.append("CDLDOJI: 十字星形态")
tech_df['CDLMORNINGSTAR'] = talib.CDLMORNINGSTAR(
tech_df['open'], tech_df['high'], tech_df['low'], tech_df['close'], penetration=0.3
)
indicator_descriptions.append("CDLMORNINGSTAR: 晨星形态")
return tech_df, indicator_descriptions
def format_output(stock_name, stock_code, info_dict, daily_data, monthly_data, yearly_data,
tech_df, descriptions, xq_spot_df):
"""格式化输出所有数据到文本,包含雪球实时数据"""
output = []
# 股票基本信息
output.append("=" * 80)
output.append(f"{stock_name}({stock_code}) 股票综合分析报告")
output.append("=" * 80 + "\n")
# 股票基本信息
output.append("【股票基本信息】")
output.append(f"股票名称: {stock_name}")
output.append(f"股票代码: {stock_code}")
output.append(f"上市日期: {info_dict.get('上市日期', '未知')}")
output.append(f"所属行业: {info_dict.get('行业', '未知')}")
output.append(f"公司简介: {info_dict.get('公司简介', '暂无')[:100]}...\n")
# 雪球实时行情数据
if not xq_spot_df.empty:
output.append("\n" + "=" * 60)
output.append("【雪球实时行情数据】")
output.append("=" * 60 + "\n")
# 选择重要的字段展示
spot_cols = ['current', 'percent', 'high', 'low', 'volume', 'amount',
'total_market_value', 'market_capital']
if all(col in xq_spot_df.columns for col in spot_cols):
spot_data = xq_spot_df[spot_cols].iloc[0].copy()
# 格式化数据
spot_data['percent'] = f"{spot_data['percent']}%"
spot_data['volume'] = f"{spot_data['volume'] / 10000:.2f}万手"
spot_data['amount'] = f"{spot_data['amount'] / 100000000:.2f}亿元"
spot_data['total_market_value'] = f"{spot_data['total_market_value'] / 100000000:.2f}亿元"
spot_data['market_capital'] = f"{spot_data['market_capital'] / 100000000:.2f}亿元"
# 添加中文描述
spot_info = {
"当前价格": spot_data['current'],
"涨跌幅": spot_data['percent'],
"当日最高": spot_data['high'],
"当日最低": spot_data['low'],
"成交量": spot_data['volume'],
"成交额": spot_data['amount'],
"总市值": spot_data['total_market_value'],
"流通市值": spot_data['market_capital']
}
for desc, value in spot_info.items():
output.append(f"{desc}: {value}")
else:
output.append("雪球实时数据结构异常,无法解析完整数据")
output.append(xq_spot_df.to_string(index=False))
else:
output.append("\n" + "=" * 60)
output.append("【雪球实时行情数据】")
output.append("=" * 60 + "\n")
output.append("未获取到雪球实时行情数据")
# 基础数据部分
output.append("\n" + "=" * 60)
output.append("【历史行情数据】")
output.append("=" * 60 + "\n")
output.append("最近30个交易日数据:")
output.append(daily_data.to_string(index=False))
output.append("\n" + "-" * 60 + "\n")
output.append("最近24个月数据:")
output.append(monthly_data.to_string(index=False))
output.append("\n" + "-" * 60 + "\n")
output.append(f"{info_dict.get('上市日期', '未知')}至今历年数据:")
output.append(yearly_data.to_string(index=False))
# 技术指标部分
output.append("\n" + "=" * 60)
output.append("【技术指标分析】")
output.append("=" * 60 + "\n")
output.append("技术指标说明:")
for desc in descriptions:
output.append(f"- {desc}")
# 最近半年技术指标数据
half_year_data = tech_df.tail(126)
output.append("\n最近半年技术指标数据:")
output.append("日期 | 收盘价 | MA5 | EMA5 | MACD | RSI14 | WILLR14 | OBV")
for _, row in half_year_data.iterrows():
line = f"{row['date'].strftime('%Y-%m-%d')} | {row['close']:6.2f} | "
line += f"{row['MA5']:6.2f} | " if pd.notna(row['MA5']) else " N/A | "
line += f"{row['EMA5']:6.2f} | " if pd.notna(row['EMA5']) else " N/A | "
line += f"{row['MACD']:7.4f} | " if pd.notna(row['MACD']) else " N/A | "
line += f"{row['RSI14']:6.2f} | " if pd.notna(row['RSI14']) else " N/A | "
line += f"{row['WILLR14']:7.2f} | " if pd.notna(row['WILLR14']) else " N/A | "
line += f"{row['OBV']:.0f}" if pd.notna(row['OBV']) else "N/A"
output.append(line)
# K线形态识别
output.append("\nK线形态识别结果(最近半年):")
output.append("日期 | 锤头线 | 十字星 | 晨星形态")
for _, row in half_year_data.iterrows():
line = f"{row['date'].strftime('%Y-%m-%d')} | "
hammer = "是" if row['CDLHAMMER'] > 0 else "否"
doji = "是" if row['CDLDOJI'] > 0 else "否"
morningstar = "是" if row['CDLMORNINGSTAR'] > 0 else "否"
line += f"{hammer:^6} | {doji:^6} | {morningstar:^8}"
output.append(line)
# 添加数据说明
output.append("\n" + "=" * 60)
output.append("数据说明:")
output.append("1. 日线数据展示最近30个交易日")
output.append("2. 月线数据为日线聚合(最近24个月)")
output.append("3. 年线数据为日线聚合(上市至今)")
output.append("4. 技术指标基于最近5年数据计算")
output.append("5. 实时数据来源:雪球")
output.append("6. 历史数据来源:东方财富/同花顺金融终端")
return "\n".join(output)
# 主程序
if __name__ == "__main__":
stock_code = input("请输入A股股票代码(例如:000001): ").strip()
try:
# 获取基础数据(包含雪球实时数据)
stock_name, info_dict, hist_daily, xq_spot_df = get_stock_basic_data(stock_code)
# 打印雪球数据(原始格式)
print("\n雪球实时行情数据(原始格式):")
print(xq_spot_df)
# 处理基础数据
daily_data, monthly_data, yearly_data = process_basic_data(hist_daily, info_dict)
# 计算技术指标
tech_df, descriptions = calculate_technical_indicators(hist_daily)
# 生成输出内容
output_text = format_output(
stock_name, stock_code, info_dict,
daily_data, monthly_data, yearly_data,
tech_df, descriptions, xq_spot_df
)
# 保存结果到文本文件
filename = f"{stock_code}_stock_analysis.txt"
with open(filename, "w", encoding="utf-8") as f:
f.write(output_text)
print(f"\n股票分析报告已生成并保存到: {filename}")
print("-" * 60)
print("提示: 按回车键退出程序...")
input()
except Exception as e:
print(f"处理过程中发生错误: {str(e)}")
print("请检查股票代码是否正确或网络连接是否正常")
input("按回车键退出...")
改为可以从外部一次性输入多个股票代码,可以用逗号,空格,或分行隔开,一次读取一个代码并输出结果,直到所有代码输出完毕,输出的所有数据不变