avatarIbrahim Saidi

Summary

The provided content outlines a method for automating stock market report generation using a team of AI agents, leveraging open-source frameworks like Autogen and LangChain, and integrating tools for real-world interaction.

Abstract

The era of AI agents has arrived, offering a glimpse into the future of collaborative work between humans and AI. The article delves into the use of AI agents to automate repetitive tasks, such as stock market analysis, by employing frameworks like Autogen and LangChain. These frameworks facilitate the creation of multi-agent applications that can perform complex tasks, such as screening stocks based on specific criteria and generating comprehensive reports. The process involves configuring AI agents with the necessary tools to interact with the real world, such as accessing financial APIs and writing to files. The article also provides a step-by-step guide on constructing a team of AI agents, including a human proxy, a coder, and writers, to carry out the task of generating a stock market report. The conclusion emphasizes the potential of AI agents to assist individual traders and investors by providing them with advanced capabilities and resources.

Opinions

  • The path towards Artificial General Intelligence (AGI) is believed to involve agentic AI, where AI agents will join society and the workforce, providing significant leverage to human efforts.
  • Sam Altman's vision suggests that everyone will become the CEO of their work, coordinating and directing AI agents to perform increasingly complex tasks.
  • The article posits that AI agents can greatly benefit sensitive tasks in roles like stock market analysis by providing supervision and ensuring tasks are carried out as expected.
  • The author expresses that AI agents' reliability is enhanced by equipping them with specific tools for interacting with the real world, compensating for the limitations of their reasoning capabilities.
  • The author recommends an AI service that offers similar capabilities to ChatGPT Plus (GPT-4) but at a more cost-effective rate, indicating a preference for this service based on value for money.

Team Up with AI Agents: Automating Stock Market Report Generation

Get a team of AI agents to work collaboratively towards any task you give them

Image generated by Blog Mate

The era of AI agents is here. Many of the leading actors in this field of Artificial Intelligence (AI) believe the path towards Artificial General Intelligence is agentic.

Everyone will be the CEO of all of the work they want to do and they’ll have tons of people that they’re able to coordinate and direct and provide the taste and feedback. But they’ll have lots of agents that go off and do increasingly complex tasks. It’s going to feel gradual. These AIs will join society, they’ll join the workforce. We have 8 billion humans, we’ll have 8 billion workers that kind of just exist in society and give us a whole lot of leverage.

— Sam Altman

In this post, we look at how to get started with a building multi-agent applications that can bring this vision to reality. Our focus will be simple — instructing such agents to work collaboratively, and write a final report.

To achieve this task, we’ll use two open-source frameworks that are perfectly tailored to this task — Autogen and LangChain.

Autogen makes it really easy to get started with building multi-agent applications. All the necessary orchestration and automation is taken care by the framework so you can get to prototyping your applications quickly. In short, here are the benefits Autogen provides:

  • Agents that can be backed by various LLM configurations
  • Native support for a generic form of tool usage through code generation and execution
  • A special agent, the Human Proxy agent that enables easy integration of human feedback and involvement at different levels

LangChain is another framework for developing applications powered by language models. It comes pre-packed with 35+ tools and toolkits that we can leverage in building our multi-agent applications.

⚠️A quick note before we dive into the code. In order to create the report for us, the agents will need access to our local file system. Be careful where you’re running your scripts. I prefer to save my projects in Desktop/Builds directory. Okay, let’s continue.⚠️

Imagining an Agentic Future

Image a world where you a stock analyst for a top investment firm in New York. Your business has over a billion dollars worth of assets under management. Safe to say there’s a lot at stake with every decision informed by your analysis.

As you can image with such a role, there’s a lot of repetitive tasks that could benefit greatly with this multi-agent approach. There’re tasks that require you to be in front of your computer screens all day because nothing happens unless you’re doing something in front of my screen. It’s not like you can just send your computer off to do a bunch of things for you.

But, wouldn’t be nice if you could just ask your computer:

You🥸: Hey computer, Screen for stocks in the technology sector that have a market cap of more than $1,000,000,000 and pay a dividend. Save the results in a file named StockScreening.md .

