From 3c519ee924b98c78d5754aec46a31ef451d68e64 Mon Sep 17 00:00:00 2001 From: mmistroni Date: Sat, 14 Jun 2025 17:56:49 +0100 Subject: [PATCH] langchain agent using openbb functions (#7131) * langchain agent using openbb functions A simple notebook that shows how to leverage openbb functions by building an agent via langchain * added prompt for strong buy in utilities * added prompt for strong buy in utilities * Simplified notebook --------- Co-authored-by: Danglewood <85772166+deeleeramone@users.noreply.github.com> Co-authored-by: Theodore Aptekarev --- examples/openbb_vs_langchain.ipynb | 384 +++++++++++++++++++++++++++++ 1 file changed, 384 insertions(+) create mode 100644 examples/openbb_vs_langchain.ipynb diff --git a/examples/openbb_vs_langchain.ipynb b/examples/openbb_vs_langchain.ipynb new file mode 100644 index 00000000000..5578fdec1ed --- /dev/null +++ b/examples/openbb_vs_langchain.ipynb @@ -0,0 +1,384 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "0PSrErguxVcn" + }, + "source": [ + "### Brief Description\n", + "\n", + "This notebook shows few examples on how to leverage OpenBB functionality via an Agent built with Langchain.\n", + "It requires the user to have\n", + "- an OpenAI Key. This is required as OpenAI is used as LLM\n", + "\n", + "\n", + "For help on how to configure Colab Secrets, please refer to this article\n", + "https://margaretmz.medium.com/use-colab-secrets-to-store-kaggle-api-key-b57c7464f9fa\n", + "\n", + "This work was inspired by examples from this repo https://github.com/AlgoTrading101/Magentic-AlgoTrading101\n", + "\n", + "Functionality shown in this notebook is purely an example of what can be done.\n", + "\n", + "### Author\n", + "Marco Mistroni" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Installing dependencies" + ], + "metadata": { + "id": "pw_DrWCXVijQ" + } + }, + { + "cell_type": "code", + "source": [ + "!pip install openbb\n", + "!pip install openbb-yfinance\n", + "!pip install openbb-finviz\n", + "!pip install langchain\n", + "!pip install langchain_core\n", + "!pip install langchain_openai\n" + ], + "metadata": { + "id": "sNhm_dyvVlI1" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### Getting keys" + ], + "metadata": { + "id": "Ua8Hmj2lWI6R" + } + }, + { + "cell_type": "code", + "source": [ + "from google.colab import userdata\n", + "OPENAI_KEY = userdata.get('OPENAI_KEY')" + ], + "metadata": { + "id": "fTUdeQpdWL81" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "TEBnI33fxVcr" + }, + "outputs": [], + "source": [ + "import os\n", + "from openbb import obb\n", + "from langchain_openai import ChatOpenAI\n", + "import logging\n", + "from langchain.agents import tool\n", + "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", + "\n", + "llm = ChatOpenAI(model=\"gpt-4.1\", temperature=0, openai_api_key=OPENAI_KEY)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6zNCxSmOxVct" + }, + "source": [ + "### OpenBB Useful functions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "28N-T7aIxVcu" + }, + "outputs": [], + "source": [ + "@tool\n", + "def get_industry_performance() -> list:\n", + " \"\"\" Return performance by industry for last week, last month, last quarter, last half year and last year\"\"\"\n", + " return obb.equity.compare.groups(group='industry', metric='performance').to_llm()\n", + "\n", + "@tool\n", + "def get_strong_buy_for_sector(sector : str) -> list :\n", + " \"\"\" Return the strong buy recommendation for a given sector\"\"\"\n", + " new_sector = '_'.join(sector.lower().split()).lower()\n", + " data = obb.equity.screener(provider='finviz', sector=new_sector, recommendation='buy')\n", + " return data.to_llm()\n", + "\n", + "@tool\n", + "def get_strong_buy_for_industry(industry : str) -> list :\n", + " \"\"\" Return the strong buy recommendation for a given industry\"\"\"\n", + " data = obb.equity.screener(provider='finviz', industry=industry, recommendation='buy')\n", + " return data.to_llm()\n", + "\n", + "@tool\n", + "def get_best_stock_performers_for_sector(sector:str) -> list :\n", + " \"\"\" Return the best 5 stock performers for last week and last month for a given sector\"\"\"\n", + " data = obb.equity.screener(provider='finviz', filters_dict={'Sector' : sector, 'Performance' : 'Week Up', 'Performance 2' : 'Month Up'}, limit=5)\n", + " return data.to_llm()\n", + "\n", + "@tool\n", + "def get_best_stock_performers_for_industry(industry:str) -> list :\n", + " \"\"\" Return the best 5 stock performers for last week and last month for an industry\"\"\"\n", + " data = obb.equity.screener(provider='finviz', filters_dict={'Industry' : industry, 'Performance' : 'Week Up', 'Performance 2' : 'Month Up'}, limit=3)\n", + " return data.to_llm()\n", + "\n", + "@tool\n", + "def get_candidate_stocks_to_invest_relaxed(industry:str) -> list:\n", + " ''' Use relaxed criteria to find best companies in an industry which are worth investing into'''\n", + " desc_filters = {\n", + " 'Market Cap.': '+Small (over $300mln)',\n", + " 'Average Volume': 'Over 200K',\n", + " }\n", + " fund_filters = {\n", + " 'InstitutionalOwnership': 'Under 60%',\n", + " 'Current Ratio' : 'Over 1.5',\n", + " 'Debt/Equity' : 'Over 0.3',\n", + " #'EPS growthnext 5 years' : 'Positive (>0%)',\n", + " }\n", + "\n", + " desc_filters.update(fund_filters)\n", + "\n", + " try:\n", + " data = obb.equity.screener(provider='finviz', industry='semiconductors',\n", + " filters_dict=desc_filters\n", + " )\n", + " return data.to_llm()\n", + " except Exception as e:\n", + " logging.info(f'No data found:{str(e)}')\n", + " return []\n", + "\n", + "\n", + "@tool\n", + "def get_valuation_for_industries(input:str) -> list:\n", + " \"\"\" Return valuation metrics for the industry provided as input\"\"\"\n", + " data = obb.equity.compare.groups(group='industry', metric='valuation', provider='finviz').to_df()\n", + " filtered = data[data.name == input]\n", + " return filtered.to_json(\n", + " orient=\"records\",\n", + " date_format=\"iso\",\n", + " date_unit=\"s\",\n", + " )\n", + "\n", + "@tool\n", + "def get_consensus(ticker:str) -> list:\n", + " \"\"\" Return analyst consensus for the ticker provided\n", + " It returns the following fields:\n", + " - target_high: float, High target of the price target consensus.\n", + " - target_low: float Low target of the price target consensus.\n", + " - target_consensus: float Consensus target of the price target consensus.\n", + " - target_median: float Median target of the price target consensus\n", + "\n", + "\n", + " \"\"\"\n", + " data = obb.equity.estimates.consensus(symbol=ticker, limit=3, provider='yfinance').to_df()\n", + " return data.to_json(\n", + " orient=\"records\",\n", + " date_format=\"iso\",\n", + " date_unit=\"s\",\n", + " )\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1hXQnSGFxVcv" + }, + "source": [ + "### Chat Memory" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "YBF6S6DxxVcv" + }, + "outputs": [], + "source": [ + "from langchain_core.prompts import MessagesPlaceholder\n", + "from langchain.memory import ConversationTokenBufferMemory\n", + "from langchain.agents.format_scratchpad.openai_tools import (\n", + " format_to_openai_tool_messages,\n", + ")\n", + "from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser\n", + "from langchain_core.output_parsers import StrOutputParser, CommaSeparatedListOutputParser\n", + "from langchain.agents import AgentExecutor\n", + "from langchain_core.messages import AIMessage, HumanMessage\n", + "\n", + "MEMORY_KEY = \"chat_history\"\n", + "prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " \"\"\" You are very powerful stock financial researcher.\n", + " You will take the user questions and answer using the tools available.\n", + " Once you have the information you need, you will answer user's questions using the data returned.\n", + " Use the following tools to answer user queries:\n", + " - get_strong_buy_for_sector to find strong buy recommendations for a sector\n", + " - get_strong_buy_for_industry to find strong buy recommendations for an industry\n", + " - get_industry_performance to find the performance for an industry\n", + " - get_valuation_for_industries to find valuation metrics for industries\n", + " - get_candidate_stocks_to_invest_relaxed to fetch all companies using relaxed criteria\n", + " - def get_consensus(ticker:str) - to find analyst consensus for a company\n", + " You should call each function only once, and you should not call the function if you already have the information you need.\n", + " \"\"\",\n", + " ),\n", + " MessagesPlaceholder(variable_name=MEMORY_KEY),\n", + " (\"user\", \"{input}\"),\n", + " MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1yrlv9jUxVcw" + }, + "outputs": [], + "source": [ + "tools = [get_industry_performance, get_strong_buy_for_sector, get_strong_buy_for_industry, get_best_stock_performers_for_industry, get_valuation_for_industries,\n", + " get_candidate_stocks_to_invest_relaxed, get_consensus]\n", + "llm_with_tools = llm.bind_tools(tools)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ToVLUWZfxVcw" + }, + "outputs": [], + "source": [ + "chat_history = []\n", + "chat_history.append(HumanMessage(content=\"Your question here\"))\n", + "chat_history.append(AIMessage(content=\"AI response here\"))\n", + "memory = ConversationTokenBufferMemory(\n", + " llm=llm, # Required for token counting\n", + " max_token_limit=16000, # Leave buffer for functions + responses\n", + " memory_key=\"chat_history\", # Must match your prompt's key\n", + " return_messages=True\n", + ")\n", + "\n", + "agent = (\n", + " {\n", + " \"input\": lambda x: x[\"input\"],\n", + " \"agent_scratchpad\": lambda x: format_to_openai_tool_messages(\n", + " x[\"intermediate_steps\"]\n", + " ),\n", + " \"chat_history\": lambda x: memory.load_memory_variables(x)[\"chat_history\"],\n", + " }\n", + " | prompt\n", + " | llm_with_tools\n", + " | OpenAIToolsAgentOutputParser()\n", + ")\n", + "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kbFXeyMUxVcy" + }, + "source": [ + "### Let's try a chain of thought approach. We start with the industry with best performance.\n", + "Then find the best performing company and check some metrics" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MlAvYHU0xVcy" + }, + "outputs": [], + "source": [ + "input1 = '''\n", + "First, find an industry that has consistently shown positive performance across quarterly, monthly, and weekly timeframes.\n", + "Second, once you have identified the industry, extract its relevant valuation metrics (e.g., P/E, P/B, EV/EBITDA).\n", + "Third, extract companies from the selected industry using relaxed criteria.\n", + "Fourth, for the best performing companies get the analyst consensus\n", + "Finally, summarize your findings in no more than 80 words detailing:\n", + "- Best performing industry\n", + "- Best performing companies in industry\n", + "- A table displaying the analyst consensus for each of the companies you found at previous step'''\n", + "result = agent_executor.invoke({\"input\": input1, \"chat_history\": chat_history})\n", + "print(result['output'])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "au-Qbu9KxVcy" + }, + "source": [ + "### Finding strong buys in the Utilities sector " + ] + }, + { + "cell_type": "code", + "source": [ + "input1 = '''\n", + "First, find the stocks recommended fro strong buy in the Utilities Sector\n", + "Second, find the valuation metrics for this stock.\n", + "Third, summarize your findings in a short paragraph.\n", + "'''\n", + "result = agent_executor.invoke({\"input\": input1, \"chat_history\": chat_history})\n", + "print(result['output'])" + ], + "metadata": { + "id": "doUw61HPU6fR" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [], + "metadata": { + "id": "rc7X4Ep4VNbS" + }, + "execution_count": null, + "outputs": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "myenv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.1" + }, + "colab": { + "provenance": [] + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file