App-Chart
view release on metacpan or search on metacpan
lib/App/Chart/Barchart.pm view on Meta::CPAN
#-----------------------------------------------------------------------------
# symbol munging
my @commodity_mung;
sub commodity_mung {
my ($pred, %table) = @_;
push @commodity_mung, [ $pred, \%table ];
}
sub symbol_to_barchart {
my ($symbol) = @_;
foreach my $elem (@commodity_mung) {
if ($elem->[0]->match ($symbol)) {
my $commodity = App::Chart::symbol_commodity ($symbol);
my $barchart = $elem->[1]->{$commodity};
if (defined $barchart) {
$symbol =~ s/^\Q$commodity/$barchart/;
}
last;
}
}
$symbol =~ s/.[^.]*$//;
return $symbol;
}
#------------------------------------------------------------------------------
# latest
#
# The intraday commodity quotes pages are used, like oats
#
# http://www2.barchart.com/ifutpage.asp?code=BSTK&sym=O
#
# which is about 35 kbytes each. An alternative would be the combined
# pages like all grains
#
# http://www2.barchart.com/mktcom.asp?code=BSTK§ion=grains
#
# which has the front month or two of various at about 50kbytes the lot.
App::Chart::LatestHandler->new
(pred => $latest_pred,
proc => \&latest_download,
max_symbols => 1);
sub latest_download {
my ($symbol_list) = @_;
my $symbol = $symbol_list->[0];
my $commodity = App::Chart::symbol_commodity ($symbol);
my $suffix = App::Chart::symbol_suffix ($symbol);
my $barchart_commodity = symbol_to_barchart ("$commodity$suffix");
App::Chart::Download::status
(__x('Barchart quote {symbol} ({barchart_commodity})',
symbol => "$commodity$suffix",
barchart_commodity => $barchart_commodity));
my $url = 'http://www2.barchart.com/ifutpage.asp?code=BSTK&sym='
. URI::Escape::uri_escape ($barchart_commodity);
my $resp = App::Chart::Download->get ($url,
cookie_jar => cookie_jar(),
referer => $url);
my $h = ifutpage_parse ($commodity, $suffix, $resp);
App::Chart::Download::write_latest_group ($h);
}
sub ifutpage_parse {
my ($commodity, $suffix, $resp) = @_;
my $content = $resp->decoded_content (raise_error => 1);
my @data = ();
my $h = { source => __PACKAGE__,
resp => $resp,
front_month => 1,
data => \@data };
# eg. " <B>CRUDE OIL</B> Delayed Futures -20:10 - Sunday, 19 June"
# " <B>SIMEX NIKKEI 225</B> Delayed Futures -18:20 - Tuesday, 12 December</td>"
# " <B>OATS </B> Daily Futures - Friday, 20 April </td>
$content =~ m{([^<>\r\n]+) *</B> Delayed Futures *- *([0-9]+:[0-9]+) *- *[A-Za-z]+, ([0-9]+ [A-Za-z]+)}is
or die 'Barchart: ifutpage name/date/time not matched';
my $name = $1;
my $head_time = $2;
my $head_date = $3;
### ifutpage head name: $name
### $head_time
### $head_date
require App::Chart::Suffix::NZ;
$head_date = App::Chart::Suffix::NZ::dm_str_to_nearest_iso ($head_date);
require HTML::TableExtract;
my $te = HTML::TableExtract->new
(headers => ['Contract', 'Last', 'Change', 'Open', 'High', 'Low', 'Time']);
$te->parse($content);
if (! $te->tables) { die 'Barchart: ifutpage price columns not matched'; }
foreach my $row ($te->rows) {
### ifutpage row: $row
my ($month, $last, $change, $open, $high, $low, $time) = @$row;
# eg. "August '05 ( CLQ05 )"
$month =~ /\( *([^ )]+) *\)/p
or die 'Barchart: ifutpage month form not recognised';
my $month_name = ${^PREMATCH}; # "August '05"
my $bar_symbol = $1; # "CLQ05"
my $MYY = substr ($bar_symbol, -3);
my $symbol = $commodity . $MYY . $suffix;
$month_name = App::Chart::collapse_whitespace ($month_name);
my $month_iso =App::Chart::Download::Decode_Date_EU_to_iso("1 $month_name");
# # subtract normal exchange delay to get quote time
# (set! quote-time (- quote-time (barchart-symbol-delay symbol)))
# (if (negative? quote-time)
# (begin
# (set! quote-time (+ quote-time 86400))
# (set! quote-adate (1- quote-adate))))
lib/App/Chart/Barchart.pm view on Meta::CPAN
if ($year) {
$year += 1900;
} else {
$year = App::Chart::Download::month_to_nearest_year ($mon+1);
}
return App::Chart::ymd_to_iso ($year, $mon+1, $mday);
}
#-----------------------------------------------------------------------------
# 5-day download
#
# This uses the rolling 5-day quote pages like
#
# http://www.barchart.com/detailedquote/futures/CLZ16
#
use constant FIVEDAY_URL_BASE =>
'http://www.barchart.com/detailedquote/futures/';
#
# which has daily open, high, low and close, volume.
#
# Going back only five days isn't good for much, but it's better than
# nothing and regular updates accumulate enough for a short-term picture.
App::Chart::DownloadHandler->new
(name => __('Barchart'),
pred => $fiveday_pred,
available_tdate => \&fiveday_available_tdate,
proc => \&fiveday_download,
max_symbols => 1);
# latest data available from barchart 5-day quote page
# the support page says the daily pages update at 5pm US central, try 5pm
# local for the 5-day
sub fiveday_available_tdate {
return App::Chart::Download::weekday_tdate_after_time
(17,0, App::Chart::TZ->chicago, -1);
}
sub fiveday_download {
my ($symbol_list) = @_;
my $symbol = $symbol_list->[0];
if (App::Chart::symbol_is_front ($symbol)) { return; }
# my $commodity = App::Chart::symbol_commodity ($symbol);
my $suffix = App::Chart::symbol_suffix ($symbol);
my $barchart_symbol = symbol_to_barchart ($symbol);
if ($barchart_symbol.$suffix eq $symbol) {
App::Chart::Download::status
(__x('Barchart five day {symbol}',
symbol => $symbol));
} else {
App::Chart::Download::status
(__x('Barchart five day {symbol} ({barchart_symbol})',
symbol => $symbol,
barchart_symbol => $barchart_symbol));
}
my $url = FIVEDAY_URL_BASE . URI::Escape::uri_escape ($barchart_symbol);
my $resp = App::Chart::Download->get ($url,
cookie_jar => cookie_jar(),
referer => $url);
my $h = fiveday_parse ($symbol, $resp);
App::Chart::Download::write_daily_group ($h);
}
sub fiveday_parse {
my ($symbol, $resp) = @_;
my @data;
my $h = { source => __PACKAGE__,
prefer_decimals => 2,
date_format => 'mdy',
data => \@data };
my $content = $resp->decoded_content (raise_error => 1);
# message in table when no info for given symbol (eg. an old month/year)
if ($content =~ /In order to form an extended quote/) {
return $h;
}
# eg. <h1 class="fl" id="symname">Crude Oil WTI December 2016 (CLZ16)</h1>
#
$content =~ m{<h1[^>]* id="symname">(([^<\r\n]*?) [a-z]+ [0-9]+)}si
or die "Barchart fiveday heading not matched";
my $name = $1;
### $name
require HTML::TableExtract;
my $te = HTML::TableExtract->new
(headers => ['Date', 'Open', 'High', 'Low', 'Last', 'Volume']);
$te->parse($content);
my ($ts) = $te->tables
or die 'Barchart: fiveday price columns not matched';
foreach my $row ($ts->rows) {
### fiveday row: $row
my ($date, $open, $high, $low, $close, $volume) = @$row;
$open = dash_frac_to_decimals ($open);
$high = dash_frac_to_decimals ($high);
$low = dash_frac_to_decimals ($low);
$close = dash_frac_to_decimals ($close);
push @data, { symbol => $symbol,
name => $name,
date => $date,
open => $open,
high => $high,
low => $low,
close => $close,
volume => $volume,
};
}
return $h;
}
lib/App/Chart/Barchart.pm view on Meta::CPAN
# E 576 by 360
# B 612 by 360
# C 720 by 432
# D 864 by 504
# sly=N linear
# L log
# Y fit available space
# late=
# ch1=011 OHLC [default]
# 012 close-only
# 013 candlestick
# ...
# ov1=
# ch2=
# ov2=
# code=BSTKIC IC for interactive chart
# vol=y/n volume (not avail for 5min)
foreach my $n (1, 2, 5) {
App::Chart::IntradayHandler->new
(pred => $intraday_pred,
proc => \&intraday_url,
mode => "${n}d",
name => __nx('_{n} Day',
'_{n} Days',
$n,
n => $n));
}
App::Chart::IntradayHandler->new
(pred => $intraday_pred,
proc => \&intraday_url,
mode => 'daily',
name => __('_Daily 2 Months'));
App::Chart::IntradayHandler->new
(pred => $intraday_pred,
proc => \&intraday_url,
mode => 'daily',
name => __('_Daily 1 Year'));
# 5 minute, linear scale
my %intraday_mode_to_data = ('1d' => '&p=I&d=L',
'2d' => '&p=I&d=O',
'3d' => '&p=I&d=M',
'4d' => '&p=I&d=H',
'5d' => '&p=I&d=X',
'daily2m' => '&p=DO&d=L',
'daily1y' => '&p=DO&d=X');
# '7d' => '&data=Z60&den=MEDHIGH',
# 'daily' => '&data=A');
sub intraday_url {
my ($self, $symbol, $mode) = @_;
App::Chart::Download::status
(__x('Barchart intraday page {symbol} {mode}',
symbol => $symbol,
mode => $mode));
my $url = 'http://www.barchart.com/chart.php?sym='
. URI::Escape::uri_escape (symbol_to_barchart ($symbol))
. $intraday_mode_to_data{$mode};
App::Chart::Download::verbose_message ("Intraday page", $url);
my $jar = cookie_jar();
my $resp = App::Chart::Download->get ($url,
cookie_jar => $jar,
referer => $url);
return (intraday_resp_to_url ($resp, $symbol),
cookie_jar => $jar,
referer => $url);
}
# separate func for offline testing ...
sub intraday_resp_to_url {
my ($resp, $symbol) = @_;
my $content = $resp->decoded_content (raise_error => 1);
require HTML::LinkExtor;
my $parser = HTML::LinkExtor->new(undef, $resp->base);
$parser->parse($content);
$parser->eof;
# eg.
# </map><img src="/cache/bde71ebe23ddac66f2d25081b1b5f953.png"
# must match some of the link target name since there's other images in
# the page
foreach my $link ($parser->links) {
my ($tag, %attr) = @$link;
$tag eq 'img' or next;
my $url = $attr{'src'};
index ($url, '/cache/') >= 0 or next;
### $url
return URI->new_abs($url,$resp->base)->as_string;
}
if ($content =~ /Could not find any symbols/i) {
die __x("No such symbol {symbol}\n",
symbol => $symbol);
} else {
die 'Barchart Customer: Intraday page not matched';
}
}
1;
__END__
# ---------------------------------------------------------------------------
# @node Barchart, Finance Quote, Data Sources, Data Sources
# @section Barchart
# @cindex @code{barchart.com}
#
# @uref{http://www.barchart.com}
#
# Barchart provides the following for various futures exchanges around the
# world,
#
( run in 0.929 second using v1.01-cache-2.11-cpan-39bf76dae61 )