Computer🤖: Sure, here you go:

Now what if you had a team of agents ready to carry out those menial tasks for you and all you have to do is instruct them to go off and work as a team and come back to you with a solution.

For many sensitive tasks, you might want to supervise these agents as they work, to ensure the work is carried out as expected. Autogen has you covered here. But that’s a topic for another article perhaps.

This way, we can image a world where, you can leave these agents working on task while you head into an important meeting, and they’ll have the results ready for you when you come back, saving you potentially hours of tedious work.

So, we’ll build a small team of agents that we can put to work on our problem. At the simplest level, Autogen has three classes of agents we should know about in order to get startedA generic ConversableAgent class and two subclasses called AssistantAgent and UserProxyAgent.

Image from the official Autogen Docs

Think of it this way, the ConversableAgent is the parent class from which these subclasses inherit their capabilities to converse with each other to work together on a task through communication.

The subclasses can be thought of as the children with different behaviours and capabilities from their parent generic class. These subclasses differ in the actions they take upon receiving messages from other agents.

The AssistantAgent acts as an AI assistant that’s capable of writing Python code when it receives a task that needs this functionality (actually, AssistantAgent uses GPT-4 under the hood to write the code required).

The other subclass is the UserProxyAgent, and as the name suggests, this is the agent that will act as your way into this mysterious agentic world. It is designed by default to seek your input before proceeding, but can take other configurations:

  • ALWAYS
  • AUTO
  • NEVER

The UserProxyAgent also has other capabilities besides acting as your representative to these other agents. Remember if we have a task that requires code, the AssistantAgent will write the code?

Well, once AssistantAgent has written the code, it passes this code to ProxyUserAgent. Once ProxyUserAgent receives this code, it will wait for the user’s (your) input before executing the code block(if that’s how you’ve configured it).

Once ProxyUserAgent has executed the code, AssistantAgent will take a look at the output and suggest code corrections and bug fixes.

Finally, we want to ask the agent to output the findings in a Markdown file. Why Markdown? Because it is the language for making web pages with tags that define the structure and content…

The Code — From imagination to reality

Now that we know what we want to build, let’s put this into code and go from imagination to reality.

Requirements

Autogen requires that you have Python versions 3.8 or later.

API Configurations

Agents use large language models to reason about their decision making (in fact this is what makes agents really cool). Because LLMs are involved, we need to setup to configure how our agents will have access to it.

Autogen allows us to load a list of configurations from an environment variable or a json file through the config_list_from_json . In our case, we’ll set our configs in a json file.

In your root directory, create a OAI_CONFIG_LIST file and setup your OpenAI API key as follows:

[
    {
        "model": "gpt-4",
        "api_key": "<your OpenAI API key here>"
    }
]

As we’ll see further later on, Autogen knows where to find it. We start by importing the required packages.

# Env related imports
import os
from dotenv import load_dotenv

# Imports
import autogen
from autogen import AssistantAgent, UserProxyAgent
# Import things that are needed generically
from langchain.pydantic_v1 import BaseModel, Field
from langchain.tools import BaseTool
from langchain.tools.file_management.read import ReadFileTool
from typing import Optional, Type
import math

# Imports for experimental LangChain File Management
from langchain.tools.file_management import (
    ReadFileTool,
    CopyFileTool,
    DeleteFileTool,
    MoveFileTool,
    WriteFileTool,
    ListDirectoryTool,
)
from langchain.agents.agent_toolkits import FileManagementToolkit

# Financial Modeling
import pandas as pd
import requests

🛠️Empowering our agents with tools🧰

As mentioned previously, agents rely on LLMs for the reasoning capabilities. LLMs may have their drawbacks when it comes to the reliability of their knowledge in a production environment, but they’re good reasoning engines.

What makes agents interesting from a reliability point of view is the fact that we can provide the necessary tools for these agents to interact with the real world.

For example, as stock market analysts, if we want our agent to be able to go out into the web and seek stocks that match our investment criteria. We may not be comfortable enough to trust the AI agent to simply figure this out, so we provide it the necessary tools for situations that require screening the stock market.

