使用 Python 进行股票投资组合分析

股票投资组合是您投资的一篮子股票。它基本上是指投资者拥有的所有股票。

AI吧Python

目录

介绍

我们的股票投资组合中都有各种各样的股票。每个人的股票投资组合都是不同的,因为它取决于投资者的风险偏好、他对该股票和该公司的相似性、股票部门(公司经营所在的行业)、投资者对宏观环境的看法、投资者的目标和一些其他因素。根据这些因素,投资者或资产经理将特定股票分配到他们的股票投资组合中。

股票投资组合是您投资的一篮子股票。它基本上是指投资者拥有的所有股票。

什么是投资组合分析

投资组合分析是通过比较投资组合中股票的回报、检查投资组合多元化、资产相关性等来分析您的股票投资组合的过程。在分析您的投资组合后,投资者或资产经理可以进行某些更改,例如重新分配某些股票、出售或购买其他股票,以最大限度地提高您的投资回报率 (ROI) 并使投资组合多样化。

如何找到股票代码

由于我们将使用 Yahoo Finance API,因此必须使用来自 yahoo Finance 网站的股票代码。您可以点击此处访问雅虎财经网站。

进入网站后,使用搜索框搜索您拥有的特定股票。

选择您的股票后,在该特定股票页面上,股票代码应显示在股票名称 () 旁边的圆括号中。

如“手动输入股票数据”部分中所做的那样,将您的每个股票代码记入列表中。

单击此处此处(API 文档)以了解有关 Yahoo Finance API 的更多信息。

使用的库

此项目中使用的库使分析数据变得非常简单。使用终端中的 pip 命令安装这些库:

pip install library_name

使用的库简要描述如下:

  • yfinance – 用于提取股票数据
  • numpy – 使分组数据中的数值计算变得简单
  • pandas – 轻松操作数据框中的数据
  • matplotlib – 可视化您的数据
  • seaborn – 用于数据可视化
  • datetime – 对日期执行计算

让我们开始编码

导入库

import yfinance as yf
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import datetime as dt
from datetime import datetime

手动输入库存数据

在本节中,我们将手动输入我们的库存数据。股票数据包括我们的股票代码(仅来自雅虎财经)、我们持有的股票、我们购买股票的价格以及股票的购买日期。

访问“如何查找股票代码”部分以查找正确的股票代码。

stocks =['IRCTC.NS','TATAMOTORS.NS','LICI.NS','WIPRO.NS','INFY.NS','HDFCBANK.NS','RELIANCE.NS']
stock_holdings=np.array([10,15,7,20,5,3,4])
buying_price=np.array([748,248,825,414,1650,1535,2035])
dates=np.array(['2021-09-08','2021-08-16','2022-05-20','2021-03-01','2021-08-02','2021-02-01','2021-07-07'])

股票价格和行业

我们将通过 Yahoo Finance API 获得我们的股票价格和行业。

price_stock_today=[] #to store stock price
sector=[] #to store stock sector

for i in stocks:
    asset=yf.Ticker(i) 
    price_stock_today.append(asset.info['regularMarketPrice'])
    sector.append(asset.info['sector'])

基本投资组合计算

amt_invested=np.multiply(stock_holdings,buying_price)

price_stock_today=np.array(price_stock_today)

stock_holdings_value=np.multiply(stock_holdings,price_stock_today)

stocks_profit_loss=stock_holdings_value-amt_invested

roi=(stocks_profit_loss/amt_invested)*100

Current_Portfolio_Value=sum(stock_holdings_value)
Total_invested=sum(amt_invested)
total_profit_loss=round(Current_Portfolio_Value-Total_invested,2)
total_roi=round((total_profit_loss/Total_invested)*100,2)

weight=np.divide(amt_invested,Total_invested)

