How to Make a Currency Converter in Python

Learn how to make a real-time currency converter using different ways and from various sources such as xe, yahoo finance, xrates and Fixer API in Python.
  · 10 min read · Updated may 2022 · Web Scraping · Application Programming Interfaces · Finance

A currency converter is an app or tool that allows you to quickly convert from one currency to another. We can easily find such tools for free on the Internet. This tutorial will make a real-time currency converter using several methods utilizing web scraping techniques and APIs.

This tutorial will cover five different ways to get the most recent foreign exchange rates, some of them parse the rates from public web pages such as X-RATES and Xe, and others use official APIs for more commercial and reliable use, such as Fixer API and ExchangeRate API, feel free to use any one of these.

Feel free to jump into the method you want to use:

To get started, we have to install the required libraries for all the methods below:

$ pip install python-dateutil requests bs4 yahoo_fin

Scraping X-RATES

In this section, we will extract the data from the x-rates.com website. If you go to the target web page, you'll see most of the currencies along with the most recent date and time. Let's scrape the page:

import requests
from bs4 import BeautifulSoup as bs
from dateutil.parser import parse
from pprint import pprint

The following function is responsible for making a request to that page and extracting the data from the tables:

def get_exchange_list_xrates(currency, amount=1):
    # make the request to x-rates.com to get current exchange rates for common currencies
    content = requests.get(f"https://www.x-rates.com/table/?from={currency}&amount={amount}").content
    # initialize beautifulsoup
    soup = bs(content, "html.parser")
    # get the last updated time
    price_datetime = parse(soup.find_all("span", attrs={"class": "ratesTimestamp"})[1].text)
    # get the exchange rates tables
    exchange_tables = soup.find_all("table")
    exchange_rates = {}
    for exchange_table in exchange_tables:
        for tr in exchange_table.find_all("tr"):
            # for each row in the table
            tds = tr.find_all("td")
            if tds:
                currency = tds[0].text
                # get the exchange rate
                exchange_rate = float(tds[1].text)
                exchange_rates[currency] = exchange_rate        
    return price_datetime, exchange_rates

The above function takes the currency and the amount as parameters and returns the exchange rates of most currencies along with the date and time of the last update.

The time of the last update is in a span tag that has the class of ratesTimestamp. Notice we use the parse() function from dateutil.parser module to automatically parse the string into a Python DateTime object.

The exchange rates are located in two tables. We extract them using the find_all() method from the BeautifulSoup object and get the currency name and the exchange rate in each row in the tables, and add them to our exchange_rates dictionary that we will return. Let's use this function:

if __name__ == "__main__":
    import sys
    source_currency = sys.argv[1]
    amount = float(sys.argv[3])
    target_currency = "GBP"
    price_datetime, exchange_rates = get_exchange_list_xrates(source_currency, amount)
    print("Last updated:", price_datetime)
    pprint(exchange_rates)

Excellent, we use the built-in sys module to get the target currency and the amount from the command line. Let's run this:

$ python currency_converter_xrates.py EUR 1000

The above run is trying to convert 1000 Euros to all other currencies. Here is the output:

Last updated: 2022-02-01 12:13:00+00:00
{'Argentine Peso': 118362.205708,     
 'Australian Dollar': 1586.232315,    
 'Bahraini Dinar': 423.780164,        
 'Botswana Pula': 13168.450636,       
 'Brazilian Real': 5954.781483,       
 'British Pound': 834.954104,
 'Bruneian Dollar': 1520.451015,      
 'Bulgarian Lev': 1955.83,
 'Canadian Dollar': 1430.54405,       
 'Chilean Peso': 898463.818465,       
 'Chinese Yuan Renminbi': 7171.445692,
 'Colombian Peso': 4447741.922165,    
 'Croatian Kuna': 7527.744707,        
 'Czech Koruna': 24313.797041,
 'Danish Krone': 7440.613895,
 'Emirati Dirham': 4139.182587,
 'Hong Kong Dollar': 8786.255952,
 'Hungarian Forint': 355958.035747,
 'Icelandic Krona': 143603.932438,
 'Indian Rupee': 84241.767127,
 'Indonesian Rupiah': 16187150.010697,
 'Iranian Rial': 47534006.535121,
 'Israeli Shekel': 3569.191411,
 'Japanese Yen': 129149.364679,
 'Kazakhstani Tenge': 489292.515538,
 'Kuwaiti Dinar': 340.959682,
 'Libyan Dinar': 5196.539901,
 'Malaysian Ringgit': 4717.485104,
 'Mauritian Rupee': 49212.933037,
 'Mexican Peso': 23130.471272,
 'Nepalese Rupee': 134850.008728,
 'New Zealand Dollar': 1703.649473,
 'Norwegian Krone': 9953.078431,
 'Omani Rial': 433.360301,
 'Pakistani Rupee': 198900.635421,
 'Philippine Peso': 57574.278782,
 'Polish Zloty': 4579.273862,
 'Qatari Riyal': 4102.552652,
 'Romanian New Leu': 4946.638369,
 'Russian Ruble': 86197.012666,
 'Saudi Arabian Riyal': 4226.530892,
 'Singapore Dollar': 1520.451015,
 'South African Rand': 17159.831129,
 'South Korean Won': 1355490.097163,
 'Sri Lankan Rupee': 228245.645722,
 'Swedish Krona': 10439.125427,
 'Swiss Franc': 1037.792217,
 'Taiwan New Dollar': 31334.286611,
 'Thai Baht': 37436.518169,
 'Trinidadian Dollar': 7636.35428,
 'Turkish Lira': 15078.75981,
 'US Dollar': 1127.074905,
 'Venezuelan Bolivar': 511082584.868731}