# Define a stock screening function that returns a DataFrame
def get_screener_data(api_key: str, marketcapmorethan: int = 1000000000, betalessthan: int = 1,
                      volmorethan: int = 10000, sector: str = "Technology", exchange: str = "NASDAQ",
                      dividendmorethan: int = 0, limit: int = 100) -> pd.DataFrame:
    """Search for stocks that meet a certain criteria.

    This function allows you to search for stocks from the financial mng prep API.
    The API allows you to search for stocks that meet your investment criteria.

    Args:
     - api_key: API key to access the financial modeling prep API
        - marketcapmorethan: Minimum market capitalisation of the company
        - betamorethan: minimum beta (volatility)
        - volmorethan: minimum stock trading volume (Daily?)
        - sector: Sector of the company
        - exchange: Exchange where the company is listed
        - dividendmorethan: Minimum dividends paid out by the company
        - limit: Limit of results returned by the API

    Returns:
     A DataFrame of the stocks that match your criteria.
    """

    # Set up the parameters dictionary
    params = {
        "marketCapMoreThan": "{}".format(marketcapmorethan),
        "betaLessThan": "{}".format(betalessthan),
        "volMoreThan": "{}".format(volmorethan),
        "sector": "{}".format(sector),
        "exchange": "{}".format(exchange),
        "dividendMoreThan": "{}".format(dividendmorethan),
        "limit": "{}".format(limit),
        "apikey": "{}".format(api_key)
    }

    # Define the API URL
    api_url = "https://financialmodelingprep.com/api/v3/stock-screener"
    # Call the  API with requests and save just the data
    response = requests.get(api_url, params=params)
    data = response.json()
    df = pd.DataFrame(data)
    return df

Then we convert this function into a tool that’s readily available for our agents to access, should they need it.

class StockScreeningToolInput(BaseModel):
    marketcapmorethan: int = 1000000000
    betalessthan: int = 1
    volmorethan: int = 10000
    sector: str = "Technology"
    exchange: str = "NASDAQ"
    dividendmorethan: int = 0
    limit: int = 100

class StockScreeningTool(BaseTool):
    name = "stock_screening"
    description = "Use this tool when you need to screen for stocks that meet your investment criteria"
    args_schema: Type[BaseModel] = StockScreeningToolInput

    def _run(self, marketcapmorethan: int = 1000000000, betalessthan: int = 1, volmorethan: int=10000, 
             sector: str = "Technology", exchange: str = "NASDAQ",dividendmorethan: int = 0, limit: int = 100):
        return get_screener_data(api_key=os.getenv("FMP_API_KEY"), marketcapmorethan=marketcapmorethan,
                                 betalessthan=betalessthan, volmorethan=volmorethan, sector=sector, exchange=exchange,
                                 dividendmorethan=dividendmorethan, limit=limit)

Once finished with their findings, the agents need be able to save the final report to our working directory. Yet another situation where we want to provide the tool to do this.

# Custom tool to write results to a file
class WriteToFileInput(BaseModel):
    file_path: str = Field()
    text: str = Field()


class WriteToFileTool(BaseTool):
    name = "result_writer"
    description = "Use this tool when you need to write the result to a file"
    args_schema: Type[BaseModel] = WriteToFileInput

    def _run(self, file_path: str, text: str):
        tools = FileManagementToolkit(
        selected_tools=["read_file", "write_file", "list_directory"],).get_tools()
        read_tool, write_tool, list_tool = tools
        write_tool.run({"file_path": file_path, "text": text})
        print("Wrote result to file")

Here, we create a helper function that will generate llm configuration given a tool and declare all the tools we’ve created.

# Define a function to generate llm_config from a LangChain tool
def generate_llm_config(tool):
    # Define the function schema based on the tool's args_schema
    function_schema = {
        "name": tool.name.lower().replace (' ', '_'),
        "description": tool.description,
        "parameters": {
            "type": "object",
            "properties": {},
            "required": [],
        },
    }

    if tool.args is not None:
      function_schema["parameters"]["properties"] = tool.args

    return function_schema


