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

NAME

Finance::Shares::Lesson4 - Drawing moving averages

DESCRIPTION

An N-day moving average plots the average of the previous N days' data. They are much used in computerized stock market analysis as they can smooth out minor fluctuations in the market. The downside is that the results always turn in N days late, but then you can't have everything.

This script, called 04averages.pl can be found in the tutorial directory of the Finance::Shares package.

    #!/usr/bin/perl
    use strict;
    use warnings;
    use Getopt::Long;
    use Finance::Shares::Sample;
    use Finance::Shares::Averages;
    use Finance::Shares::Chart;

    my $help;
    my $stock = 'GM';
    my $dates = 'days';
    my $start = '2000-08-01';
    my $end   = '2001-07-31';
    my $mode  = 'cache';
    my $usage = <<END;
    Usage:
        $0 [ options ]

    where options can be any (or none) of the following:
      -c <code>  |  --code=<code>    Stock code like 'BA.L'
      -d <dmw>   | --dates=<dmw>     'days', 'weeks' or 'months'
      -s <date>  | --start=<date>    First date, as 'YYYY-MM-DD'
      -e <date>  |   --end=<date>    Last date, as 'YYYY-MM-DD'
      -m <mode>  |  --mode=<mode>    'cache', 'offline' or 'online'
      -h         |  --help           Show this help
    END

    GetOptions (
        'help|h'    => \$help,
        'code|c=s'  => \$stock,
        'dates|d=s' => \$dates,
        'start|s=s' => \$start,
        'end|e=s'   => \$end,
        'mode|m=s'  => \$mode,
    ) or $help = 1;
    print $usage and exit if $help;

    # Create MySQL object giving access to the data
    my $fss = new Finance::Shares::Sample(
        source => {
            user     => 'test',
            password => 'test',
            database => 'test',
        },

        mode        => $mode,
        symbol      => $stock,
        start_date  => $start,
        end_date    => $end,
        dates_by    => $dates,
    );

    # Create Chart object showing the data
    my $fsc = new Finance::Shares::Chart(
        sample          => $fss,
        background      => [ 1, 1, 0.9 ],
        bgnd_outline    => 1,
        dots_per_inch   => 72,
        file => {
            landscape => 1,
        },
        x_axis => {
            show_lines  => 0,
            show_year   => 1,
        },
        prices => {
            percent => 70,
            points => {
                width => 1.5,
                color => [ 0.4, 0.4, 0.7 ],
            },
        },
        volumes => {
            percent => 30,
            bars => {
                color => [ 0.5, 0.8, 0.6 ],
                width => 1,
            },
        },
    );

    # place your function(s) here

    $fsc->output($stock);
    print "$stock quotes from $start to $end saved as $stock.ps\n";

There are a few changes from previous scripts which are worth pointing out. Single letter options are now accepted and $usage reflects that. The defaults are different, providing a larger, more realistic sample. Some new options appear in the Chart constructor. Look them up in Finance::Shares::Chart.

  1. Near the end of the script is a comment. Add this call after it:

        # place your function(s) here
        $fss->simple_average();

    Run the script, and you should see a moving average added to the prices graph, with a Key panel appearing at the side. Err, that's it, more or less. Notice that all the function modules implement methods called on a Sample object. Try adding these too:

        $fss->weighted_average();
        $fss->exponential_average();

    You might find it easier to see as weeks rather than days. Invoke the script as e.g.

        $ 04average.pl -d 'weeks'
  2. All function methods take hash key/value style parameters. Draw two moving averages on the chart, with periods of 3 and 14, like this:

        $fss->simple_average(
            period => 3,
        );

    Signals might be generated when the faster moving average (3) crosses the slower moving one (14). If it crosses up, the prices are starting to rise, so buy; when it crosses down, the prices are already falling, so sell.

  3. Just like the price and volume data, it is possible to give each line a Style.

        $fss->simple_average(
            period => 14,
            style => {
                width => 2,
                color => [ 1, 0, 0 ],
            },
        );

    The Style module tries to ensure that each style is slightly different. In this case, it is still changing the dash pattern of the lines. Often this is useful, because you don't have to think about giving every line a different style. However, if you wish to turn off this automatic style selection, add the following line to the style group:

                auto => 'none',
  4. The automatic style selection is controlled by a Sequence object. There is a default sequence for each graph, and this can be used to ensure each line has a different appearance. Remove any changes you have made to the script, then add these lines after the 'place your functions' comment.

        # place your function(s) here
        my $pstyle = {
            same => 1,
            line => {
                width => 2,
                color => [1, 0, 0],
                outer_dashes => [],
            },
        };
    
        $fss->simple_average(
            period => 3,
            style => $pstyle,
        );
        $fss->simple_average(
            period => 14,
            style => $pstyle,
        );
        $fss->simple_average(
            period => 28,
            style => $pstyle,
        );

    Run it. These 3 lines are all controlled by the same style, but the default sequence object is changing their dash styles. The same option works just like the top-level Chart option, 'bgnd_outline'. Setting the outer_dashes pattern to '[]' (meaning 'no dashes') has the effect of providing a continuous 'outer_color'. Comment them out to see what they do.

  5. Now to take charge of the sequence. Change the start of these changed lines to read as follows. Notice that the color line has gone.

        # place your function(s) here
        my $pseq = $fsc->sequence('prices');
        $pseq->auto( 'red' );
        my $pstyle = {
            sequence => $pseq,
            same => 1,
            line => {
                width => 2,
                outer_dashes => [],
            },
        };

    The colours can be specified directly. Try this:

        # place your function(s) here
        my $pseq = $fsc->sequence('prices');
        $pseq->setup( 'color',
            [1,0,0], [0.5,0.5,0], [1,1,0] );
        $pseq->auto( 'color' );
        my $pstyle = {
            ...
        };
        
  6. All functions can be applied to any data or even other functions and moving averages are no exception. The data to be averaged is specified by the graph and line keys.

        $fss->simple_average(
            period => 3,
            graph => 'volumes',
            line => 'volume',
            style => $pstyle,
        );
     

    graph can be one of prices, volumes, cycles, tests. line must be an identifier for a line on the specified chart. Add this code to the end of the script. It lists all the line IDs for the prices graph.

        my @lines = $fss->known_lines( 'prices' );
        print join('; ', @lines), "\n";
  7. Finally, these are all the keys recognized by the ..._average methods.

        $fss->simple_average(
            period => 28,
            graph => 'prices',
            line => 'low',
            style => $pstyle,
            strict => 1,
            show => 1,
            key => 'LOW Prices !!!',
            id => 'not-the-usual',
        );
     

    A 28 day moving average cannot strictly be calculated until the 28th day. This sort of thing is enfored when strict is true; otherwise most functions can produce something sensible like 'the average so far'. show enables the Sample to hold working lines without them necessarily being visible on the chart. key and id allow user over-rides of these. Take care replacing id, though; it is probably better to use the internal ones which can be obtained by calling e.g.

        my $id = line_id('graph', 'line');

See Finance::Share::Averages for full details of the moving average functions. PostScript::Graph::Style is the place to go for all the stuff on Styles and Sequences.