Introduction
In my previous post, I explored the creation of an AI-powered stock analysis agent using LangChain, OpenBB, and Anthropic’s Claude 3 Opus. By leveraging these cutting-edge technologies, I laid the foundation for a powerful tool that empowers traders and investors like myself with valuable insights and data-driven recommendations.
As I continue enhancing my AI agent, I realize the importance of incorporating fundamental and additional technical analysis techniques. Fundamental analysis helps evaluate a company’s intrinsic value by examining its financial health, management, and competitive advantages. In contrast, technical analysis focuses on historical price and volume data to identify trends and patterns. Combining these two approaches can provide a more comprehensive and well-rounded assessment of a stock’s potential.
In this post, I’ll expand my AI agent’s capabilities by integrating three new tools: two for fundamental analysis and one for technical analysis. These additions will further strengthen the agent’s ability to deliver accurate and actionable insights. The GitHub repo has been updated if you’re following along.
Fundamental Analysis Tools
- Get Key Metrics: Key metrics are vital in assessing a company’s financial performance and growth potential. The
get_key_metrics
tool allows the AI agent to retrieve essential metrics such as revenue per share, net income per share, operating cash flow per share, and free cash flow per share. These metrics provide valuable insights into the company’s financial health and profitability. They help us understand how efficiently the company generates revenue, profits, and cash flows relative to its outstanding shares.
@tool(args_schema=StockStatsInput)
def get_key_metrics(symbol: str) -> str:
"""Fetch a Stock's Key Metrics by Symbol."""
try:
metrics = obb.equity.fundamental.metrics(symbol=symbol).to_df()[::-1]
if metrics.empty:
return f"\n<observation>\nNo data found for the given symbol {symbol}</observation>\n"
return wrap_dataframe(metrics)
except Exception as e:
return f"\n<observation>\nError: {e}</observation>\n"
Integrating the get_key_metrics
tool into the AI agent is straightforward. I define the tool using the @tool
decorator and specify the input schema from last time. The tool fetches the key metrics using OpenBB’s obb.equity.fundamental.metrics
function and returns the data as a formatted string.
- Get Stock Ratios: Stock ratios provide valuable insights into a company’s valuation, profitability, and efficiency. The
get_stock_ratios
tool enables my AI agent to access crucial ratios such as price-to-earnings (P/E), price-to-sales (P/S), and debt-to-equity. By comparing these ratios to industry averages and historical values, the agent can assess whether a stock is overvalued, undervalued, or reasonably priced.
@tool(args_schema=StockStatsInput)
def get_stock_ratios(symbol: str) -> str:
"""Fetch a Stock's Ratios by Symbol."""
try:
trades = obb.equity.fundamental.ratios(symbol=symbol).to_df()
if trades.empty:
return f"\n<observation>\nNo data found for the given symbol {symbol}</observation>\n"
return wrap_dataframe(trades)
except Exception as e:
return f"\n<observation>\nError: {e}</observation>\n"
Implementing the get_stock_ratios
tool follows a similar approach to the get_key_metrics
tool. I define the tool using the @tool
decorator and specify the input schema. The tool retrieves the stock ratios using OpenBB’s obb.equity.fundamental.ratios
function and returns the data as a formatted string.
I have introduced a new function, wrap_dataframe
, above, so let’s discuss that. The wrap_dataframe
function is a utility function that takes a pandas DataFrame (df
) as input and returns a formatted string representation of the DataFrame wrapped in <observation>
tags. This function is useful when passing DataFrame data to the Claude 3 LLM family as part of an observation or response.
def wrap_dataframe(df: pd.DataFrame) -> str:
df_string = df.to_markdown(index=False)
return f"<observation>\n\n{df_string}\n\n</observation>"
Enhancing Technical Analysis Tools
I’ve incorporated more advanced technical indicators to provide a comprehensive analysis of a stock’s price action, volatility, and trends using the pandas-ta library. The 52-week high and low prices, calculated using a 252-day rolling window, provide the stock’s performance context. The Average True Range (ATR) measures an asset’s volatility by considering the high, low, and closing prices over a specified period, helping traders set appropriate stop-loss levels or profit targets. The Average Daily Range (ADR) indicator also gauges the stock’s intraday volatility and potential for price movements, which are closely related to the ATR. R multiples can be used for this, too, which will be explored in a future post. The Relative Strength Index (RSI) identifies overbought or oversold market conditions by comparing the magnitude of recent gains to recent losses, with values above 70 indicating overbought conditions and values below 30 suggesting oversold conditions.
The 52-week high and low prices provide context for the current price action, helping traders understand the longer-term trend and potential support or resistance levels. The ADR
calculates the average daily price range over a 20-day period, while the ADR_PCT
expresses this range as a percentage of the closing price, allowing traders to assess the typical daily volatility of an asset. Due to heightened risk, traders may use the average daily range to avoid trading assets with unusually high ADR_PCT
. These enhanced technical indicators enable the AI agent to perform a more in-depth analysis of a stock’s technical volatility metrics, contributing to a comprehensive assessment of its potential when combined with other fundamental and technical tools.
def add_technicals(df):
df["pct_change"] = df["close"].pct_change() * 100
df["SMA_20"] = ta.sma(df["close"], length=20)
df["SMA_50"] = ta.sma(df["close"], length=50)
df["SMA_150"] = ta.sma(df["close"], length=150)
df["SMA_200"] = ta.sma(df["close"], length=200)
df["ATR"] = ta.atr(df["high"], df["low"], df["close"], length=14)
df["RSI"] = ta.rsi(df["close"], length=14)
daily_range = df["high"] - df["low"]
adr = daily_range.rolling(window=20).mean()
df["ADR"] = (adr / df["close"]) * 100
df["ADR_PCT"] = df["ADR"].fillna(0)
return df
Updating the AI Agent’s Prompt
With the addition of the new fundamental and technical analysis tools, I need to update the AI agent’s prompt template to incorporate them into its decision-making process. The updated prompt template includes specific instructions on when and how to use these tools, ensuring that the agent considers fundamental and technical factors when analyzing a stock.
HUMAN_TEMPLATE = """
You are a large language model with specialized training as a financial advisor
with advanced knowledge of trading, investing, quantitative finance, technical analysis, and fundamental analysis.
You should never perform any math on your own, but rather use the tools available to you to perform all calculations.
CRITERIA FOR BULLISH SETUPS:
----------------------------
You will find below the criteria to use for classification of bullish setups in the stock market. Any trading setups should
be based off the daily timeframe and the most recent data.
Rules for bullish setups based on the stock's most recent closing price:
1. Stock's closing price is greater than its 20 SMA.
2. Stock's closing price is greater than its 50 SMA.
3. Stock's closing price is greater than its 200 SMA.
4. Stock's 50 SMA is greater than its 150 SMA.
5. Stock's 150 SMA is greater than its 200 SMA.
6. Stock's 200 SMA is trending up for at least 1 month.
7. Stock's closing price is at least 30 percent above 52-week low.
8. Stock's closing price is within 25 percent of its 52-week high.
9. Stock's 30-day average volume is greater than 750K.
10. Stock's ADR percent is less than 5 percent and greater than 1 percent.
PREPROCESSING:
--------------
Before processing the query, you will preprocess it as follows:
1. Correct any spelling errors using a spell checker or fuzzy matching technique.
2. If the stock symbol or company name is a partial match, find the closest matching stock symbol or company name.
TOOLS:
------
You have access to the following tools:
{tools}
When accessing your tools, please use as many tools as necessary to provide the most accurate and relevant information.
In order to use a tool, you can use <tool></tool> and <tool_input></tool_input> tags. You will then get back a response in the form <observation></observation>
For example, if you have a tool called 'search' that could run a google search, in order to search for the weather in SF you would respond:
<tool>search</tool>
<tool_input>weather in SF</tool_input>
<observation>64 degrees</observation>
When you are done, respond with a final answer between <final_answer></final_answer>.
For example:
<final_answer>The weather in SF is 64 degrees</final_answer>
You must always close the tags with </tool>, </tool_input>, </observation>, or </final_answer> respectively for the parser to work correctly.
Begin!
Previous Conversation:
{chat_history}
Question: {input}
{agent_scratchpad}"""
My enhanced AI agent is in action by analyzing a specific stock, such as Microsoft (MSFT). By leveraging the get_key_metrics
, get_stock_ratios
, and the enhanced technical analysis, the agent can comprehensively assess Apple’s financial health, valuation, and technical trends. This multi-dimensional analysis enables the agent to deliver more accurate and nuanced insights, empowering traders and investors like myself to make informed decisions. I got a weird text/number collision in the text output.
Conclusion
I expanded the AI stock analysis agent’s capabilities in this post by integrating two fundamental analysis tools (get_key_metrics
and get_stock_ratios
) and enhanced technical analysis. Combining multiple analysis techniques can improve the agent’s ability to provide a holistic and well-rounded assessment of a stock’s potential.
As I continue to refine and optimize this AI agent, one must remember that more than one analysis method is required. By leveraging the strengths of both fundamental and technical analysis, one can make more informed and data-driven investment decisions. Still, there is no substitute for human hours spent in the decision-making chair.
I encourage you to experiment with the enhanced AI agent. Stay tuned for the next post in this series, where I’ll explore further enhancements and optimizations to take my AI stock analysis agent to the next level as I continue swapping symbols.