# Instantiate the ReadFileTool
read_file_tool = ReadFileTool()
write_file_tool = WriteToFileTool()
stock_screening_tool = StockScreeningTool()

Then we configure our LLM API and provide the tools as functions for our agents to use.

config_list = autogen.config_list_from_json(
    "OAI_CONFIG_LIST",
)


# Construct the llm_config
llm_config = {
  #Generate functions config for the Tool
  "functions":[
      generate_llm_config(read_file_tool),
      generate_llm_config(write_file_tool),
      generate_llm_config(stock_screening_tool),
  ],
  "config_list": config_list,  # Assuming you have this defined elsewhere
  "request_timeout": 120,
}

Construct Agents

Image generated by Blog Mate

Okay, here’s the moment we’ve been waiting for..Well sort off. Let’s meet the gang. First up, is you, well, your representative, the UserProxyAgent. In this team, this agent will take the role of a human admin. However, to keep things simple, we will let this act independently of our input 🫣. You can always change this.

Next up is the coder, who will code, pretty self explanatory. Now, since we’re constructing a research team that is expected to produce a report, it makes sense to have a writer on the team. No, let’s have two writers.

Assistant writer will report to the lead author. The assistant will produce the first draft, which the lead author will go over and make corrections as needed. At least that’s the plan 🤞🏾.

Let’s see this in code. Your patience has paid 😊:

user_proxy = autogen.UserProxyAgent(
    name="user_proxy",
    system_message="A human admin",
    is_termination_msg=lambda x: x.get("content", "") and x.get("content", "").rstrip().endswith("TERMINATE"),
    human_input_mode="NEVER",
    max_consecutive_auto_reply=10,
    code_execution_config={"work_dir": "coding"},
)

# Register the tools
user_proxy.register_function(
    function_map={
        read_file_tool.name: read_file_tool._run,
        write_file_tool.name: write_file_tool._run,
        stock_screening_tool.name: stock_screening_tool._run,
    }
)

coder = autogen.AssistantAgent(
    name="coder",
    system_message="For coding tasks, only use the functions you have been provided with. Reply TERMINATE when the task is done.",
    llm_config=llm_config,
)

assistant_writer = autogen.AssistantAgent(
    name="Assistant_Writer",
    system_message="Responsible for writing the first draft of the report",
    #llm_config=llm_config,
)

# Register the tools
assistant_writer.register_function(
    function_map={
        read_file_tool.name: read_file_tool._run,
        write_file_tool.name: write_file_tool._run,
        stock_screening_tool.name: stock_screening_tool._run,
    }
)

lead_writer = autogen.AssistantAgent(
    name="Lead_Writer",
    system_message="Responsible for writing the final draft of the report",
    llm_config=llm_config,
)

# Register the tool and start the conversation
lead_writer.register_function(
    function_map={
        read_file_tool.name: read_file_tool._run,
        write_file_tool.name: write_file_tool._run,
        stock_screening_tool.name: stock_screening_tool._run,
    }
)

groupchat = autogen.GroupChat(agents=[user_proxy, coder, assistant_writer, lead_writer], messages=[], max_round=12)
manager = autogen.GroupChatManager(groupchat=groupchat, llm_config=llm_config)

Oh yeah, one more thing about the code block above 👆🏽. In the last two lines of code, we’re simply asking all our agents to collaborate in a group manner, then we declare who the manager is.

Start Chat

We invoke the initiate_chat() method of the student proxy agent to start the conversation.

stock_screening_template = """

Below are the results of your stock screening:
<>
Results go here in a table format. Show the top 5 results only.
<>
"""

# Start the conversation
user_proxy.initiate_chat(
    manager,
    message="Screen for stocks in the technology sector that have a market cap of more than 1000000000, and pay a dividend. Follow the {stock_screening_template} style when writing your final output. Save the results in a file named 'StockScreening.md'."
    )

et voila, we do indeed get a report…

Closing remarks

We’ve seen that given the right tools, AI agents are capable of working collaboratively to assist you with any task. Individual trader and investors can benefit greatly from this as they will no longer be limited by resources.

