NAME

Math::Business::StockMonkey::Cookbook - don't reinvent the wheel, start here

DBI

These modules are all intended to be used with databases. Here is how you'd store their results alongside OHLC data.

use Finance::QuoteHist;
my $q = Finance::QuoteHist->new(
    symbols    => [qw(GOOG)],
    start_date => '6 months ago',
    end_date   => 'today',
);

my $rsi = recommended Math::Business::RSI;
my $dmi = recommended Math::Business::DMI;
my $sar = recommended Math::Business::ParabolicSAR;
my $bb  = recommended Math::Business::BollingerBands;

my $sth = $dbo->prepare("insert into something blah blah blah");

for my $row ($q->quotes) {
    my ($symbol, $date, $open, $high, $low, $close, $volume) = @$row;

    $rsi->insert( $close );
    $dmi->insert( [$high,$low,$close] );
    $sar->insert( [$open,$high,$low,$close] );
     $bb->insert( $close );

    $sth->execute(
        $open, $high, $low, $close,

        # You do not need these lexical variables, there just
        # here to describe the return values.

        my ($rsi)             = $rsi->query,
        my ($pdi, $mdi, $adx) = $dmi->query,
        my ($l,$m,$h)         =  $bb->query,

        $sar->query,

    ) or die $dbo->errstr;
}

AVOID RECALCULATING

I can't stress this strongly enough. Avoid recalculating lots of data if you can. If you're pulling daily updates to long columns of numbers and you want them as accurate as possible -- and noticed that today's value is highly dependent on yesterday's value, recursively: then use Storable, Data::Dump or Data::Dumper:

# Pretend we've just calculated 6 months as above under DBI
use Storable qw(lock_store lock_retrieve);

# Store the $bb in a file for later use
lock_store($bb, "filename");

You can now resurrect a complete snapshot of the $bb object like this:

my $resurrected_bb = lock_retrieve("filename");
   $resurrected_bb->insert($tomorrow_close);

The resurrected $bb will know just as much past history as the original. Hurray! If you have some aversion to Storable, for whatever reason, you can do nearly the same thing with the dumper modules like this:

use Data::Dump qw(dump);
open my $save, "filename" or die $!;
print $save dump($bb);
close $save;

open my $load, "filename" or die $!;
my $code = do { local $/; <$load> };
close $load;

my $resurrected_bb = eval $code; die $@ if $@;
   $resurrected_bb->insert($tomorrow_close);

GD::Graph

What good is data without plots? This is basically how you'd draw a nice Math::Business::ParabolicSAR graph.

use strict;
use Math::Business::ParabolicSAR;
use GD::Graph::mixed;
use List::Util qw(min max);

my @ohlc = reverse @{$dbo->selectall_arrayref(qq/
    select day, open,high,low,close
      from daily
     where symbol=?
     order by day desc limit 50

/, {}, "SYK")};

my $sar = Math::Business::ParabolicSAR->recommended;

my @data;
for my $p (@ohlc) {
    my $d = shift @$p;

    $sar->insert($p);

    push @{$data[0]}, $d;      # date
    push @{$data[1]}, $p->[3]; # close
    push @{$data[2]}, $p->[1]; # high
    push @{$data[3]}, $p->[2]; # low
    push @{$data[4]}, $sar->query;
}

my @all_points = grep {defined $_} map {@$_} @data[1 .. $#data];
my $min_point  = min(@all_points);
my $max_point  = max(@all_points);

my $graph = GD::Graph::mixed->new(1000, 500);
   $graph->set(
       y_label           => 'dollars',
       x_label           => 'date',
       transparent       => 0,
       markers           => [qw(7 3 9 8)],
       dclrs             => [qw(black lgreen lred lblue)],
       y_min_value       => $min_point-0.2,
       y_max_value       => $max_point+0.2,
       y_number_format   => '%0.2f',
       x_labels_vertical => 1,
       types             => [qw(linespoints points points points)],

   ) or die $graph->error;

my $gd = $graph->plot(\@data) or die $graph->error;
open my $img, '>', "sar.png" or die $!;
binmode $img;
print $img $gd->png;
close $img;