Back

Building a stock market service on Yahoo using GraphQL

Building a stock market service on Yahoo using GraphQL

In a recent case study for a well-known German investment fund company, I implemented a GraphQL service that retrieves stock market data from Yahoo Finance.

image

Last year, in 2023, I completed my Master’s degree and began searching for a job.

One of my first interviews was with an investment fund company looking for a software architect to optimize its internal processes with software tools.

I’m not a fan of HR interviews that feel like university exams, filled with tedious, theoretical questions like "Please write an primality test algorithm for determining whether an input number is prime". Please, stop it! That's really no fun for anyone.

So, I was thrilled when the company assigned me a short case study instead. Here’s how it went:

💡 Setting: The backend part of the task is recommended to be completed with Python or Node. All other choices of libraries are up to you. Please provide the solution as a private Github repository, with easy instructions on how to build and run the application.

💡 Task: Implement an API with Yahoo Finance to pull some financial data for a company of your choice. Implement a GraphQL server that is suitable as a backend to query data. Keep your GraphQL schema simple to where the company is represented and you’re able to access the financial data via one of it’s fields.

GraphQL vs. REST

I had some experience with GraphQL, but not extensively. Like any architectural decision, both GraphQL and REST have their pros and cons. The case study specified the technologies, but I also wanted to have some reasoning behind the architectural decision.

Both GraphQL and REST are used to build interfaces between systems. But what are the key differences? Here’s a quick overview:

GraphQL

The main feature of GraphQL is its ability to request and receive only the data you need. Typically, a single endpoint is used for all operations, simplifying communication between clients and servers.

image

Clients send a GraphQL query, the server interprets it, gathers the necessary data (potentially from multiple sources), and returns the result in JSON format.

With GraphQL, the client and not the server decides what data is required. This offers some advantages over REST:

  1. High performance
  2. Huge flexibility
  3. Easy versioning

REST

REST is built on the principles of stateless communication and standard HTTP methods. It’s easy to set up and lightweight, which is why it’s become the industry standard.

But... There are different implementations and most of the ones I have seen do not fulfil the REST standards. It does not force you to follow any specific guidance. As a result, many interfaces become a complete mess.

A well-defined REST API is great and will certainly suffice for most projects. However, many large corporations such as Meta and co. have completely different scaling problems. At this point, it is worth taking a look at the advantages of GraphQL.

image

Set up Apollo GraphQL

There are some projects that implement GraphQL. In my opinion, Apollo GraphQL is the best I have tested so far. Setting up the GraphQL server with Apollo is not rocket science.

import {ApolloServer} from 'apollo-server';
import {schema} from './data/schema';

const server = new ApolloServer({
    schema,
    introspection: true,
});

server.listen({port: process.env.PORT}).then(({url}) => {
    console.info(`Server is running: ${url}:${process.env.PORT}`);
});

Familiarize yourself with the configuration of the Apollo server. There are various settings available. I simply added the GraphQL schema and enabled client introspection to use the Apollo GraphQL sandbox.

The sandbox is a ready-to-use client for testing GraphQL implementations. It’s comparable to Swagger for REST APIs. Once I started the server, I could access the sandbox via localhost.

The sandbox is an incredibly powerful tool for GraphQL development. I highly recommend it to everyone.

image

Don't reinvent the wheel - Yahoo Finance

Yahoo doesn’t provide an official API for developers, but there’s a robust open-source community for Yahoo financial data.

For this project, I used the yahoo-finance2 package. It has a high download rate, indicating good quality, and it’s actively maintained and well-documented.

I implemented a simple adapter to retrieve information about stocks and their historical performance. There’s more data available, so I recommend checking out the documentation.

import YahooFinance from 'yahoo-finance2';

export const YahooFinancialAdapter = {
    readQuote: async (symbol: string) => {
        return await YahooFinance.quote(symbol);
    },
    readHistory: async (args: {
        symbol: string,
        from: string,
        to: string,
        interval: "1d" | "1wk" | "1mo"
    }) => {
        return await YahooFinance.historical(args.symbol, {
            period1: args.from,
            period2: args.to,
            interval: args.interval
        });
    },
};

GraphQL schema

GraphQL requires a schema written in its query language. The schema acts as a blueprint that defines the structure of the data clients can query from the GraphQL server.

The great thing about the schema is that it provides type safety. It outlines the available data types, the relationships between them, and the operations clients can perform.

I defined a QuoteType to retrieve standard information about a requested stock and a HistoryType to get the historical performance of stocks.

import { gql } from "apollo-server"

export const quoteType = gql`
  type Quote {
    language: String
    region: String
    currency: String
    shortName: String
    longName: String
    market: String
    marketCap: Float
    regularMarketPrice: Float
    regularMarketDayHigh: Float
    regularMarketDayLow: Float
    regularMarketVolume: Float
  }
`;

export const historyType = gql`
  type History {
    date: String
    open: Float
    high: Float
    low: Float
    close: Float
    adjClose: Float
    volume: Float
  }
`;

Simply add them, including the QueryType, to makeExecutableSchema to create the schema.

import {historyType} from "./model/history";
import {quoteType} from './model/quote';
import {queryType, resolvers as queryResolvers} from './query';
import {makeExecutableSchema} from "@graphql-tools/schema";

export const schema = makeExecutableSchema({
    typeDefs: [queryType, quoteType, historyType],
    resolvers: {
        ...queryResolvers,
    }
});

GraphQL query and resolver

To retrieve data from GraphQL, I also needed to create a query. A GraphQL query is a string that tells the server what kind of data the client is looking for based on the data types.

A simple query to get some user information for a user with ID 123 might look like this:

query {
  user(id: 123) {
    name
    email
  }
}

To deploy a query, I implemented it with Apollo GraphQL and connected it to a resolver, which I had previously added to the schema. In GraphQL, a resolver is a function responsible for retrieving the data for a specific field in a GraphQL query.

Here’s the definition of the QueryType and the resolver:

import { gql } from "apollo-server"
import { YahooFinancialAdapter } from "../adapter/yahoo-financial-adapter";

export const queryType = gql`
  type Query {
    quote(symbol: String): Quote
    history(symbol: String, from: String, to: String, interval: String): [History]
  }
`;

export const resolvers = {
  Query: {
    quote: async (_parent: any, args: {
      symbol: string
    }) => {
      return await YahooFinancialAdapter.readQuote(args.symbol);
    },
    history: async (_parent: any, args: any) => {
      return await YahooFinancialAdapter.readHistory(args) ?? [];
    },
  }
};

Using the sandbox to query data

Now that everything is set up, I started the Apollo GraphQL sandbox to test my implementation. The sandbox dashboard is divided into three sections.

On the left is the auto-generated documentation of the interface based on the schema—a fantastic feature! Queries can be made in the center of the dashboard, with auto-completion available.

On the right, the results are returned in JSON format.

image

Finally, I wanted to test the implementation. For example, to see how Apple performed in the first two weeks of September, I added the following variables to get the result:

{
  "symbol": "AAPL",
  "from": "2023-09-01",
  "to": "2023-09-13",
  "interval": "1d",
}

Conclusion

This was a great case study! The scope was perfect for a job application. GraphQL feels very flexible at first glance, and with Apollo, I had a powerful development tool that made writing and testing.

Anyway. At the end of the day, I took another opportunity at MHP - A Porsche Company as a technology strategy consultant, which promises great opportunities. But that's a story for another time!

💡 You can find the project and all the code in my Github Repository.

You are welcome to get in touch with me if you have any questions.