Using GPT's Function Calling Capability: Stock Information Demo

Using GPT's Function Calling Capability: Stock Information Demo

Accessing stock market data with OpenAI's newly released "Function Calling" capabilities

ยท

6 min read

The ability to extract insights from user input has always been an excellent use case for OpenAI APIs. I have seen a lot of people using GPT APIs to perform NLP tasks. From Intent Detection, Classification to NER (Named Entity Recognition) it's quite useful, especially for developers who do not have extensive ML backgrounds.

And this new capability of "Function Calling" takes this use case to a whole new level. In this article, we are going to explore how we can use this feature to get real data about a stock in a few simple lines of Python.

Understanding the core problem

Getting Stocks data is not difficult, it's only a matter of simple API calls.

Here are the tricky parts :

  • Identifying what the user wants to do

  • Mapping the users intent to the solution function

  • Extracting data that our function expects

  • Chaining the prompts together to respond

Before the availability of the function calling feature, we had to juggle all these things which led to complicated flow. Sure frameworks like langchain can help us simplify this juggling. But the ability of the model itself to do this for us is quite amazing.

I am going to demonstrate this with a simple example :

Answer user's questions about stock prices & other information

Get your API keys

To get the stock price data, we are going to use Polygon,

You can signup there and then access your API keys at: https://polygon.io/dashboard/api-keys

Polygon has a Python client but to keep it more abstract, we are going to use its REST API

If you don't already have your OpenAI API keys, get them from here: https://platform.openai.com/account/api-keys

Let's get into the Code

First, let's install openai and reuests

!pip install openai
!pip install requests

Now, let's setup some global variables and API keys (OpenAI & Polygon)

import openai

global OPENAI_API_KEY
global POLYGON_API_KEY

OPENAI_API_KEY="Your OpenAI API Key goes here" 
POLYGON_API_KEY="Your Polygon API Key goes here" 

openai.api_key = OPENAI_API_KEY

Imports, some more global variables and setup code

import datetime
import requests
import json

global today_str

today = datetime.date.today()
today_str = today.strftime('%Y-%m-%d')

Functions to fetch Stocks data

Let's write a function that we can use to get stock prices data from the polygon API

def get_stock_price_by_date(ticker_symbol, date):
    url = f"https://api.polygon.io/v1/open-close/{ticker_symbol}/{date}?adjusted=true&apiKey={POLYGON_API_KEY}"
    try:
        response = requests.request(method='GET', url=url)
        response.raise_for_status()
        # print(response.text)
        return json.dumps(response.text)
    except Exception as e:
        print(f"An error occurred: {e}")
        return None

You can try calling this function with manual parameter input, to make sure it works as expected

print(get_stock_price_by_date(ticker_symbol="GOOGL", date="2023-06-16"))
{
    "status": "OK",
    "from": "2023-06-15",
    "symbol": "GOOGL",
    "open": 123.14,
    "high": 125.46,
    "low": 122.4,
    "close": 125.09,
    "volume": 3.5146268e+07,
    "afterHours": 124.85,
    "preMarket": 123.48
}

Let's write another function that fetches the data about stock according to the SEC filing

def get_stock_sec_details(ticker_symbol, date = today_str):
    url = f"https://api.polygon.io/v3/reference/tickers/{ticker_symbol}?date={date}&apiKey={POLYGON_API_KEY}"
    try:
        response = requests.request(method='GET', url=url)
        response.raise_for_status()
        # print(response.text)
        return json.dumps(response.text)
    except Exception as e:
        print(f"An error occurred: {e}")
        return None

We can try calling this function to check how it responds

print(get_stock_sec_details(ticker_symbol="AAPL"))
{
    "request_id": "f7d8eb4aac2a75fbb4b8df1f25ab4ead",
    "results": {
        "ticker": "AAPL",
        "name": "Apple Inc.",
        "market": "stocks",
        "locale": "us",
        "primary_exchange": "XNAS",
        "type": "CS",
        "active": true,
        "currency_name": "usd",
        "cik": "0000320193",
        "composite_figi": "BBG000B9XRY4",
        "share_class_figi": "BBG001S5N8V8",
        "market_cap": 2.92569585902e+12,
        "phone_number": "(408) 996-1010",
        "address": {
            "address1": "ONE APPLE PARK WAY",
            "city": "CUPERTINO",
            "state": "CA",
            "postal_code": "95014"
        },
        "description": "Apple designs a wide variety of consumer electronic devices, including smartphones (iPhone), tablets (iPad), PCs (Mac), smartwatches (Apple Watch), and AirPods, among others. In addition, Apple offers its customers a variety of services such as Apple Music, iCloud, Apple Care, Apple TV+, Apple Arcade, Apple Fitness, Apple Card, and Apple Pay, among others. Apple's products include internally developed software and semiconductors, and the firm is well known for its integration of hardware, software, semiconductors, and services. Apple's products are distributed online as well as through company-owned stores and third-party retailers.",
        "sic_code": "3571",
        "sic_description": "ELECTRONIC COMPUTERS",
        "ticker_root": "AAPL",
        "homepage_url": "https://www.apple.com",
        "total_employees": 164000,
        "list_date": "1980-12-12",
        "branding": {
            "logo_url": "https://api.polygon.io/v1/reference/company-branding/d3d3LmFwcGxlLmNvbQ/images/2023-05-01_logo.svg",
            "icon_url": "https://api.polygon.io/v1/reference/company-branding/d3d3LmFwcGxlLmNvbQ/images/2023-05-01_icon.jpeg"
        },
        "share_class_shares_outstanding": 15728700000,
        "weighted_shares_outstanding": 15728702000,
        "round_lot": 100
    },
    "status": "OK"
}

