DEV Community

Cover image for Building a Real-Time Financial Market Analyst API with LLMs
Carter Harrison
Carter Harrison

Posted on

Building a Real-Time Financial Market Analyst API with LLMs

Financial market data presents unique challenges for developers: it's real-time, constantly changing, and requires careful interpretation. This tutorial walks through building a REST API that combines live financial data with language models to create an intelligent market analysis service. We'll focus on creating a clean, maintainable architecture that can scale with your needs.

Architecture Overview

Before diving into the code, let's understand the key components of our system:

  • Market Data Service: Handles all interactions with real-time financial data sources. This service abstracts away the complexity of fetching and formatting market data, providing a clean interface for the rest of our application.
  • Assistant Service: Manages the language model interactions. It takes market data and user queries, then generates intelligent analysis based on current information. By separating this into its own service, we can easily swap out different language models or modify the prompting strategy.
  • API Routes: Provides RESTful endpoints that combine the above services to handle user requests. The routes handle input validation, error handling, and response formatting.

Project Setup

First, let's set up our project structure. We'll use a modular approach that separates concerns and makes the code easier to test and maintain:

mkdir market-assistant-api
cd market-assistant-api
npm init -y
npm install express openai dotenv axios
Enter fullscreen mode Exit fullscreen mode

You will need to get both an OpenAI API Key and a Pine API Key. With these, make sure to put them in the .env file.

PINE_API_KEY=your_pine_api_key
OPENAI_API_KEY=your_openai_api_key
PORT=3000
Enter fullscreen mode Exit fullscreen mode

Now we can create this directory structure:

market-assistant-api/
├── src/
│   ├── services/
│   │   ├── marketData.js
│   │   └── assistant.js
│   └── server.js
├── .env
└── package.json
Enter fullscreen mode Exit fullscreen mode

This structure separates our code into logical components, making it easier to maintain and test. The services directory contains our core business logic, while routes handles HTTP-specific concerns.

Implementation Details

Market Data Service (marketData.js)

This service encapsulates all market data operations. Each method is focused on a specific type of data retrieval:

getStockPrice: Fetches current price and movement data for a single stock
getMarketTrends: Retrieves sector-wide trend information
getComparisonData: Gathers comparative data for multiple symbols

import axios from "axios";

class MarketDataService {
  constructor(apiKey) {
    this.client = axios.create({
      baseURL: "https://api.pine.dev",
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json",
      },
    });
  }

  async makeQuery(query) {
    const response = await this.client.post("/context", { query });
    return response.data.markdown;
  }

  async getStockPrice(symbol) {
    const query = `current price and daily movement for ${symbol} stock`;
    const data = await this.makeQuery(query);
    return data;
  }

  async getMarketTrends(sector) {
    const query = `current market trends and performance for the ${sector} sector`;
    const data = await this.makeQuery(query);
    return data;
  }

  async getComparisonData(symbols) {
    const symbolList = symbols.join(", ");
    const query = `trends for each stock: ${symbolList}`;
    const data = await this.makeQuery(query);
    return data;
  }
}

export default MarketDataService;

Enter fullscreen mode Exit fullscreen mode

Assistant Service (assistant.js)

The Assistant Service handles the intelligence layer of our API. It takes market data and user queries and generates meaningful analysis:

import { OpenAI } from "openai";

class AssistantService {
  constructor(apiKey) {
    this.client = new OpenAI(apiKey);
  }

  async analyzeQuery(query, marketData) {
    const systemPrompt = `
          You are a financial analysis API. Analyze the following market data and answer the query.
          Only use the provided market data for your analysis. If you can't answer something from
          the provided data, say so explicitly.

          Current Market Data:
          ${marketData}
    `;

    const completion = await this.client.chat.completions.create({
      model: "gpt-4",
      messages: [
        { role: "system", content: systemPrompt },
        { role: "user", content: query },
      ],
      temperature: 0.3,
      max_tokens: 2048,
    });

    return completion.choices[0].message.content;
  }
}

export default AssistantService;

Enter fullscreen mode Exit fullscreen mode

API Routes (server.js)

The API routes tie everything together, providing clean endpoints for clients to interact with:

import express from "express";
import dotenv from "dotenv";
import MarketDataService from "./services/marketData.js";
import AssistantService from "./services/assistant.js";

dotenv.config();

