Select Page

When it comes to investing in the stock market, there are 4 strategies that investors should be familiar with before they even start buying their first stock — Value Investing, Growth Investing, Momentum Investing and Dollar-Cost Averaging

Warren Buffet, one of the world’s richest people, is well known for amassing his wealth through Value Investing. His investment philosophy revolves around picking undervalued stocks that shows strong growth potential. These are also known as value stocks.

What is a Value Stock?

According to Investopedia, a value stock refers to shares of a company that appears to trade at a lower price relative to its fundamentals, such as dividends, earnings, or sales, making it appealing to value investors.

Criteria for Value Stocks Selection

On January 2022, I developed High-Performing Dividend Stocks, Mutual Funds, ETFs Screener and blogged how I built the application. It allows users to search from thousands of financial instruments and filter which ones will fall within a range of dividend yield %. They can even refine the search by showing only the ones which outperform certain stock or index.

In this tutorial, I will create another web application that outputs the list of value stocks that fulfills a set of criteria. I used the criteria from this Quantinsti.com blog post:

Businesses which are quoted at low valuations = Trailing P/E is less than 30 and Price/Book is less than 15

Businesses which have demonstrated earning power = Diluted EPS is more than 4

Businesses earning good returns on equity while employing little or no debt = Total Debt/Equity is less than 75 and Return on Equity is more than 20%

Management having substantial ownership in the business = Held by Insiders is more than 7%

This tutorial will be broken down into a 3-part series.

  • PART 1 – How to Generate the Value Stocks Screener REST API
  • PART 2 – How to Create the Value Stocks Screener User Interface
  • PART 3 – How to Deploy Value Stocks Screener Web Application in AWS

This blog post will cover PART 1 – How to Generate the Value Stocks Screener REST API. At the end of this tutorial, you should be able to launch a Spring Boot web application running on a local Tomcat server. You should see on your web browser the list of value stocks.

Pre-requisites

Before doing any development, there are two requirements to research for this project:

1. How to get a complete list of US stock ticker symbols – I used Fmp Cloud API. It is a free stock API however you must register for a free API key and there is a limit of 250 API requests/day. Example endpoint: https://fmpcloud.io/api/v3/stock-screener?exchange=NYSE,NASDAQ&apikey={YOURKEY}

2. How to get the statistics and financial data for every stock – I used Yahoo Finance API endpoint. Example: https://query2.finance.yahoo.com/v10/finance/quoteSummary/AAPL?modules=summaryDetail%2CdefaultKeyStatistics%2CfinancialData

Step 1. Setup the project workspace.

Create a Spring Boot Web application using Spring Tools for Eclipse. If you are not familiar on how to do this, you may follow the steps in this post. Name the application as ValueStocksScreen.

Step 2. Create the domain classes.

Generate the POJO classes of the JSON schema from the two Pre-Requisites above. You may create another Eclipse project for this, but I preferred https://www.jsonschema2pojo.org generating the class files for me, then copy the unzipped files into my current workspace.

Place the Java files under com.techiejackieblogs.valuestocksscreener.domain package.

Step 2a. Create the POJO class for Fmp Cloud JSON schema.

Step 2b. Create the POJO classes for the Yahoo Finance JSON schema.

Step 2c. Create the POJO class of the JSON schema that you want your REST controller to return.


package com.techiejackieblogs.valuestocksscreener.domain;

import com.fasterxml.jackson.annotation.JsonProperty;

public class ValueStock implements Comparable<Object> {
	@JsonProperty("symbol")
	private String symbol;

	@JsonProperty("previousClose")
	private double previousClose;

	@JsonProperty("trailingPE")
	private double trailingPE;

	@JsonProperty("priceToBook")
	private double priceToBook;

	@JsonProperty("dilutedEps")
	private double dilutedEps;

	@JsonProperty("debtToEquity")
	private double debtToEquity;

	@JsonProperty("returnOnEquity")
	private double returnOnEquity;

	@JsonProperty("heldPercentInsiders")
	private double heldPercentInsiders;

	public ValueStock(String symbol, double previousClose, double trailingPE, double priceToBook, double dilutedEps, double debtToEquity, double returnOnEquity, double heldPercentInsiders) {
		super();
		this.symbol = symbol;
		this.previousClose = previousClose;
		this.trailingPE = trailingPE;
		this.priceToBook = priceToBook;
		this.dilutedEps = dilutedEps;
		this.debtToEquity = debtToEquity;
		this.returnOnEquity = returnOnEquity;
		this.heldPercentInsiders = heldPercentInsiders;
	}

	@Override
	public int compareTo(Object obj) {
		ValueStock valueStockObj = (ValueStock) obj;
		return this.getSymbol().compareTo(valueStockObj.getSymbol());
	}

	public String getSymbol() {
		return symbol;
	}

	public void setSymbol(String symbol) {
		this.symbol = symbol;
	}

