Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modify Trade Republic PDF-Importer to support new transaction #4003

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,32 @@
PDFBox Version: 1.8.17
Portfolio Performance Version: 0.68.4
-----------------------------------------
TRADE REPUBLIC BANK GMBH KÖPENICKER STRASSE 40C 10179 BERLIN
yEHn kQV jkslJw PAGE 1 from 1
KebCKUwgH 7 DATE 02.05.2023
17069 FuXVDJtQ ORDER 1778-101b
EXECUTION eiX5-F5D7
SECURITIES ACCOUNT 4510405687
SECURITIES SETTLEMENT
OVERVIEW
Market-Order Sell on 02.05.2023 at 18:18 (Europe/Berlin).
The counterparty to the transaction is Société Générale S.A.
POSITION QUANTITY PRICE AMOUNT
Société Générale Effekten GmbH 490 Pcs. 0.886 EUR 434.14 EUR
MiniL O.End CBOE VIX 11,49
ISIN: DE000SQ6QKU9
TOTAL 434.14 EUR
BILLING
POSITION AMOUNT
External cost surcharge -1.00 EUR
TOTAL 433.14 EUR
BOOKING
CLEARING ACCOUNT VALUE DATE AMOUNT
DE32502109007011423226 04.05.2023 433.14 EUR
Société Générale Effekten GmbH MiniL O.End CBOE VIX 11,49 in giro collection storage in Germany.
This statement is generated automatically and therefore not signed.
If no VAT is shown, it is according to § 4 No. 8 UStG for a sales tax-free service.
Trade Republic Bank GmbH www.traderepublic.com Headquarters: Berlin Directors
Köpenicker Straße 40c service@traderepublic.com AG Charlottenburg HRB 244347 B Andreas Torner
10179 Berlin VAT-ID DE307510626 Gernot Mittendorfer
ABRE / 02.05.2023 / 94116251 / nL30-3QHZ
Expand Up @@ -2118,6 +2118,37 @@ public void testWertpapierVerkauf08()
is(Money.of(CurrencyUnit.EUR, Values.Amount.factorize(0.00))));
}

@Test
public void testSell01()
{
TradeRepublicPDFExtractor extractor = new TradeRepublicPDFExtractor(new Client());

List<Exception> errors = new ArrayList<>();

List<Item> results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Sell01.txt"), errors);

assertThat(errors, empty());
assertThat(countSecurities(results), is(1L));
assertThat(countBuySell(results), is(1L));
assertThat(countAccountTransactions(results), is(0L));
assertThat(results.size(), is(2));
new AssertImportActions().check(results, CurrencyUnit.EUR);

// check security
assertThat(results, hasItem(security( //
hasIsin("DE000SQ6QKU9"), hasWkn(null), hasTicker(null), //
hasName("Société Générale Effekten GmbH MiniL O.End CBOE VIX 11,49"), //
hasCurrencyCode("EUR"))));

// check buy sell transaction
assertThat(results, hasItem(sale( //
hasDate("2023-05-02T18:18"), hasShares(490.00), //
hasSource("Sell01.txt"), //
hasNote("Order: 1778-101b | Execution: eiX5-F5D7"), //
hasAmount("EUR", 433.14), hasGrossValue("EUR", 434.14), //
hasTaxes("EUR", 0.00), hasFees("EUR", 1.00))));
}

