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
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/>.
20 # Retrieve Quote and Security from Finanzen.net
21 class Smr::Reapers::Finanzennet
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
29 @security.save! unless @security.id # it must be a database record
33 # List of Security#types this reaper can retrieve data for.
34 def Finanzennet.security_types
35 [ :unknown, :stock, :bond, :fund ]
39 # tell whether :type can be handled by this reaper.
40 def Finanzennet.type_supported?(type)
41 Finanzennet.security_types.include? type.to_sym
45 # Retrieve metadata for Securtity#symbol and creates correct type model.
47 # Returns true on success or false on failure.
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')
51 src = open(QUERY_URL % @security.symbol)
52 rescue OpenURI::HTTPError
56 page = Nokogiri::HTML(src) do |config|
57 # config.strict.noblanks
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?
71 elsif not (keywords & ['Aktienfonds', 'Immobilienfonds', 'Rentenfonds', 'Geldmarktfonds', 'Dachfonds', 'Fonds', 'ETFs']).empty?
73 elsif not (keywords & ['Aktien', 'Realtimekurse', 'Aktienkurse']).empty?
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'
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?('-')
95 when :bond then handle_bond(clean_header, clean_data, typemodel)
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)
108 if @security.changed? or typemodel.changed?
109 (@security.save and typemodel.save)
115 def handle_bond(header, data, typemodel)
116 #p '==handle bond=> |%s| = |%s|' % [header, data]
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
134 def handle_stock(header, data, typemodel)
135 #p '==handle stock==> |%s| = |%s|' % [header, data]
137 when 'ISIN' then @security.symbol = data
138 when 'Letzte Dividende' then typemodel.dividend = data.gsub(',', '.')
142 def handle_fund(header, data, typemodel)
143 #p '==handle fund==> |%s| = |%s|' % [header, data]
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
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')
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'