From 88b6d4484b985cf6fe2d30a996837956c449ae6d Mon Sep 17 00:00:00 2001 From: Jude N Date: Mon, 29 Jun 2020 19:16:27 -0400 Subject: [PATCH] Occassional (well very rare really) update --- .gitignore | 2 +- content/hints/gnucash-stock-import.rst | 88 ++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 content/hints/gnucash-stock-import.rst diff --git a/.gitignore b/.gitignore index 1d607e7..d099dd3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ output *~ do-rsync.sh cache -content +content/pages/resume.pdf diff --git a/content/hints/gnucash-stock-import.rst b/content/hints/gnucash-stock-import.rst new file mode 100644 index 0000000..a11a476 --- /dev/null +++ b/content/hints/gnucash-stock-import.rst @@ -0,0 +1,88 @@ +Importing Stock Transactions Into Gnucash With Python +##################################################### + +:date: 2020-06-21 +:tags: hint, python, gnucash +:category: hints +:author: Jude N + +I use GnuCash, but keeping my 401(k) accounts up to date has always been a tedious, manual process. + +I came up with the python snippet below to handle the import, but it was a pain to write since I couldn't find any examples for setting up stock or mutual fund transactions. + +The `SetSharePriceAndAmount`_ method ended up being key, and I only found that by searching through the gnucash source. + +The GncNumeric class also ended up being more of a pain to use than I expected. There's probably a better way to use it, but the 'multiple values by 1000000/100000' approach is working for me now. + +I'm using the stock GnuCash and python-gnucash version 2.6.19 available in Ubuntu 18.04, so this stuck using python 2.7. + +.. code-block:: python + + #!/usr/bin/python2.7 + + import csv + from datetime import datetime + + import gnucash + + session = gnucash.Session("xml://yourfile.gnucash") + book = session.book + root_account = book.get_root_account() + + usd = book.get_table().lookup('ISO4217','USD') + + # There's probably a better way to use 'Your:Retirement:Contributions' instead .... + contrib_acct = root_account.lookup_by_name("Your").lookup_by_name("Retirement").lookup_by_name("Contributions") + + parent_acct = root_account.lookup_by_name("401k") + + with open('your_transactions.csv', 'rb') as trans_csv: + trans_reader = csv.reader(trans_csv, delimiter=',') + + # Skip over the first row since it's headers + header = next(trans_reader) + + for description, date, fund_name, share_price_str, share_amount_str, amount_str in trans_reader: + child_account = parent_acct.lookup_by_name(fund_name) + + posting_date = datetime.strptime(date,"%m/%d/%y") + + tx = gnucash.Transaction(book) + tx.BeginEdit() + + tx.SetCurrency(usd) + + tx.SetDatePostedTS(posting_date) + tx.SetDescription(description) + + sp1 = gnucash.Split(book) + sp1.SetParent(tx) + sp1.SetAccount(child_account) + + # GncNumeric(n,d) represents numbers as fractions of the form n/d, so GncNumeric(1234567/1000000) = 1.234567 + # There's probably a better way to do this... + share_price = gnucash.GncNumeric(float(share_price_str)*(10**6), 10**6) + share_amount = gnucash.GncNumeric(float(share_amount_str)*(10**6), 10**6) + + # share_price * share_amount == amount, so I could have used that instead using the value from the csv + amount = gnucash.GncNumeric(float(amount_str)*(10**6), 10**6) + + # ( ˘▽˘)っ♨ This is the secret sauce for setting the number of shares and the price. + sp1.SetSharePriceAndAmount(share_price, share_amount) + + sp2 = gnucash.Split(book) + sp2.SetParent(tx) + sp2.SetAccount(contrib_acct) + sp2.SetValue(amount.neg()) + + tx.CommitEdit() + session.save() + session.end() + + +Special thanks to `this post`_ for providing most of the code above. + +.. _this post: https://www.edureka.co/community/18845/how-to-create-transaction-in-gnucash-in-response-to-an-email +.. _SetSharePriceAndAmount: https://github.com/Gnucash/gnucash/blob/2.6.19/src/engine/Split.c#L980 + + -- 2.39.2