54  绘制曲线图

54.1 引言曲线图的力量

曲线图(Line Plot)是最基础也是最重要的数据可视化形式之一。在金融领域,曲线图帮助我们: - 观察趋势:识别价格的上升、下降、震荡趋势 - 发现模式:识别季节性、周期性模式 - 比较表现:对比不同资产的表现 - 监控变化:实时跟踪关键指标的变化

54.2 曲线图的数学基础

曲线图本质上是函数的可视化: \[ y = f(x) \]

在金融时间序列中: - 自变量 \(x\): 时间 \(t\) - 因变量 \(y\): 价格、收益率、成交量等

连续与离散: - 理论上,价格是离散的(仅在交易时刻有定义) - 但为了可视化,我们用线段连接相邻点,形成连续曲线 - 这种线性插值假设价格在交易间隔内平滑变化

54.3 基础曲线图单条线

平台任务1解答代码

以下代码与教学平台任务要求完全一致:

列表 54.1
# 注:numpy_financial包本地未安装,但平台已经内置
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import numpy as np  # 导入NumPy数值计算库
import pandas as pd  # 导入Pandas数据分析库
import matplotlib.pyplot as plt    #导入matplotlab的子模块pyplot
import  numpy_financial as nmf  # 导入NumPy库
plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置Matplotlib全局参数

r = 0.049 #贷款的年利率
n = 30   #贷款的期限(年)
principle = 6e6 #贷款的本金
pay_month = nmf.pmt(rate=r/12,nper=n*12,pv=principle,fv=0,when="end")  # 计算等额本息月供金额
print("在等额本息规则下丁先生每月偿还的金额",-round(pay_month,2))  # 输出在等额本息规则下丁先生每月偿还的金额
t = np.arange(n*12)+1  #生成一个包含每次还款期限长度的数组
principle_pay_month = nmf.ppmt(rate=r/12,per=t,nper=n*12,pv=principle,fv=0,when="end") #计算每月偿还的本金额
interest_pay_month = nmf.ipmt(rate=r/12,per=t,nper=n*12,pv=principle,fv=0,when="end")  #计算每月偿还的利息额
pay_month_array = pay_month*np.ones_like(principle_pay_month) #创建一个每月偿还的数组
plt.figure(figsize=(9,6))  # 创建图形画布
plt.plot(t,-pay_month_array,"r-",label=u"每月偿还金额",lw=2.5)  # 绑制折线图
plt.plot(t,-principle_pay_month,"m--",label=u"每月偿还本金金额",lw=2.5)  # 绑制折线图
plt.plot(t,-interest_pay_month,"b--",label =u"每月产股韩利息金额",lw=2.5)  # 绑制折线图
plt.xticks(fontsize=14)  # 设置X轴刻度标签
plt.xlabel(u"逐次偿还的期限(月)",fontsize=14)  # 设置X轴标签
plt.yticks(fontsize=13)  # 设置Y轴刻度标签
plt.ylabel(u"金额",fontsize=14)  # 设置Y轴标签
plt.title(u"等额本息还款规则下每月偿还的金额以及本金额与利息额")  # 设置图表标题
plt.legend(loc=0,fontsize=13)  # 添加图例
plt.grid()  # 显示网格线
plt.savefig("1.png")  # 保存图形至文件

平台任务2解答代码

以下代码与教学平台任务要求完全一致:

列表 54.2
# 注:numpy_financial包本地未安装,但平台已经内置
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import numpy as np  # 导入NumPy数值计算库
import matplotlib.pyplot as plt  #导入matplotlab的子模块pyplot
import numpy_financial as nmf  # 导入NumPy库
plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置Matplotlib全局参数

r = 0.049 #贷款的年利率
n = 30   #贷款的期限(年)
principle = 6e6 #贷款的本金

