package name.abuchen.portfolio.datatransfer.csv;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.csv.CSVStrategy;
import name.abuchen.portfolio.Messages;
import name.abuchen.portfolio.model.Account;
import name.abuchen.portfolio.model.AccountTransaction;
import name.abuchen.portfolio.model.Portfolio;
import name.abuchen.portfolio.model.PortfolioTransaction;
import name.abuchen.portfolio.model.Security;
import name.abuchen.portfolio.model.SecurityPrice;
import name.abuchen.portfolio.model.Transaction;
import name.abuchen.portfolio.model.Transaction.Unit;
import name.abuchen.portfolio.money.Money;
import name.abuchen.portfolio.money.Values;
/* not thread safe */
public class CSVExporter
{
/* package */static final CSVStrategy STRATEGY = new CSVStrategy(';', '"', CSVStrategy.COMMENTS_DISABLED,
CSVStrategy.ESCAPE_DISABLED, false, false, false, false);
public void exportAccountTransactions(File file, Account account) throws IOException
{
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))
{
CSVPrinter printer = new CSVPrinter(writer);
printer.setStrategy(STRATEGY);
printer.println(new String[] { Messages.CSVColumn_Date, //
Messages.CSVColumn_Type, //
Messages.CSVColumn_Value, //
Messages.CSVColumn_TransactionCurrency, //
Messages.CSVColumn_Taxes, //
Messages.CSVColumn_Shares, //
Messages.CSVColumn_ISIN, //
Messages.CSVColumn_WKN, //
Messages.CSVColumn_TickerSymbol, //
Messages.CSVColumn_SecurityName, //
Messages.CSVColumn_Note });
for (AccountTransaction t : account.getTransactions())
{
printer.print(t.getDate().toString());
printer.print(t.getType().toString());
printer.print(Values.Amount.format(t.getType().isDebit() ? -t.getAmount() : t.getAmount()));
printer.print(t.getCurrencyCode());
printer.print(t.getType() == AccountTransaction.Type.DIVIDENDS
? Values.Amount.format(t.getUnitSum(Unit.Type.TAX).getAmount()) : ""); //$NON-NLS-1$
printer.print(t.getShares() != 0 ? Values.Share.format(t.getShares()) : ""); //$NON-NLS-1$
printSecurityInfo(printer, t);
printer.print(escapeNull(t.getNote()));
printer.println();
}
}
}
public void exportAccountTransactions(File directory, List<Account> accounts) throws IOException
{
for (Account account : accounts)
exportAccountTransactions(new File(directory, account.getName() + ".csv"), account); //$NON-NLS-1$
}
public void exportPortfolioTransactions(File file, Portfolio portfolio) throws IOException
{
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))
{
CSVPrinter printer = new CSVPrinter(writer);
printer.setStrategy(STRATEGY);
printer.println(new String[] { Messages.CSVColumn_Date, //
Messages.CSVColumn_Type, //
Messages.CSVColumn_Value, //
Messages.CSVColumn_TransactionCurrency, //
Messages.CSVColumn_GrossAmount, //
Messages.CSVColumn_CurrencyGrossAmount, //
Messages.CSVColumn_ExchangeRate, //
Messages.CSVColumn_Fees, //
Messages.CSVColumn_Taxes, //
Messages.CSVColumn_Shares, //
Messages.CSVColumn_ISIN, //
Messages.CSVColumn_WKN, //
Messages.CSVColumn_TickerSymbol, //
Messages.CSVColumn_SecurityName, //
Messages.CSVColumn_Note });
for (PortfolioTransaction t : portfolio.getTransactions())
{
printer.print(t.getDate().toString());
printer.print(t.getType().toString());
printer.print(Values.Amount.format(t.getType().isLiquidation() ? -t.getAmount() : t.getAmount()));
printer.print(t.getCurrencyCode());
// gross amount
Optional<Unit> grossAmount = t.getUnit(Unit.Type.GROSS_VALUE);
if (grossAmount.isPresent())
{
Money forex = grossAmount.get().getForex();
printer.print(Values.Amount.format(forex.getAmount()));
printer.print(forex.getCurrencyCode());
printer.print(Values.ExchangeRate.format(grossAmount.get().getExchangeRate()));
}
else
{
printer.print(""); //$NON-NLS-1$
printer.print(""); //$NON-NLS-1$
printer.print(""); //$NON-NLS-1$
}
printer.print(Values.Amount.format(t.getUnitSum(Unit.Type.FEE).getAmount()));
printer.print(Values.Amount.format(t.getUnitSum(Unit.Type.TAX).getAmount()));
printer.print(Values.Share.format(t.getShares()));
printSecurityInfo(printer, t);
printer.print(escapeNull(t.getNote()));
printer.println();
}
}
}
private void printSecurityInfo(CSVPrinter printer, Transaction t)
{
Security security = t.getSecurity();
if (security != null)
{
printer.print(escapeNull(security.getIsin()));
printer.print(escapeNull(security.getWkn()));
printer.print(escapeNull(security.getTickerSymbol()));
printer.print(escapeNull(security.getName()));
}
else
{
printer.print(""); //$NON-NLS-1$
printer.print(""); //$NON-NLS-1$
printer.print(""); //$NON-NLS-1$
printer.print(""); //$NON-NLS-1$
}
}
public void exportPortfolioTransactions(File directory, List<Portfolio> portfolios) throws IOException
{
for (Portfolio portfolio : portfolios)
exportPortfolioTransactions(new File(directory, portfolio.getName() + ".csv"), portfolio); //$NON-NLS-1$
}
public void exportSecurityMasterData(File file, List<Security> securities) throws IOException
{
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))
{
CSVPrinter printer = new CSVPrinter(writer);
printer.setStrategy(STRATEGY);
printer.println(new String[] { Messages.CSVColumn_ISIN, //
Messages.CSVColumn_WKN, //
Messages.CSVColumn_TickerSymbol, //
Messages.CSVColumn_SecurityName, //
Messages.CSVColumn_Currency, Messages.CSVColumn_Note });
for (Security s : securities)
{
printer.print(escapeNull(s.getIsin()));
printer.print(escapeNull(s.getWkn()));
printer.print(escapeNull(s.getTickerSymbol()));
printer.print(escapeNull(s.getName()));
printer.print(escapeNull(s.getCurrencyCode()));
printer.print(escapeNull(s.getNote()));
printer.println();
}
}
}
public void exportSecurityPrices(File file, Security security) throws IOException
{
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))
{
CSVPrinter printer = new CSVPrinter(writer);
printer.setStrategy(STRATEGY);
printer.println(new String[] { Messages.CSVColumn_Date, Messages.CSVColumn_Quote });
for (SecurityPrice p : security.getPrices())
{
printer.print(p.getTime().toString());
printer.print(Values.Quote.format(p.getValue()));
printer.println();
}
}
}
public void exportSecurityPrices(File directory, List<Security> securities) throws IOException
{
for (Security security : securities)
exportSecurityPrices(new File(directory, security.getIsin() + ".csv"), security); //$NON-NLS-1$
}
public void exportMergedSecurityPrices(File file, List<Security> securities) throws IOException
{
// prepare: (a) find earliest date (b) ignore securities w/o quotes
LocalDate earliestDate = null;
List<Security> export = new ArrayList<Security>(securities.size());
for (Security s : securities)
{
List<SecurityPrice> prices = s.getPrices();
if (!prices.isEmpty())
{
export.add(s);
LocalDate quoteDate = prices.get(0).getTime();
if (earliestDate == null)
earliestDate = quoteDate;
else
earliestDate = earliestDate.isAfter(quoteDate) ? quoteDate : earliestDate;
}
}
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))
{
CSVPrinter printer = new CSVPrinter(writer);
printer.setStrategy(STRATEGY);
// write header
printer.print(Messages.CSVColumn_Date);
for (Security security : export)
printer.print(security.getExternalIdentifier());
printer.println();
// stop if no securities exist
if (earliestDate == null)
return;
// write quotes
LocalDate pointer = earliestDate;
LocalDate today = LocalDate.now();
while (pointer.compareTo(today) <= 0)
{
// check if any quotes exist for that day at all
int[] indices = new int[export.size()];
int ii = 0;
for (Security security : export)
{
SecurityPrice p = new SecurityPrice(pointer, 0);
indices[ii] = Collections.binarySearch(security.getPrices(), p);
ii++;
}
boolean hasValues = false;
for (ii = 0; ii < indices.length && !hasValues; ii++)
hasValues = indices[ii] >= 0;
if (hasValues)
{
printer.print(pointer.toString());
for (ii = 0; ii < indices.length; ii++)
{
if (indices[ii] < 0)
printer.print(""); //$NON-NLS-1$
else
printer.print(Values.Quote.format(export.get(ii).getPrices().get(indices[ii]).getValue()));
}
printer.println();
}
pointer = pointer.plusDays(1);
}
}
}
/* package */static String escapeNull(String value)
{
return value != null ? value : ""; //$NON-NLS-1$
}
}