Exemplo n.º 1
0
  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);
    }
  }
Exemplo n.º 2
0
  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();
    }
  }
Exemplo n.º 4
0
  @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;
    }
  }
Exemplo n.º 7
0
  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);
  }
Exemplo n.º 8
0
  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);
  }
Exemplo n.º 9
0
  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);
  }
Exemplo n.º 10
0
  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;
    }
  }