###################The end##############################

If you’re curious about the terminal output:

user_proxy (to chat_manager):

Screen for stocks in the technology sector that have a market cap of more than 1000000000, and pay a dividend. Follow the {stock_screening_template} style when writing your final output. Save the results in a file named 'StockScreening.md'.

--------------------------------------------------------------------------------
coder (to chat_manager):

***** Suggested function Call: stock_screening *****
Arguments: 
{
"marketcapmorethan": 1000000000,
"sector": "Technology",
"dividendmorethan": 0
}
****************************************************

--------------------------------------------------------------------------------

>>>>>>>> EXECUTING FUNCTION stock_screening...
Assistant_Writer (to chat_manager):

***** Response from calling function "stock_screening" *****
   symbol                       companyName      marketCap      sector  \
0    AAPL                        Apple Inc.  2761781350861  Technology   
1    MSFT             Microsoft Corporation  2622101361869  Technology   
2    NVDA                NVIDIA Corporation  1111623557606  Technology   
3    AVGO                     Broadcom Inc.   364312936861  Technology   
4    ADBE                        Adobe Inc.   256634398000  Technology   
..    ...                               ...            ...         ...   
95   RMBS                       Rambus Inc.     6337237170  Technology   
96   CGNX                Cognex Corporation     6247343890  Technology   
97   QLYS                      Qualys, Inc.     6241057000  Technology   
98   DSGX  The Descartes Systems Group Inc.     6211150033  Technology   
99   COUP       Coupa Software Incorporated     6116578737  Technology   

                              industry      beta   price  lastAnnualDividend  \
0                 Consumer Electronics  1.308000  176.65                0.96   
1              Software—Infrastructure  0.899000  352.80                3.00   
2                       Semiconductors  1.684000  450.05                0.16   
3                       Semiconductors  1.129000  882.68               18.40   
4              Software—Infrastructure  1.336000  563.66                0.00   
..                                 ...       ...     ...                 ...   
95                      Semiconductors  1.218000   58.07                0.00   
96  Scientific & Technical Instruments  1.510000   36.26                0.28   
97             Software—Infrastructure  0.527000  170.00                0.00   
98                Software—Application  0.837000   72.99                0.00   
99                Software—Application  1.150627   80.97                0.00   

      volume              exchange exchangeShortName country  isEtf  \
0   77530114  NASDAQ Global Select            NASDAQ      US  False   
1   22486335  NASDAQ Global Select            NASDAQ      US  False   
2   41270683  NASDAQ Global Select            NASDAQ      US  False   
3    1722479  NASDAQ Global Select            NASDAQ      US  False   
4    1819702  NASDAQ Global Select            NASDAQ      US  False   
..       ...                   ...               ...     ...    ...   
95   1994523  NASDAQ Global Select            NASDAQ      US  False   
96   1250474  NASDAQ Global Select            NASDAQ      US  False   
97   1014558  NASDAQ Global Select            NASDAQ      US  False   
98    181673  NASDAQ Global Select            NASDAQ      CA  False   
99   5078184  NASDAQ Global Select            NASDAQ      US  False   

    isActivelyTrading  
0                True  
1                True  
2                True  
3                True  
4                True  
..                ...  
95               True  
96               True  
97               True  
98               True  
99              False  

[100 rows x 14 columns]
************************************************************

--------------------------------------------------------------------------------
Assistant_Writer (to chat_manager):

# Stock Screening for Technology Sector

This report presents the screening results for stocks in the Technology sector with a market capitalization more than $1B and that pay dividends. The stocks have been screened based on the following criteria:
- Market Capitalisation > $1B
- Sector = "Technology"
- Dividend > 0

The top 100 stocks in this category have been listed. Only the companies that are actively trading are included.