创建的变量简述如下:

  • amt_invested –每只股票的投资总额
  • price_stock_today –一只股票的市场价格
  • stock_holdings_value –每只股票的当前价值(股票数量 x 股票市场价格)
  • stock_profit_loss –每只股票的盈亏
  • roi –每只股票的投资回报率
  • Current_Portfolio_Value –我们投资组合的总价值
  • Total_invested –投资于我们投资组合的总金额
  • total_profit_loss –投资组合的盈亏
  • total_roi –投资组合的投资回报
  • number_days –我们持有股票的天数(在下面的代码中创建)

计算天数

我们将计算我们持有投资组合中每只股票的总天数。

buy_date=[]

#converting each date from 'string' to 'datetime' datatype and storing in a list
for i in dates:
    buy_date.append(datetime.strptime(i, "%Y-%m-%d"))

#converting list to numpy array for easy calculations
buy_date=np.array(buy_date)

#finding difference between buying date and todays date

number_days=[]
for i  in buy_date:    
    number_days.append((dt.datetime.now()-i).days) # .days is an in-built function which returns only number of days

显示股票详情和投资组合结果

我们将首先将我们拥有的股票的详细信息存储在数据框中。然后我们将打印我们的数据框以查看我们的库存详细信息。之后,我们将打印整个投资组合的结果。

创建数据框

df=pd.DataFrame() #Creating empty dataframe

df['Stocks']=stocks
df['Buying Date']=dates
df['Sector']=sector
df['Quantity']=stock_holdings
df['Average Price']=buying_price
df['Invested Amount']=amt_invested
df['Current Price (d)']=price_stock_today
df['Current Value']=stock_holdings_value
df['Proft/Loss']=stocks_profit_loss
df['ROI %']=roi
df['Weight']=weight
df['Number of Days']=number_days

显示股票详情

print(df)
●portfolio_indian.ipynb - 数据相关 - Visual Studio Code 06-08-2022 22_23_52.png

显示投资组合结果

print("Number of days invested =",max(df['Number of Days']),"days")
print("Total Amount Invested =",Total_invested)
print("Current Portfolio Value =",Current_Portfolio_Value)
print("Portfolio Profit/Loss =",total_profit_loss)
print("Portfolio ROI =",total_roi,'%')

投资组合可视化

投资回报率分析

color=['Grey','Blue','Red']
plt.figure(figsize=(20,7))
plt.bar(df['Stocks'],df['ROI %'],color=color)

plt.title("Stocks ROI")
plt.ylabel("ROI")

我们可以注意到,塔塔汽车给了我们最高的投资回报率,而 LIC 给了我们最低的投资回报率。

投资金额及盈亏

plt.figure(figsize=(20,7))

x_axis = np.arange(len(stocks))

plt.bar(x_axis -0.2, amt_invested, width=0.4, label = 'Amount Invested')
plt.bar(x_axis +0.2, stocks_profit_loss, width=0.4, label = 'Profit/Loss')

plt.xticks(x_axis, stocks)

plt.legend()

# Displaying

plt.title("Amount Invested and Profit/Loss")
plt.ylabel("Price (Rs)")
plt.show()

从上述结果可以看出,我们在 Wipro、Infosys、Reliance 和 IRCTC 上的投资最多。目前只有 Wipro 从这些投资中获利;其他人目前处于亏损状态。此外,我们可以看到,尽管对塔塔汽车的投资最少,但我们的利润最高。

投资回报率和天数

plt.figure(figsize=(20,7))

x_axis = np.arange(len(stocks))

plt.bar(x_axis -0.2, roi, width=0.4, label = 'ROI',color='pink')
plt.bar(x_axis +0.2, number_days, width=0.4, label = 'Number of Days',color='grey')

plt.xticks(x_axis, stocks)

plt.legend()

# Display

plt.title("ROI and Number of Days")
plt.ylabel("Number of Days")
plt.show()

