How to Build Data-Driven Finance Apps in Python with AG Grid?

October 9, 2024

These past few months, I’ve been exploring various data visualization and manipulation tools for web applications. As a Python developer, I often need to handle large datasets and display them in interactive, customizable tables. One question that consistently bothered me was: How can I build a powerful data grid UI that integrates seamlessly with my Python backend?

There are countless options out there to build sophisticated data grids, but as a Python engineer, I have limited experience with JavaScript or any front-end framework. I was looking for a way to create a feature-rich data grid using only the language I’m most comfortable with — Python! Here’s a step-by-step guide to achieve this using Reflex and AG Grid.

1. Preparation

Before diving into the technicalities, it is essential to set up our working environment by importing the necessary libraries. The critical library for fetching the stock data is yfinance, which simplifies the process of obtaining historical market data from Yahoo Finance. Additionally, we will need pandas for data manipulation, datetime for handling date and time information, and reflex along with reflex_ag_grid for building the web application.

import reflex as rxfrom reflex_ag_grid import ag_gridimport yfinance as yffrom datetime import datetime, timedeltaimport pandas as pd

The libraries listed above are fundamental to ensure seamless functionality across various components of our application. With these libraries, we can start building a dynamic and interactive finance application. Reflex will help us in rendering the app, while AG Grid will empower our data grid with advanced features. pandas and yfinance will assist in robust data manipulation and fetching, respectively.

2. Data Collection and Transformation

In this step, we begin by defining the State class, which will hold the application’s state and business logic. The fetch_stock_data function is implemented to retrieve the stock data from Yahoo Finance and transform it into a format that is suitable for displaying in AG Grid. The State class and fetch_stock_data function are fundamental as they help in maintaining and manipulating data within our web app.

companies = ["AAPL", "MSFT", "GOOGL", "AMZN", "META"]class State(rx.State): data: pd.DataFrame dict_data: list[dict] = [{}] datetime_now: datetime = datetime.now() def fetch_stock_data(self): self.datetime_now = datetime.now() start_date = self.datetime_now - timedelta(days=180) self.data = yf.download(companies, start=start_date, end=self.datetime_now, group_by='ticker') rows = [] for ticker in companies: ticker_data = self.data[ticker] if isinstance(self.data.columns, pd.MultiIndex) else self.data for date, row in ticker_data.iterrows(): rows.append({ "ticker": ticker, "date": date.strftime("%Y-%m-%d"), "open": round(row["Open"], 2), "high": round(row["High"], 2), "mid": round((row["High"] + row["Low"]) / 2, 2), "low": round(row["Low"], 2), "close": round(row["Close"], 2), "volume": int(row["Volume"]), }) self.dict_data = sorted(rows, key=lambda x: (x["date"], x["ticker"]), reverse=True)

State variables are essential in this process, as they handle changing fields and ensure proper data storage. The data state variable stores raw stock data fetched from Yahoo Finance. This data is then transformed, rounded, and stored as a list of dictionaries suitable for AG Grid. The datetime_now state variable holds the current datetime when the data was fetched.

3. Creating AG Grid Columns

Once the data is fetched and transformed, the next step is to define the structure of our AG Grid. The column_defs list specifies the columns to be displayed in the grid. Each entry in this list defines the header_name (column title), the field (column ID), and the filter to be used for each column to make it interactive and user-friendly.

column_defs = [ ag_grid.column_def(field="ticker", header_name="Ticker", filter=ag_grid.filters.text, checkbox_selection=True), ag_grid.column_def(field="date", header_name="Date", filter=ag_grid.filters.date), ag_grid.column_def(field="open", header_name="Open", filter=ag_grid.filters.number), ag_grid.column_def(field="high", header_name="High", filter=ag_grid.filters.number), ag_grid.column_def(field="low", header_name="Low", filter=ag_grid.filters.number), ag_grid.column_def(field="close", header_name="Close", filter=ag_grid.filters.number), ag_grid.column_def(field="volume", header_name="Volume", filter=ag_grid.filters.number),]

The field key represents the identifier for each column, while the header_name sets the column titles. Filters are integrated into each column, providing capabilities such as text search and numeric filtering, enhancing the user experience when interacting with the data grid. This approach makes the app more dynamic and versatile, allowing users to sort and view data based on their requirements.

Defining the columns thoughtfully ensures the readability and usability of the data grid. Each column is tailored to allow specific types of user interactions, thus offering a comprehensive view of the dataset.

4. Displaying AG Grid

Now comes the critical part—integrating AG Grid into our app. The grid is embedded using parameters such as id, column_defs, and row_data, where id uniquely identifies the grid instance on the page. column_defs list contains the column definitions, and row_data retrieves the data stored in the state variable.

