public List<MarketSnapshot> load(ProgressListener progressListener) throws JBookTraderException { String line = ""; int lineSeparatorSize = System.getProperty("line.separator").length(); long sizeRead = 0, lineNumber = 0; List<MarketSnapshot> snapshots = new ArrayList<MarketSnapshot>(); try { while ((line = reader.readLine()) != null) { if (lineNumber % 50000 == 0) { progressListener.setProgress(sizeRead, fileSize, "Loading historical data file"); if (progressListener.isCancelled()) { break; } } lineNumber++; sizeRead += line.length() + lineSeparatorSize; boolean isComment = line.startsWith("#"); boolean isProperty = line.contains("="); boolean isBlankLine = (line.trim().length() == 0); boolean isMarketDepthLine = !(isComment || isProperty || isBlankLine); if (isMarketDepthLine) { MarketSnapshot marketSnapshot = toMarketDepth(line); if (filter == null || filter.contains(time)) { snapshots.add(marketSnapshot); } previousTime = time; } else if (isProperty) { if (line.startsWith("timeZone")) { setTimeZone(line); } } } if (sdf == null) { String msg = "Property " + "\"timeZone\"" + " is not defined in the data file." + LINE_SEP; throw new JBookTraderException(msg); } } catch (IOException ioe) { throw new JBookTraderException("Could not read data file"); } catch (Exception e) { String errorMsg = "Problem parsing line #" + lineNumber + ": " + line + LINE_SEP; String description = e.getMessage(); if (description == null) { description = e.toString(); } errorMsg += description; throw new RuntimeException(errorMsg); } return snapshots; }
/** * Reads and validates a data file containing historical market depth records. The data file is used * for back testing and optimization of trading strategies. */ public class BackTestFileReader { public static final int COLUMNS = 5; private static final String LINE_SEP = System.getProperty("line.separator"); private final BufferedReader reader; private final MarketSnapshotFilter filter; private final long fileSize; private long previousTime, time; private SimpleDateFormat sdf; private String previousDateTimeWithoutSeconds; public BackTestFileReader(String fileName, MarketSnapshotFilter filter) throws JBookTraderException { this.filter = filter; previousDateTimeWithoutSeconds = ""; try { reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName))); fileSize = new File(fileName).length(); } catch (FileNotFoundException fnfe) { throw new JBookTraderException("Could not find file " + fileName); } } private void setTimeZone(String line) throws JBookTraderException { String timeZone = line.substring(line.indexOf('=') + 1); TimeZone tz = TimeZone.getTimeZone(timeZone); if (!tz.getID().equals(timeZone)) { String msg = "The specified time zone " + "\"" + timeZone + "\"" + " does not exist." + LINE_SEP; msg += "Examples of valid time zones: " + " America/New_York, Europe/London, Asia/Singapore."; throw new JBookTraderException(msg); } sdf = new SimpleDateFormat("MMddyyHHmmss"); // Enforce strict interpretation of date and time formats sdf.setLenient(false); sdf.setTimeZone(tz); } public List<MarketSnapshot> load(ProgressListener progressListener) throws JBookTraderException { String line = ""; int lineSeparatorSize = System.getProperty("line.separator").length(); long sizeRead = 0, lineNumber = 0; List<MarketSnapshot> snapshots = new ArrayList<MarketSnapshot>(); try { while ((line = reader.readLine()) != null) { if (lineNumber % 50000 == 0) { progressListener.setProgress(sizeRead, fileSize, "Loading historical data file"); if (progressListener.isCancelled()) { break; } } lineNumber++; sizeRead += line.length() + lineSeparatorSize; boolean isComment = line.startsWith("#"); boolean isProperty = line.contains("="); boolean isBlankLine = (line.trim().length() == 0); boolean isMarketDepthLine = !(isComment || isProperty || isBlankLine); if (isMarketDepthLine) { MarketSnapshot marketSnapshot = toMarketDepth(line); if (filter == null || filter.contains(time)) { snapshots.add(marketSnapshot); } previousTime = time; } else if (isProperty) { if (line.startsWith("timeZone")) { setTimeZone(line); } } } if (sdf == null) { String msg = "Property " + "\"timeZone\"" + " is not defined in the data file." + LINE_SEP; throw new JBookTraderException(msg); } } catch (IOException ioe) { throw new JBookTraderException("Could not read data file"); } catch (Exception e) { String errorMsg = "Problem parsing line #" + lineNumber + ": " + line + LINE_SEP; String description = e.getMessage(); if (description == null) { description = e.toString(); } errorMsg += description; throw new RuntimeException(errorMsg); } return snapshots; } private MarketSnapshot toMarketDepth(String line) throws JBookTraderException, ParseException { List<String> tokens = fastSplit(line); if (tokens.size() != COLUMNS) { String msg = "The line should contain exactly " + COLUMNS + " comma-separated columns."; throw new JBookTraderException(msg); } String dateTime = tokens.get(0) + tokens.get(1); String dateTimeWithoutSeconds = dateTime.substring(0, 10); if (dateTimeWithoutSeconds.equals(previousDateTimeWithoutSeconds)) { // only seconds need to be set int milliSeconds = 1000 * Integer.parseInt(dateTime.substring(10)); long previousMilliSeconds = previousTime % 60000; time = previousTime + (milliSeconds - previousMilliSeconds); } else { time = sdf.parse(dateTime).getTime(); previousDateTimeWithoutSeconds = dateTimeWithoutSeconds; } if (time <= previousTime) { String msg = "Timestamp of this line is before or the same as the timestamp of the previous line."; throw new JBookTraderException(msg); } double balance = Double.parseDouble(tokens.get(2)); double price = Double.parseDouble(tokens.get(3)); int volume = Integer.parseInt(tokens.get(4)); return new MarketSnapshot(time, balance, price, volume); } private List<String> fastSplit(String s) { ArrayList<String> tokens = new ArrayList<String>(); int index, lastIndex = 0; while ((index = s.indexOf(',', lastIndex)) != -1) { tokens.add(s.substring(lastIndex, index)); lastIndex = index + 1; } tokens.add(s.substring(lastIndex)); return tokens; } }