我们可以从上面的输出中看到,HDFC 和 Wipro 是我们长期持有的股票。尽管如此,我们对 Wipro 的利润微乎其微,而我们在 HDFC 方面处于亏损状态。也许,是时候改变我们的一些股票持有量和配置了?

每只股票的股数

plt.pie(df['Quantity'],labels=df['Stocks'],shadow=True,autopct='%1.1f%%')
plt.title('Quantity')
plt.show()

我们可以看到,我们拥有的 Wipro 股份最多,而持有 HDFC 银行的股份最少。

投资组合多元化

多元化是指投资于多种不同的资产。在我们的案例中,投资了多种不同的加密货币。

plt.pie(df['Weight'],labels=df['Stocks'],shadow=True,autopct='%1.1f%%')
plt.title('Diversification')

plt.plot()
plt.show()

从上面的数字我们可以看出,我们最大的持股是Wipro、Infosys和HDFC Bank。塔塔汽车是我们最小的控股公司。

部门明智的多元化

每只股票都有自己的行业,例如金融、能源、技术等等。我们从 Yahoo Finance API 获得了每只股票的板块。

我们可以从我们的股票详细信息数据框df中看到,有几只股票在同一个行业中运营,因此我们需要计算每个行业的数量。我们将为我们的部门创建一个单独的数据框。

扇区数据框

sec=df.groupby(['Sector']).size() 
sec=pd.DataFrame(sec)
sec.head()

groupby 函数对扇区进行分组并返回每个扇区的计数。

我们可以看到发生了多索引。为了解决这个问题,我们必须重置我们的索引。我们可以使用下面的代码完成此操作。

sec.reset_index('Sector',inplace=True) #resolve multi-indexing
sec.columns=['Sector','Count'] #changing column names
print(sec) #displaying our sector dataframe

部门多元化

plt.pie(sec['Count'],labels=sec['Sector'],shadow=True,autopct='%1.1f%%')
plt.title('Sectors')
plt.show()

在这里,我们注意到我们拥有的大多数股票都在“金融服务”和“技术”领域运营。

进一步的分析

获取股票历史数据

d=[]

for i in range(len(stocks)):
    a = yf.Ticker(stocks[i])
    hist = a.history(start=dates[i])['Close'] #return type series
    data = hist.to_frame() #convert series to dataframe

    #data.set_index('Date',inplace=True)
    data.rename(columns={'Close':stocks[i]},inplace=True)
    d.append(data)

hist=pd.concat(d,axis=1)
hist.head()

在上面的代码中,我们使用 Yahoo Finance API 提取了我们购买特定资产时股票的历史收盘价。

执行上述代码后,我们注意到发生了多索引。因此我们需要解决这个问题。我们可以通过执行下面的代码来做到这一点。

hist=hist.reset_index(level=['Date'])#multi indexing

现在如果我们执行.head方法,我们会注意到多索引已被消除。

比较股票价格

plt.figure(figsize=(20,7))

for i in range(len(stocks)):    
    plt.plot(hist['Date'],hist[stocks[i]],label=stocks[i])

plt.legend()
plt.title("Stock Holdings Performance")
plt.ylabel("Price (Rs)")
plt.xlabel("Date")
plt.plot()
plt.show()

股票之间存在差距,因为我们在不同的日期购买了股票。

股票相关性

在本节中,我们将比较我们投资组合中股票的相关性。

  • 正相关 –正相关表明如果一只股票的价格上涨,另一只股票的价格很可能也会上涨,反之亦然(价格下跌,价格上涨)。这两个变量(股票)是相互关联的。
  • 负相关 –负相关表示如果一只股票的价格上涨,另一只股票的价格很可能会下跌,反之亦然(价格下跌,价格上涨)。这两个变量(股票)彼此不相关。
  • 相关值越接近 1,股票越有可能遵循相同的方向。
  • 相关值越接近-1,股票越有可能不会遵循相同的方向。
  • 相关值为 0 表示两个变量(股票)之间没有相关性。

