When it comes to developing modern web applications, choosing the right framework is crucial. For developers building fintech applications, performance, scalability, and reliability are top priorities. Two popular Python frameworks—Flask and FastAPI—are often compared for their capabilities.
Digital
In this blog, we will explore:
• The fundamental differences between Flask and FastAPI.
• Understand synchronous (sync) and asynchronous (async) processing.
• Practical examples showcasing their performance differences with yfinance data fetching.
• Why async programming is essential in fintech applications.
Why Flask and FastAPI Matter in Fintech
Flask
• Flask is a lightweight, synchronous web framework widely adopted for its simplicity and flexibility.
• It is often chosen for rapid development and when compatibility with legacy systems is a priority.
• Synchronous by nature: Flask processes one request at a time per worker. This simplicity makes it great for CPU-bound tasks but limits its scalability for I/O-bound operations.
FastAPI
• FastAPI is a modern, asynchronous web framework built on ASGI and designed for speed.
• It supports both sync and async processing, making it ideal for high-performance, I/O-heavy applications.
• Built-in async support: FastAPI can handle thousands of concurrent requests, making it a natural choice for scalable fintech solutions like real-time trading platforms.
Why Async Programming Is Important in Fintech
Fintech Challenges:
• Real-time Data Fetching: Fetching live financial data, such as stock prices, exchange rates, and historical data, requires high-speed operations.
• Concurrency: Applications often need to serve thousands of users simultaneously without bottlenecks.
• I/O-bound Operations: Communicating with external APIs (e.g., Yahoo Finance, Bloomberg) is I/O-heavy, which can block synchronous frameworks like Flask.
Async Programming Benefits:
• Handles multiple I/O-bound tasks concurrently.
• Reduces response time by leveraging non-blocking operations.
• Improves scalability for high-demand scenarios, such as peak trading hours.
How Sync and Async Work
Synchronous Processing
• In a synchronous framework like Flask, each request is handled sequentially.
• If one request involves waiting (e.g., for an external API to respond), the entire worker is blocked, delaying other requests.
Asynchronous Processing
• In an async framework like FastAPI, requests are handled concurrently using event loops.
• Tasks that involve waiting (e.g., I/O operations) do not block other requests, enabling efficient resource usage and faster response times.
Flask Example: Sync Processing
Here’s how Flask handles synchronous requests to fetch financial data using yfinance:
Code:
from flask import Flask, jsonify
import yfinance as yf
import numpy as np
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# Increase the connection pool size for `requests`
session = requests.Session()
adapter = HTTPAdapter(pool_connections=50, pool_maxsize=50, max_retries=Retry(total=3, backoff_factor=1))
session.mount("http://", adapter)
session.mount("https://", adapter)
# Patch the `requests` session in `yfinance`
yf.utils.requests = session
# Flask app
app = Flask(__name__)
# Hardcoded symbols
SYMBOLS = ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA",
"META", "NFLX", "ADBE", "NVDA", "ORCL",
"IBM", "INTC", "AMD", "CRM", "CSCO",
"DIS", "TMUS", "V", "MA", "PYPL"]
def fetch_symbol_sync(symbol):
"""Fetch data synchronously for a single symbol."""
raw_data = yf.Ticker(symbol).history(period="1d")
data = raw_data.reset_index().to_dict(orient="records")
for record in data:
for key, value in record.items():
if isinstance(value, (np.int64, np.float64)):
record[key] = value.item() # Convert numpy types to native Python types
return {symbol: data}
@app.route("/sync", methods=["GET"])
def get_yfinance_sync():
"""Fetch data for all symbols synchronously."""
from time import time # Import inside the route for clarity
start_time = time()
data = [fetch_symbol_sync(symbol) for symbol in SYMBOLS]
end_time = time()
total_time = end_time - start_time
print(f"Processed {len(SYMBOLS)} symbols in {total_time:.2f} seconds")
return jsonify({
"symbols": SYMBOLS,
"data": data,
"total_response_time": f"{total_time:.2f} seconds"
})
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)
Terminal Output
(venv) openalgo@openalgo testapi % python flask_app.py
* Serving Flask app 'flask_app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
Processed 20 symbols in 5.60 seconds
127.0.0.1 - - [18/Nov/2024 10:59:17] "GET /sync HTTP/1.1" 200 -
Processed 20 symbols in 4.51 seconds
127.0.0.1 - - [18/Nov/2024 10:59:55] "GET /sync HTTP/1.1" 200 -
Processed 20 symbols in 4.61 seconds
127.0.0.1 - - [18/Nov/2024 11:00:09] "GET /sync HTTP/1.1" 200 -
How It Works:
• The /sync endpoint fetches data for each symbol sequentially.
• The server blocks until each request is completed, making it less efficient for large-scale I/O operations.
Performance:
• Single request: Takes around 4-6 seconds to process 20 symbols.
• Multiple concurrent requests: Performance degrades significantly due to blocking behavior.
FastAPI Example: Async Processing
Here’s the async implementation using FastAPI:
Code:
from fastapi import FastAPI
import yfinance as yf
import asyncio
import numpy as np
import time
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# Increase the connection pool size
session = requests.Session()
adapter = HTTPAdapter(pool_connections=50, pool_maxsize=50, max_retries=Retry(total=3, backoff_factor=1))
session.mount("http://", adapter)
session.mount("https://", adapter)
# Patch the `requests` session in `yfinance`
yf.utils.requests = session
app = FastAPI()
# Hardcoded symbols
SYMBOLS = ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA",
"META", "NFLX", "ADBE", "NVDA", "ORCL",
"IBM", "INTC", "AMD", "CRM", "CSCO",
"DIS", "TMUS", "V", "MA", "PYPL"]
async def fetch_symbol(symbol):
loop = asyncio.get_event_loop()
raw_data = await loop.run_in_executor(None, yf.Ticker(symbol).history, "1d")
data = raw_data.reset_index().to_dict(orient="records")
for record in data:
for key, value in record.items():
if isinstance(value, (np.int64, np.float64)):
record[key] = value.item()
return {symbol: data}
@app.get("/async")
async def get_yfinance_async():
start_time = time.time()
tasks = [fetch_symbol(symbol) for symbol in SYMBOLS]
data = await asyncio.gather(*tasks)
end_time = time.time()
total_time = end_time - start_time
print(f"Processed {len(SYMBOLS)} symbols in {total_time:.2f} seconds")
return {
"symbols": SYMBOLS,
"data": data,
"total_response_time": f"{total_time:.2f} seconds"
}
Terminal Output
(venv) openalgo@openalgo testapi % uvicorn fastapi_app:app --host 127.0.0.1 --port 8000
INFO: Started server process [64513]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Processed 20 symbols in 1.44 seconds
INFO: 127.0.0.1:62105 - "GET /async HTTP/1.1" 200 OK
Processed 20 symbols in 0.62 seconds
INFO: 127.0.0.1:62121 - "GET /async HTTP/1.1" 200 OK
Processed 20 symbols in 0.59 seconds
INFO: 127.0.0.1:62125 - "GET /async HTTP/1.1" 200 OK
Processed 20 symbols in 0.58 seconds
INFO: 127.0.0.1:62125 - "GET /async HTTP/1.1" 200 OK
How It Works:
• The /async endpoint uses asyncio.gather() to fetch data concurrently for all symbols.
• Non-blocking operations allow other requests to be processed simultaneously.
Performance:
• Single request: Processes 20 symbols in ~0.5-1.5 seconds.
• Multiple concurrent requests: Handles requests efficiently with minimal degradation.
Performance Comparison
Framework Request Type 20 Symbols Concurrent Requests
Flask Sync ~4-6 seconds Degrades significantly
FastAPI Async ~0.5-1.5 seconds Efficient with low latency
When to Use Sync vs Async in Fintech Applications
Sync (Flask)
• Suitable for simpler applications or when integrating with legacy systems.
• Works well for CPU-bound tasks like data transformations or calculations.
Async (FastAPI)
• Essential for I/O-heavy operations like fetching financial data, real-time trading, or communicating with external APIs.
• Ideal for scalable, high-concurrency fintech platforms.
Conclusion
In the fintech world, where speed and scalability are paramount, FastAPI with async processing is a game-changer. It allows developers to build high-performance applications that can handle large-scale, concurrent operations efficiently. While Flask remains a reliable framework for simpler use cases, adopting FastAPI for I/O-intensive tasks like fetching financial data is crucial for modern fintech solutions.
Final Thoughts
Choosing the right framework depends on your application’s requirements. For fintech applications dealing with real-time data and high concurrency, FastAPI is the clear winner. However, understanding both sync and async paradigms will make you a better developer equipped to tackle any challenge.