From 7c67986adb3a4ad83e1f282fd85b8e4be3e159ac Mon Sep 17 00:00:00 2001 From: Vlad <55182132+titov-vv@users.noreply.github.com> Date: Sun, 7 Apr 2024 16:43:31 +0100 Subject: [PATCH] Interactive Brokers statement import: processing of extra tax for MLP. --- jal/data_import/broker_statements/ibkr.py | 24 +++++++++++++++++++++-- jal/data_import/statement.py | 1 + tests/test_data/ibkr_dividends.json | 15 ++++++++++---- tests/test_data/ibkr_dividends.xml | 8 ++++++++ 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/jal/data_import/broker_statements/ibkr.py b/jal/data_import/broker_statements/ibkr.py index ff1eb15c..076f6725 100644 --- a/jal/data_import/broker_statements/ibkr.py +++ b/jal/data_import/broker_statements/ibkr.py @@ -43,7 +43,8 @@ class IBKR_AssetType: 'FUT': FOF.ASSET_FUTURES, 'WAR': FOF.ASSET_WARRANT, 'RIGHT': FOF.ASSET_RIGHTS, - 'CFD': FOF.ASSET_CFD + 'CFD': FOF.ASSET_CFD, + 'MLP': FOF.ASSET_MLP } def __init__(self, asset_type, subtype): @@ -982,7 +983,26 @@ def aggregate_taxes(self, taxes: list) -> list: part['amount'] = sum(tax['amount'] for tax in group_list) # and update quantity in it lieu_aggregated.append(part) taxes_aggregated = sorted(other_taxes + lieu_aggregated, key=key_func) - return taxes_aggregated + + # There might be additional record to withhold 10% of extra tax on partnerships (currently faced for MLP) reported in different dates + key_func = lambda x: (x['account'], x['asset'], x['currency'], x['description'], x['timestamp']) + mlp_taxes = [x for x in taxes_aggregated if self._find_in_list(self._data[FOF.ASSETS], 'id', x['asset'])['type'] == FOF.ASSET_MLP] + non_mlp_taxes = [x for x in taxes_aggregated if x not in mlp_taxes] + mlp_processed = [] + for k, group in groupby(mlp_taxes, key=key_func): + group_list = sorted(list(group), key=lambda x: (x['amount'])) # sort to have higher tax first + if len(group_list) > 2: + raise Statement_ImportError(self.tr("Too many records for MLP tax: ") + f"{group_list}") + if len(group_list) == 2: # 1st line is expected to be a base tax and 2nd line seems to be additional 10% + tax = group_list[1] # Store 2nd line as separate asset payment record + tax['id'] = max([0] + [x['id'] for x in self._data[FOF.ASSET_PAYMENTS]]) + 1 + tax['type'] = FOF.PAYMENT_FEE + tax['description'] += " - Extra 10% tax due to IRS section 1446" + self.drop_extra_fields(tax, ["source", "currency", "reported"]) + self._data[FOF.ASSET_PAYMENTS].append(tax) + mlp_processed.append(group_list[0]) # Keep only base tax + taxes_processed = sorted(non_mlp_taxes + mlp_processed, key=key_func) + return taxes_processed def load_taxes(self, taxes): cnt = 0 diff --git a/jal/data_import/statement.py b/jal/data_import/statement.py index 121cc2de..56d7f724 100644 --- a/jal/data_import/statement.py +++ b/jal/data_import/statement.py @@ -43,6 +43,7 @@ class FOF: ASSET_RIGHTS = "right" ASSET_CRYPTO = "crypto" ASSET_CFD = "cfd" + ASSET_MLP = "mlp" ACTION_MERGER = "merger" ACTION_SPLIT = "split" diff --git a/tests/test_data/ibkr_dividends.json b/tests/test_data/ibkr_dividends.json index 25035d4b..975ac135 100644 --- a/tests/test_data/ibkr_dividends.json +++ b/tests/test_data/ibkr_dividends.json @@ -7,17 +7,20 @@ {"id": 1, "name": "", "type": "money"}, {"id": 2, "type": "stock", "name": "BROOKFIELD RENEWABLE PARTNER", "isin": "BMG162581083", "country": "ca"}, {"id": 3, "type": "adr", "name": "TELEFONICA SA-SPON ADR", "isin": "US8793822086", "country": "es"}, - {"id": 4, "type": "stock", "name": "OWL ROCK CAPITAL CORP", "isin": "US69121K1043", "country": "us"} + {"id": 4, "type": "stock", "name": "OWL ROCK CAPITAL CORP", "isin": "US69121K1043", "country": "us"}, + {"id": 5, "type": "mlp", "name": "USA COMPRESSION PARTNERS LP", "isin": "US90290N1090", "country": "us"} ], "symbols": [ {"id": 1, "asset": 1, "symbol": "USD"}, {"id": 2, "asset": 2, "symbol": "BEP", "currency": 1, "note": "NYSE"}, {"id": 3, "asset": 3, "symbol": "TEF", "currency": 1, "note": "NYSE"}, - {"id": 4, "asset": 4, "symbol": "ORCC", "currency": 1, "note": "NYSE"} + {"id": 4, "asset": 4, "symbol": "ORCC", "currency": 1, "note": "NYSE"}, + {"id": 5, "asset": 5, "symbol": "USAC", "currency": 1, "note": "NYSE"} ], "assets_data": [ {"id": 1, "asset": 3, "reg_number": "879382208"}, - {"id": 2, "asset": 4, "reg_number": "69121K104"} + {"id": 2, "asset": 4, "reg_number": "69121K104"}, + {"id": 3, "asset": 5, "reg_number": "90290N109"} ], "asset_payments": [ {"id": 1, "type": "stock_dividend", "account": 1, "timestamp": 1595017200, "number": "13259965038", "asset": 3, "amount": 3.0, "price": "4.73", "tax": 0, "description": "TEF (US8793822086) STOCK DIVIDEND US8793822086 416666667 FOR 10000000000"}, @@ -35,7 +38,11 @@ {"id": 13, "type": "dividend", "account": 1, "timestamp": 1621023600, "number": "", "asset": 4, "amount": 20.46, "tax": 2.05, "description": "ORCC(US69121K1043) CASH DIVIDEND USD 0.31 PER SHARE (Ordinary Dividend)"}, {"id": 14, "type": "dividend", "account": 1, "timestamp": 1628886000, "number": "", "asset": 4, "amount": 31.0, "tax": 3.1, "description": "ORCC(US69121K1043) CASH DIVIDEND USD 0.31 PER SHARE (Ordinary Dividend)"}, {"id": 15, "type": "dividend", "account": 1, "timestamp": 1611087600, "number": "", "asset": 4, "amount": 38.61, "tax": 3.58, "description": "ORCC(US69121K1043) PAYMENT IN LIEU OF DIVIDEND (Ordinary Dividend)"}, - {"id": 16, "type": "dividend", "account": 1, "timestamp": 1621023600, "number": "", "asset": 4, "amount": 10.54, "tax": 1.05,"description": "ORCC(US69121K1043) PAYMENT IN LIEU OF DIVIDEND (Ordinary Dividend)"} + {"id": 16, "type": "dividend", "account": 1, "timestamp": 1621023600, "number": "", "asset": 4, "amount": 10.54, "tax": 1.05,"description": "ORCC(US69121K1043) PAYMENT IN LIEU OF DIVIDEND (Ordinary Dividend)"}, + {"id": 17, "type": "dividend", "account": 1, "timestamp": 1675455600, "number": "", "asset": 5, "amount": 5.25, "tax": 1.94, "description": "USAC(US90290N1090) CASH DIVIDEND USD 0.525 PER SHARE (Ordinary Dividend)"}, + {"id": 18, "type": "dividend", "account": 1, "timestamp": 1683318000, "number": "", "asset": 5, "amount": 5.25, "tax": 1.94, "description": "USAC(US90290N1090) CASH DIVIDEND USD 0.525 PER SHARE (Ordinary Dividend)"}, + {"id": 19, "type": "fee", "account": 1, "timestamp": 1675455600, "number": "", "asset": 5, "amount": -0.53, "description": "USAC(US90290N1090) CASH DIVIDEND USD 0.525 PER SHARE - US TAX - Extra 10% tax due to IRS section 1446"}, + {"id": 20, "type": "fee", "account": 1, "timestamp": 1683318000, "number": "", "asset": 5, "amount": -0.53, "description": "USAC(US90290N1090) CASH DIVIDEND USD 0.525 PER SHARE - US TAX - Extra 10% tax due to IRS section 1446"} ], "corporate_actions": [], "income_spending": [ diff --git a/tests/test_data/ibkr_dividends.xml b/tests/test_data/ibkr_dividends.xml index 3bc2bde5..c722a3cb 100644 --- a/tests/test_data/ibkr_dividends.xml +++ b/tests/test_data/ibkr_dividends.xml @@ -10,6 +10,7 @@ + @@ -73,6 +74,13 @@ + + + + + + +