Utilizing Function Calling

Now is the part where we describe our function to OpenAI API

def run_conversation(message):
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo-0613",
        messages=[{"role": "user", "content": f"Date today is {today_str}. Provide answer with a date. {message}"}],
        functions=[
            {
                "name": "get_stock_price_by_date",
                "description": "Get stock prices by date",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "ticker_symbol": {
                            "type": "string",
                            "description": "The ticker symbol for the stock listed on NASDAQ exchange",
                        },
                        "date": {
                            "type": "string", 
                            "description": "date string in %Y-%m-%d format"
                        }
                    },
                    "required": ["stock_ticker", "date"],
                },
            },
            {
                "name": "get_stock_sec_details",
                "description": "Get SEC filing details by stock ticker symbol",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "ticker_symbol": {
                            "type": "string",
                            "description": "The ticker symbol for the stock listed on NASDAQ exchange",
                        },
                        "date": {
                            "type": "string", 
                            "description": "date string in %Y-%m-%d format"
                        }
                    },
                    "required": ["stock_ticker"],
                },
            }
        ],
        function_call="auto",
    )

There are 2 differences between a standard chat completion call and this one.

  1. Model: To use the function calling feature we have to use the latest models gpt-3.5-turbo-0613 or gpt-4-0613 (GPT 4)

  2. Additional Parameters to describe the function : functions & function_call

    • functions expect an array of the function descriptions, you can provide more functions to this array and the model will try to detect which function is most suitable for the given user input

    • function_call to indicate whether to generate a model-facing (auto response (none a response that will be fed to the model on subsequent call) or a user-facing response that will be presented to the user

Now based on the model's response we'll call the actual function, here you can check which function was found most suitable for the model

Here is how we can do this

## Continuing the `run_conversation` function

internal_message = response["choices"][0]["message"]

    if internal_message.get("function_call"):
        function_name = internal_message["function_call"]["name"]
        function_args = json.loads(internal_message["function_call"]["arguments"])
        function_response = ""

        # Printing which function is detected
        print(function_name)

        # If model detected stock price info is required
        if function_name == "get_stock_price_by_date":
          function_response = get_stock_price_by_date(
              ticker_symbol=function_args.get("ticker_symbol"),
              date=function_args.get("date"),
          )

        # If model detected stock SEC filing info is required
        if function_name == "get_stock_sec_details":
          function_response = get_stock_sec_details(
              ticker_symbol=function_args.get("ticker_symbol")
          )


        second_response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo-0613",
            messages=[
                {"role": "user", "content": message},
                internal_message,
                {
                    "role": "function",
                    "name": function_name,
                    "content": function_response,
                },
            ],
        )
        return second_response

One thing to note here is that the function description (objects within the functions array) will be counted against input tokens

Alright, let's test

print(run_conversation("tell me apple volume 3 days ago")["choices"][0]["message"]["content"])

get_stock_price_by_date

The volume for Apple (AAPL) three days ago, on June 13, 2023, was approximately 54,867,129 shares.

print(run_conversation("how many outstanding google shares are there")["choices"][0]["message"]["content"])

get_stock_sec_details

As of now, there are approximately 5,874,000,000 outstanding Google shares.

So to conclude, by using the function calling feature -

  • The model can detect which function should be called to fulfill the user's request

  • It can extract required info from the user's input and provide us with function arguments

You can find the full source code of this article at - https://github.com/anantrp/ai-experiments/blob/main/openai_function_calling_stock_prices_demo.ipynb

You can read more about Function Calling at - https://platform.openai.com/docs/guides/gpt/function-calling

This can go beyond simple retrieval capabilities and we can build actuator functions (functions that can perform actions) such as sending an email, ordering products, taking action on our custom software systems and much more

With Function Calling capability we have great potential to build the AI agents of tomorrow


I hope you find this valuable, if you did - that's awesome ๐Ÿ‘Œ share it with your folks who'll find it useful too. If you have any suggestions/comments please feel free.

Happy coding!

ย