Most trading bots are just if-statements wearing a costume.
RSI below 30, buy. Moving averages cross, sell. That works fine until the market does something the rules didn't anticipate, and then the bot keeps executing the same logic anyway, because it can't actually think.
If you're:
- building AI agents that need to reason over structured data,
- exploring what LLMs can actually do in a finance context,
- or evaluating how to combine market data with automated decision-making,
this is for you.
The problem with rule-based bots
A rule-based bot doesn't understand the market. It pattern-matches against a formula someone wrote months ago.
Earnings surprise. A sudden news event. A sector rotation nobody coded for. The bot has no way to account for any of it, because it was never built to interpret context, only to check conditions.
Developers usually discover this the hard way. They backtest a strategy, watch it perform well on historical data, deploy it, and then spend the next few months tweaking thresholds every time the market shifts. The bot isn't wrong. It's just blind.
There's also the data problem underneath all of this. Plenty of hobby projects lean on scraped Yahoo Finance endpoints or free tiers that cap out fast, and both tend to fail exactly when you need them most.
The real problem
A trading bot doesn't need more rules. It needs reasoning.
That's a different kind of system entirely. Instead of "if X then Y," you want something closer to "given this data, what would a reasonable analyst conclude, and why."
Large language models are good at exactly that kind of contextual judgment, as long as you feed them clean, structured data instead of asking them to guess.
The stack
This tutorial combines two pieces:
EODHD API for market data. End-of-day prices, real-time quotes, fundamentals, and historical data through a single REST interface, without maintaining scrapers that break every time a website changes its HTML.
Claude for the reasoning layer. Given a snapshot of price action, volume, and basic fundamentals, Claude produces a structured decision (buy, hold, sell) along with the reasoning behind it, in a format your code can actually parse and act on.
You could swap either piece out. The point isn't "use these exact two tools forever," it's showing how the pattern works so you can adapt it.
Want reliable market data without building your own scraping layer?
EODHD covers over 150,000 tickers with a generous free tier and no rate-limit surprises mid-project.
Get your free EODHD API key →
Setting up
You'll need two API keys: one from EODHD, one from Anthropic.
pip install requests anthropic python-dotenv
Store your keys in a .env file:
EODHD_API_KEY=your_eodhd_key_here
ANTHROPIC_API_KEY=your_anthropic_key_here
Step 1: Pull market data from EODHD
Start with a function that grabs recent price history and a live quote for a given ticker.
import os
import requests
from dotenv import load_dotenv
load_dotenv()
EODHD_KEY = os.getenv("EODHD_API_KEY")
def get_market_data(ticker: str, exchange: str = "US"):
symbol = f"{ticker}.{exchange}"
# Last 30 days of end-of-day prices
eod_url = f"https://eodhd.com/api/eod/{symbol}"
eod_params = {
"api_token": EODHD_KEY,
"period": "d",
"fmt": "json",
"order": "d",
}
eod_resp = requests.get(eod_url, params=eod_params).json()[:30]
# Live quote
quote_url = f"https://eodhd.com/api/real-time/{symbol}"
quote_params = {"api_token": EODHD_KEY, "fmt": "json"}
quote_resp = requests.get(quote_url, params=quote_params).json()
return {
"ticker": ticker,
"current_price": quote_resp.get("close"),
"change_pct": quote_resp.get("change_p"),
"volume": quote_resp.get("volume"),
"recent_history": eod_resp,
}
This gives you a clean payload: current price, percentage change, volume, and 30 days of history. No scraping, no broken HTML selectors.
Step 2: Ask Claude to reason over the data
This is the part that separates it from a rule-based bot. Instead of hardcoding thresholds, you hand Claude the data and ask for a structured judgment call.
import json
from anthropic import Anthropic
client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
def get_trading_decision(market_data: dict) -> dict:
prompt = f"""You are a trading analyst reviewing market data for {market_data['ticker']}.
Current price: {market_data['current_price']}
Change today: {market_data['change_pct']}%
Volume: {market_data['volume']}
Recent 30-day history: {json.dumps(market_data['recent_history'][:10])}
Based on this data, provide a trading decision. Respond ONLY with valid JSON in this exact format:
{{
"decision": "buy" | "hold" | "sell",
"confidence": 0.0 to 1.0,
"reasoning": "2-3 sentence explanation grounded in the data provided"
}}"""
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=300,
messages=[{"role": "user", "content": prompt}],
)
raw_text = response.content[0].text.strip()
return json.loads(raw_text)
The key detail here is the response format. Asking for structured JSON, not a paragraph, is what makes this usable in an actual pipeline instead of a chat window.
Step 3: Wire it together
def run_analysis(ticker: str):
data = get_market_data(ticker)
decision = get_trading_decision(data)
print(f"\n{ticker} — Decision: {decision['decision'].upper()}")
print(f"Confidence: {decision['confidence']}")
print(f"Reasoning: {decision['reasoning']}")
return decision
if __name__ == "__main__":
run_analysis("AAPL")
Sample output:
AAPL — Decision: HOLD
Confidence: 0.62
Reasoning: Price is up 0.8% on below-average volume, suggesting limited
conviction behind the move. Recent history shows consolidation rather than
a clear trend, so waiting for a volume-confirmed breakout makes more sense
than acting now.
That reasoning field is the whole point. You get a decision plus the logic behind it, which you can log, review, and adjust over time. A rule-based bot never gives you that.
Testing it without risking real money
Before connecting this to anything resembling a real account, run it against a paper trading environment.
Alpaca is a solid option here. Its paper trading API mirrors the live trading endpoints exactly, so you can route Claude's decisions through simulated buy and sell orders and see how the strategy would have performed, with fake money and real market conditions.
import alpaca_trade_api as tradeapi
alpaca = tradeapi.REST(
os.getenv("ALPACA_API_KEY"),
os.getenv("ALPACA_SECRET_KEY"),
"https://paper-api.alpaca.markets",
)
def execute_paper_trade(ticker: str, decision: dict):
if decision["decision"] == "hold":
print(f"No action for {ticker}, holding position.")
return
side = "buy" if decision["decision"] == "buy" else "sell"
alpaca.submit_order(
symbol=ticker,
qty=1,
side=side,
type="market",
time_in_force="day",
)
print(f"Paper {side} order submitted for {ticker}.")
Run execute_paper_trade after get_trading_decision and you have a full loop: EODHD for data, Claude for reasoning, Alpaca for execution, all without a single dollar at risk.
This is also where you'd start tracking accuracy. Log every decision, compare it against what actually happened three or five days later, and you'll quickly see whether the reasoning holds up or needs a better prompt.
From here you can build:
- a scheduler that runs this analysis daily across a watchlist
- a logging layer that scores decisions against actual outcomes
- a risk filter that blocks trades below a confidence threshold
Key takeaways
An LLM adds contextual reasoning that fixed rules can't replicate on their own.
Clean, reliable market data matters more than people expect. Claude is only as good as what you feed it, and EODHD removes the guesswork of scraping or rate-limited free APIs.
This is a starting point, not a production system. Position sizing, stop losses, and proper risk management live outside the scope of this tutorial, and skipping them before going live is how paper gains turn into real losses.
FAQs
❓ Do I need trading experience to build this?
✅ No. You need basic Python and an understanding of what a buy, hold, or sell decision means. The reasoning comes from Claude, not from you having to encode strategy logic manually.
❓ Is this real algorithmic trading?
✅ It's a form of it, specifically an LLM-assisted decision layer rather than a pure quantitative model. Traditional algo trading uses fixed mathematical rules; this approach adds a reasoning step on top of the data.
❓ Can I connect this to a real brokerage account?
✅ Technically yes, since Alpaca's live and paper APIs share the same structure. Don't, until you've backtested extensively and added proper risk controls. This tutorial is educational, not a plug-and-play trading system.
❓ Does EODHD provide real-time data or only end-of-day?
✅ Both. EODHD offers end-of-day historical data going back years, plus real-time and delayed quotes depending on your plan, which is why it works for both the historical context and the live price checks in this tutorial.
If you're a software or API company looking to explain your product through high-quality educational content, not marketing fluff, feel free to connect with me on LinkedIn: Kevin Meneses González
Building something with market data?
EODHD gives you 30+ years of historical data, real-time quotes, and fundamentals in one API.
Start free with EODHD →More tutorials like this
I write about fintech APIs, Python, and AI agents every week.
Read more on kevinmeneses.com →
Looking for technical content for your company? I can help — LinkedIn · kevinmenesesgonzalez@gmail.com
Top comments (0)