Skip to content

Commit db309a4

Browse files
committed
Added PDF Extractor for Info Reports from BISON app
* Matches crypto currencies based on ticker or name * Creates new crypto currencies with CoinGecko quote feed
1 parent e30de80 commit db309a4

File tree

9 files changed

+822
-5
lines changed

9 files changed

+822
-5
lines changed

name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/ExtractorMatchers.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import name.abuchen.portfolio.model.PortfolioTransaction;
2222
import name.abuchen.portfolio.model.PortfolioTransferEntry;
2323
import name.abuchen.portfolio.model.Security;
24+
import name.abuchen.portfolio.model.SecurityProperty;
2425
import name.abuchen.portfolio.model.Transaction;
2526
import name.abuchen.portfolio.model.Transaction.Unit;
2627
import name.abuchen.portfolio.model.Transaction.Unit.Type;
@@ -409,4 +410,16 @@ public static Matcher<Security> hasCurrencyCode(String currencyCode)
409410
Security::getCurrencyCode);
410411
}
411412

413+
public static Matcher<Security> hasFeed(String quoteFeed)
414+
{
415+
return new PropertyMatcher<>("quoteFeed", quoteFeed, //$NON-NLS-1$
416+
Security::getFeed);
417+
}
418+
419+
public static Matcher<Security> hasFeedProperty(String name, String value)
420+
{
421+
return new PropertyMatcher<Security, String>("feedProperty " + name, value, //$NON-NLS-1$
422+
s -> s.getPropertyValue(SecurityProperty.Type.FEED, name).orElse(null));
423+
}
424+
412425
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package name.abuchen.portfolio.datatransfer.pdf.bison;
2+
3+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.deposit;
4+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasAmount;
5+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasCurrencyCode;
6+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasDate;
7+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasFeed;
8+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasFeedProperty;
9+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasFees;
10+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasGrossValue;
11+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasName;
12+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasNote;
13+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasShares;
14+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasSource;
15+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasTaxes;
16+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasTicker;
17+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.inboundDelivery;
18+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.purchase;
19+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.removal;
20+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.sale;
21+
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.security;
22+
import static org.hamcrest.CoreMatchers.hasItem;
23+
import static org.hamcrest.CoreMatchers.is;
24+
import static org.hamcrest.MatcherAssert.assertThat;
25+
import static org.hamcrest.collection.IsEmptyCollection.empty;
26+
27+
import java.io.IOException;
28+
import java.util.ArrayList;
29+
import java.util.List;
30+
31+
import org.junit.Test;
32+
33+
import name.abuchen.portfolio.datatransfer.Extractor.Item;
34+
import name.abuchen.portfolio.datatransfer.actions.AssertImportActions;
35+
import name.abuchen.portfolio.datatransfer.pdf.BisonPDFExtractor;
36+
import name.abuchen.portfolio.datatransfer.pdf.PDFInputFile;
37+
import name.abuchen.portfolio.model.Client;
38+
import name.abuchen.portfolio.online.impl.CoinGeckoQuoteFeed;
39+
40+
@SuppressWarnings("nls")
41+
public class BisonPDFExtractorTest
42+
{
43+
BisonPDFExtractor extractor = new BisonPDFExtractor(new Client())
44+
{
45+
@Override
46+
protected CoinGeckoQuoteFeed lookupFeed()
47+
{
48+
// mock the list of coins to avoid remote call
49+
return new CoinGeckoQuoteFeed()
50+
{
51+
@Override
52+
public synchronized List<Coin> getCoins() throws IOException
53+
{
54+
return List.of(new Coin("bitcoin", "BTC", "Bitcoin"), new Coin("ethereum", "ETH", "Ethereum"));
55+
}
56+
};
57+
}
58+
};
59+
60+
@Test
61+
public void testInfoReport01()
62+
{
63+
List<Exception> errors = new ArrayList<>();
64+
65+
List<Item> results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "InfoReport01.txt"), errors);
66+
67+
if (!errors.isEmpty())
68+
errors.get(0).printStackTrace();
69+
70+
assertThat(errors, empty());
71+
// 13 transactions + 2 securities
72+
assertThat(results.size(), is(15));
73+
new AssertImportActions().check(results, "EUR");
74+
75+
// check crypto currencies
76+
assertThat(results,
77+
hasItem(security(hasTicker("BTC"), hasName("Bitcoin"), hasCurrencyCode("EUR"),
78+
hasFeed(CoinGeckoQuoteFeed.ID),
79+
hasFeedProperty(CoinGeckoQuoteFeed.COINGECKO_COIN_ID, "bitcoin"))));
80+
assertThat(results,
81+
hasItem(security(hasTicker("ETH"), hasName("Ethereum"), hasCurrencyCode("EUR"),
82+
hasFeed(CoinGeckoQuoteFeed.ID),
83+
hasFeedProperty(CoinGeckoQuoteFeed.COINGECKO_COIN_ID, "ethereum"))));
84+
85+
// check transactions
86+
assertThat(results, hasItem(purchase( //
87+
hasDate("2021-12-28T09:00"), hasShares(0.00028505), //
88+
hasSource("InfoReport01.txt"), hasNote(null), //
89+
hasAmount("EUR", 12.50), hasGrossValue("EUR", 12.50), //
90+
hasTaxes("EUR", 0), hasFees("EUR", 0))));
91+
92+
assertThat(results, hasItem(purchase( //
93+
hasDate("2021-11-30T09:01"), hasShares(0.00318593), //
94+
hasSource("InfoReport01.txt"), hasNote(null), //
95+
hasAmount("EUR", 12.50), hasGrossValue("EUR", 12.50), //
96+
hasTaxes("EUR", 0), hasFees("EUR", 0))));
97+
98+
assertThat(results, hasItem(deposit( //
99+
hasDate("2021-11-29T08:49"), //
100+
hasSource("InfoReport01.txt"), hasNote(null), //
101+
hasAmount("EUR", 250), hasGrossValue("EUR", 250), //
102+
hasTaxes("EUR", 0), hasFees("EUR", 0))));
103+
}
104+
105+
@Test
106+
public void testInfoReport02()
107+
{
108+
List<Exception> errors = new ArrayList<>();
109+
110+
List<Item> results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "InfoReport02.txt"), errors);
111+
112+
if (!errors.isEmpty())
113+
errors.get(0).printStackTrace();
114+
115+
assertThat(errors, empty());
116+
// 2 transactions + 2 securities
117+
assertThat(results.size(), is(4));
118+
new AssertImportActions().check(results, "EUR");
119+
120+
// check crypto currencies
121+
assertThat(results,
122+
hasItem(security(hasTicker("BTC"), hasName("Bitcoin"), hasCurrencyCode("EUR"),
123+
hasFeed(CoinGeckoQuoteFeed.ID),
124+
hasFeedProperty(CoinGeckoQuoteFeed.COINGECKO_COIN_ID, "bitcoin"))));
125+
assertThat(results,
126+
hasItem(security(hasTicker("ETH"), hasName("Ethereum"), hasCurrencyCode("EUR"),
127+
hasFeed(CoinGeckoQuoteFeed.ID),
128+
hasFeedProperty(CoinGeckoQuoteFeed.COINGECKO_COIN_ID, "ethereum"))));
129+
130+
// check transactions
131+
assertThat(results, hasItem(purchase( //
132+
hasDate("2022-06-12T21:19"), hasShares(0.00180296), //
133+
hasSource("InfoReport02.txt"), hasNote(null), //
134+
hasAmount("EUR", 47.62), hasGrossValue("EUR", 47.62), //
135+
hasTaxes("EUR", 0), hasFees("EUR", 0))));
136+
137+
assertThat(results, hasItem(sale( //
138+
hasDate("2022-06-12T21:19"), hasShares(0.03396843), //
139+
hasSource("InfoReport02.txt"), hasNote(null), //
140+
hasAmount("EUR", 47.62), hasGrossValue("EUR", 47.62), //
141+
hasTaxes("EUR", 0), hasFees("EUR", 0))));
142+
}
143+
144+
@Test
145+
public void testInfoReport03()
146+
{
147+
List<Exception> errors = new ArrayList<>();
148+
149+
List<Item> results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "InfoReport03.txt"), errors);
150+
151+
if (!errors.isEmpty())
152+
errors.get(0).printStackTrace();
153+
154+
assertThat(errors, empty());
155+
// 5 transactions + 1 security
156+
assertThat(results.size(), is(6));
157+
new AssertImportActions().check(results, "EUR");
158+
159+
// check crypto currencies
160+
assertThat(results,
161+
hasItem(security(hasTicker("BTC"), hasName("Bitcoin"), hasCurrencyCode("EUR"),
162+
hasFeed(CoinGeckoQuoteFeed.ID),
163+
hasFeedProperty(CoinGeckoQuoteFeed.COINGECKO_COIN_ID, "bitcoin"))));
164+
165+
// check transactions
166+
167+
assertThat(results, hasItem(removal( //
168+
hasDate("2020-11-22T09:51"), //
169+
hasSource("InfoReport03.txt"), hasNote(null), //
170+
hasAmount("EUR", 100), hasGrossValue("EUR", 100), //
171+
hasTaxes("EUR", 0), hasFees("EUR", 0))));
172+
173+
assertThat(results, hasItem(sale( //
174+
hasDate("2020-11-21T19:45"), hasShares(0.00636567), //
175+
hasSource("InfoReport03.txt"), hasNote(null), //
176+
hasAmount("EUR", 100), hasGrossValue("EUR", 100), //
177+
hasTaxes("EUR", 0), hasFees("EUR", 0))));
178+
179+
assertThat(results, hasItem(inboundDelivery( //
180+
hasDate("2020-01-16T11:19"), hasShares(0.00130130), //
181+
hasSource("InfoReport03.txt"), hasNote(null), //
182+
hasAmount("EUR", 10), hasGrossValue("EUR", 10), //
183+
hasTaxes("EUR", 0), hasFees("EUR", 0))));
184+
185+
assertThat(results, hasItem(purchase( //
186+
hasDate("2020-01-16T11:19"), hasShares(0.01282436), //
187+
hasSource("InfoReport03.txt"), hasNote(null), //
188+
hasAmount("EUR", 100), hasGrossValue("EUR", 100), //
189+
hasTaxes("EUR", 0), hasFees("EUR", 0))));
190+
191+
assertThat(results, hasItem(deposit( //
192+
hasDate("2020-01-16T10:30"), //
193+
hasSource("InfoReport03.txt"), hasNote(null), //
194+
hasAmount("EUR", 100), hasGrossValue("EUR", 100), //
195+
hasTaxes("EUR", 0), hasFees("EUR", 0))));
196+
197+
}
198+
199+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
PDFBox Version: 1.8.17
2+
Portfolio Performance Version: 0.62.0
3+
-----------------------------------------
4+
EUWAX Aktiengesellschaft | Börsenstraße 4 | 70174 Stuttgart EUWAX Aktiengesellschaft
5+
Börsenstraße 4
6+
Max Mustermann 70174 Stuttgart
7+
Strasse Tel.: +49 711 222 985 0
8+
9+
99999 Stadt
10+
BISON Info-Report für das Steuerjahr 2021
11+
Mit dem Info-Report stellt dir BISON deine Transaktionen übersichtlich und detailliert dar und
12+
stellt dir eine unverbindliche Berechnung über die realisierten Gewinne und Verluste bei
13+
BISON nach dem FiFo-Verfahren (First-In First-Out) zu Verfügung.
14+
Unverbindliche Berechnung des realisierten Gewinns/Verlusts nach FiFo aus Transaktionen
15+
bei BISON im Jahr 2021 mit einer Haltedauer unter einem Jahr:
16+
0,00 €
17+
Bitte beachte, dass bei dieser Berechnung Kryptowährungen, bei denen du eine Krypto Ein-
18+
oder Auszahlung getätigt hast, nicht in die Berechnung der realisierten Gewinne und Verluste
19+
eingehen. Transaktionen für diese Kryptowährungen werden in Abschnitt 1 und 2 grau
20+
hinterlegt.
21+
Diese Information stellt keine steuerliche oder rechtliche Beratung dar. Bei Fragen zu deiner
22+
Einkommensteuererklärung oder sonstigen Angelegenheiten wende dich bitte an deinen
23+
Steuerberater.
24+
Mit freundlichen Grüßen
25+
BISON
26+
Anhang:
27+
1. Info-Report für das Steuerjahr 2021
28+
2. Export deiner Transaktionsübersicht für das Steuerjahr 2021
29+
3. Export deiner Krypto-Einzahlungen
30+
4. Export deiner Krypto-Auszahlungen
31+
EUWAX +49 711 222 985 0 Sitz der Gesellschaft: Vorstand: Michael Jaeggi, Dr. Manfred Pumbo, Dragan
32+
Aktiengesellschaft [email protected] Stuttgart Radanovic
33+
Börsenstraße 4 www.bisonapp.com Registergericht: Aufsichtsratsvorsitzender: Dr. Christian Ricken
34+
70174 Stuttgart Amtsgericht
35+
Stuttgart, HRB 19972
36+
USt-ID: DE 175042226
37+
1. Info-Report für das Steuerjahr 2021
38+
Bei BISON werden realisierte Gewinne und Verluste nach dem FiFo-Verfahren (First-In First-
39+
Out) errechnet. Gewinne und Verluste mit über einem Jahr Haltedauer werden nicht
40+
berücksichtigt.
41+
Bitte beachte, dass bei dieser Berechnung Kryptowährungen, bei denen du eine Krypto Ein-
42+
oder Auszahlung getätigt hast, nicht in die Berechnung der realisierten Gewinne und Verluste
43+
eingehen. Transaktionen für diese Kryptowährungen werden in Abschnitt 1 und 2 grau
44+
hinterlegt. Zusätzlich werden deine individuellen Krypto Ein- und Auszahlungen in Abschnitt 3
45+
und 4 gesondert aufgelistet.
46+
Ethereum (ETH)
47+
Summe realisierte Gewinne/Verluste 2021 0,00 €
48+
Davon Haltedauer > 1 Jahr 0,00 €
49+
Davon Haltedauer ≤ 1 Jahr 0,00 €
50+
Bitcoin (BTC)
51+
Summe realisierte Gewinne/Verluste 2021 0,00 €
52+
Davon Haltedauer > 1 Jahr 0,00 €
53+
Davon Haltedauer ≤ 1 Jahr 0,00 €
54+
Gesamtsumme realisierte Gewinne/Verluste 2021 0,00 €
55+
Davon Haltedauer > 1 Jahr 0,00 €
56+
Davon Haltedauer ≤ 1 Jahr 0,00 €
57+
Unverbindliche Berechnung des realisierten Gewinns/Verlusts
58+
aus Transaktionen bei BISON im Jahr 2021 mit einer 0,00 €
59+
Haltedauer unter einem Jahr
60+
EUWAX +49 711 222 985 0 Sitz der Gesellschaft: Vorstand: Michael Jaeggi, Dr. Manfred Pumbo, Dragan
61+
Aktiengesellschaft [email protected] Stuttgart Radanovic
62+
Börsenstraße 4 www.bisonapp.com Registergericht: Aufsichtsratsvorsitzender: Dr. Christian Ricken
63+
70174 Stuttgart Amtsgericht
64+
Stuttgart, HRB 19972
65+
USt-ID: DE 175042226
66+
2. Export deiner Transaktionsübersicht für das Steuerjahr 2021
67+
* Im Info-Report berücksichtigte Transaktionen für die unverbindliche Berechnung des
68+
realisierten Gewinns/Verlustes aus Transaktionen bei BISON im Jahr 2021 mit einer
69+
Haltedauer unter einem Jahr.
70+
Kauf BTC 0,00028505
71+
28.12.2021 09:00 43.850,74 €/BTC - 12,50 €
72+
Kauf ETH 0,00358227
73+
28.12.2021 09:00 3.489,40 €/ETH - 12,50 €
74+
Kauf BTC 0,00028798
75+
21.12.2021 09:00 43.405,55 €/BTC - 12,50 €
76+
Kauf ETH 0,00349735
77+
21.12.2021 09:00 3.574,12 €/ETH - 12,50 €
78+
Kauf BTC 0,00029696
79+
14.12.2021 09:00 42.092,88 €/BTC - 12,50 €
80+
Kauf ETH 0,00368944
81+
14.12.2021 09:00 3.388,05 €/ETH - 12,50 €
82+
Kauf BTC 0,00027461
83+
07.12.2021 09:00 45.518,91 €/BTC - 12,50 €
84+
Kauf ETH 0,00321379
85+
07.12.2021 09:00 3.889,48 €/ETH - 12,50 €
86+
Kauf BTC 0,00029264
87+
04.12.2021 09:27 42.714,59 €/BTC - 12,50 €
88+
Kauf ETH 0,00354180
89+
04.12.2021 09:26 3.529,28 €/ETH - 12,50 €
90+
Kauf BTC 0,00024862
91+
30.11.2021 09:01 50.276,06 €/BTC - 12,50 €
92+
Kauf ETH 0,00318593
93+
30.11.2021 09:01 3.923,49 €/ETH - 12,50 €
94+
EUWAX +49 711 222 985 0 Sitz der Gesellschaft: Vorstand: Michael Jaeggi, Dr. Manfred Pumbo, Dragan
95+
Aktiengesellschaft [email protected] Stuttgart Radanovic
96+
Börsenstraße 4 www.bisonapp.com Registergericht: Aufsichtsratsvorsitzender: Dr. Christian Ricken
97+
70174 Stuttgart Amtsgericht
98+
Stuttgart, HRB 19972
99+
USt-ID: DE 175042226
100+
Einzahlung
101+
29.11.2021 08:49 + 250,00 €
102+
EUWAX +49 711 222 985 0 Sitz der Gesellschaft: Vorstand: Michael Jaeggi, Dr. Manfred Pumbo, Dragan
103+
Aktiengesellschaft [email protected] Stuttgart Radanovic
104+
Börsenstraße 4 www.bisonapp.com Registergericht: Aufsichtsratsvorsitzender: Dr. Christian Ricken
105+
70174 Stuttgart Amtsgericht
106+
Stuttgart, HRB 19972
107+
USt-ID: DE 175042226
108+
3. Export deiner Krypto-Einzahlungen
109+
Kryptowährungen, bei denen du eine Krypto-Einzahlung getätigt hast, gehen nicht in die
110+
Berechnung der realisierten Gewinne und Verluste ein, da BISON keine Informationen über
111+
das ursprüngliche Kaufdatum bzw. den Kaufpreis einer Krypto-Einzahlung hat. Eine
112+
Zuordnung nach FiFo-Verfahren (First-In First-Out) ist nicht möglich.
113+
EUWAX +49 711 222 985 0 Sitz der Gesellschaft: Vorstand: Michael Jaeggi, Dr. Manfred Pumbo, Dragan
114+
Aktiengesellschaft [email protected] Stuttgart Radanovic
115+
Börsenstraße 4 www.bisonapp.com Registergericht: Aufsichtsratsvorsitzender: Dr. Christian Ricken
116+
70174 Stuttgart Amtsgericht
117+
Stuttgart, HRB 19972
118+
USt-ID: DE 175042226
119+
4. Export deiner Krypto-Auszahlungen
120+
Kryptowährungen, bei denen du eine Krypto-Auszahlung getätigt hast, gehen nicht in die
121+
Berechnung der realisierten Gewinne und Verluste ein, da diese nicht über BISON verkauft
122+
wurden. Eine Zuordnung nach FiFo-Verfahren (First-In First-Out) ist nicht möglich.
123+
EUWAX +49 711 222 985 0 Sitz der Gesellschaft: Vorstand: Michael Jaeggi, Dr. Manfred Pumbo, Dragan
124+
Aktiengesellschaft [email protected] Stuttgart Radanovic
125+
Börsenstraße 4 www.bisonapp.com Registergericht: Aufsichtsratsvorsitzender: Dr. Christian Ricken
126+
70174 Stuttgart Amtsgericht
127+
Stuttgart, HRB 19972
128+
USt-ID: DE 175042226
129+
Hinweise zum Info-Report
130+
Für Deutschland gilt:
131+
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
132+
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
133+
(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
134+
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
135+
erzielt wurde, verrechnet werden. Ein Verlustru ̈cktrag bzw. - vortrag ist grundsa ̈tzlich mo ̈glich.
136+
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-
137+
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
138+
und Verluste sind in der Regel in der Anlage SO bei der Steuererkla ̈rung anzugeben.
139+
Diese Information stellt keine steuerliche oder rechtliche Beratung dar. Bei Fragen zu deiner Einkommensteuererkla ̈rung oder sonstigen Angelegenheiten wende dich
140+
bitte an deinen Steuerberater.
141+
Diese Bescheinigung ist maschinell erstellt und wird nicht unterschrieben.
142+
EUWAX +49 711 222 985 0 Sitz der Gesellschaft: Vorstand: Michael Jaeggi, Dr. Manfred Pumbo, Dragan
143+
Aktiengesellschaft [email protected] Stuttgart Radanovic
144+
Börsenstraße 4 www.bisonapp.com Registergericht: Aufsichtsratsvorsitzender: Dr. Christian Ricken
145+
70174 Stuttgart Amtsgericht
146+
Stuttgart, HRB 19972
147+
USt-ID: DE 175042226

0 commit comments

Comments
 (0)