【Python金融笔记】加载数据与可视化
Python金融入门
- 1. 加载数据与可视化
- 1.1. 加载数据
- 1.2. 折线图
- 1.3. 重采样
- 1.4. K线图 / 蜡烛图
- 1.5. 挑战1
- 2. 计算
- 2.1. 收益 / 回报
- 2.2. 绘制收益图
- 2.3. 累积收益
- 2.4. 波动率
- 2.5. 挑战2

1. 加载数据与可视化
1.1. 加载数据
# 导入所需库
from matplotlib import dates
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import yfinance as yf
# 使用Y Finance库下载S&P500数据以及2010年至2019年底的Apple数据
raw = yf.download('SPY AAPL', start='2010-01-01', end='2019-12-31')
raw
# Y Finance使用日期作为索引列, 每行代表一天。
# 查看列:这里有所谓的分层或多索引列,可以看到调整后的收盘价,因为我们同时要求苹果和标准普尔 500指数
# 所以我们在它下面有两个子列,可以看到收盘价、最高值、最低值、开盘价、 和Volumn
# 检查数据帧的columns属性
raw.columns
# 查看pipe的用法
raw.pipe?
import yfinance as yf#fix_cols():重命名多层列索引为单层(只保留子列名)
def fix_cols(df):columns = df.columnsouter = [col[0] for col in columns]df.columns = outerreturn df# 加载与清洗逻辑封装在一个函数里,便于复用
def tweak_data():raw = yf.download('SPY AAPL',start='2010-01-01', end='2019-12-31')return (raw.iloc[:,::2].pipe(fix_cols)) # pipe():在链式调用中插入一个自定义函数。tweak_data()
1.2. 折线图
学会使用 Pandas 创建线图(line chart),并理调整绘图的细节(如选择列、大小等)。
(raw.iloc[:,:-2:2] #从第0列开始,到倒数第2列,步长为2.pipe(fix_cols)
)
(raw.iloc[:,:-2:2].pipe(fix_cols).plot()
)
(raw.iloc[:,:-2:2].pipe(fix_cols).Close.plot()
)
(raw.iloc[:,::2].pipe(fix_cols).Volume.plot(figsize=(10,2)) # 10英寸宽,2英寸高
)
1.3. 重采样
重新采样(resampling) 是将时间序列数据的频率从一个粒度转换为另一个粒度的过程,如从每日 → 每月,Pandas 提供了.resample() 方法。
(raw.iloc[:,::2].pipe(fix_cols).Close # 每日收盘价.plot()
)
按月份分组:
(raw.iloc[:,::2].pipe(fix_cols).resample('M') # offset alias.Close
)
(raw.iloc[:,::2].pipe(fix_cols).resample('M') # offset alias.Close.mean().plot() # 索引中有日期,列中有值,这样就可以绘制了
)
1.4. K线图 / 蜡烛图
fig, ax = plt.subplots(figsize=(10,5))
def plot_candle(df, ax):# wickax.vlines(x=df.index, ymin=df.Low, ymax=df.High, colors='k', linewidth=1)# red - decreasered = df.query('Open > Close')ax.vlines(x=red.index, ymin=red.Close, ymax=red.Open, colors='r', linewidth=3)# green - increasegreen = df.query('Open <= Close')ax.vlines(x=green.index, ymin=green.Close, ymax=green.Open, colors='g', linewidth=3)ax.xaxis.set_major_locator(dates.MonthLocator())ax.xaxis.set_major_formatter(dates.DateFormatter('%b-%y'))ax.xaxis.set_minor_locator(dates.DayLocator())return df(raw.iloc[:,::2].pipe(fix_cols).resample('d').agg({'Open':'first', 'High':'max', 'Low':'min', 'Close':'last'}).loc['jan 2018':'jun 2018'].pipe(plot_candle, ax)
)
1.5. 挑战1
Plot the candles for the time period of Sep 2019 to Dec 2019. 可以做做看
2. 计算
Goal
- Explore Pandas methods like
.pct_change
- Plotting with Pandas
- Refactoring to functions
2.1. 收益 / 回报
在金融中,“回报”通常指某个资产价格在两个时间点之间的相对变化。在Pandas中使用.pct_change()
方法计算百分比变化(Percentage Change),默认按前一行计算百分比变化(periods=1)。
# 使用aapl存储
aapl = (raw.iloc[:,::2].pipe(fix_cols))
aapl
# returns
aapl.pct_change()
2.2. 绘制收益图
使用 .plot()
方法,查看回报的日常变化趋势。
# plot returns
(aapl.pct_change().Close.plot()
)
很多高频噪声,看起来像“毛毛虫”:
使用 .hist()
方法来观察回报的 分布情况(正负、极值、对称性等)。
# Histogram of Returns
(aapl.pct_change().Close.hist()
)
bins=30 表示分成 30 个区间,可以更细致地看到分布:
# Change bins
(aapl.pct_change().Close.hist(bins=30)
)
条形图用于查看一小段时间内每日的正负回报(比如最近 100 天):
# Understanding plotting in Pandas is a huge lever
# Bar Plot Returns
(aapl.pct_change().Close.iloc[-100:] # 获取最后100个值.plot.bar()
)
Pandas 会把日期索引变成“分类变量”,导致 X 轴标签重叠、无法格式化。
即使调整标签,也会显示成 1970-01 等错误日期:
# Bar Plot of Returns
# Sadly dates are broken with Pandas bar plots
# 1970s?
fig, ax = plt.subplots(figsize=(10, 4))
(aapl.pct_change().Close.iloc[-100:].plot.bar(ax=ax)
)
ax.xaxis.set_major_locator(dates.MonthLocator())
ax.xaxis.set_major_formatter(dates.DateFormatter('%b-%y'))
ax.xaxis.set_minor_locator(dates.DayLocator())
解决办法:使用 Matplotlib 手动绘制条形图
# Returns - using matplotlib
def my_bar(ser, ax):ax.bar(ser.index, ser)ax.xaxis.set_major_locator(dates.MonthLocator())ax.xaxis.set_major_formatter(dates.DateFormatter('%b-%y'))ax.xaxis.set_minor_locator(dates.DayLocator())return serfig, ax = plt.subplots(figsize=(10, 4))
_ = (aapl.pct_change().Close.iloc[-100:].pipe(my_bar, ax)
)
2.3. 累积收益
Goal:
- More complicated Pandas
- Refactoring into a function
- Explore source
- Creating new columns with
.assign
- Illustrate lambda
Cumulative Returns is the amount that investment has gained or lost over time:
(current_price-original_price)/original_price
(aapl.Close.plot())
逐步计算:
(aapl.Close.sub(aapl.Close[0]) # substract.div(aapl.Close[0]) # divide.plot())
基于日收益的累积积:
# alternatte calculation
(aapl.Close # 取出收盘价序列.pct_change() # 计算日收益率(百分比变化).add(1) # 转换为收益倍率 (1 + r).cumprod() # 计算累积乘积:累计收益倍率.sub(1) # 转换为累计收益率:累计倍率 - 1.plot() # 绘图 )
函数化重构:
# create a function for calculating
def calc_cum_returns(df,col):ser = df[col]return (ser.sub(ser[0]).div(ser[0]))
(aapl.pipe(calc_cum_returns,'Close').plot()
)
# Lambda is an *anonymous function*def get_returns(df):return calc_cum_returns(df,'Close')get_returns(aapl)
lambda这里我不太明白
(lambda df: get_returns(df))(aapl)
# Create a new column
(aapl.assign(cum_returns=lambda df: calc_cum_returns(df,'Close'))
)
# Returns - using matplotlib
def my_bar(ser, ax):ax.bar(ser.index, ser)ax.xaxis.set_major_locator(dates.MonthLocator())ax.xaxis.set_major_formatter(dates.DateFormatter('%b-%y'))ax.xaxis.set_minor_locator(dates.DayLocator())return serfig, ax = plt.subplots(figsize=(10, 4))
_ = (aapl
.pipe(calc_cum_returns, 'Close')
.iloc[-100:]
.pipe(my_bar, ax)
)
2.4. 波动率
Goals
- More complicated Pandas
- Learn about rolling operations
波动性(英语:volatility,又称波动率),指金融资产在一定时间段的变化性。在股票市场,以每天收市价计算的波动性称为历史波幅(Historical Volatility),以股票期权估算的未来波动性称为引申波幅(Implied Volatility)。着名的VIX指数是标准普尔500指数期权的30日引申波幅,以年度化表示。(维基百科)
(aapl.Close.mean()
)
(aapl.Close.std()
)
(aapl.assign(pct_change_close=aapl.Close.pct_change()).pct_change_close.std()
)
以滑动窗口方式计算“过去 N 天”的波动性:
(aapl.assign(close_vol=aapl.rolling(30).Close.std(),per_vol=aapl.Close.pct_change().rolling(30).std()).iloc[:,-2:].plot(subplots=True)
)
以固定周期(如每 15 天)进行分组计算标准差:
# 15 day volatility
(aapl.assign(pct_change_close=aapl.Close.pct_change()) # 创建一个名为百分比变化的列.resample('15D') # 15天为一个分组.std()
)
# 15 day rolling volatility
(aapl.assign(pct_change_close=aapl.Close.pct_change()) # 创建一个名为百分比变化的列.rolling(window=15, min_periods=15).std()
)
# 15 day volatility
# note if column name conflicts with method need to use
# index access ([])
(aapl.assign(pct_change=aapl.Close.pct_change()).rolling(window=15, min_periods=15).std()['pct_change'].plot())
2.5. 挑战2
Plot the rolling volatility over 30-day sliding windows for 2015-2019。(可以参考上面的代码)