Building an AI Financial Analyst with the Assistants API

In this guide, we discuss how to build an AI financial analyst using the Assistants API, function calling, and Code Interpreter.

7 months ago   •   13 min read

By Peter Foy

With OpenAI's release of the Assistants API, the race towards building autonomous AI agents has become significantly more user-friendly and reliable.

By combining tools like Retrieval, Code Interpreter, and function calling we have all the tools we need to accomplish increasingly complex tasks for users with a single prompt.

In case you're unfamiliar with the Assistants API:

An Assistant has instructions and can leverage models, tools, and knowledge to respond to user queries. The Assistants API currently supports three types of tools: Code Interpreter, Retrieval, and Function calling. 

In this guide, we'll look at how to build a financial analyst assistant using parallel function calling and the Assistants API. After setting that up, we'll also extend it to use Code Interpreter in order to visualize the data from our API calls.

In case you're unfamiliar with function calling:

Function calling allows you to describe functions to the Assistants and have it intelligently return the functions that need to be called along with their arguments.

For this example, we'll use the Financial Modeling Prep API and retrieve real-time financial data for the following endpoints:

  • Key metrics
  • Financial ratios
  • Financial growth
  • Financial statements (i.e. income statements, balance sheet, & cash flow statement)

By making each of these API calls available to our financial assistant, it will be able to retrieve data based on the user's question, analyze the results, and respond accordingly.

Also, since we now have parallel function calling, we can analyze multiple stocks for specific data with a single input. For example, you could ask questions like:

Compare the financial health of Microsoft and Apple over the last four years, focusing on their balance sheets and key financial ratios
Evaluate Amazon's financial growth trajectory over the past five quarters. Highlight key metrics and cash flow trends that indicate its growth pattern.

Overview of the Assistants API

Before we get into the code, let's briefly review the steps of creating an Assistant.

The Assistants API enables us to build specialized AI agents with distinct capabilities.

By combining Assistants with function calling, these agents can translate natural language into API calls, process the data from these API calls, and provide analyses based on the response.

Steps to Create a Financial Analyst Assistant

At a high-level, the steps we need to take to create an Assistant are:

  1. Defining financial functions: We need to define the Financial Modeling Prep API calls to retrieve financial
  2. Defining the Assistant: Our AI agent will use models and instructions alongside the function calling tool to retrieve & analyze financial data.
  3. Creating a Thread: A thread is essentially a conversation flow where we can feed the agent with user queries, function call responses, and assistant replies.
  4. Adding Messages: We then input user queries to the Thread in the form of Messages.
  5. Running the Assistant: We then create a Run, which is the Assistant processing the Thread, calling necessary tools, and generating appropriate responses.

To get started, we'll build this in Colab notebook so we'll need to set up our environment by adding our OpenAI API key & Financial Modeling Prep API key into the Secrets manager.

Step 0: Setting up the Environment

We'll can setup our environment by importing necessary libraries and setting our API keys as follows:

import os
import json
import requests
from openai import OpenAI
import time

# API keys are stored in Google Colab's Secret Manager
FMP_API_KEY = userdata.get('FMP_API_KEY')

os.environ["FMP_API_KEY"] = FMP_API_KEY

client = OpenAI()

Step 1: Defining Financial Functions

Next, we need to define our API calls to FMP in order to retrieve financial data. For this example, we'll use the following endpoints:

Of course, to create an end-to-end financial analyst we'd want to extend this function calling capability to much more financial data, but this will get us started for basic analysis.

Below, you can see how we get_income_statement fetches and returns the income statement data in JSON format for a specified company (ticker), over a quarterly or annual period (period), and a set (limit) of periods to analyze:

# Define financial statement functions
def get_income_statement(ticker, period, limit):
    url = f"{ticker}?period={period}&limit={limit}&apikey={FMP_API_KEY}"
    response = requests.get(url)
    return json.dumps(response.json())

def get_balance_sheet(ticker, period, limit):
    # Code to fetch and return cash flow statement

def get_cash_flow_statement(ticker, period, limit):
    # Code to fetch and return cash flow statement

def get_key_metrics(ticker, period, limit):
    # Code to fetch and return cash flow statement

def get_financial_ratios(ticker, period, limit):
    # Code to fetch and return cash flow statement

def get_financial_growth(ticker, period, limit):
    # Code to fetch and return cash flow statement

Step 2: Map available functions

Next, we'll create a dictionary mapping function names to their corresponding function definitions with available_functions to facilitate the integration of our API endpoints into the Assistant's workflow.

