Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions examples/Stock/Advanced/OHLC.csv
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
,"Foobar Inc."
January, 20.3, 42.4, 10.3, 32.0
February, 32.0, 37.3, 20.0, 25.9
March, 25.9, 33.7, 20.3, 29.0
April, 29.9, 53.3, 28.1, 39.0
May, 39.0, 45.0, 36.4, 41.9
June, 41.9, 63.3, 41.0, 58.7
July, 58.7, 68.5, 50.7, 55.3
January, 20.3, 42.4, 10.3, 32.0, 23
February, 32.0, 37.3, 20.0, 25.9, 28.4
March, 25.9, 33.7, 20.3, 29.0, 26.5
April, 29.9, 53.3, 28.1, 39.0, 35
May, 39.0, 45.0, 36.4, 41.9, 40
June, 41.9, 63.3, 41.0, 58.7, 55
July, 58.7, 68.5, 50.7, 55.3, 56
2 changes: 2 additions & 0 deletions examples/Stock/Advanced/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ void MainWindow::applyColor(const QColor &color)
QColor inverse(255 - color.red(), 255 - color.green(), 255 - color.blue());
m_diagram.setPen(1, QPen(inverse.darker(130)));
m_diagram.setBrush(1, QBrush(inverse));

QPalette pal = colorChooser->palette();
pal.setBrush(QPalette::Button, QBrush(color));
colorChooser->setPalette(pal);
Expand Down Expand Up @@ -133,6 +134,7 @@ void MainWindow::on_stockTypeCB_currentIndexChanged(int index)
} else if (text == "Candlestick") {
m_diagram.setType(StockDiagram::Candlestick);
m_diagram.setModel(&m_OHLCModel);
m_diagram.setDataContainsMedianValues(true);
}

m_chart->coordinatePlane()->replaceDiagram(&m_diagram);
Expand Down
27 changes: 25 additions & 2 deletions src/KDChart/Cartesian/KDChartStockDiagram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,18 @@ void StockDiagram::paint(PaintContext *context)

PainterSaver painterSaver(context->painter());
const int rowCount = attributesModel()->rowCount(attributesModelRootIndex());
const int divisor = (d->type == OpenHighLowClose || d->type == Candlestick) ? 4 : 3;
const int divisor = [&] {
switch (d->type) {
case HighLowClose:
return 3;
case OpenHighLowClose:
return 4;
case Candlestick:
return d->dataContainsMedian ? 5 : 4;
}
Q_UNREACHABLE();
}();

const int colCount = attributesModel()->columnCount(attributesModelRootIndex()) / divisor;
for (int col = 0; col < colCount; ++col) {
for (int row = 0; row < rowCount; row++) {
Expand All @@ -295,6 +306,8 @@ void StockDiagram::paint(PaintContext *context)
CartesianDiagramDataCompressor::DataPoint open;
CartesianDiagramDataCompressor::DataPoint close;
CartesianDiagramDataCompressor::DataPoint volume;
CartesianDiagramDataCompressor::DataPoint median;


if (d->type == HighLowClose) {
const CartesianDiagramDataCompressor::CachePosition highPos(row, col * divisor);
Expand All @@ -312,6 +325,10 @@ void StockDiagram::paint(PaintContext *context)
low = d->compressor.data(lowPos);
high = d->compressor.data(highPos);
close = d->compressor.data(closePos);
if (d->type == Candlestick) {
const CartesianDiagramDataCompressor::CachePosition medianPos(row, col * divisor + 4);
median = d->compressor.data(medianPos);
}
}

switch (d->type) {
Expand All @@ -323,7 +340,8 @@ void StockDiagram::paint(PaintContext *context)
d->drawOHLCBar(col, open, high, low, close, context);
break;
case Candlestick:
d->drawCandlestick(col, open, high, low, close, context);
median.hidden = !d->dataContainsMedian;
d->drawCandlestick(col, open, high, low, close, median, context);
break;
}
}
Expand Down Expand Up @@ -352,6 +370,11 @@ qreal StockDiagram::threeDItemDepth(const QModelIndex &index) const
return 1.0;
}

void StockDiagram::setDataContainsMedianValues(bool b)
{
d->dataContainsMedian = b;
}

