The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

PostScript::Graph::Stock - draw share price graph from CSV data

SYNOPSIS

Simplest

Take a CSV file such as that produced by !Yahoo Finance websites (called 'stock_prices.csv' here), draw a graph showing the price and volume data and output as a postscript file for printing ('graph.ps' in this example).

    use PostScript::Graph::Stock;

    my $pgs = new PostScript::Graph::Stock();
    $pgs->build_chart( 'stock_prices.csv' );
    $pgs->output( 'graph' );

Typical

A number of settings are provided to configure the graph's appearance, adding colour for example. The ability to add analysis lines to either chart is planned.

    use PostScript::Graph::Stock;

    my $pgs = new PostScript::Graph::Stock(
        heading        => 'MyCompany Shares',
        price_title    => 'Price in pence',
        volume_title   => 'Transaction volume',
        price_percent  => 75,
        volume_percent => 25,
        background     => [1, 1, 0.9],
        show_lines     => 1,
        x_heavy_color  => [0, 0, 0.6],
        x_heavy_width  => 0.8,
        x_mid_color    => [0.2, 0.6, 0.8],
        x_mid_width    => 0.8,
        x_light_color  => [0.5, 0.5, 1],
        x_light_width  => 0.25,
        y_heavy_color  => 0.3,
        y_heavy_width  => 0.8,
        y_mid_color    => 0.5,
        y_mid_width    => 0.8,
        y_light_color  => 0.7,
        y_light_width  => 0.25,
        days           => [qw(- Mon Dien Mitt ...)],
        months         => [qw(- Jan Feb Mars ...)],
        dates          => 'weeks',
        changes_only   => 1,
        show_weekday   => 1,
        show_day       => 1,
        show_month     => 1,
        show_year      => 1,
        color          => [1, 0, 0],
        width          => 1.5,
        shape          => 'close2',
        bgnd_outline   => 1,
        bar_color      => [0.8, 0.4, 0.15],
        bar_width      => 0.5,
    );
    $pgs->data_from_file( 'stock_prices.csv' );
    #$pgs->line_from_file( 'analysis.csv' );
    $pgs->build_chart();
    $pgs->output( 'graph' );

All options

Although a number of options are provided for convenience (see "Typical"), many of these are merely a selection of the options which can be given to the underlying objects. See the manpages for the options available in each of the modules mentioned.

    use PostScript::Graph::Stock;

    my $pgs = new PostScript::Graph::Stock(
        file   => {
            # for PostScript::File
        },
        
        price  => {
            # options for Price graph
            layout => {
                # General proportions, headings
                # for PostScript::Graph::Paper
            },
            x_axis => {
                # All settings for X axis
                # for PostScript::Graph::Paper
            },
            y_axis => {
                # All settings for Y axis
                # for PostScript::Graph::Paper
            },
            style  => {
                # Appearance of price points
                # for PostScript::Graph::Style
            },
            key    => {
                # Settings for Key area if there is one
                # for PostScript::Graph::Key
            },
        },
        
        volume => {
            # options for Volume bar chart
            layout => {
                # General proportions, headings
                # for PostScript::Graph::Paper
            },
            x_axis => {
                # All settings for X axis
                # for PostScript::Graph::Paper
            },
            y_axis => {
                # All settings for Y axis
                # for PostScript::Graph::Paper
            },
            style  => {
                # Appearance of volume bars 
                # for PostScript::Graph::Style
            },
            key    => {
                # Settings for Key area if there is one
                # for PostScript::Graph::Key
            },
        },
    );
    
    $pgs->build_chart( 'stock_prices.csv' );
    $pgs->output( 'graph' );

DESCRIPTION

