Importing fund or stock prices from an OFX file

From GnuCash
Jump to: navigation, search

The attached python scripts show how to import prices from an OFX file. This was useful for me because my 401k funds are not publicly traded so the normal import prices tool doesn't work. The OFX file has prices in the <INVPOS> aggregate. I borrowed heavily from work done by Peter Holtermann and others (price_database_example.py in src/optional/python-bindings/example_scripts).

Attached are 2 python scripts. getPrices.py reads the OFX file and outputs a CSV file in this format: fundName, date, price. The date looks like this: 20110118120000, year month day time. I suggest you try this standalone 1st and see if it works for your OFX file.

The other file, importPrice.py calls getPrices.py and uses the python bindings to update the price database in GC.

Another very useful tool is ofx.py, found at http://www.jongsma.org/gc/. I use this to download OFX files.

--- getPrices.py ----

#!/usr/bin/python
# usage: getPrices.py ofxfile outfile
# Read an OFX w/ an INVPOS aggregate and write the prices
# to a CSV file

# Brad Haack 2011-01-25

import sys

fp = open(sys.argv[1])
fout = open(sys.argv[2],'w')

flines = fp.readlines()

inlist=0
inpos=0
id = 0
unqid=[]
price=[]
date=[]

for line in flines:
    line = line.rstrip('\n')
    wd = line.split('>')
    for id in range(len(wd)):
        wi = wd[id]
        if wi == '<INVPOSLIST': # now we're in the list
            inlist = 1
        if inlist :
            if wi == '<INVPOS': # now we're in the pos info aggregate
                inpos = 1
                unqid.append(0)
                price.append(0)
                date.append(0)
            if inpos :
                if wi == '<UNIQUEID' :
                    unqid[id] = wd[id+1]
                if wi == '<UNITPRICE' :
                    price[id] = wd[id+1]
                if wi == '<DTPRICEASOF' :
                    date[id] = wd[id+1]
            if wi == '</INVPOS': # now we're out of the pos info aggregate
                inpos = 0
                str = '%s, %s, %s\n' % (unqid[id], date[id], price[id])
                fout.write( str)
            
        if wi == '\INVPOSLIST>': # now we're not in the list
            inlist=0
        

--- importPrice.py ---

#!/usr/bin/python
# Usage: importPrice.py ofxfile gcfile 
# Import prices from an OFX file into Gnucash

# Configuration Options
cur_mnemonic="USD"
namespace_name = "401k"

from gnucash import Session, Account, Split
import gnucash
import datetime
import sys
import os

ofxfile = sys.argv[1]
gcfile = sys.argv[2]
pricefile = 'pq.csv'

# Could call ofx.py here (see http://www.jongsma.org/gc/)
# ofx.py site user account

# get the prices from the ofx file
os.system('getPrices.py %s %s' % (ofxfile, pricefile) )

url = "xml://"+gcfile

# Read input data from file
f = open(pricefile)
data = []
while 1:
    tmp = f.readline()
    if(len(tmp)<2):
        break
    
    data.append(tmp)

f.close()

stock_date = []
stock_price = []
stock = []
for i in range(len(data)):
    date = data[i].rsplit(',')[1].lstrip()
    year = int(date[0:4])
    month = int(date[4:6])
    day = int(date[6:8])
    stock_date.append(datetime.datetime(year,month,day))
    stock_price.append(float(data[i].rsplit(',')[2]))
    stock.append(data[i].rsplit(',')[0])
    

# Initialize Gnucash session
session = Session(url, True, False, False)
book = session.book
pdb = book.get_price_db()
comm_table = book.get_table()
cur = comm_table.lookup("CURRENCY", cur_mnemonic)

commodities=comm_table.get_commodities(namespace_name)

for ii, stocki in enumerate(stock):
  # find the commodity cusip that matches
  for cmdt in commodities:
    if cmdt.get_cusip() == stocki:
      
      # Get stock data
      pl = pdb.get_prices(cmdt,cur)
      if len(pl)<1:
        print 'Need at least one database entry to clone ...'
        print '... %s ...' % (cmdt.get_fullname())
        exit()
      pl0 = pl[0]
      p_new = pl0.clone(book)
      p_new = gnucash.GncPrice(instance=p_new)
      print 'Adding %30s  %s  %s  %f' % (cmdt.get_fullname(),stocki,stock_date[ii],stock_price[ii])
      p_new.set_time(stock_date[ii])
      v = p_new.get_value()
      v.denom = 1000000
      v.num = int(stock_price[ii]*v.denom)
      p_new.set_value(v)
      pdb.add_price(p_new)


# Clean up
session.save()
session.end()
session.destroy()