r_list = np.linspace(0.02,0.08,100) #生成贷款利率的一个数组
pay_month = nmf.pmt(rate=r/12,nper=n*12,pv=principle,fv=0,when="end")  # 计算等额本息月供金额
pay_month_list = nmf.pmt(rate=r_list/12,nper=n*12,pv=principle,fv=0,when="end") #计算不同贷款利率条件下的每月偿还本息之和
plt.figure(figsize=(9,6))  # 创建图形画布
plt.plot(r_list,-pay_month_list,"r-",label=u"每月偿还金额",lw=2.5)  # 绑制折线图
plt.plot(r,-pay_month,"bo",label=u"贷款利率4.9%的每月偿还金额")  # 绑制折线图
plt.xticks(fontsize=14)  # 设置X轴刻度标签
plt.xlabel(u"贷款利率",fontsize=14)  # 设置X轴标签
plt.yticks(fontsize=14)  # 设置Y轴刻度标签
plt.ylabel(u"金额",fontsize=14,rotation=90)  # 设置Y轴标签
plt.legend(loc=0,fontsize=13)  # 添加图例
plt.grid()  # 显示网格线
plt.savefig("2.png")  # 保存图形至文件

平台任务3解答代码

以下代码与教学平台任务要求完全一致:

列表 54.3
# 注:numpy_financial包本地未安装,但平台已经内置
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import numpy as np  # 导入NumPy数值计算库
import matplotlib.pyplot as plt #导入matplotlab的子模块pyplot
import numpy_financial as nmf  # 导入NumPy库
plt.rcParams["font.sans-serif"] = ["SimHei"]  # 设置Matplotlib全局参数

r = 0.049 #贷款的年利率
n = 30  #贷款的期限(年)
principle = 6e6 #贷款的本金
t = np.arange(n*12)+1 #生成一个包含每次还款期限长度的数组
prin_month = principle/(n*12) #计算等额本金还款规则下的每月本金还款额
prin_month_array = np.ones(n*12)*prin_month #生成一个每月本金还款额的数组
int_month_array = np.zeros_like(prin_month_array) #生成存放每月利息还款额的初始数组
for i in np.arange(n*12):  # 遍历np.arange(n*12)中的每个i
 int_month_array[i] = (principle-i*prin_month)*r/12 #计算逐月支付的利息额
pay_total_month = prin_month_array+int_month_array #j计算等额本金还款规则下的每月还款总额
plt.figure(figsize=(9,6))  # 创建图形画布
plt.plot(t,pay_total_month,"m-",label=u"每月偿还金额",lw=2.5)  # 绑制折线图
plt.plot(t,prin_month_array,"y--",label=u"每月偿还本金额",lw=2.5)  # 绑制折线图
plt.plot(t,int_month_array,"c--",label =u"每月偿还利息额",lw=2.5)  # 绑制折线图
plt.xticks(fontsize=14)  # 设置X轴刻度标签
plt.xlabel(u"逐次偿还的期限(月)",fontsize=14)  # 设置X轴标签
plt.yticks(fontsize=14)  # 设置Y轴刻度标签
plt.ylabel(u"金额",fontsize=14,rotation=90)  # 设置Y轴标签
plt.title(u"等额本金还款规则下每月偿还的金额以及本金额与利息额",fontsize=14)  # 设置图表标题
plt.legend(loc=0,fontsize=13)  # 添加图例
plt.grid()  # 显示网格线
plt.savefig("3.png")  # 保存图形至文件
列表 54.4
# =============================================================================
# 题目:基础曲线图——贵州茅台股价走势
# =============================================================================
# 本任务演示如何使用Matplotlib绘制基础曲线图,展示单条时间序列数据
# 应用场景:可视化单个资产的股价走势

# ==================== 导入必要的库 ====================
import matplotlib.pyplot as plt  # Matplotlib的pyplot接口,提供类似MATLAB的绘图API
import pandas as pd                 # Pandas数据分析库,用于数据处理
import numpy as np                  # NumPy数值计算库

