Python codes for basic financial calculations

In this post, i will share some techniques that are commonly used in computing some financial concepts. This is more like an introductory instruction, so some of the contents may seem too young too simple, even naive for advanced users. First, we will load some required python packages.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import yfinance as yf
import numpy_financial as npf
import datetime as dt
import time
from scipy import stats
import seaborn as sns
import urllib.request
import zipfile
import requests
from bs4 import BeautifulSoup
import statsmodels.api as sm

Sometimes if you do not have then installed already, you can run the following codes, using zipfile as an example:

pip install zipfile

Now, let’s dive into the calculations!

Present Value

Suppose you are guaranteed to receive 100 dollars per year in the next 10 years, this is definitely not a free lunch. How much would you like to pay, assuming the annual interest rate is 3%?

r = 0.03
year = np.arange(1,11)
cash_flow = [100]*10

#create data fram
cf = pd.DataFrame({'year': year, 'cash_flow' : cash_flow})
cf['present_value'] = cf['cash_flow']/(1+r)**cf['year']

#print cash flow table
print(cf)

The sum of the present value column is the money that makes you indifferent (the maximum amount you are willing to pay)

Internal Rate of Return

IRR is a discount rate that makes the net present value of all cash flows equal to zero. The formula is

    \[NetPresentValue = \sum_{t=1}^T \frac{C_t}{(1+IRR)^t}-C_0\]

Here we use a cheatsheet method by calling the numpy financial package

project1 = pd.DataFrame({'year' : np.arange(1,7),
                         'cf' : [-800, 200, 250, 300, 350, 400]})
project2 = pd.DataFrame({'year' : np.arange(1,7),
                         'cf' : [-500, 150, 170, 178, 250, 300]})

irr1 = npf.irr(project1['cf'])
irr2 = npf.irr(project2['cf'])

pd.DataFrame({'Name' : ['P1', 'P2'],
              'irr' : [irr1, irr2]})

Loan Payments

Suppose you borrowed 10,000 dollars to buy a car and you need to pay the loan back in 5 years, one payment per year, assuming an interest rate of 7%. Everytime you make a payment, you are not only paying the 10,000$ part, you are also paying for the interests. Then how much does you payment go to the principal? How much to the interest? Here, again, we use a cheatsheet method by calling the numpy financial pmt function:

car_loan = 10000
interest = 0.07
years = 5
car_payments = npf.pmt(rate = interest, nper = years, pv = -car_loan)
loan_table = np.zeros((5,6))
loan_table = pd.DataFrame(loan_table)
loan_table.columns = ["Year", 'Initial_Balance', "Payments", "Interest",
                                "Principal", "Ending_Balance"]
loan_table.iloc[0,0] = 1
loan_table.iloc[0,1] = car_loan
loan_table.iloc[0,2] = car_payments
loan_table.iloc[0,3] = car_loan*interest
loan_table.iloc[0,4] = car_payments-car_loan*interest
loan_table.iloc[0,5] = car_loan-(car_payments-(car_loan*interest))

for i in range(1,5):
  loan_table.iloc[i,0] = i + 1
  loan_table.iloc[i,1] = loan_table.iloc[i-1,5]
  loan_table.iloc[i,2] = car_payments
  loan_table.iloc[i,3] = loan_table.iloc[i,1]*interest
  loan_table.iloc[i,4] = car_payments-loan_table.iloc[i,1]*interest
  loan_table.iloc[i,5] = loan_table.iloc[i,1]-(car_payments-(loan_table.iloc[i,1]*interest))

loan_table.round(2)

Future Value of Regular Deposit

Suppose Jerry deposits 3,600$ every month in the first 20 years, and 0$ in the next 30 years, while Tom deposits 0$ in the first 30 years and 3,600$ every month in the next 20 years, what the future value will be for these two methods?

Here we assume an annual interest rate of 7.5% and compounded monthly.

#Jerry's Table
annual_ret = 0.075
monthly_r = 1.075**(1/12)-1
monthly_deposit = 3600
fv_table_j = np.zeros((601,5))
fv_table_j = pd.DataFrame(fv_table_j)
fv_table_j.columns = ['months', 'beg_val', 'deposit', 'ret', 'end_val']
fv_table_j['months'] = np.arange(1,602)