This is a top level module in the PostScript::Graph series. It produces graphs of stock performance given data in either of the following formats. The first extract was obtained by requesting price quotes in CSV file format from http://uk.table.finance.yahoo.com/, the second from a query of a Finance::Shares::MySQL database.

    Date,Open,High,Low,Close,Volume
    31-Dec-01,448.75,448.75,438.00,439.48,986598
    28-Dec-01,445.00,447.25,438.00,444.52,3492096
    27-Dec-01,440.00,444.75,435.25,444.06,1053161

    Date,Open,High,Low,Close,Volume 
    2001-06-01,454.50,475.00,448.50,461.00,8535680
    2001-06-04,465.00,465.00,458.50,459.00,3254045
    2001-06-05,458.25,464.00,455.00,462.00,4615016

Options given to the constructor control the appearance of the chart. The data can be given as a CSV file to data_from_file or as an array passed to data_from_array. It will shortly be possible to superimpose linear graphs on the same charts by calling line_from_file or line_from_array. The chart is comitted to postscript format in build_chart and saved to a postscript file with the output function. This file can be either a printable *.ps file or an Encapsulated PostScript file for inclusion elsewhere.

The data is displayed as two charts showing price and volume. These only appear if there is suitable data: only a price chart is shown if the file has no volume data. The proportion of space allocated for each is configurable. A Key is shown at the right of the relevant graph when analysis lines are added.

Theoretically, many years' worth of quotes can be accomodated, although the processing time becomes increasingly obvious. The output device tends to impose a practical limit, however. For example, around 1000 vertical lines can be distinguished on an A4 landscape sheet at 300dpi - 3 years of data at a pinch. But it gets difficult to read beyond 6-9 months. To help with this, it is possible to simplify the output.

Example 1

To show only the closing values for the last day of each week, use these options:

   dates => 'weeks',
   shape => 'close',

The vertical scales largely look after themselves, although you might wish to add a bit of colour as all defaults are monochrome. The horizontal scale showing the dates requires a little more care. The module tries to ensure labels are always legible by missing some out if they become too crowded. This is indicated by minor lines appearing between the labels. If this is likely, the following settings are recommended.

    show_lines   => 1,
    changes_only => 0,

When changes_only is on (the default) the month name, for example, is only shown for the first day of a new month. But this could well be one of the dates omitted through over-crowding, in which case the labels may become misleading.

Apart from that, the defaults will probably be satisfactory if you have a monochrome printer. However, as there are over 300 configurable options, a couple of examples might be useful.

Example 2

Vertical lines can be shown and both set to red using top-level options:

    show_lines    => 1,
    x_heavy_color => [1, 0, 0],

Or the axis options may be set directly. Note that options unique to this package, such as 'show_lines' are only available at the top level.

    show_lines => 1,
    price      => { 
        x_axis =>
            heavy_color => [1, 0, 0],
        },
    },
    volume     => { 
        x_axis =>
            heavy_color => [1, 0, 0],
        },
    },

Example 3

Some things cannot be done directly using the top-level shortcuts. Here the X axis marks are made more prominent and the labels are printed in dark green 8pt Courier.

    price => {
        x_axis => {
            mark_min   => 5,
            mark_max   => 20,
            font       => 'Courier',
            font_size  => 8,
            font_color => [0, 0.4, 0],
        },
    },

Example 4

Most commonly the defaults are adequate, but using the deeper options gives more control. The following will give an orange price mark outlined in black.

   shape => 'stock2',
   color => [0.5, 0.5, 0],
   width => 1,

This does essentially the same thing except the orange is now a narrow strip inside a dark blue border.

   price => {
       style => {
           point => {
               shape => 'stock2',
               inner_color => [0.5, 0.5, 0],
               outer_color => [0, 0, 0.6],
               inner_width => 0.5,
               outer_width => 2.5,
           },
       },
   },

CONSTRUCTOR

new( [options ] )

options can either be a list of hash keys and values or a hash reference. In either case, the hash is expected to have the same structure. Some of the primary keys are simple values but a few point to sub-hashes which hold options or groups themselves.