# ==================== 设置中文字体支持 ====================
# Matplotlib默认不支持中文显示,需要手动设置中文字体
# plt.rcParams是一个全局配置字典,用于控制绘图的各种属性
plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置默认字体为黑体(SimHei),确保中文正常显示
plt.rcParams['axes.unicode_minus'] = False    # 解决负号'-'显示为方块的问题
# 这两行设置确保图表中的中文文字和负号都能正确显示

# ==================== 创建示例数据 ====================
# 场景:模拟贵州茅台30个交易日的股价数据
# pd.date_range()生成日期序列
# '2024-01-01':起始日期
# periods=30:生成30个日期(30个交易日)
dates = pd.date_range('2024-01-01', periods=30)

# 使用列表推导式生成模拟股价数据
# 基础价格1850元
# i * 2:每天上涨2元(模拟上涨趋势)
# np.random.randn() * 10:添加随机波动,标准差为10元
prices = [1850 + i * 2 + np.random.randn() * 10 for i in range(30)]
# prices是一个包含30个价格的列表,模拟真实股价的随机波动

# 创建DataFrame便于数据管理
# DataFrame是Pandas的核心数据结构,类似Excel表格
df = pd.DataFrame({
    '日期': dates,      # 日期列,作为X轴
    '收盘价': prices    # 收盘价列,作为Y轴
})

# ==================== 绘制基础曲线图 ====================
# plt.figure()创建一个新的图形窗口
# figsize=(12, 6):图形大小为12英寸宽×6英寸高
# 这个比例适合展示时间序列数据,宽度较大以容纳更多时间点
plt.figure(figsize=(12, 6))

# plt.plot()绘制折线图(曲线图)
# df['日期']:X轴数据(时间序列)
# df['收盘价']:Y轴数据(价格)
# linewidth=2:线条宽度为2,使折线更清晰可见
# color='#2E86AB':线条颜色,使用十六进制颜色代码(深蓝色)
plt.plot(df['日期'], df['收盘价'], linewidth=2, color='#2E86AB')

# ==================== 设置图表装饰 ====================
# plt.title()设置图表标题
# fontsize=16:标题字号为16(较大,突出显示)
# fontweight='bold':标题文字加粗
plt.title('贵州茅台股价走势', fontsize=16, fontweight='bold')

# plt.xlabel()设置X轴标签
# fontsize=12:标签字号为12
plt.xlabel('日期', fontsize=12)

# plt.ylabel()设置Y轴标签
plt.ylabel('收盘价(元)', fontsize=12)

# plt.grid()添加网格线,帮助读取数值
# True:显示网格线
# alpha=0.3:网格线透明度为0.3(使网格不显眼,不干扰数据)
plt.grid(True, alpha=0.3)

# plt.xticks()设置X轴刻度标签的显示方式
# rotation=45:刻度标签旋转45度(防止日期文字重叠)
plt.xticks(rotation=45)

# plt.tight_layout()自动调整布局,防止元素重叠(如标题被截断)
plt.tight_layout()

# plt.show()将图形显示出来
plt.show()

# ==================== 输出数据统计信息 ====================
print('数据统计:')
# df['收盘价'].describe()计算收盘价的描述性统计量
# 包括:计数、均值、标准差、最小值、四分位数、最大值
print(df['收盘价'].describe())

代码解析:

  1. 中文字体设置:

    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False
    • SimHei:黑体,确保中文正常显示
    • unicode_minus:解决负号显示问题
  2. plot函数参数:

    • linewidth:线宽,数值越大线越粗
    • color:颜色,可以用十六进制、RGB、英文名称
    • linestyle:线型,'-'(实线)、'--'(虚线)、':'(点线)
  3. 图表装饰:

    • title:标题
    • xlabel/ylabel:轴标签
    • grid:网格线
    • tight_layout:自动调整布局,避免元素重叠

54.4 多条曲线对比

列表 54.5
# =============================================================================
# 题目:多条曲线对比——不同股票价格走势
# =============================================================================
# 本任务演示如何在同一图表中绘制多条曲线,对比不同资产的表现

