-
Notifications
You must be signed in to change notification settings - Fork 619
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add gross value in security currency for some IBFlex transactions
Issue: #2805 Signed-off-by: csprunk <[email protected]> [squashed commits; rebased to master] Signed-off-by: Andreas Buchen <[email protected]>
- Loading branch information
Showing
3 changed files
with
215 additions
and
2 deletions.
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
...me/abuchen/portfolio/datatransfer/IBActivityStatementWithForeignDividendNoAccountInfo.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<FlexQueryResponse queryName="PortfolioPerformence" type="AF"> | ||
<FlexStatements count="1"> | ||
<FlexStatement accountId="U1234567" fromDate="20180119" toDate="20180904" period="LastMonth" whenGenerated="20210904;114604"> | ||
<Trades> | ||
<Trade accountId="U1234567" acctAlias="" model="" currency="EUR" fxRateToBase="1" assetCategory="STK" symbol="MMM" description="3M CO." conid="13098504" securityID="US88579Y1010" securityIDType="ISIN" cusip="" isin="US88579Y1010" listingExchange="FWB" underlyingConid="" underlyingSymbol="" underlyingSecurityID="" underlyingListingExchange="" issuer="" multiplier="1" strike="" expiry="" tradeID="2029054512" putCall="" reportDate="20180209" principalAdjustFactor="" dateTime="20180209;111929" tradeDate="20180209" settleDateTarget="20180213" transactionType="ExchTrade" exchange="TGATE" quantity="7" tradePrice="181.35" tradeMoney="1269.45" proceeds="-1269.45" taxes="0" ibCommission="-5.8" ibCommissionCurrency="EUR" netCash="-1275.25" closePrice="179" openCloseIndicator="O" notes="" cost="1275.25" fifoPnlRealized="0" fxPnl="0" mtmPnl="-16.45" origTradePrice="0" origTradeDate="" origTradeID="" origOrderID="0" clearingFirmID="" transactionID="8626274484" buySell="BUY" ibOrderID="999354200" ibExecID="00011b0b.aaa98f42.01.01" brokerageOrderID="" orderReference="" volatilityOrderLink="" exchOrderId="N/A" extExecID="88579Y101020180209161929225720/443524178" orderTime="20180209;111927" openDateTime="" holdingPeriodDateTime="" whenRealized="" whenReopened="" levelOfDetail="EXECUTION" changeInPrice="0" changeInQuantity="0" orderType="LMT" traderID="M5" isAPIOrder="N" accruedInt="0" serialNumber="" deliveryType="" commodityType="" fineness="0.0" weight="0.0 ()" /> | ||
<Trade accountId="U1234567" acctAlias="" model="" currency="USD" fxRateToBase="0.84807" assetCategory="STK" symbol="CDW" description="CDW CORP/DE" conid="130432552" securityID="US12514G1085" securityIDType="ISIN" cusip="12514G108" isin="US12514G1085" listingExchange="NASDAQ" underlyingConid="" underlyingSymbol="" underlyingSecurityID="" underlyingListingExchange="" issuer="" multiplier="1" strike="" expiry="" tradeID="3004185992" putCall="" reportDate="20200729" principalAdjustFactor="" dateTime="20200729;124452" tradeDate="20200729" settleDateTarget="20200731" transactionType="ExchTrade" exchange="IBKRATS" quantity="25" tradePrice="114.6" tradeMoney="2865" proceeds="-2865" taxes="0" ibCommission="-5" ibCommissionCurrency="USD" netCash="-2870" closePrice="115.64" openCloseIndicator="O" notes="" cost="2870" fifoPnlRealized="0" fxPnl="0" mtmPnl="26" origTradePrice="0" origTradeDate="" origTradeID="" origOrderID="0" clearingFirmID="" transactionID="13346288726" buySell="BUY" ibOrderID="1475852231" ibExecID="0000d323.5f2180e6.01.01" brokerageOrderID="000bb535.000193c2.5f20faa1.0005" orderReference="" volatilityOrderLink="" exchOrderId="N/A" extExecID="203807150B" orderTime="20200729;124452" openDateTime="" holdingPeriodDateTime="" whenRealized="" whenReopened="" levelOfDetail="EXECUTION" changeInPrice="0" changeInQuantity="0" orderType="LMT" traderID="M5" isAPIOrder="N" accruedInt="0" serialNumber="" deliveryType="" commodityType="" fineness="0.0" weight="0.0 ()" /> | ||
</Trades> | ||
<CashTransactions> | ||
<CashTransaction accountId="U1234567" acctAlias="" model="" currency="USD" fxRateToBase="0.8127" assetCategory="STK" symbol="MMM" description="MMM(US88579Y1010) CASH DIVIDEND 1.36000000 USD PER SHARE (Ordinary Dividend)" conid="13098504" securityID="US88579Y1010" securityIDType="ISIN" cusip="" isin="US88579Y1010" listingExchange="FWB" underlyingConid="" underlyingSymbol="" underlyingSecurityID="" underlyingListingExchange="" issuer="" multiplier="1" strike="" expiry="" putCall="" principalAdjustFactor="" dateTime="20180312;202000" settleDate="20180315" amount="9.52" type="Dividends" tradeID="" code="" transactionID="8765764573" reportDate="20180315" clientReference="" levelOfDetail="DETAIL" serialNumber="" deliveryType="" commodityType="" fineness="0.0" weight="0.0 ()" /> | ||
<CashTransaction accountId="U2379850" acctAlias="" model="" currency="USD" fxRateToBase="0.84642" assetCategory="STK" symbol="CDW" description="CDW(US12514G1085) CASH DIVIDEND USD 0.38 PER SHARE (Ordinary Dividend)" conid="130432552" securityID="US12514G1085" securityIDType="ISIN" cusip="12514G108" isin="US12514G1085" listingExchange="NASDAQ" underlyingConid="" underlyingSymbol="" underlyingSecurityID="" underlyingListingExchange="" issuer="" multiplier="1" strike="" expiry="" putCall="" principalAdjustFactor="" dateTime="20200910;202000" settleDate="20200910" amount="9.5" type="Dividends" tradeID="" code="" transactionID="13713058125" reportDate="20200910" clientReference="" levelOfDetail="DETAIL" serialNumber="" deliveryType="" commodityType="" fineness="0.0" weight="0.0 ()" /> | ||
</CashTransactions> | ||
</FlexStatement> | ||
</FlexStatements> | ||
</FlexQueryResponse> |
161 changes: 161 additions & 0 deletions
161
.../portfolio/datatransfer/IBFlexStatementExtractorWithForeignDividendNoAccountInfoTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
package name.abuchen.portfolio.datatransfer; | ||
|
||
import static org.hamcrest.CoreMatchers.instanceOf; | ||
import static org.hamcrest.CoreMatchers.is; | ||
import static org.hamcrest.CoreMatchers.notNullValue; | ||
import static org.hamcrest.MatcherAssert.assertThat; | ||
|
||
import java.io.File; | ||
import java.io.FileOutputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.time.LocalDateTime; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.stream.Collectors; | ||
|
||
import org.apache.pdfbox.io.IOUtils; | ||
import org.junit.Test; | ||
|
||
import name.abuchen.portfolio.datatransfer.Extractor.BuySellEntryItem; | ||
import name.abuchen.portfolio.datatransfer.Extractor.Item; | ||
import name.abuchen.portfolio.datatransfer.Extractor.SecurityItem; | ||
import name.abuchen.portfolio.datatransfer.Extractor.TransactionItem; | ||
import name.abuchen.portfolio.model.AccountTransaction; | ||
import name.abuchen.portfolio.model.BuySellEntry; | ||
import name.abuchen.portfolio.model.Client; | ||
import name.abuchen.portfolio.model.PortfolioTransaction; | ||
import name.abuchen.portfolio.model.Security; | ||
import name.abuchen.portfolio.model.Transaction.Unit; | ||
import name.abuchen.portfolio.money.CurrencyUnit; | ||
import name.abuchen.portfolio.money.Money; | ||
import name.abuchen.portfolio.money.Quote; | ||
import name.abuchen.portfolio.money.Values; | ||
|
||
@SuppressWarnings("nls") | ||
public class IBFlexStatementExtractorWithForeignDividendNoAccountInfoTest | ||
{ | ||
|
||
private List<Item> runExtractor(List<Exception> errors) throws IOException | ||
{ | ||
Client client = new Client(); | ||
// We add two securities to the client with EUR as currency, both will | ||
// receive dividends in USD. | ||
Security security = new Security("3M CO. already defined", CurrencyUnit.EUR); | ||
security.setIsin("US88579Y1010"); | ||
client.addSecurity(security); | ||
|
||
security = new Security("CDW CORP/DE already defined", CurrencyUnit.EUR); | ||
security.setIsin("US12514G1085"); | ||
client.addSecurity(security); | ||
|
||
InputStream activityStatement = getClass() | ||
.getResourceAsStream("IBActivityStatementWithForeignDividendNoAccountInfo.xml"); | ||
Extractor.InputFile tempFile = createTempFile(activityStatement); | ||
IBFlexStatementExtractor extractor = new IBFlexStatementExtractor(client); | ||
|
||
return extractor.extract(Collections.singletonList(tempFile), errors); | ||
} | ||
|
||
@Test | ||
public void testIBAcitvityStatement() throws IOException | ||
{ | ||
List<Exception> errors = new ArrayList<>(); | ||
List<Item> results = runExtractor(errors); | ||
assertThat(errors.isEmpty(), is(true)); | ||
int numSecurity = 0; // The two securities are already present in the | ||
// client. | ||
int numBuySell = 2; | ||
int numTransactions = 2; | ||
|
||
results.stream().filter(i -> !(i instanceof SecurityItem)) | ||
.forEach(i -> assertThat(i.getAmount(), notNullValue())); | ||
|
||
List<Extractor.Item> securityItems = results.stream().filter(SecurityItem.class::isInstance) | ||
.collect(Collectors.toList()); | ||
|
||
assertThat(securityItems.size(), is(numSecurity)); | ||
|
||
List<Extractor.Item> buySellTransactions = results.stream().filter(BuySellEntryItem.class::isInstance) | ||
.collect(Collectors.toList()); | ||
|
||
assertThat(buySellTransactions.size(), is(numBuySell)); | ||
|
||
List<Extractor.Item> accountTransactions = results.stream().filter(TransactionItem.class::isInstance) | ||
.collect(Collectors.toList()); | ||
|
||
assertThat(accountTransactions.size(), is(numTransactions)); | ||
|
||
assertThat(results.size(), is(numSecurity + numBuySell + numTransactions)); | ||
|
||
assertFirstBuySell(results.stream().filter(BuySellEntryItem.class::isInstance).findFirst()); | ||
assertFirstTransaction(results.stream().filter(TransactionItem.class::isInstance).findFirst()); | ||
assertSecondTransaction(results.stream().filter(TransactionItem.class::isInstance).skip(1).findFirst()); | ||
} | ||
|
||
private void assertFirstBuySell(Optional<Item> item) | ||
{ | ||
assertThat(item.isPresent(), is(true)); | ||
assertThat(item.orElseThrow().getSubject(), instanceOf(BuySellEntry.class)); | ||
BuySellEntry entry = (BuySellEntry) item.orElseThrow().getSubject(); | ||
|
||
assertThat(entry.getPortfolioTransaction().getType(), is(PortfolioTransaction.Type.BUY)); | ||
assertThat(entry.getAccountTransaction().getType(), is(AccountTransaction.Type.BUY)); | ||
|
||
assertThat(entry.getPortfolioTransaction().getSecurity().getName(), is("3M CO. already defined")); | ||
assertThat(entry.getPortfolioTransaction().getMonetaryAmount(), is(Money.of("EUR", 1275_25L))); | ||
assertThat(entry.getPortfolioTransaction().getDateTime(), is(LocalDateTime.parse("2018-02-09T11:19"))); | ||
assertThat(entry.getPortfolioTransaction().getShares(), is(Values.Share.factorize(7))); | ||
assertThat(entry.getPortfolioTransaction().getUnitSum(Unit.Type.FEE), is(Money.of("EUR", 5_80L))); | ||
|
||
assertThat(entry.getPortfolioTransaction().getGrossPricePerShare(), | ||
is(Quote.of("EUR", Values.Quote.factorize(181.35)))); | ||
} | ||
|
||
private void assertFirstTransaction(Optional<Item> item) | ||
{ | ||
assertThat(item.isPresent(), is(true)); | ||
assertThat(item.orElseThrow().getSubject(), instanceOf(AccountTransaction.class)); | ||
AccountTransaction entry = (AccountTransaction) item.orElseThrow().getSubject(); | ||
|
||
assertThat(entry.getType(), is(AccountTransaction.Type.DIVIDENDS)); | ||
|
||
assertThat(entry.getSecurity().getName(), is("3M CO. already defined")); | ||
assertThat(entry.getSecurity().getIsin(), is("US88579Y1010")); | ||
assertThat(entry.getMonetaryAmount(), is(Money.of("USD", 9_52L))); | ||
assertThat(entry.getCurrencyCode(), is("USD")); | ||
assertThat(entry.getSecurity().getCurrencyCode(), is("EUR")); | ||
Unit grossValue = entry.getUnit(Unit.Type.GROSS_VALUE).orElseThrow(); | ||
assertThat(grossValue.getForex(), is(Money.of("EUR", 7_74L))); | ||
assertThat(grossValue.getAmount(), is(Money.of("USD", 9_52L))); | ||
} | ||
|
||
private void assertSecondTransaction(Optional<Item> item) | ||
{ | ||
assertThat(item.isPresent(), is(true)); | ||
assertThat(item.orElseThrow().getSubject(), instanceOf(AccountTransaction.class)); | ||
AccountTransaction entry = (AccountTransaction) item.orElseThrow().getSubject(); | ||
|
||
assertThat(entry.getType(), is(AccountTransaction.Type.DIVIDENDS)); | ||
|
||
assertThat(entry.getSecurity().getName(), is("CDW CORP/DE already defined")); | ||
assertThat(entry.getSecurity().getIsin(), is("US12514G1085")); | ||
assertThat(entry.getMonetaryAmount(), is(Money.of("USD", 9_50L))); | ||
assertThat(entry.getCurrencyCode(), is("USD")); | ||
assertThat(entry.getSecurity().getCurrencyCode(), is("EUR")); | ||
Unit grossValue = entry.getUnit(Unit.Type.GROSS_VALUE).orElseThrow(); | ||
assertThat(grossValue.getForex(), is(Money.of("EUR", 8_04L))); | ||
assertThat(grossValue.getAmount(), is(Money.of("USD", 9_50L))); | ||
} | ||
|
||
private Extractor.InputFile createTempFile(InputStream input) throws IOException | ||
{ | ||
File tempFile = File.createTempFile("iBFlexStatementExtractorTest", null); | ||
FileOutputStream fos = new FileOutputStream(tempFile); | ||
|
||
IOUtils.copy(input, fos); | ||
return new Extractor.InputFile(tempFile); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters