asclass AbstractTickerPainter extends ScaledDiagram is G2Drawable, MakesBufferedImage { settable TickerSequence ticker; // the following two are old, use priceCells instead settable double percentLineDistance; settable bool drawPercentLines; settable PriceCells priceCells; new L positions; new L additionalObjects; public BufferedImage render aka getBufferedImage() { // Make black image var img = blackImage(w, h); var g = img.createGraphics(); drawOn(g); ret img; } void drawPercentLines(Graphics2D g) { if (priceCells != null) { int cellNumber = ifloor(priceCells.toCellNumber(verticalRange.end)); int safety = 250; while (safety-- > 0) { double y = priceCells.fromCellNumber(cellNumber); if (y < verticalRange.start) break; var yy = iround(yToScreen(y)); drawLine(g, 0, yy, w-1, yy, Color.gray); cellNumber--; } }/* else if (drawPercentLines && percentLineDistance != 0) { double y = verticalRange.end; int safety = 100; while (safety-- > 0 && y > verticalRange.start) { y /= 1+percentLineDistance/100; var yy = iround(yToScreen(y)); drawLine(g, 0, yy, w-1, yy, Color.gray); } }*/ } void drawPositions(Graphics2D g) { for (position : positions) { var time = position.openingTime(); var price = position.openingPrice(); var x = xToScreen(time); var y = yToScreen(price); //bool green = position.relativeValue() > 0; //bool green = position.isLong(); //var color = colorFromHex(green ? "1da2b4" : "f1493f"); var color = directionToCandleColor(position.relativeValue()); // Mark opening point fillRect(g, rectAroundPt(iround(x), iround(y), 10), color); if (not(position.isOpen())) { time = position.closingTime(); price = position.closingPrice(); var x2 = xToScreen(time); var y2 = yToScreen(price); drawArrowBetweenPoints(g, toPt_round(doublePt(x, y)), toPt_round(doublePt(x2, y2)), color); } } } void drawAdditionalObjects(Graphics2D g) { for (o : additionalObjects) o.drawOn(g); } void addVerticalLine(double x, Color color default Color.gray) { additionalObjects.add(g -> drawVerticalLine(g, x, color)); } void add(G2Drawable object) { addIfNotNull(additionalObjects, object); } void drawVerticalLine(Graphics2D g, double x, Color color default Color.gray) { int xScreen = iround(xToScreen(x)); drawLine(g, xScreen, 0, xScreen, h-1, color); } void addHorizontalLine(double y, Color color default Color.gray) { additionalObjects.add(g -> drawHorizontalLine(g, y, color)); } void drawHorizontalLine(Graphics2D g, double y, Color color default Color.gray) { int yScreen = iround(yToScreen(y)); drawLine(g, 0, yScreen, w-1, yScreen, color); } DoubleRange horizontalRangeForTicker(TickerSequence ticker) { var timeRange = ticker.timeRange(); if (timeRange == null) null; ret doubleRange(timeRange.startTime().unixDate(), timeRange.endTime().unixDate()); } DoubleRange verticalRangeForTicker(TickerSequence ticker) { ret doubleRange(ticker.minPrice(), ticker.maxPrice()); } void addTimeGrid(double minutes) { additionalObjects.add(g -> drawTimeGrid(g, minutes)); } void drawTimeGrid(Graphics2D g, double minutes) { double ms = minutesToMS(minutes); assertTrue(ms >= 100); double time = roundUpTo(ms, horizontalRange().start); while (time < horizontalRange().end) { drawVerticalLine(g, time); time += ms; } } class LineIndicator is G2Drawable { settable TickerSequence values; settable Color color; public void drawOn(Graphics2D g) { g.setColor(color); var xRange = roundToIntRange(xRange()); var sub = ticker.subSequenceByTimestamps(xFromScreen(xRange.start), xFromScreen(xRange.end)); int idx1, idx2 = ticker.indexOfTimestamp(xFromScreen(xRange.start)); int lasty = 0; for (int i = 0; i < sub.size(); i++) { double value = ticker.getPrice(i); int y = iround(yToScreen(seq.maxPrice())); if (i != 0) g.drawLine(x-1, lasty, x, y); lasty = y; } } } }