@Test
public void testTilgung01()
{
Expand Down
Expand Up @@ -86,11 +86,12 @@ private void addBuySellTransaction()

// Is type --> "Verkauf" change from BUY to SELL
.section("type").optional() //
.match("^((Limit|Stop\\-Market|Market)\\-Order(\\s)?)?" //
.match("(?i)^((Limit|Stop\\-Market|Market)\\-Order(\\s)?)?" //
+ "(?<type>(Kauf" //
+ "|Buy"
+ "|Acquisto" //
+ "|Verkauf" //
+ "|Verkauf"
+ "|Sell" //
+ "|Sparplanausf.hrung" //
+ "|SAVINGS PLAN" //
+ "|Ex.cution de l.investissement programm." //
Expand All @@ -100,6 +101,7 @@ private void addBuySellTransaction()
+ ".*$") //
.assign((t, v) -> {
if ("Verkauf".equals(v.get("type")) //
|| "Sell".equals(v.get("type")) //
|| "ZWANGSÜBERNAHME".equals(v.get("type")) //
|| "TILGUNG".equals(v.get("type")))
t.setType(PortfolioTransaction.Type.SELL);
Expand Down Expand Up @@ -223,10 +225,11 @@ private void addBuySellTransaction()
// Limit-Order Buy on 28.04.2023 at 11:13 (Europe/Berlin).
// Verkauf am 26.02.2021, um 11:44 Uhr.
// Market-OrderAcquisto il 01.06.2023 alle 10:46 (Europe/Berlin) su Lang & Schwarz Exchange.
// Market-Order Sell on 02.05.2023 at 18:18 (Europe/Berlin).
// @formatter:on
section -> section //
.attributes("date", "time") //
.match("^((Limit|Stop\\-Market|Market)\\-Order(\\s)?)?(Buy|Acquisto|Kauf|Verkauf) .* (?<date>([\\d]{2}\\.[\\d]{2}\\.[\\d]{4}|[\\d]{4}\\-[\\d]{2}\\-[\\d]{2}))(,)? (um|at|alle) (?<time>[\\d]{2}:[\\d]{2}) .*$") //
.match("^((Limit|Stop\\-Market|Market)\\-Order(\\s)?)?(Buy|Acquisto|Kauf|Verkauf|Sell) .* (?<date>([\\d]{2}\\.[\\d]{2}\\.[\\d]{4}|[\\d]{4}\\-[\\d]{2}\\-[\\d]{2}))(,)? (um|at|alle) (?<time>[\\d]{2}:[\\d]{2}).*$") //
.assign((t, v) -> t.setDate(asDate(v.get("date"), v.get("time")))),
// @formatter:off
// Exécution de l'investissement programmé le 17/01/2022 sur le Lang & Schwarz Exchange.
Expand Down Expand Up @@ -536,17 +539,14 @@ private void addBuySellTransaction()
private void addSellWithNegativeAmountTransaction()
{
DocumentType type = new DocumentType("(WERTPAPIERABRECHNUNG" //
+ "|WERTPAPIERABRECHNUNG SPARPLAN" //
+ "|SECURITIES SETTLEMENT SAVINGS PLAN" //
+ "|SECURITIES SETTLEMENT" //
+ "|REINVESTIERUNG" //
+ "|INVESTISSEMENT)", //
+ "|REGOLAMENTO TITOLI)", //
"(ABRECHNUNG CRYPTOGESCH.FT|CRYPTO SPARPLAN)");
this.addDocumentTyp(type);

Transaction<AccountTransaction> pdfTransaction = new Transaction<>();

Block firstRelevantLine = new Block("^(.*\\-Order )?Verkauf.*$");
Block firstRelevantLine = new Block("^(.*\\-Order )?(Verkauf|Sell).*$");
type.addBlock(firstRelevantLine);
firstRelevantLine.set(pdfTransaction);

Expand All @@ -565,52 +565,78 @@ private void addSellWithNegativeAmountTransaction()
// ISIN: DE000A3H23V7
// @formatter:on
.section("name", "currency", "nameContinued", "isin") //
.match("^(?<name>.*) [\\.,\\d]+ Stk\\. [\\.,\\d]+ (?<currency>[\\w]{3}) [\\.,\\d]+ [\\w]{3}$") //
.match("^(?<name>.*) [\\.,\\d]+ (Stk\\.|titre\\(s\\)|Pcs\\.|Pz\\.) [\\.,\\d]+ (?<currency>[\\w]{3}) [\\.,\\d]+ [\\w]{3}$") //
.match("^(?<nameContinued>.*)$") //
.match("^(ISIN: )?(?<isin>[A-Z]{2}[A-Z0-9]{9}[0-9])$") //
.assign((t, v) -> t.setSecurity(getOrCreateSecurity(v)))

// @formatter:off
// Clinuvel Pharmaceuticals Ltd. 80 Stk. 22,82 EUR 1.825,60 EUR
// @formatter:on
.section("shares") //
.match("^.* (?<shares>[\\.,\\d]+) Stk\\. [\\.,\\d]+ [\\w]{3} [\\.,\\d]+ [\\w]{3}$") //
.assign((t, v) -> t.setShares(asShares(v.get("shares"))))
.oneOf( //
// @formatter:off
// Clinuvel Pharmaceuticals Ltd. 80 Stk. 22,82 EUR 1.825,60 EUR
// Tencent Holdings Ltd. 0,3773 titre(s) 53,00 EUR 20,00 EUR
// zjBAM Corp. 125 Pz. 29,75 EUR 3.718,75 EUR
// @formatter:on
section -> section //
.attributes("shares") //
.match("^.* (?<shares>[\\.,\\d]+) (Stk\\.|titre\\(s\\)|Pz\\.) .*$") //
.assign((t, v) -> t.setShares(asShares(v.get("shares")))),
// @formatter:off
// Berkshire Hathaway Inc. 0.3367 Pcs. 297.00 EUR 100.00 EUR
// @formatter:on
section -> section //
.attributes("shares") //
.match("^.* (?<shares>[\\.,\\d]+) Pcs\\. .*$") //
.assign((t, v) -> t.setShares(asShares(v.get("shares"), "en", "US"))),
// @formatter:off
// Bundesrep.Deutschland 1.019 EUR 98,05 % 999,13 EUR
// @formatter:on
section -> section //
.attributes("shares") //
.match("^.* (?<shares>[\\.,\\d]+) [\\w]{3} [\\.,\\d]+ % [\\.,\\d]+ [\\w]{3}$")
.assign((t, v) -> {
// @formatter:off
// Percentage quotation, workaround for bonds
// @formatter:on
BigDecimal shares = asBigDecimal(v.get("shares"));
t.setShares(Values.Share.factorize(shares.doubleValue() / 100));
}))

// @formatter:off
// Market-Order Verkauf am 18.06.2019, um 17:50 Uhr an der Lang & Schwarz Exchange.
// Stop-Market-Order Verkauf am 10.06.2020, um 11:42 Uhr.
// Limit-Order Verkauf am 21.07.2020, um 09:30 Uhr an der Lang & Schwarz Exchange.
// Verkauf am 26.02.2021, um 11:44 Uhr.
// Market-Order Sell on 02.05.2023 at 18:18 (Europe/Berlin).
// @formatter:on
.section("date", "time") //
.match("^((Limit|Stop-Market|Market)-Order )?(Kauf|Verkauf) .* (?<date>([\\d]{2}\\.[\\d]{2}\\.[\\d]{4}|[\\d]{4}\\-[\\d]{2}\\-[\\d]{2})), um (?<time>[\\d]{2}:[\\d]{2}) .*$") //
.match("^((Limit|Stop\\-Market|Market)\\-Order(\\s)?)?(Verkauf|Sell) .* (?<date>([\\d]{2}\\.[\\d]{2}\\.[\\d]{4}|[\\d]{4}\\-[\\d]{2}\\-[\\d]{2}))(,)? (um|at|alle) (?<time>[\\d]{2}:[\\d]{2}).*$") //
.assign((t, v) -> t.setDateTime(asDate(v.get("date"), v.get("time"))))

// @formatter:off
// GESAMT 0,34 EUR
// GESAMT -0,66 EUR
// @formatter:on
.section("negative").optional() //
.match("^GESAMT [\\.,\\d]+ [\\w]{3}$") //
.match("^GESAMT (?<negative>\\-)[\\.,\\d]+ [\\w]{3}$") //
.match("^(GESAMT|TOTAL|TOTALE) [\\.,\\d]+ [\\w]{3}$") //
.match("^(GESAMT|TOTAL|TOTALE) (?<negative>\\-)[\\.,\\d]+ [\\w]{3}$") //
.assign((t, v) -> type.getCurrentContext().putBoolean("negative", true))

// @formatter:off
// GESAMT -0,66 EUR
// @formatter:on
.section("negative").optional() //
.match("^GESAMT (?<negative>\\-)[\\.,\\d]+ [\\w]{3}$") //
.match("^(GESAMT|TOTAL|TOTALE) (?<negative>\\-)[\\.,\\d]+ [\\w]{3}$") //
.assign((t, v) -> {
if (!type.getCurrentContext().getBoolean("negative"))
type.getCurrentContext().putBoolean("negative", true);
})

// @formatter:off
// Fremdkostenzuschlag -1,00 EUR
// External cost surcharge -1.00 EUR
// @formatter:on
.section("currency", "amount").optional() //
.match("^Fremdkostenzuschlag \\-(?<amount>[\\.,\\d]+) (?<currency>[\\w]{3})$") //
.match("^(Fremdkostenzuschlag|External cost surcharge) \\-(?<amount>[\\.,\\d]+) (?<currency>[\\w]{3})$") //
.assign((t, v) -> {
if (type.getCurrentContext().getBoolean("negative"))
{
Expand Down