From 407f72e19d3252906a306a38fd982d8cd6ef2b11 Mon Sep 17 00:00:00 2001 From: Marcus <> Date: Tue, 16 Jun 2026 11:59:43 +0200 Subject: [PATCH 1/2] adding additional event parsing for unknown events --- pytr/event.py | 25 +++-- tests/address_changed.json | 12 +++ tests/kauforder_abgelehnt.json | 99 ++++++++++++++++++++ tests/neues_geraet.json | 12 +++ tests/securities_transfer_incoming.json | 117 ++++++++++++++++++++++++ tests/test_events.py | 30 ++++++ 6 files changed, 288 insertions(+), 7 deletions(-) create mode 100644 tests/address_changed.json create mode 100644 tests/kauforder_abgelehnt.json create mode 100644 tests/neues_geraet.json create mode 100644 tests/securities_transfer_incoming.json diff --git a/pytr/event.py b/pytr/event.py index a923403..0c7e0e3 100644 --- a/pytr/event.py +++ b/pytr/event.py @@ -79,6 +79,8 @@ class PPEventType(EventType): "TAX_CORRECTION": PPEventType.TAX_REFUND, "TAX_REFUND": PPEventType.TAX_REFUND, "ssp_tax_correction_invoice": PPEventType.TAX_REFUND, + # Transfers + "SSP_SECURITIES_TRANSFER_INCOMING": PPEventType.TRANSFER_IN, # Trade invoices "ORDER_EXECUTED": ConditionalEventType.TRADE_INVOICE, "SAVINGS_PLAN_EXECUTED": ConditionalEventType.TRADE_INVOICE, @@ -159,6 +161,7 @@ class PPEventType(EventType): } events_known_ignored = [ + "ADDRESS_CHANGED", "AML_SOURCE_OF_WEALTH_RESPONSE_EXECUTED", "CASH_ACCOUNT_CHANGED", "CREDIT_CANCELED", @@ -196,6 +199,7 @@ class PPEventType(EventType): "STOCK_PERK_REFUNDED", "TAX_YEAR_END_REPORT", "TAX_YEAR_END_REPORT_CREATED", + "TRADING_ORDER_REJECTED", "VERIFICATION_TRANSFER_ACCEPTED", "YEAR_END_TAX_REPORT", "card_failed_verification", @@ -222,6 +226,7 @@ class PPEventType(EventType): "Ex-Post Kosteninformation", "Jährlicher Steuerreport", "Neue IBAN", + "Neues Gerät", "Persönliche Daten", "PUK versendet", "Rechtliche Dokumente", @@ -230,10 +235,12 @@ class PPEventType(EventType): events_known_ignored_subtitle = [ "Cash oder Aktie", + "Dividende. Cash oder Stockdividende?", "Erteilt", "Jährliche Hauptversammlung", "Kartenprüfung", "Kauf-Abrechnung storniert", + "Kauforder abgelehnt", "Kauforder storniert", "Limit-Buy-Order abgelaufen", "Limit-Buy-Order erstellt", @@ -309,12 +316,14 @@ def from_dict(cls, event_dict: Dict[Any, Any]): sections = event_dict.get("details", {}).get("sections", [{}]) - transaction_dict = next(filter(lambda x: x.get("title") in ["Transaktion", "Geschäft"], sections), None) + transaction_dict = next( + filter(lambda x: x.get("title") in ["Transaktion", "Geschäft", "Transaction"], sections), None + ) if transaction_dict: # old style event dump_dict["maintitle"] = transaction_dict["title"] data = transaction_dict.get("data", [{}]) - fees_dict = next(filter(lambda x: x["title"] == "Gebühr", data), None) + fees_dict = next(filter(lambda x: x["title"] in ["Gebühr", "Fee"], data), None) taxes_dict = next(filter(lambda x: x["title"] in ["Steuer", "Steuern"], data), None) uebersicht_dict = next(filter(lambda x: x.get("title") in ["Übersicht", "Overview"], sections), None) @@ -445,8 +454,6 @@ def from_dict(cls, event_dict: Dict[Any, Any]): if title == "Auszahlungskonto" and subtitle == "Geändert": ignoreEvent = True - if title == "Neues Gerät" and subtitle == "Gekoppelt": - ignoreEvent = True if title == "Wertpapierdepot" and subtitle == "Eröffnet": ignoreEvent = True if title == "Basisinformationen" and subtitle == "Erhalten": @@ -488,6 +495,7 @@ def from_dict(cls, event_dict: Dict[Any, Any]): PPEventType.SWAP, PPEventType.TAX_REFUND, PPEventType.TAXES, + PPEventType.TRANSFER_IN, ] and subtitle not in [ "Aufruf von Zwischenpapieren", "Wertlos", @@ -511,6 +519,7 @@ def from_dict(cls, event_dict: Dict[Any, Any]): PPEventType.SPLIT, PPEventType.SWAP, PPEventType.TAXES, + PPEventType.TRANSFER_IN, ]: # Parse ISIN for section in sections: @@ -607,12 +616,14 @@ def _parse_shares_value_note( sections = event_dict.get("details", {}).get("sections", [{}]) - transaction_dict = next(filter(lambda x: x.get("title") in ["Transaktion", "Geschäft"], sections), None) + transaction_dict = next( + filter(lambda x: x.get("title") in ["Transaktion", "Geschäft", "Transaction"], sections), None + ) if transaction_dict: # old style event dump_dict["maintitle"] = transaction_dict["title"] data = transaction_dict.get("data", [{}]) - shares_dict = next(filter(lambda x: x["title"] in ["Aktien", "Anteile"], data), None) + shares_dict = next(filter(lambda x: x["title"] in ["Aktien", "Anteile", "Shares"], data), None) uebersicht_dict = next(filter(lambda x: x.get("title") in ["Übersicht", "Overview"], sections), None) if uebersicht_dict: @@ -722,7 +733,7 @@ def _parse_shares_value_note( if (event_type == PPEventType.SPINOFF or subtitle == "Wertlos") and value is None: value = 0 - if event_type in [PPEventType.SPLIT, PPEventType.SWAP] and value is None: + if event_type in [PPEventType.SPLIT, PPEventType.SWAP, PPEventType.TRANSFER_IN] and value is None: value = 0 if event_type in [PPEventType.SPINOFF, PPEventType.SWAP]: diff --git a/tests/address_changed.json b/tests/address_changed.json new file mode 100644 index 0000000..69ea961 --- /dev/null +++ b/tests/address_changed.json @@ -0,0 +1,12 @@ +{ + "id": "059ce705-3c54-4019-b008-79946d835a37", + "timestamp": "2024-09-13T14:58:01.873+0000", + "title": "Adresse", + "icon": "logos/timeline_flag/v2", + "subtitle": "Geändert", + "action": null, + "eventType": "ADDRESS_CHANGED", + "hidden": false, + "deleted": false, + "source": "timelineActivity" +} diff --git a/tests/kauforder_abgelehnt.json b/tests/kauforder_abgelehnt.json new file mode 100644 index 0000000..31a9244 --- /dev/null +++ b/tests/kauforder_abgelehnt.json @@ -0,0 +1,99 @@ +{ + "id": "7385dbae-c8c4-4e59-bc40-da708a074e96", + "timestamp": "2026-03-03T11:35:17.995+0000", + "title": "Silver 3x Lev USD (Acc)", + "icon": "logos/IE00B7XD2195/v2", + "subtitle": "Kauforder abgelehnt", + "action": { + "type": "timelineDetail", + "payload": "7385dbae-c8c4-4e59-bc40-da708a074e96" + }, + "eventType": "TRADING_ORDER_REJECTED", + "hidden": false, + "deleted": false, + "source": "timelineActivity", + "details": { + "id": "7385dbae-c8c4-4e59-bc40-da708a074e96", + "sections": [ + { + "title": "Deine Kauforder wurde abgelehnt", + "data": { + "icon": "logos/IE00B7XD2195/v2", + "timestamp": "2026-03-03T11:34:47.966Z", + "status": "executed" + }, + "action": { + "payload": "IE00B7XD2195", + "type": "instrumentDetail" + }, + "type": "header" + }, + { + "title": "Übersicht", + "data": [ + { + "title": "Kauf", + "detail": { + "text": "Abgelehnt", + "functionalStyle": "CANCELED", + "type": "status" + }, + "style": "plain" + }, + { + "title": "Asset", + "detail": { + "text": "Silver 3x Lev USD (Acc)", + "displayValue": { + "text": "Silver 3x Lev USD (Acc)" + }, + "type": "text" + }, + "style": "plain" + }, + { + "title": "Transaktion", + "detail": { + "text": "0,505689 × --", + "action": { + "payload": { + "id": "00000000-0000-0000-0000-000000000000", + "sections": [ + { + "title": "Transaktion", + "type": "title" + }, + { + "data": [ + { + "title": "Aktien", + "detail": { + "text": "0,505689", + "displayValue": { + "text": "0,505689" + }, + "type": "text" + }, + "style": "plain" + } + ], + "type": "table" + } + ] + }, + "type": "infoPage" + }, + "displayValue": { + "text": "--", + "prefix": "0,505689 × " + }, + "type": "text" + }, + "style": "plain" + } + ], + "type": "table" + } + ] + } +} diff --git a/tests/neues_geraet.json b/tests/neues_geraet.json new file mode 100644 index 0000000..6005727 --- /dev/null +++ b/tests/neues_geraet.json @@ -0,0 +1,12 @@ +{ + "id": "5f054afa-2eb5-409b-805d-6d3d9d45ffad", + "timestamp": "2026-02-18T17:45:01.178+0000", + "title": "Neues Gerät", + "icon": "logos/timeline_computers/v2", + "subtitle": null, + "action": null, + "eventType": null, + "hidden": false, + "deleted": false, + "source": "timelineActivity" +} diff --git a/tests/securities_transfer_incoming.json b/tests/securities_transfer_incoming.json new file mode 100644 index 0000000..ad3032e --- /dev/null +++ b/tests/securities_transfer_incoming.json @@ -0,0 +1,117 @@ +{ + "id": "756ce232-a67d-4d85-a431-57483426450a", + "timestamp": "2025-10-13T12:43:03.988+0000", + "title": "MSCI EM USD (Acc)", + "icon": "logos/IE00BTJRMP35/v2", + "subtitle": "Aktien erhalten", + "action": { + "type": "timelineDetail", + "payload": "756ce232-a67d-4d85-a431-57483426450a" + }, + "eventType": "SSP_SECURITIES_TRANSFER_INCOMING", + "hidden": false, + "deleted": false, + "source": "timelineActivity", + "details": { + "id": "756ce232-a67d-4d85-a431-57483426450a", + "sections": [ + { + "type": "header", + "title": "You received 7 shares", + "data": { + "status": "executed", + "icon": "logos/IE00BTJRMP35/v2", + "timestamp": "2025-10-13T12:43:03.988647Z" + } + }, + { + "type": "table", + "title": "Overview", + "data": [ + { + "title": "Status", + "style": "plain", + "detail": { + "type": "status", + "text": "Completed", + "functionalStyle": "EXECUTED" + } + }, + { + "title": "Asset", + "style": "plain", + "detail": { + "type": "text", + "text": "MSCI EM USD (Acc)" + } + }, + { + "title": "Action", + "style": "plain", + "detail": { + "type": "text", + "text": "Securities transfer" + } + } + ] + }, + { + "type": "steps", + "title": "Status", + "steps": [ + { + "leading": { + "avatar": { + "type": "bullet", + "status": "completed" + }, + "connection": { + "order": "first" + } + }, + "content": { + "title": "Securities received by Trade Republic", + "timestamp": "2025-10-13T12:43:03.988647Z" + } + }, + { + "leading": { + "avatar": { + "type": "bullet", + "status": "completed" + }, + "connection": { + "order": "last" + } + }, + "content": { + "title": "Transfer complete" + } + } + ] + }, + { + "type": "table", + "title": "Transaction", + "data": [ + { + "title": "Shares", + "style": "plain", + "detail": { + "type": "text", + "text": "7" + } + }, + { + "title": "Fee", + "style": "plain", + "detail": { + "type": "text", + "text": "Free" + } + } + ] + } + ] + } +} diff --git a/tests/test_events.py b/tests/test_events.py index 1bd47a8..15505fa 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -6,6 +6,10 @@ def test_events(): test_data = [ + { + "filename": "address_changed.json", + "event_type": None, + }, { "filename": "aktien_entfernt.json", "event_type": ConditionalEventType.TRADE_INVOICE, @@ -814,6 +818,10 @@ def test_events(): } ], }, + { + "filename": "kauforder_abgelehnt.json", + "event_type": None, + }, { "filename": "legacy_verkauforder_neu_abgerechnet.json", "event_type": ConditionalEventType.TRADE_INVOICE, @@ -974,6 +982,10 @@ def test_events(): } ], }, + { + "filename": "neues_geraet.json", + "event_type": None, + }, { "filename": "outgoing_transfer.json", "event_type": PPEventType.REMOVAL, @@ -1423,6 +1435,24 @@ def test_events(): } ], }, + { + "filename": "securities_transfer_incoming.json", + "event_type": PPEventType.TRANSFER_IN, + "title": "MSCI EM USD (Acc)", + "isin": "IE00BTJRMP35", + "shares": 7, + "value": 0, + "transactions": [ + { + "Datum": "2025-10-13T12:43:03", + "Typ": "Umbuchung (Eingang)", + "Wert": 0, + "Notiz": "MSCI EM USD (Acc)", + "ISIN": "IE00BTJRMP35", + "Stück": 7, + } + ], + }, { "filename": "sell_stop.json", "event_type": ConditionalEventType.TRADE_INVOICE, From b890dcc2de00858f45c235671718e20896a8df5e Mon Sep 17 00:00:00 2001 From: shukali <15327691+shukali@users.noreply.github.com> Date: Tue, 16 Jun 2026 12:37:37 +0200 Subject: [PATCH 2/2] bump project version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a50b9b1..7417906 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "pytr" -version = "0.4.10" +version = "0.4.11" description = "Use TradeRepublic in terminal" readme = "README.md" requires-python = ">=3.10"