前言
记录数理金融实验小学期作业。
一、作业一
(1) 收集上海证券交易所上市的 10 种股票从 2020 年 6 月 1 日-2021 年 5 月 30 日的历史数据, 形成 excel 文件.(要求每位同学需要查询代码为 “60**+ 座位号” 对应的股票信息, 其中 *=0∼9)
·关于数据收集,建议使用三个网站:
1.国内股票:Tushare数据
2.国外股票:https://cn.investing.com/,国外股票有限选择美股
3.全域数据:https://ca.yahoo.com/(需要科学上网)
数据收集建议使用爬虫获取数据,这样可以快速获取大量数据,python实现也方便对数据进行处理,在Tushare数据网站获取国内股票数据实现如下
import tushare as ts
import pandas as pd
# 设置你的Tushare API token
ts.set_token('这里替换为你的tushare的token')//token获取方式见下图
pro = ts.pro_api()
# 生成所有符合条件的股票代码
tickers = []
for i in range(10):
for j in range(10):
ticker = f'30{i}{j}42.SZ'//按照要求生成符合条件股票代码
tickers.append(ticker)
# 定义时间范围
start_date = '20230601'
end_date = '20240530'
# 创建一个 ExcelWriter 对象,指定引擎为 'xlsxwriter',指定文件名
excel_writer = pd.ExcelWriter('shanghai_stocks_data3.xlsx', engine='xlsxwriter')
try:
# 获取每只股票的数据并写入不同的 sheet
for ticker in tickers:
stock_data = pro.daily(ts_code=ticker, start_date=start_date, end_date=end_date)
if not stock_data.empty:
stock_data['Ticker'] = ticker # 添加一列来标识股票代码
sheet_name = ticker.replace('.', '_') # Excel sheet 名称不能包含特殊字符如 '.'
# 按日期排序,由远到近,可按照需求添加数据处理字段
stock_data_sorted = stock_data.sort_values(by='trade_date', ascending=True)
stock_data_sorted.to_excel(excel_writer, sheet_name=sheet_name, index=False)
# 关闭 ExcelWriter 对象(保存文件)
excel_writer.close()
print("Excel文件已成功保存为 'shanghai_stocks_data.xlsx3'")
except Exception as e:
print(f"保存Excel文件时出错:{str(e)}")
import yfinance as yf
import pandas as pd
# 按照要求定义所需的股票代码,这里特定题目要求 "O"开头
tickers = ["PEP", "PFE", "PG", "PNC", "PNW", "PPL", "PRU", "PSX", "PVH", "PXD"]
# 时间范围
start_date = '2023-06-01'
end_date = '2024-05-30'
#抓取数据
stock_data = {}
for ticker in tickers:
try:
data = yf.download(ticker, start=start_date, end=end_date)
stock_data[ticker] = data
except Exception as e:
print(f"Error fetching data for {ticker}: {str(e)}")
# 保存数据
file_name = 'foreign_stocks_data2.xlsx'
with pd.ExcelWriter(file_name, engine='xlsxwriter') as writer:
for ticker, data in stock_data.items():
data.to_excel(writer, sheet_name=ticker)
print(f"Data for {len(stock_data)} stocks saved to '{file_name}'")
(2) 收集国外上市的 10 种股票从 2019 年 9 月 1 日-2020 年 8 月 30 日的 历史数据, 形成 excel 文件.(要求:按照座位号确定代码首字母 A∼Z) 将所有股票价格按照日期排序, 对齐.
在Yahoo网站获取国外股票数据
import yfinance as yf
import pandas as pd
# 按照要求定义所需的股票代码,这里特定题目要求 "O"开头
tickers = ["PEP", "PFE", "PG", "PNC", "PNW", "PPL", "PRU", "PSX", "PVH", "PXD"]
# 时间范围
start_date = '2023-06-01'
end_date = '2024-05-30'
#抓取数据
stock_data = {}
for ticker in tickers:
try:
data = yf.download(ticker, start=start_date, end=end_date)
stock_data[ticker] = data
except Exception as e:
print(f"Error fetching data for {ticker}: {str(e)}")
# 保存数据
file_name = 'foreign_stocks_data2.xlsx'
with pd.ExcelWriter(file_name, engine='xlsxwriter') as writer:
for ticker, data in stock_data.items():
data.to_excel(writer, sheet_name=ticker)
print(f"Data for {len(stock_data)} stocks saved to '{file_name}'")
(3) 对上述每种股票进行收益率的正态检验
需要注意,正态检验结果大部分数据应该都不符合正态分布
Matlab 检验分布命令:
jbtest 格式: H=jbtest(X,ALPHA) 检验 X 在置信度为 1 − α 水平下是否服从正态分布.
建议使用MATLAB实现,示例代码如下(我的日期格式有问题,所以补充了很多调整代码):
% 读取 Excel 文件中的数据
filename = 'shanghai_data.xlsx'; % 修改为你的 Excel 文件路径
sheetnames = {'600141_SH','600241_SH','600641_SH','600741_SH','600841_SH',
'600941_SH','603041_SH','603341_SH','688141_SH','688041_SH'}; % 假设这是每只股票的 sheet 名称
% 创建一个空的收益率数据结构
returns_data = struct();
try
% 循环读取每个 sheet 中的数据并计算收益率
for i = 1:length(sheetnames)
% 从 Excel 中读取数据
[~, ~, raw] = xlsread(filename, sheetnames{i});
% 提取日期和收盘价数据
date_data = raw(2:end, 1); % 日期在第一列,跳过表头
close_prices = cell2mat(raw(2:end, 5)); % 假设收盘价在第五列,跳过表头
% 尝试使用多种格式解析日期字符串
try
if iscellstr(date_data) % 如果日期数据是字符串格式
% 尝试多种日期格式
formats = {'yyyyMMdd', 'dd/MM/yyyy', 'MM/dd/yyyy', 'yyyy-MM-dd'};
for fmt = formats
try
dates = datetime(date_data, 'InputFormat', fmt{1});
break; % 成功解析则跳出循环
catch
% 继续尝试下一个格式
end
end
% 如果没有任何格式成功解析,抛出错误
if ~exist('dates', 'var')
error('所有日期格式尝试均失败');
end
else % 如果日期数据是数值格式
dates = datetime(date_data, 'ConvertFrom', 'excel');
end
catch dateError
fprintf('日期解析失败: %s\n', dateError.message);
continue;
end
% 合并日期和收盘价数据
data_table = table(dates, close_prices);
% 按日期排序
data_table = sortrows(data_table, 'dates', 'ascend'); % 由远及近排序
% 提取排序后的数据
sorted_dates = data_table.dates;
sorted_close_prices = data_table.close_prices;
% 计算收益率
returns = price2ret(sorted_close_prices);
% 正态性检验
[h, p, jbstat, critval] = jbtest(returns);
% 打印检验结果
fprintf('股票代码: %s\n', sheetnames{i});
fprintf('Jarque-Bera检验结果:\n');
fprintf('JB统计量: %f\n', jbstat);
fprintf('临界值: %f\n', critval);
fprintf('p值: %f\n', p);
% 判断并打印是否符合正态分布
if h == 0
fprintf('数据符合正态分布\n');
else
fprintf('数据不符合正态分布\n');
end
% 绘制正态概率图
figure;
normplot(returns);
title(['股票代码: ' sheetnames{i} ' 的正态概率图']);
% 存储收益率数据
returns_data(i).ticker = sheetnames{i};
returns_data(i).dates = sorted_dates;
returns_data(i).returns = returns;
end
catch ME
fprintf('读取或处理数据时出错: %s\n', ME.message);
end
二、作业二
(1) 某股票价格为 42, 期权敲定价格为 40, 六月后到期, 无风险利率为 10%, 波动率为 20%, 分别计算该期权是看涨和看跌时的价格 (使用 B-S 公式与 Monte Carlo 模拟分别计算, 并比较两种结果的差异, 如何弥补这 种差异.)
B-S计算期权价格原理
[call,put]=blsprice(price,strike,rate,time,volatility,yield)
说明:
输入变量 (影响期权价格的六大因素)
price: 标的资产价格
strike: 敲定价格
rate: 无风险利率
time: 期权有效期 (现在到期满的时间)
volatility: 波动率 yield: 红利率 输出变量
call: 欧式看涨期权价格 put: 欧式看跌期权价格
>> [call, put] = blsprice(42, 40, 0.10, 0.5, 0.20, 0)%b-s公式计算期权价格
call =
4.7594
put =
0.8086
Monte Carlo 模拟期权价格
原理: 假设在风险中性市场中, 标的资产服从分布 dSt = rStdt + σStdBt 其中 r 为无风险利率. 期权的损益为 f(ST), T 为到期日. 则期权的价格为 f = e −rTEˆ[f(ST)]. 对于欧式看涨期权, f(ST) = max(ST − X, 0), 在 dSt = rStdt + σStdBt 假设 下, ST = S0 exp[(r − σ 2 2 )T + σ √ Tε] 其中 ε ∼ N(0, 1). 欧式看涨期权的价格为 c = e −rTEˆ[max(0, S0 exp[(r − σ 2 2 )T + σ √ Tε] − X)]
S0 = 42; % 当前股票价格
K = 40; % 行权价
r = 0.10; % 无风险利率
T = 0.5; % 到期时间 (6个月)
sigma = 0.20; % 波动率
N = 10000; % 模拟路径数量
% 生成标准正态分布随机变量
epsilon = randn(N, 1);
% 计算股票价格在到期时的分布
ST = S0 * exp((r - 0.5 * sigma^2) * T + sigma * sqrt(T) * epsilon);
% 计算看涨期权的终值
callPayoff = max(ST - K, 0);
% 折现期权终值到当前时刻
callPrice = exp(-r * T) * mean(callPayoff);
% 输出结果
fprintf('看涨期权价格: %f\n', callPrice);
分析差异
差异产生原因:
a. Monte Carlo模拟使用随机数,每次运行的结果都会有些不同
b. 数值计算中浮点数可能会带来微小的误差
c. Monte Carlo模拟的路径数量影响结果的准确性,路径越多越准确
减小差异可以采取的策略:
a. 多次模拟生成数取平均值
b. 增加路径数量,即代码中N的值
c. 使用更精确的随机数,使用更高质量的随机数生成器或更复杂的随机数生成方法
(2) 计算 (1) 中所述两种期权对应的所有敏感度.
使用Matlab内置的Black-Scholes函数来计算期权的敏感度(Greeks)
Delta: 期权价格对标的资产价格的敏感度 [calldelta,putdelta]=blsdelta(price,strike,rate,time,volatility,yield)
Gamma: Delta 对标的资产价格的敏感度 (看涨与看跌有相同的敏 感度) Gamma=blsgamma(price,strike,rate,time,volatility,yield)
Theta: 期权价格对时间的敏感度 [calltheta,puttheta]=blstheta(price,strike,rate,time,volatility,yield) Rho: 期权价格对无风险利率的敏感度 [callrho,putrho]=blsdelta(price,strike,rate,time,volatility,yield) Vega: 期权价格对标的资产波动率的敏感度 (看涨与看跌相同) Vega=blsvega(price,strike,rate,time,volatility,yield)
price = 42; % 当前股票价格
strike = 40; % 行权价
rate = 0.10; % 无风险利率
time = 0.5; % 到期时间 (6个月)
volatility = 0.20; % 波动率
yield = 0; % 股息收益率
% 计算Delta
[calldelta, putdelta] = blsdelta(price, strike, rate, time, volatility, yield);
% 计算Gamma
gamma = blsgamma(price, strike, rate, time, volatility, yield);
% 计算Theta
[calltheta, puttheta] = blstheta(price, strike, rate, time, volatility, yield);
% 计算Rho
[callrho, putrho] = blsrho(price, strike, rate, time, volatility, yield);
% 计算Vega
vega = blsvega(price, strike, rate, time, volatility, yield);
% 输出结果
fprintf('看涨期权Delta: %f\n', calldelta);
fprintf('看跌期权Delta: %f\n', putdelta);
fprintf('Gamma: %f\n', gamma);
fprintf('看涨期权Theta: %f\n', calltheta);
fprintf('看跌期权Theta: %f\n', puttheta);
fprintf('看涨期权Rho: %f\n', callrho);
fprintf('看跌期权Rho: %f\n', putrho);
fprintf('Vega: %f\n', vega);