File Coverage

blib/lib/App/Chart/Suffix/ATH.pm
Criterion Covered Total %
statement 15 17 88.2
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 21 23 91.3


line stmt bran cond sub pod time code
1             # Athens Stock Exchange setups. -*- coding: iso-8859-7 -*-
2              
3             # Copyright 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2015 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              
20             # http://www.helex.gr/web/guest/securities-market-products
21             # latest price of everything
22             # http://www.helex.gr/web/guest/stock-snapshot/-/select-stock/43
23             # snapshot, about 280k
24             # http://www.helex.gr/web/guest/stock-historic/-/select-stock/43
25             # last 30 days
26              
27              
28             # cf http://www.ase.gr/content/en/Companies/ListedCo/Profiles/pr_Snapshot.asp?Cid=99&coname=HELLENIC+TELECOM.+ORG.
29             #
30             # http://www.ase.gr/content/en/Companies/ListedCo/Profiles/pr_Snapshot.asp?share=HTO
31             # http://www.ase.gr/content/en/marketdata/stocks/prices/Share_SearchResults.asp?share=HTO
32              
33              
34              
35             package App::Chart::Suffix::ATH;
36 1     1   482 use 5.006;
  1         4  
37 1     1   5 use strict;
  1         1  
  1         18  
38 1     1   3 use warnings;
  1         2  
  1         24  
39 1     1   251 use URI::Escape;
  1         1095  
  1         50  
40 1     1   312 use Locale::TextDomain 'App-Chart';
  1         13308  
  1         6  
41              
42 1     1   5734 use App::Chart;
  0            
  0            