ag_grid( id="myAgGrid", column_defs=column_defs, row_data=State.dict_data, pagination=True, pagination_page_size=20, pagination_page_size_selector=[10, 20, 50, 100], theme=State.grid_theme, on_selection_changed=State.handle_selection, width="100%", height="60vh",)

To enhance user experience, pagination is enabled with specific variables, allowing users to navigate through large datasets efficiently. Additionally, the theme of the grid is set, offering visual consistency and better readability.

Displaying the AG Grid involves several critical parameters that ensure seamless integration and optimal functionality. Pagination features ensure that large datasets are manageable, while themes offer aesthetic customization.

5. Adjusting AG Grid Theme

Customizing the look and feel of AG Grid can significantly improve the user experience. The grid_theme state variable, housed within the rx.select component, sets the theme for the grid. Theme selection is facilitated by a dropdown menu, providing various options for grid customization.

class State(rx.State): grid_theme: str = "quartz" themes: list[str] = ["quartz", "balham", "alpine", "material"]rx.select( State.themes, value=State.grid_theme, on_change=State.set_grid_theme, size="1",)

Each state variable has a built-in method for easy value setting, in this case, set_grid_theme, enabling users to switch themes dynamically. This flexibility allows users to tailor the grid’s appearance based on personal or project-specific preferences.

The capability to change AG Grid’s theme is crucial for maintaining an engaging user interface. Diverse options and easy switching enhance the overall user experience, making the application versatile and adaptive to various contexts.

6. Graphical Representation of Company Data

An interactive and visually appealing element of the app is the graphical representation of company data. The on_selection_changed event trigger in AG Grid calls the handle_selection method when a row is selected. This method updates the selected rows and invokes the update_line_graph function to render the graph.

class State(rx.State): selected_rows: list[dict] = None company: str data: pd.DataFrame dff_ticker_hist: list[dict] = None def handle_selection(self, selected_rows, _, __): self.selected_rows = selected_rows self.update_line_graph() def update_line_graph(self): if self.selected_rows: ticker = self.selected_rows[0]["ticker"] else: self.dff_ticker_hist = None return self.company = ticker dff_ticker_hist = self.data[ticker].reset_index() dff_ticker_hist["Date"] = pd.to_datetime(dff_ticker_hist["Date"]).dt.strftime("%Y-%m-%d") dff_ticker_hist["Mid"] = (dff_ticker_hist["Open"] + dff_ticker_hist["Close"]) / 2 dff_ticker_hist["DayDifference"] = dff_ticker_hist.apply( lambda row: [row["High"] - row["Mid"], row["Mid"] - row["Low"]], axis=1 ) self.dff_ticker_hist = dff_ticker_hist.to_dict(orient="records")

The graph displays the company’s historical data over the past six months, rendered using rx.recharts.line_chart. Users can visually explore stock trends, making the data analysis process more intuitive.

rx.recharts.line_chart( rx.recharts.line( rx.recharts.error_bar( data_key="DayDifference", direction="y", width=4, stroke_width=2, stroke="red", ), data_key="Mid", ), rx.recharts.x_axis(data_key="Date"), rx.recharts.y_axis(domain=["auto", "auto"]), data=State.dff_ticker_hist, width="100%", height=300,)

Incorporating an interactive chart alongside the data grid provides a comprehensive data analysis tool. Users can select specific rows in the grid and instantly see the corresponding graphical representation, enhancing data exploration and insight discovery.

7. Conclusion

Over the past few months, I’ve been diving deep into various tools for data visualization and manipulation, specifically for web applications. As a Python developer, I frequently work with large datasets that need to be displayed in interactive and customizable tables. The burning question for me has been: How can I create a robust data grid UI that integrates smoothly with my Python backend?

There are numerous options available to build sophisticated data grids. However, as a Python engineer with limited experience in JavaScript or front-end frameworks, I sought out a solution that would allow me to work within the comfort zone of Python. I wanted to create a feature-rich data grid without having to dive into languages or frameworks I’m not familiar with.

After extensive research and experimentation, I found a way to achieve this using Reflex and AG Grid. Reflex provides a Pythonic way to handle interactive web applications, while AG Grid offers a powerful and flexible data grid component. By combining these two tools, I managed to build a dynamic data grid that fits perfectly with my Python-based projects.

This approach allows Python developers to create advanced data grids without needing to switch between languages or learn new front-end technologies. Here’s a step-by-step guide to accomplishing this integration, leveraging the strengths of both Reflex and AG Grid to streamline your data management and display processes.

Subscribe to our weekly news digest.

Join now and become a part of our fast-growing community.

Invalid Email Address
Thanks for Subscribing!
We'll be sending you our best soon!
Something went wrong, please try again later