public PortfolioShare addOrUpdateShareForAmount( Stock stock, BigDecimal unitAmount, Date currentDate, MonitorLevel monitorLevel, Currency transactionCurrency) throws InvalidQuantityException, InvalidAlgorithmParameterException { try { Quotations quotations = QuotationsFactories.getFactory() .getQuotationsInstance( stock, currentDate, true, transactionCurrency, ValidityFilter.CLOSE); BigDecimal valueAtDate = quotations.getClosestCloseForDate(currentDate); BigDecimal quantity = unitAmount.divide(valueAtDate, 10, BigDecimal.ROUND_HALF_EVEN); return addOrUpdateShare( stock, quantity, currentDate, valueAtDate, monitorLevel, transactionCurrency, TransactionType.AIN); } catch (NoQuotationsException e) { throw new InvalidAlgorithmParameterException(e); } }
public PortfolioShare addOrUpdateShareForQuantity( Stock stock, BigDecimal quantity, Date currentDate, MonitorLevel monitorLevel, Currency transactionCurrency) throws InvalidQuantityException, InvalidAlgorithmParameterException, NoQuotationsException { BigDecimal valueAtDate = BigDecimal.ZERO; if (quantity.compareTo(BigDecimal.ZERO) > 0) { Quotations quotations = QuotationsFactories.getFactory() .getQuotationsInstance( stock, currentDate, true, transactionCurrency, ValidityFilter.CLOSE); valueAtDate = quotations.getClosestCloseForDate(currentDate); } return addOrUpdateShare( stock, quantity, currentDate, valueAtDate, monitorLevel, transactionCurrency, TransactionType.AIN); }
private TuningResDTO buildResOnValidPeriods( List<PeriodRatingDTO> periods, SortedMap<Date, Number> mapFromQuotationsClose, Quotations quotations, Stock stock, Date startDate, Date endDate, String analyseName, SortedMap<Date, double[]> calcOutput, String evtDefInfo, Observer observer) throws IOException, InvalidAlgorithmParameterException { String trendFile = "noOutputAvailable"; String chartFile = "noChartAvailable"; Double trendFollowProfit = 1.00; Boolean generateBuySellCsv = MainPMScmd.getMyPrefs().getBoolean("autoporfolio.generatecsv", true); Boolean generateSmaCmpOutChart = MainPMScmd.getMyPrefs().getBoolean("autoporfolio.generatepng", true); // Init output file String endDateStamp = ""; if (MainPMScmd.getMyPrefs().getBoolean("perceptron.stampoutput", false)) { endDateStamp = new SimpleDateFormat("yyyyMMdd").format(endDate); } BufferedWriter csvWriter = null; String fileName = "noOutputAvailable"; if (generateBuySellCsv) { fileName = "autoPortfolioLogs" + File.separator + analyseName + stock.getSymbol() + "_" + evtDefInfo + "_BuyAndSellRecords" + endDateStamp + ".csv"; File file = new File(System.getProperty("installdir") + File.separator + fileName); file.delete(); csvWriter = new BufferedWriter(new FileWriter(file)); csvWriter.write("Date, Quotations, Bearish, Bullish, Output \n"); } // Other init BigDecimal lastClose = (BigDecimal) mapFromQuotationsClose.get(mapFromQuotationsClose.lastKey()); Double csvDispFactor = 1.00; SortedMap<Date, Double> buySerie = new TreeMap<Date, Double>(); SortedMap<Date, Double> sellSerie = new TreeMap<Date, Double>(); int lastRealisedBullIdx = -1; PeriodRatingDTO previousPeriod = null; for (PeriodRatingDTO currentPeriod : periods) { // Exports if (generateBuySellCsv || generateSmaCmpOutChart) { // csv gaps SortedMap<Date, Number> gapQuotationMap; if (generateBuySellCsv && previousPeriod != null && (gapQuotationMap = mapFromQuotationsClose.subMap( previousPeriod.getTo(), currentPeriod.getFrom())) .size() > 1) { for (Date gapDate : gapQuotationMap.keySet()) { Double closeForGapDate = gapQuotationMap.get(gapDate).doubleValue(); double[] output = calcOutput.get(gapDate); exportLine( generateBuySellCsv, false, csvDispFactor, csvWriter, buySerie, sellSerie, gapDate, closeForGapDate, EventType.NONE, output); } } previousPeriod = currentPeriod; // export period SortedMap<Date, Number> periodQuotationMap = mapFromQuotationsClose.subMap(currentPeriod.getFrom(), currentPeriod.getTo()); EventType periodTrend = EventType.valueOf(currentPeriod.getTrend()); for (Date periodInnerDate : periodQuotationMap.keySet()) { Double closeForInnerDate = periodQuotationMap.get(periodInnerDate).doubleValue(); double[] output = calcOutput.get(periodInnerDate); exportLine( generateBuySellCsv, generateSmaCmpOutChart, csvDispFactor, csvWriter, buySerie, sellSerie, periodInnerDate, closeForInnerDate, periodTrend, output); } } // Calculate profit if (EventType.valueOf(currentPeriod.getTrend()).equals(EventType.BULLISH) && currentPeriod.isRealised()) { lastRealisedBullIdx = periods.indexOf(currentPeriod); Double followPriceRateOfChange = currentPeriod.getPriceRateOfChange(); if (followPriceRateOfChange.isNaN() || followPriceRateOfChange.isInfinite()) { String message = "Error calculating followPriceRateOfChange for " + stock.getFriendlyName() + " : " + currentPeriod; LOGGER.error(message); throw new InvalidAlgorithmParameterException(message); } // Follow Profit if (LOGGER.isDebugEnabled()) LOGGER.debug( "Buy : Compound profit is " + trendFollowProfit + " at " + currentPeriod.getFrom() + ". " + "First price is " + currentPeriod.getPriceAtFrom() + " at " + currentPeriod.getFrom() + ". " + "Last price is " + currentPeriod.getPriceAtTo() + " at " + currentPeriod.getTo() + ". "); trendFollowProfit = trendFollowProfit * (followPriceRateOfChange + 1); if (LOGGER.isDebugEnabled()) LOGGER.debug( "New Compound at " + currentPeriod.getTo() + " : prevTotProfit*(" + followPriceRateOfChange + "+1)=" + trendFollowProfit); } else if (EventType.valueOf(currentPeriod.getTrend()).equals(EventType.BEARISH)) { // Follow Profit if (LOGGER.isDebugEnabled()) LOGGER.debug( "Sell : Compound profit is " + trendFollowProfit + " at " + currentPeriod.getFrom() + ". " + "Period " + currentPeriod + " : followPriceRateOfChange for period " + currentPeriod.getPriceRateOfChange()); } else if (EventType.valueOf(currentPeriod.getTrend()).equals(EventType.BULLISH) && !currentPeriod.isRealised()) { // Nothing if (LOGGER.isDebugEnabled()) LOGGER.debug("Unrealised bull period " + currentPeriod); } } // End for over periods // Finalise Csv file if (generateBuySellCsv) { csvWriter.close(); trendFile = fileName; } // Finalise PNG Chart if (generateSmaCmpOutChart) { try { String chartFileName = "autoPortfolioLogs" + File.separator + analyseName + stock.getSymbol() + "_" + evtDefInfo + "_OutChart" + endDateStamp + ".png"; generateOutChart(chartFileName, calcOutput, quotations, buySerie, sellSerie); observer.update( null, new ObserverMsg(stock, ObserverMsg.ObsKey.PRGSMSG, "Output images generated ...")); chartFile = chartFileName; } catch (NotEnoughDataException e) { LOGGER.warn("Can't generate chart for " + stock, e, true); chartFile = "noChartAvailable"; } catch (Exception e) { LOGGER.error("Can't generate chart for " + stock, e); chartFile = "noChartAvailable"; } } else { chartFile = "noChartAvailable"; } observer.update( null, new ObserverMsg(stock, ObserverMsg.ObsKey.PRGSMSG, "Output file generated ...")); // Output boundaries Date outputFirstKey = startDate; Date outputLastKey = endDate; if (!calcOutput.isEmpty()) { outputFirstKey = calcOutput.firstKey(); outputLastKey = calcOutput.lastKey(); } // Finalise profits trendFollowProfit = trendFollowProfit - 1; if (!periods.isEmpty()) { PeriodRatingDTO firstPeriod = periods.get(0); PeriodRatingDTO lastPeriod = periods.get(periods.size() - 1); if (lastRealisedBullIdx != -1) { Date firstBullFrom = firstPeriod.getFrom(); BigDecimal firstBullStartPrice = quotations.getClosestCloseForDate(firstBullFrom); PeriodRatingDTO lastBullPeriod = periods.get(lastRealisedBullIdx); Date lastBullTo = lastBullPeriod.getTo(); BigDecimal lastBullStartPrice = quotations.getClosestCloseForDate(lastBullTo); LOGGER.info( "Trend following compounded profit calculation is first Close " + firstBullStartPrice + " at " + firstBullFrom + " and last Close " + lastBullStartPrice + " at " + lastBullTo + " : " + trendFollowProfit); } else { LOGGER.info( "Trend following profit calculation is unknown (No bullish periods were detected or no trend change detected)"); } // Buy and hold profit BigDecimal firstClose = quotations.getClosestCloseForDate(firstPeriod.getFrom()); Double buyAndHoldProfit = (firstClose.compareTo(BigDecimal.ZERO) != 0) ? lastClose .subtract(firstClose) .divide(firstClose, 10, BigDecimal.ROUND_HALF_EVEN) .doubleValue() : Double.NaN; LOGGER.info( "Buy and hold profit calculation is first Close " + firstClose + " at " + firstPeriod.getFrom() + " and last Close " + lastClose + " at " + endDate + " : (" + lastClose + "-" + firstClose + ")/" + firstClose + "=" + buyAndHoldProfit); return new TuningResDTO( periods, trendFile, chartFile, lastPeriod.getTrend(), trendFollowProfit, Double.NaN, buyAndHoldProfit, outputFirstKey, outputLastKey); } LOGGER.info("No event detected"); return new TuningResDTO( periods, trendFile, chartFile, EventType.NONE.toString(), Double.NaN, Double.NaN, Double.NaN, outputFirstKey, outputLastKey); }
private String extractTransactionLog( Date startDate, Date endDate, Currency targetCurrency, Date cpgPeriodStart, Date cpgPeriodEnd, BigDecimal transactionFee, BigDecimal exchangeFee) throws Throwable { try { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); SortedSet<TransactionElement> sortedByStock = transactionsSortedByStock(startDate, endDate); CurrencyConverter currencyConverter = PortfolioMgr.getInstance().getCurrencyConverter(); String messagePortCurrency = "Transactions (" + targetCurrency + " - " + dateFormat.format(cpgPeriodStart) + " -> " + dateFormat.format(cpgPeriodEnd) + " - transaction fee " + transactionFee + " - exchange fee " + exchangeFee + ") in " + getName() + " :\n" + "stock, date, transaction price, quantity in, amount in, quantity out, amount out, realised capital gain, currency, close price"; String messageNoConvertion = "Transactions (Original currencies - " + dateFormat.format(cpgPeriodStart) + " -> " + dateFormat.format(cpgPeriodEnd) + " - transaction fee " + transactionFee + ") in " + getName() + " :\n" + "stock, date, transaction price, quantity in, amount in, quantity out, amount out, realised capital gain, currency, close price, exchange rate"; Stock currentStock = null; // Transactions Map<Stock, BigDecimal[]> pss = new HashMap<Stock, BigDecimal[]>(); for (TransactionElement te : sortedByStock) { if (currentStock == null || !currentStock.equals(te.getStock())) { // init stock currentStock = te.getStock(); // pss.add(getShareForStock(currentStock)); pss.put(currentStock, new BigDecimal[] {BigDecimal.ZERO, BigDecimal.ZERO}); } BigDecimal closePrice = BigDecimal.ZERO; BigDecimal convertedClosePrice = BigDecimal.ZERO; BigDecimal convertionRate = BigDecimal.ONE; try { Quotations quotations = QuotationsFactories.getFactory() .getQuotationsInstance( currentStock, te.getDate(), true, currentStock.getMarketValuation().getCurrency(), ValidityFilter.CLOSE); closePrice = quotations.getClosestCloseForDate(te.getDate()); Quotations convertedQuotations = QuotationsFactories.getFactory() .getQuotationsInstance( currentStock, te.getDate(), true, targetCurrency, ValidityFilter.CLOSE); convertedClosePrice = convertedQuotations.getClosestCloseForDate(te.getDate()); convertionRate = currencyConverter.convert( currentStock.getMarketValuation(), targetCurrency, BigDecimal.ONE, te.getDate()); } catch (Exception e) { LOGGER.warn("Error loading stock prices for " + currentStock + " : " + e); } boolean buy = te.getQuantity().compareTo(BigDecimal.ZERO) > 0; BigDecimal transPrice = applyFee(buy, te.getPrice(), transactionFee); BigDecimal convertedTransPrice = applyFee( buy, currencyConverter.convert( te.getCurrency(), targetCurrency, transPrice, te.getDate()), exchangeFee); BigDecimal transAmount = transPrice.multiply(te.getQuantity()).setScale(2, BigDecimal.ROUND_HALF_EVEN); BigDecimal convertedTransAmount = convertedTransPrice.multiply(te.getQuantity()).setScale(2, BigDecimal.ROUND_HALF_EVEN); if (buy) { messagePortCurrency = messagePortCurrency + "\n" + currentStock.getFriendlyName() + "," + dateFormat.format(te.getDate()) + "," + convertedTransPrice + "," + te.getQuantity() + "," + convertedTransAmount + ",,,0.00," + targetCurrency + "," + convertedClosePrice; messageNoConvertion = messageNoConvertion + "\n" + currentStock.getFriendlyName() + "," + dateFormat.format(te.getDate()) + "," + transPrice + "," + te.getQuantity() + "," + transAmount + ",,,0.00," + te.getCurrency() + "," + closePrice + "," + convertionRate; } else { // sell Boolean isSellWithinCpgPeriod = te.getDate().compareTo(cpgPeriodStart) >= 0 && te.getDate().compareTo(cpgPeriodEnd) <= 0; BigDecimal cpgPortCurrency = BigDecimal.ZERO; if (isSellWithinCpgPeriod) { BigDecimal priceAvgBuyPortCur = applyFee( true, applyFee( true, this.getShareForStock(currentStock) .getPriceAvgBuy(startDate, te.getDate(), targetCurrency), transactionFee), exchangeFee); cpgPortCurrency = te.getQuantity() .multiply(priceAvgBuyPortCur) .setScale(2, BigDecimal.ROUND_HALF_EVEN) .subtract(convertedTransAmount); pss.get(currentStock)[0] = pss.get(currentStock)[0].add(cpgPortCurrency); } messagePortCurrency = messagePortCurrency + "\n" + currentStock.getFriendlyName() + "," + dateFormat.format(te.getDate()) + "," + convertedTransPrice + ",,," + te.getQuantity() + "," + convertedTransAmount + "," + cpgPortCurrency + "," + targetCurrency + "," + convertedClosePrice; BigDecimal cpgNoConv = BigDecimal.ZERO; if (isSellWithinCpgPeriod) { BigDecimal priceAvgBuyNoConv = applyFee( true, this.getShareForStock(currentStock) .getPriceAvgBuy( startDate, te.getDate(), currentStock.getMarketValuation().getCurrency()), transactionFee); cpgNoConv = te.getQuantity() .multiply(priceAvgBuyNoConv) .setScale(2, BigDecimal.ROUND_HALF_EVEN) .subtract(transAmount); pss.get(currentStock)[1] = pss.get(currentStock)[1].add(cpgNoConv); } messageNoConvertion = messageNoConvertion + "\n" + currentStock.getFriendlyName() + "," + dateFormat.format(te.getDate()) + "," + transPrice + ",,," + te.getQuantity() + "," + transAmount + "," + cpgNoConv + "," + te.getCurrency() + "," + closePrice + "," + convertionRate; } } messagePortCurrency = messagePortCurrency + "\n\n" + "Totals (" + targetCurrency + " - " + dateFormat.format(cpgPeriodStart) + " -> " + dateFormat.format(cpgPeriodEnd) + " - transaction fee " + transactionFee + " - exchange fee " + exchangeFee + ") in " + getName() + " :\n" + "stock, on the, average price, quantity, invested (in-out), value, realised capital gain, potential capital gain, currency, last close"; messageNoConvertion = messageNoConvertion + "\n\n" + "Totals (Original currencies - " + dateFormat.format(cpgPeriodStart) + " -> " + dateFormat.format(cpgPeriodEnd) + " - transaction fee " + transactionFee + ") in " + getName() + " :\n" + "stock, on the, average price, quantity, invested (in-out), value, realised capital gain, potential capital gain, currency, last close, last exchange rate"; // Totals for (Stock stock : pss.keySet()) { PortfolioShare ps = getShareForStock(stock); try { Quotations quotations = QuotationsFactories.getFactory() .getQuotationsInstance( stock, endDate, true, stock.getMarketValuation().getCurrency(), ValidityFilter.CLOSE); BigDecimal lastClosePrice = quotations.getClosestCloseForDate(endDate); Quotations convertedQuotations = QuotationsFactories.getFactory() .getQuotationsInstance( stock, endDate, true, targetCurrency, ValidityFilter.CLOSE); BigDecimal lastConvertedClosePrice = convertedQuotations.getClosestCloseForDate(endDate); BigDecimal lastConvertionRate = currencyConverter.convert( stock.getMarketValuation(), targetCurrency, BigDecimal.ONE, endDate); BigDecimal quantity = getQuantityFor(ps, startDate, endDate); boolean isQuantityPositiveAtEndPeriod = ps.getQuantity(startDate, cpgPeriodEnd).compareTo(BigDecimal.ZERO) > 0; BigDecimal invPortCur = applyFee( true, applyFee( true, ps.getCashin(startDate, endDate, targetCurrency), transactionFee), exchangeFee) .subtract( applyFee( false, applyFee( false, ps.getCashout(startDate, endDate, targetCurrency), transactionFee), exchangeFee)); BigDecimal valuePortCur = applyFee( false, applyFee(false, ps.getValue(startDate, endDate, targetCurrency), transactionFee), exchangeFee); messagePortCurrency = messagePortCurrency + "\n" + ps.getStock().getFriendlyName() + ", " + dateFormat.format(endDate) + ", " + applyFee( true, applyFee( true, ps.getPriceAvgBuy(startDate, endDate, targetCurrency), transactionFee), exchangeFee) + ", " + quantity + "," + invPortCur + ", " + valuePortCur + ", " + pss.get(stock)[0] + "," + (isQuantityPositiveAtEndPeriod ? valuePortCur.subtract(invPortCur) : BigDecimal.ZERO) + ", " + targetCurrency + ", " + lastConvertedClosePrice; BigDecimal invNoConv = applyFee( true, ps.getCashin(startDate, endDate, stock.getMarketValuation().getCurrency()), transactionFee) .subtract( applyFee( false, ps.getCashout( startDate, endDate, stock.getMarketValuation().getCurrency()), transactionFee)); BigDecimal valueNoConv = applyFee( false, ps.getValue(startDate, endDate, stock.getMarketValuation().getCurrency()), transactionFee); messageNoConvertion = messageNoConvertion + "\n" + ps.getStock().getFriendlyName() + ", " + dateFormat.format(endDate) + ", " + applyFee( true, ps.getPriceAvgBuy( startDate, endDate, stock.getMarketValuation().getCurrency()), transactionFee) + ", " + quantity + "," + invNoConv + ", " + valueNoConv + ", " + pss.get(stock)[1] + "," + (isQuantityPositiveAtEndPeriod ? valueNoConv.subtract(invNoConv) : BigDecimal.ZERO) + ", " + stock.getMarketValuation().getCurrency() + ", " + lastClosePrice + ", " + lastConvertionRate; } catch (Exception e) { LOGGER.warn("Error loading last stock prices for " + stock + " : " + e); } } return messagePortCurrency + "\n\n" + messageNoConvertion; } catch (Throwable e) { throw e; } }