stockmonkey
view release on metacpan or search on metacpan
examples/precursor-bayesian-nightmare-stockmonkey.pl view on Meta::CPAN
#!/usr/bin/perl
use strict;
use warnings;
use Finance::QuoteHist;
use Storable qw(freeze thaw);
use Math::Business::RSI;
use Math::Business::LaguerreFilter;
use Math::Business::BollingerBands;
use Math::Business::ConnorRSI;
use Math::Business::ADX;
use MySQL::Easy;
use Date::Manip;
use Algorithm::NaiveBayes;
use List::Util qw(min max sum);
use GD::Graph::lines;
use GD::Graph::Hooks;
my $dbo = MySQL::Easy->new("scratch"); # reads .my.cnf for password and host
my @tickers = split(m/[^A-Z]/, shift || "JPM,SCTY,P,TSLA,ATVI,HIMX,ZNGA,BEARX");
my $phist = shift || 180; # final plot history items
my $slurpp = "10 years"; # data we want to fetch
my @proj = map {[map {int $_} split m{/}]} @ARGV; # projections
# proj is a list of projections to consider, days/percent
@proj = (
[10,3],[10,5],[10,10],
[20,3],[20,5],[20,10],
);
if( $ENV{NEWK} ) {
$dbo->do("drop table if exists stockplop");
$dbo->do("drop table if exists stockplop_annotations");
$dbo->do("drop table if exists stockplop_glaciers");
}
find_quotes_for() unless $ENV{NO_FETCH};
annotate_all_tickers() unless $ENV{NO_ANNOTATE};
plot_result();
# {{{ sub find_quotes_for
sub find_quotes_for {
for my $ticker (@tickers) {
my $lf = Math::Business::LaguerreFilter->new(2/(1+4));
my $ls = Math::Business::LaguerreFilter->new(2/(1+8));
my $bb = Math::Business::BollingerBands->recommended;
my $crsi = Math::Business::ConnorRSI->recommended;
my $adx = Math::Business::ADX->recommended;
my $rsi = Math::Business::RSI->recommended;
# NOTE: if you add to indicies, you probably need to 'newk'
my @indicies = ($lf, $ls, $crsi, $rsi, $bb, $adx);
my %picky_insert = (
$adx->tag => sub {
my ($open, $high, $low, $close, $volume) = @_;
$adx->insert([$high, $low, $close]); # curry picky inserts
}
);
my %has_multi_column_output = ( $bb->tag => 1 );
my $time = $slurpp;
# {{{ SCHEMA:
SCHEMA: {
my @moar_columns;
for( @indicies ) {
my $tag = $_->tag;
my $type = $has_multi_column_output{$tag} ? "varchar(50)" : "decimal(6,4)";
push @moar_columns, "`$tag` $type,";
}
$dbo->do("create table if not exists stockplop(
rowid int unsigned not null auto_increment primary key,
seqno int not null default '0',
ticker char(5) not null,
qtime date not null,
open decimal(6,2) unsigned not null,
high decimal(6,2) unsigned not null,
low decimal(6,2) unsigned not null,
close decimal(6,2) unsigned not null,
volume int unsigned not null,
examples/precursor-bayesian-nightmare-stockmonkey.pl view on Meta::CPAN
description text not null,
primary key(rowid)
)^);
}
my $keep = max(map($_->[0], @proj)); my %uniq;
my @proj_ud = grep {!$uniq{$_->[0]}++} @proj;
my $sql_cols = join(", ", map {"t$_->[0].close t$_->[0]_close, t$_->[0].rowid t$_->[0]_rowid"} @proj_ud);
my @sql_join = map {"join t$_->[0] using (seqno)"} @proj_ud;
my $ins = $dbo->ready("insert into stockplop_annotations set rowid=?, description=?");
my $sth = $dbo->ready(my $cross_product_sql =
"select stockplop.*, $sql_cols from stockplop @sql_join where ticker=?");
# DESCRIBE SITUATIONS WITH TECHNICAL ANALYSIS
for my $ticker ($dbo->firstcol("select distinct(ticker) from stockplop")) {
print "\nannotating $ticker\n";
my %instance_history;
for(@proj_ud) {
print "creating temporary table t$_->[0]\n";
$dbo->do("drop table if exists t$_->[0]");
$dbo->do("create temporary table t$_->[0]
select (seqno-$_->[0])seqno,qtime,close,rowid
from (select seqno,qtime,close,rowid from stockplop where ticker=? and seqno>$_->[0]
order by qtime desc)
the_future
order by qtime asc", $ticker);
}
my $t = $cross_product_sql;
$t =~ s{\?}{'$ticker'};
print "executing fetch ($t)\n";
$sth->execute($ticker);
print "analyzing result rows\n";
my @events;
my %events;
my @last;
while( my $row = $sth->fetchrow_hashref ) {
if( defined (my $rsi = $row->{'RSI(27)'}) ) {
for (90,80,70) { $events{"rsi_$_"} = 1 if $rsi >= $_ }
for (10,20,30) { $events{"rsi_$_"} = 1 if $rsi <= $_ }
}
if( defined (my $rsi = $row->{'CRSI(3,2,100)'}) ) {
for (90,80,70) { $events{"crsi_$_"} = 1 if $rsi >= $_ }
for (10,20,30) { $events{"crsi_$_"} = 1 if $rsi <= $_ }
}
if( defined (my $adx = $row->{'ADX(14)'}) ) {
$adx = int(100 * $adx);
for (10, 20, 30, 40, 50) { $events{"adx_$_"} = 1 if $adx >= $_ }
}
if( defined ( my $bbs = $row->{'BOLL(2,20)'}) ) {
my ($L, $M, $U) = map {$_ eq "-" ? undef : (0.0+$_)} split m{/}, $bbs;
if( defined $L and defined $M and defined $U ) {
$events{boll_overbought} = 1 if $row->{close} >= $U;
$events{boll_oversold} = 1 if $row->{close} <= $L;
}
}
if( @last ) {
if( defined $last[-1]{"LAG(8)"} and defined $last[-1]{"LAG(4)"} ) {
$events{lag_break_up} = 1
if $last[-1]{'LAG(4)'} < $last[-1]{"LAG(8)"} and $row->{'LAG(4)'} > $row->{"LAG(8)"};
$events{lag_break_down} = 1
if $last[-1]{'LAG(4)'} > $last[-1]{"LAG(8)"} and $row->{'LAG(4)'} < $row->{"LAG(8)"};
}
for( 10, 20, 30 ) {
$events{rsi_up} = 1 if $events[-1]{"rsi_$_"} and not $events{"rsi_$_"};
$events{rsi_down} = 1 if not $events[-1]{"rsi_$_"} and $events{"rsi_$_"};
$events{crsi_up} = 1 if $events[-1]{"crsi_$_"} and not $events{"crsi_$_"};
$events{crsi_down} = 1 if not $events[-1]{"crsi_$_"} and $events{"crsi_$_"};
}
for( 90, 80, 70 ) {
$events{rsi_up} = 1 if not $events[-1]{"rsi_$_"} and $events{"rsi_$_"};
$events{rsi_down} = 1 if $events[-1]{"rsi_$_"} and not $events{"rsi_$_"};
$events{crsi_up} = 1 if not $events[-1]{"crsi_$_"} and $events{"crsi_$_"};
$events{crsi_down} = 1 if $events[-1]{"crsi_$_"} and not $events{"crsi_$_"};
}
for (10, 20, 30, 40, 50) {
$events{adx_up} = 1 if not $events[-1]{"adx_$_"} and $events{"adx_$_"};
$events{adx_down} = 1 if $events[-1]{"adx_$_"} and not $events{"adx_$_"};
}
# TODO:
# - other concepts like: we had a lag break and there was an rsi break x days ago
# - looking for divergences in the various indexes would go here
# - support levels on closing prices goes here
}
my @desc = sort keys %events;
$ins->execute($row->{rowid}, "@desc");
# train using items from @last, far enough back to avoid data snooping bias
for(@proj) {
my ($days, $percent) = @$_;
if( @last >= $days ) {
my $_row = $last[-$days] || die "bad maths";
my $_ev = $events[-$days] || die "bad maths";
if( keys %$_ev ) {
if( $row->{close} >= $_row->{close} * (1 + ($percent/100)) ) {
my %d = (attributes=>$_ev, label=>my $l = "p$_->[0]_$_->[1]");
push @{$instance_history{$l}}, \%d;
}
elsif( $row->{close} <= $_row->{close} * (1 - ($percent/100)) ) {
my %d = (attributes=>$_ev, label=>my $l = "m$_->[0]_$_->[1]");
push @{$instance_history{$l}}, \%d;
( run in 1.905 second using v1.01-cache-2.11-cpan-71847e10f99 )