persistable sclass TickerSequences is ByteIO, IntSize, Iterable { gettable S market; // e.g. "TRBUSDT" gettable new L tickers; *(Iterable sequences) { main addAll(tickers, sequences); } *(TickerSequence... sequences) { main addAll(tickers, sequences); } void add(TickerSequence ticker) { tickers.add(ticker); } TickerSequence get(int i) { ret tickers.get(i); } void sort { sortInPlaceByCalculatedField(tickers, ->. startTime()); } public int size() { ret tickers.size(); } toString { ret commaCombine( spaceCombine( n2(tickers, " ticker sequence"), empty(market) ? null : "for " + market), formatDays(totalTime()), n2(nPricePoints(), "price point") ); } long nPricePoints() { ret longSum(map(tickers, t -> (long) t.size())); } long totalTime() { ret longSum(map(tickers, ticker -> { var tr = ticker.timeRange(); ret tr == null ? 0 : tr.length(); })); } selfType dropOverlaps() { sort(); for (int i = 0; i+1 < l(tickers); i++) { var t1 = tickers.get(i); var t2 = tickers.get(i+1); long time2 = t2.startTime(); while (!t1.isEmpty() && t1.endTime() >= time2) t1.dropLastPricePoint(); t1.trimToSize(); } this; } selfType merge(double maximumGapSeconds default 120) { sort(); for (int i = 0; i+1 < l(tickers); i++) { var t1 = tickers.get(i); var t2 = tickers.get(i+1); if (t2.startTime()-t1.endTime() <= fromSeconds(maximumGapSeconds)) { t1.add(t2); tickers.remove(i+1); --i; } } this; } double firstPrice() { var ticker = first(tickers); ret ticker == null ? Double.NaN : ticker.firstPrice(); } ItIt prices() { ret nestedIterator(tickers, ticker -> ticker.prices.iterator()); } long startTime() { ret empty(tickers) ? null : first(tickers).startTime(); } long endTime() { ret empty(tickers) ? null : last(tickers).endTime(); } TickerSequence longest() { ret highestBy(tickers(), ticker -> duration(ticker.timeRange())); } TickerSequences mapTickers(IF1 f) { new TickerSequences ts; for (t : tickers()) ts.add(f.get(t)); ret ts; } TickerSequences filterTickers(IPred pred) { new TickerSequences ts; for (t : tickers()) if (pred.get(t)) ts.add(t); ret ts; } /*TickerSequences digitizeToPercent(double basePrice, double cellSizeInPercent) { ret mapTickers(ts -> ts.digitizeToPercent(basePrice, cellSizeInPercent)); }*/ selfType marketIfEmpty(S market) { if (empty(this.market)) market(market); this; } selfType market(S market) { this.market = market; for (t : tickers) t.market(market); this; } public void write(ByteHead head) { for (t : tickers) t.write(head); } public void read(ByteHead head) { TickerSequence t; while ((t = TickerSequence.read(head)) != null) add(t); if (empty(market) && nempty(tickers)) market(first(tickers).market); } public void readWrite(ByteHead head) { if (head.readMode()) read(head); if (head.writeMode()) write(head); } public Iterator iterator() { ret tickers.iterator(); } }