案例背景
人工智能概念如火如荼的夏天,在这个2024年,我觉得需要提早布局一下这个概念。所以我们找一下A股里面人们的人工智能概念股,然后分析他们的数据应用三因子模型,也就是最经典的资本资产定价模型的衍生版去研究他们各自的投资价值。
(本案例仅用于研究学术分享,不构成任何投资建议。)
数据介绍
本次案例只有三因子数据是本地的,其他的数据都爬虫或者API接口获得,从互联网上爬取,首先从这个网站上获取:人工智能概念股名单一览_2024A股、B股人工智能概念上市公司有哪些 – 华西证券,热门的概念的股票名称,然后再使用akshare这个非常好用且免费的金融数据库,获取这些股票的交易数据,然后再采用三因子模型进行回归对比评估。
当然数据我也下载到本地了,和三因子数据打包一起放在这里,需要的这些数据和全部代的同学可以参考:人工智能data
代码实现
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import statsmodels.api as sm
plt.rcParams ['font.sans-serif'] ='SimHei' #显示中文
plt.rcParams ['axes.unicode_minus']=False #显示负号
获取人工智能概念股票
下面直接进行爬虫,懂网页文件的同学可以看看代码,不懂的同学直接用就行
import requests
from bs4 import BeautifulSoup
# 目标网页地址
url = 'https://m.hx168.com.cn/stock/concept/BK2196.html'
# 发送HTTP请求获取网页内容
response = requests.get(url)
response.encoding = 'utf-8' # 确保中文正确显示
# 解析网页
soup = BeautifulSoup(response.text, 'html.parser')
def get_code(class_name=''):
# 定位到目标表格
tables = soup.find_all('table', {'class': 'am-table am-table-bordered am-table-striped am-text-nowrap'})
#print(len(tables))
dfs=pd.DataFrame()
for table in tables:
# 解析表格标题
headings = [th.get_text().strip() for th in table.find('thead').find_all('th')]
# 解析表格数据
data = []
for row in table.find('tbody').find_all('tr'):
cells = row.find_all('td')
if len(cells) > 0: # 确保行内有数据
data_row = []
for cell in cells:
a_tag = cell.find('a')
if a_tag:
data_row.append(a_tag.get_text().strip()) # 如果单元格内有链接,则获取链接文本
else:
data_row.append(cell.get_text().strip())
data.append(data_row)
df=pd.DataFrame(data=data,columns=headings)
dfs=pd.concat([dfs,df],axis=0,ignore_index=True)
# 打印结果
return dfs
运行然后,查看:
dfs=get_code()
dfs.head()
然后我们用列表跟字典把它们这些名称和代码都装起来。
code_name=dict(zip(dfs['股票代码'],dfs['股票简称']))
code_list=dfs['股票代码'].to_list()
获取这些股票的交易数据
自定义一个函数从ak share这个库里面获取
import akshare as ak
# 定义获取A股历史交易数据的函数
def get_stock_data(stock_code, start_date, end_date):
"""
:param stock_code: 股票代码,如 '000001'
:param start_date: 开始日期,格式为 'YYYYMMDD'
:param end_date: 结束日期,格式为 'YYYYMMDD'
:return: 指定时间段内的股票交易数据(DataFrame)
"""
# 使用 AkShare 的 stock_zh_a_hist 接口获取数据
stock_df = ak.stock_zh_a_hist(symbol=stock_code, period="daily", start_date=start_date, end_date=end_date, adjust="qfq")
stock_df['收益率'] = stock_df['收盘'].pct_change()
return stock_df.dropna()
下面开始循环遍历所有的代码,然后获取他们的交易数据。
return_dict={}
#创建一个 ExcelWriter 对象
#writer = pd.ExcelWriter('股票数据.xlsx', engine='xlsxwriter')
for code in code_list:
try:
return_dict[code]=get_stock_data(stock_code=code,start_date='20230201',end_date='20240201')[['日期','收盘','收益率']].set_index('日期')
#get_stock_data(stock_code=code,start_date='20230201',end_date='20240201').to_excel(writer, sheet_name=code)
except:
pass
#writer.save()
#writer.close()
因为存的是字典,我们拿出来看一下其中的一个数据的情况。
return_dict['002415'].head(4)
数据没有什么问题,下面我们开始使用三因子模型去进行分析。
三因子模型
这是gpt写的模型的简介,我觉得还写的挺好的,大家可以看看。
下面读取三因子的数据,三因子的数据。我的数据里面有五个因子。但是我这里只用了三因子就没搞那么多。
#读取三因子数据
three_factors=pd.read_csv('fivefactor_daily.csv')[['trddy','mkt_rf','smb','hml']].rename(columns={'trddy':'日期'}).set_index('日期')
three_factors=three_factors.loc['2023-02-01':'2024-02-01',:]
three_factors.index=pd.to_datetime(three_factors.index)
three_factors.head(3)
- trddy [交易日期]
- mkt_rf [市场风险因子]
- smb [规模风险因子]
- hml [账面市值比风险因子]
- rf [无风险利率]
自定义一些股票里面常用的,评价一个资产表现的函数。
def sum_return_ratio(price_list):
'''实际总收益率'''
price_list = price_list.to_numpy()
return (price_list[-1] - price_list[0]) / price_list[0]
def MaxDrawdown(price_list):
'''最大回撤率'''
i = np.argmax((np.maximum.accumulate(price_list) - price_list) / np.maximum.accumulate(price_list)) # 结束位置
if i == 0:
return 0
j = np.argmax(price_list[:i]) # 开始位置
return (price_list[j] - price_list[i]) / (price_list[j])
def sharpe_ratio(price_list, rf=0.000041):
'''夏普比率'''
# 公式 夏普率 = (回报率均值 - 无风险率) / 回报率的标准差
# pct_change()是pandas里面的自带的计算每日增长率的函数
daily_return = price_list.pct_change()
return (daily_return.mean() - rf) / daily_return.std()
def Information_Ratio(price_list, rf=0.000041):
'''信息比率'''
chaoer = sum_return_ratio(price_list) - ((1 + rf) ** 365 - 1)
return chaoer / np.std(price_list.pct_change() - rf)
def skewness_return(price_list):
'''负偏态收益'''
daily_return = price_list.pct_change()
return daily_return.skew()
def downside_upside_volatility_ratio(price_list):
'''下跌波动比率'''
daily_return = price_list.pct_change()
# 获取下跌和上涨的日子的收益率
returns_down = daily_return[daily_return < 0]
returns_up = daily_return[daily_return > 0]
# 计算各自的天数和收益率平方和
n_down = len(returns_down)
n_up = len(returns_up)
sum_squared_returns_down = (returns_down ** 2).sum()
sum_squared_returns_up = (returns_up ** 2).sum()
# 计算DUVOL
if n_down == 0 or n_up == 0: # 避免除以0
return np.nan
duvol = np.log((n_up * sum_squared_returns_down) / (n_down * sum_squared_returns_up))
return duvol
自定义一个函数,输入这个股票的代码,我们就能够去计算它这些所有的资产表现的评价指标。方便复用。
def deal(code=''):
day_return = return_dict[code]#['收益率']
day_return.index=pd.to_datetime(day_return.index)
zgpa_threefactor = pd.merge(three_factors, day_return,left_index=True, right_index=True)
result = sm.OLS(zgpa_threefactor['收益率'], sm.add_constant(zgpa_threefactor.loc[:,['mkt_rf','smb','hml']])).fit()
betas=result.params
实际总收益率=sum_return_ratio(day_return['收盘'])
最大回测率=MaxDrawdown(day_return['收盘'])
夏普比率=sharpe_ratio(day_return['收盘'])
信息比率=Information_Ratio(day_return['收盘'])
负偏态收益 = skewness_return(day_return['收盘'])
下跌波动比率 = downside_upside_volatility_ratio(day_return['收盘'])
return pd.DataFrame({'阿尔法': betas[0], '市场风险因子MKT': betas[1], '市值因子SMB': betas[2], '账面市值因子HML': betas[3],
'实际总收益率': 实际总收益率, '最大回测率': 最大回测率, '夏普比率': 夏普比率, '信息比率': 信息比率,
'负偏态收益': 负偏态收益, '下跌波动比率': 下跌波动比率, '股票代码': code}, index=[0])
循环遍历去计算
df_results=pd.DataFrame()
for code,df_one in return_dict.items():
result=deal(code=code) ; result['股票名称']=code_name[code]
df_results=pd.concat([df_results,result],axis=0,ignore_index=True)
我们来查看结果
df_results
选出阿尔法前十的股票 来分析画图
阿尔法就是超额收益嘛,也就是回归里面的截距,我们选出前10的来画图看看。
df_results=df_results[['股票代码', '股票名称','阿尔法', '市场风险因子MKT', '市值因子SMB', '账面市值因子HML', '实际总收益率', '最大回测率', '夏普比率', '信息比率','负偏态收益','下跌波动比率']].sort_values(by='阿尔法',ascending=False)
df_results.head(10)
plt.figure(figsize=(10, 8),dpi=128)
# 创建多子图布局
for i, column in enumerate(['阿尔法', '市场风险因子MKT', '市值因子SMB', '账面市值因子HML', '实际总收益率', '最大回测率', '夏普比率', '信息比率','负偏态收益','下跌波动比率'], 1):
plt.subplot(5, 2, i)
plt.bar(df_results.head(10)['股票名称'], df_results.head(10)[column], color='skyblue')
plt.title(column)
plt.xticks(rotation=45) # 旋转标签,避免重叠
# 调整布局
plt.tight_layout()
plt.show()
通过对比各股票的阿尔法值(Alpha)、贝塔值(Beta)、市值因子(SMB)、账面市值因子(HML)、实际总收益率、最大回测率、夏普比率和信息比率这些指标来分析和比较不同股票的表现。
阿尔法值(Alpha): 表示投资组合相对于基准业绩的超额回报,是投资者获取的与市场无关的回报。阿尔法值越高,说明股票表现越好。在您的列表中,昆仑万维的阿尔法值最高,表示在考虑风险因素后,其超额回报最高。
贝塔值(Beta): 表示股票相对于整个市场的波动性。贝塔值大于1意味着股票的价格波动大于市场平均水平,小于1则表示波动性小于市场。例如,昆仑万维的贝塔值是2.564679,这意味着它比市场波动性大,风险较高。
市值因子(SMB)和账面市值因子(HML): SMB表示小市值公司相对于大市值公司的超额回报,HML表示高账面市值比的公司相对于低账面市值比公司的超额回报。在您的数据中,鸿博股份的SMB值最高,表明它在小市值公司中表现较好;而同样的鸿博股份的HML值也是正的,意味着高账面市值比的公司表现更好。
实际总收益率: 表明股票在某段时间内的总回报。例如,中科信息的实际总收益率最高,说明它在过去一段时间内的表现最好。
最大回撤率: 表示在选定的周期内,投资组合可能遭受的最大损失。低最大回撤率意味着下跌风险较小。例如,柯力传感的最大回撤率最低,说明其价格下跌的风险相对较小。
夏普比率: 表示投资的每单位风险带来的超额回报,夏普比率越高,意味着单位风险带来的超额回报越高。鸿博股份的夏普比率最高,表明它在风险调整后的回报上表现最佳。
信息比率: 表示投资组合超额回报相对于跟踪误差的比率,信息比率越高,说明投资经理超越基准指数的能力越强。万兴科技的信息比率最高,表明其相对于其跟踪基准的表现最为出色。
综上所述,如果是在寻找高风险高回报的股票,可能会考虑昆仑万维或中科信息,因为它们具有较高的阿尔法值和实际总收益率。如果更注重稳定性和低风险,柯力传感可能是一个更好的选择,因为其最大回撤率较低。另外,从风险调整后的回报来看,鸿博股份和万兴科技表现较好,它们的夏普比率和信息比率较高。
储存结果
### 储存结果
df_results.to_csv('人工智能三因子结果.csv',index=False)
创作不易,看官觉得写得还不错的话点个关注和赞吧,本人会持续更新python数据分析领域的代码文章~(需要定制类似的代码可私信)