#calculate the first month
fv_table_j.iloc[0,1] = 0
fv_table_j.iloc[0,2] = monthly_deposit
fv_table_j.iloc[0,3] = monthly_deposit*monthly_r
fv_table_j.iloc[0,4] = fv_table_j.iloc[0,2] + fv_table_j.iloc[0,3]

#iterate for following months
for i in range(1,241):
  fv_table_j.iloc[i,1] = fv_table_j.iloc[(i-1),4]
  fv_table_j.iloc[i,2] = monthly_deposit
  fv_table_j.iloc[i,3] = (fv_table_j.iloc[i,1]+fv_table_j.iloc[i,2])*monthly_r
  fv_table_j.iloc[i,4] = fv_table_j.iloc[i,1] + fv_table_j.iloc[i,2] + fv_table_j.iloc[i,3]

for i in range(241,601):
  fv_table_j.iloc[i,1] = fv_table_j.iloc[i-1,4]
  fv_table_j.iloc[i,2] = 0
  fv_table_j.iloc[i,3] = (fv_table_j.iloc[i,1]+fv_table_j.iloc[i,2])*monthly_r
  fv_table_j.iloc[i,4] = fv_table_j.iloc[i,1] + fv_table_j.iloc[i,2] + fv_table_j.iloc[i,3]

fv_table_j.round(2)
#Tom's table
annual_ret = 0.075
monthly_r = 1.075**(1/12)-1
monthly_deposit = 3600
fv_table_t = np.zeros((601,5))
fv_table_t = pd.DataFrame(fv_table_t)
fv_table_t.columns = ['months', 'beg_val', 'deposit', 'ret', 'end_val']
fv_table_t['months'] = np.arange(1,602)

#calculate the first month
fv_table_t.iloc[0,1] = 0
fv_table_t.iloc[0,2] = 0
fv_table_t.iloc[0,3] = 0
fv_table_t.iloc[0,4] = fv_table_t.iloc[0,2] + fv_table_t.iloc[0,3]

#iterate for following months
for i in range(1,361):
  fv_table_t.iloc[i,1] = fv_table_t.iloc[(i-1),4]
  fv_table_t.iloc[i,2] = 0
  fv_table_t.iloc[i,3] = (fv_table_t.iloc[i,1]+fv_table_t.iloc[i,2])*monthly_r
  fv_table_t.iloc[i,4] = fv_table_t.iloc[i,1] + fv_table_t.iloc[i,2] + fv_table_t.iloc[i,3]

for i in range(361,601):
  fv_table_t.iloc[i,1] = fv_table_t.iloc[i-1,4]
  fv_table_t.iloc[i,2] = monthly_deposit
  fv_table_t.iloc[i,3] = (fv_table_t.iloc[i,1]+fv_table_t.iloc[i,2])*monthly_r
  fv_table_t.iloc[i,4] = fv_table_t.iloc[i,1] + fv_table_t.iloc[i,2] + fv_table_t.iloc[i,3]

fv_table_t.round(2)
#join tables
fv_table_both = pd.merge(fv_table_t,fv_table_j, on = "months", how = "outer")
fv_table_both.columns = ['months', "Tom beg", 'Tom deposit', 'Tom return', 'Tom end', 'Jerry beg', 'Jerry deposit', 'Jerry return', 'Jerry end']

#show graphs
plt.plot(fv_table_both['months'], fv_table_both['Tom end'], label = 'Tom\'s savings')
plt.plot(fv_table_both['months'], fv_table_both['Jerry end'], label = 'Jerry\'s savings')
plt.xlabel('Year')
plt.ylabel('Dollars')
plt.legend(loc = "best")
plt.show()

Download Stocks’ Prices and Calculate Returns

