manage bookmarks, bugfixes
[smr.git] / gui / lib / smr / reapers / finanzen_net.rb
blob8a869fe16e00c8b2d53be2eca81d3e7241dd06f2
2 # This file is part of SMR.
4 # SMR is free software: you can redistribute it and/or modify it under the
5 # terms of the GNU General Public License as published by the Free Software
6 # Foundation, either version 3 of the License, or (at your option) any later
7 # version.
9 # SMR is distributed in the hope that it will be useful, but WITHOUT ANY
10 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License along with
14 # SMR.  If not, see <http://www.gnu.org/licenses/>.
16 require 'open-uri'
17 require 'nokogiri'
20 # Retrieve Quote and Security from Finanzen.net
21 class Smr::Reapers::Finanzennet
22     include Smr::Reapers
23     QUERY_URL = 'https://www.finanzen.net/suchergebnis.asp?strSuchString=%s'
25     def initialize(security)
26         raise 'Security object required' unless security.is_a? Security
27         raise 'Security.type=%s not supported by this reaper' % security.type unless Finanzennet::type_supported? security.type
28         @security = security
29         @security.save! unless @security.id  # it must be a database record
30     end
32     ##
33     # List of Security#types this reaper can retrieve data for.
34     def Finanzennet.security_types
35         [ :unknown, :stock, :bond, :fund ]
36     end
38     ##
39     # tell whether :type can be handled by this reaper.
40     def Finanzennet.type_supported?(type)
41         Finanzennet.security_types.include? type.to_sym
42     end
44     ##
45     # Retrieve metadata for Securtity#symbol and creates correct type model.
46     #
47     # Returns true on success or false on failure.
48     def metadata
49         #src = open(QUERY_URL % @security.symbol, 'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36')
50         begin
51             src = open(QUERY_URL % @security.symbol)
52         rescue OpenURI::HTTPError
53             return false
54         end
56         page = Nokogiri::HTML(src) do |config|
57 #                config.strict.noblanks
58         end
60         # check result URL: nothing found shows a search page sometimes
61         property = page.at_xpath('//meta[@property="og:url"]')
62         unless property['content'] == 'http://www.finanzen.net/suchergebnis.asp'
63             @security.url = property['content']
64         else return(false) end
66         meta = page.at_xpath('//meta[@name="keywords"]')
67         meta = page.at_xpath('//meta[@name="Keywords"]') if meta.blank?
68         keywords = meta['content'].split(%r{,\s*})
69         if not (keywords & ['Unternehmensanleihen', 'Staatsanleihen', 'Anleihen-Kurse']).empty?
70             type = :bond
71         elsif not (keywords & ['Aktienfonds', 'Immobilienfonds', 'Rentenfonds', 'Geldmarktfonds', 'Dachfonds', 'Fonds', 'ETFs']).empty?
72             type = :fund
73         elsif not (keywords & ['Aktien', 'Realtimekurse', 'Aktienkurse']).empty?
74             type = :stock
75         else
76             return false
77         end
79         # create and fill the :typemodel
80         typemodel = @security.create_type(type)
82         css_selector = case @security.type
83             when :fund  then 'table.table-small tr td:first'
84             when :stock then 'div.box table.table tr td:first'
85             else 'div.box table.table tr td:first'
86         end
88         page.css(css_selector).each do |dataline|
89             clean_header = clean_special_chars dataline.text
90             next unless dataline.next_element
91             clean_data = clean_special_chars dataline.next_element.text
92             next if clean_data.blank? or clean_data.eql?('-')
94             case @security.type
95                 when :bond then handle_bond(clean_header, clean_data, typemodel)
97                 when :stock
98                     property = page.at_xpath('//meta[@property="og:title"]')
99                     @security.brief = property['content'].split('|').first.chop
100                     handle_stock(clean_header, clean_data, typemodel)
102                 when :fund then handle_fund(clean_header, clean_data, typemodel)
104                 else return false
105            end
106         end
108         if @security.changed? or typemodel.changed?
109             (@security.save and typemodel.save)
110         else false end
111     end
113  protected
115     def handle_bond(header, data, typemodel)
116 #p '==handle bond=> |%s| = |%s|' % [header, data]
117         case header
118             when 'Name'       then @security.brief = data
119             when 'ISIN'       then @security.symbol = data
120             when 'Kupon in %' then typemodel.coupon = data.gsub(',', '.')
121             when 'Fälligkeit' then
122                 typemodel.time_maturity = data
123                 typemodel.redemption_installments = 1
124             when 'Erstes Kupondatum'    then typemodel.time_first_coupon = data
125             when 'Letztes Kupondatum'   then typemodel.time_last_coupon = data
126             when 'Zinstermine pro Jahr' then typemodel.coupon_interval = 12 / data.to_i
127             when 'Emissionswährung'     then typemodel.currency = data if data != Smr::DEFAULT_CURRENCY
128             when 'Emissionsvolumen*'    then typemodel.issue_size = data.gsub('.', '').to_i
129             when 'Stückelung'           then typemodel.denomination = data
130             when 'Nachrang' then  if data.downcase == 'ja' then typemodel.is_callable = true end
131         end
132     end
134     def handle_stock(header, data, typemodel)
135 #p '==handle stock==> |%s| = |%s|' % [header, data]
136         case header
137             when 'ISIN'      then @security.symbol = data
138             when 'Letzte Dividende' then typemodel.dividend = data.gsub(',', '.')
139         end
140     end
142     def handle_fund(header, data, typemodel)
143 #p '==handle fund==> |%s| = |%s|' % [header, data]
144         case header
145             when 'Name'         then @security.brief = data.chomp
146             when 'ISIN'         then @security.symbol = data
147             when 'Auflagedatum' then typemodel.time_inception = data
148             when 'Währung'      then typemodel.currency = data if data != Smr::DEFAULT_CURRENCY
149             when 'Geschäftsjahresende' then typemodel.time_fiscalyearend = data
150             when 'Ausgabeaufschlag'
151                 if (v = data.gsub(',', '.')).to_i > 0 then typemodel.issue_surcharge = v end
152             when 'Verwaltungsgebühr'
153                 if (v = data.gsub(',', '.')).to_i > 0 then typemodel.management_fee = v end
154 #                when 'Depotbankgebühr'      then typemodel. ? not implemented
155 #                when 'Pauschalgebühr'      then typemodel. ? not implemented
156             when 'Mindestanlage'
157                 if (v = data.gsub('.', '').gsub(',', '.')).to_i > 0 then typemodel.investment_minimum = v end
158             when 'Ausschüttung'  then typemodel.is_retaining_profits = true if data.downcase.include?('thesaur')
159             when 'Kategorie'
160                 typemodel.type = :equity   if data.include? 'Aktienfonds'
161                 typemodel.type = :bond     if data.include? 'Rentenfonds'
162                 typemodel.type = :balanced if data.include? 'Mischfonds'
163                 typemodel.type = :real_estate   if data.include? 'Immobilienfonds'
164                 typemodel.type = :fund_of_funds if data.include? 'Rentenfonds'
165                 typemodel.type = :commodity     if data.include? 'Rohstoff'
166         end
167     end