4

I have recently been interested in plotting financial data with pgfplots. The style I am trying to achieve is similar to what can be produced with gnuplot. However, I am familiar with pgfplots and have grown to love its output, which is why I would prefer to use the former. So far, I have been unable to find any similar attempts or examples.

I am trying to create plots which are similar to those which can be seen in these gnuplot examples. Clearly the line graphs are trivial, but what I am interested in is plotting using 'finance bars' and candlesticks, but primarily finance bars. Below I have included an example taken from the linked page.

Currently I don't know where to start with this. The gnuplot codes in the examples are very concise and I was hoping to achieve similar levels of concision with pgfplots. However, I am not sure if this is possible.

Does anyone know how to plot time series using finance bars or candlesticks using pgfplots? I would be interested to see what you can come up with.

A graph with 'finance bars' in black.


UPDATE 1

I have had some time to fiddle around with this briefly. I am including something which is close to the candlestick format, and a small data file to illustrate this with. Thank you @Torbjørn T. for your answer that you linked—you will see it does most of the heavy lifting in the example.

aapl.csv:

date,open,high,low,close,volume
2013-02-08,67.7142,68.4014,66.8928,67.8542,158168416
2013-02-11,68.0714,69.2771,67.6071,68.5614,129029425
2013-02-12,68.5014,68.9114,66.8205,66.8428,151829363
2013-02-13,66.7442,67.6628,66.1742,66.7156,118721995
2013-02-14,66.3599,67.3771,66.2885,66.6556,88809154
2013-02-15,66.9785,67.1656,65.7028,65.7371,97924631
2013-02-19,65.8714,66.1042,64.8356,65.7128,108854046
2013-02-20,65.3842,65.3842,64.1142,64.1214,118891367
2013-02-21,63.7142,64.1671,63.2599,63.7228,111596821
2013-02-22,64.1785,64.5142,63.7999,64.4014,82583823

The almost working example code:

\documentclass[border=3mm]{standalone}
\usepackage{pgfplotstable}
\pgfplotsset{compat=1.10}
\usepgfplotslibrary{statistics}