start = dt.datetime(2023,1,1)
end = dt.datetime.today()
tickers = ["AAPL", "MSFT", "AMZN", "K", "O"]
df = yf.download(tickers , start=start, end=end)
df['Adj Close'].plot()
plt.xlabel("Date")
plt.ylabel("Adjusted Price")
plt.title("price data")
plt.show()
#Download the data
start = dt.datetime(2022,1,1)
end = dt.datetime.today()
netflix = yf.download('NFLX', start = start, end = end)
netflix.index = pd.to_datetime(netflix.index)

netflix_daily_returns = netflix['Adj Close'].pct_change()
netflix_cum_returns = (netflix_daily_returns + 1).cumprod()
netflix_cum_returns.plot()

Calculate Portfolio Return

symbols = ['VOO','VEA', 'VB', 'VWO','BND']
price_data = yf.download(symbols,
                               start = '2013-01-01',
                               end = '2018-03-01')

price_data = price_data['Adj Close']
w = [0.1,0.2,0.25,0.25,0.2]
ret_data = price_data.pct_change()[1:]
weighted_ret = ret_data * w
port_ret = weighted_ret.sum(axis = 1)
port_ret = (port_ret+1).cumprod()
fig, ax = plt.subplots()
ax.plot(port_ret)
# A better, clearer version
wts_table = pd.DataFrame({'symbol':symbols,
                          'wts':[0.25,0.25,0.2,0.2,0.1]})

ret_data_tidy = pd.melt(ret_data.reset_index(),
                        id_vars = 'Date',
                        var_name = 'symbol',
                        value_name = 'ret')
ret_data_tidy_wts = pd.merge(ret_data_tidy, wts_table, on = 'symbol')
ret_data_tidy_wts['wt_returns'] = ret_data_tidy_wts['ret'] * ret_data_tidy_wts['wts'] 

port_ret_tidy = ret_data_tidy_wts.groupby('Date').sum()['wt_returns']
port_ret_tidy

Portfolio Sharpe Ratio and Beta

tickers = ['BND', 'VB', 'VEA', 'VOO', 'VWO']
wts = [0.1,0.2,0.25,0.25,0.2]

price_data = yf.download(tickers, start = '2013-01-01', end = '2018-03-01')
price_data = price_data['Adj Close']

ret_data = price_data.pct_change()[1:]
port_ret = (ret_data * wts).sum(axis = 1)

geo_port_ret = np.prod(port_ret + 1) ** (252/port_ret.shape[0]) - 1
annual_std = np.std(port_ret) * np.sqrt(252)
geo_port_ret/annual_std

benchmark_price = yf.download('spy', start = '2013-01-01', end = '2018-03-01')
benchmark_ret = benchmark_price['Adj Close'].pct_change()[1:]

smf.OLS(benchmark_ret, smf.add_constant(port_ret)).fit().summary()

Mean-Variance Optimization

tick = ['AMZN', 'AAPL', 'NFLX', 'XOM', 'T']
price_data = yf.download(tick,
                           start = '2014-01-01',
                           end = '2018-05-31')['Adj Close']
                           
ret_data = price_data.pct_change()[1:] + 1
log_ret = np.log(ret_data)
cov_mat = log_ret.cov()*252
num_port = 5000

all_wts = np.zeros((num_port, len(log_ret.columns)))
port_returns = np.zeros((num_port))
port_risks = np.zeros((num_port))
port_sharpe = np.zeros((num_port))

for i in range(num_port):
  wts = np.random.uniform(size = len(log_ret.columns))
  wts = wts/np.sum(wts)

  all_wts[i, :] = wts

  port_ret = np.sum(log_ret.mean() * wts)
  port_ret = (port_ret + 1) ** 252 - 1

  port_returns[i] = port_ret

  port_sd = np.sqrt(np.dot(wts.T, np.dot(cov_mat, wts)))

  port_risks[i] = port_sd

  sr = port_ret/port_sd

  port_sharpe[i] = sr
  
names = price_data.columns
min_var = all_wts[port_risks.argmin()]
max_sr = all_wts[port_sharpe.argmax()]
max_sr = pd.Series(max_sr, index=names)

plt.plot(port_risks, port_returns, 'bo', markersize = 0.5)
plt.ylabel('Returns')
plt.xlabel('Risks')
plt.show()

Posted