All color options can take either monochrome or colour format values. If a single number from 0 to 1.0 inclusive, this is interpreted as a shade of grey, with 0 being black and 1 being white. Alternatively an array ref holding three such values is read as holding red, green and blue values - again 1 is the brightest possible value.

    Value           Interpretation
    =====           ==============
    0               black
    0.5             grey
    1               white
    [1, 0, 0]       red
    [0, 1, 0]       green
    [0, 0, 1]       blue
    [1, 1, 0.9]     light cream
    [0, 0.8, 0.6]   turquoise

Other numbers are floating point values in PostScript native units (72 per inch).

background

Fill colour for price and volume chart backgrounds. (Default: 1)

Shortcut for price =>{ layout =>{ background => ... }} and/or volume =>{ layout =>{ background => ... }}.

bar_color

Sets the colour of the volume bars. (Defaults to color)

Shortcut for volume =>{ style =>{ point =>{ color => ... }}}.

bar_width

Sets the width of the volume bar outline. (Defaults to width)

Shortcut for volume =>{ style =>{ bar =>{ width => ... }}}.

bgnd_outline

By default shapes 'stock2' and 'close2' (see shape) are outlined with the complementary colour to the background, making them stand out. Setting this to 1 makes the outline default to the background colour itself. (Default: 0)

changes_only

The date labels are made up of weekday, day, month and year. Which sections are shown by default depends on the dates setting. If this is 1, each part is only shown if it has changed from the previous label. If 0, all the selected parts are shown. (Default: 1)

color

Sets the colour of the price marks and/or volume bars. (Default: 0)

Shortcut for price =>{ style =>{ point =>{ color => ... }}} and/or volume =>{ style =>{ bar =>{ color => ... }}}.

dates

This string determines how the dates are distributed across the X axis.

data

The dates are those present in the CSV file, but in chronological order.

days

Every day between the first and last day in the CSV file is listed, whether there is data for that day or not.

workdays

Every day except Saturdays and Sundays. Occasional holidays are ignored, just showing as days with no data.

weeks

Only the data for last trading day of each week is presented. No attempt is made to take the rest of the week into account - those days are just hidden. If any trading is recorded for that week, the latest day is given; if not the last working day is shown, with no data.

months

As weeks, but showing the last trading day of each month.

days

This allows the weekday abbreviations to be presented in a different language. It should be an array ref containing strings. Monday = 1, so there should probably be a dummy string for 0. (Defaults to English).

file

This may be either a PostScript::File object or a hash ref holding options for it. See PostScript::File for details. Options within this group include the paper size, orientation, debugging features and whether it is an EPS or a normal PostScript file.

Creating the PostScript::File object first has the advantage of allowing more than one chart to be printed from the same document.

Example

    use PostScript::Graph::Stock;
    use PostScript::File;
    my $pgf = new PostScript::File();
    
    my $gs1 = new PostScript::Graph::Stock(
                    file => pgf,
                );
    $gs1->build_chart( 'stock1.csv' );
   
    $pgf->newpage();
    my $gs1 = new PostScript::Graph::Stock(
                    file => pgf,
                );
    $gs1->build_chart( 'stock2.csv' );
    
    $pgf->output( 'graph' );

heading

A string which appears centrally above the price chart (or the volume chart if there are no prices). (Default: '')

Shortcut for price =>{ layout =>{ heading => ... }} or volume =>{ layout =>{ heading => ... }}.

months

This allows the month abbreviations to be presented in a different language. It should be an array ref containing strings. January = 1, so there should probably be a dummy string for 0. (Defaults to English).

price

This holds all the options pertaining to the price chart. It is similar in structure to PostScript::Graph::XY options with the following exceptions.

file

The 'file' section is not used here but is a seperate top-level option.

style

This section only controls the price points, so the 'line' and 'bar' subsections are not used. There is only one PostScript::Graph::Style object used to show the price points, so 'sequence' and 'auto' are irrelevent too.

chart

There is no 'chart' group. Settings specific to the stock graph are given to the constructor directly, at the top level.

