We are building a stock briefing agent that takes a list of ticker symbols, fetches live price data through a Python tool, and returns a concise market summary. This is a practical template for anyone automating research workflows or monitoring portfolios with LLMs. Because the agent runs multiple tool calls inside a single conversation loop, context length grows quickly, which is where Oxlo.ai's request-based pricing becomes useful compared to token-based billing. You can explore the predictable costs at https://oxlo.ai/pricing.
What you'll need
- Python 3.10 or newer
- The OpenAI SDK and yfinance:
pip install openai yfinance - An Oxlo.ai API key from https://portal.oxlo.ai
1. Bootstrap the Oxlo.ai client
I always start by verifying the endpoint with a simple ping. This confirms that my API key and the Oxlo.ai base URL are wired correctly.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Say hello and confirm you are ready."},
],
)
print(response.choices[0].message.content)
2. Define the system prompt and tool schema
The agent needs a clear directive and a structured tool it can call. I keep the system prompt explicit about output format and step order.
SYSTEM_PROMPT = """You are a financial briefing agent. Your job is to:
1. Accept a list of stock ticker symbols.
2. Call the get_stock_snapshot tool for each ticker.
3. Wait for the results.
4. Write a concise markdown briefing with current price, daily change, and a one-sentence outlook per ticker.
Only call the tool once per ticker. Do not invent data."""
TOOLS = [
{
"type": "function",
"function": {
"name": "get_stock_snapshot",
"description": "Fetch the current stock price and daily change percent for a given ticker.",
"parameters": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "Stock ticker symbol, e.g. AAPL"
}
},
"required": ["ticker"]
}
}
}
]
3. Implement the data fetcher
The tool itself uses yfinance to pull the last two closing prices so we can calculate a daily change. Keeping this deterministic prevents the LLM from hallucinating numbers.
import yfinance as yf
import json
def get_stock_snapshot(ticker: str):
stock = yf.Ticker(ticker)
hist = stock.history(period="2d")
if hist.empty:
return {"ticker": ticker, "error": "No data found."}
current = hist["Close"].iloc[-1]
previous = hist["Close"].iloc[-2]
change_pct = round(((current - previous) / previous) * 100, 2)
return {
"ticker": ticker,
"price": round(current, 2),
"change_percent": change_pct
}
4. Wire the agent loop
Now I connect the LLM to the tool. The loop sends the conversation to Oxlo.ai, checks for a tool_calls payload, executes any requested functions, and appends the results back to the context window.
def run_agent(tickers):
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Provide a briefing for: {', '.join(tickers)}."},
]
while True:
response = client.chat.completions.create(
model="qwen-3-32b",
messages=messages,
tools=TOOLS,
tool_choice="auto",
)
message = response.choices[0].message
if message.tool_calls:
messages.append({
"role": "assistant",
"content": message.content or "",
"tool_calls": [tc.model_dump() for tc in message.tool_calls]
})
for tc in message.tool_calls:
if tc.function.name == "get_stock_snapshot":
args = json.loads(tc.function.arguments)
result = get_stock_snapshot(args["ticker"])
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"name": tc.function.name,
"content": json.dumps(result),
})
continue
return message.content
5. Execute and review
Running the agent on a small watchlist demonstrates the full loop in action.
if __name__ == "__main__":
tickers = ["AAPL", "TSLA", "NVDA"]
briefing = run_agent(tickers)
print(briefing)
Run it
When I run the script, the agent typically produces output similar to this:
## Stock Briefing
**AAPL**
- Price: $223.45
- Change: +1.23%
- Outlook: Momentum remains steady ahead of earnings.
**TSLA**
- Price: $245.60
- Change: -0.85%
- Outlook: Slight pullback after last week's rally.
**NVDA**
- Price: $135.20
- Change: +2.10%
- Outlook: Strong buying interest continues in semiconductor space.
Because Oxlo.ai bills per request rather than per token, the cost of this multi-turn loop is predictable even when the context window grows with tool results. For teams running dozens of these agents on long schedules, that stability matters. See https://oxlo.ai/pricing for plan details.
Next steps
Swap yfinance for an internal database or REST API to turn this into a private research assistant. You can also add a second tool that fetches recent news headlines and instruct the model to correlate sentiment with price action, which pushes the agent into richer multi-tool planning territory.
Top comments (0)