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; settable Color percentStripesColor = Color.darkGray; 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 y2 = priceCells.fromCellNumber(cellNumber-1); if (y2 < verticalRange.start) break; double y = priceCells.fromCellNumber(cellNumber); var yy = iround(yToScreen(y)); var yy2 = iround(yToScreen(y2)); //drawLine(g, 0, yy, w-1, yy, Color.gray); fillRect(g, 0, yy, w-1, yy2-yy, percentStripesColor); cellNumber -= 2; } } } void drawPositions(Graphics2D g) { for (position : positions) { var time = position.openingTime(); var price = position.openingPrice(); var x = xToScreen(time); var y = yToScreen(price); var color = colorForPosition(position); // 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) pcall { 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 = Color.yellow; *() {} *(TickerSequence *values) {} *(TickerSequence *values, Color *color) {} public void drawOn(Graphics2D g) { g.setColor(color); var xRange = roundToIntRange(xRange()); var sub = values.subSequenceByTimestamps( lround(xFromScreen(xRange.start)), lround(xFromScreen(xRange.end))); int lastx = 0, lasty = 0; for (int i = 0; i < sub.size(); i++) { double price = sub.getPrice(i); long time = sub.getTimestamp(i); int x = iround(xToScreen(time)); int y = iround(yToScreen(price)); if (i != 0) g.drawLine(lastx, lasty, x, y); lastx = x; lasty = y; } } } selfType priceCells aka cellSize(double cellSize) { ret priceCells(new GeometricPriceCells(cellSize)); } swappable Color colorForPosition(TradingPosition position) { bool winner = position.isWinner(); //bool green = position.isLong(); //var color = colorFromHex(green ? "1da2b4" : "f1493f"); ret winner ? directionToCandleColor(position.direction()) : position.isLong() ? Color.yellow : Color.blue; } }