我们首先将删除我们股票价格数据(历史数据帧)的所有 Null 值以获得准确的结果并将其存储在另一个数据帧中。

no_null=hist.dropna()

我们现在将找到相关性。

c=no_null.corr()
plt.figure(figsize=(10,7))
sns.heatmap(data = c,annot=True)

在这里我们可以注意到,HDFC 和 Reliance 之间的相关性最低(-0.059)。这是真的,因为 HDFC 属于金融服务部门,而依赖属于能源 部门。

很多时候,不同行业的股票交易也具有相关性,例如在我们的案例中,您可以注意到 IRCTC(工业)和 Wipro(技术)的相关性最高,为 0.84。

我们还可以注意到,在同一行业(即技术)运营的 Wipro 和 Infosys 高度相关(0.81)。

通常,强大的投资组合多样化需要少量高度相关的股票。我们投资组合中的大多数股票相关性不高,这表明我们的投资组合非常多元化。

股票波动率

在本节中,我们将比较我们投资组合每日收益中股票的波动性。波动率被定义为任何资产的价格(百分比)随时间的主要变化。

date=hist['Date'] #Storing Dates
percent_change=(hist.drop('Date',axis=1)).pct_change()

如果我们打印percent_change,我们可能会注意到我们有许多 Null 值。这是真的,因为我们在不同的时间间隔购买了我们的股票。

plt.figure(figsize=(15,8))

for i in percent_change.columns.values :
    plt.plot(date,percent_change[i] ,label = i)

plt.legend(loc = 'upper right')
plt.title('Volatility')
plt.xlabel('Date')
plt.ylabel('Daily Simple Returns')

plt.show()

从上图可以看出,蓝线(IRCTC)跌幅最大(一天18%),而黄线(塔塔汽车)涨幅最大(一天20%)。

此外,我们可能会注意到黄线(塔塔汽车)在两个方向上都有一些最大的波动。因此,这表明塔塔汽车是一只不稳定的股票。

投资组合表现

投资组合价值加班

在本节中,我们将从购买第一只股票开始计算整个投资组合的价值。

hist.fillna(value = 0,inplace = True) #Replaicing Null Values with 0

s=0
for i in hist:    
    s=0
    for j in range(len(stocks)):
        p=stock_holdings[j]*hist[stocks[j]] #calculating each stock value
        s=s+p #sum of each stock
    hist['Portfolio Value']=s

可视化投资组合价值

plt.figure(figsize=(20,7))
plt.plot(hist['Date'],hist['Portfolio Value'])

plt.title("Portfolio Value Overtime")
plt.ylabel("Price (Rs)")
plt.xlabel("Date")

我们投资组合的突然飙升是由于我们的投资组合中增加了新股票。

投资组合统计

plt.figure(figsize=(10,7))
hist.boxplot('Portfolio Value')

plt.title("Portfolio Statistics")
plt.ylabel("Price (Rs)")

在这里,我们看到,我们的投资组合达到的最大值约为 58,000,我们的投资组合的最小值约为 4,000(我们此时开始投资),我们的中值约为 47,000。

投资组合与指数的比较

在本节中,我们将把我们的投资组合累积回报与 NIFTY 50、Gold、SENSEX 和 S&P 500 等指数进行比较。

指数基本上是衡量一组股票表现的指标。

收集索引数据

index_data=[]

indexes=['NIFTY 50','SENSEX','GOLD','S&P 500']
index_ticker=['^NSEI','^BSESN','GC=F','^GSPC'] #yahoo finance tickers

for i in range(len(index_ticker)):
    nse=yf.Ticker(index_ticker[i])
    data=nse.history(start='2021-02-01')['Close'] 

    data=data.to_frame()
    data.rename(columns={'Close':indexes[i]},inplace=True)
    index_data.append(data)

df_index=pd.concat(index_data,axis=1)