See the manpage indicated for the details on what is relevant for each subsection.

    price => {
        layout => {
            # General proportions, headings
            # See PostScript::Graph::Paper
        },
        x_axis => {
            # All settings for X axis
            # See PostScript::Graph::Paper
        },
        y_axis => {
            # All settings for Y axis
            # See PostScript::Graph::Paper
        },
        style  => {
            # Appearance of price points
            # See PostScript::Graph::Style
        },
        key    => {
            # Settings for Key area if there is one
            # See PostScript::Graph::Key
        },
    },
    

price_percent

The percentage of paper allocated to the price as opposed to the volume chart. This is more of a 'rough ratio' rather than a percentage, but it does give some control over the relative sizes. The price_percent value includes the date labels area whereas the volume_percent value does not.

price_title

A string labelling the Y axis on the price chart. (Default: '')

Shortcut for price =>{ y_axis =>{ title => ... }}.

show_day

Show the date of day within the month. (Default: depends on dates)

shape

Sets the shape of the price marks. Suitable values are 'stock', 'stock2', 'close' and 'close2'. (Default: 'stock2')

The stock2 and close2 variants are drawn with inner and outer colours where the others are drawn just once using the inner colour.

Shortcut for price =>{ style =>{ point =>{ shape => ... }}}. Do NOT use the values normally available for point shapes. The postscript routines for 'dot', 'diamond' etc. require 2 parameters instead of the 5 used here. Using them would cause the code to fail unpredictably. See "POSTSCRIPT CODE" for further details.

show_lines

If 1, vertical lines are drawn on the charts. 0 means only horizontal graph lines are visible. (Default: 0)

show_month

Show the month. (Default: depends on dates)

show_weekday

Show the day of the week. (Default: depends on dates)

show_year

Show the month. (Default: depends on dates)

volume

This holds all the options pertaining to the volume chart. It is similar in structure to PostScript::Graph::Bar. See price for the structure and most of the exceptions. Of course in the style section, it is 'bar' that is relevant with 'point' and 'line' ignored.

volume_percent

The percentage of paper allocated to the volume as opposed to the price chart. This is more of a 'rough ratio' rather than a percentage, but it does give some control over the relative sizes. The price_percent value includes the date labels area whereas the volume_percent value does not.

volume_title

A string labelling the Y axis on the volume chart. (Default: '')

Shortcut for volume =>{ y_axis =>{ title => ... }}.

width

Sets the width of lines in the price marks. (Default: 1.0)

Shortcut for price =>{ style =>{ point =>{ width => ... }}}.

x_heavy_color

The colour of the main vertical lines, if show_lines is set. (Default: 0.4)

Shortcut for price =>{ x_axis =>{ heavy_color => ... }} and/or volume =>{ x_axis =>{ heavy_color => ... }}.

x_heavy_width

The width of the main vertical lines, if show_lines is set. (Default: 0.75)

Shortcut for price =>{ x_axis =>{ heavy_width => ... }} and/or volume =>{ x_axis =>{ heavy_width => ... }}.

x_mid_color

If show_lines is set and some date labels have been supressed, the unlabelled marks have lines of this colour. (Default: 0.5)

Shortcut for price =>{ x_axis =>{ mid_color => ... }} and/or volume =>{ x_axis =>{ mid_color => ... }}.

x_mid_width

The width of mid lines. See x_mid_color. (Default: 0.5)

Shortcut for price =>{ x_axis =>{ mid_width => ... }} and/or volume =>{ x_axis =>{ mid_width => ... }}.

y_heavy_color

Colour of major (labelled) lines on the Y axis. (Default: 0.4)

Shortcut for price =>{ y_axis =>{ heavy_color => ... }} and/or volume =>{ y_axis =>{ heavy_color => ... }}.

y_heavy_width

Width of major (labelled) lines on the Y axis. (Default: 0.75)