\makeatletter
\pgfplotsset{
    boxplot prepared from table/.code={
        \def\tikz@plot@handler{\pgfplotsplothandlerboxplotprepared}%
        \pgfplotsset{
            /pgfplots/boxplot prepared from table/.cd,
            #1,
        }
    },
    /pgfplots/boxplot prepared from table/.cd,
        table/.code={\pgfplotstablecopy{#1}\to\boxplot@datatable},
        row/.initial=0,
        make style readable from table/.style={
            #1/.code={
                \pgfplotstablegetelem{\pgfkeysvalueof{/pgfplots/boxplot prepared from table/row}}{##1}\of\boxplot@datatable
                \pgfplotsset{boxplot/#1/.expand once={\pgfplotsretval}}
            }
        },
        make style readable from table=lower whisker,
        make style readable from table=upper whisker,
        make style readable from table=lower quartile,
        make style readable from table=upper quartile,
        make style readable from table=median,
        make style readable from table=lower notch,
        make style readable from table=upper notch
}
\makeatother

\pgfplotstableread[col sep=comma]{aapl.csv}\datatable

\begin{document}
\begin{tikzpicture}
\begin{axis}[
    boxplot/draw direction=y,
    cycle list={{red},{black}},
    title={AAPL Candlestick Chart},
    xlabel={Date},
    ylabel={Price / USD}
    ]
\pgfplotstablegetrowsof{\datatable}
\pgfmathtruncatemacro\TotalRows{\pgfplotsretval-1}
\pgfplotsinvokeforeach{0,...,\TotalRows}
{
    \addplot+[
    boxplot prepared from table={
        table=\datatable,
        row=#1,
        lower whisker=low,
        upper whisker=high,
        lower quartile=open,
        upper quartile=close,
        median=close,
    },
    boxplot prepared={
        whisker extend=\pgfkeysvalueof{/pgfplots/boxplot/box extend}*0,
        every whisker/.style={thick}  
    },
    fill,fill opacity=0.5,
    ]
    coordinates {};
}
\end{axis}
\end{tikzpicture}
\end{document}

I have attached the output below; it is starting to look how I hoped it would. There are some areas that are not perfect. For instance, you can see the whiskers passing through the middle of the plot where the upper and lower quartile are listed the wrong way round. A quick and dirty fix for this is to set the opacity to 1.

I have set the colour cycle to red and black to inspire anyone that has an idea how to assign one intelligently, based on the data.

I welcome any improvements to this or attempts at the finance bars. Thanks again. my_candlesticks


UPDATE 2

I have played around with this a little more. Here is my solution that plots with finance bars. I think the idea can be extended to correctly formatting the candlesticks too if one wishes. I have had to manually set the limits. Maybe the best thing to do is run my previous solution first, plotted in white, to size the plot and then this over the top to generate the output.

Please see the code and output below. As before, any improvements are welcome.

\documentclass[border=3mm]{standalone}
\usepackage{pgfplotstable}
\pgfplotsset{compat=1.10}

\pgfplotsset{
    select row/.style={
        x filter/.code={\ifnum\coordindex=#1\else\def\pgfmathresult{}\fi}
    }
}

\newcommand{\financebar}[5]{
% takes arguements date (x coord), open, close, high, low 
\edef\tmp{\noexpand\draw (axis cs:#1,#5) -- (axis cs:#1,#4);}\tmp
\edef\tmp{\noexpand\draw (axis cs:#1,#2) -- (axis cs:{#1-0.5},#2);}\tmp
\edef\tmp{\noexpand\draw (axis cs:#1,#3) -- (axis cs:{#1+0.5},#3);}\tmp
}

\pgfplotstableread[col sep=comma]{aapl.csv}\datatable
\begin{document}
\begin{tikzpicture}
\begin{axis}[
    cycle list={{red},{black}},
    title={AAPL Finance Bar Chart},
    xlabel={Date},
    ylabel={Price / USD},
    xmin=0, xmax=11,
    ymin=63, ymax=69.75 
    ]
    
\pgfplotstablegetrowsof{\datatable}
\pgfmathtruncatemacro\TotalRows{\pgfplotsretval-1}
\pgfplotsinvokeforeach{0,...,\TotalRows}
{
    
    \pgfplotstablegetelem{#1}{open}\of{\datatable}\edef\openPrice{\pgfplotsretval}
    \pgfplotstablegetelem{#1}{close}\of{\datatable}\edef\closePrice{\pgfplotsretval}
    \pgfplotstablegetelem{#1}{high}\of{\datatable}\edef\highPrice{\pgfplotsretval}
    \pgfplotstablegetelem{#1}{low}\of{\datatable}\edef\lowPrice{\pgfplotsretval}

    \financebar{#1+1}{\openPrice}{\closePrice}{\highPrice}{\lowPrice}
}
\end{axis}
\end{tikzpicture}
\end{document}

enter image description here

  • 1
    Hi, welcome. I see from the Gnuplot manual that candlestick plots are very similar to boxplots. pgfplots can make boxplots (see section 5.12 Statistics in the manual), but I don't think there is any builtin way for making time series of box plots. I did find one question describing how to make multiple boxplots from a single data file (which it turned out I answered myself ..): tex.stackexchange.com/q/399163 It's perhaps not very elegant, but might be a start. – Torbjørn T. Jul 16 at 19:53
  • Hi there, I had a feeling that I would be steered towards the boxplots. They are very similar to the candlesticks and I am sure can be made more so with some tinkering. I also notice some similarities with the finance bars. I will play around with these and will post an update if I get to something resembling the gnuplot output. Thanks for taking a look and pointing me in the right direction. – FoxCharles Jul 16 at 21:11
  • @FoxCharles I'd be much interested in your answer :) – JeT Jul 16 at 21:51
  • 1
    @JeT Looking at the manual (page 502 for version dated 29 Feb 2020), you can set the width with the box extend key. The width is given in axis units, so not ideal for this usecase. There is also a whisker extend key. – Torbjørn T. Jul 16 at 22:38
  • 1
    I believe you will need to construct each box individually if you want to apply a distinctive colour according to whether the daily price has increased or dropped, which is how candlestick plots are constructed. This suggests you will need to put addplot+ in a loop to construct each box individually and apply the appropriate colour. This answer by @Mike Changing colors in plot pgfplots loading data from pgfplotstable colours individual bars according to a value in the datafile for a barplot.. – Ross Jul 19 at 4:55
3

We can use R for this by retrieving the relevant data using the tq_get function from the tidyquant package and plotting the candlestick and moving average using ggplot, a function in the tidyverse collection of packages. This example largely follows Matt Dancho's vignette for his tidyquant package posted here: Charting with tidyquant

enter image description here

\documentclass[a4paper,10pt]{article}
\usepackage{tikz}
\pagestyle{empty}

<<Setup,echo=FALSE>>=
suppressPackageStartupMessages({
library(tidyverse)
library(tidyquant)
library(tikzDevice)
})

theme_set(theme_bw() +
          theme(axis.text.x     = element_text(face="bold",size=14,angle=0, hjust=0.5),
                axis.text.y     = element_text(face="bold",size=14,hjust=1),
                axis.title      = element_text(face="bold",size=16),
                plot.title      = element_text(face="bold",size=14)))
@

<<GetData,echo=FALSE>>=
#https://cran.r-project.org/web/packages/tidyquant/vignettes/TQ04-charting-with-tidyquant.html#candlestick-chart
# Get AAPL and AMZN Stock Prices
start <- as_date("2015-09-01")
end <- as_date("2017-01-15")
AAPL <- tq_get("AAPL", get = "stock.prices", from = start, to = end)

@

\begin{document}
<<Candlestick,device='tikz',echo=FALSE>>=
AAPL %>%
    ggplot(aes(x = date, y = close)) +
    geom_candlestick(aes(open = open, high = high, low = low, close = close)) +
    geom_ma(ma_fun = SMA, n = 3, linetype = "dotted", size = 1.25,color="green") +
    labs(title = "AAPL Candlestick Chart",
         subtitle = "24 Nov 2016 - 15 Jan 2017",
         y = "Closing Price", x = "") + 
    coord_x_date(xlim = c(as_date("2016-11-24"), end+days(2)),
                 ylim = c(105, 120))
@

\end{document}
| improve this answer | |
  • Wow. My first downvote. – Ross Jul 17 at 17:06
  • 1
    Hi Ross, I was hoping for a solution that uses pgfplots. Whilst this is in the spirit of using tikz and moving away from gnuplot it is not quite what I am attempting to do. If you can manage the same with pgfplots I will happily accept that answer. Thanks. – FoxCharles Jul 17 at 18:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.