That's about 1127.07 in USD at the time of writing this tutorial. Notice the last updated date and time; it usually updates every minute.

Scraping Xe

Xe is an online foreign exchange tools and services company. It is best known for its online currency converter. In this section, we use requests and BeautifulSoup libraries to make a currency converter based on it.

Open up a new Python file and import the necessary libraries:

import requests
from bs4 import BeautifulSoup as bs
import re
from dateutil.parser import parse

Now let's make a function that accepts the source currency, target currency, and the amount we want to convert, and then returns the converted amount along with the exchange rate date and time:

def convert_currency_xe(src, dst, amount):
    def get_digits(text):
        """Returns the digits and dots only from an input `text` as a float
        Args:
            text (str): Target text to parse
        """
        new_text = ""
        for c in text:
            if c.isdigit() or c == ".":
                new_text += c
        return float(new_text)
    
    url = f"https://www.xe.com/currencyconverter/convert/?Amount={amount}&From={src}&To={dst}"
    content = requests.get(url).content
    soup = bs(content, "html.parser")
    exchange_rate_html = soup.find_all("p")[2]
    # get the last updated datetime
    last_updated_datetime = parse(re.search(r"Last updated (.+)", exchange_rate_html.parent.parent.find_all("div")[-2].text).group()[12:])
    return last_updated_datetime, get_digits(exchange_rate_html.text)

At the time of writing this tutorial, the exchange rate is located in the third paragraph on the HTML page. This explains the soup.find_all("p")[2]. Make sure to change the extraction whenever a change is made to the HTML page. Hopefully, I'll keep an eye out whenever a change is made.

The latest date and time of the exchange rate is located at the second parent of the exchange rate paragraph in the HTML DOM.

Since the exchange rate contains string characters, I made the get_digits() function to extract only the digits and dots from a given string, which is helpful in our case.

Let's use the function now:

if __name__ == "__main__":
    import sys
    source_currency = sys.argv[1]
    destination_currency = sys.argv[2]
    amount = float(sys.argv[3])
    last_updated_datetime, exchange_rate = convert_currency_xe(source_currency, destination_currency, amount)
    print("Last updated datetime:", last_updated_datetime)
    print(f"{amount} {source_currency} = {exchange_rate} {destination_currency}")

This time, we get the source and target currencies as well as the amount from the command-lines, trying to convert 1000 EUR to USD:

$ python currency_converter_xe.py EUR USD 1000

Output:

Last updated datetime: 2022-02-01 13:04:00+00:00
1000.0 EUR = 1125.8987 USD

That's great! Xe usually updates every minute too, so it's real-time!

Scraping Yahoo Finance

Yahoo Finance provides financial news, currency data, stock quotes, press releases, and financial reports. This section uses the yahoo_fin library in Python to make a currency exchanger based on Yahoo Finance data.

Importing the libraries:

import yahoo_fin.stock_info as si
from datetime import datetime, timedelta

yahoo_fin does an excellent job of extracting the data from the Yahoo Finance web page, and it is still maintained now; we use the get_data() method from the stock_info module and pass the currency symbol to it.

Below is the function that uses this function and returns the converted amount from one currency to another:

def convert_currency_yahoofin(src, dst, amount):
    # construct the currency pair symbol
    symbol = f"{src}{dst}=X"
    # extract minute data of the recent 2 days
    latest_data = si.get_data(symbol, interval="1m", start_date=datetime.now() - timedelta(days=2))
    # get the latest datetime
    last_updated_datetime = latest_data.index[-1].to_pydatetime()
    # get the latest price
    latest_price = latest_data.iloc[-1].close
    # return the latest datetime with the converted amount
    return last_updated_datetime, latest_price * amount

We pass "1m" to the interval parameter in the get_data() method to extract minute data instead of daily data (default). We also get minute data of the previous two days, as it may cause issues on the weekends, just to be cautious.

The significant advantage of this method is you can get historical data by simply changing start_date and end_date parameters on this method. You can also change the interval to be "1d" for daily, "1wk" for weekly, and "1mo" for monthly.

Let's use the function now:

if __name__ == "__main__":
    import sys
    source_currency = sys.argv[1]
    destination_currency = sys.argv[2]
    amount = float(sys.argv[3])
    last_updated_datetime, exchange_rate = convert_currency_yahoofin(source_currency, destination_currency, amount)
    print("Last updated datetime:", last_updated_datetime)
    print(f"{amount} {source_currency} = {exchange_rate} {destination_currency}")

Running the code:

$ python currency_converter_yahoofin.py EUR USD 1000