# ==================== 创建多只股票数据 ====================
# 场景:模拟三只股票30个交易日的价格数据
# 使用之前生成的dates作为时间序列
stocks_data = pd.DataFrame({
    '日期': dates,
    # 贵州茅台:高价股(1850元起),每天上涨1.5元,标准差8元
    '贵州茅台': [1850 + i * 1.5 + np.random.randn() * 8 for i in range(30)],
    # 五粮液:中价股(220元起),每天上涨0.8元,标准差5元
    '五粮液': [220 + i * 0.8 + np.random.randn() * 5 for i in range(30)],
    # 招商银行:低价股(45元起),每天上涨0.3元,标准差2元
    '招商银行': [45 + i * 0.3 + np.random.randn() * 2 for i in range(30)]
})

# ==================== 绘制多条曲线 ====================
plt.figure(figsize=(12, 6))

# 绘制第一条曲线:贵州茅台(红色)
# label参数设置图例标签,将在图例中显示
plt.plot(stocks_data['日期'], stocks_data['贵州茅台'],
         label='贵州茅台', linewidth=2, color='#E3120B')

# 绘制第二条曲线:五粮液(青色)
plt.plot(stocks_data['日期'], stocks_data['五粮液'],
         label='五粮液', linewidth=2, color='#008080')

# 绘制第三条曲线:招商银行(深灰色)
plt.plot(stocks_data['日期'], stocks_data['招商银行'],
         label='招商银行', linewidth=2, color='#2C3E50')

# ==================== 设置图表装饰 ====================
plt.title('不同股票价格走势对比', fontsize=16, fontweight='bold')
plt.xlabel('日期', fontsize=12)
plt.ylabel('收盘价(元)', fontsize=12)

# plt.legend()显示图例
# loc='best':自动选择最佳位置(避免遮挡数据)
# fontsize=11:图例字号为11
plt.legend(loc='best', fontsize=11)

plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

# ==================== 输出各股票统计信息 ====================
print('各股票统计信息:')
# describe()对每只股票分别计算统计量
print(stocks_data.describe())

视觉层次设计:

当绘制多条曲线时,需要考虑: 1. 颜色选择:使用对比度高、易于区分的颜色 2. 线宽:主要数据用较粗的线,次要数据用较细的线 3. 图例位置:loc='best'自动选择最佳位置 4. Y轴范围:如果数据量级差异大,考虑用双Y轴归一化

54.5 双Y轴图

列表 54.6
# =============================================================================
# 题目:双Y轴图——价格与成交量
# =============================================================================
# 本任务演示如何创建双Y轴图表,同时展示量价关系
# 应用场景:分析价格与成交量、收益率与波动率等不同量级的数据

# ==================== 创建价格和成交量数据 ====================
df_dual = pd.DataFrame({
    '日期': dates,
    # 收盘价:45元起,每天上涨0.5元,标准差3元
    '收盘价': [45 + i * 0.5 + np.random.randn() * 3 for i in range(30)],
    # 成交量:5000手起,每天增加100手,标准差500手
    # 成交量与价格的量级不同(价格是几十,成交量是几千),需要双Y轴
    '成交量': [5000 + i * 100 + np.random.randn() * 500 for i in range(30)]
})

# ==================== 创建图表和轴对象 ====================
# plt.subplots()创建图形和轴对象
# fig:图形对象,包含整个图形
# ax1:左Y轴对象(第一个轴)
# figsize=(12, 6):图形大小
fig, ax1 = plt.subplots(figsize=(12, 6))

# ==================== 绘制左Y轴(价格) ====================
color1 = '#E3120B'  # 红色,用于价格线

# ax1.set_xlabel()设置X轴标签
ax1.set_xlabel('日期', fontsize=12)

# ax1.set_ylabel()设置左Y轴标签
# color=color1:标签颜色与线条颜色一致,便于识别
ax1.set_ylabel('收盘价(元)', color=color1, fontsize=12)

