How to Build a Stocks Market Web App with Python
The creation of a web application written in Python using Flask.
This guide will delve into developing a web application in Python, utilizing Flask. The application will harness the EODHD API’s Python library for data provision, enabling users to explore API data via their browser, rendered attractively in Bootstrap tables. We’ll discuss setting up the Python Flask development project, fetching exchange lists, diving into exchange details to access markets, and ultimately, obtaining hourly market data. Navigation ease will be ensured through breadcrumbs, and data presentation will leverage Bootstrap’s data table, featuring built-in capabilities for search, sort, filter, and more.
Prerequisites
- Obtain an API Token from EODHD APIs.
- Ensure installation of the latest Python version, with guidance available on Real Python.
- Opt for an IDE such as Visual Studio Code.
- Establish a project directory for your work.
Directory and File Structure
Your initial directory and file setup will look like this:
.
|____requirements.txt
|____app.py
|____config.py
|____api
| |______init__.py
| |____financial_data.py
|____templates
| |____exchanges.html
We’ll add more to this, but it’s our start. Explaining step by step makes it easier to understand.
File Contents
requirements.txt
flask
eodhd
This file lists the libraries we need, mainly “flask” and “eodhd”. To install these libraries, we do the following:
% python3 -m pip install -r requirements.txt -U
config.py
API_TOKEN = "YOUR_TOKEN"
This file should just include your API TOKEN from EODHD APIs.
app.py
from flask import Flask, render_template
from api.financial_data import EODHDAPIsDataFetcher
from config import API_TOKEN
app = Flask(__name__)
data_fetcher = EODHDAPIsDataFetcher(API_TOKEN)
@app.route("/")
def exchanges():
exchanges = data_fetcher.fetch_exchanges()
return render_template("exchanges.html", exchanges=exchanges)
if __name__ == "__main__":
app.run()
This acts as our application’s entry point, along with some initial code. We import the API_TOKEN from config.py and pass it to the class named “EODHDAPIsDataFetcher“.
The default route “/”, for example, “http://127.0.0.1:5000/”, will request a list of exchanges from “data_fetcher“. The result from this function will be delivered as a JSON response.
The JSON response will be forwarded to an HTML template named “exchanges.html”, with the exchanges data conveyed to the template as “exchanges”.
We’ll run it in the normal way:
% python3 app.py
This will be expanded to support configurable arguments as described:
% python3 app.py --host 127.0.0.1 --port 5000 --debug
Once the web app is operational, it can be accessed at: http://127.0.0.1:5000
templates/exchanges.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Exchanges</title>
</head>
<body>
{% for exchange in exchanges %}
{{ exchange.Code }} <br />
{% endfor %}
</body>
</html>
This isn’t the complete file but a simplified version for demonstration. Its primary function is to illustrate how to import the “exchanges” variable array and iterate through it. Each iteration returns a row as “exchange”, with “Code” being a row attribute. Variables are displayed within double curly brackets. This structure can be applied across all your API endpoints.
Later, we’ll integrate a Bootstrap data table, significantly enhancing the visual appeal of the output.
api/financial_data.py
import json
from eodhd import APIClient
class EODHDAPIsDataFetcher:
def __init__(self, api_token):
self._api_token = api_token
def fetch_exchanges(self):
try:
api = APIClient(self._api_token)
df = api.get_exchanges()
return json.loads(df.to_json(orient="records"))
except Exception as e:
print(f"Error fetching data: {e}")
return None
When a user navigates to “http://127.0.0.1:5000" in their browser, it will direct them to the “/” route as outlined in “app.py”. This route triggers the “fetch_exchanges()” function, initiating an API request to the EODHD API’s Python library to fetch the exchange list. The response from the library is received as a Pandas dataframe, which we’ll convert to JSON for rendering in the “exchanges.html” template.
Extending templates/exchanges.html
In the simple code snippet we shared before, we showed how to display the returned data in an HTML format. Although it’s functional, it’s not particularly engaging without some styling. Bootstrap, a really good JavaScript library, dramatically improves how tabulated data looks. It also provides built-in functionality for page size adjustments, search, sorting, pagination, and more, right out of the box.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Exchanges</title>
https://code.jquery.com/jquery-3.6.0.min.js
https://cdn.datatables.net/1.11.5/js/jquery.dataTables.min.js
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.0/css/jquery.dataTables.min.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js
<script>
$(document).ready(function() {
$('#exchangesTable').DataTable();
});
</script>
</head>
<body>
<div class="container">
<h1>Exchanges</h1>
<table id="exchangesTable" class="table table-sm table-light table-bordered table-hover table-striped">
<thead>
<tr>
<th>Name</th>
<th>Code</th>
<th>OperatingMIC</th>
<th>Currency</th>
<th>CountryISO2</th>
<th>CountryISO3</th>
</tr>
</thead>
<tbody>
{% for exchange in exchanges %}
<tr>
<td><a href="/exchange/{{ exchange.Code }}">{{ exchange.Name }}</a></td>
<td>{{ exchange.Code }}</td>
<td>{{ exchange.OperatingMIC }}</td>
<td>{{ exchange.Currency }}</td>
<td>{{ exchange.CountryISO2 }}</td>
<td>{{ exchange.CountryISO3 }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
</html>
In the example provided, you’ll notice we imported the JQuery and Bootstrap libraries along with their stylesheets.
All these features, from page size adjustments to search, sorting, and pagination, are available “out of the box” with this setup.
Building on from here.
Expanding on this, you’ll see that in “app.py”, the Flask application initiates with “app.run()”. While functional, the run() method can accept additional parameters, such as the IP address to listen on, the port number, and whether to operate in debug mode. Let’s add some command-line arguments to enhance this feature.
import re
import sys
import argparse
from flask import Flask, render_template
from api.financial_data import EODHDAPIsDataFetcher
from config import API_TOKEN
parser = argparse.ArgumentParser(description="EODHD APIs")
parser.add_argument(
"--host",
type=str,
help="Web service IP (default: 127.0.0.1)",
)
parser.add_argument(
"--port",
type=int,
help="Web service port (default: 5000)",
)
parser.add_argument("--debug", action="store_true", help="Enable debugging")
args = parser.parse_args()
# Listen on local host
http_host = "0.0.0.0"
if args.host is not None:
p = re.compile(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
if p.match(args.host):
http_host = args.host
else:
parser.print_help(sys.stderr)
# Listen on local port
http_port = 5000
if args.port is not None:
if args.port >= 0 and args.port <= 65535:
http_port = args.port
else:
parser.print_help(sys.stderr)
app = Flask(__name__)
data_fetcher = EODHDAPIsDataFetcher(API_TOKEN)
@app.route("/")
def exchanges():
exchanges = data_fetcher.fetch_exchanges()
return render_template("exchanges.html", exchanges=exchanges)
if __name__ == "__main__":
app.run(host=http_host, port=http_port, debug=args.debug)
This enhancement enables us to launch the application like this:
% python3 app.py --host 127.0.0.1 --port 5000 --debug
Moreover, as seen in the Exchanges screenshot, each exchange is linked via a hyperlink. For instance, the first entry directs to “http://127.0.0.1:5000/exchange/AT".
To introduce a new route like “/exchange/<code>” within Flask, we begin by adding it to “app.py”.
@app.route("/exchange/<code>")
def exchange_markets(code):
markets = data_fetcher.fetch_exchange_markets(code)
return render_template("markets.html", code=code, markets=markets)
Some crucial details to note:
- The route includes a configurable parameter named “<code>”. In the URL mentioned, “AT” serves as the example for “<code>”.
- This “code” parameter is conveyed to the “exchange_markets(code)” function, which then forwards it to “fetch_exchange_markets(code)”.
- Additionally, we’re passing the variables “code” and “markets” to the “markets.html” template.
A corresponding fetch function for this operation needs to be integrated into the “api/financial_data.py” file.
def fetch_exchange_markets(self, code: str = ""):
try:
api = APIClient(self._api_token)
df = api.get_exchange_symbols(code)
return json.loads(df.to_json(orient="records"))
except Exception as e:
print(f"Error fetching data: {e}")
return None
This function mirrors the earlier one but requires “code” as an argument.
Finally, the “templates/markets.html” will look as follows.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>markets</title>
https://code.jquery.com/jquery-3.6.0.min.js
https://cdn.datatables.net/1.11.5/js/jquery.dataTables.min.js
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.0/css/jquery.dataTables.min.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js
<script>
$(document).ready(function() {
$('#marketsTable').DataTable();
});
</script>
</head>
<body>
<div class="container">
<a href="/">Exchanges</a> > <b>{{ code }}</b>
<p />
<h1>{{ code }}</h1>
<table id="marketsTable" class="table table-sm table-light table-bordered table-hover table-striped">
<thead>
<tr>
<th>Code</th>
<th>Name</th>
<th>Country</th>
<th>Exchange</th>
<th>Currency</th>
<th>Type</th>
<th>Isin</th>
</tr>
</thead>
<tbody>
{% for item in markets %}
<tr>
<td><a href="/exchange/CC/{{ item.Code }}/1h">{{ item.Code }}</a></td>
<td>{{ item.Name }}</td>
<td>{{ item.Country }}</td>
<td>{{ item.Exchange }}</td>
<td>{{ item.Currency }}</td>
<td>{{ item.Type }}</td>
<td>{{ item.Isin }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
</html>
It will be structured almost similarly to the “exchanges.html” file
As illustrated, we’ve now delved into the “Athens Exchange (AT)” to observe its markets. At the top, you’ll find breadcrumbs, facilitating an effortless return to the exchange list. Moreover, it’s possible to navigate deeper into the “Austriacard Holdings AG (ACAG)” market to examine the hourly candlestick data.
Conclusion
And there we have it, a working web application crafted in Python using Flask, enabling the browsing of EODHD API data via a browser. While this provides a solid foundation, it’s merely the start of the journey. At present, our application operates in a development setting. For actual deployment, it should transition to a production-grade WSGI server, such as mod_wsgi on Apache 2.4.
Currently, the application showcases hourly candlestick data, but this could be expanded to include additional intervals supported by the API. Consider adding buttons on the market list page for specific interval drilling.
Furthermore, the application displays raw API data, which could be enriched by incorporating technical indicators, including trend, momentum, volume, volatility indicators, and more.
To further enhance the web application’s visual appeal, integrating graphs using a library like D3.js could be beneficial.
For the complete solution, check out this Github repository.
For those eager to delve deeper into such insightful articles and broaden their understanding of different strategies in financial markets, we invite you to follow our account and subscribe for email notifications.
We publish 2 pieces per week!
Stay tuned for more valuable articles that aim to enhance your data science skills and market analysis capabilities.