protected void exportLine( Boolean generateBuySellCsv, Boolean generateSmaCmpOutChart, Double csvDispFactor, BufferedWriter csvWriter, SortedMap<Date, Double> buySerie, SortedMap<Date, Double> sellSerie, Date periodInnerDate, Double closeForInnerDate, EventType periodTrend, double[] output) { DateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss"); String line = simpleDateFormat.format(periodInnerDate); line = line + ", " + closeForInnerDate; String outputString = (output == null) ? "NaN" : output[0] + ""; if (periodTrend.equals(EventType.BULLISH)) { if (generateBuySellCsv) { line = line + ", , " + (closeForInnerDate * csvDispFactor) + ", " + outputString + "\n"; try { csvWriter.append(line); } catch (IOException e) { LOGGER.error("failed to export csv ", e); } } if (generateSmaCmpOutChart) buySerie.put(periodInnerDate, closeForInnerDate); } else if (periodTrend.equals(EventType.BEARISH)) { if (generateBuySellCsv) { line = line + ", " + (closeForInnerDate * csvDispFactor) + ", , " + outputString + "\n"; try { csvWriter.append(line); } catch (IOException e) { LOGGER.error("failed to export csv ", e); } } if (generateSmaCmpOutChart) sellSerie.put(periodInnerDate, closeForInnerDate); } else { if (generateBuySellCsv) { line = line + ", , , " + outputString + "\n"; try { csvWriter.append(line); } catch (IOException e) { LOGGER.error("failed to export csv ", e); } } } }
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); } }
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 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; }