import com.google.gson.JsonObject; import com.google.gson.JsonParser; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; public class FundOverlap { public static void main(String[] args) throws Exception { // Read both funds' files; each has the date of its own issuer's file var http = HttpClient.newHttpClient(); Map funds = new LinkedHashMap<>(); for (String ticker : List.of("VOO", "QQQ")) { var request = HttpRequest.newBuilder(URI.create( "https://tickerinside.com/api/v1/fund/" + ticker.toLowerCase(Locale.ROOT) + ".json")) .header("User-Agent", "MyProject/1.0 (+https://example.com)") .build(); var response = http.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() != 200) { throw new RuntimeException("HTTP " + response.statusCode()); } JsonObject fund = JsonParser.parseString(response.body()) .getAsJsonObject(); funds.put(ticker, fund); System.out.println(ticker + " holdings_as_of " + fund.get("holdings_as_of").getAsString()); } // Overlap by weight, as the site computes it: for every ticker both // funds hold, add the smaller of its two weights JsonObject a = funds.get("VOO").getAsJsonObject("holdings"); JsonObject b = funds.get("QQQ").getAsJsonObject("holdings"); double overlap = 0; int shared = 0; for (String t : a.keySet()) { if (b.has(t)) { overlap += Math.min(a.get(t).getAsDouble(), b.get(t).getAsDouble()); shared++; } } System.out.println(String.format(Locale.ROOT, "overlap by weight %.1f%%, %d companies in both", overlap, shared)); // The lines to print under the figure, one per fund file for (JsonObject fund : funds.values()) { System.out.println(fund.get("cite_as").getAsString()); } } }