# ax1.plot()在左Y轴上绘制数据
# color=color1:线条颜色
# linewidth=2:线宽
# label='收盘价':图例标签
line1 = ax1.plot(df_dual['日期'], df_dual['收盘价'],
                 color=color1, linewidth=2, label='收盘价')

# ax1.tick_params()设置刻度标签样式
# axis='y':只设置Y轴刻度
# labelcolor=color1:Y轴刻度标签颜色与线条颜色一致
ax1.tick_params(axis='y', labelcolor=color1)

# ax1.grid()只在左Y轴添加网格线
ax1.grid(True, alpha=0.3)

# ==================== 创建共享X轴的右Y轴 ====================
# ax1.twinx()创建一个共享X轴的新Y轴(右Y轴)
# 这样两个Y轴对应同一个X轴(时间轴)
ax2 = ax1.twinx()

# ==================== 绘制右Y轴(成交量) ====================
color2 = '#008080'  # 青色,用于成交量线

# ax2.set_ylabel()设置右Y轴标签
ax2.set_ylabel('成交量(手)', color=color2, fontsize=12)

# ax2.plot()在右Y轴上绘制数据
# linestyle='--':使用虚线,与实线的价格线区分
line2 = ax2.plot(df_dual['日期'], df_dual['成交量'],
                 color=color2, linewidth=2, linestyle='--', label='成交量')

# ax2.tick_params()设置右Y轴刻度标签样式
ax2.tick_params(axis='y', labelcolor=color2)

# ==================== 合并图例 ====================
# 因为ax1和ax2各有图例,需要合并成一个统一的图例
# lines = line1 + line2:合并两条线
lines = line1 + line2
# [l.get_label() for l in lines]:提取每条线的标签
labels = [l.get_label() for l in lines]
# ax1.legend()在左Y轴上显示合并后的图例
# lines:线条对象列表
# labels:标签列表
ax1.legend(lines, labels, loc='best', fontsize=11)

