Mark-to-Market for a Portfolio of Stocks

by Carolyn Koay
Last updated on 26 March 2018

This code takes an input file that details a portfolio of stocks. It then queries the Alphavantage free API to get the latest closing prices of the stocks and the latest currency exchange rates. Finally, the portfolio is valued according to the market price, after converting to the investor’s home currency as specified.

Import Libraries

import pandas as pd
import time
import requests
import json 

Define Functions

The functions below are written to query the API more effectively.

Per best practice, the API key is stored in a text file within the programming environment, to prevent revealing the key in the codes. The file has one single line shown below. The X’s is a dummy for the actual API key which can be obtained for free from Alphavantage.

alphavantage: XXXXXXXXXXXXXXXX

def getKeys(keyFile):
    f = open(keyFile)
    lines = f.readlines()
    f.close()
    keys = {}
    for l in lines:
        k,v = l[:-1].split(':')
        keys[k] = v
    return keys

def getParamString(params):
    paramString = ''
    for k, v  in params.items():
        if paramString == '':
            paramString = k + '=' + v
        else:
            paramString = paramString + '&' + k + '=' + v
    return paramString

def tryGET(maxTries, reqURL, progress = True, sleep = 5):
    reqStatus = -1
    returnedStatus = ''
    data = {}
    tries = 0
    
    while tries < maxTries: # monitor number of successful requests made. 
        try:
            req = requests.get(reqURL)
        except requests.exceptions.RequestException as e:
            print(e)
            time.sleep(10)   
            continue # try indefinitely every 10 seconds until request is successful.
            
        if progress:
            print('Try: %i. ' %(tries+1))
        
        time.sleep(sleep)
        tries +=1
        reqStatus = req.status_code
        if reqStatus != 200:  
            continue # try again if the successful request's returned status is not 200.
        else: 
            data = json.loads(req.text)
            if 'Error Message' in data:
                returnedStatus = data['Error Message']
            else:
                returnedStatus = 'OK'
                
            if returnedStatus != 'OK': 
                continue # try again if API returned error.
            else:
                break
                
    return reqStatus, returnedStatus, data, tries

User Inputs

User should review the variables here and change accordingly.

query_date = '2018-03-23'
my_currency = 'SGD'
key_file ='API_key.txt'
portfolio_file = 'my_portfolio.csv'

Read files

An input file that contains the details of the portfolio will be read. The file will have 3 columns: Symbol, Number of shares and Currency, as shown below

keys = getKeys(key_file)
pf = pd.read_csv(portfolio_file)
pf
Symbol Number of shares Currency
0 C 1000 USD
1 JPM 500 USD
2 STAN.L 200 GBP
3 GE 100 USD
4 TSLA 500 USD
5 BABA 400 USD
6 D05.SI 400 SGD

Query API

Retrieve the currency exchange rate

currencies = pf['Currency'].unique().tolist()
url = 'https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&apikey='+keys['alphavantage']
rates = {my_currency:1}

for c in currencies:
    if c == my_currency:
        continue
    print('Getting exchange rates for %s' %c) 
    params = {'from_currency': c,
              'to_currency': my_currency}
    paramStrings = getParamString(params)
    reqStatus, returnedStatus, data, tries = tryGET(3, url+'&'+ paramStrings , progress = True, sleep = 5)
        
    if 'Realtime Currency Exchange Rate' not in data:
        xchange_rate = 'NA'
    elif '5. Exchange Rate' not in data['Realtime Currency Exchange Rate']:
        xchange_rate = 'NA'
    else:
        xchange_rate = float(data['Realtime Currency Exchange Rate']['5. Exchange Rate'])  
    
    rates[c] = xchange_rate

Getting exchange rates for USD
Try: 1.
Getting exchange rates for GBP
Try: 1.

Retrieve the stock price

url = 'https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&apikey='+keys['alphavantage']
stock_price = {}

for i in range(pf.shape[0]):
    sym = pf.loc[i,'Symbol']
    print('Getting closing price for %s' %sym)
    
    params = {'symbol': sym}
    paramStrings = getParamString(params)
    reqStatus, returnedStatus, data, tries = tryGET(3, url+'&'+ paramStrings , progress = True, sleep = 5)
    
    if 'Time Series (Daily)' not in data:
        close_price = 'NA'
    elif query_date not in data['Time Series (Daily)']:
        close_price = 'NA'
    elif '4. close' not in data['Time Series (Daily)'][query_date]:
        close_price = 'NA'
    else:
        close_price = float(data['Time Series (Daily)'][query_date]['4. close'])        
    
    stock_price[sym] = close_price    

Getting closing price for C
Try: 1.
Getting closing price for JPM
Try: 1.
Getting closing price for STAN.L
Try: 1.
Getting closing price for GE
Try: 1.
Getting closing price for TSLA
Try: 1.
Getting closing price for BABA
Try: 1.
Getting closing price for D05.SI
Try: 1.

Calculate portfolio value

pf = pd.read_csv(portfolio_file)
pf = pf.join(pd.Series([rates[c] for c in pf['Currency']], name = 'Exchange rate'))
pf.set_index('Symbol', inplace = True)
pf['Close price'] = pd.DataFrame.from_dict(stock_price, orient = 'Index')
pf['Value'] = round(pf['Number of shares']*pf['Exchange rate']*pf['Close price'],2)
print('Portfolio market value is %s %.2f' %(my_currency,round(pf['Value'].sum(),2)))
pf

Portfolio market value is SGD 730413.88

Number of shares Currency Exchange rate Close price Value
Symbol
C 1000 USD 1.311647 67.90 89060.83
JPM 500 USD 1.311647 107.01 70179.67
STAN.L 200 GBP 1.861765 713.50 265673.87
GE 100 USD 1.311647 13.07 1714.32
TSLA 500 USD 1.311647 301.54 197757.02
BABA 400 USD 1.311647 181.20 95068.17
D05.SI 400 SGD 1.000000 27.40 10960.00