df_index=df_index.reset_index(level=['Date']) #since multi indexing occured
df_index.dropna(inplace=True)
df_index.head()

data=nse.history(start=’2021-02-01′)[‘Close’]行中,开始日期是指我们购买第一只股票的日期。

计算累积回报

首先,我们将计算指数的累积回报。

l=df_index.columns

for i in range(1,5): #beginning from 1 since we do not want to perform calculations on date
    df_index[l[i],'Percent Change']=(df_index[l[i]].pct_change()+1).cumprod()

df_index.dropna(inplace=True) #Since first row value becomes NULL
df_index.head()

现在我们将计算我们的投资组合价值的累积回报。复制复制复制COPY

cummulative_returns=((hist['Portfolio Value'].pct_change())+1).cumprod()

比较累积回报 – 投资组合与指数

由于我们的投资组合的累积收益远高于每个指数的累积收益,因此我们需要使用子图和双轴。

l=df_index.columns
plt.rcParams['figure.figsize']=(20,7)
fig, ax1=plt.subplots()
ax2=ax1.twinx()

y=df_index['Date']

for i in range(5,9):
    ax1.plot(y,df_index[l[i]],label=l[i-4])

ax2.plot(hist['Date'],cummulative_returns,color='Black',label='Portfolio C. Return')

h1, l1 = ax1.get_legend_handles_labels()
h2, l2 = ax2.get_legend_handles_labels()
ax1.legend(handles=h1+h2, labels=l1+l2, loc='upper left')

右侧的 y 轴是我们投资组合的累积回报,而左侧的 y 轴是指数的累积回报。

我们可以注意到我们的投资组合的表现远远超过了许多指数。

投资组合月度表现

为了计算我们的月度表现,我们需要在我们的数据框中创建三个额外的表 – ‘Year’、’Month’ 和 ‘YearMonth’。

创建附加表

hist['Month']=hist['Date'].dt.month
hist['Year']=hist['Date'].dt.year

如果我们运行.head()方法,我们可以注意到另外两列。现在我们简单地无法绘制月度回报,因为我们有 2021 年和 2022 年的数据,并且该数据将包含两个月,即 2021 年 2 月和 2022 年 2 月。因此,我们需要创建一个额外的列来区分这两个月。

def getYearMonth(s):
    s=s.strftime("%m/%d/%Y")
    return s.split("/")[0] + "-" + s.split("/")[2]

hist['YearMonth'] = hist['Date'].apply(lambda x: getYearMonth(x))

我们创建的getYearMonth()函数将返回给我们一个结果,例如每个月和年的“02-2021”和“02-2022”。

数据操作

现在我们已经创建了另外三个列。我们的下一步是按“年月”对数据进行分组,因为我们正在计算投资组合的月度表现。我们将这些分组数据存储在另一个变量“a”中。

a=hist.groupby(["YearMonth"]).mean()
a.reset_index('YearMonth',inplace=True) #resolving multi-indexing

接下来,我们将根据年份和月份对“a”进行排序,并将这个排序后的数据框存储在另一个变量“b”中。

b=(a.sort_values(by=['Year','Month']))

可视化投资组合的月度回报

plt.bar(b['YearMonth'],b['Portfolio Value'],color='darkgreen')

plt.title("Portfolio Monthly Value")
plt.ylabel("Price Rs")
plt.xlabel("Months")

我们可以注意到,在 10 月份,我们的投资组合的价值最高。从 10 月开始,我们的投资组合处于整合阶段。

结论

是的,我知道,这是一个很长的博客。但是,如果您坚持到最后,我可以向您保证,您将掌握大量的知识和想法。这个项目不是财务建议。在这个项目中所做的所有工作纯粹是为了教育目的。请随时添加您自己的股票持有量,如“手动输入股票数据”部分中所做的那样,并分析您的投资组合。请继续关注即将推出的“加密货币投资组合分析”项目。

发表评论

邮箱地址不会被公开。 必填项已用*标注