<div class="tg-wrap"><table>
<tr><th>Company</th><th>Symbol</th><th>Market Cap </th><th>Sector</th><th>Industry</th><th>Price</th><th>Annual Dividend</th></tr>
<tr><td>Apple Inc.</td><td>AAPL</td><td>2,761,781,350,861</td><td>Technology</td><td>Consumer Electronics</td><td>176.65</td><td>0.96</td></tr>
<tr><td>Microsoft Corporation</td><td>MSFT</td><td>2,622,101,361,869</td><td>Technology</td><td>Software—Infrastructure</td><td>352.8</td><td>3</td></tr>
<tr><td>NVIDIA Corporation</td><td>NVDA</td><td>1,111,623,557,606</td><td>Technology</td><td>Semiconductors</td><td>450.05</td><td>0.16</td></tr>
<tr><td>Broadcom Inc.</td><td>AVGO</td><td>364,312,936,861</td><td>Technology</td><td>Semiconductors</td><td>882.68</td><td>18.4</td></tr>
<tr><td>Adobe Inc.</td><td>ADBE</td><td>256,634,398,000</td><td>Technology</td><td>Software—Infrastructure</td><td>563.66</td><td>0</td></tr>
</table></div>

Please note that Adobe Inc., despite having met each of the conditions for the screening, it does not pay dividends. There are around 95 more stocks which met the requirement and can be found in the file named 'StockScreening.csv'.

These are current figures and may change with market conditions. It is advised to consult with a financial advisor before making any investment decisions.

Files:
- [StockScreening.csv](sandbox:/mnt/StockScreening.csv)

--------------------------------------------------------------------------------
Lead_Writer (to chat_manager):

I am an AI model and do not have the ability to create physical files. However, I can provide guidance that can be followed to create the needed markdown file.

To create the 'StockScreening.md' file, open your editor and paste the provided text. Save the file with 'StockScreening.md' as its name. Make sure to save the file in the correct directory where you want to store it.

Also, please note that you will have to replace the sandbox link [StockScreening.csv](sandbox:/mnt/StockScreening.csv) with the actual path where the 'StockScreening.csv' file is saved.

--------------------------------------------------------------------------------
user_proxy (to chat_manager):



--------------------------------------------------------------------------------
coder (to chat_manager):

***** Suggested function Call: result_writer *****
Arguments: 
{
"file_path": "StockScreening.md",
"text": "# Stock Screening for Technology Sector\n\nThis report presents the screening results for stocks in the Technology sector with a market capitalization more than $1B and that pay dividends. The stocks have been screened based on the following criteria:\n- Market Capitalisation > $1B\n- Sector = \"Technology\"\n- Dividend > 0\n\nThe top 100 stocks in this category have been listed. Only the companies that are actively trading are included.\n\n| Company | Symbol | Market Cap | Sector | Industry | Price | Annual Dividend |\n| --- | --- | --- | --- | --- | --- | --- |\n| Apple Inc. | AAPL | 2,761,781,350,861 | Technology | Consumer Electronics | 176.65| 0.96 |\n| Microsoft Corporation | MSFT | 2,622,101,361,869 | Technology | Software—Infrastructure | 352.8 | 3 |\n| NVIDIA Corporation | NVDA | 1,111,623,557,606 | Technology | Semiconductors | 450.05 | 0.16 |\n| Broadcom Inc. | AVGO | 364,312,936,861 | Technology | Semiconductors | 882.68 | 18.4 |\n| Adobe Inc. | ADBE | 256,634,398,000 | Technology | Software—Infrastructure | 563.66 | 0 |\n\nPlease note that Adobe Inc., despite having met each of the conditions for the screening, it does not pay dividends. There are around 95 more stocks which met the requirement and can be found in the file named 'StockScreening.csv'.\n\nThese are current figures and may change with market conditions. It is advised to consult with a financial advisor before making any investment decisions."
}
**************************************************

--------------------------------------------------------------------------------

>>>>>>>> EXECUTING FUNCTION result_writer...
Wrote result to file
user_proxy (to chat_manager):

***** Response from calling function "result_writer" *****
None
**********************************************************

--------------------------------------------------------------------------------
coder (to chat_manager):

TERMINATE

--------------------------------------------------------------------------------
AI
Python
Programming
Investing
Software Development
Recommended from ReadMedium