We are going to build a stock research agent that fetches mock price and sentiment data, then reasons about whether to buy, sell, or hold a given ticker. This is a compact example of an agentic workload: the model plans which tools to call, processes the results, and synthesizes a structured decision without human intervention. Because the agent may loop several times and pass large JSON contexts back and forth, running it on Oxlo.ai keeps costs predictable with flat per-request pricing instead of ballooning token counts.
What you'll need
- Python 3.10 or newer
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK installed with
pip install openai
Step 1: Set up the Oxlo.ai client and mock tools
I start by pointing the OpenAI SDK at Oxlo.ai's base URL and defining two mock functions that stand in for real market data APIs. Keeping them deterministic makes the tutorial easy to run without extra API keys.
import json
from openai import OpenAI
# Point the OpenAI SDK at Oxlo.ai
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
# Stand-ins for real market data APIs
def get_stock_price(symbol: str) -> dict:
prices = {"AAPL": 182.50, "TSLA": 175.20, "NVDA": 890.10}
return {"symbol": symbol.upper(), "price": prices.get(symbol.upper(), 100.0)}
def get_news_sentiment(symbol: str) -> dict:
sentiment = {"AAPL": "positive", "TSLA": "negative", "NVDA": "positive"}
return {"symbol": symbol.upper(), "sentiment": sentiment.get(symbol.upper(), "neutral")}
Step 2: Write the system prompt
The system prompt is the agent's contract. It lists the available tools, the JSON format for calling them, and the JSON format for the final report. Using Qwen 3 32B on Oxlo.ai works well here because it is built for agent workflows.
SYSTEM_PROMPT = """You are a stock research agent. Your goal is to decide whether to buy, sell, or hold a stock.
You have access to these tools:
- get_stock_price(symbol: str) -> returns current price
- get_news_sentiment(symbol: str) -> returns sentiment string
When you need data, output exactly one JSON tool call:
{"tool": "get_stock_price", "arguments": {"symbol": "AAPL"}}
After gathering enough information, output exactly one final JSON report:
{"decision": "buy|sell|hold", "reasoning": "...", "confidence": "high|medium|low"}
Do not use markdown code blocks. Do not ask the user questions. Use the tools until you are ready to report."""
Step 3: Build the reasoning loop
The heart of the agent is the reasoning loop. I send the conversation history to Oxlo.ai, inspect the reply, and if the model emits a tool call I execute the function and append the result as a new user message. This loop continues until the model returns a final JSON report instead of a tool request.
import json
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
def get_stock_price(symbol: str) -> dict:
prices = {"AAPL": 182.50, "TSLA": 175.20, "NVDA": 890.10}
return {"symbol": symbol.upper(), "price": prices.get(symbol.upper(), 100.0)}
def get_news_sentiment(symbol: str) -> dict:
sentiment = {"AAPL": "positive", "TSLA": "negative", "NVDA": "positive"}
return {"symbol": symbol.upper(), "sentiment": sentiment.get(symbol.upper(), "neutral")}
SYSTEM_PROMPT = """You are a stock research agent. Your goal is to decide whether to buy, sell, or hold a stock.
You have access to these tools:
- get_stock_price(symbol: str) -> returns current price
- get_news_sentiment(symbol: str) -> returns sentiment string
When you need data, output exactly one JSON tool call:
{"tool": "get_stock_price", "arguments": {"symbol": "AAPL"}}
After gathering enough information, output exactly one final JSON report:
{"decision": "buy|sell|hold", "reasoning": "...", "confidence": "high|medium|low"}
Do not use markdown code blocks. Do not ask the user questions. Use the tools until you are ready to report."""
def run_agent(symbol: str, max_iterations: int = 5) -> str:
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Should I invest in {symbol} today?"}
]
for i in range(max_iterations):
response = client.chat.completions.create(
model="qwen-3-32b",
messages=messages,
temperature=0.2,
)
content = response.choices[0].message.content.strip()
print(f"Iteration {i+1}: {content}")
if content.startswith("{") and '"tool"' in content:
try:
call = json.loads(content)
tool_name = call.get("tool")
args = call.get("arguments", {})
if tool_name == "get_stock_price":
result = get_stock_price(**args)
elif tool_name == "get_news_sentiment":
result = get_news_sentiment(**args)
else:
result = {"error": f"Unknown tool {tool_name}"}
messages.append({"role": "assistant", "content": content})
messages.append({"role": "user", "content": json.dumps(result)})
except json.JSONDecodeError:
messages.append({"role": "assistant", "content": content})
messages.append({"role": "user", "content": "Invalid JSON. Use the exact format specified in your instructions."})
else:
return content
return "Max iterations reached without a final decision."
Step 4: Add the entry point and pretty-print the report
I add a small helper that pretty-prints the final JSON report, then call the agent from the main block. At this point the script is fully runnable end to end.
import json
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
def get_stock_price(symbol: str) -> dict:
prices = {"AAPL": 182.50, "TSLA": 175.20, "NVDA": 890.10}
return {"symbol": symbol.upper(), "price": prices.get(symbol.upper(), 100.0)}
def get_news_sentiment(symbol: str) -> dict:
sentiment = {"AAPL": "positive", "TSLA": "negative", "NVDA": "positive"}
return {"symbol": symbol.upper(), "sentiment": sentiment.get(symbol.upper(), "neutral")}
SYSTEM_PROMPT = """You are a stock research agent. Your goal is to decide whether to buy, sell, or hold a stock.
You have access to these tools:
- get_stock_price(symbol: str) -> returns current price
- get_news_sentiment(symbol: str) -> returns sentiment string
When you need data, output exactly one JSON tool call:
{"tool": "get_stock_price", "arguments": {"symbol": "AAPL"}}
After gathering enough information, output exactly one final JSON report:
{"decision": "buy|sell|hold", "reasoning": "...", "confidence": "high|medium|low"}
Do not use markdown code blocks. Do not ask the user questions. Use the tools until you are ready to report."""
def run_agent(symbol: str, max_iterations: int = 5) -> str:
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Should I invest in {symbol} today?"}
]
for i in range(max_iterations):
response = client.chat.completions.create(
model="qwen-3-32b",
messages=messages,
temperature=0.2,
)
content = response.choices[0].message.content.strip()
print(f"Iteration {i+1}: {content}")
if content.startswith("{") and '"tool"' in content:
try:
call = json.loads(content)
tool_name = call.get("tool")
args = call.get("arguments", {})
if tool_name == "get_stock_price":
result = get_stock_price(**args)
elif tool_name == "get_news_sentiment":
result = get_news_sentiment(**args)
else:
result = {"error": f"Unknown tool {tool_name}"}
messages.append({"role": "assistant", "content": content})
messages.append({"role": "user", "content": json.dumps(result)})
except json.JSONDecodeError:
messages.append({"role": "assistant", "content": content})
messages.append({"role": "user", "content": "Invalid JSON. Use the exact format specified in your instructions."})
else:
return content
return "Max iterations reached without a final decision."
def analyze(symbol: str):
raw = run_agent(symbol)
try:
report = json.loads(raw)
print("\n=== FINAL REPORT ===")
print(f"Ticker: {symbol.upper()}")
print(f"Decision: {report.get('decision')}")
print(f"Confidence: {report.get('confidence')}")
print(f"Reasoning: {report.get('reasoning')}")
except json.JSONDecodeError:
print("\nAgent output was not valid JSON:")
print(raw)
if __name__ == "__main__":
analyze("TSLA")
Run it
Save the script as stock_agent.py, export your key, and run it. You should see the agent call each mock tool in sequence before emitting the final structured report.
export OXLO_API_KEY="YOUR_OXLO_API_KEY"
python stock_agent.py
Example output:
Iteration 1: {"tool": "get_stock_price", "arguments": {"symbol": "TSLA"}}
Iteration 2: {"tool": "get_news_sentiment", "arguments": {"symbol": "TSLA"}}
Iteration 3: {"decision": "sell", "reasoning": "TSLA is trading at 175.20 with negative news sentiment, suggesting downward pressure.", "confidence": "medium"}
=== FINAL REPORT ===
Ticker: TSLA
Decision: sell
Confidence: medium
Reasoning: TSLA is trading at 175.20 with negative news sentiment, suggesting downward pressure.
Next steps
Replace the mock functions with real HTTP requests to a financial data provider such as Polygon or Yahoo Finance. If you later expand the agent to ingest full earnings transcripts or multi-page SEC filings, the flat per-request pricing on Oxlo.ai becomes a practical advantage over token-based providers because the cost does not grow with the size of each loop's context window.
Top comments (0)