43             use App::Chart::Download;
44             use App::Chart::DownloadHandler;
45             use App::Chart::DownloadHandler::DividendsPage;
46             use App::Chart::Sympred;
47             use App::Chart::TZ;
48             use App::Chart::Weblink;
49              
50              
51             my $timezone_athens = App::Chart::TZ->new
52             (name => __('Athens'),
53             choose => [ 'Europe/Athens' ],
54             fallback => 'EET-2');
55             my $pred = App::Chart::Sympred::Suffix->new ('.ATH');
56             $timezone_athens->setup_for_symbol ($pred);
57              
58             # (source-help! athens-symbol?
59             # (__p('manual-node','Athens Stock Exchange'))
60              
61              
62             #------------------------------------------------------------------------------
63             # weblink - company info
64             #
65             # The greek pages "/gr/" need greek symbols, the english doesn't work, hence
66             # only an english link here, for now.
67              
68             App::Chart::Weblink->new
69             (pred => $pred,
70             name => __('ATHEX _Company Information'),
71             desc => __('Open web browser at the Athens Stock Exchange page for this company'),
72             proc => sub {
73             my ($symbol) = @_;
74             return 'http://www.ase.gr/content/en/Companies/ListedCo/Profiles/Profile.asp?name='
75             . URI::Escape::uri_escape (App::Chart::symbol_sans_suffix ($symbol));
76             });
77              
78              
79             #------------------------------------------------------------------------------
80             # 8859-7 transliteration
81             #
82             # The 8859-7 bytes here in the source are for ease of seeing what they're
83             # supposed to be, but they're only in the comments, the code is all-ascii.
84             #
85             # $translit is a Regexp::Tr mapping Perl wide-chars which are certain greek
86             # characters (from iso-8859-7) to some latin equivalents.
87             #
88             # This is for some greek characters found in otherwise English names, like
89             # ÂÁÍÊ (0xC2,0xC1,0xCD,0xCA) for BANK in ALPHA.ATH. That comes out looking
90             # ok in Gtk or anywhere with good fonts, but for a tty a change to the
91             # actual intended latin characters is needed to make it printable.
92              
93             our $translit; # global for testing
94             {
95             my %table
96             = (
97             # # A0   NO-BREAK SPACE
98             # # A1 ¡ MODIFIER LETTER REVERSED COMMA
99             # # A2 ¢ MODIFIER LETTER APOSTROPHE
100             # # A3 £ POUND SIGN
101             # # A4
102             # # A5
103             # # A6 ¦ BROKEN BAR
104             # # A7 § SECTION SIGN
105             # # A8 ¨ DIAERESIS
106             # # A9
107             # # AA
108             # # AB « LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
109             # # AC ¬ NOT SIGN
110             # # AD ­ SOFT HYPHEN
111             # # AE
112             # # AF ¯ HORIZONTAL BAR
113             # # B0 ° DEGREE SIGN
114             # # B1 ± PLUS-MINUS SIGN
115             # # B2 ² SUPERSCRIPT TWO
116             # # B3 ³ SUPERSCRIPT THREE
117             # # B4 ´ GREEK TONOS
118             # # B5 µ GREEK DIALYTIKA TONOS
119             0xB6 => 'A', # B6 ¶ GREEK CAPITAL LETTER ALPHA WITH TONOS
120             # # B7 · MIDDLE DOT
121             0xB8 => 'E', # B8 ¸ GREEK CAPITAL LETTER EPSILON WITH TONOS
122             0xB9 => 'H', # B9 ¹ GREEK CAPITAL LETTER ETA WITH TONOS
123             0xBA => 'I', # BA º GREEK CAPITAL LETTER IOTA WITH TONOS
124             # # BB » RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
125             # # BC ¼ GREEK CAPITAL LETTER OMICRON WITH TONOS
126             # # BD ½ VULGAR FRACTION ONE HALF
127             # # BE ¾ GREEK CAPITAL LETTER UPSILON WITH TONOS
128             0xBF => 'O', # BF ¿ GREEK CAPITAL LETTER OMEGA WITH TONOS
129             # # C0 À GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
130             0xC1 => 'A', # C1 Á GREEK CAPITAL LETTER ALPHA
131             0xC2 => 'B', # C2 Â GREEK CAPITAL LETTER BETA
132             0xC3 => 'G', # C3 Ã GREEK CAPITAL LETTER GAMMA
133             0xC4 => 'D', # C4 Ä GREEK CAPITAL LETTER DELTA
134             0xC5 => 'E', # C5 Å GREEK CAPITAL LETTER EPSILON
135             0xC6 => 'Z', # C6 Æ GREEK CAPITAL LETTER ZETA
136             0xC7 => 'H', # C7 Ç GREEK CAPITAL LETTER ETA
137             # # C8 È GREEK CAPITAL LETTER THETA
138             0xC9 => 'I', # C9 É GREEK CAPITAL LETTER IOTA
139             0xCA => 'K', # CA Ê GREEK CAPITAL LETTER KAPPA
140             0xCB => 'L', # CB Ë GREEK CAPITAL LETTER LAMDA
141             0xCC => 'M', # CC Ì GREEK CAPITAL LETTER MU
142             0xCD => 'N', # CD Í GREEK CAPITAL LETTER NU
143             0xCE => 'X', # CE Î GREEK CAPITAL LETTER XI
144             # # CF Ï GREEK CAPITAL LETTER OMICRON
145             0xD0 => 'P', # D0 Ð GREEK CAPITAL LETTER PI
146             0xD1 => 'R', # D1 Ñ GREEK CAPITAL LETTER RHO
147             # # D2
148             0xD3 => 'S', # D3 Ó GREEK CAPITAL LETTER SIGMA
149             0xD4 => 'T', # D4 Ô GREEK CAPITAL LETTER TAU
150             # # D5 Õ GREEK CAPITAL LETTER UPSILON
151             # # D6 Ö GREEK CAPITAL LETTER PHI
152             # # D7 × GREEK CAPITAL LETTER CHI
153             # # D8 Ø GREEK CAPITAL LETTER PSI
154             0xD9 => 'O', # D9 Ù GREEK CAPITAL LETTER OMEGA
155             # # DA Ú GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
156             # # DB Û GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
157             0xDC => 'a', # DC Ü GREEK SMALL LETTER ALPHA WITH TONOS
158             0xDD => 'e', # DD Ý GREEK SMALL LETTER EPSILON WITH TONOS
159             # # DE Þ GREEK SMALL LETTER ETA WITH TONOS
160             0xDF => 'i', # DF ß GREEK SMALL LETTER IOTA WITH TONOS
161             # # E0 à GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
162             0xE1 => 'a', # E1 á GREEK SMALL LETTER ALPHA
163             0xE2 => 'b', # E2 â GREEK SMALL LETTER BETA
164             0xE3 => 'g', # E3 ã GREEK SMALL LETTER GAMMA
165             0xE4 => 'd', # E4 ä GREEK SMALL LETTER DELTA
166             0xE5 => 'e', # E5 å GREEK SMALL LETTER EPSILON
167             0xE6 => 'z', # E6 æ GREEK SMALL LETTER ZETA
168             # # E7 ç GREEK SMALL LETTER ETA
169             # # E8 è GREEK SMALL LETTER THETA
170             0xE9 => 'i', # E9 é GREEK SMALL LETTER IOTA
171             0xEA => 'k', # EA ê GREEK SMALL LETTER KAPPA
172             0xEB => 'l', # EB ë GREEK SMALL LETTER LAMDA
173             0xEC => 'm', # EC ì GREEK SMALL LETTER MU
174             0xED => 'n', # ED í GREEK SMALL LETTER NU
175             # # EE î GREEK SMALL LETTER XI
176             # # EF ï GREEK SMALL LETTER OMICRON
177             0xF0 => 'p', # F0 ð GREEK SMALL LETTER PI
178             0xF1 => 'r', # F1 ñ GREEK SMALL LETTER RHO
179             0xF2 => 's', # F2 ò GREEK SMALL LETTER FINAL SIGMA
180             0xF3 => 's', # F3 ó GREEK SMALL LETTER SIGMA
181             0xF4 => 't', # F4 ô GREEK SMALL LETTER TAU
182             # # F5 õ GREEK SMALL LETTER UPSILON
183             # # F6 ö GREEK SMALL LETTER PHI
184             # # F7 ÷ GREEK SMALL LETTER CHI
185             # # F8 ø GREEK SMALL LETTER PSI
186             0xF9 => 'o', # F9 ù GREEK SMALL LETTER OMEGA
187             0xFA => 'i', # FA ú GREEK SMALL LETTER IOTA WITH DIALYTIKA
188             # # FB û GREEK SMALL LETTER UPSILON WITH DIALYTIKA
189             # # FC ü GREEK SMALL LETTER OMICRON WITH TONOS
190             # # FD ý GREEK SMALL LETTER UPSILON WITH TONOS
191             0xFE => 'o', # FE þ GREEK SMALL LETTER OMEGA WITH TONOS
192             # # FF
193             );
194              
195             require Encode;
196             my $tr_from = join ('',
197             map { Encode::decode ('iso-8859-7', chr($_)) }
198             keys %table);
199             my $tr_to = join ('', values %table);
200              
201             $tr_to =~ s/-/\\-/g; # escape "tr" dash as range
202             $tr_from =~ s/-/\\-/g;
203              
204             require Regexp::Tr;
205             $translit = Regexp::Tr->new ($tr_from, $tr_to);
206             Regexp::Tr->flush;
207             ### $translit
208             }
209              
210             #-----------------------------------------------------------------------------
211             # download - last 30 days by symbol
212             #
213             # This uses the prices pages like
214             #
215             # http://www.ase.gr/content/en/marketdata/stocks/prices/Share_SearchResults.asp?share=HTO
216             #
217             # Various places link to those price pages using a "SID" id number, but the
218             # symbol works too.
219             #
220             # There's no ETag or Last-Modified to save re-downloading if our idea of
221             # what should be available is a bit out.
222              
223             App::Chart::DownloadHandler->new
224             (name => __ 'ATHEX',
225             pred => $pred,
226             proc => \&last30_download,
227             max_symbols => 1,
228             available_date_time => \&last30_available_date_time,
229             );
230              
231             # Dunno when to expect new data. Try after 6pm Athens time.
232             sub last30_available_date_time {
233             return (App::Chart::Download::weekday_date_after_time
234             (18,0, $timezone_athens),
235             '18:00:00');
236             }
237              
238             sub last30_download {
239             my ($symbol_list) = @_;
240              
241             foreach my $symbol (@$symbol_list) {
242             App::Chart::Download::status (__x ('ATHEX 30 days data {symbol}',
243             symbol => $symbol));
244             my $url = 'http://www.ase.gr/content/en/marketdata/stocks/prices/Share_SearchResults.asp?share='
245             . URI::Escape::uri_escape (App::Chart::symbol_sans_suffix ($symbol));
246             my $resp = App::Chart::Download->get ($url);
247             App::Chart::Download::write_daily_group (last30_parse ($resp));
248             }
249             }
250              
251             sub last30_parse {
252             my ($resp) = @_;
253             my $content = $resp->decoded_content (raise_error => 1);
254              
255             my @data = ();
256             my $h = { source => __PACKAGE__,
257             currency => 'EUR',
258             last_download => 1,
259             cost_key => 'athens-last30',
260             date_format => 'dmy',
261             resp => $resp,
262             data => \@data };
263              
264             # message in page if bad symbol
265             if ($content =~ /Your search didn't return any results/) {
266             return $h;
267             }
268              
269             $content =~ m{Share Closing Prices: ([A-Z]+)[^-]*-[^>]*>([^<]+)</a>}
270             or die "ATHEX last30 name not matched";
271             my $symbol = $1 . '.ATH';
272             my $name = $2;
273              
274             # some names on the english pages have greek 8859-7 capitals, mung those
275             # to plain ascii
276             $h->{'name'} = $translit->trans ($name);
277              
278             require HTML::TableExtract;
279             my $te = HTML::TableExtract->new
280             (headers => ['Date', 'Open', 'Max', 'Min', 'Price', 'Volume' ]);
281             $te->parse($content);
282             if (! $te->tables) {
283             die "ATHEX last30 table not matched";
284             }
285              
286             foreach my $row ($te->rows) {
287             my ($date, $open, $high, $low, $close, $volume) = @$row;
288             push @data, { symbol => $symbol,
289             date => $date,
290             open => $open,
291             high => $high,
292             low => $low,
293             close => $close,
294             volume => $volume };
295             }
296             return $h;
297             }
298              
299              
300             #------------------------------------------------------------------------------
301             # dividends
302             #
303             # This uses the dividend page at
304             #
305             use constant DIVIDENDS_URL =>
306             'http://www.ase.gr/content/en/announcements/dailypress/Daily_Dividends.asp';
307             #
308             # As of May 2008 alas there's no ETag or Last-Modified to avoid
309             # re-downloading, so leave at the default DividendsPage recheck frequency.
310             #
311              
312             App::Chart::DownloadHandler::DividendsPage->new
313             (name => __('ATHEX dividends'),
314             pred => $pred,
315             url => DIVIDENDS_URL,
316             parse => \&dividends_parse,
317             key => 'ATH-dividends');
318              
319             sub dividends_parse {
320             my ($resp) = @_;
321             my $body = $resp->decoded_content (raise_error => 1);
322              
323             my @dividends = ();
324             my $h = { source => __PACKAGE__,
325             resp => $resp,
326             date_format => 'dmy',
327             # amounts are like "0.360", trim to 2 decimals
328             prefer_decimals => 2,
329             dividends => \@dividends };
330              
331             # "Price in &euro;" reaches here as wide char \x{20AC}, probably, maybe,
332             # hopefully, but don't bother to try to match that.
333             #
334             require HTML::TableExtract;
335             my $te = HTML::TableExtract->new
336             (headers => [ 'Symbol',
337             'Ex-Dividend Date',
338             'Start Payment Date',
339             'Price in' ]);
340             $te->parse($body);
341             my @tables = $te->tables
342             or die "ATHEX dividend table not matched";
343              
344             foreach my $ts (@tables) {
345             foreach my $row ($ts->rows) {
346             my ($symbol, $ex_date, $pay_date, $amount) = @$row;
347              
348             # skip blank separator rows
349             if (! defined $symbol) { next; }
350              
351             # skip second row of headings under "Pre-Paid Dividends"
352             if ($symbol eq 'Symbol') { next; }
353              
354             push @dividends, { symbol => "$symbol.ATH",
355             ex_date => $ex_date,
356             pay_date => $pay_date,
357             amount => $amount };
358             }
359             }
360             return $h;
361             }
362              
363             1;
364             __END__