Skip to content

Commit

Permalink
Added PDF Extractor for Info Reports from BISON app
Browse files Browse the repository at this point in the history
* Matches crypto currencies based on ticker or name
* Creates new crypto currencies with CoinGecko quote feed
  • Loading branch information
buchen committed Aug 1, 2023
1 parent e30de80 commit db309a4
Show file tree
Hide file tree
Showing 9 changed files with 822 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import name.abuchen.portfolio.model.PortfolioTransaction;
import name.abuchen.portfolio.model.PortfolioTransferEntry;
import name.abuchen.portfolio.model.Security;
import name.abuchen.portfolio.model.SecurityProperty;
import name.abuchen.portfolio.model.Transaction;
import name.abuchen.portfolio.model.Transaction.Unit;
import name.abuchen.portfolio.model.Transaction.Unit.Type;
Expand Down Expand Up @@ -409,4 +410,16 @@ public static Matcher<Security> hasCurrencyCode(String currencyCode)
Security::getCurrencyCode);
}

public static Matcher<Security> hasFeed(String quoteFeed)
{
return new PropertyMatcher<>("quoteFeed", quoteFeed, //$NON-NLS-1$
Security::getFeed);
}

public static Matcher<Security> hasFeedProperty(String name, String value)
{
return new PropertyMatcher<Security, String>("feedProperty " + name, value, //$NON-NLS-1$
s -> s.getPropertyValue(SecurityProperty.Type.FEED, name).orElse(null));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package name.abuchen.portfolio.datatransfer.pdf.bison;

import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.deposit;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasAmount;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasCurrencyCode;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasDate;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasFeed;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasFeedProperty;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasFees;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasGrossValue;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasName;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasNote;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasShares;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasSource;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasTaxes;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasTicker;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.inboundDelivery;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.purchase;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.removal;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.sale;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.security;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsEmptyCollection.empty;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import name.abuchen.portfolio.datatransfer.Extractor.Item;
import name.abuchen.portfolio.datatransfer.actions.AssertImportActions;
import name.abuchen.portfolio.datatransfer.pdf.BisonPDFExtractor;
import name.abuchen.portfolio.datatransfer.pdf.PDFInputFile;
import name.abuchen.portfolio.model.Client;
import name.abuchen.portfolio.online.impl.CoinGeckoQuoteFeed;

@SuppressWarnings("nls")
public class BisonPDFExtractorTest
{
BisonPDFExtractor extractor = new BisonPDFExtractor(new Client())
{
@Override
protected CoinGeckoQuoteFeed lookupFeed()
{
// mock the list of coins to avoid remote call
return new CoinGeckoQuoteFeed()
{
@Override
public synchronized List<Coin> getCoins() throws IOException
{
return List.of(new Coin("bitcoin", "BTC", "Bitcoin"), new Coin("ethereum", "ETH", "Ethereum"));
}
};
}
};

@Test
public void testInfoReport01()
{
List<Exception> errors = new ArrayList<>();

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

if (!errors.isEmpty())
errors.get(0).printStackTrace();

assertThat(errors, empty());
// 13 transactions + 2 securities
assertThat(results.size(), is(15));
new AssertImportActions().check(results, "EUR");

// check crypto currencies
assertThat(results,
hasItem(security(hasTicker("BTC"), hasName("Bitcoin"), hasCurrencyCode("EUR"),
hasFeed(CoinGeckoQuoteFeed.ID),
hasFeedProperty(CoinGeckoQuoteFeed.COINGECKO_COIN_ID, "bitcoin"))));
assertThat(results,
hasItem(security(hasTicker("ETH"), hasName("Ethereum"), hasCurrencyCode("EUR"),
hasFeed(CoinGeckoQuoteFeed.ID),
hasFeedProperty(CoinGeckoQuoteFeed.COINGECKO_COIN_ID, "ethereum"))));

// check transactions
assertThat(results, hasItem(purchase( //
hasDate("2021-12-28T09:00"), hasShares(0.00028505), //
hasSource("InfoReport01.txt"), hasNote(null), //
hasAmount("EUR", 12.50), hasGrossValue("EUR", 12.50), //
hasTaxes("EUR", 0), hasFees("EUR", 0))));

assertThat(results, hasItem(purchase( //
hasDate("2021-11-30T09:01"), hasShares(0.00318593), //
hasSource("InfoReport01.txt"), hasNote(null), //
hasAmount("EUR", 12.50), hasGrossValue("EUR", 12.50), //
hasTaxes("EUR", 0), hasFees("EUR", 0))));

assertThat(results, hasItem(deposit( //
hasDate("2021-11-29T08:49"), //
hasSource("InfoReport01.txt"), hasNote(null), //
hasAmount("EUR", 250), hasGrossValue("EUR", 250), //
hasTaxes("EUR", 0), hasFees("EUR", 0))));
}

@Test
public void testInfoReport02()
{
List<Exception> errors = new ArrayList<>();

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

if (!errors.isEmpty())
errors.get(0).printStackTrace();

assertThat(errors, empty());
// 2 transactions + 2 securities
assertThat(results.size(), is(4));
new AssertImportActions().check(results, "EUR");

// check crypto currencies
assertThat(results,
hasItem(security(hasTicker("BTC"), hasName("Bitcoin"), hasCurrencyCode("EUR"),
hasFeed(CoinGeckoQuoteFeed.ID),
hasFeedProperty(CoinGeckoQuoteFeed.COINGECKO_COIN_ID, "bitcoin"))));
assertThat(results,
hasItem(security(hasTicker("ETH"), hasName("Ethereum"), hasCurrencyCode("EUR"),
hasFeed(CoinGeckoQuoteFeed.ID),
hasFeedProperty(CoinGeckoQuoteFeed.COINGECKO_COIN_ID, "ethereum"))));

// check transactions
assertThat(results, hasItem(purchase( //
hasDate("2022-06-12T21:19"), hasShares(0.00180296), //
hasSource("InfoReport02.txt"), hasNote(null), //
hasAmount("EUR", 47.62), hasGrossValue("EUR", 47.62), //
hasTaxes("EUR", 0), hasFees("EUR", 0))));

assertThat(results, hasItem(sale( //
hasDate("2022-06-12T21:19"), hasShares(0.03396843), //
hasSource("InfoReport02.txt"), hasNote(null), //
hasAmount("EUR", 47.62), hasGrossValue("EUR", 47.62), //
hasTaxes("EUR", 0), hasFees("EUR", 0))));
}

@Test
public void testInfoReport03()
{
List<Exception> errors = new ArrayList<>();

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

if (!errors.isEmpty())
errors.get(0).printStackTrace();

assertThat(errors, empty());
// 5 transactions + 1 security
assertThat(results.size(), is(6));
new AssertImportActions().check(results, "EUR");

// check crypto currencies
assertThat(results,
hasItem(security(hasTicker("BTC"), hasName("Bitcoin"), hasCurrencyCode("EUR"),
hasFeed(CoinGeckoQuoteFeed.ID),
hasFeedProperty(CoinGeckoQuoteFeed.COINGECKO_COIN_ID, "bitcoin"))));

// check transactions

assertThat(results, hasItem(removal( //
hasDate("2020-11-22T09:51"), //
hasSource("InfoReport03.txt"), hasNote(null), //
hasAmount("EUR", 100), hasGrossValue("EUR", 100), //
hasTaxes("EUR", 0), hasFees("EUR", 0))));

assertThat(results, hasItem(sale( //
hasDate("2020-11-21T19:45"), hasShares(0.00636567), //
hasSource("InfoReport03.txt"), hasNote(null), //
hasAmount("EUR", 100), hasGrossValue("EUR", 100), //
hasTaxes("EUR", 0), hasFees("EUR", 0))));

assertThat(results, hasItem(inboundDelivery( //
hasDate("2020-01-16T11:19"), hasShares(0.00130130), //
hasSource("InfoReport03.txt"), hasNote(null), //
hasAmount("EUR", 10), hasGrossValue("EUR", 10), //
hasTaxes("EUR", 0), hasFees("EUR", 0))));

assertThat(results, hasItem(purchase( //
hasDate("2020-01-16T11:19"), hasShares(0.01282436), //
hasSource("InfoReport03.txt"), hasNote(null), //
hasAmount("EUR", 100), hasGrossValue("EUR", 100), //
hasTaxes("EUR", 0), hasFees("EUR", 0))));

assertThat(results, hasItem(deposit( //
hasDate("2020-01-16T10:30"), //
hasSource("InfoReport03.txt"), hasNote(null), //
hasAmount("EUR", 100), hasGrossValue("EUR", 100), //
hasTaxes("EUR", 0), hasFees("EUR", 0))));

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
PDFBox Version: 1.8.17
Portfolio Performance Version: 0.62.0
-----------------------------------------
EUWAX Aktiengesellschaft | Börsenstraße 4 | 70174 Stuttgart EUWAX Aktiengesellschaft
Börsenstraße 4
Max Mustermann 70174 Stuttgart
Strasse Tel.: +49 711 222 985 0
7 E-Mail: [email protected]
99999 Stadt
BISON Info-Report für das Steuerjahr 2021
Mit dem Info-Report stellt dir BISON deine Transaktionen übersichtlich und detailliert dar und
stellt dir eine unverbindliche Berechnung über die realisierten Gewinne und Verluste bei
BISON nach dem FiFo-Verfahren (First-In First-Out) zu Verfügung.
Unverbindliche Berechnung des realisierten Gewinns/Verlusts nach FiFo aus Transaktionen
bei BISON im Jahr 2021 mit einer Haltedauer unter einem Jahr:
0,00 €
Bitte beachte, dass bei dieser Berechnung Kryptowährungen, bei denen du eine Krypto Ein-
oder Auszahlung getätigt hast, nicht in die Berechnung der realisierten Gewinne und Verluste
eingehen. Transaktionen für diese Kryptowährungen werden in Abschnitt 1 und 2 grau
hinterlegt.
Diese Information stellt keine steuerliche oder rechtliche Beratung dar. Bei Fragen zu deiner
Einkommensteuererklärung oder sonstigen Angelegenheiten wende dich bitte an deinen
Steuerberater.
Mit freundlichen Grüßen
BISON
Anhang:
1. Info-Report für das Steuerjahr 2021
2. Export deiner Transaktionsübersicht für das Steuerjahr 2021
3. Export deiner Krypto-Einzahlungen
4. Export deiner Krypto-Auszahlungen
EUWAX +49 711 222 985 0 Sitz der Gesellschaft: Vorstand: Michael Jaeggi, Dr. Manfred Pumbo, Dragan
Aktiengesellschaft [email protected] Stuttgart Radanovic
Börsenstraße 4 www.bisonapp.com Registergericht: Aufsichtsratsvorsitzender: Dr. Christian Ricken
70174 Stuttgart Amtsgericht
Stuttgart, HRB 19972
USt-ID: DE 175042226
1. Info-Report für das Steuerjahr 2021
Bei BISON werden realisierte Gewinne und Verluste nach dem FiFo-Verfahren (First-In First-
Out) errechnet. Gewinne und Verluste mit über einem Jahr Haltedauer werden nicht
berücksichtigt.
Bitte beachte, dass bei dieser Berechnung Kryptowährungen, bei denen du eine Krypto Ein-
oder Auszahlung getätigt hast, nicht in die Berechnung der realisierten Gewinne und Verluste
eingehen. Transaktionen für diese Kryptowährungen werden in Abschnitt 1 und 2 grau
hinterlegt. Zusätzlich werden deine individuellen Krypto Ein- und Auszahlungen in Abschnitt 3
und 4 gesondert aufgelistet.
Ethereum (ETH)
Summe realisierte Gewinne/Verluste 2021 0,00 €
Davon Haltedauer > 1 Jahr 0,00 €
Davon Haltedauer ≤ 1 Jahr 0,00 €
Bitcoin (BTC)
Summe realisierte Gewinne/Verluste 2021 0,00 €
Davon Haltedauer > 1 Jahr 0,00 €
Davon Haltedauer ≤ 1 Jahr 0,00 €
Gesamtsumme realisierte Gewinne/Verluste 2021 0,00 €
Davon Haltedauer > 1 Jahr 0,00 €
Davon Haltedauer ≤ 1 Jahr 0,00 €
Unverbindliche Berechnung des realisierten Gewinns/Verlusts
aus Transaktionen bei BISON im Jahr 2021 mit einer 0,00 €
Haltedauer unter einem Jahr
EUWAX +49 711 222 985 0 Sitz der Gesellschaft: Vorstand: Michael Jaeggi, Dr. Manfred Pumbo, Dragan
Aktiengesellschaft [email protected] Stuttgart Radanovic
Börsenstraße 4 www.bisonapp.com Registergericht: Aufsichtsratsvorsitzender: Dr. Christian Ricken
70174 Stuttgart Amtsgericht
Stuttgart, HRB 19972
USt-ID: DE 175042226
2. Export deiner Transaktionsübersicht für das Steuerjahr 2021
* Im Info-Report berücksichtigte Transaktionen für die unverbindliche Berechnung des
realisierten Gewinns/Verlustes aus Transaktionen bei BISON im Jahr 2021 mit einer
Haltedauer unter einem Jahr.
Kauf BTC 0,00028505
28.12.2021 09:00 43.850,74 €/BTC - 12,50 €
Kauf ETH 0,00358227
28.12.2021 09:00 3.489,40 €/ETH - 12,50 €
Kauf BTC 0,00028798
21.12.2021 09:00 43.405,55 €/BTC - 12,50 €
Kauf ETH 0,00349735
21.12.2021 09:00 3.574,12 €/ETH - 12,50 €
Kauf BTC 0,00029696
14.12.2021 09:00 42.092,88 €/BTC - 12,50 €
Kauf ETH 0,00368944
14.12.2021 09:00 3.388,05 €/ETH - 12,50 €
Kauf BTC 0,00027461
07.12.2021 09:00 45.518,91 €/BTC - 12,50 €
Kauf ETH 0,00321379
07.12.2021 09:00 3.889,48 €/ETH - 12,50 €
Kauf BTC 0,00029264
04.12.2021 09:27 42.714,59 €/BTC - 12,50 €
Kauf ETH 0,00354180
04.12.2021 09:26 3.529,28 €/ETH - 12,50 €
Kauf BTC 0,00024862
30.11.2021 09:01 50.276,06 €/BTC - 12,50 €
Kauf ETH 0,00318593
30.11.2021 09:01 3.923,49 €/ETH - 12,50 €
EUWAX +49 711 222 985 0 Sitz der Gesellschaft: Vorstand: Michael Jaeggi, Dr. Manfred Pumbo, Dragan
Aktiengesellschaft [email protected] Stuttgart Radanovic
Börsenstraße 4 www.bisonapp.com Registergericht: Aufsichtsratsvorsitzender: Dr. Christian Ricken
70174 Stuttgart Amtsgericht
Stuttgart, HRB 19972
USt-ID: DE 175042226
Einzahlung
29.11.2021 08:49 + 250,00 €
EUWAX +49 711 222 985 0 Sitz der Gesellschaft: Vorstand: Michael Jaeggi, Dr. Manfred Pumbo, Dragan
Aktiengesellschaft [email protected] Stuttgart Radanovic
Börsenstraße 4 www.bisonapp.com Registergericht: Aufsichtsratsvorsitzender: Dr. Christian Ricken
70174 Stuttgart Amtsgericht
Stuttgart, HRB 19972
USt-ID: DE 175042226
3. Export deiner Krypto-Einzahlungen
Kryptowährungen, bei denen du eine Krypto-Einzahlung getätigt hast, gehen nicht in die
Berechnung der realisierten Gewinne und Verluste ein, da BISON keine Informationen über
das ursprüngliche Kaufdatum bzw. den Kaufpreis einer Krypto-Einzahlung hat. Eine
Zuordnung nach FiFo-Verfahren (First-In First-Out) ist nicht möglich.
EUWAX +49 711 222 985 0 Sitz der Gesellschaft: Vorstand: Michael Jaeggi, Dr. Manfred Pumbo, Dragan
Aktiengesellschaft [email protected] Stuttgart Radanovic
Börsenstraße 4 www.bisonapp.com Registergericht: Aufsichtsratsvorsitzender: Dr. Christian Ricken
70174 Stuttgart Amtsgericht
Stuttgart, HRB 19972
USt-ID: DE 175042226
4. Export deiner Krypto-Auszahlungen
Kryptowährungen, bei denen du eine Krypto-Auszahlung getätigt hast, gehen nicht in die
Berechnung der realisierten Gewinne und Verluste ein, da diese nicht über BISON verkauft
wurden. Eine Zuordnung nach FiFo-Verfahren (First-In First-Out) ist nicht möglich.
EUWAX +49 711 222 985 0 Sitz der Gesellschaft: Vorstand: Michael Jaeggi, Dr. Manfred Pumbo, Dragan
Aktiengesellschaft [email protected] Stuttgart Radanovic
Börsenstraße 4 www.bisonapp.com Registergericht: Aufsichtsratsvorsitzender: Dr. Christian Ricken
70174 Stuttgart Amtsgericht
Stuttgart, HRB 19972
USt-ID: DE 175042226
Hinweise zum Info-Report
Für Deutschland gilt:
Der Tausch oder Ru ̈cktausch von Kryptowa ̈hrung, die vom Verka ̈ufer nicht selbst generiert wurden, in Euro oder eine andere Kryptowa ̈hrung fu ̈hren zu einem
privaten Veraü ßerungsgescha ̈ft i. S. des § 23 Abs. 1 Satz 1 Nr. 2 EStG. Das gilt jedoch nur, wenn zwischen Erwerb und Vera ̈ußerung nicht mehr als ein Jahr liegt
(Spekulationsfrist) und die ja ̈hrliche Freigrenze in Ho ̈he von 600 EUR u ̈berschritten wurde. Diese Freigrenze gilt allerdings fur̈ den Gesamtgewinn aus sa ̈mtlichen
privaten Vera ̈ußerungsgescha ̈ften innerhalb eines Jahres. Verluste dur̈ fen nur bis zu Ho ̈he des Gewinns, der im gleichen Jahr aus privaten Vera ̈ußerungsgeschaf̈ ten
erzielt wurde, verrechnet werden. Ein Verlustru ̈cktrag bzw. - vortrag ist grundsa ̈tzlich mo ̈glich.
Der Gewinn aus der Vera ̈ußerung von Kryptowa ̈hrung ergibt sich als DiNerenz zwischen den AnschaNungskosten und dem Vera ̈ußerungspreis. Hierbei wird die Fifo-
Methode angewandt (§ 23 Abs. 1 Nr. 2 Satz 3 EStG), wonach unterstellt wird, dass die zuerst angeschaNten Besta ̈nde zuerst vera ̈ußert wurden. Realisierte Gewinne
und Verluste sind in der Regel in der Anlage SO bei der Steuererkla ̈rung anzugeben.
Diese Information stellt keine steuerliche oder rechtliche Beratung dar. Bei Fragen zu deiner Einkommensteuererkla ̈rung oder sonstigen Angelegenheiten wende dich
bitte an deinen Steuerberater.
Diese Bescheinigung ist maschinell erstellt und wird nicht unterschrieben.
EUWAX +49 711 222 985 0 Sitz der Gesellschaft: Vorstand: Michael Jaeggi, Dr. Manfred Pumbo, Dragan
Aktiengesellschaft [email protected] Stuttgart Radanovic
Börsenstraße 4 www.bisonapp.com Registergericht: Aufsichtsratsvorsitzender: Dr. Christian Ricken
70174 Stuttgart Amtsgericht
Stuttgart, HRB 19972
USt-ID: DE 175042226
Loading

0 comments on commit db309a4

Please sign in to comment.