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->data_from_file( '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',
background => [1, 1, 0.9],
show_lines => 1,
price_percent => 75,
analysis_percent => 25,
volume_percent => 25,
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,
dates => {
days => [qw(- Mon Dien Mitt ...)],
months => [qw(- Jan Feb Mars ...)],
by => 'weeks',
changes_only => 1,
show_weekday => 1,
show_day => 1,
show_month => 1,
show_year => 1,
},
analysis_low => -20,
analysis_high => 35,
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->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
},
dates => {
# as 'Typical' above
},
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
# as 'price' above
},
analysis => {
# options for Analysis chart
# as 'price' above
},
);
$pgs->data_from_file( 'stock_prices.csv' );
# build data and style structures
$pgs->add_price_line( $data1, $style1, 'One');
$pgs->add_analysis_line( $data2, $style2, 'Two');
$pgs->add_volume_line( $data3, $style3, 'Three');
$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 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 => {
by => '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,
},
},
},
Methods Available
There are three ways stock quotes may be inserted, though only one set is possible at a time. data_from_file reads from a CSV file and has already been introduced. An array of price data can be passed directly to data_from_array. The third way uses data_from_sample to accept data from a Finance::Shares::Sample object.
add_price_line, add_analysis_line and add_volume_line provide ways of superimposing trend lines on the price, analysis and volume charts. The underlying PostScript::Graph::Paper objects are also available for adding shaded areas, for example.
Once the data has been assembled the graph needs to be converted into PostScript code which does the actual drawing. The simplest way is just to call output which builds and saves the file in one go.
Alternatively, several stock graphs may be output to the same PostScript file (on seperate pages). When each graph is ready build_graph is called instead of 'output'. Finally a call to output saves the multi-page document ready for viewing or printing.
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).
analysis
A sub-hash holding all settings for the analysis chart. See price
below for most of the details. The exception is that the 'style' sub-hash is ignored as no data is presented here.
This graph is provided for adding momentum analysis or other lines that don't use price or volume scales. At present only one analysis graph is possible, so the Y axis must be chosen to accomodate all the required lines.
analysis_high
Top of Y axis on the analysis graph. (Default: undefined)
Shortcut for:
analysis =>{ y_axis =>{ high => ... }}
analysis_low
Bottom of Y axis on the analysis graph. (Default: undefined)
Shortcut for:
analysis =>{ y_axis =>{ low => ... }}
analysis_percent
Percentage of space allocated to the analysis graph. (Default: 0)
Shortcut for:
analysis =>{ percent ... }
background
Fill colour for price and volume chart backgrounds. (Default: 1)
Shortcut for:
price =>{ layout =>{ background => ... }}
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)
Shortcut for:
price =>{ style =>{ same => (not ...) }}
volume =>{ style =>{ same => (not ...) }}
color
Sets the colour of the price marks and/or volume bars. (Default: 0)
Shortcut for:
price =>{ style =>{ point =>{ color => ... }}}
volume =>{ style =>{ bar =>{ color => ... }}}
dates
This is a sub-hash controlling how the X axis is laid out. See prepare_dates for details.
Example
my $ss = new PostScript::Graph::Stock(
dates => {
by => 'weeks',
},
);
Not really a shortcut for any specific price or volume settings, but a replacement for various x_axis values.
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. See build_graph.
heading
A string which appears centrally above the top chart. (Default: '')
Shortcut for:
price =>{ layout =>{ heading => ... }}
analysis =>{ layout =>{ heading => ... }}
volume =>{ layout =>{ heading => ... }}
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 graph. 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.
Shortcut for:
price =>{ percent => ... }
price_title
A string labelling the Y axis on the price chart. (Default: '')
Shortcut for:
price =>{ y_axis =>{ title => ... }}
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.
Shortcut for:
price =>{ style =>{ point =>{ shape => ... }}}
show_key
Set to 0 to hide the Key boxes that appear on the left when lines are added over the data. (Default: 1)
show_lines
If 1, vertical lines are drawn on the charts. 0 means only horizontal graph lines are visible. (Default: 0)
Shortcut for:
price =>{ x_axis =>{ show_lines => ... }}
analysis =>{ x_axis =>{ show_lines => ... }}
volume =>{ x_axis =>{ show_lines => ... }}
smallest
This is the granularity of the axes. Setting it to a larger value reduces the number of axis marks. (The default depends upon the value given to file =>{ dpi => ... })
Shortcut for
price =>{ x_axis =>{ smallest => ... }}
price =>{ y_axis =>{ smallest => ... }}
analysis =>{ x_axis =>{ smallest => ... }}
analysis =>{ y_axis =>{ smallest => ... }}
volume =>{ x_axis =>{ smallest => ... }}
volume =>{ y_axis =>{ smallest => ... }}
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.
Shortcut for:
volume =>{ percent => ... }
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 => ... }}
analysis =>{ x_axis =>{ heavy_color => ... }}
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 => ... }}
analysis =>{ x_axis =>{ heavy_width => ... }}
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 => ... }}
analysis =>{ x_axis =>{ mid_color => ... }}
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 => ... }}
analysis =>{ x_axis =>{ mid_width => ... }}
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 => ... }}
analysis =>{ y_axis =>{ heavy_color => ... }}
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 => ... }}
analysis =>{ y_axis =>{ heavy_width => ... }}
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 => ... }}
analysis =>{ y_axis =>{ mid_color => ... }}
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 => ... }}
analysis =>{ y_axis =>{ mid_width => ... }}
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 => ... }}
analysis =>{ y_axis =>{ light_color => ... }}
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 => ... }}
analysis =>{ y_axis =>{ light_width => ... }}
volume =>{ y_axis =>{ light_width => ... }}
PRINCIPAL 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 (andfile
is not already an absolute path), it is prepended tofile
.
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:
The Finance::Shares::MySQL format is tried first, YYYY-MM-DD.
European format dates are tried next using Date::Pcalc's Decode_Date_EU().
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.
This method identifies the dates used for plotting the data and constructs suitable labels. See the new option dates. If dates
is weeks the average price and volume data for the week is given under the last known day. Average prices are also calculated for months.
data_from_sample( [sample] )
This specialized method uses data held in a Finance::Shares::Sample. It is provided so that graphs may be drawn from data processed by Finance::Share packages.
add_price_line( data, key [, style] )
data
-
An array ref indicating a list of points. Each point has a date and a price value. It is the callers responsibility to check that the data will fit on the price graph.
key
-
A string to appear next to the style in the Key box.
style
-
This can either be a PostScript::Graph::Style object or a hash ref holding options for one. The line is drawn with the resulting style. The styles for all lines are expected to belong to the same PostScript::Graph::Sequence, even if the 'auto' feature is not being used. This allows only the differences between styles to be written to the PostScript file.
Draw a line of one or more segments in given style. The line is superimposed on the price chart, with the style and text added to a Key area on the right.
Example
my $pgs = new PostScript::Graph::Stock;
$pgs->data_from_file('prices.csv');
my $data = [
[ '2002-10-18', 462 ],
[ '2002-10-21', 396 ],
[ '2002-10-22', 129 ]
];
my $style = {
auto => 'none',
color => 1,
line => {
width => 2,
color => [0.7, 0, 0],
dashes => [5, 5],
},
point => {
size => 4,
shape => 'dot',
color => [1, 0.3, 0],
},
};
$pgs->add_price_line( $data, $style, 'Example' );
$pgs->output('pricegraph');
add_volume_line( data, key [, style] )
data
-
An array ref indicating a list of points. Each point has a date and a volume. It is the caller's responsibility to check that the data will fit on the price graph.
key
-
A string to appear next to the style in the Key box.
style
-
This can either be a PostScript::Graph::Style object or a hash ref holding options for one. The line is drawn with the resulting style. The styles for all lines are expected to belong to the same PostScript::Graph::Sequence, even if the 'auto' feature is not being used. This allows only the differences between styles to be written to the PostScript file.
Draw a line of one or more segments in given style. The line is superimposed on the volume chart, with the style and text added to a Key area on the right.
add_analysis_line( data, key [, style] )
data
-
An array ref indicating a list of points. Each point has a date and a volume. It is the caller's responsibility to check that the data will fit on the price graph.
key
-
A string to appear next to the style in the Key box.
style
-
This can either be a PostScript::Graph::Style object or a hash ref holding options for one. The line is drawn with the resulting style. The styles for all lines are expected to belong to the same PostScript::Graph::Sequence, even if the 'auto' feature is not being used. This allows only the differences between styles to be written to the PostScript file.
Draw a line of one or more segments in given style. The line is superimposed on the volume chart, with the style and text added to a Key area on the right. For this to appear, new options analysis_percent
, analysis_low
and analysis_high
should have been set somehow.
build_graph
This will not need to be called in most circumstances as it is called automatically by output.
However, if more than one graph is being written to the same PostScript::File document, this will need to be called directly. It constructs the PostScript code for the graph without actually saving the file.
Example
use PostScript::Graph::Stock;
use PostScript::File;
my $psfile = new PostScript::File();
my $gs1 = new PostScript::Graph::Stock(
file => $psfile,
);
$gs1->data_from_file( 'stock1.csv' );
$gs1->build_graph();
$psfile->newpage();
my $gs2 = new PostScript::Graph::Stock(
file => $psfile,
);
$gs2->data_from_file( 'stock2.csv' );
$gs2->build_graph();
$psfile->output( 'graph' );
output( file [, dir] )
The charts are constructed and written out to a PostScript file. A suitable suffix (.ps, .epsi or .epsf) will be appended to the file name.
ACCESS METHODS
file
Return the underlying PostScript::File object.
price_graph_paper
Return the PostScript::Graph::Paper object upon which the prices are drawn.
volume_graph_paper
Return the PostScript::Graph::Paper object upon which the volume data are drawn.
price_sequence
Return the PostScript::Graph::Sequence object used for the price line styles, allowing the defaults to be altered.
volume_sequence
Return the PostScript::Graph::Sequence object used for the volume styles, allowing the defaults to be altered.
CLASS METHODS
The PostScript code is a class method so that it may be available to other classes that don't need a Stock object.
The useful functions in the 'gstockdict' dictionary draw a stock chart mark and a close mark in either one or two colours.
make_stock
make_stock2
make_close
make_close2
The all consume 5 numbers from the stack (even if they aren't all used):
x yopen ylow yhigh yclose
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
BUGS
This module is quite complex and still in the early stages of testing. So please report any bugs you find to the author.
AUTHOR
Chris Willmot, chris@willmot.org.uk
SEE ALSO
PostScript::File, PostScript::Graph::Style, PostScript::Graph::Key, PostScript::Graph::Paper, PostScript::Graph::XY, PostScript::Graph::Bar, Finance::Shares::Sample and Finance::Shares::MySQL.