package com.crio.warmup.stock;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;
import com.crio.warmup.stock.dto.AnnualizedReturn;
import com.crio.warmup.stock.dto.Candle;
import com.crio.warmup.stock.dto.PortfolioTrade;
import com.crio.warmup.stock.dto.TiingoCandle;
import com.crio.warmup.stock.dto.TotalReturnsDto;
import com.crio.warmup.stock.log.UncaughtExceptionHandler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.apache.logging.log4j.ThreadContext;
import org.springframework.web.client.RestTemplate;
public class PortfolioManagerApplication {
// TODO: CRIO_TASK_MODULE_JSON_PARSING
// Task:
// - Read the json file provided in the argument[0], The file is available in
// the classpath.
// - Go through all of the trades in the given file,
// - Prepare the list of all symbols a portfolio has.
// - if "trades.json" has trades like
// [{ "symbol": "MSFT"}, { "symbol": "AAPL"}, { "symbol": "GOOGL"}]
// Then you should return ["MSFT", "AAPL", "GOOGL"]
// Hints:
// 1. Go through two functions provided - #resolveFileFromResources() and
// #getObjectMapper
// Check if they are of any help to you.
// 2. Return the list of all symbols in the same order as provided in json.
// Note:
// 1. There can be few unused imports, you will need to fix them to make the
// build pass.
// 2. You can use "./gradlew build" to check if your code builds successfully.
public static List<String> mainReadFile(String[] args) throws IOException, URISyntaxException {
// 1. Read the json file provided in the argument[0]
File fileName = resolveFileFromResources(args[0]);
// - Go through all of the trades in the given file,
// 2. get an instance of the ObjectMapper to deserialize the json data to a java
// object
ObjectMapper om = getObjectMapper();
PortfolioTrade[] portfolioTrades = om.readValue(fileName, PortfolioTrade[].class);
// Store symbols in ArrayList and return ArrayList
List<String> symbols = new ArrayList<>();
// Iterate over the list of java objects and extract stock symbols
for (PortfolioTrade portfolioTrade : portfolioTrades) {
symbols.add(portfolioTrade.getSymbol());
}
// Then you should return ["MSFT", "AAPL", "GOOGL"]
return symbols;
}
private static void printJsonObject(Object object) throws IOException {
Logger logger = Logger.getLogger(PortfolioManagerApplication.class.getCanonicalName());
ObjectMapper mapper = new ObjectMapper();
logger.info(mapper.writeValueAsString(object));
}
private static File resolveFileFromResources(String filename) throws URISyntaxException {
return Paths.get(Thread.currentThread().getContextClassLoader()
.getResource(filename)
.toURI())
.toFile();
}
private static ObjectMapper getObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
return objectMapper;
}
// TODO: CRIO_TASK_MODULE_JSON_PARSING
// Follow the instructions provided in the task documentation and fill up the
// correct values for
// the variables provided. First value is provided for your reference.
// A. Put a breakpoint on the first line inside mainReadFile() which says
// return Collections.emptyList();
// B. Then Debug the test #mainReadFile provided in
// PortfoliomanagerApplicationTest.java
// following the instructions to run the test.
// Once you are able to run the test, perform following tasks and record the
// output as a
// String in the function below.
// Use this link to see how to evaluate expressions -
// https://code.visualstudio.com/docs/editor/debugging#_data-inspection
// 1. evaluate the value of "args[0]" and set the value
// to the variable named valueOfArgument0 (This is implemented for your
// reference.)
// 2. In the same window, evaluate the value of expression below and set it
// to resultOfResolveFilePathArgs0
// expression ==> resolveFileFromResources(args[0])
// 3. In the same window, evaluate the value of expression below and set it
// to toStringOfObjectMapper.
// You might see some garbage numbers in the output. Dont worry, its expected.
// expression ==> getObjectMapper().toString()
// 4. Now Go to the debug window and open stack trace. Put the name of the
// function you see at
// second place from top to variable functionNameFromTestFileInStackTrace
// 5. In the same window, you will see the line number of the function in the
// stack trace window.
// assign the same to lineNumberFromTestFileInStackTrace
// Once you are done with above, just run the corresponding test and
// make sure its working as expected. use below command to do the same.
// ./gradlew test --tests PortfolioManagerApplicationTest.testDebugValues
public static List<String> debugOutputs() {
String valueOfArgument0 = "trades.json";
String resultOfResolveFilePathArgs0 = "";
String toStringOfObjectMapper = "";
String functionNameFromTestFileInStackTrace = "";
String lineNumberFromTestFileInStackTrace = "";
return Arrays.asList(new String[] { valueOfArgument0, resultOfResolveFilePathArgs0,
toStringOfObjectMapper,
functionNameFromTestFileInStackTrace, lineNumberFromTestFileInStackTrace });
}
// Note:
// Remember to confirm that you are getting same results for annualized returns
// as in Module 3.
// and deserialize the results in List<Candle>
// Note:
// 1. You may need to copy relevant code from #mainReadQuotes to parse the Json.
// 2. Remember to get the latest quotes from Tiingo API.
// TODO: CRIO_TASK_MODULE_REST_API
// Find out the closing price of each stock on the end_date and return the list
// of all symbols in ascending order by its close value on end date.
// Note:
// 1. You may have to register on Tiingo to get the api_token.
// 2. Look at args parameter and the module instructions carefully.
// 2. You can copy relevant code from #mainReadFile to parse the Json.
// 3. Use RestTemplate#getForObject in order to call the API,
// and deserialize the results in List<Candle>
public static List<String> mainReadQuotes(String[] args) throws IOException, URISyntaxException, RuntimeException {
String token= "6533773c48e117120384519d96e6c41851c72564";
List<PortfolioTrade> trades = readTradesFromJson(args[0]);
List<TotalReturnsDto> result = new ArrayList<TotalReturnsDto>();
List<String> finalResult = new ArrayList<String>();
for (PortfolioTrade trade : trades) {
String url = prepareUrl(trade, LocalDate.parse(args[1]), token);
TiingoCandle[] tiingoCandle = new RestTemplate().getForObject(url, TiingoCandle[].class);
List<TiingoCandle> tiingoCandleList = Arrays.asList(tiingoCandle);
Collections.sort(tiingoCandleList, new StockComparator());
TiingoCandle item = tiingoCandleList.get(0);
result.add(new TotalReturnsDto(trade.getSymbol(), item.getClose()));
}
Collections.sort(result, new StockLowComparator());
for (TotalReturnsDto item : result) {
finalResult.add(item.getSymbol());
}
return finalResult;
}
static class StockComparator implements Comparator<TiingoCandle> {
@Override
public int compare(TiingoCandle tiingoCandle1, TiingoCandle tiingoCandle2) {
if (tiingoCandle1.getDate() == tiingoCandle2.getDate())
return 0;
if (tiingoCandle1.getDate().isAfter(tiingoCandle2.getDate()))
return 1;
return -1;
}
}
static class StockLowComparator implements Comparator<TotalReturnsDto> {
@Override
public int compare(TotalReturnsDto totalReturnsDto1, TotalReturnsDto totalReturnsDto2) {
if (totalReturnsDto1.getClosingPrice() == totalReturnsDto2.getClosingPrice())
return 0;
if (totalReturnsDto1.getClosingPrice() > totalReturnsDto2.getClosingPrice())
return 1;
return -1;
}
}
// TODO:
// After refactor, make sure that the tests pass by using these two commands
// ./gradlew test --tests PortfolioManagerApplicationTest.readTradesFromJson
// ./gradlew test --tests PortfolioManagerApplicationTest.mainReadFile
public static List<PortfolioTrade> readTradesFromJson(String filename) throws IOException, URISyntaxException {
ObjectMapper mapper = getObjectMapper();
PortfolioTrade[] portfolioTrade = mapper.readValue(resolveFileFromResources(filename), PortfolioTrade[].class);
List<PortfolioTrade> list = Arrays.asList(portfolioTrade);
return list;
}
// TODO:
// Build the Url using given parameters and use this function in your code to
// cann the API.
public static String prepareUrl(PortfolioTrade trade, LocalDate endDate, String token) {
String symbol = trade.getSymbol();
LocalDate startDate = trade.getPurchaseDate();
String url = "https://api.tiingo.com/tiingo/daily/" + symbol + "/prices?startDate=" + startDate + "&endDate="
+ endDate + "&token=" + token;
return url;
}
// TODO: CRIO_TASK_MODULE_CALCULATIONS
// Now that you have the list of PortfolioTrade and their data, calculate annualized returns
// for the stocks provided in the Json.
// Use the function you just wrote #calculateAnnualizedReturns.
// Return the list of AnnualizedReturns sorted by annualizedReturns in descending order.
// Note:
// 1. You may need to copy relevant code from #mainReadQuotes to parse the Json.
// 2. Remember to get the latest quotes from Tiingo API.
// TODO:
// Ensure all tests are passing using below command
// ./gradlew test --tests ModuleThreeRefactorTest
static Double getOpeningPriceOnStartDate(List<Candle> candles) {
List<TiingoCandle> listTiingoCandle = new ArrayList<>();
for(Candle candy : candles){
listTiingoCandle.add((TiingoCandle)candy);
}
Collections.sort(listTiingoCandle, new StockComparator());
TiingoCandle item = listTiingoCandle.get(0);
return item.getOpen();
}
public static Double getClosingPriceOnEndDate(List<Candle> candles) {
List<TiingoCandle> listTiingoCandle = new ArrayList<>();
for(Candle candy : candles){
listTiingoCandle.add((TiingoCandle)candy);
}
Collections.sort(listTiingoCandle, new StockComparator());
TiingoCandle item = listTiingoCandle.get(listTiingoCandle.size()-1);
return item.getClose();
}
public static List<Candle> fetchCandles(PortfolioTrade trade, LocalDate endDate, String token) {
String url = prepareUrl(trade, endDate, token);
TiingoCandle[] tiingoCandle = new RestTemplate().getForObject(url, TiingoCandle[].class);
List<Candle> tiingoCandleList = Arrays.asList(tiingoCandle);
return tiingoCandleList;
}
public static List<AnnualizedReturn> mainCalculateSingleReturn(String[] args)
throws IOException, URISyntaxException {
String token = "6533773c48e117120384519d96e6c41851c72564";
// read the trades.json file
List<PortfolioTrade> trades = readTradesFromJson(args[0]);
List<TotalReturnsDto> result = new ArrayList<TotalReturnsDto>();
List<AnnualizedReturn> finalResult = new ArrayList<>();
// call the tingo api to fetch stock quote data for the symbol in each transaction of the json file
for (PortfolioTrade trade : trades) {
String url = prepareUrl(trade, LocalDate.parse(args[1]), token);
TiingoCandle[] tiingoCandle = new RestTemplate().getForObject(url, TiingoCandle[].class);
List<TiingoCandle> tiingoCandleList = Arrays.asList(tiingoCandle);
Collections.sort(tiingoCandleList, new StockComparator());
TiingoCandle item = tiingoCandleList.get(0);
AnnualizedReturn annualizedReturns = calculateAnnualizedReturns(LocalDate.parse(args[1]), trade, item.getOpen(),item.getClose());
finalResult.add(annualizedReturns);
}
Collections.sort(finalResult, new AnnualizedReturnComparator());
return finalResult;
}
static class AnnualizedReturnComparator implements Comparator<AnnualizedReturn> {
@Override
public int compare(AnnualizedReturn annualizedReturn1, AnnualizedReturn annualizedReturn2) {
if (annualizedReturn1.getAnnualizedReturn() == annualizedReturn2.getAnnualizedReturn())
return 0;
if (annualizedReturn1.getAnnualizedReturn() > annualizedReturn2.getAnnualizedReturn())
return 1;
return -1;
}
}
// TODO: CRIO_TASK_MODULE_CALCULATIONS
// Return the populated list of AnnualizedReturn for all stocks.
// Annualized returns should be calculated in two steps:
// 1. Calculate totalReturn = (sell_value - buy_value) / buy_value.
// 1.1 Store the same as totalReturns
// 2. Calculate extrapolated annualized returns by scaling the same in years span.
// The formula is:
// annualized_returns = (1 + total_returns) ^ (1 / total_num_years) - 1
// 2.1 Store the same as annualized_returns
// Test the same using below specified command. The build should be successful.
// ./gradlew test --tests PortfolioManagerApplicationTest.testCalculateAnnualizedReturn
public static AnnualizedReturn calculateAnnualizedReturns(LocalDate endDate,
PortfolioTrade trade, Double buyPrice, Double sellPrice) {
Period diff = Period.between(trade.getPurchaseDate(), endDate);
Double totalNumYears = (Double.valueOf(diff.getYears())) ;
Double totalReturns = ( sellPrice - buyPrice ) / buyPrice;
Double annualizedReturns = Math.pow(( 1 + totalReturns ), ( 1 / totalNumYears ) - 1);
return new AnnualizedReturn(trade.getSymbol(), annualizedReturns, totalReturns);
}
public static void main(String[] args) throws Exception {
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler());
ThreadContext.put("runId", UUID.randomUUID().toString());
printJsonObject(mainReadFile(args));
printJsonObject(mainReadQuotes(args));
printJsonObject(mainCalculateSingleReturn(args));
}
public static String getToken() {
String token= "6533773c48e117120384519d96e6c41851c72564";
return token;
}
}