persistable sclass TickerSequence is IntSize { settable S market; // e.g. "TRBUSDT" // in case the sequence is digitized settable PriceCells priceCells; // are we live? settable bool live; // Is the data incomplete (still loading)? settable bool loading; new SynchronizedLongBuffer timestamps; new SynchronizedDoubleBuffer prices; // timestamp of last ticker event even though price may not // have changed settable long lastTickerEvent; event pricePointAdded(long timestamp); synchronized void addIfPriceChanged(double price, long timestamp) { lastTickerEvent = max(lastTickerEvent, timestamp); if (isEmpty() || last(prices) != price) add(price, timestamp); } synchronized void add aka addPrice(double price, long timestamp) { lastTickerEvent = max(lastTickerEvent, timestamp); timestamps.add(timestamp); prices.add(price); pricePointAdded(timestamp); } synchronized void add(double[] prices, long[] timestamps) { assertEquals(l(prices), l(timestamps)); if (empty(prices)) ret; // TODO: call pricePointAdded? this.prices.addAll(asList(prices)); this.timestamps.addAll(asList(timestamps)); lastTickerEvent = max(lastTickerEvent, last(timestamps)); } synchronized void add(TickerSequence ticker) { add(ticker.prices.toArray(), ticker.timestamps.toArray()); } synchronized toString { ret "TickerSequence from " + timeRange() + ", " + n2(l(prices), "price change"); } synchronized TickerSequence subSequence(int start, int end) { new TickerSequence s; s.add( subDoubleArray(prices.toArray(), start, end), subArray(timestamps.toArray(), start, end)); ret s; } synchronized TickerSequence cutOffAfterSeconds(double seconds) { int idx = indexOfTimestamp(startTime()+secondsToMS(seconds)); ret subSequence(0, idx); } synchronized TickerSequence subSequenceByTimestamps(long from, long to) { int idx1 = indexOfTimestamp(from); int idx2 = indexOfTimestamp(to); ret subSequence(idx1, idx2); } synchronized int indexOfTimestamp(double ts) { ret binarySearch_insertionPoint(timestamps.asVirtualList(), lround(ts)); } synchronized long nextTimestamp(double ts) { ret getTimestamp(indexOfTimestamp(ts)+1); } synchronized double priceAtTimestamp(double ts) { ret getPrice(indexOfTimestamp(ts)); } public int size() { ret l(prices); } public bool isEmpty() { ret size() == 0; } synchronized TimestampRange timeRange() { ret isEmpty() ? null : TimestampRange(first(timestamps), last(timestamps)); } Duration duration() { ret main duration(timeRange()); } synchronized long startTime() { ret empty(timestamps) ? 0 : first(timestamps); } synchronized long endTime() { ret empty(timestamps) ? 0 : last(timestamps); } synchronized double getPrice(int i) { ret prices.get(clampToLength(size(), i)); } synchronized long getTimestamp(int i) { ret timestamps.get(clampToLength(size(), i)); } synchronized TickerSequence removePlateaus() { new TickerSequence seq; seq.market(market); int n = size(); for i to n: { var value = getPrice(i); seq.prices.add(value); seq.timestamps.add(getTimestamp(i)); while (i+1 < n && getPrice(i+1) == value) ++i; } ret seq.trimToSize(); } synchronized TickerSequence alignTimestamps(int interval) { new TickerSequence seq; seq.market(market); int n = size(); for i to n: { var value = getPrice(i); long time = roundDownTo(interval, getTimestamp(i)); if (time != seq.endTime()) seq.addIfPriceChanged(value, time); } ret seq.trimToSize(); } selfType trimToSize() { prices.trimToSize(); timestamps.trimToSize(); this; } double minPrice() { ret doubleMin(prices.toArray()); } double maxPrice() { ret doubleMax(prices.toArray()); } synchronized double firstPrice() { ret empty(prices) ? Double.NaN : first(prices); } synchronized double lastPrice() { ret empty(prices) ? Double.NaN : last(prices); } synchronized TickerPoint lastPoint() { ret new TickerPoint(this, endTime()); } // parse line from a .ticker file synchronized void addJSONLine(S line) { Map data = parseJSONMap(line); double price = toDouble(data.get("bestAsk")); long time = toLong(data.get("systemTime")); addIfPriceChanged(price, time); } TickerSequence digitizeToPercent(double basePrice, double cellSizeInPercent) { ret digitize(geometricPriceDigitizer(basePrice, cellSizeInPercent)); } TickerSequence digitize(PriceDigitizer digitizer) { new TickerSequence seq; seq.market(market); seq.priceCells(digitizer.priceCells()); int n = size(); for i to n: { seq.addIfPriceChanged(digitizer.digitize(getPrice(i)), getTimestamp(i)); } ret seq.trimToSize(); } L pricePoints() { ret listFromFunction getPricePoint(size());, ); PricePoint getPricePoint(int idx) { ret new PricePoint(timestamps.get(idx), prices.get(idx)); } }