	public double getPreviousClose() {
		return previousClose;
	}

	public void setPreviousClose(double previousClose) {
		this.previousClose = previousClose;
	}

	public double getTrailingPE() {
		return trailingPE;
	}

	public void setTrailingPE(double trailingPE) {
		this.trailingPE = trailingPE;
	}

	public double getPriceToBook() {
		return priceToBook;
	}

	public void setPriceToBook(double priceToBook) {
		this.priceToBook = priceToBook;
	}

	public double getDilutedEps() {
		return dilutedEps;
	}

	public void setDilutedEps(double dilutedEps) {
		this.dilutedEps = dilutedEps;
	}

	public double getDebtToEquity() {
		return debtToEquity;
	}

	public void setDebtToEquity(double debtToEquity) {
		this.debtToEquity = debtToEquity;
	}

	public double getReturnOnEquity() {
		return returnOnEquity;
	}

	public void setReturnOnEquity(double returnOnEquity) {
		this.returnOnEquity = returnOnEquity;
	}

	public double getHeldPercentInsiders() {
		return heldPercentInsiders;
	}

	public void setHeldPercentInsiders(double heldPercentInsiders) {
		this.heldPercentInsiders = heldPercentInsiders;
	}

	@Override
	public String toString() {
		return "ValueStock [symbol=" + symbol + ", trailingPE=" + trailingPE + ", priceToBook=" + priceToBook + ", dilutedEps=" + dilutedEps + ", debtToEquity=" + debtToEquity + ", returnOnEquity=" + returnOnEquity + ", heldPercentInsiders=" + heldPercentInsiders + "]";
	}

}

Step 3. Create a REST Controller class under com.techiejackieblogs.valuestocksscreener.controller package.

package com.techiejackieblogs.valuestocksscreener.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.techiejackieblogs.valuestocksscreener.domain.ValueStock;
import com.techiejackieblogs.valuestocksscreener.service.StockService;

@RestController
public class ConsumerController {

	@Autowired
	private StockService stockService;

	@RequestMapping(value = "/valueStocksScreener", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
	public List<ValueStock> getvalueStocksScreenerResults(
	      @RequestParam(name = "maxTPE", defaultValue = "40") double maxTrailingPE,
	      @RequestParam(name = "maxPB", defaultValue = "15") double maxPriceToBook,
	      @RequestParam(name = "minDEPS", defaultValue = "4") double minDilutedEps,
	      @RequestParam(name = "maxDE", defaultValue = "75") double maxDebtToEquity,
	      @RequestParam(name = "minROE", defaultValue = "20") double minReturnOnEquity,
	      @RequestParam(name = "minPercInsiders", defaultValue = "7") double minHeldPercentInsiders) {
		return stockService.getValueStocks(maxTrailingPE, maxPriceToBook, minDilutedEps, maxDebtToEquity, minReturnOnEquity, minHeldPercentInsiders);
	}
}

Step 4. Create the Service interface and the implementation class.

StockService.java
package com.techiejackieblogs.valuestocksscreener.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.techiejackieblogs.valuestocksscreener.domain.ValueStock;

@Service
public interface StockService {

	public List<ValueStock> getValueStocks(double maxTrailingPE, double maxPriceToBook, double minDilutedEps, double maxDebtToEquity, double minReturnOnEquity, double minHeldPercentInsiders);

}

StockServiceImpl.java
package com.techiejackieblogs.valuestocksscreener.service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;

import com.techiejackieblogs.valuestocksscreener.domain.ValueStock;
import com.techiejackieblogs.valuestocksscreener.domain.Stock;
import com.techiejackieblogs.valuestocksscreener.domain.DefaultKeyStatistics;
import com.techiejackieblogs.valuestocksscreener.domain.FinancialData;
import com.techiejackieblogs.valuestocksscreener.domain.Response;
import com.techiejackieblogs.valuestocksscreener.domain.Result;
import com.techiejackieblogs.valuestocksscreener.domain.SummaryDetail;

public class StockServiceImpl implements StockService {

	private static String FMPCLOUD_URL = "https://fmpcloud.io/api/v3/stock-screener?exchange=NYSE,NASDAQ&apikey=YOUR_KEY";
	private static String YAHOO_FINANCE_URL = "https://query2.finance.yahoo.com/v10/finance/quoteSummary/TICKER?modules=summaryDetail%2CdefaultKeyStatistics%2CfinancialData";

	@Autowired
	private RestTemplate restTemplate;