Shortcut for price =>{ y_axis =>{ heavy_width => ... }} and/or volume =>{ y_axis =>{ heavy_width => ... }}.

y_mid_color

Colour of intermediate (unlabelled) lines on the Y axis. (Default: 0.5)

Shortcut for price =>{ y_axis =>{ mid_color => ... }} and/or volume =>{ y_axis =>{ mid_color => ... }}.

y_mid_width

Width of intermediate (unlabelled) lines on the Y axis. (Default: 0.5)

Shortcut for price =>{ y_axis =>{ mid_width => ... }} and/or volume =>{ y_axis =>{ mid_width => ... }}.

y_light_color

Colour of lightest intermediate (unlabelled) lines on the Y axis. (Default: 0.6)

Shortcut for price =>{ y_axis =>{ light_color => ... }} and/or volume =>{ y_axis =>{ light_color => ... }}.

y_light_width

Width of lightest intermediate (unlabelled) lines on the Y axis. (Default: 0.25)

Shortcut for price =>{ y_axis =>{ light_width => ... }} and/or volume =>{ y_axis =>{ light_width => ... }}.

OBJECT METHODS

data_from_file( file [, dir] )

file

An optional fully qualified path-and-file or a simple file name. If omitted, the special file File::Spec->devnull() is returned.

dir

An optional directory dir. If present (and file is not already an absolute path), it is prepended to file.

A CSV file is read and converted to price and/or volume data, as appropriate. The comma seperated values are interpreted by Text::CSV_XS and so are currently unable to tolerate white space. See data_from_array for how the field contents are handled.

Any leading '~' is expanded to the users home directory. If no absolute directory is given either as part of file, it is placed within the current directory. File::Spec|File::Spec is used throughout so file access should be portable.

data_from_array( array_ref )

The array should be a list of array-refs, with each sub-array holding data for one day. Three formats are recognized:

    Date, Open, High, Low, Close, Volume
    Date, Open, High, Low, Close
    Date, Volume

Examples

    $pgs->data_from_array( [
        [2001-04-26, 345, 400, 300, 321, 12345678],
        [Apr-1-01, 234.56, 240.00, 230.00, 239.99],
        [13/4/01, 987654],
    ] );

The first field must be a date. Attempts are made to recognize the format in turn:

  1. The Finance::Shares::MySQL format is tried first, YYYY-MM-DD.

  2. European format dates are tried next using Date::Pcalc's Decode_Date_EU().

  3. Finally US dates are tried, picking up the !Yahoo format, Mar-01-99.

The four price values are typically decimals and the volume is usually an integer in the millions. These are used to automatically calculate the vertical axes.

build_chart( [array_ref | file [, dir]] )

This is the heart of the class which constructs the charts and produces the postscript code to draw them. If an array_ref is passed as the argument, this is given to data_from_array. Any other arguments are passed to data_from_file. These are just for convenience. build_chart itself requires that the necessary options have been given to new and that it has some stock data to work on.

file

Return the underlying PostScript::File object.

output( file [, dir] )

A convenience function to output the chart as a file. See "output" in PostScript::File.

POSTSCRIPT CODE

gstockdict

A few functions are defined in the gstockdict dictionary. These provide the code for the shapes drawn as price marks. Of the 20 dictionary entries, 12 are defined, so there is room for a few more marks to be added externally.

    make_stock  Draw single price mark
    make_stock2 Draw double price mark
    make_close  Draw single closing price mark
    make_close2 Draw double closing price mark
    yclose      parameter
    ylow        parameter
    yhigh       parameter
    yopen       parameter
    x           parameter
    dx          working value

A postscript function suitable for passing to the shape option to new must have 'make_' preprended to the name. It should take 5 parameters similar to the code for shape = 'stock'> which is called as follows.

    x yopen ylow yhigh yclose make_stock
    

2 POD Errors

The following errors were encountered while parsing the POD:

Around line 502:

You forgot a '=back' before '=head3'

Around line 595:

=back without =over