# ==================== 设置标题并显示 ====================
plt.title('价格与成交量走势', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()

双Y轴的使用场景:

  1. 量价关系:价格与成交量
  2. 收益率与波动率:收益与风险
  3. 价格与指标:价格与移动平均线、RSI等技术指标
  4. 不同量级数据:股价(元)与市值(万亿元)

注意事项: - 双Y轴可能误导读者,需谨慎使用 - 明确标注哪个轴对应哪条线 - 考虑用子图(subplots)替代,更清晰

54.6 填充区域图

列表 54.7
# =============================================================================
# 题目:填充区域图——价格区间
# =============================================================================
# 本任务演示如何绘制填充区域图,展示预测区间或置信区间
# 应用场景:布林带、价格预测区间、期权盈亏区间

# ==================== 创建带上下限的数据 ====================
df_area = pd.DataFrame({
    '日期': dates,
    # 价格:100元起,每天上涨1元,标准差3元
    '价格': [100 + i + np.random.randn() * 3 for i in range(30)],
    # 上限:105元起,每天上涨1元,标准差2元(价格上方5元)
    '上限': [105 + i + np.random.randn() * 2 for i in range(30)],
    # 下限:95元起,每天上涨1元,标准差2元(价格下方5元)
    '下限': [95 + i + np.random.randn() * 2 for i in range(30)]
})

# ==================== 绘制填充区域图 ====================
plt.figure(figsize=(12, 6))

# 绘制实际价格曲线(深灰色)
plt.plot(df_area['日期'], df_area['价格'],
         label='实际价格', linewidth=2, color='#2C3E50')

# plt.fill_between()填充两条曲线之间的区域
# df_area['日期']:X轴数据
# df_area['下限']:区域下边界
# df_area['上限']:区域上边界
# alpha=0.3:透明度0.3(使填充区域不遮挡网格线)
# color='#008080':填充颜色(青色)
# label='预测区间':图例标签
plt.fill_between(df_area['日期'], df_area['下限'], df_area['上限'],
                 alpha=0.3, color='#008080', label='预测区间')

# ==================== 设置图表装饰 ====================
plt.title('价格走势与预测区间', fontsize=16, fontweight='bold')
plt.xlabel('日期', fontsize=12)
plt.ylabel('价格(元)', fontsize=12)
plt.legend(loc='best', fontsize=11)
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

# ==================== 计算区间宽度统计 ====================
print('区间宽度统计:')
# 区间宽度 = 上限 - 下限
df_area['区间宽度'] = df_area['上限'] - df_area['下限']
# describe()计算区间宽度的统计量
print(df_area['区间宽度'].describe())

金融应用:

  1. 置信区间:统计模型预测的置信区间
  2. 布林带:移动平均线 ± 标准差
  3. 期权策略:期权到期时的盈亏区间
  4. 风险管理:VaR(在险价值)的区间表示

fill_between参数: - alpha:透明度(0-1),越小越透明 - color:填充颜色 - where:条件填充,只填充满足条件的区域 - interpolate:在边界处插值,使填充更平滑

54.7 堆叠面积图

列表 54.8
# =============================================================================
# 题目:堆叠面积图——投资组合构成
# =============================================================================
# 本任务演示如何绘制堆叠面积图,展示各部分随时间的变化及总体趋势
# 应用场景:投资组合资产配置、公司收入构成、市场份额变化

# ==================== 创建投资组合数据 ====================
df_portfolio = pd.DataFrame({
    '日期': pd.date_range('2024-01-01', periods=12),  # 12个月
    # 股票:100万元起,每月增长5万元,标准差3万元
    '股票': [100 + i*5 + np.random.randn()*3 for i in range(12)],
    # 债券:80万元起,每月增长2万元,标准差2万元
    '债券': [80 + i*2 + np.random.randn()*2 for i in range(12)],
    # 现金:20万元起,每月增长0.5万元,标准差1万元
    '现金': [20 + i*0.5 + np.random.randn()*1 for i in range(12)]
})

# ==================== 绘制堆叠面积图 ====================
plt.figure(figsize=(12, 6))

# plt.stackplot()绘制堆叠面积图
# df_portfolio['日期']:X轴数据(时间)
# 接下来的三个参数是要堆叠的Y值序列
# df_portfolio['股票']:第一层(最底层)
# df_portfolio['债券']:第二层(堆叠在股票之上)
# df_portfolio['现金']:第三层(最顶层)
# alpha=0.8:透明度0.8(使区域不太透明)
# colors:颜色列表,分别对应三个序列
# labels:标签列表,用于图例
plt.stackplot(df_portfolio['日期'],
              df_portfolio['股票'],
              df_portfolio['债券'],
              df_portfolio['现金'],
              alpha=0.8,
              colors=['#E3120B', '#008080', '#F0A700'],
              labels=['股票', '债券', '现金'])

# ==================== 设置图表装饰 ====================
plt.title('投资组合资产配置变化', fontsize=16, fontweight='bold')
plt.xlabel('日期', fontsize=12)
plt.ylabel('资产价值(万元)', fontsize=12)

# loc='upper left':图例位置在左上角
plt.legend(loc='upper left', fontsize=11)

# axis='y':只显示Y轴网格线(水平网格线)
plt.grid(True, alpha=0.3, axis='y')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

# ==================== 计算各资产占比 ====================
print('各资产占比:')
# total:每月的资产总值(股票+债券+现金)
# sum(axis=1):按行求和(横向求和)
total = df_portfolio[['股票', '债券', '现金']].sum(axis=1)

# 计算最新月份(最后一行)各资产占比
for asset in ['股票', '债券', '现金']:
    # df_portfolio[asset].iloc[-1]:获取该资产的最后一行值(最新值)
    # total.iloc[-1]:获取总资产的最后一行值
    ratio = df_portfolio[asset].iloc[-1] / total.iloc[-1]
    # f'{asset}: {ratio:.1%}':格式化输出,百分比保留1位小数
    print(f'{asset}: {ratio:.1%}')

堆叠图的数学含义:

\[ y_{\text{total}} = y_1 + y_2 + \cdots + y_n \]

每层的实际值: \[ y_{\text{layer }i} = \sum_{j=1}^i y_j \]

金融应用: - 资产配置:展示股票、债券、现金的配置变化 - 收入构成:主营业务收入、其他业务收入 - 市场份额:不同公司在市场中的份额变化

54.8 对数坐标轴

列表 54.9
# =============================================================================
# 题目:对数Y轴——复利增长可视化
# =============================================================================
# 本任务演示如何使用对数坐标轴展示指数增长数据
# 应用场景:复利增长、投资回报、跨越数量级的数据

# ==================== 创建复利增长数据 ====================
# years:年份序列,从2000年到2023年
years = np.arange(2000, 2024)

initial_value = 100      # 初始值:100元
growth_rate = 0.15       # 年化增长率:15%
# values:使用复利公式计算每年的价值
# (1 + growth_rate) ** (years - 2000):复利因子
# 例如:2000年:(1.15)^0 = 1,价值100元
#       2001年:(1.15)^1 = 1.15,价值115元
#       2002年:(1.15)^2 = 1.3225,价值132.25元
values = initial_value * (1 + growth_rate) ** (years - 2000)

# ==================== 创建对比:线性vs对数 ====================
# plt.subplots(1, 2, figsize=(15, 6))创建1行2列的子图布局
# fig:图形对象
# (ax1, ax2):两个轴对象的元组
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))