	@Override
	public List<ValueStock> getValueStocks(double maxTrailingPE, double maxPriceToBook, double minDilutedEps, double maxDebtToEquity, double minReturnOnEquity, double minHeldPercentInsiders) {
		Stock[] stocks = restTemplate.getForObject(FMPCLOUD_URL, Stock[].class);
		List<ValueStock> valueStockList = new ArrayList<ValueStock>();
		StringBuffer buff = new StringBuffer();
		if (stocks != null && stocks.length > 0) {
			String url = "";
			ValueStock valueStock = null;
			int ctr = 0;
			Response response = null;
			for (Stock stock : stocks) {
				url = YAHOO_FINANCE_URL.replaceAll("TICKER", stock.getSymbol());
				ctr++;
				System.out.print(ctr + " " + stock.getSymbol() + " | ");
				try {
					response = restTemplate.getForObject(url, Response.class);
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					if (response.getQuoteSummary().getResult() != null) {
						valueStock = getValueStock(response, stock.getSymbol(), maxTrailingPE, maxPriceToBook, minDilutedEps, maxDebtToEquity, minReturnOnEquity, minHeldPercentInsiders);
						if (valueStock != null) {
							valueStockList.add(valueStock);
						}
					}
				} catch (HttpStatusCodeException ex) {
					// HTTP status code e.g. '404 NOT_FOUND'
				}
			}
		}
		Collections.sort(valueStockList);
		return valueStockList;
	}

	private ValueStock getValueStock(Response response, String symbol, double maxTrailingPE, double maxPriceToBook, double minDilutedEps, double maxDebtToEquity, double minReturnOnEquity, double minHeldPercentInsiders) {
		ValueStock valueStock = null;
		List<Result> resultList = response.getQuoteSummary().getResult();
		SummaryDetail sd = null;
		DefaultKeyStatistics dks = null;
		FinancialData fd = null;
		double previousClose = 0, trailingPE = 0, priceToBook = 0, dilutedEps = 0, debtToEquity = 0, returnOnEquity = 0, heldPercentInsiders = 0;
		for (Result res : resultList) {
			sd = res.getSummaryDetail();
			dks = res.getDefaultKeyStatistics();
			fd = res.getFinancialData();
			if (sd != null && dks != null && fd != null) {
				trailingPE = sd.getTrailingPE() != null && sd.getTrailingPE().getRaw() != null ? sd.getTrailingPE().getRaw() : 0;
				priceToBook = dks.getPriceToBook() != null && dks.getPriceToBook().getRaw() != null ? dks.getPriceToBook().getRaw() : 0;
				dilutedEps = dks.getTrailingEps() != null && dks.getTrailingEps().getRaw() != null ? dks.getTrailingEps().getRaw() : 0;
				debtToEquity = fd.getDebtToEquity() != null && fd.getDebtToEquity().getRaw() != null ? fd.getDebtToEquity().getRaw() : 0;
				returnOnEquity = fd.getReturnOnEquity() != null && fd.getReturnOnEquity().getRaw() != null ? fd.getReturnOnEquity().getRaw() : 0;
				heldPercentInsiders = dks.getHeldPercentInsiders() != null && dks.getHeldPercentInsiders().getRaw() != null ? dks.getHeldPercentInsiders().getRaw() : 0;
				if (trailingPE < maxTrailingPE &&
					priceToBook < maxPriceToBook &&
					dilutedEps > minDilutedEps &&
					debtToEquity < maxDebtToEquity &&
					returnOnEquity > minReturnOnEquity / 100 &&
					heldPercentInsiders > minHeldPercentInsiders / 100) {
					previousClose = sd.getPreviousClose() != null && sd.getPreviousClose().getRaw() != null ? sd.getPreviousClose().getRaw() : 0;
					valueStock = new ValueStock(symbol, previousClose, trailingPE, priceToBook, dilutedEps, debtToEquity, returnOnEquity * 100, heldPercentInsiders * 100);
				}
			}
		}
		return valueStock;
	}
}

Step 5. From SpringBootApplication class, add the StockService and RestTemplate Beans.

package com.techiejackieblogs.valuestocksscreener;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

import com.techiejackieblogs.valuestocksscreener.service.StockService;
import com.techiejackieblogs.valuestocksscreener.service.StockServiceImpl;

@SpringBootApplication
public class ValueStocksScreenerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ValueStocksScreenerApplication.class, args);
	}

	@Bean
	public StockService getStockService() {
		return new StockServiceImpl();
	}

	@Bean
	public RestTemplate getRestTemplate() {
		return new RestTemplate();
	}
}

Step 6. Start Tomcat server.

From the menu, select Run –> Run Configurations… –> Spring Boot App –> New Launch Configuration.

Select ValueStocksScreener as the Project, and com.techiejackieblogs.valuestocksscreener.ValueStocksScreenerApplication as the Main type.

Step 7. Enter http://localhost:8080/valueStocksScreener?maxTPE=30&maxPB=15&minDEPS=4&maxDE=75&minROE&20&minPercInsiders=7 on your web browser.

Congratuations! You now have completed the steps on how to create a REST API for filtering stocks based on certain criteria. You may use the same logic in the future, should you have your own set of criteria for refine the list of stocks.

References