From 0958eab63f279075222c6e004641dcdc66e913bf Mon Sep 17 00:00:00 2001 From: VibeNL Date: Tue, 19 Sep 2023 15:47:01 +0200 Subject: [PATCH] Fix parser of DeGiro with the changed format --- .../FileImporter/DeGiro/DeGiroParserTests.cs | 35 ++++++++++++++- .../FileImporter/FileImporterTaskTests.cs | 2 +- .../DeGiro/Example1/TestFileSingleOrder.csv | 4 +- .../Example3/TestFileMultipleOrders.csv | 33 +++++++------- .../DeGiro/Example4/TestFileDividend.csv | 3 ++ .../GhostfolioSidekick.UnitTests.csproj | 3 ++ .../FileImporter/DeGiro/DeGiroParser.cs | 45 +++++++++++++++---- .../FileImporter/DeGiro/DeGiroRecord.cs | 3 +- 8 files changed, 98 insertions(+), 30 deletions(-) create mode 100644 GhostfolioSidekick.UnitTests/FileImporter/TestFiles/DeGiro/Example4/TestFileDividend.csv diff --git a/GhostfolioSidekick.UnitTests/FileImporter/DeGiro/DeGiroParserTests.cs b/GhostfolioSidekick.UnitTests/FileImporter/DeGiro/DeGiroParserTests.cs index 53522359..02eb4569 100644 --- a/GhostfolioSidekick.UnitTests/FileImporter/DeGiro/DeGiroParserTests.cs +++ b/GhostfolioSidekick.UnitTests/FileImporter/DeGiro/DeGiroParserTests.cs @@ -74,7 +74,7 @@ public async Task ConvertToOrders_TestFileSingleOrder_Converted() } [Fact] - public async Task ConvertToOrders_TestFileMuitpleOrders_Converted() + public async Task ConvertToOrders_TestFileMultipleOrders_Converted() { // Arrange var parser = new DeGiroParser(api.Object); @@ -120,5 +120,38 @@ public async Task ConvertToOrders_TestFileMuitpleOrders_Converted() ReferenceCode = "67e39ca1-2f10-4f82-8365-1baad98c398f" } }); } + + [Fact] + public async Task ConvertToOrders_TestFileDividend_Converted() + { + // Arrange + var parser = new DeGiroParser(api.Object); + var fixture = new Fixture(); + + var asset = fixture.Build().With(x => x.Currency, "EUR").Create(); + var account = fixture.Create(); + + api.Setup(x => x.GetAccountByName(account.Name)).ReturnsAsync(account); + api.Setup(x => x.FindSymbolByISIN("NL0009690239", null)).ReturnsAsync(asset); + + // Act + var orders = await parser.ConvertToOrders(account.Name, new[] { "./FileImporter/TestFiles/DeGiro/Example4/TestFileDividend.csv" }); + + // Assert + orders.Should().BeEquivalentTo(new[] { new Order { + AccountId = account.Id, + Asset = asset, + Comment = "Transaction Reference: [DIVIDEND_14-9-2023_06:32_NL0009690239]", + Currency = asset.Currency, + FeeCurrency = asset.Currency, + Date = new DateTime(2023,09,14,6, 32,0, DateTimeKind.Utc), + Fee = 1.44M, + Quantity = 1, + Type = OrderType.DIVIDEND, + UnitPrice = 9.57M, + ReferenceCode = "DIVIDEND_14-9-2023_06:32_NL0009690239" + } }); + } + } } \ No newline at end of file diff --git a/GhostfolioSidekick.UnitTests/FileImporter/FileImporterTaskTests.cs b/GhostfolioSidekick.UnitTests/FileImporter/FileImporterTaskTests.cs index f86b3b0a..d84d2b5d 100644 --- a/GhostfolioSidekick.UnitTests/FileImporter/FileImporterTaskTests.cs +++ b/GhostfolioSidekick.UnitTests/FileImporter/FileImporterTaskTests.cs @@ -30,7 +30,7 @@ public async Task LoadAccounts() // Assert testImporter.Verify(x => x.ConvertToOrders("Coinbase", It.Is>(y => y.Count() == 2)), Times.Once); - testImporter.Verify(x => x.ConvertToOrders("DeGiro", It.Is>(y => y.Count() == 3)), Times.Once); + testImporter.Verify(x => x.ConvertToOrders("DeGiro", It.Is>(y => y.Count() == 4)), Times.Once); testImporter.Verify(x => x.ConvertToOrders("ScalableCapital", It.Is>(y => y.Count() == 5)), Times.Once); testImporter.Verify(x => x.ConvertToOrders("Trading212", It.Is>(y => y.Count() == 8)), Times.Once); } diff --git a/GhostfolioSidekick.UnitTests/FileImporter/TestFiles/DeGiro/Example1/TestFileSingleOrder.csv b/GhostfolioSidekick.UnitTests/FileImporter/TestFiles/DeGiro/Example1/TestFileSingleOrder.csv index fd68ed38..919c90ea 100644 --- a/GhostfolioSidekick.UnitTests/FileImporter/TestFiles/DeGiro/Example1/TestFileSingleOrder.csv +++ b/GhostfolioSidekick.UnitTests/FileImporter/TestFiles/DeGiro/Example1/TestFileSingleOrder.csv @@ -3,6 +3,6 @@ Datum,Tijd,Valutadatum,Product,ISIN,Omschrijving,FX,Mutatie,,Saldo,,Order Id 10-07-2023,17:34,10-07-2023,FLATEX EURO BANKACCOUNT,NLFLATEXACNT,Degiro Cash Sweep Transfer,,EUR,"78,30",EUR,"100,00", 07-07-2023,05:01,06-07-2023,,,iDEAL Deposit,,EUR,"100,00",EUR,"21,70", 07-07-2023,05:01,06-07-2023,,,Reservation iDEAL / Sofort Deposit,,EUR,"-100,00",EUR,"-78,30", -06-07-2023,09:39,06-07-2023,VANGUARD S&P500,IE00B3XXRP09,DEGIRO Transactiekosten en/of kosten van derden,,EUR,"-1,00",EUR,"21,70",b7ab0494-1b46-4e2f-9bd2-f79e6c87cb5b -06-07-2023,09:39,06-07-2023,VANGUARD S&P500,IE00B3XXRP09,"Koop 1 @ 77,3 EUR",,EUR,"-77,30",EUR,"22,70",b7ab0494-1b46-4e2f-9bd2-f79e6c87cb5b +06-07-2023,09:39,06-07-2023,VANGUARD S&P500,IE00B3XXRP09,DEGIRO Transactiekosten en/of kosten van derden,,EUR,-1.00,EUR,21.70,b7ab0494-1b46-4e2f-9bd2-f79e6c87cb5b +06-07-2023,09:39,06-07-2023,VANGUARD S&P500,IE00B3XXRP09,"Koop 1 @ 77,3 EUR",,EUR,-77.30,EUR,22.70,b7ab0494-1b46-4e2f-9bd2-f79e6c87cb5b 06-07-2023,09:37,06-07-2023,,,Reservation iDEAL / Sofort Deposit,,EUR,"100,00",EUR,"100,00", diff --git a/GhostfolioSidekick.UnitTests/FileImporter/TestFiles/DeGiro/Example3/TestFileMultipleOrders.csv b/GhostfolioSidekick.UnitTests/FileImporter/TestFiles/DeGiro/Example3/TestFileMultipleOrders.csv index 0ee3deeb..295a258c 100644 --- a/GhostfolioSidekick.UnitTests/FileImporter/TestFiles/DeGiro/Example3/TestFileMultipleOrders.csv +++ b/GhostfolioSidekick.UnitTests/FileImporter/TestFiles/DeGiro/Example3/TestFileMultipleOrders.csv @@ -1,17 +1,18 @@ Datum,Tijd,Valutadatum,Product,ISIN,Omschrijving,FX,Mutatie,,Saldo,,Order Id -13-07-2023,11:49,12-07-2023,,,"Overboeking van uw geldrekening bij flatexDEGIRO Bank 976,17 EUR",,,,EUR,"23,83", -13-07-2023,11:49,12-07-2023,FLATEX EURO BANKACCOUNT,NLFLATEXACNT,Degiro Cash Sweep Transfer,,EUR,"976,17",EUR,"1000,00", -12-07-2023,04:51,11-07-2023,,,iDEAL Deposit,,EUR,"1000,00",EUR,"23,83", -12-07-2023,04:51,11-07-2023,,,Reservation iDEAL / Sofort Deposit,,EUR,"-1000,00",EUR,"-976,17", -11-07-2023,16:34,11-07-2023,,,"Overboeking van uw geldrekening bij flatexDEGIRO Bank 21,7 EUR",,,,EUR,"23,83", -11-07-2023,16:34,11-07-2023,FLATEX EURO BANKACCOUNT,NLFLATEXACNT,Degiro Cash Sweep Transfer,,EUR,"21,70",EUR,"45,53", -11-07-2023,09:33,11-07-2023,VANECK GLOBAL REAL ESTATE UCITS ETF,NL0009690239,DEGIRO Transactiekosten en/of kosten van derden,,EUR,"-1,00",EUR,"23,83",67e39ca1-2f10-4f82-8365-1baad98c398f -11-07-2023,09:33,11-07-2023,VANECK GLOBAL REAL ESTATE UCITS ETF,NL0009690239,"Koop 29 @ 34,375 EUR",,EUR,"-996,88",EUR,"24,83",67e39ca1-2f10-4f82-8365-1baad98c398f -11-07-2023,08:51,11-07-2023,,,Reservation iDEAL / Sofort Deposit,,EUR,"1000,00",EUR,"1021,70", -10-07-2023,17:34,10-07-2023,,,"Overboeking van uw geldrekening bij flatexDEGIRO Bank 78,3 EUR",,,,EUR,"21,70", -10-07-2023,17:34,10-07-2023,FLATEX EURO BANKACCOUNT,NLFLATEXACNT,Degiro Cash Sweep Transfer,,EUR,"78,30",EUR,"100,00", -07-07-2023,05:01,06-07-2023,,,iDEAL Deposit,,EUR,"100,00",EUR,"21,70", -07-07-2023,05:01,06-07-2023,,,Reservation iDEAL / Sofort Deposit,,EUR,"-100,00",EUR,"-78,30", -06-07-2023,09:39,06-07-2023,VANGUARD S&P500,IE00B3XXRP09,DEGIRO Transactiekosten en/of kosten van derden,,EUR,"-1,00",EUR,"21,70",b7ab0494-1b46-4e2f-9bd2-f79e6c87cb5b -06-07-2023,09:39,06-07-2023,VANGUARD S&P500,IE00B3XXRP09,"Koop 1 @ 77,3 EUR",,EUR,"-77,30",EUR,"22,70",b7ab0494-1b46-4e2f-9bd2-f79e6c87cb5b -06-07-2023,09:37,06-07-2023,,,Reservation iDEAL / Sofort Deposit,,EUR,"100,00",EUR,"100,00", +13-07-2023,11:49,12-07-2023,,,"Overboeking van uw geldrekening bij flatexDEGIRO Bank 976,17 EUR",,,,EUR,23.83, +13-07-2023,11:49,12-07-2023,FLATEX EURO BANKACCOUNT,NLFLATEXACNT,Degiro Cash Sweep Transfer,,EUR,976.17,EUR,1000.00, +12-07-2023,04:51,11-07-2023,,,iDEAL Deposit,,EUR,1000.00,EUR,23.83, +12-07-2023,04:51,11-07-2023,,,Reservation iDEAL / Sofort Deposit,,EUR,-1000.00,EUR,-976.17, +11-07-2023,16:34,11-07-2023,,,"Overboeking van uw geldrekening bij flatexDEGIRO Bank 21,7 EUR",,,,EUR,23.83, +11-07-2023,16:34,11-07-2023,FLATEX EURO BANKACCOUNT,NLFLATEXACNT,Degiro Cash Sweep Transfer,,EUR,21.70,EUR,45.53, +11-07-2023,09:33,11-07-2023,VANECK GLOBAL REAL ESTATE UCITS ETF,NL0009690239,DEGIRO Transactiekosten en/of kosten van derden,,EUR,-1.00,EUR,23.83,67e39ca1-2f10-4f82-8365-1baad98c398f +11-07-2023,09:33,11-07-2023,VANECK GLOBAL REAL ESTATE UCITS ETF,NL0009690239,"Koop 29 @ 34,375 EUR",,EUR,-996.88,EUR,24.83,67e39ca1-2f10-4f82-8365-1baad98c398f +11-07-2023,08:51,11-07-2023,,,Reservation iDEAL / Sofort Deposit,,EUR,1000.00,EUR,1021.70, +10-07-2023,17:34,10-07-2023,,,"Overboeking van uw geldrekening bij flatexDEGIRO Bank 78,3 EUR",,,,EUR,21.70, +10-07-2023,17:34,10-07-2023,FLATEX EURO BANKACCOUNT,NLFLATEXACNT,Degiro Cash Sweep Transfer,,EUR,78.30,EUR,100.00, +07-07-2023,05:01,06-07-2023,,,iDEAL Deposit,,EUR,100.00,EUR,21.70, +07-07-2023,05:01,06-07-2023,,,Reservation iDEAL / Sofort Deposit,,EUR,-100.00,EUR,-78.30, +06-07-2023,09:39,06-07-2023,VANGUARD S&P500,IE00B3XXRP09,DEGIRO Transactiekosten en/of kosten van derden,,EUR,-1.00,EUR,21.70,b7ab0494-1b46-4e2f-9bd2-f79e6c87cb5b +06-07-2023,09:39,06-07-2023,VANGUARD S&P500,IE00B3XXRP09,"Koop 1 @ 77,3 EUR",,EUR,-77.30,EUR,22.70,b7ab0494-1b46-4e2f-9bd2-f79e6c87cb5b +06-07-2023,09:37,06-07-2023,,,Reservation iDEAL / Sofort Deposit,,EUR,100.00,EUR,100.00, +02-07-2023,16:41,30-06-2023,,,Flatex Interest Income,,EUR,0.00,EUR,0.00, diff --git a/GhostfolioSidekick.UnitTests/FileImporter/TestFiles/DeGiro/Example4/TestFileDividend.csv b/GhostfolioSidekick.UnitTests/FileImporter/TestFiles/DeGiro/Example4/TestFileDividend.csv new file mode 100644 index 00000000..bb014348 --- /dev/null +++ b/GhostfolioSidekick.UnitTests/FileImporter/TestFiles/DeGiro/Example4/TestFileDividend.csv @@ -0,0 +1,3 @@ +Datum,Tijd,Valutadatum,Product,ISIN,Omschrijving,FX,Mutatie,,Saldo,,Order Id +14-09-2023,06:32,13-09-2023,VANECK GLOBAL REAL ESTATE UCITS ETF,NL0009690239,Dividend,,EUR,9.57,EUR,33.96, +14-09-2023,06:32,13-09-2023,VANECK GLOBAL REAL ESTATE UCITS ETF,NL0009690239,Dividendbelasting,,EUR,-1.44,EUR,24.39, diff --git a/GhostfolioSidekick.UnitTests/GhostfolioSidekick.UnitTests.csproj b/GhostfolioSidekick.UnitTests/GhostfolioSidekick.UnitTests.csproj index b10acff8..28abcac7 100644 --- a/GhostfolioSidekick.UnitTests/GhostfolioSidekick.UnitTests.csproj +++ b/GhostfolioSidekick.UnitTests/GhostfolioSidekick.UnitTests.csproj @@ -44,6 +44,9 @@ Always + + Always + Always diff --git a/GhostfolioSidekick/FileImporter/DeGiro/DeGiroParser.cs b/GhostfolioSidekick/FileImporter/DeGiro/DeGiroParser.cs index 46625abb..0bd850fc 100644 --- a/GhostfolioSidekick/FileImporter/DeGiro/DeGiroParser.cs +++ b/GhostfolioSidekick/FileImporter/DeGiro/DeGiroParser.cs @@ -22,6 +22,11 @@ protected override async Task> ConvertOrders(DeGiroRecord rec var asset = await api.FindSymbolByISIN(record.ISIN); var fee = GetFee(record, allRecords); + if (string.IsNullOrWhiteSpace(record.OrderId)) + { + record.OrderId = $"{orderType}_{record.Datum}_{record.Tijd}_{record.ISIN}"; + } + var order = new Order { AccountId = account.Id, @@ -31,9 +36,9 @@ protected override async Task> ConvertOrders(DeGiroRecord rec Comment = $"Transaction Reference: [{record.OrderId}]", Fee = Math.Abs(fee?.Item1 ?? 0), FeeCurrency = fee?.Item2 ?? record.Mutatie, - Quantity = GetQuantity(record), + Quantity = GetQuantity(orderType, record), Type = orderType.Value, - UnitPrice = GetUnitPrice(record), + UnitPrice = GetUnitPrice(orderType, record), ReferenceCode = record.OrderId, }; @@ -47,18 +52,27 @@ protected override CsvConfiguration GetConfig() HasHeaderRecord = true, CacheFields = true, Delimiter = ",", + }; } private Tuple? GetFee(DeGiroRecord record, IEnumerable allRecords) { - var feeRecord = allRecords.SingleOrDefault(x => x.OrderId == record.OrderId && x != record); - if (feeRecord == null) + // Costs of stocks + var feeRecord = allRecords.SingleOrDefault(x => !string.IsNullOrWhiteSpace(x.OrderId) && x.OrderId == record.OrderId && x != record); + if (feeRecord != null) { - return null; + return Tuple.Create(feeRecord.Total, feeRecord.Mutatie); } - return Tuple.Create(feeRecord.Total, feeRecord.Mutatie); + // Taxes of dividends + feeRecord = allRecords.SingleOrDefault(x => x.Datum == record.Datum && x.ISIN == record.ISIN && x.Omschrijving == "Dividendbelasting"); + if (feeRecord != null) + { + return Tuple.Create(feeRecord.Total, feeRecord.Mutatie); + } + + return null; } private OrderType? GetOrderType(DeGiroRecord record) @@ -68,18 +82,33 @@ protected override CsvConfiguration GetConfig() return OrderType.BUY; } + if (record.Omschrijving.Equals("Dividend")) + { + return OrderType.DIVIDEND; + } + // TODO, implement other options return null; } - private decimal GetQuantity(DeGiroRecord record) + private decimal GetQuantity(OrderType? orderType, DeGiroRecord record) { + if (orderType == OrderType.DIVIDEND) + { + return 1; + } + var quantity = Regex.Match(record.Omschrijving, $"Koop (?\\d+) @ (?.*) EUR").Groups[1].Value; return decimal.Parse(quantity, GetCultureForParsingNumbers()); } - private decimal GetUnitPrice(DeGiroRecord record) + private decimal GetUnitPrice(OrderType? orderType, DeGiroRecord record) { + if (orderType == OrderType.DIVIDEND) + { + return record.Total.GetValueOrDefault(); + } + var quantity = Regex.Match(record.Omschrijving, $"Koop (?\\d+) @ (?.*) EUR").Groups[2].Value; return decimal.Parse(quantity, GetCultureForParsingNumbers()); } diff --git a/GhostfolioSidekick/FileImporter/DeGiro/DeGiroRecord.cs b/GhostfolioSidekick/FileImporter/DeGiro/DeGiroRecord.cs index 488a3cee..9ba43362 100644 --- a/GhostfolioSidekick/FileImporter/DeGiro/DeGiroRecord.cs +++ b/GhostfolioSidekick/FileImporter/DeGiro/DeGiroRecord.cs @@ -1,5 +1,4 @@ using CsvHelper.Configuration.Attributes; -using System.Globalization; namespace GhostfolioSidekick.FileImporter.DeGiro { @@ -25,7 +24,7 @@ public class DeGiroRecord public string Mutatie { get; set; } [Index(8)] - [CultureInfo("nl-NL")] + //[CultureInfo("en-EN")] public decimal? Total { get; set; } public string Saldo { get; set; }