persistable sclass TickerSequences is IntSize { settable S market; // e.g. "TRBUSDT" gettable new L tickers; 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), formatDouble1(toDays(totalTime())) + " days", 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(); })); } void 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; } } } 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 digitizeToPercent(double basePrice, double cellSizeInPercent) { ret mapTickers(ts -> ts.digitizeToPercent(basePrice, cellSizeInPercent)); } }