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); }
@Override public Number[] targetShareData( SlidingPortfolioShare portfolioShare, Quotations stockQuotations, MInteger startDateQuotationIndex, MInteger endDateQuotationIndex) { if (arbitraryStartDate != null && arbitraryEndDate != null) { Date startDate = getStartDate(stockQuotations); startDateQuotationIndex.value = stockQuotations.getClosestIndexBeforeOrAtDateOrIndexZero(0, startDate); Date endDate = getEndDate(stockQuotations); endDateQuotationIndex.value = stockQuotations.getClosestIndexBeforeOrAtDateOrIndexZero( startDateQuotationIndex.value, endDate); BigDecimal investPerUnit = BigDecimal.ZERO; if (includeMoneyOut) { investPerUnit = portfolioShare.getPriceUnitCost( portfolioShare.calcSlidingEndDate(), portfolioShare.getTransactionCurrency()); } else { investPerUnit = portfolioShare.getPriceAvgBuy( portfolioShare.calcSlidingEndDate(), portfolioShare.getTransactionCurrency()); } return relativeCloses( stockQuotations, startDateQuotationIndex, endDateQuotationIndex, investPerUnit); } else { throw new InvalidParameterException(); } }
@Override protected double[][] getInputData(Quotations quotations) { double[] closeValues = quotations.getCloseValues(); double inLow[] = quotations.getLowValues(); double inHigh[] = quotations.getHighValues(); double[][] ret = new double[3][Math.max(Math.max(closeValues.length, inHigh.length), inLow.length)]; ret[0] = closeValues; ret[1] = inLow; ret[2] = inHigh; return ret; }
private BigDecimal[] relativeCloses( Quotations stockQuotations, MInteger startDateQuotationIndex, MInteger endDateQuotationIndex, BigDecimal unitCost) { ArrayList<BigDecimal> retA = new ArrayList<BigDecimal>(); for (int i = startDateQuotationIndex.value; i <= endDateQuotationIndex.value; i++) { BigDecimal value = BigDecimal.ZERO; if (unitCost.compareTo(BigDecimal.ZERO) != 0) { BigDecimal close = stockQuotations.get(i).getClose(); value = (close.subtract(unitCost).divide(unitCost.abs(), 10, BigDecimal.ROUND_HALF_EVEN)); } retA.add(value); } return retA.toArray(new BigDecimal[0]); }
@Override protected FormulatRes eventFormulaCalculation(QuotationUnit qU, Integer quotationIdx) throws InvalidAlgorithmParameterException { FormulatRes res = new FormulatRes(getEventDefinition()); res.setCurrentDate(qU.getDate()); int chaikinIdx = getIndicatorIndexFromQuotationIndex(this.chaikinOscillator, quotationIdx); double[] chaikinLookBackP = Arrays.copyOfRange( this.chaikinOscillator.getChaikinOsc(), chaikinIdx - getDaysSpan(), chaikinIdx); double[] quotationLookBackP = Arrays.copyOfRange( quotationsCopy.getCloseValues(), quotationIdx - getDaysSpan(), quotationIdx); int smaIndex = getIndicatorIndexFromQuotationIndex(this.sma, quotationIdx); double[] quotationLookBackPThresh = Arrays.copyOfRange(this.sma.getSma(), smaIndex - getDaysSpan(), smaIndex); double[] chaikinThreshCurve = new double[chaikinLookBackP.length]; { Boolean isPriceDown = lowerLow(quotationLookBackP, quotationLookBackPThresh); Boolean isChaikinUp = higherLow(chaikinLookBackP, chaikinThreshCurve); res.setBullishCrossOver(isPriceDown && isChaikinUp); if (res.getBullishCrossOver()) return res; } { Boolean isPriceUp = higherHigh(quotationLookBackP, quotationLookBackPThresh); Boolean isChaikinDown = lowerHigh(chaikinLookBackP, chaikinThreshCurve); res.setBearishCrossBellow(isPriceUp && isChaikinDown); return res; } }
private void generateOutChart( String chartFileName, SortedMap<Date, double[]> calcOutput, Quotations quotations, SortedMap<Date, Double> buySerie, SortedMap<Date, Double> sellSerie) throws NotEnoughDataException, IOException, FileNotFoundException { ChartGenerator chartGenerator = new ChartGenerator("Premium Markets output V. Lag fixed SMA 100"); // line series SortedMap<DataSetBarDescr, SortedMap<Date, Double>> lineSeries = new TreeMap<DataSetBarDescr, SortedMap<Date, Double>>(); SortedMap<Date, Double> quotes = QuotationsFactories.getFactory() .buildSMapFromQuotationsClose( quotations, quotations.getFirstDateShiftedIdx(), quotations.getLastDateIdx()); lineSeries.put(new DataSetBarDescr(2, "historical prices", Color.BLUE.darker()), quotes); SortedMap<Date, Double> fixedSma100; try { TalibSmaSmoother smaSmoother = new TalibSmaSmoother(100); fixedSma100 = smaSmoother.sSmooth(quotes, true); lineSeries.put( new DataSetBarDescr(1, "Artificial no lag sma 100", Color.ORANGE, 3f), fixedSma100); } catch (NegativeArraySizeException e) { throw new NotEnoughDataException( quotations.getStock(), "Generate chart : No enough data to calculate sma for " + quotations.getStock(), e); } SortedMap<Date, Double> sCalcOutput = sCalcOutput(calcOutput); lineSeries.put( new DataSetBarDescr(0, "Premium Markets trend line", new Color(46, 236, 236), 3f), sCalcOutput); // bar series SortedMap<DataSetBarDescr, SortedMap<Date, Double>> barSeries = new TreeMap<DataSetBarDescr, SortedMap<Date, Double>>(); /// sma b&s SDiscretDerivator derivator = new SlopeDerivator(1, 0, true, true); SortedMap<Date, Double> sma100Drv = derivator.sDerivateDiscret(fixedSma100); SortedMap<Date, Double> fixedSmaBearish = new TreeMap<Date, Double>(); SortedMap<Date, Double> fixedSmaBullish = new TreeMap<Date, Double>(); for (Date date : sma100Drv.keySet()) { Double fixedSmaAtDate = sma100Drv.get(date); if (fixedSmaAtDate == 0) { fixedSmaBullish.put(date, quotes.get(date) * 1.25); } else if (fixedSmaAtDate == 1) { fixedSmaBearish.put(date, quotes.get(date) * 1.25); } } barSeries.put( new DataSetBarDescr(4, "Artificial no lag bearish sma", new Color(246, 173, 173)), fixedSmaBearish); barSeries.put( new DataSetBarDescr(3, "Artificial no lag bullish sma", new Color(189, 249, 189)), fixedSmaBullish); // Prediction period Date predStart = maxLastKey(fixedSmaBearish, fixedSmaBullish); Date predEnd = maxLastKey(sellSerie, buySerie); SortedMap<Date, Double> pred = new TreeMap<Date, Double>(); SortedMap<Date, Double> predSubMap = quotes.subMap(predStart, predEnd); ApacheStats apacheStatsQ = new ApacheStats(new Max()); double maxQ = apacheStatsQ.sEvaluate(quotes.values()); ApacheStats apacheStatsO = new ApacheStats(new Max()); double maxO = apacheStatsO.evaluate(calcOutput.values()); double fact = Math.max(maxQ, maxO); for (Date date : predSubMap.keySet()) { pred.put(date, fact * 1.25); } barSeries.put(new DataSetBarDescr(0, "Prediction zone", new Color(128, 128, 128, 100)), pred); /// Output b&s barSeries.put(new DataSetBarDescr(1, "Bearish prediction", Color.RED), sellSerie); barSeries.put(new DataSetBarDescr(2, "Bullish prediction", Color.GREEN), buySerie); // chart chartGenerator.generateChartPNGFor( new FileOutputStream( new File(System.getProperty("installdir") + File.separator + chartFileName)), false, lineSeries, barSeries); }
public TuningResDTO buildTuningRes( Stock stock, Date startDate, Date endDate, Date endCalcRes, String analyseName, SortedMap<Date, double[]> calcOutput, Collection<EventValue> eventListForEvtDef, String noResMsg, String evtDefInfo, Observer observer) throws IOException, NoQuotationsException, NotEnoughDataException, InvalidAlgorithmParameterException { LOGGER.info( "Building Tuning res for " + stock.getFriendlyName() + " and " + evtDefInfo + " between " + startDate + " and " + endDate + ", end calculation res is " + endCalcRes); Quotations quotations = QuotationsFactories.getFactory() .getQuotationsInstance( stock, startDate, endCalcRes, true, stock.getMarketValuation().getCurrency(), 1, ValidityFilter.CLOSE); SortedMap<Date, Number> mapFromQuotationsClose = QuotationsFactories.getFactory() .buildExactBMapFromQuotations( quotations, QuotationDataType.CLOSE, 0, quotations.size() - 1); LOGGER.info( "Quotations map for " + stock.getFriendlyName() + " ranges from " + mapFromQuotationsClose.firstKey() + " to " + mapFromQuotationsClose.lastKey() + " while requested from " + startDate + " to " + endCalcRes); List<PeriodRatingDTO> periods = validPeriods( mapFromQuotationsClose, stock, startDate, endDate, endCalcRes, analyseName, calcOutput, eventListForEvtDef, noResMsg, evtDefInfo); return buildResOnValidPeriods( periods, mapFromQuotationsClose, quotations, stock, startDate, endDate, analyseName, calcOutput, evtDefInfo, observer); }
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; } }