const QPair<QPointF, QPointF> StockDiagram::calculateDataBoundaries() const
{
const int rowCount = attributesModel()->rowCount(attributesModelRootIndex());
Expand Down
2 changes: 2 additions & 0 deletions src/KDChart/Cartesian/KDChartStockDiagram.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ class KDCHART_EXPORT StockDiagram : public AbstractCartesianDiagram
qreal threeDItemDepth(int column) const override;
qreal threeDItemDepth(const QModelIndex &index) const override;

void setDataContainsMedianValues(bool b = true);

protected:
const QPair<QPointF, QPointF> calculateDataBoundaries() const override;
};
Expand Down
27 changes: 27 additions & 0 deletions src/KDChart/Cartesian/KDChartStockDiagram_p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ void StockDiagram::Private::drawCandlestick(int /*dataset*/, const CartesianDiag
const CartesianDiagramDataCompressor::DataPoint &high,
const CartesianDiagramDataCompressor::DataPoint &low,
const CartesianDiagramDataCompressor::DataPoint &close,
const CartesianDiagramDataCompressor::DataPoint &median,
PaintContext *context)
{
PainterSaver painterSaver(context->painter());
Expand All @@ -342,6 +343,7 @@ void StockDiagram::Private::drawCandlestick(int /*dataset*/, const CartesianDiag
QPen pen;
bool drawLowerLine;
bool drawCandlestick = !open.hidden && !close.hidden;
bool drawMedian = !median.hidden;
bool drawUpperLine;

// Find out if we need to paint a down-trend or up-trend candlestick
Expand All @@ -363,6 +365,19 @@ void StockDiagram::Private::drawCandlestick(int /*dataset*/, const CartesianDiag
drawUpperLine = !low.hidden && !open.hidden;
}

auto medianColor = [&] {
auto brushColor = brush.color();
if (brushColor.isValid()) {
return (0.21 * brushColor.red() + 0.72 * brushColor.green() + 0.07 * brushColor.blue()) < 127
? QColor(Qt::white)
: QColor(Qt::black);
} else {
// Fallback to pen color, we could do better by letting the caller provide the pen, but I'm out of attention span for that.
return pen.color();
}
};


StockBarAttributes attr = stockDiagram()->stockBarAttributes(col);
ThreeDBarAttributes threeDAttr = stockDiagram()->threeDBarAttributes(col);

Expand All @@ -375,6 +390,11 @@ void StockDiagram::Private::drawCandlestick(int /*dataset*/, const CartesianDiag
QRectF candlestick = projectCandlestick(context, bottomCandlestickPoint,
topCandlestickPoint, attr.candlestickWidth());

const auto medianLine = [&] {
const QPointF medianPoint = projectPoint(context, QPointF(median.key, median.value));
return QLineF(candlestick.left(), medianPoint.y(), candlestick.right(), medianPoint.y());
};

// Remember the drawn polygon to add it to the ReverseMapper later
QPolygonF drawnPolygon;

Expand All @@ -396,13 +416,15 @@ void StockDiagram::Private::drawCandlestick(int /*dataset*/, const CartesianDiag
drawnPolygon = threeDPainter.drawThreeDRect(candlestick, brush, pen, threeDProps);
if (drawUpperLine)
drawnPolygon = threeDPainter.drawTwoDLine(upperLine, pen, threeDProps);
// TODO: Draw median in 3D
} else {
if (drawUpperLine)
drawnPolygon = threeDPainter.drawTwoDLine(upperLine, pen, threeDProps);
if (drawCandlestick)
drawnPolygon = threeDPainter.drawThreeDRect(candlestick, brush, pen, threeDProps);
if (drawLowerLine)
drawnPolygon = threeDPainter.drawTwoDLine(lowerLine, pen, threeDProps);
// TODO: Draw median in 3D
}
} else {
QPainter *const painter = context->painter();
Expand All @@ -414,6 +436,11 @@ void StockDiagram::Private::drawCandlestick(int /*dataset*/, const CartesianDiag
painter->drawLine(upperLine);
if (drawCandlestick)
painter->drawRect(candlestick);
if (drawMedian) {
painter->setPen(medianColor());
painter->drawLine(medianLine());
painter->setPen(pen);
}

// The 2D representation is the projected candlestick itself
drawnPolygon = candlestick;
Expand Down
2 changes: 2 additions & 0 deletions src/KDChart/Cartesian/KDChartStockDiagram_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class StockDiagram::Private : public AbstractCartesianDiagram::Private

QPen lowHighLinePen;
QMap<int, QPen> lowHighLinePens;
bool dataContainsMedian = false;

void drawOHLCBar(int dataset, const CartesianDiagramDataCompressor::DataPoint &open,
const CartesianDiagramDataCompressor::DataPoint &high,
Expand All @@ -59,6 +60,7 @@ class StockDiagram::Private : public AbstractCartesianDiagram::Private
const CartesianDiagramDataCompressor::DataPoint &high,
const CartesianDiagramDataCompressor::DataPoint &low,
const CartesianDiagramDataCompressor::DataPoint &close,
const CartesianDiagramDataCompressor::DataPoint &median,
PaintContext *context);

private:
Expand Down