Output:

Last updated datetime: 2022-02-01 13:26:34
1000.0 EUR = 1126.1261701583862 USD

Using ExchangeRate API

As mentioned at the beginning of this tutorial, if you want a more reliable way to make a currency converter, you have to choose an API for that. There are several APIs for this purpose. However, we have picked two APIs that seem convenient and easy to get started.

ExchangeRate API supports 161 currencies and offers a free monthly 1,500 requests if you want to try it out, and there is an open API as well that offers daily updated data, and that's what we are going to use:

import requests
from dateutil.parser import parse 

def get_all_exchange_rates_erapi(src):
    url = f"https://open.er-api.com/v6/latest/{src}"
    # request the open ExchangeRate API and convert to Python dict using .json()
    data = requests.get(url).json()
    if data["result"] == "success":
        # request successful
        # get the last updated datetime
        last_updated_datetime = parse(data["time_last_update_utc"])
        # get the exchange rates
        exchange_rates = data["rates"]
    return last_updated_datetime, exchange_rates

The above function requests the open API and returns the exchange rates for all the currencies with the latest date and time. Let's use this function to make a currency converter function:

def convert_currency_erapi(src, dst, amount):
    # get all the exchange rates
    last_updated_datetime, exchange_rates = get_all_exchange_rates_erapi(src)
    # convert by simply getting the target currency exchange rate and multiply by the amount
    return last_updated_datetime, exchange_rates[dst] * amount

As usual, let's make the main code:

if __name__ == "__main__":
    import sys
    source_currency = sys.argv[1]
    destination_currency = sys.argv[2]
    amount = float(sys.argv[3])
    last_updated_datetime, exchange_rate = convert_currency_erapi(source_currency, destination_currency, amount)
    print("Last updated datetime:", last_updated_datetime)
    print(f"{amount} {source_currency} = {exchange_rate} {destination_currency}")

Running it:

$ python currency_converter_erapi.py EUR USD 1000

Output:

Last updated datetime: 2022-02-01 00:02:31+00:00
1000.0 EUR = 1120.0 USD

The rates update daily, and it does not offer the exact exchange number as it's an open API; you can freely sign up for an API key to get precise exchange rates.

Using Fixer API

One of the promising alternatives is Fixer API. It is a simple and lightweight API for real-time and historical foreign exchange rates. You can easily create an account and get the API key.

After you've done that, you can use the /convert endpoint to convert from one currency to another. However, that's not included in the free plan and requires upgrading your account.

There is the /latest endpoint that does not require an upgrade and works in a free account just fine. It returns the exchange rates for the currency of your region. We can pass the source and target currencies we want to convert and calculate the exchange rate between both. Here's the function:

import requests
from datetime import datetime

API_KEY = "<YOUR_API_KEY_HERE>"

def convert_currency_fixerapi_free(src, dst, amount):
    """converts `amount` from the `src` currency to `dst` using the free account"""
    url = f"http://data.fixer.io/api/latest?access_key={API_KEY}&symbols={src},{dst}&format=1"
    data = requests.get(url).json()
    if data["success"]:
        # request successful
        rates = data["rates"]
        # since we have the rate for our currency to src and dst, we can get exchange rate between both
        # using below calculation
        exchange_rate = 1 / rates[src] * rates[dst]
        last_updated_datetime = datetime.fromtimestamp(data["timestamp"])
        return last_updated_datetime, exchange_rate * amount

Below is the function that uses the /convert endpoint in case you have an upgraded account:

def convert_currency_fixerapi(src, dst, amount):
    """converts `amount` from the `src` currency to `dst`, requires upgraded account"""
    url = f"https://data.fixer.io/api/convert?access_key={API_KEY}&from={src}&to={dst}&amount={amount}"
    data = requests.get(url).json()
    if data["success"]:
        # request successful
        # get the latest datetime
        last_updated_datetime = datetime.fromtimestamp(data["info"]["timestamp"])
        # get the result based on the latest price
        result = data["result"]
        return last_updated_datetime, result

Let's use either function:

if __name__ == "__main__":
    import sys
    source_currency = sys.argv[1]
    destination_currency = sys.argv[2]
    amount = float(sys.argv[3])
    # free account
    last_updated_datetime, exchange_rate = convert_currency_fixerapi_free(source_currency, destination_currency, amount)
    # upgraded account, uncomment if you have one
    # last_updated_datetime, exchange_rate = convert_currency_fixerapi(source_currency, destination_currency, amount)
    print("Last updated datetime:", last_updated_datetime)
    print(f"{amount} {source_currency} = {exchange_rate} {destination_currency}")

Before running the script, make sure to replace API_KEY with the API key you get when registering for an account.

Running the script:

Last updated datetime: 2022-02-01 15:54:04
1000.0 EUR = 1126.494 USD

You can check the documentation of Fixer API here.

Conclusion

There are many ways to make a currency converter, and we have covered five of them. If one method does not work for you, you can choose another one!

You can get the complete code for all the files here.

Learn also: Webhooks in Python with Flask.

Happy coding ♥

View Full Code
Sharing is caring!



Read Also



Comment panel