const router = express.Router();
const marketData = new MarketDataService(process.env.PINE_API_KEY);
const assistant = new AssistantService(process.env.OPENAI_API_KEY);

// Get analysis for a single stock
router.get("/analyze/stock/:symbol", async (req, res) => {
  const { symbol } = req.params;
  const { query } = req.query;

  const data = await marketData.getStockPrice(symbol);
  const analysis = await assistant.analyzeQuery(query, data);

  res.json({
    symbol,
    analysis,
    timestamp: new Date().toISOString(),
  });
});

// Get sector analysis
router.get("/analyze/sector/:sector", async (req, res) => {
  const { sector } = req.params;
  const { query } = req.query;

  const data = await marketData.getMarketTrends(sector);
  const analysis = await assistant.analyzeQuery(query, data);

  res.json({
    sector,
    analysis,
    timestamp: new Date().toISOString(),
  });
});

// Compare multiple stocks
router.post("/analyze/compare", async (req, res) => {
  const { symbols, query } = req.body;

  if (!Array.isArray(symbols) || symbols.length < 2) {
    return res.status(400).json({
      error: "Please provide at least two symbols to compare",
    });
  }

  const data = await marketData.getComparisonData(symbols);
  const analysis = await assistant.analyzeQuery(query, data);

  res.json({
    symbols,
    analysis,
    timestamp: new Date().toISOString(),
  });
});

const app = express();
const port = process.env.PORT || 3000;

app.use(express.json());
app.use("/api", router);

app.listen(port, () => {
  console.log(`Server is running on port http://localhost:${port}`);
});
Enter fullscreen mode Exit fullscreen mode

API Usage

First, we must start the server

node ./src/server.js
Enter fullscreen mode Exit fullscreen mode

Now, let's look at some practical examples of using it:

Analyzing a Single Stock

Request:

curl "http://localhost:3000/api/analyze/stock/AAPL?query=How%20has%20the%20stock%20performed%20today"
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "symbol": "AAPL",
  "analysis": "Based on the provided market data, Apple Inc. (AAPL) closed at $242.65, which is an increase of $3.06 or 1.28% from the previous close of $239.59. After hours, the stock price slightly increased by $0.10 or 0.04% to $242.75. The day's trading range was between $238.90 and $242.76.",
  "timestamp": "2024-12-03T22:28:13.998Z"
}
Enter fullscreen mode Exit fullscreen mode

Sector Analysis

Request:

curl "http://localhost:3000/api/analyze/sector/technology?query=What%20are%20the%20current%20sector%20trends"
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "sector": "technology",
  "analysis": "Based on the provided market data, the current sector trends in the technology industry for 2024 include:\n\n1. **Generative AI (Gen AI)**: This trend has seen significant growth...",
  "timestamp": "2024-12-03T22:29:09.848Z"
}
Enter fullscreen mode Exit fullscreen mode

Stock Comparison

Request:

curl -X POST "http://localhost:3000/api/analyze/compare" \
     -H "Content-Type: application/json" \
     -d '{
           "symbols": ["AAPL", "MSFT", "GOOGL"],
           "query": "Compare their performance today"
         }'
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "symbols": ["AAPL", "MSFT", "GOOGL"],
  "analysis": "Based on the provided market data, here is the performance comparison for today:\n\n1. **Apple Inc. (AAPL)**\n   - Current Price: $242.65\n   - Change: +3.06 (1.28%)\n...",
  "timestamp": "2024-12-03T22:31:31.991Z"
}
Enter fullscreen mode Exit fullscreen mode

Next Steps

If you want to continue building with the API, these may be some good next steps:

Performance

To maintain good performance, consider implementing:

Caching Layer

  • Cache frequent queries

  • Store recent market data

  • Implement cache invalidation strategies

Rate Limiting

  • Limit requests per client

  • Implement token bucket algorithm

  • Add retry mechanisms

Request Optimization

  • Batch similar requests

  • Implement request queuing

  • Add request timeout handling

For Production

Authentication & Authorization

  • Implement JWT authentication

  • Add role-based access control

  • Set up API key management

Advanced Features

  • Historical data analysis

  • Technical indicators

  • Sentiment analysis

  • Portfolio management

Monitoring & Logging

  • Add structured logging

  • Implement performance monitoring

  • Set up alerting systems

And thats it! Let me know what y'all think and if you have any questions!

Top comments (0)