Converting Investment Transaction to DIVIDEND_REINVEST

john.vogtle+infinitekind's Avatar

john.vogtle+infinitekind

15 Nov, 2024 12:57 PM

Hi

My brokerage sends statements that show Dividend Reinvestments but the OFX downloads are two separate transactions:

  • One is InvestTxnType.DIVIDEND (technically the parent txn)
  • One is InvestTxnType.BUY I want to delete the DIVIDEND transaction and convert the BUY to DIVIDEND_REINVEST. In the UI, I usually just delete the DIV transaction and change the type of the other to DivReinvest

From a code standpoint:

  • The delete is easy.
  • Is the conversion of the BUY to DIVIDEND_INVEST as simple as changing the transaction type or do I need to do something like:
parent_txn = txn.getParentTxn()
fields = InvestFields()
fields.setFieldStatus(InvestTxnType.DIVIDEND_REINVEST, parent_txn)
fields.storeFields(parent_txn)
parent_txn.syncItem()

Thanks in advance...

  1. Support Staff 1 Posted by Sean Reilly on 15 Nov, 2024 01:06 PM

    Sean Reilly's Avatar

    Hi John,

    That is very close! What I would do is something like this:

    fields = InvestFields()
    fields.setFieldStatus(buyTxn) // loads the details of the buy (price, #shares, amount, fee) since that's what you want to keep
    fields.txnType = InvestTxnType.DIVIDEND_REINVEST
    fields.category = dividendIncomeCategory
    fields.storeFields(buyTxn)
    buyTxn.syncItem()
    

    It is on my to-do list to add an option to automatically merge separate dividend and reinvest transactions.

    Thanks,
    Sean

    --
    Sean Reilly
    Developer, The Infinite Kind
    https://infinitekind.com

  2. 2 Posted by john.vogtle+inf... on 15 Nov, 2024 04:18 PM

    john.vogtle+infinitekind's Avatar

    Thanks, Sean. I'll give it a shot and report back!

  3. 3 Posted by john.vogtle+inf... on 29 Nov, 2024 04:53 PM

    john.vogtle+infinitekind's Avatar

    Hi Sean -

    Just getting back to this after an "interesting" couple of weeks. I think I'm almost there but while the transaction now says DivReinvest, it acts in the account register like a Buy. I've attached a screen shot showing the weirdness.

    If I the visibly no-op operation of setting the transaction in the UI to DivReinvest, the calculation straightens out.

    Here's the current code for converting the transactions.

    def cvt_divbuy_to_divreinvest(account_name = None):
        div_txns = dict()
        investment_acct = ra.getAccountByName(account_name)
    
        txn_cnt = 0
    
        acct_txns = moneydance_data.getTransactionSet().getTransactionsForAccount(investment_acct)
        all_txns = moneydance_data.getTransactionSet()
        for txn in acct_txns:
    
            if txn.getClearedStatus() != AbstractTxn.ClearedStatus.CLEARED:
                parent_txn = txn.getParentTxn()
    
                date = parent_txn.getDateInt()
                description = txn.getDescription()
                amt = txn.getValue()
    
                key = (date, description, abs(amt))
                div_txns.setdefault(key, [])
                div_txns.get(key).append(txn)
    
        for key in div_txns.keys():
            print repr(key), len(div_txns.get(key))
            if len(div_txns.get(key)) == 2:
                for txn in div_txns.get(key):
                    parent_txn = txn.getParentTxn()
                    if parent_txn.getInvestTxnType() == InvestTxnType.DIVIDEND:
                        if not dry_run:
                            print "About to delete transaction: {}".format(repr(txn))
                            all_txns.removeTxn(txn)
                        else:
                            print "Need to delete" + repr(txn)
    
                    elif parent_txn.getInvestTxnType() == InvestTxnType.BUY:
                        print "Need to convert to DIVIDEND_REINVEST" + repr(parent_txn)
    
                        # From Sean
                        fields = InvestFields()
                        # loads the details of the buy (price, #shares, amount, fee) since that's what you want to keep
                        fields.setFieldStatus(txn)
                        fields.txnType = InvestTxnType.DIVIDEND_REINVEST
                        fields.category = AccountUtil.getDefaultCategoryForAcct(investment_acct)
                        print "       New Investment Field Settings: " + str(fields)
                        txn_cnt += 1
    
                        if not dry_run:
                            fields.storeFields(txn)
                            txn.syncItem()
    
        if txn_cnt == 0:
            print "No DIV/BUY transactions needed to be converted to DIV_REINVEST in {}".format(account_name)
    
  4. 4 Posted by john.vogtle+inf... on 08 Dec, 2024 10:29 PM

    john.vogtle+infinitekind's Avatar

    @Sean - can you have a look at the code above and report what I might be missing...

  5. 5 Posted by Stuart Beesley ... on 09 Dec, 2024 07:13 AM

    Stuart Beesley (Mr Toolbox)'s Avatar

    Can you right click the txn after your code runs and grab show raw details. Then noop edit it and grab a new raw details. Post both here. I suspect there’s a leftover parameter that needs clearing out. With the raw details it will be easy to spot.

  6. 6 Posted by Stuart Beesley ... on 09 Dec, 2024 07:43 AM

    Stuart Beesley (Mr Toolbox)'s Avatar

    Ok. I haven’t tested this but taking a quick peek tells me you need to do this too:

    fields.hasCategory = True
    

    …right before you set the fields.category

    Then everything else should fire into place

    Let me know?

  7. 7 Posted by john.vogtle+inf... on 09 Dec, 2024 03:15 PM

    john.vogtle+infinitekind's Avatar

    Just saw this Stuart. I'll respond back with my findings.

  8. 8 Posted by john.vogtle+inf... on 10 Dec, 2024 01:31 AM

    john.vogtle+infinitekind's Avatar

    Hey Stuart. Busy work day. Here's what I've found:

    Raw Output Post Script

    0.acctid: 0f550735-75d1-4af2-9782-379c07151e19
    0.desc: Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L
    0.id: 664915ea-1161-4d11-8ade-e35c7f80ed2c
    0.invest.splittype: sec
    0.obj_type: 
    0.pamt: -248
    0.samt: 25500
    0.stat:
    acctid: 68803903-60c3-40e5-b15e-018caed07ec8 chk: desc: Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L dt: 20241202 dtentered: 1733757269614 id: 393ae0e1-8235-43a0-9fb0-9be9c71cdcad invest.txntype: divr memo: Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L obj_type: txn ol.match-status: 1 ol.match-type: 0 ol.orig-memo: Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L ol.orig-payee: Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L ol.orig-txn: { "dtinit-int" = "20241202" "dtpstd-int" = "20241202" "fitxnid" = "464HR7100000051241202008055329" "invst.buytype" = "BUY" "invst.commission" = "0" "invst.fees" = "0" "invst.load" = "0" "invst.numshares" = "0.255" "invst.ofxtxntype" = "BUYMF" "invst.securityid" = "94904P831" "invst.securityidtype" = "CUSIP" "invst.shareprice" = "9.7254902" "invst.subaccfund" = "OTHER" "invst.subaccttype" = "OTHER" "invst.taxes" = "0" "invst.totalamt" = "-248" "invst.txntype" = "buy" "memo" = "Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L" "orig_data" = "<BUYMF> <INVBUY> <INVTRAN> <FITID>464HR7100000051241202008055329 <DTTRADE>20241202120000.000[-5:EST] <DTSETTLE>20241202120000.000[-5:EST] <MEMO>Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L </INVTRAN> <SECID> <UNIQUEID>94904P831 <UNIQUEIDTYPE>CUSIP </SECID> <UNITS>0.255 <UNITPRICE>9.7254902 <COMMISSION>0.00 <TAXES>0.00 <FEES>0.00 <TOTAL>-2.48 <SUBACCTSEC>OTHER <SUBACCTFUND>OTHER </INVBUY> <BUYTYPE>BUY </BUYMF> " }


    ol_fitid_1: 464HR7100000051241202008055329 reinvest: true stat:
    td: 20241202 ts: 1733782880450 xfer_type: xfrtp_dividend
    After "no-op" DivReinvest
    0.acctid: 0f550735-75d1-4af2-9782-379c07151e19
    0.desc: Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L
    0.id: 664915ea-1161-4d11-8ade-e35c7f80ed2c
    0.invest.splittype: sec
    0.obj_type: 
    0.pamt: -248
    0.rate: 102.8225806451613
    0.samt: 25500
    0.stat:
    1.acctid: 0e0b3d4f-12cd-4ea9-987c-a17e0ce3a884 1.desc: Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L 1.id: fab26ce3-4de6-4d78-a28b-149baaa2645c 1.invest.splittype: inc 1.obj_type: 1.pamt: 248 1.samt: -248 acctid: 68803903-60c3-40e5-b15e-018caed07ec8 chk: desc: Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L dt: 20241202 dtentered: 1733757269614 id: 393ae0e1-8235-43a0-9fb0-9be9c71cdcad invest.txntype: divr memo: Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L obj_type: txn ol.match-status: 1 ol.match-type: 0 ol.orig-memo: Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L ol.orig-payee: Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L ol.orig-txn: { "dtinit-int" = "20241202" "dtpstd-int" = "20241202" "fitxnid" = "464HR7100000051241202008055329" "invst.buytype" = "BUY" "invst.commission" = "0" "invst.fees" = "0" "invst.load" = "0" "invst.numshares" = "0.255" "invst.ofxtxntype" = "BUYMF" "invst.securityid" = "94904P831" "invst.securityidtype" = "CUSIP" "invst.shareprice" = "9.7254902" "invst.subaccfund" = "OTHER" "invst.subaccttype" = "OTHER" "invst.taxes" = "0" "invst.totalamt" = "-248" "invst.txntype" = "buy" "memo" = "Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L" "orig_data" = "<BUYMF> <INVBUY> <INVTRAN> <FITID>464HR7100000051241202008055329 <DTTRADE>20241202120000.000[-5:EST] <DTSETTLE>20241202120000.000[-5:EST] <MEMO>Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L </INVTRAN> <SECID> <UNIQUEID>94904P831 <UNIQUEIDTYPE>CUSIP </SECID> <UNITS>0.255 <UNITPRICE>9.7254902 <COMMISSION>0.00 <TAXES>0.00 <FEES>0.00 <TOTAL>-2.48 <SUBACCTSEC>OTHER <SUBACCTFUND>OTHER </INVBUY> <BUYTYPE>BUY </BUYMF> " }


    ol_fitid_1: 464HR7100000051241202008055329 reinvest: true stat:
    td: 20241202 ts: 1733783075673 xfer_type: xfrtp_dividend
    The difference seems to be the addition of several lines:
    0.rate: 102.8225806451613
    0.samt: 25500                -- Unchanged
    0.stat:                             -- Unchanged
    1.acctid: 0e0b3d4f-12cd-4ea9-987c-a17e0ce3a884
    1.desc: Purchased 0.255 shares @ $9.7254902 WEITZ CORE PLUS INCOME FUND INSTL CL N/L
    1.id: fab26ce3-4de6-4d78-a28b-149baaa2645c
    1.invest.splittype: inc
    1.obj_type: 
    1.pamt: 248
    1.samt: -248
    
    I then added the fields.hasCategory = True to the script as you suggested and it worked like a champ!

    Thanks so much! Can you tell me where you found the fields documented or did you decompile a Java class to find out?

    Thanks again.
    -John

  9. 9 Posted by Stuart Beesley ... on 10 Dec, 2024 09:44 AM

    Stuart Beesley (Mr Toolbox)'s Avatar

    It would be great to swap coding tips with you. You if you like, email me:

  10. 10 Posted by Stuart Beesley ... on 10 Dec, 2024 09:47 AM

    Stuart Beesley (Mr Toolbox)'s Avatar

  11. 11 Posted by Stuart Beesley ... on 10 Dec, 2024 09:51 AM

    Stuart Beesley (Mr Toolbox)'s Avatar

    PS. Glad it worked. Yes your code did not trigger InvestFields to add the income split when changing the InvestType. Ideally this utility class could do with a changeInvestType() method. Setting that hasCategory field to True effectively did what was necessary for the internal code to add the missing split.

  12. Support Staff 12 Posted by Sean Reilly on 10 Dec, 2024 09:54 AM

    Sean Reilly's Avatar

    Hi John,

    The hasCategory property is set internally to the InvestFields object based on the transaction type. I should change it to be a read-only field (or private-set). For your code, instead of this:

    fields = InvestFields()
    # loads the details of the buy (price, #shares, amount, fee) since that's what you want to keep
    fields.setFieldStatus(txn)
    fields.txnType = InvestTxnType.DIVIDEND_REINVEST
    

    I recommend doing this:

    fields = InvestFields()
    fields.setFieldStatus(InvestTxnType.DIVIDEND_REINVEST, txn)
    
    which should set the hasCategory (and everything else) properly.

    Can you let me know if that doesn't solve the problem?

    Thanks,
    Sean

    --
    Sean Reilly
    Developer, The Infinite Kind
    https://infinitekind.com

  13. Support Staff 13 Posted by Sean Reilly on 10 Dec, 2024 10:15 AM

    Sean Reilly's Avatar

    ah yes, I see that I originally suggested the original code too... sorry about that! I'll take another look at this and actually run it to make sure it works before I send an update.

    --
    Sean Reilly
    Developer, The Infinite Kind
    https://infinitekind.com

  14. 14 Posted by john.vogtle+inf... on 11 Dec, 2024 01:25 AM

    john.vogtle+infinitekind's Avatar

    Thanks so much, Sean. I've made the changes to my code. I need to wait until the next time my Broker updates my account to see if the change you suggested works. Much appreciated.

Reply to this discussion

Internal reply

Formatting help / Preview (switch to plain text) No formatting (switch to Markdown)

Attaching KB article:

»

Attached Files

You can attach files up to 10MB

If you don't have an account yet, we need to confirm you're human and not a machine trying to post spam.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac