Example #1
0
  public FinalRating calculateRating(TuningResDTO tuningRes) {

    int nbSuccess = 0;
    int nbFailure = 0;
    for (PeriodRatingDTO periodRatingDTO : tuningRes.getPeriods()) {
      Validity periodValidity =
          smoothedPeriodValidity(periodRatingDTO, tuningRes.getStockPriceChange());

      // nbSuccess = nbSuccess + ((periodValidity.equals(Validity.SUCCESS))?1:0);
      if (periodRatingDTO
          .getTrend()
          .equals(EventType.BULLISH.toString())) { // we tally only the bullish failures and success
        nbSuccess = nbSuccess + ((periodValidity.equals(Validity.SUCCESS)) ? 1 : 0);
        nbFailure = nbFailure + ((periodValidity.equals(Validity.FAILURE)) ? 1 : 0);
      }
    }

    FinalRating finalRating =
        tuningFinalizationRating(
            tuningRes.getFollowProfit(), tuningRes.getStockPriceChange(), nbSuccess, nbFailure);

    // Test
    // finalRating.validity = Validity.SUCCESS;

    return finalRating;
  }
Example #2
0
  private Validity smoothedPeriodValidity(
      PeriodRatingDTO periodRatingDTO, Double totalPriceChange) {

    Validity periodValidity = Validity.NORATING;

    double errorDelta = 0.0;
    double ratioToTotPriceChange = 0.10;
    boolean isBullAndNeg =
        periodRatingDTO.getTrend().equals(EventType.BULLISH.name())
            && periodRatingDTO.getPriceRateOfChange() < -errorDelta;
    boolean isBearAndPos =
        periodRatingDTO.getTrend().equals(EventType.BEARISH.name())
            && periodRatingDTO.getPriceRateOfChange() > errorDelta;
    boolean isPeriodSignificant =
        Math.abs(periodRatingDTO.getPriceRateOfChange())
            > Math.abs(totalPriceChange * ratioToTotPriceChange);

    if (isBullAndNeg || isBearAndPos) {
      if (isPeriodSignificant) {
        periodValidity = Validity.FAILURE;
      } else {
        periodValidity = Validity.NORATING;
      }
    } else {
      periodValidity = Validity.SUCCESS;
    }

    return periodValidity;
  }
Example #3
0
  /**
   * Builds a *_ConfigRating.csv file for the stock containing the trend periods results of the
   * stock tuning. The from and to dates are the date for the start of a trend (BULLISH/BEARISH) and
   * the end of the trend. The file contains one line per config elected over the tuning. For each
   * config its trend period of occurrence and the trend values during this period. Hence you can
   * have several lines with different configs over the same trend period where they were
   * consecutively elected with the same trend results. As in fact the config elected will change at
   * the pace of the tuning periods which are different from the trend periods. On the other hand,
   * when trend changes, also does the trend period dates. In the same way the same config can be
   * elected over several consecutive trend period changes.
   */
  public void exportConfigRating(
      String analysisName,
      TuningResDTO tuningRes,
      Date startDate,
      Date endDate,
      FinalRating calculatedRating)
      throws IOException {

    String fileName = "tmp" + File.separator + analysisName + "_ConfigRating.csv";
    File configRatings = new File(System.getProperty("installdir") + File.separator + fileName);
    FileWriter fileWriter = new FileWriter(configRatings);
    fileWriter.write(
        "config, cfg start, cfg end, trend start, trend end, length, price change, trend, success/failure, compound profit \n");

    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy MM dd");
    Double compoundProfit = 1d;
    for (PeriodRatingDTO periodRatingDTO : tuningRes.getPeriods()) {

      String successOrFailure =
          ((periodRatingDTO.getTrend().equals(EventType.BULLISH.name())
                      && periodRatingDTO.getPriceRateOfChange() <= 0)
                  || (periodRatingDTO.getTrend().equals(EventType.BEARISH.name())
                      && periodRatingDTO.getPriceRateOfChange() > 0))
              ? "FAILURE"
              : "SUCCESS";

      if (periodRatingDTO.getTrend().equals(EventType.BULLISH.name()))
        compoundProfit = compoundProfit * (periodRatingDTO.getPriceRateOfChange() + 1);

      String cfgStr =
          dateFormat.format(periodRatingDTO.getFrom())
              + " , "
              + dateFormat.format(periodRatingDTO.getTo())
              + " , "
              + periodRatingDTO.getPeriodLenght()
              + " , "
              + periodRatingDTO.getPriceRateOfChange()
              + " , "
              + periodRatingDTO.getTrend()
              + " , "
              + successOrFailure
              + " , "
              + compoundProfit;

      if (periodRatingDTO.getConfigs().size() > 0) {

        for (String pConfig : periodRatingDTO.getConfigs()) {
          String cfgStart = dateFormat.format(startDate);
          String cfgEnd = dateFormat.format(endDate);
          fileWriter.write(pConfig + " , " + cfgStart + " , " + cfgEnd + " , " + cfgStr + "\n");
        }

      } else { // No config
        fileWriter.write("No config? " + " , , , " + cfgStr + "\n");
      }
    }

    fileWriter.write(
        "total , percent gain : "
            + tuningRes.getFollowProfit()
            + ", price change : "
            + tuningRes.getStockPriceChange()
            + "\n");
    tuningRes.setConfigRatingFile(fileName);

    fileWriter.write("rating , " + calculatedRating);
    fileWriter.close();
  }
Example #4
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);
  }
