Introduction
In the world of stock trading, investors rely on various tools and methods to make informed decisions. Fundamental analysis is a common approach that provides actionable insights by assessing a company’s financial health and stock performance. With advancements in artificial intelligence and machine learning, stock analysis can now be highly automated. In this article, we will explore how to create a stock performance analysis Agent using LangChain, LangGraph, and Yahoo Finance, leveraging real-time stock data and key technical indicators.
Whether you’re a financial enthusiast, a developer, or a data scientist, this step-by-step tutorial will help you create your own intelligent agent. Let’s dive in!
What Will This Autonomous Financial Analyst Agent Do?
-
Fetch stock price data using Yahoo Finance. -
Calculate technical indicators such as RSI, MACD, VWAP, etc.. -
Evaluate financial metrics like P/E ratio, debt-to-equity ratio, and profit margins. -
Utilize OpenAI’s powerful language model to provide structured, AI-based analysis.
Tools We Will Use
-
LangGraph: A library for orchestrating tools and building conversational agents. -
OpenAI GPT-4: Used to generate intelligent and structured financial insights. -
yfinance: For retrieving stock prices and financial ratios. -
ta (Technical Analysis Library): For calculating key technical indicators. -
Python Libraries: pandas, dotenv, and datetime for data manipulation and environment setup.
Step 1: Set Up the Environment
First, install the required libraries:
pip install -U langgraph langchain langchain_openai pandas ta python-dotenv yfinance
Set up a <span>.env</span>
file to securely store your OpenAI API key:
OPENAI_API_KEY=your_openai_api_key
Step 2: Prepare Tools for the Analyst
Fetch Stock Prices
This tool fetches historical data for a stock and calculates several technical indicators.
from typing import Union, Dict, Set, List, TypedDict, Annotated
import pandas as pd
from langchain_core.tools import tool
import yfinance as yf
from ta.momentum import RSIIndicator, StochasticOscillator
from ta.trend import SMAIndicator, EMAIndicator, MACD
from ta.volume import volume_weighted_average_price
import datetime as dt
@tool
def get_stock_prices(ticker: str) -> Union[Dict, str]:
"""
Fetch historical price data and all technical indicators for a given stock.
"""
try:
data = yf.download(
ticker,
start=dt.datetime.now() - dt.timedelta(weeks=24*3),
end=dt.datetime.now(),
interval='1wk'
)
df = data.copy()
data.reset_index(inplace=True)
data.Date = data.Date.astype(str)
indicators = {}
rsi_series = RSIIndicator(df['Close'], window=14).rsi().iloc[-12:]
indicators["RSI"] = {date.strftime('%Y-%m-%d'): int(value) for date, value in rsi_series.dropna().to_dict().items()}
sto_series = StochasticOscillator(df['High'], df['Low'], df['Close'], window=14).stoch().iloc[-12:]
indicators["Stochastic_Oscillator"] = {date.strftime('%Y-%m-%d'): int(value) for date, value in sto_series.dropna().to_dict().items()}
macd = MACD(df['Close'])
macd_series = macd.macd().iloc[-12:]
indicators["MACD"] = {date.strftime('%Y-%m-%d'): int(value) for date, value in macd_series.to_dict().items()}
macd_signal_series = macd.macd_signal().iloc[-12:]
indicators["MACD_Signal"] = {date.strftime('%Y-%m-%d'): int(value) for date, value in macd_signal_series.to_dict().items()}
vwap_series = volume_weighted_average_price(
high=df['High'],
low=df['Low'],
close=df['Close'],
volume=df['Volume'],
).iloc[-12:]
indicators["vwap"] = {date.strftime('%Y-%m-%d'): int(value) for date, value in vwap_series.to_dict().items()}
return {'stock_price': data.to_dict(orient='records'), 'indicators': indicators}
except Exception as e:
return f"Error fetching price data: {str(e)}"
Fetch Financial Metrics
This tool retrieves key financial health ratios.
@tool
def get_financial_metrics(ticker: str) -> Union[Dict, str]:
"""
Fetch key financial ratios for a given stock.
"""
try:
stock = yf.Ticker(ticker)
info = stock.info
return {
'pe_ratio': info.get('forwardPE'),
'price_to_book': info.get('priceToBook'),
'debt_to_equity': info.get('debtToEquity'),
'profit_margins': info.get('profitMargins')
}
except Exception as e:
return f"Error fetching ratios: {str(e)}"
Step 3: Build LangGraph
LangGraph allows us to efficiently orchestrate tools and manage conversational logic.
1. Define the Graph
We first define a StateGraph to manage the workflow:
from langgraph.graph import StateGraph, START, END
from langchain_core.messages import SystemMessage
from langchain_core.messages import HumanMessage
from langchain_core.messages import AIMessage
class State(TypedDict):
messages: List
stock: str
graph_builder = StateGraph(State)
2. Define OpenAI and Bind Tools
We will integrate the tools into LangGraph and create a feedback loop for analysis.
import dotenv
dotenv.load_dotenv()
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model='gpt-4')
tools = [get_stock_prices, get_financial_metrics]
llm_with_tools = llm.bind_tools(tools)
3. Analyst Node
The prompt ensures AI understands its role and provides structured output.
FUNDAMENTAL_ANALYST_PROMPT = """ You are a fundamental analyst specializing in evaluating companies (with stock code {company}).
You have access to the following tools:
Medium Q Search
### Your Task:
1. ** Input Stock Code **: Use the provided stock code to query tools and fetch data.
2. ** Analyze Data **: Evaluate the results returned by the tools and identify potential trends.
3. ** Provide Summary **: Write a concise, well-structured summary highlighting:
- Recent stock price trends, patterns, and potential resistance levels.
- Key insights from technical indicators (e.g., whether the stock is overbought or oversold).
- Financial health and performance based on financial metrics.
### Constraints:
- Only use data provided by the tools.
- Avoid speculative language; focus on observable data and trends.
- If any tool fails to provide data, clearly state this in the summary.
### Output Format:
Respond in the following format: "stock": "<stock_code>",
"price_analysis": "<detailed analysis of stock price trends>",
"technical_analysis": "<detailed time series analysis of all technical indicators>",
"financial_analysis": "<detailed analysis of financial metrics>",
"final Summary": "<complete conclusion based on the above analysis>"
Please ensure your response is objective, concise, and actionable.
"""
def fundamental_analyst(state: State):
messages = [
SystemMessage(content=FUNDAMENTAL_ANALYST_PROMPT.format(company=state['stock'])),
HumanMessage(content="Please analyze this stock.")
] + state['messages']
return {'messages': llm_with_tools.invoke(messages)}
4. Add Tools to the Graph and Compile
graph_builder.add_node('fundamental_analyst', fundamental_analyst)
graph_builder.add_edge(START, 'fundamental_analyst')
graph_builder.add_node(ToolNode(tools))
graph_builder.add_conditional_edges('fundamental_analyst', tools_condition)
graph_builder.add_edge('tools', 'fundamental_analyst')
graph = graph_builder.compile()
5. Execute the Graph
events = graph.stream({'messages': [('user', 'Should I buy this stock?')], 'stock': 'TSLA'}, stream_mode='values')
for event in events:
if 'messages' in event:
event['messages'][-1].pretty_print()
Example Output
{
"stock": "TSLA",
"price_analysis": "Recent stock price of TSLA shows volatility,...",
"technical_analysis": "Technical indicators present a mixed outlook. RSI indicates overbought condition,...",
"financial_analysis": "TSLA's financial metrics indicate high valuation,...",
"final Summary": "In conclusion, TSLA shows strong recent price recovery potential but also has high valuation and overbought indicators,...",
"Asked Question Answer": "Given the current overbought indicators and high valuation,..."
}
Full code can be found in the community: