Python实战:从零构建股市数据抓取与分析系统
1. 环境准备与数据源选择
想用Python玩转股市数据,第一步不是急着写代码,而是把“厨房”收拾好,把“食材”的来源搞清楚。我刚开始学的时候,也犯过这个错误,一上来就猛敲代码,结果不是库装不上,就是数据源连不通,白白浪费很多时间。所以,咱们先花点时间,把基础打牢。
1.1 搭建你的Python数据分析环境
对于新手来说,我强烈推荐使用 Anaconda 来管理你的Python环境。它就像是一个预装好了各种数据分析工具的“全家桶”,省去了你一个个手动安装库的麻烦,特别是处理库与库之间的依赖关系时,能避免很多头疼的问题。你可以去Anaconda官网下载对应你操作系统的安装包,一路“下一步”安装就行,非常简单。
安装好Anaconda后,我习惯用它的图形化界面 Anaconda Navigator 来启动 Jupyter Notebook。为什么用Jupyter?因为它特别适合做数据分析和探索。你可以把代码、运行结果、文字说明甚至图表都放在一个文档里,写起来像记笔记一样,思路不会断,而且可以分块运行代码,看到每一步的结果,对调试和学习非常友好。当然,如果你更喜欢在命令行里操作,也可以用 conda 命令来创建和管理独立的环境,比如 conda create -n stock_analysis python=3.9 创建一个名为 stock_analysis 的Python 3.9环境,然后用 conda activate stock_analysis 激活它。这样做的好处是,你不同的项目可以用不同版本的环境,互不干扰。
接下来就是安装核心的库了。虽然Anaconda自带了很多,但我们还是需要确保几个关键库的版本是最适合的。打开你的终端(或Anaconda Prompt),在激活的环境下,运行这几条命令:
pip install pandas numpy yfinance matplotlib seaborn
我来简单说说这几个库是干嘛的。pandas 是数据处理的核心,它能把表格数据(比如Excel里的数据)读进来,进行各种筛选、计算、变形,是Python数据分析的“瑞士军刀”。numpy 是它的好搭档,负责高效的数值计算。yfinance 是我们今天的主角之一,一个专门用来从雅虎财经抓取数据的库,免费、稳定、数据全,对个人学习和研究非常友好。matplotlib 和 seaborn 则是画图的好手,能把枯燥的数字变成直观的图表。
1.2 寻找可靠且免费的数据源
数据是分析的基石,源头一定要稳。对于A股和港股、美股投资者,数据源的选择略有不同。我实测过不少,给大家分享几个最常用、最稳定的。
首先是 yfinance,它背后对接的是雅虎财经的API。雅虎财经的数据覆盖面非常广,几乎涵盖了全球主要市场的股票、指数、基金、期货甚至加密货币的历史行情和基本面数据。它的优点是接口极其简单,几行代码就能拉取到指定时间段内某只股票的所有日线数据(开盘价、最高价、最低价、收盘价、调整后收盘价、成交量)。对于美股,数据非常准确。对于A股,你需要使用股票的代码加上对应的市场后缀,比如腾讯控股在港股是 0700.HK,贵州茅台在A股是 600519.SS(SS代表上海证券交易所)。这个库是我最常用的,因为免费且无需注册。
另一个备选是 akshare。这是一个非常强大的中文财经数据接口库,由国内开发者维护,专门针对A股、港股、宏观、基金等中国市场数据。它的数据源更贴近国内投资者,比如能获取到龙虎榜数据、融资融券数据、财报数据等更细节的信息。安装命令是 pip install akshare。它的接口也很丰富,但可能因为数据源是公开网站,偶尔会有反爬虫策略调整导致接口暂时失效的情况,不过社区维护很活跃,通常很快会修复。
对于只想快速看一眼行情或者做简单分析的朋友,也可以考虑一些财经网站提供的CSV导出功能,但这不适合自动化。记住一个原则:对于自动化、系统化的分析,一定要选择有稳定API或专门库支持的数据源。 手动下载和复制粘贴,在数据量面前会变得极其低效且容易出错。
2. 动手抓取你的第一份股票数据
环境搭好了,数据源也选定了,手是不是有点痒了?别急,咱们从最简单的开始,一步步把数据“抓”到自己的电脑里。这个过程就像钓鱼,选好鱼塘(数据源),放下鱼饵(代码),然后静静等待收获。
2.1 使用yfinance进行基础数据抓取
让我们直接上代码,感受一下yfinance的便捷。假设我想分析苹果公司(Apple Inc.)的股票,它的美股代码是 AAPL。
import yfinance as yf
import pandas as pd
# 定义要下载的股票代码和时间范围
ticker_symbol = "AAPL"
start_date = "2023-01-01"
end_date = "2023-12-31"
# 使用yfinance的download函数下载数据
# 注意:这里下载的是日线数据
apple_data = yf.download(ticker_symbol, start=start_date, end=end_date)
# 查看数据的前5行
print("数据前5行:")
print(apple_data.head())
# 查看数据的整体信息
print("\n数据信息:")
print(apple_data.info())
运行这段代码,你会看到一个进度条,然后控制台会打印出一个漂亮的表格。这个表格就是一个 pandas DataFrame,它是pandas里最核心的数据结构,你可以把它理解成一个功能超级强大的Excel表格。表格的每一行代表一个交易日,列则包括:
Open: 开盘价High: 最高价Low: 最低价Close: 收盘价Adj Close: 调整后收盘价(考虑了分红、拆股等因素,对于长期分析更重要)Volume: 成交量
apple_data.info() 这行命令会告诉你这个表格有多少行、多少列,每一列的数据类型是什么,有没有缺失值。这是拿到数据后的第一个好习惯,快速了解数据的“长相”。
2.2 进阶抓取技巧与数据整理
只会下载一只股票一段时间的日线数据还不够。在实际分析中,我们常常需要对比多只股票,或者获取更长时间、更细粒度的数据。
多股票同时抓取:yfinance支持一次性下载多个股票的数据,只需要把股票代码放在一个列表里传给 download 函数。返回的数据会是一个多层索引(MultiIndex)的DataFrame,非常方便进行横向对比。
# 同时下载苹果、微软和谷歌的股票数据
ticker_list = ["AAPL", "MSFT", "GOOGL"]
multi_data = yf.download(ticker_list, start="2023-01-01", end="2023-12-31")
print(multi_data.head())
获取更长时间或不同周期的数据:yf.download 函数默认是日线数据。但你可以通过 interval 参数来指定周期,比如 interval='1h' 获取小时线,interval='1wk' 获取周线,interval='1mo' 获取月线。对于历史数据,你甚至可以不用指定 end_date,只指定 start_date,它会一直抓到最近一个交易日。
获取除行情外的其他信息:yfinance不仅能抓价格,还能抓很多公司基本面信息。yf.Ticker() 对象是个宝库。
# 创建苹果公司的Ticker对象
apple = yf.Ticker("AAPL")
# 获取公司基本信息
print(apple.info['longName']) # 公司全名
print(apple.info['sector']) # 所属行业
print(apple.info['marketCap']) # 市值
# 获取分红历史
dividends = apple.dividends
print(dividends.head())
# 获取股票拆合历史
splits = apple.splits
print(splits.head())
抓取到的数据,我强烈建议立刻保存到本地,比如CSV文件。这样下次分析时就不用重复下载,既节省时间,也避免因网络问题或API限制导致的分析中断。
# 将数据保存到CSV文件
apple_data.to_csv('AAPL_2023.csv')
# 下次使用时直接读取
loaded_data = pd.read_csv('AAPL_2023.csv', index_col='Date', parse_dates=True)
print(loaded_data.head())
注意 parse_dates=True 和 index_col='Date' 参数,它们能帮我们把日期列自动解析成时间格式并设置为索引,这样后续按时间筛选会非常方便。
3. 数据清洗与预处理:从原始数据到分析就绪
抓回来的数据,我们通常称之为“原始数据”或“脏数据”。它可能有不完整、格式不对、有明显错误的地方。直接拿这样的数据去做分析,结论很可能不靠谱。所以,清洗和预处理是至关重要的一步,这部分工作可能枯燥,但决定了你整个分析大厦的地基是否牢固。
3.1 处理缺失值与异常值
首先检查缺失值。在股市数据里,缺失值可能因为节假日(比如A股的国庆长假、美股的圣诞节)或者极端情况下的数据源问题。
# 检查每一列是否有缺失值
missing_values = apple_data.isnull().sum()
print("缺失值统计:")
print(missing_values)
# 检查缺失值占比
missing_percentage = (apple_data.isnull().sum() / len(apple_data)) * 100
print("\n缺失值占比:")
print(missing_percentage)
如果缺失值很少(比如只有零星几天),常见的处理方法是向前填充或向后填充。在时间序列数据中,用前一天的收盘价填充今天的缺失值,是一个比较合理的假设。
# 使用前一个有效值填充缺失值(向前填充)
data_filled = apple_data.fillna(method='ffill')
# 也可以同时用后一个有效值填充(向后填充),确保开头没有NaN
data_filled = data_filled.fillna(method='bfill')
对于异常值,比如某天收盘价突然变成0,或者成交量出现一个天文数字,这很可能是数据错误。我们可以通过简单的统计方法(如3σ原则)或业务规则来识别。例如,我们可以认为股价单日涨跌幅超过20%(非涨跌停市场)为异常,需要查验。
# 计算每日收益率
data_filled['Daily_Return'] = data_filled['Adj Close'].pct_change()
# 找出收益率绝对值大于20%的异常日
abnormal_days = data_filled[abs(data_filled['Daily_Return']) > 0.20]
if not abnormal_days.empty:
print("发现异常波动日:")
print(abnormal_days[['Adj Close', 'Daily_Return']])
# 对于异常值,可以将其设为NaN,然后用填充法处理,或者直接删除该行(需谨慎)
3.2 特征工程:创造更有价值的指标
原始数据只有价格和成交量,但我们可以通过计算,衍生出大量对分析更有用的技术指标,这个过程就叫特征工程。这是量化分析中最有创造性的部分之一。
基础特征:我们可以直接计算一些简单但重要的指标。
# 计算每日价格波动范围(振幅)
data_filled['Price_Range'] = data_filled['High'] - data_filled['Low']
# 计算收盘价相对于当日价格区间的位置
data_filled['Close_Position'] = (data_filled['Close'] - data_filled['Low']) / data_filled['Price_Range']
# 计算简单移动平均线(SMA),比如5日均线和20日均线
data_filled['SMA_5'] = data_filled['Adj Close'].rolling(window=5).mean()
data_filled['SMA_20'] = data_filled['Adj Close'].rolling(window=20).mean()
# 计算指数移动平均线(EMA),对近期价格赋予更高权重
data_filled['EMA_12'] = data_filled['Adj Close'].ewm(span=12, adjust=False).mean()
data_filled['EMA_26'] = data_filled['Adj Close'].ewm(span=26, adjust=False).mean()
# 计算MACD指标(一个常用的趋势指标)
data_filled['MACD'] = data_filled['EMA_12'] - data_filled['EMA_26']
data_filled['MACD_Signal'] = data_filled['MACD'].ewm(span=9, adjust=False).mean()
data_filled['MACD_Histogram'] = data_filled['MACD'] - data_filled['MACD_Signal']
更复杂的指标:我们可以利用 ta 库(Technical Analysis library)来快速计算上百种技术指标,避免重复造轮子。
pip install ta
import ta
# 为DataFrame添加RSI(相对强弱指数)指标
data_filled['RSI'] = ta.momentum.RSIIndicator(close=data_filled['Adj Close'], window=14).rsi()
# 添加布林带(Bollinger Bands)
bollinger = ta.volatility.BollingerBands(close=data_filled['Adj Close'], window=20, window_dev=2)
data_filled['BB_Upper'] = bollinger.bollinger_hband()
data_filled['BB_Middle'] = bollinger.bollinger_mavg()
data_filled['BB_Lower'] = bollinger.bollinger_lband()
经过这些步骤,你的数据就从最初只有几列的“原始矿石”,变成了包含各种衍生指标的“富矿”,为接下来的深度分析做好了充分准备。记得把清洗和加工好的数据也保存下来,比如 data_filled.to_csv('AAPL_2023_processed.csv')。
4. 数据分析与可视化:让数据开口说话
数据准备好了,现在是最有意思的部分——分析和可视化。我们的目标是把那些隐藏在数字背后的模式、趋势和关系,用图表和统计量清晰地展现出来。Python的Matplotlib和Seaborn库在这方面是绝佳的组合。
4.1 绘制核心价格与指标图表
一张好的图表胜过千言万语。我们先从最基础的股价走势图开始。
import matplotlib.pyplot as plt
import seaborn as sns
# 设置图表风格,让图更好看
sns.set_style("whitegrid")
plt.figure(figsize=(14, 7))
# 绘制调整后收盘价曲线
plt.plot(data_filled.index, data_filled['Adj Close'], label='Adj Close Price', linewidth=2, color='blue')
# 添加移动平均线
plt.plot(data_filled.index, data_filled['SMA_20'], label='20-Day SMA', linewidth=1.5, color='orange', linestyle='--')
# 填充布林带区域(增加视觉上的波动区间感)
plt.fill_between(data_filled.index, data_filled['BB_Upper'], data_filled['BB_Lower'], color='gray', alpha=0.2, label='Bollinger Bands')
plt.title('Apple Stock Price with Technical Indicators (2023)', fontsize=16)
plt.xlabel('Date', fontsize=12)
plt.ylabel('Price (USD)', fontsize=12)
plt.legend(loc='upper left')
plt.xticks(rotation=45) # 旋转日期标签,避免重叠
plt.tight_layout() # 自动调整布局
plt.show()
这张图一下子就把股价趋势、中期均线以及波动范围(布林带)都展示出来了。你可以清晰地看到价格是否在均线上方(可能处于上升趋势),以及价格是否触及布林带上下轨(可能意味着超买或超卖)。
接下来,我们可以用子图的方式,把多个相关指标放在一起对比观察,这能发现更多线索。
fig, axes = plt.subplots(3, 1, figsize=(14, 10), sharex=True)
# 子图1:价格与成交量
axes[0].plot(data_filled.index, data_filled['Adj Close'], color='tab:blue')
axes[0].set_ylabel('Price', color='tab:blue')
axes[0].tick_params(axis='y', labelcolor='tab:blue')
axes0_twin = axes[0].twinx() # 创建共享x轴的双y轴
axes0_twin.bar(data_filled.index, data_filled['Volume'], alpha=0.3, color='tab:orange')
axes0_twin.set_ylabel('Volume', color='tab:orange')
axes0_twin.tick_params(axis='y', labelcolor='tab:orange')
axes[0].set_title('Price and Volume')
# 子图2:MACD指标
axes[1].plot(data_filled.index, data_filled['MACD'], label='MACD', color='tab:green')
axes[1].plot(data_filled.index, data_filled['MACD_Signal'], label='Signal', color='tab:red')
axes[1].bar(data_filled.index, data_filled['MACD_Histogram'], label='Histogram', alpha=0.5)
axes[1].axhline(y=0, color='black', linestyle='-', linewidth=0.5)
axes[1].set_ylabel('MACD')
axes[1].legend(loc='upper left')
axes[1].set_title('MACD Indicator')
# 子图3:RSI指标
axes[2].plot(data_filled.index, data_filled['RSI'], color='purple')
axes[2].axhline(y=70, color='red', linestyle='--', linewidth=1, label='Overbought (70)')
axes[2].axhline(y=30, color='green', linestyle='--', linewidth=1, label='Oversold (30)')
axes[2].fill_between(data_filled.index, 30, 70, color='yellow', alpha=0.1)
axes[2].set_ylabel('RSI')
axes[2].set_xlabel('Date')
axes[2].set_title('RSI Indicator')
axes[2].legend()
plt.tight_layout()
plt.show()
这个多图组合非常经典。上方的价量图可以看“价量齐升”或“价升量缩”等形态。中间的MACD图帮助判断趋势的强度和转折。下方的RSI图则提示超买超卖区域。三者结合,能对市场状态有一个立体的认知。
4.2 相关性分析与简单的统计洞察
除了看图,我们还需要用数字说话。计算不同指标之间的相关性,可以帮助我们理解它们是如何联动的。
# 选择我们感兴趣的特征列
features_for_corr = ['Adj Close', 'Volume', 'SMA_20', 'RSI', 'MACD']
correlation_data = data_filled[features_for_corr]
# 计算相关系数矩阵
corr_matrix = correlation_data.corr()
# 用热力图可视化相关系数矩阵
plt.figure(figsize=(8, 6))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', center=0, square=True, linewidths=1)
plt.title('Correlation Matrix of Selected Features')
plt.show()
热力图中,颜色越暖(红),表示正相关性越强;颜色越冷(蓝),表示负相关性越强。比如,你可能会发现股价(Adj Close)和20日均线(SMA_20)高度正相关,这是符合预期的。而RSI和股价的相关性可能不那么强,因为它是一个震荡指标。
我们还可以做一些基础的统计描述,快速了解数据的分布。
# 对收盘价进行描述性统计
print(data_filled['Adj Close'].describe())
# 计算年度化波动率(一个衡量风险的常用指标)
daily_returns = data_filled['Adj Close'].pct_change().dropna()
annual_volatility = daily_returns.std() * (252 ** 0.5) # 假设一年有252个交易日
print(f"\n年度化波动率: {annual_volatility:.2%}")
这些分析虽然基础,但已经能为我们提供关于股票历史表现、波动特性以及各指标间关系的大量洞见。可视化让这些洞见一目了然,而统计数字则提供了精确的衡量。至此,一个完整的、从数据抓取到清洗再到分析可视化的闭环就完成了。你可以把这个流程应用到任何你感兴趣的股票上,构建属于自己的分析仪表盘。
更多推荐
所有评论(0)