App-Chart
view release on metacpan or search on metacpan
lib/App/Chart/Barchart.pm view on Meta::CPAN
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))))
#
# trailing "s" on last for settlement price
# have also seen "c", maybe for close
$last =~ s/[cs]$//i;
my ($last_date, $last_time);
if ($time =~ /:/) {
# time is HH:MM on same day as the quote
$last_date = $head_date;
$last_time = $time;
lib/App/Chart/Barchart.pm view on Meta::CPAN
#
# 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;
}
#-----------------------------------------------------------------------------
# intraday
#
# This uses the charting pages like
#
# http://www.barchart.com/charts/futures/CLZ10
#
# which for a 2-day intraday is
#
# http://www.barchart.com/chart.php?sym=CLZ10&style=technical&p=I&d=O&im=&sd=&ed=&size=M&log=0&t=BAR&v=2&g=1&evnt=1&late=1&o1=&o2=&o3=&x=41&y=11&indicators=&addindicator=&submitted=1&fpage=&txtDate=#jump
#
# must be fetched (about 50kbytes unfortunately), and it has a gif url.
# That url is some generated 4-digit number, apparently different each
# time, even outside trading hours. The server doesn't give an etag or
# last-modified to avoid re-downloading.
#
# The form fields are
lib/App/Chart/Barchart.pm view on Meta::CPAN
# 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,
#
# @itemize
# @c @item
# @c Quotes updated every 5 minutes, delayed according to the exchange.
# @c @item
# @c Intraday graphs.
# @item
# Historical data, but only for the past 5 days, and no volume figures.
# @item
# Historical data as graphs.
# @end itemize
#
# All information is for personal use only, see the terms at
#
# @quotation
# @uref{http://www2.barchart.com/agreement.asp} @*
( run in 0.854 second using v1.01-cache-2.11-cpan-437f7b0c052 )