# Map available functions
available_functions = {
    "get_income_statement": get_income_statement,
    "get_balance_sheet": get_balance_sheet,
    "get_cash_flow_statement": get_cash_flow_statement,
    "get_key_metrics": get_key_metrics,
    "get_financial_ratios": get_cash_flow_statement,
    "get_financial_growth": get_financial_ratios

Step 3: Creating the Assistant

Next, we'll create a main function called run_assistant that takes in the user_messsage. This user message will map the input to the ticker, period, and limit.

Next, we create our Assistant with the following parameters:

  • Instructions: This controls the overall behavior of the assistant i.e. Act as a financial analyst by accessing financial data through the Financial Modeling Prep API. Your capabilities include analyzing key metrics, comprehensive financial statements, vital financial ratios, and tracking financial growth trends, etc, etc.
  • Model: We'll use GPT-4 Turbo gpt-4-1106-preview
  • Tools: Here we provide the functions the assistant has access to i.e. get_income_statement, etc.
# Define the main function
def run_assistant(user_message):

  # Creating an assistant with specific instructions and tools
  assistant = client.beta.assistants.create(
      instructions="Act as a financial analyst by accessing detailed financial data through the Financial Modeling Prep API. Your capabilities include analyzing key metrics, comprehensive financial statements, vital financial ratios, and tracking financial growth trends. ",
            {"type": "function", "function": {"name": "get_income_statement", "parameters": {"type": "object", "properties": {"ticker": {"type": "string"}, "period": {"type": "string"}, "limit": {"type": "integer"}}}}},
            # same for the rest of the financial functions

Step 4: Initiating a Thread

Next, we need to create a Thread, which as OpenAI describes is:

A conversation session between an Assistant and a user. Threads store Messages and automatically handle truncation to fit content into a model’s context.
  # Creating a new thread
  thread = client.beta.threads.create()

Step 5: Adding Messages to the Thread

Next, we can add a user Message to the thread.

Note that since we're using function calling, it will automatically map the user message to the required parameters for the API call, meaning we can retrieve ticker, period, year from natural language inputs...quite powerful.

  # Adding a user message to the thread

Step 6: Running and Monitoring the Assistant

Now that we have our Assistant, Thread, and Message, let's create a Run, which initiates the Assistant's processing of the user's request on the Thread.

6.1 Start a Run

  # Running the assistant on the created thread
  run = client.beta.threads.runs.create(,

6.2 Monitor and Manage the Run:

Next we want to loop continuously checks the status of the Run until it's completed.

  • Retrieve Run Status: Regularly checks the current status of the Run.
    • run = client.beta.threads.runs.retrieve(,
  • Retrieve Run Steps: Fetches the steps the Run has taken, providing insight into the actions performed by the Assistant (mainly for monitoring and debugging purposes)
    • run_steps = client.beta.threads.runs.steps.list(,
  • Handle in_progress and queued Statuses: If the Run is still in progress or queued, wait for a short period before checking the status again.
    • Introduces a delay (time.sleep(5)) to avoid overwhelming the API with requests.
  • Handle requires_action Status: If the Run requires action, typically when function calls are involved.
    • Extract tool calls from the Run and loop through them. Each tool call represents a function that the Assistant has decided to invoke based on the user's request.
    • Execute each function using the arguments provided by the Assistant and gather the outputs.
    • Submit the outputs back to the Run so the Assistant can continue processing.
      • client.beta.threads.runs.submit_tool_outputs(...)
  • Handle completed Status: When the Run completes, fetch and print the messages (responses) generated by the Assistant.
    • Loop through messages and display their content based on the role (User or Assistant). Exit the loop after processing all the messages.
# Loop until the run completes or requires action
    while True:
        run = client.beta.threads.runs.retrieve(,

        # Add run steps retrieval here
        run_steps = client.beta.threads.runs.steps.list(,
        print("Run Steps:", run_steps)

        if run.status == "requires_action":
            tool_calls = run.required_action.submit_tool_outputs.tool_calls
            tool_outputs = []

            for tool_call in tool_calls:
                function_name =
                function_args = json.loads(tool_call.function.arguments)

                if function_name in available_functions:
                    function_to_call = available_functions[function_name]
                    output = function_to_call(**function_args)
                        "output": output,

            # Submit tool outputs and update the run

        elif run.status == "completed":
            # List the messages to get the response
            messages = client.beta.threads.messages.list(
            for message in
                role_label = "User" if message.role == "user" else "Assistant"
                message_content = message.content[0].text.value
                print(f"{role_label}: {message_content}\n")
            break  # Exit the loop after processing the completed run

        elif run.status == "failed":
            print("Run failed.")

        elif run.status in ["in_progress", "queued"]:
            print(f"Run is {run.status}. Waiting...")
            time.sleep(5)  # Wait for 5 seconds before checking again

            print(f"Unexpected status: {run.status}")

Step 7: Analyzing the Output

Finally, let's go and execute the run_assistant function with the following user message:

Can you compare the financial health of Microsoft and Apple over the last four years, focusing on their balance sheets and key financial ratios?

Which gives the following result...

Financial Assistant Response (click to open)

### Balance Sheet Analysis

#### Microsoft (MSFT)
Over the last four years, Microsoft's total assets have grown consistently:
- 2020: $301.31 billion
- 2021: $333.78 billion
- 2022: $364.84 billion
- 2023: $411.98 billion

Their total liabilities have also increased but at a slower pace than their total assets, which has led to a steady rise in stockholders' equity:
- 2020: $183.01 billion liabilities, $118.30 billion stockholders' equity
- 2021: $191.79 billion liabilities, $141.99 billion stockholders' equity
- 2022: $198.30 billion liabilities, $166.54 billion stockholders' equity
- 2023: $205.75 billion liabilities, $206.22 billion stockholders' equity

Microsoft's increased cash position from $13.93 billion to $34.70 billion between 2020 and 2023. The net debt decreased from $57.42 billion in 2020 to $25.26 billion in 2023, showing a stronger financial position.

#### Apple (AAPL)
Apple's total assets have remained fairly stable over the period:
- 2020: $323.89 billion
- 2021: $351.00 billion
- 2022: $352.76 billion
- 2023: $352.58 billion

Similarly, their liabilities have remained quite stable with total equity also remaining consistent:
- 2020: $258.55 billion liabilities, $65.34 billion stockholders' equity
- 2021: $287.91 billion liabilities, $63.09 billion stockholders' equity
- 2022: $302.08 billion liabilities, $50.67 billion stockholders' equity
- 2023: $290.44 billion liabilities, $62.15 billion stockholders' equity

Apple's cash position increased from $38.02 billion in 2020 to $29.97 billion in 2023, while its net debt increased from $74.42 billion to $81.12 billion.

### Key Financial Ratios Comparison

#### Profitability Ratios
**Return on Equity (ROE)**: A measure of the profitability relative to shareholders' equity.
- Microsoft's ROE has varied across the four years, typically indicating strong profitability with the highest in 2021 at 43.18% and decreased to 35.09% in 2023.
- Apple's ROE generally has been high, showing strong profitability with its highest at 28.46% in 2020 and decreased to 23.07% in 2023.

**Net Profit Margin**: Percentage of revenue left after all expenses have been deducted.
- Microsoft has maintained a high net profit margin, peaking at 38.53% in 2022.
- Apple has also displayed strong profitability, with its net profit margin reaching 26.69% in 2022.

**Return on Assets (ROA)**: Indicator of how effective a company is at using its assets to generate earnings.
- Microsoft's ROA has shown consistent performance with a high of 22.65% in 2021.
- Apple's ROA has been slightly lower than Microsoft's, with a maximum of 20.57% in 2021.

#### Liquidity Ratios
**Current Ratio**: Measures the ability of a company to pay short-term obligations with short-term assets.
- Microsoft's current ratio is above 1 for all years, reaching as high as 2.58 in 2023, implying good short-term financial health.
- Apple's current ratio also indicates sufficiency in meeting short-term liabilities, with a ratio of 1.17 in 2023.

#### Solvency Ratios
**Debt to Equity Ratio**: Indicates the relative proportion of shareholders' equity and debt used to finance assets.
- Microsoft's debt to equity ratio has reduced over the four years, standing at 0.29 in 2023, suggesting less reliance on debt financing.
- Apple, on the other hand, has a higher debt to equity ratio, although it decreased from 1.72 in 2020 to 1.68 in 2023, it suggests a higher reliance on debt.

#### Efficiency Ratios
**Asset Turnover Ratio**: Reveals how good a company is at using its assets to generate revenue.
- Microsoft has seen a slight decrease in its asset turnover ratio from 0.59 in 2020 to 0.54 in 2023.
- Apple maintains a higher asset turnover ratio compared to Microsoft, with 0.81 in 2023, indicating better efficiency in using assets to generate sales.

### Conclusion
Both Microsoft and Apple have displayed growth and strong financial health over the past four years. Microsoft has seen a more significant growth in total assets compared to Apple and has a higher net profit margin and ROE. Meanwhile, Apple has managed to keep a higher asset turnover ratio, reflecting efficient asset usage.

Apple tends to utilize more debt financing as observed in its higher debt to equity ratio. Both companies have a current ratio indicating adequate liquidity to cover short-term liabilities.

This analysis indicates strong performance by both companies with Microsoft showing a higher growth trajectory in assets and equity, while Apple displays more efficient asset utilization.

Would you like a deeper analysis or have any other specific metrics or aspects you wish to compare between these two companies?

Step 8: Enabling Code Interpreter

Alright, now that our financial assistant is working with function calling, let's take it a step further and enable the Code Interpreter tool:

Code Interpreter allows the Assistants API to write and run Python code in a sandboxed execution environment. This tool can process files with diverse data and formatting, and generate files with data and images of graphs. 

To do this, all we need to add the tool to our Assistant:

        # previous assistant code
          {"type": "code_interpreter"},
          {"type": "function", "function": {"name": "get_income_statement"...}

Next, all we need to do is handle the file outputs when the status is complete and save them in our notebook:

    elif run.status == "completed":
      # same code as before
                # Check the type of message content and handle accordingly
                for content in message.content:
                    if content.type == "text":
                        message_content = content.text.value
                        print(f"{role_label}: {message_content}\n")
                    elif content.type == "image_file":
                        # Handle image file content, e.g., print the file ID or download the image
                        image_file_id = content.image_file.file_id
                         # Define a path to save the image
                        image_save_path = f"./image_{image_file_id}.png"
                        # Download and save the image
                        download_and_save_image(image_file_id, image_save_path)

Now, if I ask a question like...

Evaluate Microsoft vs. Googles's revenue & profitability} growth over the past 4 quarters. Visualize the results with one or more charts.

This gives us...

The analysis of Microsoft and Google's revenue and profitability over the past 4 quarters is visualized in the provided charts:

1. Revenue Comparison:
- Microsoft's revenue has been relatively stable over the last 4 quarters, with a slight increase in the most recent quarter.
- Google's revenue was higher for all the quarters compared to Microsoft and showed a decrease in the third quarter but increased again in the fourth quarter.

2. Net Income Comparison:
- Microsoft's net income has been progressively increasing each quarter.
- Google's net income has been varying, with the lowest in the fourth quarter of the previous year, and the highest in the most recent quarter. On average, Google's net income is slightly lower than Microsoft's net income.

3. Profit Margin Comparison:
- Microsoft's profit margin has been stable and shows a slight uptrend in the most recent quarter.
- Google's profit margin saw a dip in the second quarter of the year but has been increasing in the last two quarters.

Overall, Google has consistently higher revenues compared to Microsoft for the reviewed quarters. However, Microsoft's net income has been higher in all but the most recent quarter, and its profit margins are also comparable or higher than those of Google. This indicates that while Google has a larger revenue base, Microsoft has been able to generate more profit from its revenues.


Of course there's much more we can do to improve these results, either with some prompt engineering or fine tuning, but we'll save that for later.

Summary: Building an AI Financial Research Assistant

As we discussed, the Assistants API is likely going to lead the current AI agent revolution as it makes developing these agentic experiences significantly more accessible and reliable for both developers and users.

We'll continue building out more advanced examples of Assistants over the coming weeks, but for now here's a brief summary:

  • πŸš€ Assistants API: OpenAI's Assistants API offers tools like Retrieval, Code Interpreter, and function calling, making complex task handling more efficient.
  • πŸ€– Assistant Capabilities: Assistants can use models, tools, and knowledge to respond to queries, with the Assistants API supporting Code Interpreter, Retrieval, and Function calling.
  • πŸ“ˆ Financial Analyst Assistant: The guide demonstrates building a financial analyst assistant using parallel function calling and the Assistants API, incorporating Code Interpreter for data visualization.
  • πŸ§‘β€πŸ’» Function Calling: Function calling enables Assistants to intelligently return necessary functions and arguments for specific tasks.
  • πŸ”„ Assistant Creation Steps: To create a financial analyst assistant, steps involve defining financial functions, creating an Assistant with specific instructions, setting up a conversation thread, adding user queries, and processing responses..
  • πŸ“ Output Analysis: The final step involves executing the run_assistant function with a specific user query and analyzing the assistant's financial data response.

Access the full code for this tutorial

You can access the complete code to this tutorial along with other premium content by joining the MLQ Academy.

This content is only available to subscribers

Subscribe now and have access to all our stories, enjoy exclusive content and stay up to date with constant updates.

Sign up now

Spread the word

Keep reading