/* Copyright (c) 2008-2024 Jan W. Krieger () This software is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation, either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License (LGPL) for more details. You should have received a copy of the GNU Lesser General Public License (LGPL) along with this program. If not, see . */ #include "jkqtplotter/graphs/jkqtpfinancial.h" #include "jkqtplotter/jkqtpbaseplotter.h" #include #include #include #include "jkqtplotter/jkqtptools.h" #include "jkqtplotter/jkqtplotter.h" #define SmallestGreaterZeroCompare_xvsgz() if ((xvsgz>10.0*DBL_MIN)&&((smallestGreaterZero<10.0*DBL_MIN) || (xvsgz JKQTPFinancialGraph::parentsAlreadySeen={}; void JKQTPFinancialGraph::clearColorAssignStore() { parentsAlreadySeen.clear(); } JKQTPFinancialGraph::JKQTPFinancialGraph(JKQTBasePlotter* parent): JKQTPXGraph(parent), graphType(FinancialGraphType::CandleStick), openColumn(-1), closeColumn(-1), highColumn(-1), lowColumn(-1), width(0.8), shift(0) { //qDebug()<<"1: pos: "<getCurrentPlotterStyle().graphsStyle.financialStyle.positiveDefaultColor); m_fillStyleNegative.setFillColor(parent->getCurrentPlotterStyle().graphsStyle.financialStyle.negativeDefaultColor); m_lineStylePositive.setLineColor(parent->getCurrentPlotterStyle().graphsStyle.financialStyle.positiveDefaultColor); m_lineStyleNegative.setLineColor(parent->getCurrentPlotterStyle().graphsStyle.financialStyle.negativeDefaultColor); } parentsAlreadySeen.insert(parent); //qDebug()<<"3: pos: "<getPlotter()) { } void JKQTPFinancialGraph::drawKeyMarker(JKQTPEnhancedPainter& painter, const QRectF& r) { painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); const QPen pp=m_lineStylePositive.getKeyLinePen(painter, r, parent); const QPen pn=m_lineStyleNegative.getKeyLinePen(painter, r, parent); const QBrush bp=m_fillStylePositive.getFillBrush(painter, parent); const QBrush bn=m_fillStyleNegative.getFillBrush(painter, parent); if (graphType==CandleStick) { painter.setPen(pp); painter.setBrush(bp); QRectF rect=r; rect.setWidth(r.width()-pp.widthF()); rect.setHeight(r.height()*0.8); rect.setX(rect.x()+pp.widthF()/2.0); rect.setY(rect.y()+r.height()*0.1); painter.drawPolygon(QPolygonF()<getDatastore(); if (datastore==nullptr) return; drawErrorsBefore(painter); const QPen pp=m_lineStylePositive.getLinePenForRects(painter, parent); const QPen pn=m_lineStyleNegative.getLinePenForRects(painter, parent); const QBrush bp=m_fillStylePositive.getFillBrush(painter, parent); const QBrush bn=m_fillStyleNegative.getFillBrush(painter, parent); int imax=0; int imin=0; double left=-1e6; double right=1e6; bool firstXY=true; if (getIndexRange(imin, imax)) { reserveHitTestData(imax-imin); painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); double delta=1; double deltap=0; double deltam=0; intSortData(); for (int iii=imin; iiiget(static_cast(xColumn),static_cast(i)); const int sr=datastore->getNextLowerIndex(xColumn, i); const int lr=datastore->getNextHigherIndex(xColumn, i); const double vO=datastore->get(static_cast(openColumn),static_cast(i)); const double vC=datastore->get(static_cast(closeColumn),static_cast(i)); const double vL=datastore->get(static_cast(lowColumn),static_cast(i)); const double vH=datastore->get(static_cast(highColumn),static_cast(i)); if (sr<0 && lr<0) { // only one x-value deltam=0.5; deltap=0.5; } else if (lr<0) { // the right-most x-value deltap=deltam=fabs(xv-datastore->get(xColumn,sr))/2.0; } else if (sr<0) { // the left-most x-value deltam=deltap=fabs(datastore->get(xColumn,lr)-xv)/2.0; } else { deltam=fabs(xv-datastore->get(xColumn,sr))/2.0; deltap=fabs(datastore->get(xColumn,lr)-xv)/2.0; } delta=deltap+deltam; if (JKQTPIsOKFloat(xv)) { const double x=transformX(xv+shift*delta); const double xm=transformX(xv+shift*delta-qMin(width*deltam,width*deltap)); const double xp=transformX(xv+shift*delta+qMin(width*deltam,width*deltap)); const double yO=transformY(vO); const double yC=transformY(vC); const double yL=[&]() { double y=transformY(vL); if (!JKQTPIsOKFloat(y)) y=transformY(qMin(vO,vC)); return y; }(); const double yH=[&]() { double y=transformY(vH); if (!JKQTPIsOKFloat(y)) y=transformY(qMax(vO,vC)); return y; }(); if (JKQTPIsOKFloat(x) && JKQTPIsOKFloat(xm) && JKQTPIsOKFloat(xp) && JKQTPIsOKFloat(yC) && JKQTPIsOKFloat(yO)) { const bool isPos=(vC>=vO); if (firstXY) { left=xm; right=xp; } else { left=qMin(left, xm); right=qMax(right, xp); } firstXY=false; if (isPos) { painter.setPen(pp); painter.setBrush(bp); } else { painter.setPen(pn); painter.setBrush(bn); } switch(graphType) { case OHLC: painter.drawLine(QLineF(x,yH,x,yL)); painter.drawLine(QLineF(xm,yO,x,yO)); painter.drawLine(QLineF(xp,yC,x,yC)); break; case CandleStick: { const QRectF rec(xm, qMin(yO,yC),xp-xm,fabs(yO-yC)); painter.drawLine(QLineF(x,yH,x,rec.top())); painter.drawLine(QLineF(x,yL,x,rec.bottom())); painter.drawRect(rec); } break; } auto addUlAt=[](QStringList sl, int i) { sl[i]="\\ul{"+sl[i]+"}"; return sl; }; auto addVerbAll=[](QStringList sl) { for (int i=0; igetGraphCount(); i++) { JKQTPPlotElement* g=parent->getGraph(i); JKQTPFinancialGraph* gb=qobject_cast(g); if (gb && considerForAutoscaling(gb)) { cntH++; } } double widthH=1.0/cntH*maxWidth*shrinkFactor; double dH=maxWidth/(cntH); double h=0.1+dH/2.0; for (size_t i=0; igetGraphCount(); i++) { JKQTPPlotElement* g=parent->getGraph(i); JKQTPFinancialGraph* gb=qobject_cast(g); if (gb && considerForAutoscaling(gb)) { if (cntH>1) { gb->width=widthH; gb->shift=h-0.5; h=h+dH; } else { gb->width=maxWidth; gb->shift=0.0; } } } } } void JKQTPFinancialGraph::autoscaleBoxWidthAndShiftSeparatedGroups(double groupWidth) { autoscaleBoxWidthAndShift(groupWidth, 0.8); } void JKQTPFinancialGraph::setShift(double __value) { this->shift = __value; } double JKQTPFinancialGraph::getShift() const { return this->shift; } void JKQTPFinancialGraph::setWidth(double __value) { this->width = __value; } double JKQTPFinancialGraph::getWidth() const { return this->width; } JKQTPGraphFillStyleMixin &JKQTPFinancialGraph::fillStyleNegative() { return m_fillStyleNegative; } const JKQTPGraphFillStyleMixin &JKQTPFinancialGraph::fillStyleNegative() const { return m_fillStyleNegative; } JKQTPGraphFillStyleMixin &JKQTPFinancialGraph::fillStylePositive() { return m_fillStylePositive; } const JKQTPGraphFillStyleMixin &JKQTPFinancialGraph::fillStylePositive() const { return m_fillStylePositive; } JKQTPGraphLineStyleMixin &JKQTPFinancialGraph::lineStyleNegative() { return m_lineStyleNegative; } const JKQTPGraphLineStyleMixin &JKQTPFinancialGraph::lineStyleNegative() const { return m_lineStyleNegative; } JKQTPGraphLineStyleMixin &JKQTPFinancialGraph::lineStylePositive() { return m_lineStylePositive; } const JKQTPGraphLineStyleMixin &JKQTPFinancialGraph::lineStylePositive() const { return m_lineStylePositive; } bool JKQTPFinancialGraph::getIndexRange(int &imin, int &imax) const { bool ok=JKQTPXGraph::getIndexRange(imin, imax); /*if (ok) { if (parent==nullptr) return false; const JKQTPDatastore* datastore=parent->getDatastore(); if (openColumn<0) return false; if (closeColumn<0) return false; if (highColumn<0) return false; if (lowColumn<0) return false; const int rowsO=static_cast(datastore->getRows(static_cast(openColumn))); const int rowsC=static_cast(datastore->getRows(static_cast(closeColumn))); const int rowsH=static_cast(datastore->getRows(static_cast(highColumn))); const int rowsL=static_cast(datastore->getRows(static_cast(lowColumn))); imax=qMin(imax, rowsO); imax=qMin(imax, rowsC); imax=qMin(imax, rowsH); imax=qMin(imax, rowsL); }*/ return ok; } bool JKQTPFinancialGraph::getYMinMax(double &miny, double &maxy, double &smallestGreaterZero) { bool start=true; miny=0; maxy=0; smallestGreaterZero=0; if (parent==nullptr) return false; const JKQTPDatastore* datastore=parent->getDatastore(); int imin=0; int imax=0; if (getIndexRange(imin, imax)) { for (int i=imin; iget(static_cast(openColumn),static_cast(i)); if (JKQTPIsOKFloat(yvO)) { if (start || yvO>maxy) maxy=yvO; if (start || yvOget(static_cast(closeColumn),static_cast(i)); if (JKQTPIsOKFloat(yvC)) { if (start || yvC>maxy) maxy=yvC; if (start || yvCget(static_cast(lowColumn),static_cast(i)); if (JKQTPIsOKFloat(yvL)) { if (start || yvL>maxy) maxy=yvL; if (start || yvLget(static_cast(highColumn),static_cast(i)); if (JKQTPIsOKFloat(yvH)) { if (start || yvH>maxy) maxy=yvH; if (start || yvHgetCurrentPlotterStyle().plotBackgroundBrush.color(),cLine,cLine); else setColor(cLine,Qt::transparent,cLine,cLine); } void JKQTPFinancialGraph::setOHLCTwoColor(QColor cPositive, QColor cNegative) { setGraphType(OHLC); setColor(cPositive, cNegative); } void JKQTPFinancialGraph::setOpenColumn(int __value) { openColumn=static_cast(__value); } void JKQTPFinancialGraph::setOpenColumn(size_t __value) { openColumn=static_cast(__value); } void JKQTPFinancialGraph::setCloseColumn(int __value) { closeColumn=static_cast(__value); } void JKQTPFinancialGraph::setCloseColumn(size_t __value) { closeColumn=static_cast(__value); } void JKQTPFinancialGraph::setHighColumn(int __value) { highColumn=static_cast(__value); } void JKQTPFinancialGraph::setHighColumn(size_t __value) { highColumn=static_cast(__value); } void JKQTPFinancialGraph::setLowColumn(int __value) { lowColumn=static_cast(__value); } void JKQTPFinancialGraph::setLowColumn(size_t __value) { lowColumn=static_cast(__value); } bool JKQTPFinancialGraph::considerForAutoscaling(JKQTPFinancialGraph *other) const { return (dynamic_cast(other)!=nullptr); }