# ==================== 线性坐标(左图) ====================
# ax1.plot()在第一个子图绘制
ax1.plot(years, values, linewidth=2, color='#E3120B')
ax1.set_title('线性坐标', fontsize=14, fontweight='bold')
ax1.set_xlabel('年份', fontsize=12)
ax1.set_ylabel('价值(元)', fontsize=12)
ax1.grid(True, alpha=0.3)

# ==================== 对数坐标(右图) ====================
# ax2.semilogy()绘制Y轴为对数坐标的图表
# semilogy = semi-logarithmic y-axis(Y轴对数化)
# 在对数坐标下,指数增长呈直线
ax2.semilogy(years, values, linewidth=2, color='#008080')
ax2.set_title('对数坐标', fontsize=14, fontweight='bold')
ax2.set_xlabel('年份', fontsize=12)
# Y轴标签注明"对数",提醒读者
ax2.set_ylabel('价值(元,对数)', fontsize=12)
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# ==================== 输出复利增长分析 ====================
print('复利增长分析:')
print(f'初始值: {initial_value}元')
print(f'年化增长率: {growth_rate:.1%}')
print(f'2023年价值: {values[-1]:.2f}元')
# 计算总增长倍数
print(f'总增长倍数: {values[-1]/initial_value:.1f}倍')

对数坐标的金融意义:

线性坐标:展示绝对值变化 对数坐标:展示相对变化(百分比)

在金融中: - 复利效应:对数坐标下,恒定增长率呈直线 - 收益率比较:不同起始点的资产可以直接比较斜率 - 长期趋势:更适合展示跨越数量级的数据

数学原理: \[ \ln(y) = \ln(A \cdot e^{rt}) = \ln(A) + rt \]

在半对数图上,指数增长呈直线,斜率即为增长率 \(r\)

54.9 金融应用移动平均线

列表 54.10
# =============================================================================
# 题目:移动平均线——技术分析基础
# =============================================================================
# 本任务演示如何绘制移动平均线,识别金叉和死叉信号
# 应用场景:技术分析、趋势跟踪、交易信号生成

# ==================== 创建价格数据 ====================
# np.random.seed(42)设置随机种子,确保每次运行结果相同
np.random.seed(42)

# df_price:模拟60个交易日的股价数据
df_price = pd.DataFrame({
    '日期': pd.date_range('2024-01-01', periods=60),
    # 收盘价:100元起
    # np.random.normal(0.5, 2, 60)生成60个正态分布随机数(均值0.5,标准差2)
    # np.cumsum()计算累积和,模拟价格的随机游走
    '收盘价': 100 + np.cumsum(np.random.normal(0.5, 2, 60))
})

