| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# Reserve Bank of Australia setups. |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# Copyright 2007, 2008, 2009, 2010, 2011, 2012, 2016 Kevin Ryde |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# This file is part of Chart. |
|
6
|
|
|
|
|
|
|
# |
|
7
|
|
|
|
|
|
|
# Chart is free software; you can redistribute it and/or modify it under the |
|
8
|
|
|
|
|
|
|
# terms of the GNU General Public License as published by the Free Software |
|
9
|
|
|
|
|
|
|
# Foundation; either version 3, or (at your option) any later version. |
|
10
|
|
|
|
|
|
|
# |
|
11
|
|
|
|
|
|
|
# Chart is distributed in the hope that it will be useful, but WITHOUT ANY |
|
12
|
|
|
|
|
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|
13
|
|
|
|
|
|
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
|
14
|
|
|
|
|
|
|
# details. |
|
15
|
|
|
|
|
|
|
# |
|
16
|
|
|
|
|
|
|
# You should have received a copy of the GNU General Public License along |
|
17
|
|
|
|
|
|
|
# with Chart. If not, see <http://www.gnu.org/licenses/>. |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
package App::Chart::Suffix::RBA; |
|
20
|
1
|
|
|
1
|
|
521
|
use 5.010; |
|
|
1
|
|
|
|
|
3
|
|
|
21
|
1
|
|
|
1
|
|
5
|
use strict; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
17
|
|
|
22
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
29
|
|
|
23
|
1
|
|
|
1
|
|
5
|
use Carp; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
50
|
|
|
24
|
1
|
|
|
1
|
|
221
|
use Date::Calc; |
|
|
1
|
|
|
|
|
4683
|
|
|
|
1
|
|
|
|
|
61
|
|
|
25
|
1
|
|
|
1
|
|
10
|
use List::Util qw(min max); |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
82
|
|
|
26
|
1
|
|
|
1
|
|
317
|
use Set::IntSpan::Fast; |
|
|
1
|
|
|
|
|
6431
|
|
|
|
1
|
|
|
|
|
49
|
|
|
27
|
1
|
|
|
1
|
|
13
|
use Locale::TextDomain ('App-Chart'); |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
14
|
|
|
28
|
|
|
|
|
|
|
|
|
29
|
1
|
|
|
1
|
|
540
|
use App::Chart; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
use App::Chart::Database; |
|
31
|
|
|
|
|
|
|
use App::Chart::Download; |
|
32
|
|
|
|
|
|
|
use App::Chart::DownloadHandler; |
|
33
|
|
|
|
|
|
|
use App::Chart::Sympred; |
|
34
|
|
|
|
|
|
|
use App::Chart::Latest; |
|
35
|
|
|
|
|
|
|
use App::Chart::TZ; |
|
36
|
|
|
|
|
|
|
use App::Chart::Weblink; |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# uncomment this to run the ### lines |
|
39
|
|
|
|
|
|
|
# use Smart::Comments; |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# Not yet using Finance::Quote::RBA |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
my $pred = App::Chart::Sympred::Suffix->new ('.RBA'); |
|
44
|
|
|
|
|
|
|
App::Chart::TZ->sydney->setup_for_symbol ($pred); |
|
45
|
|
|
|
|
|
|
App::Chart::setup_source_help |
|
46
|
|
|
|
|
|
|
($pred, __p('manual-node','Reserve Bank of Australia')); |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
use constant RBA_COPYRIGHT_URL => |
|
49
|
|
|
|
|
|
|
'http://www.rba.gov.au/copyright/'; |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
53
|
|
|
|
|
|
|
# weblink - home page |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
App::Chart::Weblink->new |
|
56
|
|
|
|
|
|
|
(pred => $pred, |
|
57
|
|
|
|
|
|
|
name => __('_RBA Home Page'), |
|
58
|
|
|
|
|
|
|
desc => __('Open web browser at the Reserve Bank of Australia home page'), |
|
59
|
|
|
|
|
|
|
url => 'http://www.rba.gov.au'); |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
63
|
|
|
|
|
|
|
# three day page |
|
64
|
|
|
|
|
|
|
# |
|
65
|
|
|
|
|
|
|
# This uses the rates at: |
|
66
|
|
|
|
|
|
|
# |
|
67
|
|
|
|
|
|
|
use constant RBA_EXCHANGE_URL => |
|
68
|
|
|
|
|
|
|
'http://www.rba.gov.au/statistics/frequency/exchange-rates.html'; |
|
69
|
|
|
|
|
|
|
use constant RBA_EXCHANGE_URL_DAYS => 3; |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# would it be a few minutes after 4pm ? |
|
72
|
|
|
|
|
|
|
sub threeday_available_date_time { |
|
73
|
|
|
|
|
|
|
return (App::Chart::Download::weekday_date_after_time |
|
74
|
|
|
|
|
|
|
(16,0, App::Chart::TZ->sydney), |
|
75
|
|
|
|
|
|
|
'16:01:00'); |
|
76
|
|
|
|
|
|
|
} |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub threeday_available_tdate { |
|
79
|
|
|
|
|
|
|
my ($iso, $time) = threeday_available_date_time(); |
|
80
|
|
|
|
|
|
|
return App::Chart::Download::iso_to_tdate_floor ($iso); |
|
81
|
|
|
|
|
|
|
} |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub threeday_parse { |
|
84
|
|
|
|
|
|
|
my ($resp) = @_; |
|
85
|
|
|
|
|
|
|
my @data = (); |
|
86
|
|
|
|
|
|
|
my $h = { url => RBA_EXCHANGE_URL, |
|
87
|
|
|
|
|
|
|
copyright => RBA_COPYRIGHT_URL, |
|
88
|
|
|
|
|
|
|
source => __PACKAGE__, |
|
89
|
|
|
|
|
|
|
resp => $resp, |
|
90
|
|
|
|
|
|
|
cover_pred => $pred, |
|
91
|
|
|
|
|
|
|
data => \@data }; |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
my $content = $resp->decoded_content(raise_error=>1); |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# mung <tr id="USD"> to add <td>USD</td> so it appears in the TableExtract |
|
96
|
|
|
|
|
|
|
$content =~ s{<tr>}{<tr><td></td>}ig; |
|
97
|
|
|
|
|
|
|
$content =~ s{(<tr +id="([^"]*)">)}{$1<td>$2</td>}ig; |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
require HTML::TableExtract; |
|
100
|
|
|
|
|
|
|
my $te = HTML::TableExtract->new |
|
101
|
|
|
|
|
|
|
(headers => ['Units of foreign currency per'], |
|
102
|
|
|
|
|
|
|
slice_columns => 0); |
|
103
|
|
|
|
|
|
|
$te->parse($content); |
|
104
|
|
|
|
|
|
|
my $ts = $te->first_table_found(); |
|
105
|
|
|
|
|
|
|
if (! $ts) { die "RBA: html table not found\n"; } |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
my $rows = $ts->rows(); |
|
108
|
|
|
|
|
|
|
my $lastrow = $#$rows; |
|
109
|
|
|
|
|
|
|
my $lastcol = $#{$rows->[0]}; |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
# date like "03 Sep 2007" |
|
112
|
|
|
|
|
|
|
my @dates; |
|
113
|
|
|
|
|
|
|
foreach my $c (2 .. $lastcol) { |
|
114
|
|
|
|
|
|
|
$dates[$c] = App::Chart::Download::Decode_Date_EU_to_iso($rows->[0]->[$c]); |
|
115
|
|
|
|
|
|
|
} |
|
116
|
|
|
|
|
|
|
$h->{'lo_date'} = List::Util::minstr (grep {defined} @dates); |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
foreach my $r (1 .. $lastrow) { |
|
119
|
|
|
|
|
|
|
my $row = $rows->[$r]; |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
my $symbol = $row->[0] // next; |
|
122
|
|
|
|
|
|
|
$symbol =~ s/_.*//; # _4pm on TWI |
|
123
|
|
|
|
|
|
|
$symbol = "AUD$symbol.RBA"; |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
my $name = $row->[1]; |
|
126
|
|
|
|
|
|
|
$name =~ s/ \(4pm\)$//; # 4pm on TWI |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
foreach my $c (2 .. $lastcol) { |
|
129
|
|
|
|
|
|
|
my $rate = $row->[$c]; |
|
130
|
|
|
|
|
|
|
# bank holiday columns have "BANK HOLIDAY" with one letter per row or |
|
131
|
|
|
|
|
|
|
# blank which comes through as undef, skip those |
|
132
|
|
|
|
|
|
|
next if ! Scalar::Util::looks_like_number($rate); |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
push @data, { symbol => $symbol, |
|
135
|
|
|
|
|
|
|
name => $name, |
|
136
|
|
|
|
|
|
|
date => $dates[$c], |
|
137
|
|
|
|
|
|
|
last_time => '16:00:00', |
|
138
|
|
|
|
|
|
|
close => $rate, |
|
139
|
|
|
|
|
|
|
currency => substr($symbol,3,3), |
|
140
|
|
|
|
|
|
|
}; |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
} |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
return $h; |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
148
|
|
|
|
|
|
|
# latest quotes |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
App::Chart::LatestHandler->new |
|
151
|
|
|
|
|
|
|
(pred => $pred, |
|
152
|
|
|
|
|
|
|
url_tags_key => 'RBA-latest', |
|
153
|
|
|
|
|
|
|
proc => \&latest_download, |
|
154
|
|
|
|
|
|
|
available_date_time => \&threeday_available_date_time); |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub latest_download { |
|
157
|
|
|
|
|
|
|
my ($symbol_list) = @_; |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
App::Chart::Download::status (__('RBA past three days')); |
|
160
|
|
|
|
|
|
|
my $resp = App::Chart::Download->get (RBA_EXCHANGE_URL, |
|
161
|
|
|
|
|
|
|
url_tags_key => 'RBA-latest'); |
|
162
|
|
|
|
|
|
|
App::Chart::Download::write_latest_group (threeday_parse ($resp)); |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
167
|
|
|
|
|
|
|
# historical xls page |
|
168
|
|
|
|
|
|
|
# |
|
169
|
|
|
|
|
|
|
# This downloads and parses up the page: |
|
170
|
|
|
|
|
|
|
# http://www.rba.gov.au/statistics/historical-data.html |
|
171
|
|
|
|
|
|
|
# |
|
172
|
|
|
|
|
|
|
# 2014 to present |
|
173
|
|
|
|
|
|
|
# http://www.rba.gov.au/statistics/tables/csv/f11.1-data.csv |
|
174
|
|
|
|
|
|
|
# 213k or 47k compressed, also byte ranges |
|
175
|
|
|
|
|
|
|
# http://www.rba.gov.au/statistics/tables/xls-hist/2014-current.xls |
|
176
|
|
|
|
|
|
|
# 438k |
|
177
|
|
|
|
|
|
|
# |
|
178
|
|
|
|
|
|
|
use constant RBA_HISTORICAL_PAGE_URL => |
|
179
|
|
|
|
|
|
|
'http://www.rba.gov.au/statistics/hist-exchange-rates/index.html'; |
|
180
|
|
|
|
|
|
|
# |
|
181
|
|
|
|
|
|
|
# which offers various xls files for past rates. |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub historical_info { |
|
184
|
|
|
|
|
|
|
require App::Chart::Pagebits; |
|
185
|
|
|
|
|
|
|
return App::Chart::Pagebits::get |
|
186
|
|
|
|
|
|
|
(name => __('RBA historical page'), |
|
187
|
|
|
|
|
|
|
url => RBA_HISTORICAL_PAGE_URL, |
|
188
|
|
|
|
|
|
|
key => 'rba-historical', |
|
189
|
|
|
|
|
|
|
freq_days => 1, |
|
190
|
|
|
|
|
|
|
parse => \&historical_parse); |
|
191
|
|
|
|
|
|
|
} |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
sub historical_parse { |
|
194
|
|
|
|
|
|
|
my ($content) = @_; |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# Eg. |
|
197
|
|
|
|
|
|
|
# <a href="/statistics/hist-exchange-rates/2007-2009.xls" |
|
198
|
|
|
|
|
|
|
# target="_blank" title="Link, opening in a new window, to XLS file."> |
|
199
|
|
|
|
|
|
|
# 2007 to 2009</a> <span class="nonHtml">[XLS 227K]</span> |
|
200
|
|
|
|
|
|
|
# |
|
201
|
|
|
|
|
|
|
# because the size is outside the link it doesn't really suit |
|
202
|
|
|
|
|
|
|
# HTML::LinkExtor / HTML::Parser etc |
|
203
|
|
|
|
|
|
|
# |
|
204
|
|
|
|
|
|
|
my @files; |
|
205
|
|
|
|
|
|
|
while ($content =~ m%href=\"([^\"]*[0-9][0-9][0-9][0-9]\.xls)\" |
|
206
|
|
|
|
|
|
|
(.*\n){0,3} |
|
207
|
|
|
|
|
|
|
.*\[XLS\s*([0-9.]+)([MK]) |
|
208
|
|
|
|
|
|
|
%igmx) { |
|
209
|
|
|
|
|
|
|
my $link = $1; |
|
210
|
|
|
|
|
|
|
my $size = $3; |
|
211
|
|
|
|
|
|
|
my $size_units = uc($4); |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
if ($size_units eq 'K') { $size *= 1_000 } |
|
214
|
|
|
|
|
|
|
if ($size_units eq 'M') { $size *= 1_000_000 } |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
my $uri = URI->new_abs($link, RBA_HISTORICAL_PAGE_URL); |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# eg per above: 2003-2007.xls |
|
219
|
|
|
|
|
|
|
# or just: 2007.xls |
|
220
|
|
|
|
|
|
|
$link =~ /([0-9][0-9][0-9][0-9])((to|-)([0-9][0-9][0-9][0-9]))?\.xls$/ |
|
221
|
|
|
|
|
|
|
or die "RBA: oops, unrecognised link: $link"; |
|
222
|
|
|
|
|
|
|
my $lo_year = $1; |
|
223
|
|
|
|
|
|
|
my $hi_year = $4 || $1; |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
push @files, { url => $uri->as_string, |
|
226
|
|
|
|
|
|
|
cost => $size, |
|
227
|
|
|
|
|
|
|
lo_year => $lo_year, |
|
228
|
|
|
|
|
|
|
hi_year => $hi_year, |
|
229
|
|
|
|
|
|
|
}; |
|
230
|
|
|
|
|
|
|
} |
|
231
|
|
|
|
|
|
|
return { files => \@files }; |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
|
236
|
|
|
|
|
|
|
# download - historical monthly prices |
|
237
|
|
|
|
|
|
|
# |
|
238
|
|
|
|
|
|
|
# This parses the monthly rates spreadsheet file from the |
|
239
|
|
|
|
|
|
|
# RBA_HISTORICAL_PAGE_URL page above, |
|
240
|
|
|
|
|
|
|
# |
|
241
|
|
|
|
|
|
|
# http://www.rba.gov.au/statistics/tables/xls/f11hist-1969-2009.xls |
|
242
|
|
|
|
|
|
|
# |
|
243
|
|
|
|
|
|
|
# but only the part from 1983 back is wanted since there's daily data for |
|
244
|
|
|
|
|
|
|
# 1983 onwards. |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
my %monthly_fx_to_currency |
|
247
|
|
|
|
|
|
|
= ( |
|
248
|
|
|
|
|
|
|
# 'TWI' # trade weighted index |
|
249
|
|
|
|
|
|
|
'CR' => 'CNY', # chinese renminbi |
|
250
|
|
|
|
|
|
|
'JY' => 'JPY', # japanese yen |
|
251
|
|
|
|
|
|
|
# 'EUR' |
|
252
|
|
|
|
|
|
|
# 'USD' |
|
253
|
|
|
|
|
|
|
'SKW' => 'KRW', # South Korean won |
|
254
|
|
|
|
|
|
|
'UKPS' => 'GBP', # british pound sterling |
|
255
|
|
|
|
|
|
|
'SD' => 'SGD', # singapore dollar |
|
256
|
|
|
|
|
|
|
'IRE' => 'INR', # Indian rupee |
|
257
|
|
|
|
|
|
|
'TB' => 'THB', # Thai baht |
|
258
|
|
|
|
|
|
|
# 'NZD' |
|
259
|
|
|
|
|
|
|
'NTD' => 'TWD', # taiwan dollar |
|
260
|
|
|
|
|
|
|
'MR' => 'MYR', # malaysian ringgit |
|
261
|
|
|
|
|
|
|
'IR' => 'IDR', # indonesian rupiah |
|
262
|
|
|
|
|
|
|
'VD' => 'VND', # Vietnamese dong |
|
263
|
|
|
|
|
|
|
'UAED' => 'AED', # UAE dirham |
|
264
|
|
|
|
|
|
|
'PNGK' => 'PGK', # PNG kina |
|
265
|
|
|
|
|
|
|
# 'HKD' # Hong Kong dollar |
|
266
|
|
|
|
|
|
|
'CD' => 'CAD', # Canadian dollar |
|
267
|
|
|
|
|
|
|
'SARD' => 'ZAR', # South African rand |
|
268
|
|
|
|
|
|
|
'SARY' => 'SAR', # Saudi riyal |
|
269
|
|
|
|
|
|
|
'SF' => 'CHF', # Swiss franc |
|
270
|
|
|
|
|
|
|
'SK' => 'SEK', # Swedish krona |
|
271
|
|
|
|
|
|
|
# 'SDR' # special drawing right |
|
272
|
|
|
|
|
|
|
); |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
sub monthly_parse { |
|
275
|
|
|
|
|
|
|
my ($resp, $stop_iso) = @_; |
|
276
|
|
|
|
|
|
|
### RBA monthly_parse() ... |
|
277
|
|
|
|
|
|
|
my $content = $resp->decoded_content(raise_error=>1); |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
my @data = (); |
|
280
|
|
|
|
|
|
|
my $h = { source => __PACKAGE__, |
|
281
|
|
|
|
|
|
|
copyright => RBA_COPYRIGHT_URL, |
|
282
|
|
|
|
|
|
|
data => \@data }; |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
require Spreadsheet::ParseExcel; |
|
285
|
|
|
|
|
|
|
require Spreadsheet::ParseExcel::Utility; |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
my $excel = Spreadsheet::ParseExcel::Workbook->Parse (\$content); |
|
288
|
|
|
|
|
|
|
my $sheet = $excel->Worksheet (0); |
|
289
|
|
|
|
|
|
|
### SheetCount: $excel->{'SheetCount'} |
|
290
|
|
|
|
|
|
|
### Name: $sheet->{'Name'} |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
my ($minrow, $maxrow) = $sheet->RowRange; |
|
293
|
|
|
|
|
|
|
my ($mincol, $maxcol) = $sheet->ColRange; |
|
294
|
|
|
|
|
|
|
### rows: $minrow, $maxrow |
|
295
|
|
|
|
|
|
|
### cols: $mincol, $maxcol |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
# heading row repeats the filename "F11HIST.XLS" and then the currencies |
|
298
|
|
|
|
|
|
|
# in columns as say "FXRJY" |
|
299
|
|
|
|
|
|
|
my $heading_row = List::Util::first { |
|
300
|
|
|
|
|
|
|
my $cell = $sheet->Cell($_,$mincol); |
|
301
|
|
|
|
|
|
|
$cell && $cell->Value eq 'F11HIST.XLS' } |
|
302
|
|
|
|
|
|
|
($minrow .. $maxrow) |
|
303
|
|
|
|
|
|
|
or die "RBA monthly: headings not found"; |
|
304
|
|
|
|
|
|
|
### $heading_row |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
my @currencies = map { |
|
307
|
|
|
|
|
|
|
my $cell = $sheet->Cell($heading_row,$_); |
|
308
|
|
|
|
|
|
|
my $currency = $cell ? $cell->Value : ''; |
|
309
|
|
|
|
|
|
|
$currency =~ s/^FXR//; |
|
310
|
|
|
|
|
|
|
($monthly_fx_to_currency{$currency} || $currency) |
|
311
|
|
|
|
|
|
|
} ($mincol .. $maxcol); |
|
312
|
|
|
|
|
|
|
### @currencies |
|
313
|
|
|
|
|
|
|
### count: scalar(@currencies) |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
my %currency_started; |
|
316
|
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
ROW: foreach my $row ($heading_row+1 .. $maxrow) { |
|
318
|
|
|
|
|
|
|
my $datecell = $sheet->Cell($row,0) or next; |
|
319
|
|
|
|
|
|
|
# seen 'Numeric', but presumably 'Date' is ok |
|
320
|
|
|
|
|
|
|
if ($datecell->{'Type'} ne 'Numeric' |
|
321
|
|
|
|
|
|
|
&& $datecell->{'Type'} ne 'Date') { |
|
322
|
|
|
|
|
|
|
next; # skip blanks at end |
|
323
|
|
|
|
|
|
|
} |
|
324
|
|
|
|
|
|
|
my $month = Spreadsheet::ParseExcel::Utility::ExcelFmt |
|
325
|
|
|
|
|
|
|
('yyyy-mm-dd', $datecell->{'Val'}, $excel->{'Flg1904'}); |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
foreach my $col ($mincol+1 .. $maxcol) { |
|
328
|
|
|
|
|
|
|
my $currency = $currencies[$col-$mincol] or next; |
|
329
|
|
|
|
|
|
|
my $ratecell = $sheet->Cell($row,$col) or next; |
|
330
|
|
|
|
|
|
|
my $rate = $ratecell->Value; |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
# avoid empty records until the start of data for a given currency is |
|
333
|
|
|
|
|
|
|
# reached |
|
334
|
|
|
|
|
|
|
if (! $rate && ! $currency_started{$currency}) { next; } |
|
335
|
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
my $symbol = "AUD$currency.RBA"; |
|
337
|
|
|
|
|
|
|
$currency_started{$currency} = 1; |
|
338
|
|
|
|
|
|
|
foreach my $date (iso_weekdays_in_month ($month)) { |
|
339
|
|
|
|
|
|
|
if ($date gt $stop_iso) { last ROW; } |
|
340
|
|
|
|
|
|
|
push @data, { symbol => $symbol, |
|
341
|
|
|
|
|
|
|
currency => $currency, |
|
342
|
|
|
|
|
|
|
date => $date, |
|
343
|
|
|
|
|
|
|
close => $rate, |
|
344
|
|
|
|
|
|
|
}; |
|
345
|
|
|
|
|
|
|
} |
|
346
|
|
|
|
|
|
|
} |
|
347
|
|
|
|
|
|
|
} |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
return $h; |
|
350
|
|
|
|
|
|
|
} |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# return a list of ISO date strings like '2008-09-08' which is all the |
|
353
|
|
|
|
|
|
|
# weekdays in the month of ISO date $str |
|
354
|
|
|
|
|
|
|
sub iso_weekdays_in_month { |
|
355
|
|
|
|
|
|
|
my ($str) = @_; |
|
356
|
|
|
|
|
|
|
my ($lo_year, $lo_month, undef) = App::Chart::iso_to_ymd ($str); |
|
357
|
|
|
|
|
|
|
my ($hi_year, $hi_month, undef) = Date::Calc::Add_Delta_YM |
|
358
|
|
|
|
|
|
|
($lo_year,$lo_month,1, 0,1); |
|
359
|
|
|
|
|
|
|
my $lo = App::Chart::ymd_to_tdate_ceil ($lo_year, $lo_month, 1); |
|
360
|
|
|
|
|
|
|
my $hi = App::Chart::ymd_to_tdate_ceil ($hi_year, $hi_month, 1) - 1; |
|
361
|
|
|
|
|
|
|
return map { App::Chart::tdate_to_iso($_) } ($lo .. $hi); |
|
362
|
|
|
|
|
|
|
} |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
366
|
|
|
|
|
|
|
# xls parse |
|
367
|
|
|
|
|
|
|
# |
|
368
|
|
|
|
|
|
|
# This parses xls spreadsheet files like |
|
369
|
|
|
|
|
|
|
# |
|
370
|
|
|
|
|
|
|
# http://www.rba.gov.au/Statistics/HistoricalExchangeRates/2003to2007.xls |
|
371
|
|
|
|
|
|
|
# |
|
372
|
|
|
|
|
|
|
# The files aren't huge (500k upwards) but there's a lot of cells, which |
|
373
|
|
|
|
|
|
|
# makes Spreadsheet::ParseExcel fairly slow and eat up about 50Mb of core |
|
374
|
|
|
|
|
|
|
# (as of version 0.32), even before getting to the $h data build. |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
sub xls_parse { |
|
377
|
|
|
|
|
|
|
my ($resp) = @_; |
|
378
|
|
|
|
|
|
|
### RBA xls_parse() ... |
|
379
|
|
|
|
|
|
|
my $content = $resp->decoded_content(raise_error=>1); |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
my @data = (); |
|
382
|
|
|
|
|
|
|
my $h = { source => __PACKAGE__, |
|
383
|
|
|
|
|
|
|
copyright => RBA_COPYRIGHT_URL, |
|
384
|
|
|
|
|
|
|
data => \@data }; |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
require Spreadsheet::ParseExcel; |
|
387
|
|
|
|
|
|
|
require Spreadsheet::ParseExcel::Utility; |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
my $excel = Spreadsheet::ParseExcel::Workbook->Parse (\$content); |
|
390
|
|
|
|
|
|
|
my $sheet = $excel->Worksheet (0); |
|
391
|
|
|
|
|
|
|
### sheet: $sheet->{'Name'} |
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
my ($minrow, $maxrow) = $sheet->RowRange; |
|
394
|
|
|
|
|
|
|
my ($mincol, $maxcol) = $sheet->ColRange; |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
# heading row "DAILY 4PM", or "Mnemonic" and the currencies in columns |
|
397
|
|
|
|
|
|
|
my $heading_row = List::Util::first { |
|
398
|
|
|
|
|
|
|
my $cell = $sheet->Cell($_,$mincol); |
|
399
|
|
|
|
|
|
|
$cell && ($cell->Value eq 'DAILY 4PM' |
|
400
|
|
|
|
|
|
|
|| $cell->Value eq 'Mnemonic') } |
|
401
|
|
|
|
|
|
|
($minrow .. $maxrow) |
|
402
|
|
|
|
|
|
|
or die "RBA historical: currency code headings row not found"; |
|
403
|
|
|
|
|
|
|
### heading row: $heading_row |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
foreach my $row ($heading_row+1 .. $maxrow) { |
|
406
|
|
|
|
|
|
|
my $datecell = $sheet->Cell($row,$mincol) or next; |
|
407
|
|
|
|
|
|
|
$datecell->{'Type'} eq 'Date' or next; # skip blanks |
|
408
|
|
|
|
|
|
|
my $date = Spreadsheet::ParseExcel::Utility::ExcelFmt |
|
409
|
|
|
|
|
|
|
('yyyy-mm-dd', $datecell->{'Val'}, $excel->{'Flg1904'}); |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
foreach my $col ($mincol+1 .. $maxcol) { |
|
412
|
|
|
|
|
|
|
my $cell = $sheet->Cell($row,$col) |
|
413
|
|
|
|
|
|
|
or next; # skip lots of blanks |
|
414
|
|
|
|
|
|
|
my $rate = $cell->Value |
|
415
|
|
|
|
|
|
|
or next; # skip lots of blanks |
|
416
|
|
|
|
|
|
|
my $currency = $sheet->Cell($heading_row,$col)->Value; |
|
417
|
|
|
|
|
|
|
$currency =~ s/^FXR//; # leading "FXR" circa 2012 |
|
418
|
|
|
|
|
|
|
$currency = ($monthly_fx_to_currency{$currency} || $currency); |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
push @data, { symbol => "AUD$currency.RBA", |
|
421
|
|
|
|
|
|
|
currency => $currency, |
|
422
|
|
|
|
|
|
|
date => $date, |
|
423
|
|
|
|
|
|
|
close => $rate, |
|
424
|
|
|
|
|
|
|
}; |
|
425
|
|
|
|
|
|
|
} |
|
426
|
|
|
|
|
|
|
} |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
return $h; |
|
429
|
|
|
|
|
|
|
} |
|
430
|
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
434
|
|
|
|
|
|
|
# csv parse |
|
435
|
|
|
|
|
|
|
# |
|
436
|
|
|
|
|
|
|
# This parses csv download files like |
|
437
|
|
|
|
|
|
|
# |
|
438
|
|
|
|
|
|
|
# http://www.rba.gov.au/statistics/tables/csv/f11.1-data.csv |
|
439
|
|
|
|
|
|
|
# |
|
440
|
|
|
|
|
|
|
# row Title, A$1=USD, Trade-weighted Index May 1970 = 100, A$1=CNY, A$1=JPY, |
|
441
|
|
|
|
|
|
|
# |
|
442
|
|
|
|
|
|
|
# row Description,AUD/USD Exchange Rate; see notes for further detail., |
|
443
|
|
|
|
|
|
|
# Australian Dollar Trade-weighted Index, |
|
444
|
|
|
|
|
|
|
# AUD/CNY Exchange Rate,AUD/JPY Exchange Rate, |
|
445
|
|
|
|
|
|
|
# |
|
446
|
|
|
|
|
|
|
# row Frequency,Daily,Daily,Daily |
|
447
|
|
|
|
|
|
|
# row Type,Indicative,Indicative, |
|
448
|
|
|
|
|
|
|
# row Units,USD,Index,CNY,JPY, |
|
449
|
|
|
|
|
|
|
# row Source,WM/Reuters,RBA,RBA, |
|
450
|
|
|
|
|
|
|
# row Publication date,17-Feb-2016,17-Feb-2016 |
|
451
|
|
|
|
|
|
|
# row Series ID,FXRUSD,FXRTWI,FXRCR,FXRJY,FXREUR, |
|
452
|
|
|
|
|
|
|
# |
|
453
|
|
|
|
|
|
|
# row 17-Feb-2016,0.7090,60.80,4.6265,80.61, |
|
454
|
|
|
|
|
|
|
# |
|
455
|
|
|
|
|
|
|
# csv |
|
456
|
|
|
|
|
|
|
# AED CAD CHF CNY EUR GBP HKD IDR INR JPY KRW MYR NZD PGK PHP SDR SGD THB TWD TWI USD VND ZAR |
|
457
|
|
|
|
|
|
|
# |
|
458
|
|
|
|
|
|
|
# exchange page |
|
459
|
|
|
|
|
|
|
# AED CAD CHF CNY EUR GBP HKD IDR INR JPY KRW MYR NZD PGK PHP SDR SGD THB TWD TWI USD VND |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
use constant RBA_CURRENT_CSV_URL => |
|
462
|
|
|
|
|
|
|
'http://www.rba.gov.au/statistics/tables/csv/f11.1-data.csv'; |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
sub csv_parse { |
|
465
|
|
|
|
|
|
|
my ($resp) = @_; |
|
466
|
|
|
|
|
|
|
### RBA csv_parse() ... |
|
467
|
|
|
|
|
|
|
my $content = $resp->decoded_content(raise_error=>1); |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
my @data = (); |
|
470
|
|
|
|
|
|
|
my $h = { source => __PACKAGE__, |
|
471
|
|
|
|
|
|
|
copyright => RBA_COPYRIGHT_URL, |
|
472
|
|
|
|
|
|
|
date_format => 'dmy', |
|
473
|
|
|
|
|
|
|
data => \@data }; |
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
my @currencies; |
|
476
|
|
|
|
|
|
|
foreach my $line (split /\n/, $content) { |
|
477
|
|
|
|
|
|
|
my @fields = split /,\s*/, $line; |
|
478
|
|
|
|
|
|
|
my $key = shift @fields // next; |
|
479
|
|
|
|
|
|
|
### $key |
|
480
|
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
if ($key eq 'Title') { |
|
482
|
|
|
|
|
|
|
@currencies = map { |
|
483
|
|
|
|
|
|
|
my $field = $_; |
|
484
|
|
|
|
|
|
|
if ($field eq '') { # empty fields at end of line |
|
485
|
|
|
|
|
|
|
undef; |
|
486
|
|
|
|
|
|
|
} elsif ($field =~ /Trade.Weighted.Index/i) { |
|
487
|
|
|
|
|
|
|
'TWI'; |
|
488
|
|
|
|
|
|
|
} elsif ($field =~ /A\$1=(.*)/i) { |
|
489
|
|
|
|
|
|
|
$1; |
|
490
|
|
|
|
|
|
|
} else { |
|
491
|
|
|
|
|
|
|
warn "RBA: unrecognised Title field: $field"; |
|
492
|
|
|
|
|
|
|
undef; |
|
493
|
|
|
|
|
|
|
} |
|
494
|
|
|
|
|
|
|
} @fields; |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
} elsif ($key =~ /\d+-[a-z]+-\d+/i) { |
|
497
|
|
|
|
|
|
|
# row like 17-Feb-2016,0.7090,60.80,4.6265,80.61, |
|
498
|
|
|
|
|
|
|
foreach my $i (0 .. $#fields) { |
|
499
|
|
|
|
|
|
|
my $currency = $currencies[$i] // next; |
|
500
|
|
|
|
|
|
|
push @data, { symbol => "AUD$currency.RBA", |
|
501
|
|
|
|
|
|
|
currency => $currency, |
|
502
|
|
|
|
|
|
|
date => $key, |
|
503
|
|
|
|
|
|
|
close => $fields[$i], |
|
504
|
|
|
|
|
|
|
}; |
|
505
|
|
|
|
|
|
|
} |
|
506
|
|
|
|
|
|
|
} |
|
507
|
|
|
|
|
|
|
} |
|
508
|
|
|
|
|
|
|
return $h; |
|
509
|
|
|
|
|
|
|
} |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
514
|
|
|
|
|
|
|
# data downloading |
|
515
|
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
App::Chart::DownloadHandler->new |
|
517
|
|
|
|
|
|
|
(name => __('RBA'), |
|
518
|
|
|
|
|
|
|
pred => $pred, |
|
519
|
|
|
|
|
|
|
proc => \&download, |
|
520
|
|
|
|
|
|
|
# backto => \&backto, |
|
521
|
|
|
|
|
|
|
available_date_time => \&threeday_available_date_time); |
|
522
|
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
sub download { |
|
524
|
|
|
|
|
|
|
my ($symbol_list) = @_; |
|
525
|
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
my $lo_tdate = App::Chart::Download::start_tdate_for_update (@$symbol_list); |
|
527
|
|
|
|
|
|
|
my $hi_tdate = threeday_available_tdate(); |
|
528
|
|
|
|
|
|
|
### RBA wanting ... |
|
529
|
|
|
|
|
|
|
### $lo_tdate |
|
530
|
|
|
|
|
|
|
### $hi_tdate |
|
531
|
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
# desired range is <= 3 days, so try the threeday page |
|
533
|
|
|
|
|
|
|
if ($hi_tdate - $lo_tdate + 1 <= RBA_EXCHANGE_URL_DAYS) { |
|
534
|
|
|
|
|
|
|
App::Chart::Download::status (__('RBA past three days')); |
|
535
|
|
|
|
|
|
|
my $resp = App::Chart::Download->get (RBA_EXCHANGE_URL); |
|
536
|
|
|
|
|
|
|
my $h = threeday_parse ($resp); |
|
537
|
|
|
|
|
|
|
App::Chart::Download::write_latest_group($h); |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
# if $lo_tdate is within the threeday data then write that and done |
|
540
|
|
|
|
|
|
|
my $threeday_lo_tdate |
|
541
|
|
|
|
|
|
|
= App::Chart::Download::iso_to_tdate_ceil ($h->{'lo_date'}); |
|
542
|
|
|
|
|
|
|
if ($threeday_lo_tdate <= $lo_tdate) { |
|
543
|
|
|
|
|
|
|
App::Chart::Download::write_daily_group ($h); |
|
544
|
|
|
|
|
|
|
return; |
|
545
|
|
|
|
|
|
|
} |
|
546
|
|
|
|
|
|
|
} |
|
547
|
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
# desired range > 3 days, or the threeday found was in fact not enough, |
|
549
|
|
|
|
|
|
|
# get the csv |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
my $url = RBA_CURRENT_CSV_URL; |
|
552
|
|
|
|
|
|
|
require File::Basename; |
|
553
|
|
|
|
|
|
|
my $filename = File::Basename::basename($url); |
|
554
|
|
|
|
|
|
|
App::Chart::Download::status (__x('RBA data {filename}', |
|
555
|
|
|
|
|
|
|
filename => $filename)); |
|
556
|
|
|
|
|
|
|
my $resp = App::Chart::Download->get($url); |
|
557
|
|
|
|
|
|
|
my $h = csv_parse($resp); |
|
558
|
|
|
|
|
|
|
App::Chart::Download::write_daily_group ($h); |
|
559
|
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
# This code looked at the historical downloads page and chose among the |
|
561
|
|
|
|
|
|
|
# various XLS files. |
|
562
|
|
|
|
|
|
|
# |
|
563
|
|
|
|
|
|
|
# my $info = historical_info(); |
|
564
|
|
|
|
|
|
|
# my $files = $info->{'files'}; |
|
565
|
|
|
|
|
|
|
# $files = App::Chart::Download::choose_files ($files, $lo_tdate, $hi_tdate); |
|
566
|
|
|
|
|
|
|
# $files = [ sort {$a->{'lo_tdate'} <=> $b->{'lo_tdate'}} @$files ]; |
|
567
|
|
|
|
|
|
|
# foreach my $f (@$files) { |
|
568
|
|
|
|
|
|
|
# my $url = $f->{'url'}; |
|
569
|
|
|
|
|
|
|
# require File::Basename; |
|
570
|
|
|
|
|
|
|
# my $filename = File::Basename::basename($url); |
|
571
|
|
|
|
|
|
|
# App::Chart::Download::status (__x('RBA data {filename}', |
|
572
|
|
|
|
|
|
|
# filename => $filename)); |
|
573
|
|
|
|
|
|
|
# my $resp = App::Chart::Download->get ($url); |
|
574
|
|
|
|
|
|
|
# my $h = xls_parse ($resp); |
|
575
|
|
|
|
|
|
|
# App::Chart::Download::write_daily_group ($h); |
|
576
|
|
|
|
|
|
|
# } |
|
577
|
|
|
|
|
|
|
} |
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
sub backto { |
|
580
|
|
|
|
|
|
|
my ($symbol_list, $backto_tdate) = @_; |
|
581
|
|
|
|
|
|
|
die "Not implemented"; |
|
582
|
|
|
|
|
|
|
} |
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
1; |
|
585
|
|
|
|
|
|
|
__END__ |