diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/Messages.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/Messages.java index acc5e9777d..8de9f723fa 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/Messages.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/Messages.java @@ -940,6 +940,7 @@ public class Messages extends NLS public static String SecurityMenuAddPrice; public static String SecurityMenuBuy; public static String SecurityMenuConfigureOnlineUpdate; + public static String SecurityMenuCover; public static String SecurityMenuCreateManually; public static String SecurityMenuCreateQuotesFromTransactions; public static String SecurityMenuDebugGetHistoricalQuotes; @@ -960,6 +961,7 @@ public class Messages extends NLS public static String SecurityMenuRemoveFromWatchlist; public static String SecurityMenuSearch4Securities; public static String SecurityMenuSell; + public static String SecurityMenuShort; public static String SecurityMenuStockSplit; public static String SecurityMenuAddEvent; public static String SecurityMenuTransfer; diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/palette/TransactionElements.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/palette/TransactionElements.java index cdb515b7fe..d2790cc400 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/palette/TransactionElements.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/palette/TransactionElements.java @@ -120,6 +120,8 @@ public List getElements() for (final PortfolioTransaction.Type type : EnumSet.of( // PortfolioTransaction.Type.BUY, // PortfolioTransaction.Type.SELL, // + PortfolioTransaction.Type.COVER, // + PortfolioTransaction.Type.SHORT, // PortfolioTransaction.Type.DELIVERY_INBOUND, // PortfolioTransaction.Type.DELIVERY_OUTBOUND)) { diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AbstractSecurityTransactionModel.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AbstractSecurityTransactionModel.java index 4d502688a1..ed7ee86b82 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AbstractSecurityTransactionModel.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AbstractSecurityTransactionModel.java @@ -604,9 +604,11 @@ protected long calculateConvertedGrossValue() switch (type) { case BUY: + case COVER: case DELIVERY_INBOUND: return Math.max(0, total - feesAndTaxes); case SELL: + case SHORT: case DELIVERY_OUTBOUND: return total + feesAndTaxes; default: @@ -621,9 +623,11 @@ private long calculateTotal() switch (type) { case BUY: + case COVER: case DELIVERY_INBOUND: return convertedGrossValue + feesAndTaxes; case SELL: + case SHORT: case DELIVERY_OUTBOUND: return Math.max(0, convertedGrossValue - feesAndTaxes); default: diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionDialog.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionDialog.java index 8fa418788f..9a3d71e078 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionDialog.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionDialog.java @@ -411,6 +411,8 @@ private String getTotalLabel() // NOSONAR return Messages.ColumnCreditNote; case BUY: case SELL: + case COVER: + case SHORT: case TRANSFER_IN: case TRANSFER_OUT: default: diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionModel.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionModel.java index c51e6c5228..d0d7fbbdeb 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionModel.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/AccountTransactionModel.java @@ -92,6 +92,8 @@ private void checkType() return; case BUY: case SELL: + case COVER: + case SHORT: case TRANSFER_IN: case TRANSFER_OUT: default: diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/BuySellModel.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/BuySellModel.java index cdfe6dc985..456dd46c2b 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/BuySellModel.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/BuySellModel.java @@ -28,7 +28,10 @@ public BuySellModel(Client client, PortfolioTransaction.Type type) @Override public boolean accepts(PortfolioTransaction.Type type) { - return type == PortfolioTransaction.Type.BUY || type == PortfolioTransaction.Type.SELL; + return type == PortfolioTransaction.Type.BUY + || type == PortfolioTransaction.Type.SELL + || type == PortfolioTransaction.Type.COVER + || type == PortfolioTransaction.Type.SHORT; } @Override diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/SecurityTransactionDialog.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/SecurityTransactionDialog.java index 92958d9f26..c5cbd711e2 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/SecurityTransactionDialog.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/dialogs/transactions/SecurityTransactionDialog.java @@ -59,7 +59,10 @@ public SecurityTransactionDialog(@Named(IServiceConstants.ACTIVE_SHELL) Shell pa @PostConstruct private void createModel(ExchangeRateProviderFactory factory, PortfolioTransaction.Type type) // NOSONAR { - boolean isBuySell = type == PortfolioTransaction.Type.BUY || type == PortfolioTransaction.Type.SELL; + boolean isBuySell = type == PortfolioTransaction.Type.BUY + || type == PortfolioTransaction.Type.SELL + || type == PortfolioTransaction.Type.COVER + || type == PortfolioTransaction.Type.SHORT; AbstractSecurityTransactionModel model = isBuySell ? new BuySellModel(client, type) : new SecurityDeliveryModel(client, type); model.setExchangeRateProviderFactory(factory); @@ -289,9 +292,11 @@ private String sign() switch (model().getType()) { case BUY: + case COVER: case DELIVERY_INBOUND: return "+ "; //$NON-NLS-1$ case SELL: + case SHORT: case DELIVERY_OUTBOUND: return "- "; //$NON-NLS-1$ default: @@ -304,10 +309,12 @@ private String getTotalLabel() switch (model().getType()) { case BUY: + case COVER: return Messages.ColumnDebitNote; case DELIVERY_INBOUND: return Messages.LabelValueInboundDelivery; case SELL: + case SHORT: return Messages.ColumnCreditNote; case DELIVERY_OUTBOUND: return Messages.LabelValueOutboundDelivery; diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/handlers/NewTransactionMenuContribution.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/handlers/NewTransactionMenuContribution.java index 7efbc6602b..d911b59765 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/handlers/NewTransactionMenuContribution.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/handlers/NewTransactionMenuContribution.java @@ -42,6 +42,8 @@ public void aboutToShow(@Named(IServiceConstants.ACTIVE_PART) MPart part, List p.getValuation().getAmount() > 0) // .sorted((l, r) -> Long.compare(r.getValuation().getAmount(), l.getValuation().getAmount())) // .forEach(p -> { String name = JSONObject.escape(p.getDescription()); String percentage = Values.Percent2.format(p.getShare()); joiner.add(String.format(ENTRY, name, // - p.getValuation().getAmount(), // + Math.abs(p.getValuation().getAmount()), // colors.next(), // name, percentage, Values.Share.format(p.getPosition().getShares()), // Values.Money.format(p.getValuation() diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecuritiesChart.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecuritiesChart.java index d73ff14f2e..196a9bd823 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecuritiesChart.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecuritiesChart.java @@ -925,6 +925,7 @@ private void addInvestmentMarkerLines(ChartInterval chartInterval) List purchase = client.getPortfolios().stream().flatMap(p -> p.getTransactions().stream()) .filter(t -> t.getSecurity() == security) .filter(t -> t.getType() == PortfolioTransaction.Type.BUY + || t.getType() == PortfolioTransaction.Type.COVER || t.getType() == PortfolioTransaction.Type.DELIVERY_INBOUND) .filter(t -> chartInterval.contains(t.getDateTime())) // .sorted(new Transaction.ByDate()).collect(Collectors.toList()); @@ -934,6 +935,7 @@ private void addInvestmentMarkerLines(ChartInterval chartInterval) List sales = client.getPortfolios().stream().flatMap(p -> p.getTransactions().stream()) .filter(t -> t.getSecurity() == security) .filter(t -> t.getType() == PortfolioTransaction.Type.SELL + || t.getType() == PortfolioTransaction.Type.SHORT || t.getType() == PortfolioTransaction.Type.DELIVERY_OUTBOUND) .filter(t -> chartInterval.contains(t.getDateTime())) // .sorted(new Transaction.ByDate()).collect(Collectors.toList()); diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecuritiesTable.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecuritiesTable.java index 1a366a1bcc..9560f50c7f 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecuritiesTable.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecuritiesTable.java @@ -904,6 +904,18 @@ private void fillTransactionContextMenu(IMenuManager manager, Security security) .with(security) // .addTo(manager); + new OpenDialogAction(view, Messages.SecurityMenuCover + "...") //$NON-NLS-1$ + .type(SecurityTransactionDialog.class) // + .parameters(PortfolioTransaction.Type.COVER) // + .with(security) // + .addTo(manager); + + new OpenDialogAction(view, Messages.SecurityMenuShort + "...") //$NON-NLS-1$ + .type(SecurityTransactionDialog.class) // + .parameters(PortfolioTransaction.Type.SHORT) // + .with(security) // + .addTo(manager); + new OpenDialogAction(view, Messages.SecurityMenuDividends + "...") //$NON-NLS-1$ .type(AccountTransactionDialog.class) // .parameters(AccountTransaction.Type.DIVIDENDS) // diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecurityContextMenu.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecurityContextMenu.java index 12553d45eb..e97b9fbccd 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecurityContextMenu.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecurityContextMenu.java @@ -64,6 +64,20 @@ public void menuAboutToShow(IMenuManager manager, final Security security, final .with(security) // .addTo(manager); + new OpenDialogAction(owner, Messages.SecurityMenuCover + "...") //$NON-NLS-1$ + .type(SecurityTransactionDialog.class) // + .parameters(PortfolioTransaction.Type.COVER) // + .with(portfolio) // + .with(security) // + .addTo(manager); + + new OpenDialogAction(owner, Messages.SecurityMenuShort + "...") //$NON-NLS-1$ + .type(SecurityTransactionDialog.class) // + .parameters(PortfolioTransaction.Type.SHORT) // + .with(portfolio) // + .with(security) // + .addTo(manager); + new OpenDialogAction(owner, Messages.SecurityMenuDividends + "...") //$NON-NLS-1$ .type(AccountTransactionDialog.class) // .parameters(AccountTransaction.Type.DIVIDENDS) // diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecurityListView.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecurityListView.java index 5e4dffb582..16c84b257d 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecurityListView.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/SecurityListView.java @@ -250,9 +250,11 @@ private long getSharesHeld(Client client, Security security) switch (t.getType()) { case BUY: + case COVER: case DELIVERY_INBOUND: return t.getShares(); case SELL: + case SHORT: case DELIVERY_OUTBOUND: return -t.getShares(); default: diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/TransactionsViewer.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/TransactionsViewer.java index 82d92e4295..f7f8bfb539 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/TransactionsViewer.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/TransactionsViewer.java @@ -550,7 +550,9 @@ private void fillContextMenuPortfolioTxList(IMenuManager manager, IStructuredSel PortfolioTransaction ptx = tx.getTransaction(); allBuyOrSellType &= ptx.getType() == PortfolioTransaction.Type.BUY - || ptx.getType() == PortfolioTransaction.Type.SELL; + || ptx.getType() == PortfolioTransaction.Type.SELL + || ptx.getType() == PortfolioTransaction.Type.COVER + || ptx.getType() == PortfolioTransaction.Type.SHORT; allDelivery &= ptx.getType() == PortfolioTransaction.Type.DELIVERY_INBOUND || ptx.getType() == PortfolioTransaction.Type.DELIVERY_OUTBOUND; diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/actions/RevertBuySellAction.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/actions/RevertBuySellAction.java index d5210fc21f..819a60968b 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/actions/RevertBuySellAction.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/actions/RevertBuySellAction.java @@ -25,14 +25,20 @@ public RevertBuySellAction(Client client, TransactionPair transaction) { PortfolioTransaction.Type type = ((PortfolioTransaction) transaction.getTransaction()).getType(); - if (type != PortfolioTransaction.Type.BUY && type != PortfolioTransaction.Type.SELL) + if (type != PortfolioTransaction.Type.BUY + && type != PortfolioTransaction.Type.SELL + && type != PortfolioTransaction.Type.COVER + && type != PortfolioTransaction.Type.SHORT) throw new IllegalArgumentException(); } else if (transaction.getTransaction() instanceof AccountTransaction) { AccountTransaction.Type type = ((AccountTransaction) transaction.getTransaction()).getType(); - if (type != AccountTransaction.Type.BUY && type != AccountTransaction.Type.SELL) + if (type != AccountTransaction.Type.BUY + && type != AccountTransaction.Type.SELL + && type != AccountTransaction.Type.COVER + && type != AccountTransaction.Type.SHORT) throw new IllegalArgumentException(); } else @@ -71,6 +77,20 @@ else if (tx.getType() == PortfolioTransaction.Type.SELL) buysell.setMonetaryAmount(grossAmount.add(feesAndTaxes)); } + else if (tx.getType() == PortfolioTransaction.Type.COVER) + { + buysell.getAccountTransaction().setType(AccountTransaction.Type.SHORT); + tx.setType(PortfolioTransaction.Type.SHORT); + + buysell.setMonetaryAmount(grossAmount.add(feesAndTaxes)); + } + else if (tx.getType() == PortfolioTransaction.Type.SHORT) + { + buysell.getAccountTransaction().setType(AccountTransaction.Type.COVER); + tx.setType(PortfolioTransaction.Type.COVER); + + buysell.setMonetaryAmount(grossAmount.add(feesAndTaxes)); + } else { throw new IllegalArgumentException(); diff --git a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/dashboard/ActivityWidget.java b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/dashboard/ActivityWidget.java index 2413e045b9..c0d890d18d 100644 --- a/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/dashboard/ActivityWidget.java +++ b/name.abuchen.portfolio.ui/src/name/abuchen/portfolio/ui/views/dashboard/ActivityWidget.java @@ -265,12 +265,18 @@ public void update(List> transactions) createSeries(chartType, interval, transactions, yearMonths, PortfolioTransaction.Type.BUY, Colors.ICON_BLUE); + createSeries(chartType, interval, transactions, yearMonths, PortfolioTransaction.Type.COVER, + Colors.ICON_BLUE); + createSeries(chartType, interval, transactions, yearMonths, PortfolioTransaction.Type.DELIVERY_INBOUND, Colors.brighter(Colors.ICON_BLUE)); createSeries(chartType, interval, transactions, yearMonths, PortfolioTransaction.Type.SELL, Colors.ICON_ORANGE); + createSeries(chartType, interval, transactions, yearMonths, PortfolioTransaction.Type.SHORT, + Colors.ICON_ORANGE); + createSeries(chartType, interval, transactions, yearMonths, PortfolioTransaction.Type.DELIVERY_OUTBOUND, Colors.brighter(Colors.ICON_ORANGE)); diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/checks/impl/CrossEntryCheck.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/checks/impl/CrossEntryCheck.java index 15be2f194f..7623891653 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/checks/impl/CrossEntryCheck.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/checks/impl/CrossEntryCheck.java @@ -89,6 +89,8 @@ private void collectAccountTransactions() { case BUY: case SELL: + case COVER: + case SHORT: case TRANSFER_IN: case TRANSFER_OUT: accountTransactions.add(new AccountEntry(account, t)); @@ -113,6 +115,8 @@ private void collectPortfolioTransactions() { case BUY: case SELL: + case SHORT: + case COVER: case TRANSFER_IN: case TRANSFER_OUT: portfolioTransactions.add(new PortfolioEntry(portfolio, t)); @@ -132,7 +136,9 @@ private void matchBuySell() AccountEntry suspect = iterAccount.next(); if (suspect.transaction.getType() != AccountTransaction.Type.BUY - && suspect.transaction.getType() != AccountTransaction.Type.SELL) + && suspect.transaction.getType() != AccountTransaction.Type.SELL + && suspect.transaction.getType() != AccountTransaction.Type.COVER + && suspect.transaction.getType() != AccountTransaction.Type.SHORT) continue; if (suspect.transaction.getSecurity() == null) diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/checks/impl/SharesHeldConsistencyCheck.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/checks/impl/SharesHeldConsistencyCheck.java index d92967a3ea..e23108f10d 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/checks/impl/SharesHeldConsistencyCheck.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/checks/impl/SharesHeldConsistencyCheck.java @@ -87,11 +87,13 @@ public List execute(Client client) switch (t.getType()) { case BUY: + case COVER: case TRANSFER_IN: case DELIVERY_INBOUND: shares[index] += t.getShares(); break; case SELL: + case SHORT: case TRANSFER_OUT: case DELIVERY_OUTBOUND: shares[index] -= t.getShares(); diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/Extractor.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/Extractor.java index 4a1061401e..f49dd21dce 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/Extractor.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/Extractor.java @@ -217,6 +217,8 @@ public TransactionItem(AccountTransaction transaction) { if (EnumSet.of(AccountTransaction.Type.BUY, // AccountTransaction.Type.SELL, // + AccountTransaction.Type.COVER, // + AccountTransaction.Type.SHORT, // AccountTransaction.Type.TRANSFER_IN, // AccountTransaction.Type.TRANSFER_OUT) // .contains(transaction.getType())) @@ -228,6 +230,8 @@ public TransactionItem(PortfolioTransaction transaction) { if (EnumSet.of(PortfolioTransaction.Type.BUY, // PortfolioTransaction.Type.SELL, // + PortfolioTransaction.Type.COVER, // + PortfolioTransaction.Type.SHORT, // PortfolioTransaction.Type.TRANSFER_IN, // PortfolioTransaction.Type.TRANSFER_OUT) // .contains(transaction.getType())) diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/IBFlexStatementExtractor.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/IBFlexStatementExtractor.java index c0f67fa2a5..dcf6ad1503 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/IBFlexStatementExtractor.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/IBFlexStatementExtractor.java @@ -305,6 +305,14 @@ else if (element.getAttribute("buySell").equals("SELL")) { transaction.setType(PortfolioTransaction.Type.SELL); } + else if (element.getAttribute("buySell").equals("SHORT")) + { + transaction.setType(PortfolioTransaction.Type.SHORT); + } + else if (element.getAttribute("buySell").equals("COVER")) + { + transaction.setType(PortfolioTransaction.Type.COVER); + } else { throw new IllegalArgumentException(); diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/actions/CheckCurrenciesAction.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/actions/CheckCurrenciesAction.java index efed5cf343..afccc1dc17 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/actions/CheckCurrenciesAction.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/actions/CheckCurrenciesAction.java @@ -24,8 +24,10 @@ public class CheckCurrenciesAction implements ImportAction { - private static final Set TRANSACTIONS_WO_UNITS = EnumSet.of(AccountTransaction.Type.BUY, - AccountTransaction.Type.SELL, AccountTransaction.Type.TRANSFER_IN); + private static final Set TRANSACTIONS_WO_UNITS = EnumSet.of( + AccountTransaction.Type.BUY, AccountTransaction.Type.SELL, + AccountTransaction.Type.COVER, AccountTransaction.Type.SHORT, + AccountTransaction.Type.TRANSFER_IN); @Override public Status process(Security security) @@ -81,7 +83,8 @@ public Status process(PortfolioTransaction transaction, Portfolio portfolio) return status; if (transaction.getType() == PortfolioTransaction.Type.DELIVERY_INBOUND - || transaction.getType() == PortfolioTransaction.Type.BUY) + || transaction.getType() == PortfolioTransaction.Type.BUY + || transaction.getType() == PortfolioTransaction.Type.SHORT) { // tax + fees must be < than transaction amount Money taxAndFees = transaction.getUnits() // diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/actions/CheckValidTypesAction.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/actions/CheckValidTypesAction.java index f93f08abf3..24918a8fc5 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/actions/CheckValidTypesAction.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/actions/CheckValidTypesAction.java @@ -18,6 +18,8 @@ public Status process(AccountTransaction transaction, Account account) { case BUY: case SELL: + case COVER: + case SHORT: case TRANSFER_IN: case TRANSFER_OUT: return new Status(Status.Code.ERROR, MessageFormat.format(Messages.MsgCheckInvalidTransactionType, @@ -44,6 +46,8 @@ public Status process(PortfolioTransaction transaction, Portfolio portfolio) { case BUY: case SELL: + case COVER: + case SHORT: case TRANSFER_IN: case TRANSFER_OUT: return new Status(Status.Code.ERROR, MessageFormat.format(Messages.MsgCheckInvalidTransactionType, diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/model/Account.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/model/Account.java index e66622f3ea..f79734cfe1 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/model/Account.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/model/Account.java @@ -137,6 +137,7 @@ public long getCurrentAmount(LocalDateTime date) case DIVIDENDS: case INTEREST: case SELL: + case SHORT: case TRANSFER_IN: case TAX_REFUND: case FEES_REFUND: @@ -146,6 +147,7 @@ public long getCurrentAmount(LocalDateTime date) case TAXES: case REMOVAL: case BUY: + case COVER: case TRANSFER_OUT: return -t.getAmount(); default: diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/model/AccountTransaction.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/model/AccountTransaction.java index a19d3cfba9..a6b75382a3 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/model/AccountTransaction.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/model/AccountTransaction.java @@ -19,6 +19,7 @@ public enum Type FEES(true), FEES_REFUND(false), // TAXES(true), TAX_REFUND(false), // BUY(true), SELL(false), // + COVER(true), SHORT(false), // TRANSFER_IN(false), TRANSFER_OUT(true); private static final ResourceBundle RESOURCES = ResourceBundle.getBundle("name.abuchen.portfolio.model.labels"); //$NON-NLS-1$ diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/model/Client.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/model/Client.java index 79d04bed21..b29fe78b3e 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/model/Client.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/model/Client.java @@ -440,7 +440,8 @@ public List> getAllTransactions() .map(t -> new TransactionPair<>(portfolio, t)).forEach(transactions::add); EnumSet exclude = EnumSet.of(AccountTransaction.Type.TRANSFER_IN, - AccountTransaction.Type.BUY, AccountTransaction.Type.SELL); + AccountTransaction.Type.BUY, AccountTransaction.Type.SELL, + AccountTransaction.Type.COVER, AccountTransaction.Type.SHORT); for (Account account : accounts) { diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/model/PortfolioTransaction.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/model/PortfolioTransaction.java index f863017ab5..ba0eb9c697 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/model/PortfolioTransaction.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/model/PortfolioTransaction.java @@ -20,6 +20,10 @@ public enum Type BUY(true), /** Records the sale of a security. */ SELL(false), + /** Records the short selling of a security. */ + SHORT(true), + /** Records the short covering of a security. */ + COVER(false), /** Records the transfer of assets from another portfolio. */ TRANSFER_IN(true), /** Records the transfer of assets to another portfolio. */ @@ -139,6 +143,11 @@ public long getGrossValueAmount() long taxAndFees = getUnits().filter(u -> u.getType() == Unit.Type.TAX || u.getType() == Unit.Type.FEE) .collect(MoneyCollectors.sum(getCurrencyCode(), u -> u.getAmount())).getAmount(); + if (Type.SHORT == this.type) + return getAmount() + taxAndFees; + else if (Type.COVER == this.type) + return getAmount() - taxAndFees; + if (this.type.isPurchase()) return getAmount() - taxAndFees; else diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/model/labels.properties b/name.abuchen.portfolio/src/name/abuchen/portfolio/model/labels.properties index c4fdc13446..3618d40500 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/model/labels.properties +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/model/labels.properties @@ -1,5 +1,6 @@ account.BUY = Buy +account.COVER = Cover account.DEPOSIT = Deposit account.DIVIDENDS = Dividend account.FEES = Fees @@ -8,6 +9,7 @@ account.INTEREST = Interest account.INTEREST_CHARGE = Interest Charge account.REMOVAL = Removal account.SELL = Sell +account.SHORT = Short account.TAXES = Taxes account.TAX_REFUND = Tax Refund account.TRANSFER_IN = Transfer (Inbound) @@ -18,8 +20,10 @@ event.NOTE = Note event.STOCK_SPLIT = Stock Split portfolio.BUY = Buy +portfolio.COVER = Cover portfolio.DELIVERY_INBOUND = Delivery (Inbound) portfolio.DELIVERY_OUTBOUND = Delivery (Outbound) portfolio.SELL = Sell +portfolio.SHORT = Short portfolio.TRANSFER_IN = Transfer (Inbound) portfolio.TRANSFER_OUT = Transfer (Outbound) diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/ClientIRRYield.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/ClientIRRYield.java index 4f0dba29a6..de810f5948 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/ClientIRRYield.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/ClientIRRYield.java @@ -66,6 +66,8 @@ private static void collectPortfolioTransactions(Client client, Interval interva break; case BUY: case SELL: + case COVER: + case SHORT: break; default: throw new UnsupportedOperationException(); @@ -91,6 +93,8 @@ private static void collectAccountTransactions(Client client, Interval interval, break; case BUY: case SELL: + case COVER: + case SHORT: case FEES: case FEES_REFUND: case TAXES: diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/ClientPerformanceSnapshot.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/ClientPerformanceSnapshot.java index 04ee10a844..2a7dcd0e07 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/ClientPerformanceSnapshot.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/ClientPerformanceSnapshot.java @@ -388,6 +388,8 @@ private void addEarnings() break; case BUY: case SELL: + case COVER: + case SHORT: case TRANSFER_IN: case TRANSFER_OUT: // no operation @@ -431,6 +433,8 @@ private void addEarnings() break; case BUY: case SELL: + case COVER: + case SHORT: case TRANSFER_IN: case TRANSFER_OUT: break; @@ -520,6 +524,7 @@ private void addCurrencyGains() case DEPOSIT: case TAX_REFUND: case SELL: + case SHORT: case FEES_REFUND: value.subtract(t.getMonetaryAmount().with(converter.at(t.getDateTime()))); break; @@ -528,6 +533,7 @@ private void addCurrencyGains() case INTEREST_CHARGE: case TAXES: case BUY: + case COVER: value.add(t.getMonetaryAmount().with(converter.at(t.getDateTime()))); break; case TRANSFER_IN: diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/GroupEarningsByAccount.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/GroupEarningsByAccount.java index 7cfae4a99b..791a35c314 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/GroupEarningsByAccount.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/GroupEarningsByAccount.java @@ -121,6 +121,8 @@ public GroupEarningsByAccount(ClientPerformanceSnapshot snapshot) break; case BUY: case SELL: + case COVER: + case SHORT: CrossEntry crossEntry = at.getCrossEntry(); if (crossEntry instanceof BuySellEntry) { diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/SecurityPosition.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/SecurityPosition.java index 7f1eb34903..8ca2857a36 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/SecurityPosition.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/SecurityPosition.java @@ -160,14 +160,14 @@ private void calculatePurchaseValuePriceMvgAvg(CurrencyConverter converter, List if (t.getType().isPurchase()) { - // this is a buy + // this is a buy or short sharesHeld += numShares; netInvestment += netAmount; grossInvestment += grossAmount; } else { - // this is a sell + // this is a sell or cover long remainingShares = sharesHeld - numShares; if (remainingShares <= 0 || sharesHeld == 0) @@ -247,10 +247,12 @@ public SecurityPosition(Security security, CurrencyConverter converter, Security switch (t.getType()) { case BUY: + case COVER: case TRANSFER_IN: case DELIVERY_INBOUND: return t.getShares(); case SELL: + case SHORT: case TRANSFER_OUT: case DELIVERY_OUTBOUND: return -t.getShares(); diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/filter/ClientClassificationFilter.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/filter/ClientClassificationFilter.java index 03814b2690..8485594c84 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/filter/ClientClassificationFilter.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/filter/ClientClassificationFilter.java @@ -185,7 +185,9 @@ private void addBuySellT(CalculationState state, Portfolio portfolio, PortfolioT long taxes = value(t.getUnitSum(Unit.Type.TAX).getAmount(), securityWeight); long securityAmount = value(t.getAmount(), securityWeight); - securityAmount = t.getType() == PortfolioTransaction.Type.BUY ? securityAmount - taxes : securityAmount + taxes; + securityAmount = (t.getType() == PortfolioTransaction.Type.BUY + || t.getType() == PortfolioTransaction.Type.COVER) ? + securityAmount - taxes : securityAmount + taxes; Account account = (Account) t.getCrossEntry().getCrossOwner(t); int accountWeight = state.getWeight(account); @@ -219,7 +221,8 @@ private void addBuySellT(CalculationState state, Portfolio portfolio, PortfolioT { AccountTransaction ta = new AccountTransaction(t.getDateTime(), t.getCurrencyCode(), accountAmount - commonAmount, null, - t.getType() == PortfolioTransaction.Type.BUY ? AccountTransaction.Type.REMOVAL + (t.getType() == PortfolioTransaction.Type.BUY + || t.getType() == PortfolioTransaction.Type.COVER) ? AccountTransaction.Type.REMOVAL : AccountTransaction.Type.DEPOSIT); state.asReadOnly(account).internalAddTransaction(ta); @@ -235,8 +238,9 @@ private void addBuySellT(CalculationState state, Portfolio portfolio, PortfolioT tp.setCurrencyCode(t.getCurrencyCode()); tp.setSecurity(t.getSecurity()); tp.setShares(value(t.getShares(), securityWeight - commonWeight)); - tp.setType(t.getType() == PortfolioTransaction.Type.BUY ? PortfolioTransaction.Type.DELIVERY_INBOUND - : PortfolioTransaction.Type.DELIVERY_OUTBOUND); + tp.setType((t.getType() == PortfolioTransaction.Type.BUY + || t.getType() == PortfolioTransaction.Type.COVER) ? + PortfolioTransaction.Type.DELIVERY_INBOUND : PortfolioTransaction.Type.DELIVERY_OUTBOUND); tp.setAmount(securityAmount - commonAmount); @@ -282,6 +286,7 @@ private void adaptAccountTransactions(CalculationState state, Account account) switch (t.getType()) { case SELL: + case SHORT: // only if the security is not included (and therefore // buy/sell transactions are handled by the // #adaptPortfolioTransactions method), create a deposit or @@ -292,6 +297,7 @@ private void adaptAccountTransactions(CalculationState state, Account account) break; case BUY: + case COVER: if (!state.isCategorized(t.getSecurity())) state.asReadOnly(account).internalAddTransaction(new AccountTransaction(t.getDateTime(), t.getCurrencyCode(), amount, null, AccountTransaction.Type.REMOVAL)); @@ -500,8 +506,10 @@ private void collectSecurityRelatedTx(CalculationState state, Account account) // ignore taxes when calculating performance of // securities case BUY: + case COVER: case TRANSFER_IN: case SELL: + case SHORT: case TRANSFER_OUT: case DEPOSIT: case REMOVAL: diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/filter/ClientSecurityFilter.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/filter/ClientSecurityFilter.java index 5b2c9cf0e9..d4a8fca191 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/filter/ClientSecurityFilter.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/filter/ClientSecurityFilter.java @@ -80,11 +80,13 @@ private void addPortfolioTransaction(Function getP switch (pair.getTransaction().getType()) { case BUY: + case COVER: case DELIVERY_INBOUND: getPortfolio.apply((Portfolio) pair.getOwner()).internalAddTransaction( convertToDelivery(pair.getTransaction(), PortfolioTransaction.Type.DELIVERY_INBOUND)); break; case SELL: + case SHORT: case DELIVERY_OUTBOUND: getPortfolio.apply((Portfolio) pair.getOwner()).internalAddTransaction( convertToDelivery(pair.getTransaction(), PortfolioTransaction.Type.DELIVERY_OUTBOUND)); @@ -132,7 +134,9 @@ private void addAccountTransaction(Function getAccount // ignore taxes break; case BUY: + case COVER: case SELL: + case SHORT: case DEPOSIT: case REMOVAL: case TRANSFER_IN: diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/filter/PortfolioClientFilter.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/filter/PortfolioClientFilter.java index f41119ce93..1dad632003 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/filter/PortfolioClientFilter.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/filter/PortfolioClientFilter.java @@ -109,6 +109,7 @@ private void adaptPortfolioTransactions(Portfolio portfolio, Map handled by portfolio transaction break; case TRANSFER_IN: diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/CapitalGainsCalculation.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/CapitalGainsCalculation.java index 247ed182d9..d6580def6b 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/CapitalGainsCalculation.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/CapitalGainsCalculation.java @@ -95,12 +95,14 @@ public void visit(CurrencyConverter converter, CalculationLineItem.TransactionIt switch (t.getType()) { case BUY: + case SHORT: case DELIVERY_INBOUND: fifo.add(new LineItem(t.getShares(), t.getDateTime().toLocalDate(), convertedGrossValue.getAmount(), txTrail, transactionItem)); break; case SELL: + case COVER: case DELIVERY_OUTBOUND: long value = convertedGrossValue.getAmount(); @@ -149,10 +151,20 @@ public void visit(CurrencyConverter converter, CalculationLineItem.TransactionIt .substract(startTrail); } - realizedCapitalGains.addCapitalGains(Money.of(termCurrency, end - start)); - realizedCapitalGains.addCapitalGainsTrail(txTrail // - .fraction(Money.of(termCurrency, end), soldShares, t.getShares()) - .substract(startTrail)); + if (PortfolioTransaction.Type.COVER == t.getType()) + { + realizedCapitalGains.addCapitalGains(Money.of(termCurrency, start - end)); + realizedCapitalGains.addCapitalGainsTrail(startTrail // + .fraction(Money.of(termCurrency, start), soldShares, t.getShares()) + .substract(txTrail)); + } + else + { + realizedCapitalGains.addCapitalGains(Money.of(termCurrency, end - start)); + realizedCapitalGains.addCapitalGainsTrail(txTrail // + .fraction(Money.of(termCurrency, end), soldShares, t.getShares()) + .substract(startTrail)); + } realizedCapitalGains.addForexCaptialGains(Money.of(termCurrency, forexGain)); realizedCapitalGains.addForexCapitalGainsTrail(forexGainTrail); @@ -347,8 +359,19 @@ public void finish(CurrencyConverter converter, List lineIt .substract(startTrail); } - unrealizedCapitalGains.addCapitalGains(Money.of(termCurrency, end - start)); - unrealizedCapitalGains.addCapitalGainsTrail(endTrail.substract(startTrail)); + if (PortfolioTransaction.Type.SHORT.toString().equals(startTrail.getLabel()) + || (Messages.LabelTrailWithoutTaxesAndFees.equals(startTrail.getLabel()) && + startTrail.getInputs().stream().map(TrailRecord::getLabel) + .allMatch(label->PortfolioTransaction.Type.SHORT.toString().equals(label)))) + { + unrealizedCapitalGains.addCapitalGains(Money.of(termCurrency, end + start)); + unrealizedCapitalGains.addCapitalGainsTrail(endTrail.add(startTrail)); + } + else + { + unrealizedCapitalGains.addCapitalGains(Money.of(termCurrency, end - start)); + unrealizedCapitalGains.addCapitalGainsTrail(endTrail.substract(startTrail)); + } unrealizedCapitalGains.addForexCaptialGains(Money.of(termCurrency, forexGain)); unrealizedCapitalGains.addForexCapitalGainsTrail(forexGainTrail); } diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/CostCalculation.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/CostCalculation.java index 4a1cf7b583..5919ca6073 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/CostCalculation.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/CostCalculation.java @@ -88,6 +88,7 @@ public void visit(CurrencyConverter converter, CalculationLineItem.TransactionIt switch (t.getType()) { case BUY: + case SHORT: case DELIVERY_INBOUND: long grossAmount = t.getMonetaryAmount(converter).getAmount(); long netAmount = t.getGrossValue(converter).getAmount(); @@ -104,6 +105,7 @@ public void visit(CurrencyConverter converter, CalculationLineItem.TransactionIt break; case SELL: + case COVER: case DELIVERY_OUTBOUND: long sold = t.getShares(); diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/DeltaCalculation.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/DeltaCalculation.java index 515256c7bc..f369cfe82e 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/DeltaCalculation.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/DeltaCalculation.java @@ -64,12 +64,14 @@ public void visit(CurrencyConverter converter, CalculationLineItem.TransactionIt switch (t.getType()) { case BUY: + case COVER: case DELIVERY_INBOUND: Money amount = t.getMonetaryAmount().with(converter.at(t.getDateTime())); delta.subtract(amount); cost.add(amount); break; case SELL: + case SHORT: case DELIVERY_OUTBOUND: delta.add(t.getMonetaryAmount().with(converter.at(t.getDateTime()))); break; diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/IRRCalculation.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/IRRCalculation.java index f6e7452d61..140709414a 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/IRRCalculation.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/IRRCalculation.java @@ -75,11 +75,13 @@ public void visit(CurrencyConverter converter, CalculationLineItem.TransactionIt switch (t.getType()) { case BUY: + case COVER: case DELIVERY_INBOUND: case TRANSFER_IN: values.add((-amount + taxes) / Values.Amount.divider()); break; case SELL: + case SHORT: case DELIVERY_OUTBOUND: case TRANSFER_OUT: values.add((amount + taxes) / Values.Amount.divider()); diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/SecurityPerformanceSnapshot.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/SecurityPerformanceSnapshot.java index 5075f15290..aa8e4fb23c 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/SecurityPerformanceSnapshot.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/SecurityPerformanceSnapshot.java @@ -123,6 +123,8 @@ private static void extractSecurityRelatedAccountTransactions(Account account, I break; case BUY: case SELL: + case COVER: + case SHORT: break; case DEPOSIT: case REMOVAL: diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/SharesHeldCalculation.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/SharesHeldCalculation.java index 22e305de4d..ff3bcf2663 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/SharesHeldCalculation.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/security/SharesHeldCalculation.java @@ -21,10 +21,12 @@ public void visit(CurrencyConverter converter, CalculationLineItem.TransactionIt switch (t.getType()) { case BUY: + case COVER: case DELIVERY_INBOUND: sharesHeld += t.getShares(); break; case SELL: + case SHORT: case DELIVERY_OUTBOUND: sharesHeld -= t.getShares(); break; diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/trades/Trade.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/trades/Trade.java index af0708e107..f61ca972de 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/trades/Trade.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/trades/Trade.java @@ -26,6 +26,7 @@ public class Trade implements Adaptable private LocalDateTime start; private LocalDateTime end; private final long shares; + private PortfolioTransaction.Type type; private List> transactions = new ArrayList<>(); @@ -34,11 +35,12 @@ public class Trade implements Adaptable private long holdingPeriod; private double irr; - public Trade(Security security, Portfolio portfolio, long shares) + public Trade(Security security, Portfolio portfolio, long shares, PortfolioTransaction.Type type) { this.security = security; this.shares = shares; this.portfolio = portfolio; + this.type = type; } /* package */ void calculate(CurrencyConverter converter) @@ -77,6 +79,12 @@ public Trade(Security security, Portfolio portfolio, long shares) .sum() / (double) shares); } + if (PortfolioTransaction.Type.SHORT == this.type) + { + this.entryValue = Money.of(this.entryValue.getCurrencyCode(), -this.entryValue.getAmount()); + this.exitValue = Money.of(this.exitValue.getCurrencyCode(), -this.exitValue.getAmount()); + } + calculateIRR(converter); } @@ -104,6 +112,11 @@ private void calculateIRR(CurrencyConverter converter) } this.irr = IRR.calculate(dates, values); + + if (PortfolioTransaction.Type.SHORT == this.type) + { + this.irr = -this.irr; + } } public Security getSecurity() @@ -141,6 +154,17 @@ public long getShares() return shares; } + public PortfolioTransaction.Type getType() + { + return type; + } + + public Trade setType(PortfolioTransaction.Type type) + { + this.type = type; + return this; + } + public List> getTransactions() { return transactions; diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/trades/TradeCollector.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/trades/TradeCollector.java index 18472e62c6..1d81f64829 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/trades/TradeCollector.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/snapshot/trades/TradeCollector.java @@ -54,11 +54,13 @@ public List collect(Security security) throws TradeCollectorException switch (t.getType()) { case BUY: + case SHORT: case DELIVERY_INBOUND: openTransactions.computeIfAbsent(portfolio, p -> new ArrayList<>()).add(pair); break; case SELL: + case COVER: case DELIVERY_OUTBOUND: trades.add(createNewTradeFromSell(openTransactions, pair)); break; @@ -88,7 +90,7 @@ public List collect(Security security) throws TradeCollectorException long shares = position.stream().mapToLong(p -> p.getTransaction().getShares()).sum(); - Trade newTrade = new Trade(security, entry.getKey(), shares); + Trade newTrade = new Trade(security, entry.getKey(), shares, position.get(0).getTransaction().getType()); newTrade.setStart(position.get(0).getTransaction().getDateTime()); newTrade.getTransactions().addAll(position); @@ -104,7 +106,7 @@ private Trade createNewTradeFromSell(Map pair) throws TradeCollectorException { Trade newTrade = new Trade(pair.getTransaction().getSecurity(), (Portfolio) pair.getOwner(), - pair.getTransaction().getShares()); + pair.getTransaction().getShares(), pair.getTransaction().getType()); List> open = openTransactions.get(pair.getOwner()); @@ -119,6 +121,8 @@ private Trade createNewTradeFromSell(Map