# ==================== 计算移动平均线 ====================
# .rolling(window=5).mean():5日移动平均线
# rolling(5):创建一个5日的滚动窗口
# mean():计算窗口内数据的均值
df_price['MA5'] = df_price['收盘价'].rolling(window=5).mean()

# 20日移动平均线(中期趋势线)
df_price['MA20'] = df_price['收盘价'].rolling(window=20).mean()

# 60日移动平均线(长期趋势线)
df_price['MA60'] = df_price['收盘价'].rolling(window=60).mean()

# ==================== 绘制移动平均线 ====================
plt.figure(figsize=(14, 7))

# 绘制收盘价(深灰色,半透明)
plt.plot(df_price['日期'], df_price['收盘价'],
         label='收盘价', linewidth=1.5, color='#2C3E50', alpha=0.7)

# 绘制MA5(红色,短期均线)
plt.plot(df_price['日期'], df_price['MA5'],
         label='MA5', linewidth=1.5, color='#E3120B')

# 绘制MA20(青色,中期均线)
plt.plot(df_price['日期'], df_price['MA20'],
         label='MA20', linewidth=1.5, color='#008080')

# 绘制MA60(黄色,长期均线,线宽加粗)
plt.plot(df_price['日期'], df_price['MA60'],
         label='MA60', linewidth=2, color='#F0A700')

# ==================== 标注金叉和死叉 ====================
# 金叉:短期均线上穿长期均线(买入信号)
# (MA5 > MA20):当前MA5大于MA20
# .shift(1):将数据下移一行,获取前一日的值
# (MA5.shift(1) <= MA20.shift(1)):前一日的MA5小于等于MA20
# 两个条件都满足时,表示MA5刚刚上穿MA20
golden_cross = (df_price['MA5'] > df_price['MA20']) & (df_price['MA5'].shift(1) <= df_price['MA20'].shift(1))

# 死叉:短期均线下穿长期均线(卖出信号)
death_cross = (df_price['MA5'] < df_price['MA20']) & (df_price['MA5'].shift(1) >= df_price['MA20'].shift(1))

# plt.scatter()绘制金叉点(红色向上三角)
# df_price['日期'][golden_cross]:金叉发生的日期
# df_price['MA5'][golden_cross]:金叉点的MA5值
# color='red':红色
# s=100:散点大小为100
# marker='^':向上三角符号
# label='金叉':图例标签
# zorder=5:图层顺序为5(确保散点显示在线条上方)
plt.scatter(df_price['日期'][golden_cross], df_price['MA5'][golden_cross],
           color='red', s=100, marker='^', label='金叉', zorder=5)

# plt.scatter()绘制死叉点(绿色向下三角)
plt.scatter(df_price['日期'][death_cross], df_price['MA5'][death_cross],
           color='green', s=100, marker='v', label='死叉', zorder=5)

# ==================== 设置图表装饰 ====================
plt.title('移动平均线与交叉信号', fontsize=16, fontweight='bold')
plt.xlabel('日期', fontsize=12)
plt.ylabel('价格(元)', fontsize=12)
plt.legend(loc='best', fontsize=11)
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

# ==================== 输出移动平均线统计 ====================
print('移动平均线统计:')
print(df_price[['收盘价', 'MA5', 'MA20', 'MA60']].describe())

移动平均线的金融理论:

简单移动平均(Simple Moving Average, SMA): \[ \text{SMA}_t = \frac{1}{n}\sum_{i=0}^{n-1} P_{t-i} \]

交易信号: - 金叉(Golden Cross):短期均线上穿长期均线,买入信号 - 死叉(Death Cross):短期均线下穿长期均线,卖出信号

局限性: - 滞后性:基于历史价格,信号滞后 - 震荡市场:在横盘震荡中频繁产生错误信号 - 参数敏感性:不同周期效果差异大