Skip to content

Instantly share code, notes, and snippets.

@njh
Created June 4, 2024 12:09
Show Gist options
  • Save njh/d564e00374aff08122cb541b65505ed5 to your computer and use it in GitHub Desktop.
Save njh/d564e00374aff08122cb541b65505ed5 to your computer and use it in GitHub Desktop.
Script to parse the HSBC mortgage rate page
#!/usr/bin/env ruby
#
# Script to parse the HSBC mortgage rate page
# to see if they have chanegd the 2 Year Term Tracker Fee Saver
#
# Subtracts the Bank of England rate from the HSBC advertised rates
#
require 'net/http'
require 'nokogiri'
require 'bigdecimal'
require 'uri'
EXPECTED_RATE = BigDecimal('0.84')
boe_rate_uri = URI('https://www.bankofengland.co.uk/boeapps/database/Bank-Rate.asp')
hsbc_rates_uri = URI('https://www.hsbc.co.uk/mortgages/existing-customers/switch/rates/')
boe_html = Net::HTTP.get(boe_rate_uri)
boe_doc = Nokogiri::HTML.parse(boe_html)
boe_base_rate = boe_doc.at('.featured-stat/.stat-figure').inner_text
if boe_base_rate =~ /(\d+\.?\d*)\s*%?/
boe_base_rate = BigDecimal($1)
else
raise "Failed to get bank of England base rate: #{boe_base_rate}"
end
hsbc_html = Net::HTTP.get(hsbc_rates_uri)
hsbc_doc = Nokogiri::HTML.parse(hsbc_html)
rates = {}
hsbc_doc.search('table.desktop').each do |table|
caption = table.at('caption')
next if caption.nil?
if caption.inner_text =~ /60% Maximum Loan to Value/
table.search('tr').each do |row|
cells = row.search('td,th')
next if cells.empty?
name,rate = cells.map {|e| e.inner_text.strip}
name.gsub!(/[^\w\s]+/, '')
next if name == 'Mortgage'
if rate =~ /(\d+\.?\d*)\s*%?/
rates[name] = BigDecimal($1)
end
end
end
end
tracker_count = rates.keys.select {|k| k =~ /Tracker/i}.count
if tracker_count != 4
puts "Number of HSBC tracker rates isn't 4:"
rates.each_pair do |name, value|
if name =~ /tracker/i
puts ' ' + name + ': ' + value.to_s('F') + '%'
end
end
end
tracker_fee_saver = rates['2 Year Term Tracker Fee Saver']
rate_above = tracker_fee_saver - boe_base_rate
if rate_above != EXPECTED_RATE
puts "HSBC Tracker rate has changed to: #{rate_above.to_s('F')} (used to be #{EXPECTED_RATE.to_s('F')})"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment