NEW: added financial graphs (Candlestick or OHLC) and example for these
@ -60,6 +60,7 @@ This software is licensed under the term of the [GNU Lesser General Public Licen
|
|||||||
- large variety of [image plots](http://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__imagelots.html) (inclusing different color-scale modes, RGBA-plots, overlays/masks)
|
- large variety of [image plots](http://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__imagelots.html) (inclusing different color-scale modes, RGBA-plots, overlays/masks)
|
||||||
- [contour plots](https://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__imagelots__contour.html)
|
- [contour plots](https://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__imagelots__contour.html)
|
||||||
- [vector field graphs/quiver plots](https://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__vectorfieldgraphs.html)
|
- [vector field graphs/quiver plots](https://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__vectorfieldgraphs.html)
|
||||||
|
- [financial graphs (candlestick/OHLC)](https://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__financialgraphs.html)
|
||||||
- [geometric forms](http://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__geoplots.html) / [annotations](http://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__annotations.html)
|
- [geometric forms](http://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__geoplots.html) / [annotations](http://jkriege2.github.io/JKQtPlotter/group__jkqtplotter__annotations.html)
|
||||||
- can be easily extended by deriving a new graph from [JKQTPPlotElement](http://jkriege2.github.io/JKQtPlotter/class_j_k_q_t_p_plot_element.html), [JKQTPPlotAnnotationElement](http://jkriege2.github.io/JKQtPlotter/class_j_k_q_t_p_plot_annotation_element.html), [JKQTPGeometricPlotElement](http://jkriege2.github.io/JKQtPlotter/class_j_k_q_t_p_geometric_plot_element.html), [JKQTPGraph](http://jkriege2.github.io/JKQtPlotter/class_j_k_q_t_p_graph.html)
|
- can be easily extended by deriving a new graph from [JKQTPPlotElement](http://jkriege2.github.io/JKQtPlotter/class_j_k_q_t_p_plot_element.html), [JKQTPPlotAnnotationElement](http://jkriege2.github.io/JKQtPlotter/class_j_k_q_t_p_plot_annotation_element.html), [JKQTPGeometricPlotElement](http://jkriege2.github.io/JKQtPlotter/class_j_k_q_t_p_geometric_plot_element.html), [JKQTPGraph](http://jkriege2.github.io/JKQtPlotter/class_j_k_q_t_p_graph.html)
|
||||||
- optional: [OpenCV interface](http://jkriege2.github.io/JKQtPlotter/group__jkqtpinterfaceopencv.html), [CImg interfaces](http://jkriege2.github.io/JKQtPlotter/group__jkqtpinterfacecimg.html)
|
- optional: [OpenCV interface](http://jkriege2.github.io/JKQtPlotter/group__jkqtpinterfaceopencv.html), [CImg interfaces](http://jkriege2.github.io/JKQtPlotter/group__jkqtpinterfacecimg.html)
|
||||||
|
@ -247,6 +247,7 @@ if(JKQtPlotter_BUILD_EXAMPLES)
|
|||||||
graphlabels/graphlabels,graphlabels_hor
|
graphlabels/graphlabels,graphlabels_hor
|
||||||
vectorfield
|
vectorfield
|
||||||
paramvectorfield
|
paramvectorfield
|
||||||
|
financialgraphs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -277,6 +278,7 @@ if(JKQtPlotter_BUILD_EXAMPLES)
|
|||||||
graphlabels/JKQTPGLabelAwayFromXAxis,JKQTPGLabelAwayFromYAxis,JKQTPGLabelTowardsXAxis,JKQTPGLabelTowardsYAxis,JKQTPGLabelAboveData,JKQTPGLabelRightHandSide,JKQTPGLabelBelowData,JKQTPGLabelLeftHandSide,JKQTPGLabelCenteredOnData,JKQTPGLabelCenteredOnDataVertical,JKQTPGLSimpleBox,JKQTPGLSimpleBoxVertical,JKQTPGLSimpleBoxAndLine,JKQTPGLSimpleBoxAndLineVertical,JKQTPGLSimpleBoxAndLineONLYLABELS,JKQTPGLSimpleBoxAndLineONLYLABELSVertical/--iteratefunctorsteps--smallscreenshotplot
|
graphlabels/JKQTPGLabelAwayFromXAxis,JKQTPGLabelAwayFromYAxis,JKQTPGLabelTowardsXAxis,JKQTPGLabelTowardsYAxis,JKQTPGLabelAboveData,JKQTPGLabelRightHandSide,JKQTPGLabelBelowData,JKQTPGLabelLeftHandSide,JKQTPGLabelCenteredOnData,JKQTPGLabelCenteredOnDataVertical,JKQTPGLSimpleBox,JKQTPGLSimpleBoxVertical,JKQTPGLSimpleBoxAndLine,JKQTPGLSimpleBoxAndLineVertical,JKQTPGLSimpleBoxAndLineONLYLABELS,JKQTPGLSimpleBoxAndLineONLYLABELSVertical/--iteratefunctorsteps--smallscreenshotplot
|
||||||
vectorfield/JKQTPVectorFieldGraph,JKQTPVectorFieldGraphAnchorBottom,JKQTPVectorFieldGraphAnchorMid,JKQTPVectorFieldGraphAnchorTip,JKQTPVectorFieldGraphAutoscaleLength,JKQTPVectorFieldGraphLengthFromData,JKQTPVectorFieldGraphIgnoreLength,JKQTPVectorFieldGraphIgnoreLengthAutoscaleLineWidthFromLength,JKQTPVectorFieldGraphAutoscaleLengthAutoscaleLineWidthFromLength/--iteratefunctorsteps
|
vectorfield/JKQTPVectorFieldGraph,JKQTPVectorFieldGraphAnchorBottom,JKQTPVectorFieldGraphAnchorMid,JKQTPVectorFieldGraphAnchorTip,JKQTPVectorFieldGraphAutoscaleLength,JKQTPVectorFieldGraphLengthFromData,JKQTPVectorFieldGraphIgnoreLength,JKQTPVectorFieldGraphIgnoreLengthAutoscaleLineWidthFromLength,JKQTPVectorFieldGraphAutoscaleLengthAutoscaleLineWidthFromLength/--iteratefunctorsteps
|
||||||
paramvectorfield/JKQTPParametrizedVectorFieldGraph,JKQTPParametrizedVectorFieldGraphColorFromMagnitude,JKQTPParametrizedVectorFieldGraphColorFromAngle,JKQTPParametrizedVectorFieldGraphDefaultColor/--iteratefunctorsteps
|
paramvectorfield/JKQTPParametrizedVectorFieldGraph,JKQTPParametrizedVectorFieldGraphColorFromMagnitude,JKQTPParametrizedVectorFieldGraphColorFromAngle,JKQTPParametrizedVectorFieldGraphDefaultColor/--iteratefunctorsteps
|
||||||
|
financialgraphs/JKQTPFinancialGraph,JKQTPFinancialGraphCandleStick,JKQTPFinancialGraphSetCandlestickTwoColor,JKQTPFinancialGraphSetCandlestickTwoColor2,JKQTPFinancialGraphSetCandlestickOneColor,JKQTPFinancialGraphOHLC,JKQTPFinancialGraphSetOHLCTwoColor,JKQTPFinancialGraphSidyBySide/--iteratefunctorsteps
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,6 +100,9 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
|
|||||||
<tr><td> \image html paramvectorfield_small.png
|
<tr><td> \image html paramvectorfield_small.png
|
||||||
<td> \subpage JKQTPParametrizedVectorFieldGraphExample
|
<td> \subpage JKQTPParametrizedVectorFieldGraphExample
|
||||||
<td> `JKQTPParametrizedVectorFieldGraph`
|
<td> `JKQTPParametrizedVectorFieldGraph`
|
||||||
|
<tr><td> \image html financialgraph_small.png
|
||||||
|
<td> \subpage JKQTPlotterFinancialChartExample
|
||||||
|
<td> `JKQTPFinancialGraph` (Candlestick/OHLC graphs), date-axis, rotated labels
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
@ -303,6 +303,20 @@ This group assembles graphs that represent vector fields (i.e. sets of quadruple
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
\defgroup jkqtplotter_financialgraphs Financial Graphs
|
||||||
|
\ingroup jkqtplotter_concretegraphs
|
||||||
|
|
||||||
|
This group assembles graphs that represent financial data (such as price mmovement of stocks, derivatives, ... and other traded goods) in a plot:
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th> Screenshot
|
||||||
|
<th> Classes
|
||||||
|
<tr>
|
||||||
|
<td>\image html financialgraphs_small.png
|
||||||
|
<td> JKQTPFinancialGraph
|
||||||
|
</table>
|
||||||
|
|
||||||
\defgroup jkqtplotter_imagelots Matrix/Image Plotting
|
\defgroup jkqtplotter_imagelots Matrix/Image Plotting
|
||||||
\ingroup jkqtplotter_concretegraphs
|
\ingroup jkqtplotter_concretegraphs
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
- \ref jkqtplotter_imagelots_elements "extensive library of image plots" (inclusing different color-scale modes, RGBA-plots, overlays/masks)
|
- \ref jkqtplotter_imagelots_elements "extensive library of image plots" (inclusing different color-scale modes, RGBA-plots, overlays/masks)
|
||||||
- \ref jkqtplotter_imagelots_contour "contour plots"
|
- \ref jkqtplotter_imagelots_contour "contour plots"
|
||||||
- \ref jkqtplotter_vectorfieldgraphs "vector field plots"
|
- \ref jkqtplotter_vectorfieldgraphs "vector field plots"
|
||||||
|
- \ref jkqtplotter_financialgraphs "financial charts (candlestick, OHLC, ...)
|
||||||
- \ref jkqtplotter_geoplots "geometric forms/annotations"
|
- \ref jkqtplotter_geoplots "geometric forms/annotations"
|
||||||
- can be easily extended by deriving a new graph from JKQTPPlotElement or JKQTPGeometricPlotElement
|
- can be easily extended by deriving a new graph from JKQTPPlotElement or JKQTPGeometricPlotElement
|
||||||
- <b>OPTIONAL: Interfaces to external libraries</b>
|
- <b>OPTIONAL: Interfaces to external libraries</b>
|
||||||
|
@ -14,8 +14,6 @@ This page lists several todos and wishes for future version of JKQTPlotter
|
|||||||
<li>graphic elements: annotation graphic element with text positionable like legend, or with (0..1),(0..1)-coordinates within plot</li>
|
<li>graphic elements: annotation graphic element with text positionable like legend, or with (0..1),(0..1)-coordinates within plot</li>
|
||||||
<li>graphic elements: cubic/bezier curves for graphic elements</li>
|
<li>graphic elements: cubic/bezier curves for graphic elements</li>
|
||||||
<li>graphic elements: make coordinate systems selectable for all: x/y-axis, 0..1/0..1, topleft/topright... </li>
|
<li>graphic elements: make coordinate systems selectable for all: x/y-axis, 0..1/0..1, topleft/topright... </li>
|
||||||
<li>graphs: add OHLC charts (financial, see https://en.m.wikipedia.org/wiki/Open-high-low-close_chart)</li>
|
|
||||||
<li>graphs: add candlestick charts (financial, see https://en.m.wikipedia.org/wiki/Candlestick_chart)</li>
|
|
||||||
<li>graphs: barchart/ranges chart with (x,y1,y2) or (x1,x2,y)</li>
|
<li>graphs: barchart/ranges chart with (x,y1,y2) or (x1,x2,y)</li>
|
||||||
<li>graphs: gant-chart as simplified vector field with (x,y1,y2) or (x1,x2,y), or (x,y,dx), (x,y,dy) ... different head/tail style</li>
|
<li>graphs: gant-chart as simplified vector field with (x,y1,y2) or (x1,x2,y), or (x,y,dx), (x,y,dy) ... different head/tail style</li>
|
||||||
<li>graphs: matrix plots with boxes, labels, ...</li>
|
<li>graphs: matrix plots with boxes, labels, ...</li>
|
||||||
|
@ -131,6 +131,7 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
|
|||||||
<li>NEW: Base class JKQTPXYAndVectorGraph for graphs like vector fields, e.g. (x,y,dx,dy) or (x,y,angle,length) </li>
|
<li>NEW: Base class JKQTPXYAndVectorGraph for graphs like vector fields, e.g. (x,y,dx,dy) or (x,y,angle,length) </li>
|
||||||
<li>NEW: JKQTPVectorFieldGraph for drawing "simple" vector fields/quiver plots (+example \ref JKQTPlotterVectorFieldExample)</li>
|
<li>NEW: JKQTPVectorFieldGraph for drawing "simple" vector fields/quiver plots (+example \ref JKQTPlotterVectorFieldExample)</li>
|
||||||
<li>NEW: JKQTPParametrizedVectorFieldGraph for drawing color-coded vector fields/quiver plots (+example \ref JKQTPParametrizedVectorFieldGraphExample)</li>
|
<li>NEW: JKQTPParametrizedVectorFieldGraph for drawing color-coded vector fields/quiver plots (+example \ref JKQTPParametrizedVectorFieldGraphExample)</li>
|
||||||
|
<li>NEW: JKQTPFinancialGraph for drawing candlestick or OHLC graphs of financial data, such as stock amrket prices (+example \ref JKQTPlotterFinancialChartExample)</li>
|
||||||
</ul></li>
|
</ul></li>
|
||||||
|
|
||||||
<li>JKQTMathText:<ul>
|
<li>JKQTMathText:<ul>
|
||||||
|
BIN
doc/images/JKQTPFinancialGraph.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
doc/images/JKQTPFinancialGraphCandleStick.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
doc/images/JKQTPFinancialGraphOHLC.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
doc/images/JKQTPFinancialGraphSetCandlestickOneColor.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
doc/images/JKQTPFinancialGraphSetCandlestickTwoColor.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
doc/images/JKQTPFinancialGraphSetCandlestickTwoColor2.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
doc/images/JKQTPFinancialGraphSetOHLCTwoColor.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
doc/images/JKQTPFinancialGraphSidyBySide.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
doc/images/financialgraphs_basics_candlestick.cdr
Normal file
BIN
doc/images/financialgraphs_basics_candlestick.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
doc/images/financialgraphs_basics_ohlc.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
doc/images/financialgraphs_basics_olhc.cdr
Normal file
BIN
doc/images/financialgraphs_shiftwidth.cdr
Normal file
BIN
doc/images/financialgraphs_shiftwidth.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
doc/images/financialgraphs_width.cdr
Normal file
BIN
doc/images/financialgraphs_width.png
Normal file
After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 320 KiB After Width: | Height: | Size: 298 KiB |
@ -69,6 +69,7 @@ if (JKQtPlotter_BUILD_LIB_JKQTPLOTTER)
|
|||||||
add_subdirectory(evalcurve)
|
add_subdirectory(evalcurve)
|
||||||
add_subdirectory(filledgraphs)
|
add_subdirectory(filledgraphs)
|
||||||
add_subdirectory(filledgraphs_errors)
|
add_subdirectory(filledgraphs_errors)
|
||||||
|
add_subdirectory(financialgraphs)
|
||||||
add_subdirectory(functionplot)
|
add_subdirectory(functionplot)
|
||||||
add_subdirectory(geo_arrows)
|
add_subdirectory(geo_arrows)
|
||||||
add_subdirectory(geo_simple)
|
add_subdirectory(geo_simple)
|
||||||
|
25
examples/financialgraphs/CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.23)
|
||||||
|
|
||||||
|
set(EXAMPLE_NAME financialgraphs)
|
||||||
|
set(EXENAME jkqtptest_${EXAMPLE_NAME})
|
||||||
|
|
||||||
|
message( STATUS ".. Building Example ${EXAMPLE_NAME}" )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
add_executable(${EXENAME} WIN32 ${EXAMPLE_NAME}.cpp)
|
||||||
|
target_link_libraries(${EXENAME} JKQTPExampleToolsLib)
|
||||||
|
target_include_directories(${EXENAME} PRIVATE ../../lib)
|
||||||
|
target_link_libraries(${EXENAME} ${jkqtplotter_namespace}JKQTPlotter${jkqtplotter_LIBNAME_VERSION_PART})
|
||||||
|
|
||||||
|
# precomiled headers to speed up compilation
|
||||||
|
if (JKQtPlotter_BUILD_WITH_PRECOMPILED_HEADERS)
|
||||||
|
target_precompile_headers(${EXENAME} REUSE_FROM jkqtptest_simpletest)
|
||||||
|
endif (JKQtPlotter_BUILD_WITH_PRECOMPILED_HEADERS)
|
||||||
|
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
install(TARGETS ${EXENAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
|
||||||
|
#Installation of Qt DLLs on Windows
|
||||||
|
jkqtplotter_deployqt(${EXENAME})
|
27
examples/financialgraphs/README.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Example (JKQTPlotter): Financial Chart (Candlestick/OHLC) Example {#JKQTPlotterFinancialChartExample}
|
||||||
|
This project (see [`financialgraphs`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/financialgraphs) demonstrates the use of JKQTPFinancialGraph to visualize financial data (i.e. market price development of stocks, derivatives, ...).
|
||||||
|
|
||||||
|
The source code of the main application is (see [`financialgraphs.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/financialgraphs/financialgraphs.cpp).
|
||||||
|
|
||||||
|
Here is a short summary of the important parts of the code:
|
||||||
|
|
||||||
|
```.cpp
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
The result looks like this:
|
||||||
|
|
||||||
|
![financialgraphs](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/financialgraphs.png)
|
||||||
|
|
||||||
|
|
||||||
|
By default, the length of the drawn vector is determined from the actual length in the data via an autoscaling algorithm that is supposed to prevent the vectors from overlapping.
|
||||||
|
But you can modify this behaviour by adding a line
|
||||||
|
|
||||||
|
```.cpp
|
||||||
|
graph1->setVectorLengthMode(JKQTPVectorFieldGraph::LengthFromData);
|
||||||
|
```
|
||||||
|
|
||||||
|
which will use the given lengths directly (only scaled by an optional factor defined by JKQTPVectorFieldGraph::setLengthScaleFactor() ). The result then looks like this:
|
||||||
|
|
||||||
|
![financialgraphs](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/doc/images/JKQTPVectorFieldGraphLengthFromData.png)
|
||||||
|
|
175
examples/financialgraphs/financialgraphs.cpp
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
/** \example financialgraphs.cpp
|
||||||
|
* Display financial data
|
||||||
|
*
|
||||||
|
* \ref JKQTPlotterFinancialChartExample
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "jkqtpexampleapplication.h"
|
||||||
|
#include <QApplication>
|
||||||
|
#include "jkqtplotter/jkqtplotter.h"
|
||||||
|
#include "jkqtplotter/graphs/jkqtpfinancial.h"
|
||||||
|
#include "jkqtpexampleapplication.h"
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
JKQTPAppSettingController highDPIController(argc,argv);
|
||||||
|
JKQTPExampleApplication app(argc, argv);
|
||||||
|
|
||||||
|
|
||||||
|
// 1. setup a plotter window and get a pointer to the internal datastore (for convenience)
|
||||||
|
JKQTPlotter plot;
|
||||||
|
plot.getPlotter()->setUseAntiAliasingForGraphs(true); // nicer (but slower) plotting
|
||||||
|
plot.getPlotter()->setUseAntiAliasingForSystem(true); // nicer (but slower) plotting
|
||||||
|
plot.getPlotter()->setUseAntiAliasingForText(true); // nicer (but slower) text rendering
|
||||||
|
JKQTPDatastore* ds=plot.getDatastore();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 2. now we create 5 datacolumns with length Ndays entries in the datastore
|
||||||
|
// these will later hold the time-step and simulated market data of two stocks
|
||||||
|
const size_t NDays=31;
|
||||||
|
const size_t columnT=ds->addColumn("time");
|
||||||
|
const size_t columnO1=ds->addColumn("open(stock 1)");
|
||||||
|
const size_t columnC1=ds->addColumn("close(stock 1)");
|
||||||
|
const size_t columnL1=ds->addColumn("low(stock 1)");
|
||||||
|
const size_t columnH1=ds->addColumn("high(stock 1)");
|
||||||
|
const size_t columnO2=ds->addColumn("open(stock 2)");
|
||||||
|
const size_t columnC2=ds->addColumn("close(stock 2)");
|
||||||
|
const size_t columnL2=ds->addColumn("low(stock 2)");
|
||||||
|
const size_t columnH2=ds->addColumn("high(stock 2)");
|
||||||
|
const QDate t0(2024,1,8); // start date
|
||||||
|
|
||||||
|
// 3. now we simulate stock market data with a simple random walk
|
||||||
|
// with a few steps per day that are summmarized into the four
|
||||||
|
// properties Open,Close,Low,High for each day
|
||||||
|
double price1=30; // start price of stock 1
|
||||||
|
double price2=25; // start price of stock 2
|
||||||
|
const size_t stepsPerDay=20;
|
||||||
|
std::random_device rd; // random number generators:
|
||||||
|
std::mt19937 gen{rd()};
|
||||||
|
gen.seed(12352);
|
||||||
|
std::normal_distribution<double> dist1(0.001, 0.6);
|
||||||
|
std::normal_distribution<double> dist2(-0.0001, 0.4);
|
||||||
|
for (size_t t=0; t<NDays; t+=1) {// iterate over all simulated days (we don't care for weekends)
|
||||||
|
// step time from noon to noon in steps of days
|
||||||
|
const QDate d=t0.addDays(t);
|
||||||
|
ds->appendToColumn(columnT, QDateTime(d,QTime(12,0,0)).toMSecsSinceEpoch());
|
||||||
|
// the open-Value is the start-price of each day:
|
||||||
|
ds->appendToColumn(columnO1, price1);
|
||||||
|
ds->appendToColumn(columnO2, price2);
|
||||||
|
|
||||||
|
// now we iterate the market over the day
|
||||||
|
double L1=price1, L2=price2, H1=price1, H2=price2;
|
||||||
|
for (size_t i=0; i<stepsPerDay; i++) { // iterate over week days (Mon-Fri)
|
||||||
|
// prices are calculated from a sinple random walk (the while-loops ensure that we have positive prices only
|
||||||
|
double np;
|
||||||
|
while ((np=price1+dist1(gen))<=0.0); price1=np;
|
||||||
|
while ((np=price2+dist2(gen))<=0.0); price2=np;
|
||||||
|
// now we track the highest/lowest prices
|
||||||
|
L1=qMin(L1, price1);
|
||||||
|
L2=qMin(L2, price2);
|
||||||
|
H1=qMax(H1, price1);
|
||||||
|
H2=qMax(H2, price2);
|
||||||
|
}
|
||||||
|
// the close-Value is the final price of each day:
|
||||||
|
ds->appendToColumn(columnC1, price1);
|
||||||
|
ds->appendToColumn(columnC2, price2);
|
||||||
|
|
||||||
|
// finally we just have to add the highest and lowest prices of each day:
|
||||||
|
ds->appendToColumn(columnL1, L1);
|
||||||
|
ds->appendToColumn(columnL2, L2);
|
||||||
|
ds->appendToColumn(columnH1, H1);
|
||||||
|
ds->appendToColumn(columnH2, H2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. create two JKQTPFinancialGraph to display the data:
|
||||||
|
JKQTPFinancialGraph* graphCandleStick=new JKQTPFinancialGraph(&plot);
|
||||||
|
graphCandleStick->setXColumn(columnT);
|
||||||
|
graphCandleStick->setOpenColumn(columnO1);
|
||||||
|
graphCandleStick->setHighColumn(columnH1);
|
||||||
|
graphCandleStick->setLowColumn(columnL1);
|
||||||
|
graphCandleStick->setCloseColumn(columnC1);
|
||||||
|
graphCandleStick->setGraphType(JKQTPFinancialGraph::CandleStick);
|
||||||
|
graphCandleStick->setTitle(QObject::tr("stock 1 (candlestick)"));
|
||||||
|
graphCandleStick->setCandlestickTwoColor(QColor("darkgreen"), QColor("maroon"));
|
||||||
|
|
||||||
|
JKQTPFinancialGraph* graphOHLC=new JKQTPFinancialGraph(&plot);
|
||||||
|
graphOHLC->setXColumn(columnT);
|
||||||
|
graphOHLC->setOpenColumn(columnO2);
|
||||||
|
graphOHLC->setHighColumn(columnH2);
|
||||||
|
graphOHLC->setLowColumn(columnL2);
|
||||||
|
graphOHLC->setCloseColumn(columnC2);
|
||||||
|
graphOHLC->setGraphType(JKQTPFinancialGraph::OHLC);
|
||||||
|
graphOHLC->setTitle(QObject::tr("stock 2 (OHLC)"));
|
||||||
|
graphOHLC->setOHLCTwoColor(QColor("darkgreen"), QColor("maroon"));
|
||||||
|
|
||||||
|
// 4. add the graphs to the plot, so it is actually displayed
|
||||||
|
plot.addGraph(graphCandleStick);
|
||||||
|
plot.addGraph(graphOHLC);
|
||||||
|
|
||||||
|
// 5. scale the plot so the graph is contained and make x-axis a date-axis
|
||||||
|
plot.getXAxis()->setTickLabelType(JKQTPCALTdate);
|
||||||
|
plot.getXAxis()->setAxisLabel("time");
|
||||||
|
plot.getXAxis()->setTickLabelAngle(35); // rotate axis tick label by 35°, so they are readable
|
||||||
|
plot.getYAxis()->setAxisLabel("stock price [\\$]");
|
||||||
|
plot.getMainKey()->setPosition(JKQTPKeyInsideTopLeft);
|
||||||
|
plot.zoomToFit();
|
||||||
|
|
||||||
|
// show plotter and make it a decent size
|
||||||
|
plot.setWindowTitle("JKQTPVectorFieldGraph example");
|
||||||
|
plot.show();
|
||||||
|
plot.resize(600/plot.devicePixelRatioF(),450/plot.devicePixelRatioF());
|
||||||
|
|
||||||
|
app.addExportStepFunctor([&](){
|
||||||
|
graphCandleStick->setVisible(true);
|
||||||
|
graphOHLC->setVisible(false);
|
||||||
|
plot.zoomToFit();
|
||||||
|
plot.setX(plot.getXMin(), plot.getXMin()+(plot.getXMax()-plot.getXMin())/2.0);
|
||||||
|
plot.redrawPlot();
|
||||||
|
});
|
||||||
|
app.addExportStepFunctor([&](){
|
||||||
|
graphCandleStick->setCandlestickTwoColor(QColor("blue"), QColor("orange"));
|
||||||
|
plot.redrawPlot();
|
||||||
|
});
|
||||||
|
app.addExportStepFunctor([&](){
|
||||||
|
graphCandleStick->setCandlestickTwoColor(QColor("green"), QColor("red"), QColor("black"));
|
||||||
|
plot.redrawPlot();
|
||||||
|
});
|
||||||
|
app.addExportStepFunctor([&](){
|
||||||
|
graphCandleStick->setCandlestickOneColor(QColor("black"));
|
||||||
|
plot.redrawPlot();
|
||||||
|
});
|
||||||
|
app.addExportStepFunctor([&](){
|
||||||
|
graphCandleStick->setVisible(false);
|
||||||
|
graphOHLC->setVisible(true);
|
||||||
|
plot.zoomToFit();
|
||||||
|
plot.setX(plot.getXMin(), plot.getXMin()+(plot.getXMax()-plot.getXMin())/2.0);
|
||||||
|
plot.redrawPlot();
|
||||||
|
});
|
||||||
|
app.addExportStepFunctor([&](){
|
||||||
|
graphOHLC->setOHLCTwoColor(QColor("darkblue"), QColor("darkorange"));
|
||||||
|
plot.redrawPlot();
|
||||||
|
});
|
||||||
|
app.addExportStepFunctor([&](){
|
||||||
|
for (size_t t=0; t<NDays; t+=1) {
|
||||||
|
const double drift=-3.0*static_cast<double>(t)/static_cast<double>(NDays);
|
||||||
|
ds->set(columnO2, t, ds->get(columnO1,t)+drift);
|
||||||
|
ds->set(columnH2, t, ds->get(columnH1,t)+drift);
|
||||||
|
ds->set(columnL2, t, ds->get(columnL1,t)+drift);
|
||||||
|
ds->set(columnC2, t, ds->get(columnC1,t)+drift);
|
||||||
|
}
|
||||||
|
graphCandleStick->setVisible(true);
|
||||||
|
graphOHLC->setVisible(true);
|
||||||
|
graphCandleStick->setCandlestickTwoColor(QColor("darkgreen"), QColor("maroon"));
|
||||||
|
graphOHLC->setCandlestickTwoColor(QColor("blue"), QColor("orange"));
|
||||||
|
graphCandleStick->autoscaleBoxWidthAndShiftSeparatedGroups();
|
||||||
|
plot.zoomToFit();
|
||||||
|
plot.setX(plot.getXMin(), plot.getXMin()+(plot.getXMax()-plot.getXMin())/2.0);
|
||||||
|
plot.redrawPlot();
|
||||||
|
});
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
@ -60,6 +60,7 @@ isEmpty(JKQTP_PLOTTER_PRI_INCLUDED) {
|
|||||||
$$PWD/jkqtplotter/graphs/jkqtpgraphlabelstylemixin.h \
|
$$PWD/jkqtplotter/graphs/jkqtpgraphlabelstylemixin.h \
|
||||||
$$PWD/jkqtplotter/graphs/jkqtpgraphlabels.h \
|
$$PWD/jkqtplotter/graphs/jkqtpgraphlabels.h \
|
||||||
$$PWD/jkqtplotter/graphs/jkqtpvectorfield.h \
|
$$PWD/jkqtplotter/graphs/jkqtpvectorfield.h \
|
||||||
|
$$PWD/jkqtplotter/graphs/jkqtpfinancial.h \
|
||||||
$$PWD/jkqtplotter/gui/jkqtpcomboboxes.h \
|
$$PWD/jkqtplotter/gui/jkqtpcomboboxes.h \
|
||||||
$$PWD/jkqtplotter/gui/jkqtpenhancedspinboxes.h \
|
$$PWD/jkqtplotter/gui/jkqtpenhancedspinboxes.h \
|
||||||
$$PWD/jkqtplotter/gui/jkqtpenhancedtableview.h \
|
$$PWD/jkqtplotter/gui/jkqtpenhancedtableview.h \
|
||||||
@ -117,6 +118,7 @@ isEmpty(JKQTP_PLOTTER_PRI_INCLUDED) {
|
|||||||
$$PWD/jkqtplotter/graphs/jkqtpgraphlabelstylemixin.cpp \
|
$$PWD/jkqtplotter/graphs/jkqtpgraphlabelstylemixin.cpp \
|
||||||
$$PWD/jkqtplotter/graphs/jkqtpgraphlabels.cpp \
|
$$PWD/jkqtplotter/graphs/jkqtpgraphlabels.cpp \
|
||||||
$$PWD/jkqtplotter/graphs/jkqtpvectorfield.cpp \
|
$$PWD/jkqtplotter/graphs/jkqtpvectorfield.cpp \
|
||||||
|
$$PWD/jkqtplotter/graphs/jkqtpfinancial.cpp \
|
||||||
$$PWD/jkqtplotter/gui/jkqtpcomboboxes.cpp \
|
$$PWD/jkqtplotter/gui/jkqtpcomboboxes.cpp \
|
||||||
$$PWD/jkqtplotter/gui/jkqtpenhancedspinboxes.cpp \
|
$$PWD/jkqtplotter/gui/jkqtpenhancedspinboxes.cpp \
|
||||||
$$PWD/jkqtplotter/gui/jkqtpenhancedtableview.cpp \
|
$$PWD/jkqtplotter/gui/jkqtpenhancedtableview.cpp \
|
||||||
|
@ -13,6 +13,7 @@ target_sources(${lib_name} PRIVATE
|
|||||||
jkqtpevaluatedfunctionbase.cpp
|
jkqtpevaluatedfunctionbase.cpp
|
||||||
jkqtpevaluatedfunction.cpp
|
jkqtpevaluatedfunction.cpp
|
||||||
jkqtpfilledcurve.cpp
|
jkqtpfilledcurve.cpp
|
||||||
|
jkqtpfinancial.cpp
|
||||||
jkqtpgeometric.cpp
|
jkqtpgeometric.cpp
|
||||||
jkqtpgeoannotations.cpp
|
jkqtpgeoannotations.cpp
|
||||||
jkqtpgeobase.cpp
|
jkqtpgeobase.cpp
|
||||||
@ -44,6 +45,7 @@ target_sources(${lib_name} PUBLIC FILE_SET HEADERS TYPE HEADERS
|
|||||||
jkqtpevaluatedfunctionbase.h
|
jkqtpevaluatedfunctionbase.h
|
||||||
jkqtpevaluatedfunction.h
|
jkqtpevaluatedfunction.h
|
||||||
jkqtpfilledcurve.h
|
jkqtpfilledcurve.h
|
||||||
|
jkqtpfinancial.h
|
||||||
jkqtpgeometric.h
|
jkqtpgeometric.h
|
||||||
jkqtpgeoannotations.h
|
jkqtpgeoannotations.h
|
||||||
jkqtpgeobase.h
|
jkqtpgeobase.h
|
||||||
|
508
lib/jkqtplotter/graphs/jkqtpfinancial.cpp
Normal file
@ -0,0 +1,508 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2008-2024 Jan W. Krieger (<jan@jkrieger.de>)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "jkqtplotter/graphs/jkqtpfinancial.h"
|
||||||
|
#include "jkqtplotter/jkqtpbaseplotter.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <iostream>
|
||||||
|
#include "jkqtplotter/jkqtptools.h"
|
||||||
|
#include "jkqtplotter/jkqtplotter.h"
|
||||||
|
|
||||||
|
#define SmallestGreaterZeroCompare_xvsgz() if ((xvsgz>10.0*DBL_MIN)&&((smallestGreaterZero<10.0*DBL_MIN) || (xvsgz<smallestGreaterZero))) smallestGreaterZero=xvsgz;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
JKQTPFinancialGraph::JKQTPFinancialGraph(JKQTBasePlotter* parent):
|
||||||
|
JKQTPXGraph(parent),
|
||||||
|
graphType(FinancialGraphType::CandleStick),
|
||||||
|
openColumn(-1),
|
||||||
|
closeColumn(-1),
|
||||||
|
highColumn(-1),
|
||||||
|
lowColumn(-1),
|
||||||
|
width(0.8),
|
||||||
|
shift(0)
|
||||||
|
|
||||||
|
{
|
||||||
|
m_fillStylePositive.initFillStyle(parent, parentPlotStyle, JKQTPPlotStyleType::Boxplot);
|
||||||
|
m_fillStyleNegative.initFillStyleInvertedColor(&m_fillStylePositive);
|
||||||
|
|
||||||
|
m_lineStylePositive.initLineStyle(parent, parentPlotStyle, JKQTPPlotStyleType::Boxplot);
|
||||||
|
m_lineStyleNegative.initLineStyle(parent, parentPlotStyle, JKQTPPlotStyleType::Boxplot);
|
||||||
|
m_lineStyleNegative.setLineColor(m_fillStyleNegative.getFillColor());
|
||||||
|
|
||||||
|
static QSet<JKQTBasePlotter*> parentsAlreadySeen;
|
||||||
|
if (!parentsAlreadySeen.contains(parent)) {
|
||||||
|
// ensure, the first graph in a plot has default red/green colors
|
||||||
|
// further plots will have colors
|
||||||
|
m_fillStylePositive.setFillColor(QColor("darkgreen"));
|
||||||
|
m_fillStyleNegative.setFillColor(QColor("maroon"));
|
||||||
|
m_lineStylePositive.setLineColor(QColor("darkgreen"));
|
||||||
|
m_lineStyleNegative.setLineColor(QColor("maroon"));
|
||||||
|
}
|
||||||
|
parentsAlreadySeen.insert(parent);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JKQTPFinancialGraph::JKQTPFinancialGraph(JKQTPlotter* parent):
|
||||||
|
JKQTPFinancialGraph(parent->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()<<rect.topLeft()<<rect.topRight()<<rect.bottomLeft());
|
||||||
|
painter.drawLine(r.center().x(),r.top(),r.center().x(),rect.top());
|
||||||
|
painter.setPen(pn);
|
||||||
|
painter.setBrush(bn);
|
||||||
|
painter.drawPolygon(QPolygonF()<<rect.topRight()<<rect.bottomRight()<<rect.bottomLeft());
|
||||||
|
painter.drawLine(r.center().x(),r.bottom(),r.center().x(),rect.bottom());
|
||||||
|
|
||||||
|
} else if (graphType==OHLC) {
|
||||||
|
painter.setPen(pp);
|
||||||
|
painter.setBrush(bp);
|
||||||
|
painter.drawLine(r.center().x(),r.top(),r.center().x(),r.center().y());
|
||||||
|
painter.drawLine(r.center().x(),r.top()+r.height()/4.0,r.center().x()+r.width()/4,r.top()+r.height()/4.0);
|
||||||
|
painter.setPen(pn);
|
||||||
|
painter.setBrush(bn);
|
||||||
|
painter.drawLine(r.center().x(),r.center().y(),r.center().x(),r.bottom());
|
||||||
|
painter.drawLine(r.center().x(),r.top()+r.height()*0.8,r.center().x()-r.width()/4,r.top()+r.height()*0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor JKQTPFinancialGraph::getKeyLabelColor() const {
|
||||||
|
return m_lineStylePositive.getLineColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::draw(JKQTPEnhancedPainter &painter)
|
||||||
|
{
|
||||||
|
#ifdef JKQTBP_AUTOTIMER
|
||||||
|
JKQTPAutoOutputTimer jkaaot("JKQTPFinancialGraph::draw");
|
||||||
|
#endif
|
||||||
|
if (parent==nullptr) return;
|
||||||
|
const JKQTPDatastore* datastore=parent->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)) {
|
||||||
|
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
|
||||||
|
|
||||||
|
double delta=1;
|
||||||
|
double deltap=0;
|
||||||
|
double deltam=0;
|
||||||
|
intSortData();
|
||||||
|
for (int iii=imin; iii<imax; iii++) {
|
||||||
|
const int i=qBound(imin, getDataIndex(iii), imax);
|
||||||
|
const double xv=datastore->get(static_cast<size_t>(xColumn),static_cast<size_t>(i));
|
||||||
|
const int sr=datastore->getNextLowerIndex(xColumn, i);
|
||||||
|
const int lr=datastore->getNextHigherIndex(xColumn, i);
|
||||||
|
const double vO=datastore->get(static_cast<size_t>(openColumn),static_cast<size_t>(i));
|
||||||
|
const double vC=datastore->get(static_cast<size_t>(closeColumn),static_cast<size_t>(i));
|
||||||
|
const double vL=datastore->get(static_cast<size_t>(lowColumn),static_cast<size_t>(i));
|
||||||
|
const double vH=datastore->get(static_cast<size_t>(highColumn),static_cast<size_t>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawErrorsAfter(painter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::autoscaleBoxWidthAndShift(double maxWidth, double shrinkFactor)
|
||||||
|
{
|
||||||
|
if (parent) {
|
||||||
|
double cntH=0;
|
||||||
|
for (size_t i=0; i<parent->getGraphCount(); i++) {
|
||||||
|
JKQTPPlotElement* g=parent->getGraph(i);
|
||||||
|
JKQTPFinancialGraph* gb=qobject_cast<JKQTPFinancialGraph*>(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; i<parent->getGraphCount(); i++) {
|
||||||
|
JKQTPPlotElement* g=parent->getGraph(i);
|
||||||
|
JKQTPFinancialGraph* gb=qobject_cast<JKQTPFinancialGraph*>(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<int>(datastore->getRows(static_cast<size_t>(openColumn)));
|
||||||
|
const int rowsC=static_cast<int>(datastore->getRows(static_cast<size_t>(closeColumn)));
|
||||||
|
const int rowsH=static_cast<int>(datastore->getRows(static_cast<size_t>(highColumn)));
|
||||||
|
const int rowsL=static_cast<int>(datastore->getRows(static_cast<size_t>(lowColumn)));
|
||||||
|
imax=qMin<int>(imax, rowsO);
|
||||||
|
imax=qMin<int>(imax, rowsC);
|
||||||
|
imax=qMin<int>(imax, rowsH);
|
||||||
|
imax=qMin<int>(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; i<imax; i++) {
|
||||||
|
const double yvO=datastore->get(static_cast<size_t>(openColumn),static_cast<size_t>(i));
|
||||||
|
if (JKQTPIsOKFloat(yvO)) {
|
||||||
|
if (start || yvO>maxy) maxy=yvO;
|
||||||
|
if (start || yvO<miny) miny=yvO;
|
||||||
|
double xvsgz;
|
||||||
|
xvsgz=yvO; SmallestGreaterZeroCompare_xvsgz();
|
||||||
|
start=false;
|
||||||
|
}
|
||||||
|
const double yvC=datastore->get(static_cast<size_t>(closeColumn),static_cast<size_t>(i));
|
||||||
|
if (JKQTPIsOKFloat(yvC)) {
|
||||||
|
if (start || yvC>maxy) maxy=yvC;
|
||||||
|
if (start || yvC<miny) miny=yvC;
|
||||||
|
double xvsgz;
|
||||||
|
xvsgz=yvC; SmallestGreaterZeroCompare_xvsgz();
|
||||||
|
start=false;
|
||||||
|
}
|
||||||
|
const double yvL=datastore->get(static_cast<size_t>(lowColumn),static_cast<size_t>(i));
|
||||||
|
if (JKQTPIsOKFloat(yvL)) {
|
||||||
|
if (start || yvL>maxy) maxy=yvL;
|
||||||
|
if (start || yvL<miny) miny=yvL;
|
||||||
|
double xvsgz;
|
||||||
|
xvsgz=yvL; SmallestGreaterZeroCompare_xvsgz();
|
||||||
|
start=false;
|
||||||
|
}
|
||||||
|
const double yvH=datastore->get(static_cast<size_t>(highColumn),static_cast<size_t>(i));
|
||||||
|
if (JKQTPIsOKFloat(yvH)) {
|
||||||
|
if (start || yvH>maxy) maxy=yvH;
|
||||||
|
if (start || yvH<miny) miny=yvH;
|
||||||
|
double xvsgz;
|
||||||
|
xvsgz=yvH; SmallestGreaterZeroCompare_xvsgz();
|
||||||
|
start=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !start;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JKQTPFinancialGraph::usesColumn(int column) const
|
||||||
|
{
|
||||||
|
return JKQTPXGraph::usesColumn(column)||(column==openColumn)||(column==closeColumn)||(column==highColumn)||(column==lowColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int JKQTPFinancialGraph::getOpenColumn() const
|
||||||
|
{
|
||||||
|
return openColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JKQTPFinancialGraph::getCloseColumn() const
|
||||||
|
{
|
||||||
|
return closeColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JKQTPFinancialGraph::getHighColumn() const
|
||||||
|
{
|
||||||
|
return highColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JKQTPFinancialGraph::getLowColumn() const
|
||||||
|
{
|
||||||
|
return lowColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
JKQTPFinancialGraph::FinancialGraphType JKQTPFinancialGraph::getGraphType() const
|
||||||
|
{
|
||||||
|
return graphType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::setGraphType(JKQTPFinancialGraph::FinancialGraphType t)
|
||||||
|
{
|
||||||
|
graphType=t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::setColor(QColor cPositive, QColor cNegative)
|
||||||
|
{
|
||||||
|
m_fillStylePositive.setFillColor(cPositive);
|
||||||
|
m_fillStyleNegative.setFillColor(cNegative);
|
||||||
|
m_lineStylePositive.setLineColor(cPositive);
|
||||||
|
m_lineStyleNegative.setLineColor(cNegative);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::setColor(QColor linePositive, QColor fillPositive, QColor lineNegative, QColor fillNegative)
|
||||||
|
{
|
||||||
|
m_fillStylePositive.setFillColor(fillPositive);
|
||||||
|
m_fillStyleNegative.setFillColor(fillNegative);
|
||||||
|
m_lineStylePositive.setLineColor(linePositive);
|
||||||
|
m_lineStyleNegative.setLineColor(lineNegative);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::setCandlestickTwoColor(QColor cPositive, QColor cNegative)
|
||||||
|
{
|
||||||
|
setGraphType(CandleStick);
|
||||||
|
setColor(cPositive, cNegative);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::setCandlestickTwoColor(QColor cPositive, QColor cNegative, QColor lineColor)
|
||||||
|
{
|
||||||
|
setGraphType(CandleStick);
|
||||||
|
setColor(lineColor, cPositive, lineColor, cNegative);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::setCandlestickOneColor(QColor cLine)
|
||||||
|
{
|
||||||
|
setGraphType(CandleStick);
|
||||||
|
if (parent) setColor(cLine,parent->getCurrentPlotterStyle().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<int>(__value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::setOpenColumn(size_t __value)
|
||||||
|
{
|
||||||
|
openColumn=static_cast<int>(__value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::setCloseColumn(int __value)
|
||||||
|
{
|
||||||
|
closeColumn=static_cast<int>(__value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::setCloseColumn(size_t __value)
|
||||||
|
{
|
||||||
|
closeColumn=static_cast<int>(__value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::setHighColumn(int __value)
|
||||||
|
{
|
||||||
|
highColumn=static_cast<int>(__value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::setHighColumn(size_t __value)
|
||||||
|
{
|
||||||
|
highColumn=static_cast<int>(__value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::setLowColumn(int __value)
|
||||||
|
{
|
||||||
|
lowColumn=static_cast<int>(__value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPFinancialGraph::setLowColumn(size_t __value)
|
||||||
|
{
|
||||||
|
lowColumn=static_cast<int>(__value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JKQTPFinancialGraph::considerForAutoscaling(JKQTPFinancialGraph *other) const
|
||||||
|
{
|
||||||
|
return (dynamic_cast<JKQTPFinancialGraph*>(other)!=nullptr);
|
||||||
|
}
|
337
lib/jkqtplotter/graphs/jkqtpfinancial.h
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2008-2024 Jan W. Krieger (<jan@jkrieger.de>)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef jkqtpfinancial_H
|
||||||
|
#define jkqtpfinancial_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPair>
|
||||||
|
#include "jkqtplotter/jkqtptools.h"
|
||||||
|
#include "jkqtplotter/jkqtplotter_imexport.h"
|
||||||
|
#include "jkqtplotter/jkqtpgraphsbase.h"
|
||||||
|
#include "jkqtplotter/jkqtpgraphsbasestylingmixins.h"
|
||||||
|
|
||||||
|
|
||||||
|
/** \brief This class draws an Open-High-Low-Close (OHLC) or candle-stick graph typically representing financial data
|
||||||
|
* \ingroup jkqtplotter_financialgraphs
|
||||||
|
*
|
||||||
|
* This graph draws either <b>candlstick charts</b>:
|
||||||
|
* \image html financialgraphs_basics_candlestick.png
|
||||||
|
* or <b>Open-High-Low-Close (OHLC) charts</b> (depending on what you set in setGraphType() ).
|
||||||
|
* \image html financialgraphs_basics_ohlc.png
|
||||||
|
*
|
||||||
|
* Here is an example graph showcasing both variants:
|
||||||
|
*
|
||||||
|
* \image html JKQTPFinancialGraph.png
|
||||||
|
*
|
||||||
|
* \see For details on the graph types see https://en.wikipedia.org/wiki/Candlestick_chart and https://en.wikipedia.org/wiki/Open-high-low-close_chart
|
||||||
|
* \see JKQTPXGraph
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* \section JKQTPFinancialGraphStyling Styling the Financial Graphs
|
||||||
|
* You can control the type of graph and it's appearance with these functions (example images are in the documentation of the functions):
|
||||||
|
* - setGraphType()
|
||||||
|
* - setCandlestickTwoColor()
|
||||||
|
* - setCandlestickOneColor()
|
||||||
|
* - setOHLCTwoColor()
|
||||||
|
* .
|
||||||
|
*
|
||||||
|
* For fine-tuning, you can access the line- and fill-styles for positiove (close>open) and negative (close<open)
|
||||||
|
* items separately via:
|
||||||
|
* - lineStylePositive() and fillStylePositive()
|
||||||
|
* - lineStyleNegative() and fillStyleNegative()
|
||||||
|
* .
|
||||||
|
*
|
||||||
|
* Note that the width of the single elements for each x-value is automatically from the distance to the
|
||||||
|
* next neighbours. Here set parameter width (setWidth(), getWidth() ) is employed. It sets the relative
|
||||||
|
* amount of space that an element occupies in its part of the graph. Typically the default width is 0.8 (or 80%).
|
||||||
|
*
|
||||||
|
* \image html financialgraphs_width.png
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* \section JKQTPFinancialGraphSideBySide Severyl Financial Graphs side-by-side
|
||||||
|
* In case you want to display several of these graphs in the same plot, you can make them appear side-by-side, even
|
||||||
|
* if they all cover the same x-values and would thus be draw atop each other. We apply the same principles that
|
||||||
|
* were used for barcharts in JKQTPBarGraphBase and add a shift-parameter (setShift() ) that shifts each element
|
||||||
|
* in the available space:
|
||||||
|
*
|
||||||
|
* \image html financialgraphs_basics.png
|
||||||
|
*
|
||||||
|
* For example for two financial graph elements per x-value one would set:
|
||||||
|
* \verbatim
|
||||||
|
* width=0.4
|
||||||
|
* shift=-0.5 / +0.5
|
||||||
|
* \endverbatim
|
||||||
|
*
|
||||||
|
* \image html financialgraphs_shiftwidth.png
|
||||||
|
*
|
||||||
|
* Two functions are provided (JKQTPFinancialGraph::autoscaleBoxWidthAndShift(), JKQTPFinancialGraph::autoscaleBoxWidthAndShiftSeparatedGroups() )
|
||||||
|
* that can be applied to any JKQTPFinancialGraph in the plot and will calculate all JKQTPFinancialGraphs' shift and width parameter
|
||||||
|
* in such a way that the graphs appear tidied up into groups by x-value. With these functions you don't have to calculate
|
||||||
|
* the shift and width values by hand! A call always only affects the JKQTPFinancialGraph in the plot with the same orientation
|
||||||
|
* (vertical or horizontal).
|
||||||
|
*
|
||||||
|
* \image html JKQTPFinancialGraphSidyBySide.png
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class JKQTPLOTTER_LIB_EXPORT JKQTPFinancialGraph: public JKQTPXGraph {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
/** \brief types of financial graphs that can be drawn by this class */
|
||||||
|
enum FinancialGraphType {
|
||||||
|
CandleStick, //!< \brief candlestick graphs \image html JKQTPFinancialGraphCandleStick.png
|
||||||
|
OHLC //!< \brief OHLC (Open-High-Low-Close) graphs \image html JKQTPFinancialGraphOHLC.png
|
||||||
|
};
|
||||||
|
Q_ENUM(FinancialGraphType)
|
||||||
|
|
||||||
|
/** \brief class constructor */
|
||||||
|
JKQTPFinancialGraph(JKQTBasePlotter* parent=nullptr);
|
||||||
|
/** \brief class constructor */
|
||||||
|
JKQTPFinancialGraph(JKQTPlotter* parent);
|
||||||
|
|
||||||
|
/** \copydoc JKQTPXGraph::drawKeyMarker() */
|
||||||
|
virtual void drawKeyMarker(JKQTPEnhancedPainter& painter, const QRectF& rect) override;
|
||||||
|
/** \copydoc JKQTPXGraph::getKeyLabelColor() */
|
||||||
|
virtual QColor getKeyLabelColor() const override;
|
||||||
|
/** \copydoc JKQTPXGraph::draw() */
|
||||||
|
virtual void draw(JKQTPEnhancedPainter& painter) override;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** \copydoc shift */
|
||||||
|
double getShift() const;
|
||||||
|
/** \copydoc width */
|
||||||
|
double getWidth() const;
|
||||||
|
|
||||||
|
/** \copydoc m_fillStyleNegative */
|
||||||
|
JKQTPGraphFillStyleMixin &fillStyleNegative();
|
||||||
|
/** \copydoc m_fillStyleNegative */
|
||||||
|
const JKQTPGraphFillStyleMixin& fillStyleNegative() const;
|
||||||
|
/** \copydoc m_fillStylePositive */
|
||||||
|
JKQTPGraphFillStyleMixin &fillStylePositive();
|
||||||
|
/** \copydoc m_fillStylePositive */
|
||||||
|
const JKQTPGraphFillStyleMixin& fillStylePositive() const;
|
||||||
|
|
||||||
|
|
||||||
|
/** \copydoc m_lineStyleNegative */
|
||||||
|
JKQTPGraphLineStyleMixin &lineStyleNegative();
|
||||||
|
/** \copydoc m_lineStyleNegative */
|
||||||
|
const JKQTPGraphLineStyleMixin& lineStyleNegative() const;
|
||||||
|
/** \copydoc m_lineStylePositive */
|
||||||
|
JKQTPGraphLineStyleMixin &lineStylePositive();
|
||||||
|
/** \copydoc m_lineStylePositive */
|
||||||
|
const JKQTPGraphLineStyleMixin& lineStylePositive() const;
|
||||||
|
|
||||||
|
/** \copydoc JKQTPXGraph::getIndexRange() */
|
||||||
|
virtual bool getIndexRange(int &imin, int &imax) const override;
|
||||||
|
/** \copydoc JKQTPXGraph::getYMinMax() */
|
||||||
|
virtual bool getYMinMax(double& miny, double& maxy, double& smallestGreaterZero) override;
|
||||||
|
/** \copydoc JKQTPXGraph::usesColumn() */
|
||||||
|
virtual bool usesColumn(int column) const override;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** \copydoc openColumn */
|
||||||
|
int getOpenColumn() const;
|
||||||
|
|
||||||
|
/** \copydoc closeColumn */
|
||||||
|
int getCloseColumn() const;
|
||||||
|
|
||||||
|
/** \copydoc highColumn */
|
||||||
|
int getHighColumn() const;
|
||||||
|
|
||||||
|
/** \copydoc lowColumn */
|
||||||
|
int getLowColumn() const;
|
||||||
|
/** \copydoc graphType */
|
||||||
|
FinancialGraphType getGraphType() const;
|
||||||
|
|
||||||
|
Q_PROPERTY(int openColumn READ getOpenColumn WRITE setOpenColumn)
|
||||||
|
Q_PROPERTY(int closeColumn READ getCloseColumn WRITE setCloseColumn)
|
||||||
|
Q_PROPERTY(int highColumn READ getHighColumn WRITE setHighColumn)
|
||||||
|
Q_PROPERTY(int lowColumn READ getLowColumn WRITE setLowColumn)
|
||||||
|
Q_PROPERTY(FinancialGraphType graphType READ getGraphType WRITE setGraphType)
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
|
||||||
|
/** \brief finds all financial charts of the same orientation and determines width and shift, so they stand side by side
|
||||||
|
*
|
||||||
|
* \param maxWidth the maximum (relative) width, that all boxes will span of the (doubled) inter-box distance
|
||||||
|
* \param shrinkFactor factor, by which the boxes are shrinked compared to the available space
|
||||||
|
*
|
||||||
|
* \note This function will scale ALL graphs of the parent plot, which were derived from JKQTPFinancialGraph, that match in orientation (currently all).
|
||||||
|
*/
|
||||||
|
virtual void autoscaleBoxWidthAndShift(double maxWidth=0.8, double shrinkFactor=0.8);
|
||||||
|
|
||||||
|
/** \brief equivalent to \c autoscaleBoxWidthAndShift(groupWidth,0.8);
|
||||||
|
*/
|
||||||
|
void autoscaleBoxWidthAndShiftSeparatedGroups(double groupWidth=0.65);
|
||||||
|
/** \copydoc shift */
|
||||||
|
void setShift(double __value);
|
||||||
|
/** \copydoc width */
|
||||||
|
void setWidth(double __value);
|
||||||
|
/** \copydoc graphType */
|
||||||
|
void setGraphType(FinancialGraphType __value);
|
||||||
|
|
||||||
|
/** \brief set outline and fill color at the same time, for both positive and negative boxes
|
||||||
|
*
|
||||||
|
* \param cPositive color for positive items (close>open)
|
||||||
|
* \param cNegative color for negative items (close<open)
|
||||||
|
*/
|
||||||
|
virtual void setColor(QColor cPositive, QColor cNegative);
|
||||||
|
/** \brief set outline and fill color at the same time, for both positive and negative boxes
|
||||||
|
*
|
||||||
|
* \param linePositive line-color for positive items (close>open)
|
||||||
|
* \param fillPositive fill-color for positive items (close>open)
|
||||||
|
* \param lineNegative line-color for negative items (close<open)
|
||||||
|
* \param fillNegative fill-color for negative items (close<open)
|
||||||
|
*/
|
||||||
|
virtual void setColor(QColor linePositive, QColor fillPositive, QColor lineNegative, QColor fillNegative);
|
||||||
|
/** \brief sets graphType = FinancialGraphType::CandleStick and sets the positive and negative color (for fill and line equal!)
|
||||||
|
*
|
||||||
|
* \param cPositive color for positive items (close>open)
|
||||||
|
* \param cNegative color for negative items (close<open)
|
||||||
|
*
|
||||||
|
* Here is an example output for <code>setCandlestickTwoColor(QColor("blue"), QColor("orange"))</code>:
|
||||||
|
*
|
||||||
|
* \image html JKQTPFinancialGraphSetCandlestickTwoColor.png
|
||||||
|
*/
|
||||||
|
void setCandlestickTwoColor(QColor cPositive, QColor cNegative);
|
||||||
|
/** \brief sets graphType = FinancialGraphType::CandleStick and sets the positive and negative fill color and a single line-color
|
||||||
|
*
|
||||||
|
* \param cPositive color for fill of positive items (close>open)
|
||||||
|
* \param cNegative color for fill of negative items (close<open)
|
||||||
|
*
|
||||||
|
* Here is an example output for <code>setCandlestickTwoColor(QColor("green"), QColor("red"), QColor("black"))</code>:
|
||||||
|
*
|
||||||
|
* \image html JKQTPFinancialGraphSetCandlestickTwoColor2.png
|
||||||
|
*/
|
||||||
|
void setCandlestickTwoColor(QColor cPositive, QColor cNegative, QColor lineCOlor);
|
||||||
|
/** \brief sets graphType = FinancialGraphType::CandleStick and sets one color \a cLine for positive and negative items, where positive items are unfilled and negative items are filled
|
||||||
|
*
|
||||||
|
* \param cLine line-color for all items and also fill-color for positive items (close>open), negative items are unfilled
|
||||||
|
*
|
||||||
|
* Here is an example output for <code>setCandlestickOneColor(QColor("black"))</code>:
|
||||||
|
*
|
||||||
|
* \image html JKQTPFinancialGraphSetCandlestickOneColor.png
|
||||||
|
*/
|
||||||
|
void setCandlestickOneColor(QColor cLine);
|
||||||
|
/** \brief sets graphType = FinancialGraphType::OHLC and sets the positive and negative color (for fill and line equal!)
|
||||||
|
*
|
||||||
|
* \param cPositive color for positive items (close>open)
|
||||||
|
* \param cNegative color for negative items (close<open)
|
||||||
|
*
|
||||||
|
* Here is an example output for <code>setOHLCTwoColor(QColor("darkblue"), QColor("darkorange"))</code>:
|
||||||
|
*
|
||||||
|
* \image html JKQTPFinancialGraphSetOHLCTwoColor.png
|
||||||
|
*/
|
||||||
|
void setOHLCTwoColor(QColor cPositive, QColor cNegative);
|
||||||
|
|
||||||
|
|
||||||
|
/** \copydoc openColumn */
|
||||||
|
void setOpenColumn(int __value);
|
||||||
|
/** \copydoc openColumn */
|
||||||
|
void setOpenColumn (size_t __value);
|
||||||
|
/** \copydoc closeColumn */
|
||||||
|
void setCloseColumn(int __value);
|
||||||
|
/** \copydoc closeColumn */
|
||||||
|
void setCloseColumn (size_t __value);
|
||||||
|
/** \copydoc highColumn */
|
||||||
|
void setHighColumn(int __value);
|
||||||
|
/** \copydoc highColumn */
|
||||||
|
void setHighColumn (size_t __value);
|
||||||
|
/** \copydoc lowColumn */
|
||||||
|
void setLowColumn(int __value);
|
||||||
|
/** \copydoc lowColumn */
|
||||||
|
void setLowColumn (size_t __value);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** \brief type of the financial graph (OHLC or candle-stick)
|
||||||
|
*
|
||||||
|
* \see setGraphType(), getGraphType(), FinancialGraphType
|
||||||
|
*/
|
||||||
|
FinancialGraphType graphType;
|
||||||
|
/** \brief column for the "Open" value of the financial graph
|
||||||
|
*
|
||||||
|
* \see setOpenColumn(), getOpenColumn()
|
||||||
|
*/
|
||||||
|
int openColumn;
|
||||||
|
/** \brief column for the "Close" value of the financial graph
|
||||||
|
*
|
||||||
|
* \see setCloseColumn(), getCloseColumn()
|
||||||
|
*/
|
||||||
|
int closeColumn;
|
||||||
|
/** \brief column for the "High" value of the financial graph
|
||||||
|
*
|
||||||
|
* \see setHighColumn(), getHighColumn()
|
||||||
|
*/
|
||||||
|
int highColumn;
|
||||||
|
/** \brief column for the "Low" value of the financial graph
|
||||||
|
*
|
||||||
|
* \see setLowColumn(), getLowColumn()
|
||||||
|
*/
|
||||||
|
int lowColumn;
|
||||||
|
|
||||||
|
/** \brief the width of the boxes, relative to the distance between the current and the next x-value
|
||||||
|
*
|
||||||
|
* See the following graphic to understand this concept (same principle as described in the JKQTPBarGraphBase documentation):
|
||||||
|
* \image html financialgraphs_shiftwidth.png
|
||||||
|
*/
|
||||||
|
double width;
|
||||||
|
/** \brief the shift of the boxes, relative to the distance between the current and the next x-value
|
||||||
|
*
|
||||||
|
* See the following graphic to understand this concept (same principle as described in the JKQTPBarGraphBase documentation):
|
||||||
|
* \image html financialgraphs_shiftwidth.png
|
||||||
|
*/
|
||||||
|
double shift;
|
||||||
|
/** \brief fill style for positive items (close>open)
|
||||||
|
*
|
||||||
|
* \see fillStylePositive()
|
||||||
|
*/
|
||||||
|
JKQTPGraphFillStyleMixin m_fillStylePositive;
|
||||||
|
/** \brief fill style for negative items (close<open)
|
||||||
|
*
|
||||||
|
* \see fillStyleNegative()
|
||||||
|
*/
|
||||||
|
JKQTPGraphFillStyleMixin m_fillStyleNegative;
|
||||||
|
/** \brief fill style for positive items (close>open)
|
||||||
|
*
|
||||||
|
* \see lineStylePositive()
|
||||||
|
*/
|
||||||
|
JKQTPGraphLineStyleMixin m_lineStylePositive;
|
||||||
|
/** \brief fill style for negative items (close<open)
|
||||||
|
*
|
||||||
|
* \see lineStyleNegative()
|
||||||
|
*/
|
||||||
|
JKQTPGraphLineStyleMixin m_lineStyleNegative;
|
||||||
|
/** \brief this function is used by autoscaleBoxWidthAndShift() to determine whether a given graph shall be taken into account when autoscaling.
|
||||||
|
* Typically this returns \c true for all JKQTPFinancialGraph-derved objects with the same orientation (horizontal or vertical, i.e. currently all) */
|
||||||
|
virtual bool considerForAutoscaling( JKQTPFinancialGraph* other) const;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // jkqtpfinancial_H
|
@ -1306,3 +1306,127 @@ bool JKQTPXYAndVectorGraph::getIndexRange(int &imin, int &imax) const
|
|||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JKQTPXGraph::JKQTPXGraph(JKQTBasePlotter *parent):
|
||||||
|
JKQTPGraph(parent), xColumn(-1), sortData(Unsorted)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JKQTPXGraph::getXMinMax(double &minx, double &maxx, double &smallestGreaterZero)
|
||||||
|
{
|
||||||
|
bool start=true;
|
||||||
|
minx=0;
|
||||||
|
maxx=0;
|
||||||
|
smallestGreaterZero=0;
|
||||||
|
|
||||||
|
if (parent==nullptr) return false;
|
||||||
|
|
||||||
|
const JKQTPDatastore* datastore=parent->getDatastore();
|
||||||
|
int imin=0;
|
||||||
|
int imax=0;
|
||||||
|
getIndexRange(imin, imax);
|
||||||
|
|
||||||
|
for (int i=imin; i<imax; i++) {
|
||||||
|
double xv=datastore->get(static_cast<size_t>(xColumn),static_cast<size_t>(i));
|
||||||
|
if (JKQTPIsOKFloat(xv)) {
|
||||||
|
if (start || xv>maxx) maxx=xv;
|
||||||
|
if (start || xv<minx) minx=xv;
|
||||||
|
double xvsgz;
|
||||||
|
xvsgz=xv; SmallestGreaterZeroCompare_xvsgz();
|
||||||
|
start=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !start;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JKQTPXGraph::usesColumn(int column) const
|
||||||
|
{
|
||||||
|
return (column==xColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
int JKQTPXGraph::getXColumn() const
|
||||||
|
{
|
||||||
|
return xColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
JKQTPXGraph::DataSortOrder JKQTPXGraph::getDataSortOrder() const
|
||||||
|
{
|
||||||
|
return sortData;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JKQTPXGraph::getKeyColumn() const
|
||||||
|
{
|
||||||
|
return getXColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPXGraph::setDataSortOrder(int __value)
|
||||||
|
{
|
||||||
|
sortData=static_cast<DataSortOrder>(__value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPXGraph::setDataSortOrder(DataSortOrder __value)
|
||||||
|
{
|
||||||
|
sortData=__value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPXGraph::setXColumn(int __value)
|
||||||
|
{
|
||||||
|
xColumn = static_cast<int>(__value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPXGraph::setXColumn(size_t __value)
|
||||||
|
{
|
||||||
|
xColumn = static_cast<int>(__value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPXGraph::setKeyColumn(int __value)
|
||||||
|
{
|
||||||
|
setXColumn(__value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JKQTPXGraph::intSortData()
|
||||||
|
{
|
||||||
|
sortedIndices.clear();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (parent==nullptr) return ;
|
||||||
|
if (sortData==JKQTPXYLineGraph::Unsorted) return ;
|
||||||
|
|
||||||
|
JKQTPDatastore* datastore=parent->getDatastore();
|
||||||
|
int imin=0;
|
||||||
|
int imax=0;
|
||||||
|
getIndexRange(imin, imax);
|
||||||
|
|
||||||
|
QVector<double> datas;
|
||||||
|
|
||||||
|
if (sortData==SortedX) {
|
||||||
|
|
||||||
|
for (int i=0; i<imax; i++) {
|
||||||
|
double xv=datastore->get(static_cast<size_t>(xColumn),static_cast<size_t>(i));
|
||||||
|
sortedIndices<<i;
|
||||||
|
datas<<xv;
|
||||||
|
}
|
||||||
|
|
||||||
|
jkqtpQuicksortDual(datas.data(), sortedIndices.data(), datas.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JKQTPXGraph::getIndexRange(int &imin, int &imax) const
|
||||||
|
{
|
||||||
|
if (parent==nullptr) return false;
|
||||||
|
if (xColumn>=0) {
|
||||||
|
JKQTPDatastore* datastore=parent->getDatastore();
|
||||||
|
imin=0;
|
||||||
|
imax=static_cast<int>(datastore->getRows(static_cast<size_t>(xColumn)));
|
||||||
|
// ensure correct order, i.e. imin<=imax
|
||||||
|
if (imax<imin) qSwap(imin,imax);
|
||||||
|
// ranges are always >=0
|
||||||
|
if (imin<0) imin=0;
|
||||||
|
if (imax<0) imax=0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -1092,4 +1092,102 @@ protected:
|
|||||||
virtual bool getIndexRange(int &imin, int &imax) const override;
|
virtual bool getIndexRange(int &imin, int &imax) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** \brief This virtual JKQTPGraph descendent may be used as base for all graphs that use at least one column
|
||||||
|
* that specifies x coordinates for the single plot points.
|
||||||
|
* \ingroup jkqtplotter_basegraphs
|
||||||
|
*
|
||||||
|
* This class implements basic management facilities for the data columns:
|
||||||
|
* - setXColumn() to set the columns to be used for the graph data
|
||||||
|
* - setDataSortOrder() to specify whether and how the data should be sorted before drawing
|
||||||
|
* \image html jkqtplotter_unsorted.png "Unsorted Data"
|
||||||
|
* \image html jkqtplotter_sortedx.png "Data sorted along x-axis (DataSortOrder::SortedX)"
|
||||||
|
* .
|
||||||
|
*
|
||||||
|
* ... and overrides/implements the functions:
|
||||||
|
* - getXMinMax()
|
||||||
|
* - usesColumn()
|
||||||
|
* .
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class JKQTPLOTTER_LIB_EXPORT JKQTPXGraph: public JKQTPGraph {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
/** \brief specifies how to sort the data in a JKQTPXGraph before drawing
|
||||||
|
*
|
||||||
|
* \image html jkqtplotter_unsorted.png "Unsorted Data"
|
||||||
|
*
|
||||||
|
* \image html jkqtplotter_sortedx.png "Data sorted along x-axis (DataSortOrder::SortedX)"
|
||||||
|
*/
|
||||||
|
enum DataSortOrder {
|
||||||
|
Unsorted=0, /*!< \brief the data for a JKQTPXYGraph is not sorted before drawing */
|
||||||
|
SortedX=1, /*!< \brief the data for a JKQTPXYGraph is sorted so the x-values appear in ascending before drawing */
|
||||||
|
};
|
||||||
|
Q_ENUM(DataSortOrder)
|
||||||
|
|
||||||
|
|
||||||
|
/** \brief class constructor */
|
||||||
|
JKQTPXGraph(JKQTBasePlotter* parent=nullptr);
|
||||||
|
|
||||||
|
/** \copydoc JKQTPGraph::getXMinMax() */
|
||||||
|
virtual bool getXMinMax(double& minx, double& maxx, double& smallestGreaterZero) override;
|
||||||
|
|
||||||
|
/** \copydoc JKQTPGraph::usesColumn() */
|
||||||
|
virtual bool usesColumn(int column) const override;
|
||||||
|
|
||||||
|
/** \copydoc xColumn */
|
||||||
|
int getXColumn() const;
|
||||||
|
/** \copydoc sortData */
|
||||||
|
DataSortOrder getDataSortOrder() const;
|
||||||
|
/** \brief returns the column used as "key" for the current graph (typically this call getXColumn(), but for horizontal graphs like filled curves or barcharts it may call getYColumn() ) */
|
||||||
|
virtual int getKeyColumn() const;
|
||||||
|
|
||||||
|
Q_PROPERTY(DataSortOrder sortData READ getDataSortOrder WRITE setDataSortOrder)
|
||||||
|
Q_PROPERTY(int xColumn READ getXColumn WRITE setXColumn)
|
||||||
|
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
/** \copydoc sortData */
|
||||||
|
void setDataSortOrder(int __value);
|
||||||
|
/** \copydoc sortData */
|
||||||
|
void setDataSortOrder(DataSortOrder __value);
|
||||||
|
/** \copydoc xColumn */
|
||||||
|
void setXColumn(int __value);
|
||||||
|
/** \copydoc xColumn */
|
||||||
|
void setXColumn (size_t __value);
|
||||||
|
/** \brief sets the column used as "key" for the current graph (typically this call setXColumn(), but for horizontal graphs like filled curves or barcharts it may call setYColumn() ) */
|
||||||
|
virtual void setKeyColumn(int __value);
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/** \brief the column that contains the x-component of the datapoints */
|
||||||
|
int xColumn;
|
||||||
|
|
||||||
|
/** \brief if \c !=Unsorted, the data is sorted before plotting */
|
||||||
|
DataSortOrder sortData;
|
||||||
|
/** \brief this array contains the order of indices, in which to access the data in the data columns */
|
||||||
|
QVector<int> sortedIndices;
|
||||||
|
/** \brief sorts data according to the specified criterion in \a sortData ... The result is stored as a index-map in sorted Indices */
|
||||||
|
virtual void intSortData();
|
||||||
|
/** \brief returns the index of the i-th datapoint (where i is an index into the SORTED datapoints)
|
||||||
|
*
|
||||||
|
* This function can beu used to get the correct datapoint after sorting the datapoints,
|
||||||
|
* As sorting is done by sorting an index and not reordering the data in the columns themselves.
|
||||||
|
*
|
||||||
|
* \see setDataSortOrder(), getDataSortOrder()
|
||||||
|
* */
|
||||||
|
inline int getDataIndex(int i) const {
|
||||||
|
if (sortData==Unsorted) return i;
|
||||||
|
return sortedIndices.value(i,i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief determines the range of row indexes available in the data columns of this graph
|
||||||
|
*
|
||||||
|
* \param[out] imin first usable row-index
|
||||||
|
* \param[out] imax last usable row-index
|
||||||
|
* \return \c true on success and \c false if the information is not available
|
||||||
|
*/
|
||||||
|
virtual bool getIndexRange(int &imin, int &imax) const;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // JKQTPGRAPHSBASE_H
|
#endif // JKQTPGRAPHSBASE_H
|
||||||
|
@ -77,6 +77,19 @@ QColor JKQTPGraphLineStyleMixin::getLineColor() const
|
|||||||
return m_linePen.color();
|
return m_linePen.color();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JKQTPGraphLineStyleMixin::setLineColorInvertedFrom(QColor __noninvertedColor)
|
||||||
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
|
||||||
|
float h=0,s=0,v=0,a=0;
|
||||||
|
#else
|
||||||
|
qreal h=0,s=0,v=0,a=0;
|
||||||
|
#endif
|
||||||
|
__noninvertedColor.getHsvF(&h, &s, &v, &a);
|
||||||
|
h=std::fmod(h+120.0/360.0, 1.0);
|
||||||
|
__noninvertedColor.setHsvF(h,s,v,a);
|
||||||
|
setLineColor(__noninvertedColor);
|
||||||
|
}
|
||||||
|
|
||||||
void JKQTPGraphLineStyleMixin::setLineStyle(Qt::PenStyle __value)
|
void JKQTPGraphLineStyleMixin::setLineStyle(Qt::PenStyle __value)
|
||||||
{
|
{
|
||||||
m_linePen.setStyle(__value);
|
m_linePen.setStyle(__value);
|
||||||
|
@ -55,7 +55,6 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPGraphLineStyleMixin {
|
|||||||
JKQTPGraphLineStyleMixin();
|
JKQTPGraphLineStyleMixin();
|
||||||
/** \brief initiaize the line style (from the parent plotter) */
|
/** \brief initiaize the line style (from the parent plotter) */
|
||||||
void initLineStyle(JKQTBasePlotter *parent, int &parentPlotStyle, JKQTPPlotStyleType styletype=JKQTPPlotStyleType::Default);
|
void initLineStyle(JKQTBasePlotter *parent, int &parentPlotStyle, JKQTPPlotStyleType styletype=JKQTPPlotStyleType::Default);
|
||||||
|
|
||||||
virtual ~JKQTPGraphLineStyleMixin();
|
virtual ~JKQTPGraphLineStyleMixin();
|
||||||
|
|
||||||
/** \brief set the color of the graph line */
|
/** \brief set the color of the graph line */
|
||||||
@ -64,6 +63,8 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPGraphLineStyleMixin {
|
|||||||
void setLineColor(const QColor & __value, double alpha);
|
void setLineColor(const QColor & __value, double alpha);
|
||||||
/** \brief get the color of the graph line */
|
/** \brief get the color of the graph line */
|
||||||
QColor getLineColor() const;
|
QColor getLineColor() const;
|
||||||
|
/** \brief sets the line-color as an inverted version of the given color */
|
||||||
|
void setLineColorInvertedFrom(QColor __noninvertedColor);
|
||||||
|
|
||||||
/** \brief set the style of the graph line */
|
/** \brief set the style of the graph line */
|
||||||
void setLineStyle(Qt::PenStyle __value);
|
void setLineStyle(Qt::PenStyle __value);
|
||||||
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 115 KiB |
BIN
screenshots/financialgraphs.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
screenshots/financialgraphs_small.png
Normal file
After Width: | Height: | Size: 7.7 KiB |