From 222f883b6dbc79044e0638ecc4b059b129e3c3dc Mon Sep 17 00:00:00 2001 From: Nirus2000 Date: Sat, 28 Oct 2023 20:10:59 +0200 Subject: [PATCH] Modify Tiger Brokers PDF-Importer to support new transaction Fix wrong amount buy transactions Add importer information block https://forum.portfolio-performance.info/t/pdf-import-from-tiger-brokers/20484/21 --- .../tigerbrokerspteltd/AccountStatement06.txt | 142 ++++ .../TigerBrokersPteLtdPDFExtractorTest.java | 114 ++- .../pdf/TigerBrokersPteLtdPDFExtractor.java | 680 ++++++++++-------- 3 files changed, 584 insertions(+), 352 deletions(-) create mode 100644 name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/tigerbrokerspteltd/AccountStatement06.txt diff --git a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/tigerbrokerspteltd/AccountStatement06.txt b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/tigerbrokerspteltd/AccountStatement06.txt new file mode 100644 index 0000000000..46634c1ccf --- /dev/null +++ b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/tigerbrokerspteltd/AccountStatement06.txt @@ -0,0 +1,142 @@ +PDFBox Version: 1.8.17 +Portfolio Performance Version: 0.65.4 +----------------------------------------- +Tiger Brokers (Singapore) PTE.LTD. +Activity Statement : 2023.08.14 - 2023.10.12 +OdpTnj LUWwQCoS +Account : 50385408 +Address : ORivKKr pyFyUugeE WEPBmiVvSdMFK 471 tipIy 2 Ps LSYWem JIwyDE 37809 Base Currency : USD +Securities&FuturesAssets : 36,444.11USD +Account Capabilities : Cash(Securities&Futures) +Account Overview +Cash Stock Bond Option Future Funds in Transit Interest Accruals Dividend Accruals Total +Beginning Of The Period 314.70 36,368.64 0.00 0.00 0.00 0.00 0.00 0.00 36,683.34 +End Of The Period 382.48 36,037.35 0.00 0.00 0.00 0.00 0.00 24.28 36,444.11 +Cash Report +Currency: USD +Total Securities Futures +Starting Cash 314.70 314.70 0.00 +Dividends 65.29 65.29 0 +Withholding Tax 2.49 2.49 0 +Ending Cash 382.48 382.48 0.00 +Ending Settled Cash 382.48 382.48 0 +Open Positions + +Stock +Address: 1 Raffles Place, #35-61 One Raffles Place Tower 2, Singapore 048616. 1/6 +Tiger Brokers (Singapore) PTE.LTD. +Activity Statement : 2023.08.14 - 2023.10.12 +Symbol Quantity Mult Cost Price Close Price Settle Price Value Unrealized P/L Initial Margin Maint Margin Currency +QQQ 53 1 332.8633717 369.93000 19,606.29 1,964.53 0.00 0.00 USD +VOO 25 1 391.4812000 398.52000 9,963.00 175.97 0.00 0.00 USD +VT 69 1 96.8340580 93.74000 6,468.06 -213.49 0.00 0.00 USD +Total(In USD) 147 36,037.35 1,927.01 0.00 0.00 USD + +Dividends +Date Description Amount Currency +2023-09-21 VT Cash Dividend 0.4055 USD per Share (Ordinary Dividend) 27.98 USD +2023-10-04 VOO Cash Dividend 1.4925 USD per Share (Ordinary Dividend) 37.31 USD +Total(In USD) 65.29 USD + +Withholding Tax +Date Description Amount Currency +2023-09-21 VT Cash Dividend 0.4055 USD per Share - Tax -4.20 USD +2023-09-21 Cash Dividend USD per Share - Tax 12.29 USD +2023-10-04 VOO Cash Dividend 1.4925 USD per Share - Tax -5.60 USD +Total(In USD) 2.49 USD + +Change in Dividend Accruals +Currency: USD +Address: 1 Raffles Place, #35-61 One Raffles Place Tower 2, Singapore 048616. 2/6 +Tiger Brokers (Singapore) PTE.LTD. +Activity Statement : 2023.08.14 - 2023.10.12 +Symbol Date Ex Date Pay Date Quantity Tax GST Fee(include ADR) Gross Rate Gross Amount Net Amount +QQQ 2023-09-18 2023-09-18T00:00-04:00[US/Eastern] 2023-09-22T00:00-04:00[US/Eastern] 53 4.28 0.00 0.00 28.56 24.28 +VT 2023-09-18 2023-09-18T00:00-04:00[US/Eastern] 2023-09-21T00:00-04:00[US/Eastern] 69 4.20 0.00 0.00 27.98 23.78 +VOO 2023-09-28 2023-09-28T00:00-04:00[US/Eastern] 2023-10-03T00:00-04:00[US/Eastern] 25 5.60 0.00 0.00 37.31 31.71 +Total 14.08 0.00 0.00 93.85 79.77 +Financial Instrument Information + +Stock +Symbol Issuer Description Multiplier Expiry Strike Right +QQQ Invesco QQQ Trust-ETF 1 +VOO Vanguard S&P 500 ETF 1 +VT VANGUARD INTL EQUITY INDEX FUND INC TOTAL WORLD STK INDEX FUND ETF SHS 1 +Base Currency Exchange Rate +Date 2023-10-12 +USD 1 +AUD 0.63139 +CAD 0.73037 +CNH 0.13680 +EUR 1.05280 +GBP 1.21745 +HKD 0.12782 +Address: 1 Raffles Place, #35-61 One Raffles Place Tower 2, Singapore 048616. 3/6 +Tiger Brokers (Singapore) PTE.LTD. +Activity Statement : 2023.08.14 - 2023.10.12 +Date 2023-10-12 +IDR 0.00006 +JPY 0.00668 +NZD 0.59260 +SGD 0.72984 +Address: 1 Raffles Place, #35-61 One Raffles Place Tower 2, Singapore 048616. 4/6 +Tiger Brokers (Singapore) PTE.LTD. +Activity Statement : 2023.08.14 - 2023.10.12 +Annotation +1.Accrued dividends: when the company declares the dividends of shares, this part will calculate the dividends to be paid to the account, and the dividends paid to the account during form checking period will be included into cash. +2.Accrued interest: interest is charged on a monthly basis, so this part will calculate the total interest earned by the account during the statement period, not including the interest received. +3.Net Trades (Sales): Cash from selling securities +4.Net Trades (Purchase): Cash spent on buying securities +5.Other Fees: Including ADR fees, IPO fees, withdraw fees, dividends and other expenses. +6.Withholding Tax: Hong Kong stocks dividend tax +7.Segment Transfer: The transfer of funds between securities and futures +8.Multiplier: in an individual stock option or futures transaction, the value of the contract is expressed as the product of a certain monetary amount and the underlying index. The certain monetary amount is fixed by the contract, +which is referred to as the contract multiplier. At present, the stock is 1 by default and the individual stock option is 100. +9.Settle Price: Futures settlement price is not equal to the closing price, futures is based on the settlement price to calculate the profit and loss of position. +10.Proceeds: Trading amount is trading price multiplied by trading volume +11.Net Amount: When buying, the net amount is recorded as the transaction amount of the purchased financial asset plus the amount of commission and tax +when selling, the net amount is recorded as the cost amortization when selling the financial asset, including the realized profit and loss. +12.Comm/Fee: In the trades module, for trades before December 2021, 'commission' includes commission + platform fee + transaction levy + stamp tax + transaction fee + settlement fee, etc. For trades after December 2021, +'commission' only includes commission, and other expenses are shown separately. +13.Custodian fees: no charge, $0 +14.Exchange rate: You could view U.S. Dollar Currency Index chart to see the exchange rate at the last day for searching. (USD is transaction currency) +15.Transaction is based on FIFO method +16.Futures' Starting Cash and Ending Cash represents the starting and ending asset value of futures account, the difference between Starting and Ending Cash includes changes in position value and cash transactions (not showing +up on statement) +17.Funds in Transit includes IPO transit subscription and IPO transit subscription fee +Notes: +1. Please examine the details in the monthly statement and promptly report any inaccuracy or discrepancy found to service@tigerbrokers.com.sg within 14 calendar days from the date of the statement. All the above details shall be +deemed correct and conclusive if no written notice of objection is received by us within 14 days from the date of the monthly statement. +2. Tiger Brokers (Singapore) Pte Ltd is a member of Financial Institutions Dispute Resolution Centre (FIDReC), which is an independent and impartial institution specialising in the resolution of disputes between financial institutions +and consumers. Unresolved issues can also be brought up to FIDReC at www.fidrec.com.sg for mediation. +3. Information on our fees can be found at https://www.tigerbrokers.com.sg/commissions/fees +4. Information regarding your account can be found in the Terms and Conditions at https://www.tigerbrokers.com.sg/info/terms-n-conditions +5. Customer money and assets are deposited in trust and held in custody, respectively in accordance with the Securities and Futures (Licensing and Conduct of Business) Regulations, governing the protection of client money and +assets.; +Address: 1 Raffles Place, #35-61 One Raffles Place Tower 2, Singapore 048616. 5/6 +Tiger Brokers (Singapore) PTE.LTD. +Activity Statement : 2023.08.14 - 2023.10.12 +Disclaimer: +If any of the information stated on this statement is incorrect, please notify Tiger Brokers (Singapore) Pte Ltd (TBSPL) within fourteen (14 ) business days from the date of this statement , otherwise the information stated shall be +deemed conclusive and binding. Any and all objections put forth shall not be valid unless accompanied by supporting evidence for such objections. However, if TBSPL realise an error has been made on the said statement, it is entitled +at any time to rectify such error. This statement is intended for the named recipient only, if you have received this statement in error, please notify us immediately.; +Important: +1.Regulation 33A(2) of the Financial Advisers Regulations through its schedule now provides for a list of “ Listed Excluded Investment Products” are presumed to be generally well understood by investors. As such, Executed Related +Advice (ERA) in respect to these Excluded Investment Products is exempted from the application of section 27 FAA. This exemption indicates TBSPL is not obliged to ensure any recommendation or advice in relation to such excluded +investment products are suitable for you to invest or transact. As a general summary, this list includes all listed securities approved for listing on a securities exchange available to the public to trade in. As such, under the law TBSPL +assume that you are able to and will make your own decisions on whether any excluded investment product is or is not suitable for you , as well as whether ERA in respect of an excluded investment product is or is not suitable to +you. You are therefore hereby notified: +①Any and every ERA that you may receive with respect to any listed excluded investment product does not consider your investment objectives, financial situation and particular needs; and +②It is your sole responsibility to ensure the suitability of the product recommended to you. Despite the foregoing, Regulation 33A of the Financial Advisers Regulations require the provision of any ERA with respect to any listed +excluded investment product must be accompanied by the rationale for trading in such product. You should therefore take specific note (you should ask for it if it is not provided) of the rationale that the relevant representatives +provide as the reason(s) for offering the ERA to you so that you may understand the reason for the ERA given. +2.The valuations provided above are for information purposes only; they are indicative and do not necessarily reflect the realisable values. The prices used for the valuation of your positions may be based on the last available market +price or prices provided to us by the relevant counterparties (as applicable) or our approximations thereof, all of which are based on sources which we believe to be reliable but which we have not independently verified. For securities +where a market price is not available, the nominal value, the last known market price or offer price (for initial Public Offering) may be used at our discretion. Foreign exchange conversion rates used in the calculation of portfolio value +are for reference only. +3.The monthly statement will be downloaded in your account if there is transaction within that month. Otherwise, statement will be sent to you on quarterly basis. +TIGER BROKERS (SINGAPORE) PTE. LTD. ++65 6331 2277 +WhatsApp +65 6331 2277 +service@tigerbrokers.com.sg +Address: 1 Raffles Place, #35-61 One Raffles Place Tower 2, Singapore 048616. 6/6 \ No newline at end of file diff --git a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/tigerbrokerspteltd/TigerBrokersPteLtdPDFExtractorTest.java b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/tigerbrokerspteltd/TigerBrokersPteLtdPDFExtractorTest.java index 58f2ac43de..b5d30922ea 100644 --- a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/tigerbrokerspteltd/TigerBrokersPteLtdPDFExtractorTest.java +++ b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/tigerbrokerspteltd/TigerBrokersPteLtdPDFExtractorTest.java @@ -1,14 +1,5 @@ package name.abuchen.portfolio.datatransfer.pdf.tigerbrokerspteltd; -import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countAccountTransactions; -import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countBuySell; -import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countSecurities; -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 static org.junit.Assert.assertNull; - import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.dividend; import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasAmount; import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasCurrencyCode; @@ -25,6 +16,15 @@ import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasWkn; import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.purchase; import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.security; +import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.taxRefund; +import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countAccountTransactions; +import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countBuySell; +import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countSecurities; +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 static org.junit.Assert.assertNull; import java.time.LocalDateTime; import java.util.ArrayList; @@ -105,9 +105,9 @@ public void testAccountStatement01() assertNull(entry.getNote()); assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(16070.40)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(16072.54)))); assertThat(entry.getPortfolioTransaction().getGrossValue(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(16068.12)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(16070.26)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(0.00)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), @@ -126,9 +126,9 @@ public void testAccountStatement01() assertNull(entry.getNote()); assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(1302.00)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(1304.13)))); assertThat(entry.getPortfolioTransaction().getGrossValue(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(1299.86)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(1301.99)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(0.00)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), @@ -147,9 +147,9 @@ public void testAccountStatement01() assertNull(entry.getNote()); assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(7063.20)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(7065.33)))); assertThat(entry.getPortfolioTransaction().getGrossValue(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(7061.02)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(7063.15)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(0.00)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), @@ -168,9 +168,9 @@ public void testAccountStatement01() assertNull(entry.getNote()); assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(2719.50)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(2721.63)))); assertThat(entry.getPortfolioTransaction().getGrossValue(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(2717.35)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(2719.48)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(0.00)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), @@ -189,9 +189,9 @@ public void testAccountStatement01() assertNull(entry.getNote()); assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(6679.20)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(6681.34)))); assertThat(entry.getPortfolioTransaction().getGrossValue(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(6676.85)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(6678.99)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(0.00)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), @@ -396,9 +396,9 @@ public void testAccountStatement03() assertNull(entry.getNote()); assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(262.79)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(264.94)))); assertThat(entry.getPortfolioTransaction().getGrossValue(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(260.64)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(262.79)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(0.00)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), @@ -499,9 +499,9 @@ public void testAccountStatement04() assertNull(entry.getNote()); assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(16070.40)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(16072.54)))); assertThat(entry.getPortfolioTransaction().getGrossValue(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(16068.12)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(16070.26)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(0.00)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), @@ -520,9 +520,9 @@ public void testAccountStatement04() assertNull(entry.getNote()); assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(1302.00)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(1304.13)))); assertThat(entry.getPortfolioTransaction().getGrossValue(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(1299.86)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(1301.99)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(0.00)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), @@ -541,9 +541,9 @@ public void testAccountStatement04() assertNull(entry.getNote()); assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(7063.20)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(7065.33)))); assertThat(entry.getPortfolioTransaction().getGrossValue(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(7061.02)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(7063.15)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(0.00)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), @@ -562,9 +562,9 @@ public void testAccountStatement04() assertNull(entry.getNote()); assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(2719.50)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(2721.63)))); assertThat(entry.getPortfolioTransaction().getGrossValue(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(2717.35)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(2719.48)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(0.00)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), @@ -583,9 +583,9 @@ public void testAccountStatement04() assertNull(entry.getNote()); assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(6679.20)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(6681.34)))); assertThat(entry.getPortfolioTransaction().getGrossValue(), - is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(6676.85)))); + is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(6678.99)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.TAX), is(Money.of(CurrencyUnit.USD, Values.Amount.factorize(0.00)))); assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), @@ -615,8 +615,8 @@ public void testAccountStatement05() hasCurrencyCode("USD")))); assertThat(results, hasItem(security( // - hasIsin(null), hasWkn(null), hasTicker("VOO"), // - hasName("Vanguard S&P 500 ETF"), // + hasIsin(null), hasWkn(null), hasTicker("QQQ"), // + hasName("Invesco QQQ Trust"), // hasCurrencyCode("USD")))); assertThat(results, hasItem(security( // @@ -628,7 +628,7 @@ public void testAccountStatement05() assertThat(results, hasItem(purchase( // hasDate("2023-01-06T03:33:08"), hasShares(1), // hasSource("AccountStatement05.txt"), hasNote(null), // - hasAmount("USD", 262.79), hasGrossValue("USD", 260.64), // + hasAmount("USD", 264.94), hasGrossValue("USD", 262.79), // hasTaxes("USD", 0.00), hasFees("USD", 0.99 + 0.16 + 1.00)))); // check dividend transaction @@ -708,4 +708,50 @@ public void testAccountStatement05() hasAmount("USD", 22.70), hasGrossValue("USD", 26.71), // hasTaxes("USD", 4.01), hasFees("USD", 0.00)))); } + + @Test + public void testAccountStatement06() + { + TigerBrokersPteLtdPDFExtractor extractor = new TigerBrokersPteLtdPDFExtractor(new Client()); + + List errors = new ArrayList<>(); + + List results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "AccountStatement06.txt"), errors); + + assertThat(errors, empty()); + assertThat(countSecurities(results), is(2L)); + assertThat(countBuySell(results), is(0L)); + assertThat(countAccountTransactions(results), is(3L)); + assertThat(results.size(), is(5)); + new AssertImportActions().check(results, CurrencyUnit.USD); + + // check security + assertThat(results, hasItem(security( // + hasIsin(null), hasWkn(null), hasTicker("VT"), // + hasName("VANGUARD INTL EQUITY INDEX FUND INC TOTAL WORLD STK INDEX FUND ETF SHS"), // + hasCurrencyCode("USD")))); + + assertThat(results, hasItem(security( // + hasIsin(null), hasWkn(null), hasTicker("VOO"), // + hasName("Vanguard S&P 500 ETF"), // + hasCurrencyCode("USD")))); + + // check dividend transaction + assertThat(results, hasItem(dividend( // + hasDate("2023-09-21T00:00"), hasShares(68), // + hasSource("AccountStatement06.txt"), hasNote("Ordinary Dividend"), // + hasAmount("USD", 23.78), hasGrossValue("USD", 27.98), // + hasTaxes("USD", 4.20), hasFees("USD", 0.00)))); + + // check dividend transaction + assertThat(results, hasItem(dividend( // + hasDate("2023-10-04T00:00"), hasShares(25), // + hasSource("AccountStatement06.txt"), hasNote("Ordinary Dividend"), // + hasAmount("USD", 31.71), hasGrossValue("USD", 37.31), // + hasTaxes("USD", 5.60), hasFees("USD", 0.00)))); + + // check tax refund transaction + assertThat(results, hasItem(taxRefund(hasDate("2023-09-21"), hasAmount("USD", 12.29), // + hasSource("AccountStatement06.txt"), hasNote(null)))); + } } diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/TigerBrokersPteLtdPDFExtractor.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/TigerBrokersPteLtdPDFExtractor.java index 98974ebb22..b594dcf345 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/TigerBrokersPteLtdPDFExtractor.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/TigerBrokersPteLtdPDFExtractor.java @@ -1,8 +1,7 @@ package name.abuchen.portfolio.datatransfer.pdf; -import static name.abuchen.portfolio.util.TextUtil.trim; - import static name.abuchen.portfolio.datatransfer.ExtractorUtils.checkAndSetTax; +import static name.abuchen.portfolio.util.TextUtil.trim; import java.math.BigDecimal; import java.math.RoundingMode; @@ -24,9 +23,25 @@ import name.abuchen.portfolio.model.BuySellEntry; import name.abuchen.portfolio.model.Client; import name.abuchen.portfolio.model.PortfolioTransaction; +import name.abuchen.portfolio.money.CurrencyUnit; import name.abuchen.portfolio.money.Money; import name.abuchen.portfolio.money.Values; +/** + * @formatter:off + * @implNote Tiger Brokers (Singapore) PTE.LTD. is a US-based financial services company. + * The currency is USD. + * + * All security currencies are USD. + * When there is no trade in progress, the securities currency is not issued. + * We then set the securities currency to USD. + * @see Test file --> AccountStatement06.txt + * + * @implSpec In case of purchase and sale, the amount is given in gross. + * To get the correct net amount, we need to add the fees. + * @formatter:on + */ + @SuppressWarnings("nls") public class TigerBrokersPteLtdPDFExtractor extends AbstractPDFExtractor { @@ -50,7 +65,7 @@ public String getLabel() private void addAccountStatementTransaction() { final DocumentType type = new DocumentType("Activity Statement", (context, lines) -> { - Pattern pCurrency = Pattern.compile("^Currency: (?[\\w]{3})$"); + Pattern pCurrency = Pattern.compile("^.* Base Currency : (?[\\w]{3})$"); Pattern pSecurity = Pattern.compile("^(?(?!(GST|Net))[A-Z0-9]{2,4}) (?.*) [\\d]$"); Pattern pSecurityCurrency = Pattern.compile("^Stock Currency: (?[\\w]{3})$"); Pattern pDividendTaxes = Pattern.compile("^(?[\\d]{4}\\-[\\d]{2}\\-[\\d]{2}) (?[A-Z0-9]{2,4}) Cash Dividend .* \\-(?[\\.,\\d]+).*$"); @@ -72,7 +87,8 @@ private void addAccountStatementTransaction() SecurityListHelper securityListHelper = new SecurityListHelper(); context.putType(securityListHelper); - // Extract security information using pSecurity pattern and add pSecurityCurrency pattern + // Extract security information using pSecurity pattern and add + // pSecurityCurrency pattern List securityItems = new ArrayList<>(); for (String line : lines) @@ -83,7 +99,7 @@ private void addAccountStatementTransaction() SecurityItem securityItem = new SecurityItem(); securityItem.tickerSymbol = mSecurity.group("tickerSymbol"); securityItem.name = mSecurity.group("name"); - securityItem.currency = asCurrencyCode(securityCurrency); + securityItem.currency = (securityCurrency == null) ? CurrencyUnit.USD : asCurrencyCode(securityCurrency); securityItems.add(securityItem); securityListHelper.items.add(securityItem); } @@ -113,11 +129,6 @@ private void addAccountStatementTransaction() this.addDocumentTyp(type); Transaction buyBlock_Format01 = new Transaction<>(); - buyBlock_Format01.subject(() -> { - BuySellEntry entry = new BuySellEntry(); - entry.setType(PortfolioTransaction.Type.BUY); - return entry; - }); // @formatter:off // Settlement Fee: -0.14 @@ -129,53 +140,61 @@ private void addAccountStatementTransaction() firstRelevantLineForBuyBlock_Format01.setMaxSize(3); firstRelevantLineForBuyBlock_Format01.set(buyBlock_Format01); - buyBlock_Format01 - .section("tickerSymbol", "date", "time", "shares", "amount") - .match("^Settlement Fee: \\-[\\.,\\d]+$") - .match("^(?[A-Z0-9]{2,4}) " - + "(?[\\d]{4}\\-[\\d]{2}\\-[\\d]{2}), " - + "(?