alexprengere / currencyconverter

A Python currency converter using the European Central Bank data.
http://alexprengere.github.io/currencyconverter
Apache License 2.0
215 stars 59 forks source link

Add support for real-time conversion based on XE Currency Converter #12

Closed zianke closed 6 years ago

zianke commented 6 years ago

I've added a function of real-time currency conversion based on XE Currency Converter. Following is the usage with output generated at UTC 2018-07-10 16:10:00.784721. Data is fetched from https://www.xe.com/currencyconverter/convert/?Amount=100&From=USD&To=EUR, which is updated every minute. Hope you like this function!

>>> from currency_converter import CurrencyConverter
>>> c = CurrencyConverter()
>>> c.convert(100, 'USD', date='now')
85.2593
alexprengere commented 6 years ago

Hello, First of all, thank you very much for your contribution, it is appreciated. I like your fonction and I think it is useful, but it seems this could live in a separate code base. For example, the new convert method does not rely on self and could be a staticmethod, or just written as a standalone function like this.

import re
from urllib import urlopen

XE_URL = 'https://www.xe.com/currencyconverter/convert/?Amount={0}&From={1}&To={2}'

def convert(amount, currency, new_currency):
    xe_url = XE_URL.format(amount, currency, new_currency)
    xe_html = urlopen(xe_url).read().decode('utf-8')

    found_currencies = re.findall(
        r'XE Currency Converter: ([A-Z]{3}) to ([A-Z]{3})', xe_html)
    result_amount = re.findall(
        r"<span class='uccResultAmount'>([0-9]*\.?[0-9]+)</span>", xe_html)

    if not found_currencies or not result_amount:
        raise ValueError('XE Currency Converter HTML cannot be parsed')

    if (currency, new_currency) != found_currencies[0]:
        raise ValueError(
            '{0} to {1} is not supported by XE Currency Converter'.format(
                currency, new_currency))

    return float(result_amount[0])

The idea behind the current implementation (this might not be well documented) is to load the rates once when creating the CurrencyConverter object (about 400ms), then get near-instantaneous results when performing conversions (about 7µs). By comparison, getting and parsing the HTML takes about 700ms per conversion. This might not be a problem if you do not plan to perform thousands of conversions (by that time you might have other problems, like getting blacklisted or rate limited from the website your are scraping).

Another point: the format the currency converter uses is the ECB format, but you can use your own data as long as the format is the same. So you can generate once a file.csv containing the rates with the real time convert function, then use c = CurrencyConverter('file.csv'). This way you can use near real time rates, but benefit from the current code handling the missing data etc.

In conclusion, I am not going to merge this as is, but perhaps this could be part of the documentation, or a separate function, as a way to get real time rates.

zianke commented 6 years ago

It makes sense. Thank you for your reply.