Example #5
0
  private void addFilteredPeriod(
      List<PeriodRatingDTO> periods, PeriodRatingDTO period, int sizeConstraint) {

    if ((periods.size() == 0) && EventType.valueOf(period.getTrend()).equals(EventType.BULLISH)) {
      LOGGER.info("First bullish period discarded : " + period);
      return;
    }

    if (sizeConstraint != -1 && period.getPeriodLenght() < sizeConstraint) {
      String invFlasePositiveTrend =
          (EventType.valueOf(period.getTrend()).equals(EventType.BULLISH))
              ? EventType.NONE.toString()
              : EventType.BULLISH.toString();
      LOGGER.info(
          "Period is too short (false positive) : "
              + period
              + ". Trend will be set as "
              + invFlasePositiveTrend);
      period.setTrend(invFlasePositiveTrend);
      if ((periods.size() == 0) && EventType.valueOf(period.getTrend()).equals(EventType.BULLISH)) {
        LOGGER.info("First bullish period discarded : " + period);
        return;
      }
    }

    PeriodRatingDTO previousPeriod;
    if (periods.size() > 0
        && (previousPeriod = periods.get(periods.size() - 1))
            .getTrend()
            .equals(period.getTrend())) {
      previousPeriod.setTo(period.getTo());
      previousPeriod.setPriceAtTo(period.getPriceAtTo());
      previousPeriod.setRealised(period.isRealised());
    } else {
      periods.add(period);
    }
  }
Example #6
0
  private List<PeriodRatingDTO> validPeriods(
      SortedMap<Date, Number> mapFromQuotationsClose,
      Stock stock,
      Date startDate,
      Date endDate,
      Date endCalcRes,
      String analyseName,
      SortedMap<Date, double[]> calcOutput,
      Collection<EventValue> eventListForEvtDef,
      String noResMsg,
      String evtDefInfo)
      throws NotEnoughDataException {

    List<PeriodRatingDTO> periods = new ArrayList<PeriodRatingDTO>();

    EventType prevEventType = null;
    PeriodRatingDTO period = null;

    BigDecimal lastClose =
        (BigDecimal) mapFromQuotationsClose.get(mapFromQuotationsClose.lastKey());

    Calendar currentIterationDateCal = zeroTimeCal(startDate);

    // First event
    Iterator<EventValue> eventsIt = eventListForEvtDef.iterator();
    EventValue currentEvent = null;
    if (eventsIt.hasNext()) {
      currentEvent = eventsIt.next();
    }

    for (Date currentIterationDate : mapFromQuotationsClose.keySet()) {

      currentIterationDateCal.setTime(currentIterationDate);

      BigDecimal closeForDate = (BigDecimal) mapFromQuotationsClose.get(currentIterationDate);
      if (closeForDate.compareTo(BigDecimal.ZERO) == 0)
        LOGGER.error(
            "Close for date is zero for at "
                + currentIterationDate
                + " for "
                + stock.getFriendlyName()
                + " and "
                + evtDefInfo
                + " between "
                + startDate
                + " and "
                + endDate
                + ", end calculation res is "
                + endCalcRes);

      // Some events may have been skipped (ie WE events) and must be disregarded
      while (currentEvent != null
          && currentIterationDateCal.getTime().after(currentEvent.getDate())
          && eventsIt.hasNext()) {
        currentEvent = eventsIt.next();
      }

      // We process the event when reached
      if (currentEvent != null
          && currentIterationDateCal.getTime().compareTo(currentEvent.getDate())
              == 0) { // Event date reached

        EventType eventType = currentEvent.getEventType();
        if (prevEventType == null
            || (!eventType.equals(EventType.NONE)
                && !prevEventType.equals(eventType))) { // First trend or Valid trend change

          // TO
          if (prevEventType
              != null) { // Not the First trend : We close the current period and add it to the list

            period.setTo(currentEvent.getDate());
            period.setPriceAtTo(closeForDate.doubleValue());
            period.setRealised(true);
            addFilteredPeriod(periods, period, 15);

          } else { // First trend : Nothing to close

          }

          // FROM : First trend or new one start
          period =
              new PeriodRatingDTO(
                  currentEvent.getDate(), closeForDate.doubleValue(), eventType.toString());

          // Updating loop vars
          if (eventsIt.hasNext()) {
            currentEvent = eventsIt.next();
          }
          prevEventType = eventType;

        } else { // Same trend or invalid trend (ie NONE)

          // Updating loop vars
          if (eventsIt.hasNext()) {
            currentEvent = eventsIt.next();
          }
        }
      }
    } // End for quotations dates iteration

    // Not enough data were found : we get out of here
    if (prevEventType == null) {

      String message = "No trend forecast events were found after calculation.\n";

      LOGGER.warn(
          noResMsg
              + message
              + ".\n"
              + "Stock :"
              + stock
              + "\n"
              + "Available neural events  :"
              + ((eventListForEvtDef == null) ? "null" : eventListForEvtDef.size())
              + "\n"
              + "Call Params : start "
              + startDate
              + ", end "
              + endDate
              + ", analysis "
              + analyseName
              + ", event Def "
              + evtDefInfo
              + "\n"
              + "Output :  endCalc "
              + endCalcRes
              + ", Calculated output size "
              + ((calcOutput == null) ? "null" : calcOutput.size()));

      Date firstAvailDate = new Date(0);
      Date lastAvailDate = new Date(0);
      if (mapFromQuotationsClose.size() > 0) {
        firstAvailDate = mapFromQuotationsClose.firstKey();
        lastAvailDate = mapFromQuotationsClose.lastKey();
      }
      throw new NotEnoughDataException(
          stock, firstAvailDate, lastAvailDate, noResMsg + message, new Throwable());
    }

    // Finalising last trend
    period.setTo(endDate);
    period.setPriceAtTo(lastClose.doubleValue());
    period.setRealised(prevEventType.equals(EventType.BEARISH));
    addFilteredPeriod(periods, period, -1);

    return periods;
  }