利用 LLM 和 Python 进行股价预测实战
引言
随着大语言模型(LLM)技术的快速发展,其在金融领域的应用逐渐受到关注。传统的量化交易多依赖统计模型或深度学习网络处理时间序列数据,而 LLM 凭借其强大的语义理解和推理能力,为结合市场新闻、宏观数据与历史价格进行综合预测提供了新的思路。本文将以中远海控(601919.SH)为例,演示如何使用 Python 获取股票数据,结合 LLM 进行短期股价趋势预测,并生成可视化报告。
注意:股市有风险,投资需谨慎。本文代码仅用于技术演示,不构成任何投资建议。LLM 在金融预测中存在幻觉风险,实际应用中需结合传统风控模型。
环境准备
在开始之前,请确保您的开发环境已安装以下依赖库:
pip install akshare pandas matplotlib seaborn numpy
此外,您需要一个可用的 LLM API 接口(如 OpenAI、Claude 或国内大模型服务),以便调用 llm_client 进行预测和分析。
1. 获取股票数据
我们使用 akshare 库获取 A 股日线行情数据。该库支持实时获取大量金融数据,无需注册即可使用。
1.1 数据获取逻辑
我们需要获取指定时间段内的收盘价数据。对于中远海控,股票代码为 601919.SH。
import akshare as ak
import datetime
# 获取当前日期
end_date = datetime.date.today().strftime("%Y%m%d")
start_date = "20240101"
# 获取日线数据
symbol = "601919.SH"
cosco_daily_data_2024 = ak.stock_zh_a_daily(symbol=symbol, start_date=start_date, end_date=end_date)
print(f"获取到 {len(cosco_daily_data_2024)} 条数据")
2. 数据预处理
机器学习模型对输入数据的格式有严格要求。原始数据通常包含缺失值或非连续的时间索引,需要进行清洗。
2.1 数据清洗函数
我们将数据转换为以日期为索引的 DataFrame,并按日频率重采样,填充缺失值。
import pandas as pd
def data_processor(data):
# 设置日期索引
df = data.set_index('date')
# 按日频率重采样
df = df.asfreq('D')
# 前向填充缺失值
df = df.fillna(method='ffill')
# 仅保留最近 30 天数据作为上下文
return df.iloc[-30:]
2.2 响应解析
LLM 返回的通常是自然语言文本,我们需要从中提取数值。这里使用正则表达式匹配小数点数字。
import re
def response_processor(response, num_of_predict):
numbers = re.findall(r'\d+\.\d+', response)
predictions = [float(num) for num in numbers[-num_of_predict:]]
# 生成预测日期索引
index = pd.date_range(start=pd.Timestamp.now() + pd.Timedelta(days=1), periods=num_of_predict)
return pd.Series(predictions, name='close', index=index)
3. LLM 预测集成
由于 llm_client 是外部封装类,在实际项目中需要替换为您具体的 API 调用逻辑。下面提供一个模拟实现的结构,展示如何构建 Prompt。
3.1 构建提示词
将历史数据的关键统计信息(均值、波动率、最新价)放入 Prompt,要求模型输出未来 N 天的预测价格。
# 模拟 LLM 客户端调用
# 实际使用时请替换为真实的 llm_factory.get_instance()
class MockLLMClient:
def predict(self, input_data, num_of_predict, **kwargs):
# 模拟返回预测结果
last_price = input_data['close'].iloc[-1]
# 简单模拟随机波动
import random
preds = [last_price * (1 + random.uniform(-0.02, 0.02)) for _ in range(num_of_predict)]
return pd.Series(preds, name='close')
llm_client = MockLLMClient()
predictions = llm_client.predict(input_data, num_of_predict=5)
4. 数据可视化
使用 matplotlib 绘制历史走势与预测曲线,直观对比模型表现。
4.1 字体配置
为了正确显示中文标签,需根据操作系统加载对应的中文字体。
import matplotlib.pyplot as plt
import platform
def configure_matplotlib_for_chinese():
system = platform.system()
if system == 'Windows':
font_name = 'SimHei'
elif system == 'Darwin':
font_name = 'STHeiti'
else:
font_path = '/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc'
if os.path.exists(font_path):
from matplotlib import font_manager
font_manager.fontManager.addfont(font_path)
font_name = font_manager.FontProperties(fname=font_path).get_name()
else:
raise FileNotFoundError("Font file not found")
plt.rcParams['font.sans-serif'] = [font_name]
plt.rcParams['axes.unicode_minus'] = False
configure_matplotlib_for_chinese()
4.2 绘图与保存
合并历史与预测数据,绘制双曲线图。
plt.figure(figsize=(12, 6))
historical_data.index = pd.to_datetime(historical_data.index)
plt.plot(historical_data.index, historical_data['close'], label='历史收盘价', linewidth=2)
plt.plot(predictions.index, predictions['close'], color='red', linestyle='--', label='预测收盘价', linewidth=2)
plt.title('中远海控股价历史与预测趋势')
plt.xlabel('日期')
plt.ylabel('价格 (元)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.savefig('stock_prediction.png')
plt.close()
5. 智能分析与报告生成
除了数值预测,LLM 还可以基于数据特征生成自然语言的市场分析。
5.1 统计分析
计算关键指标,如预测期内的涨跌幅。
last_price = historical_data['close'].iloc[-1]
first_pred = predictions['close'].iloc[0]
change_rate = (predictions['close'].iloc[-1] - first_pred) / first_pred * 100
5.2 生成分析报告
将统计数据转化为 Prompt,请求 LLM 给出简短的投资建议。
prompt = f"基于最后价格{last_price:.2f}及预测变化率{change_rate:.2f}%,分析未来5天趋势。"
response = llm_client.one_chat(prompt)
print(response)
6. 风险提示与局限性
在使用 LLM 进行金融预测时,必须清醒认识到以下局限:
- 数据时效性:LLM 训练数据存在截止期,无法感知实时突发新闻。
- 幻觉问题:模型可能编造不存在的财务数据或趋势。
- 非结构化噪声:股价受情绪、政策等多因素影响,纯文本模型难以完全捕捉。
- 过拟合风险:短期预测容易受历史噪声干扰,长期预测准确率极低。
建议将此系统作为辅助工具,而非决策核心。务必配合严格的风控策略。
7. 完整代码示例
以下是整合后的完整脚本,修复了变量定义问题,并增加了必要的异常处理。
import akshare as ak
import datetime
import pandas as pd
import re
import matplotlib.pyplot as plt
import os
import platform
import uuid
# 1. 初始化配置
os.makedirs('output', exist_ok=True)
system = platform.system()
if system == 'Windows':
font_name = 'SimHei'
elif system == 'Darwin':
font_name = 'STHeiti'
else:
font_path = '/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc'
if os.path.exists(font_path):
from matplotlib import font_manager
font_manager.fontManager.addfont(font_path)
font_name = font_manager.FontProperties(fname=font_path).get_name()
else:
font_name = 'Arial Unicode MS'
plt.rcParams['font.sans-serif'] = [font_name]
plt.rcParams['axes.unicode_minus'] = False
# 2. 数据获取
try:
symbol = "601919.SH"
today = datetime.date.today()
end_date = today.strftime("%Y%m%d")
cosco_daily_data_2024 = ak.stock_zh_a_daily(symbol=symbol, start_date="20240101", end_date=end_date)
except Exception as e:
print(f"数据获取失败:{e}")
exit(1)
# 3. 数据处理
def data_processor(data):
df = data[['date', 'close']].set_index('date')
df = df.asfreq('D').fillna(method='ffill').iloc[-30:]
return df
input_data = data_processor(cosco_daily_data_2024)
# 4. 模拟 LLM 预测 (实际请替换真实 API)
class MockLLMClient:
def predict(self, input_data, num_of_predict):
last_val = input_data['close'].iloc[-1]
preds = [last_val * (1 + i * 0.005) for i in range(num_of_predict)]
idx = pd.date_range(start=input_data.index[-1] + pd.Timedelta(days=1), periods=num_of_predict)
return pd.Series(preds, name='close', index=idx)
llm_client = MockLLMClient()
predictions = llm_client.predict(input_data, num_of_predict=5)
# 5. 可视化
historical_data = cosco_daily_data_2024.copy()
historical_data['date'] = pd.to_datetime(historical_data['date'])
historical_data.set_index('date', inplace=True)
combined_data = pd.concat([historical_data['close'], predictions['close']])
plt.figure(figsize=(12, 6))
plt.plot(historical_data.index, historical_data['close'], label='历史数据', linewidth=2)
plt.plot(predictions.index, predictions['close'], color='red', label='预测数据', linestyle='--')
plt.title('中远海控股价历史数据和预测')
plt.xlabel('日期')
plt.ylabel('收盘价')
plt.legend()
plt.grid(True)
file_name = f"output/{uuid.uuid4()}.png"
plt.savefig(file_name)
plt.close()
# 6. 统计与分析
last_historical_price = historical_data['close'].iloc[-1]
first_prediction_price = predictions['close'].iloc[0]
prediction_change = (predictions['close'].iloc[-1] - first_prediction_price) / first_prediction_price * 100
results = []
results.append(f"")
results.append(f"1. 历史收盘价:{last_historical_price:.2f}")
results.append(f"2. 预测首日价:{first_prediction_price:.2f}")
results.append(f"3. 预测变化率:{prediction_change:.2f}%")
results.append("4. 趋势判断:" + ("上升" if prediction_change > 0 else "下降"))
analysis_result = "\n".join(results)
print(analysis_result)

