| 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 |  | 369 | use 5.006; | 
|  | 1 |  |  |  |  | 2 |  | 
| 37 | 1 |  |  | 1 |  | 4 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 16 |  | 
| 38 | 1 |  |  | 1 |  | 4 | use warnings; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 20 |  | 
| 39 | 1 |  |  | 1 |  | 241 | use URI::Escape; | 
|  | 1 |  |  |  |  | 1101 |  | 
|  | 1 |  |  |  |  | 63 |  | 
| 40 | 1 |  |  | 1 |  | 311 | use Locale::TextDomain 'App-Chart'; | 
|  | 1 |  |  |  |  | 16396 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 41 |  |  |  |  |  |  |  | 
| 42 | 1 |  |  | 1 |  | 5292 | 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 => \÷nds_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 €" 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__ |