sclass TickerGraphPainter extends AbstractTickerPainter is IToolTipMaker, MakesBufferedImage { settable S pricePrefix = "Price="; settable bool fillRedAndGreen; *() {} *(TickerSequence ticker) { ticker(ticker); } public void drawOn(Graphics2D g) { antiAliasOn(g); if (verticalRange == null) verticalRange(verticalRangeForTicker(ticker)); if (horizontalRange == null) horizontalRange(horizontalRangeForTicker(ticker)); if (horizontalRange == null) ret; drawPercentLines(g); drawAdditionalObjects(g); // Draw ticker graph var xRange = roundToIntRange(xRange()); int idx1 = ticker.indexOfTimestampAtOrBefore(xFromScreen(xRange.start)); int x = xRange.start; if (idx1 == 0) x = max(x, ifloor(xToScreen(ticker.startTime()))); int idx2 = idx1; int y0 = yToScreen_int(0); for (; x < xRange.end; x++) { idx1 = idx2; idx2 = ticker.indexOfTimestampAtOrBefore(xFromScreen(x+1)); var seq = ticker.subSequence(idx1, idx2+1); double minPrice = seq.minPrice(); double maxPrice = seq.maxPrice(); int y1 = yToScreen_int(maxPrice); int y2 = yToScreen_int(minPrice); if (fillRedAndGreen) { if (minPrice > 0) drawLine(g, x, y2+1, x, y0-1, directionToCandleColor(1)); else if (maxPrice < 0) drawLine(g, x, y0, x, y1-1, directionToCandleColor(-1)); } drawLine(g, x, y1, x, y2, Color.white); } drawPositions(g); } public BufferedImage render() { var img = super.render(); var img2 = cloneBufferedImageWithMeta(img); metaSet(img2, IToolTipMaker, this); ret img2; } public S getToolTip(Pt p) { double time = xFromScreen(p.x); double price = ticker.priceAtTimestamp(time); ret spaceCombine( ticker.market, formatPrice(price), "at", formatLocalDateWithSeconds(lround(time))); } swappable S formatPrice(double price) { ret pricePrefix() + formatDouble3X(price); } }