Occassional (well very rare really) update
authorJude N <jude@pwan.org>
Mon, 29 Jun 2020 23:16:27 +0000 (19:16 -0400)
committerJude N <jude@pwan.org>
Mon, 29 Jun 2020 23:16:27 +0000 (19:16 -0400)
.gitignore
content/hints/gnucash-stock-import.rst [new file with mode: 0644]

index 1d607e7..d099dd3 100644 (file)
@@ -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 (file)
index 0000000..a11a476
--- /dev/null
@@ -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
+
+