diff --git a/.github/workflows/sync-sdk-docs.yml b/.github/workflows/sync-sdk-docs.yml index e56ad05..d1ee2b5 100644 --- a/.github/workflows/sync-sdk-docs.yml +++ b/.github/workflows/sync-sdk-docs.yml @@ -31,17 +31,18 @@ on: branches: [staging] paths: - 'sdk/js/**' + - 'sdk/java/**' - 'lib/mdx-to-md.js' - 'scripts/export-sdk-docs.js' - '.github/workflows/sync-sdk-docs.yml' workflow_dispatch: inputs: sdk: - description: 'Which SDK to sync (pilot: js only)' + description: 'Which SDK to sync' required: true default: 'js' type: choice - options: [js] + options: [js, java] env: SOURCE_OWNER: MarketDataApp @@ -56,7 +57,7 @@ jobs: strategy: fail-fast: false matrix: - sdk: [js] + sdk: [js, java] steps: - name: Checkout documentation source uses: actions/checkout@v4 diff --git a/api/authentication.mdx b/api/authentication.mdx index 0f4077f..283f9e1 100644 --- a/api/authentication.mdx +++ b/api/authentication.mdx @@ -140,6 +140,44 @@ func main() { } ``` + + + +```java title="App.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockQuoteRequest; + +public class App { + public static void main(String[] args) { + // The no-arg constructor reads your token from the MARKETDATA_TOKEN + // environment variable (or a .env file in the working directory). + // Alternatively, pass it directly: + // new MarketDataClient("your_token_here", null, null, true) + try (MarketDataClient client = new MarketDataClient()) { + client.stocks().quote(StockQuoteRequest.of("SPY")).values().forEach(System.out::println); + } + } +} +``` + + + + +```kotlin title="App.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockQuoteRequest + +fun main() { + // The no-arg constructor reads your token from the MARKETDATA_TOKEN + // environment variable (or a .env file in the working directory). + // Alternatively, pass it directly: + // MarketDataClient("your_token_here", null, null, true) + MarketDataClient().use { client -> + client.stocks().quote(StockQuoteRequest.of("SPY")).values().forEach(::println) + } +} +``` + diff --git a/api/funds/candles.mdx b/api/funds/candles.mdx index 83da93d..2bdc508 100644 --- a/api/funds/candles.mdx +++ b/api/funds/candles.mdx @@ -119,6 +119,52 @@ $candles = $client->mutual_funds->candles( // Display formatted candles summary echo $candles; ``` + + + +```java title="FundCandles.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.funds.FundCandlesRequest; +import com.marketdata.sdk.funds.FundResolution; + +public class FundCandles { + public static void main(String[] args) { + try (MarketDataClient client = new MarketDataClient()) { + // VFINX is available without authentication (free test symbol). + client.funds() + .candles(FundCandlesRequest.builder(FundResolution.DAILY, "VFINX") + .countback(30) + .build()) + .values() + .forEach(System.out::println); + } + } +} +``` + + + + +```kotlin title="FundCandles.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.funds.FundCandlesRequest +import com.marketdata.sdk.funds.FundResolution + +fun main() { + MarketDataClient().use { client -> + // VFINX is available without authentication (free test symbol). + client.funds() + .candles( + FundCandlesRequest.builder(FundResolution.DAILY, "VFINX") + .countback(30) + .build() + ) + .values() + .forEach(::println) + } +} +``` + diff --git a/api/markets/status.mdx b/api/markets/status.mdx index 408dc2d..3dd546d 100644 --- a/api/markets/status.mdx +++ b/api/markets/status.mdx @@ -143,6 +143,62 @@ $yesterday = $client->markets->status( ); echo $yesterday; ``` + + + +```java title="MarketStatusApp.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.markets.MarketStatusRequest; +import java.time.LocalDate; + +public class MarketStatusApp { + public static void main(String[] args) { + try (MarketDataClient client = new MarketDataClient()) { + client.markets() + .status(MarketStatusRequest.builder() + .from(LocalDate.of(2020, 1, 1)) + .to(LocalDate.of(2020, 12, 31)) + .build()) + .values() + .forEach(System.out::println); + + client.markets() + .status(MarketStatusRequest.of()) + .values() + .forEach(System.out::println); + } + } +} +``` + + + + +```kotlin title="MarketStatusApp.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.markets.MarketStatusRequest +import java.time.LocalDate + +fun main() { + MarketDataClient().use { client -> + client.markets() + .status( + MarketStatusRequest.builder() + .from(LocalDate.of(2020, 1, 1)) + .to(LocalDate.of(2020, 12, 31)) + .build() + ) + .values() + .forEach(::println) + + client.markets() + .status(MarketStatusRequest.of()) + .values() + .forEach(::println) + } +} +``` + diff --git a/api/options/chain.mdx b/api/options/chain.mdx index f7db1f7..2a94a3e 100644 --- a/api/options/chain.mdx +++ b/api/options/chain.mdx @@ -108,6 +108,30 @@ $calls = $chain->getCalls(); $puts = $chain->getPuts(); $strikes = $chain->getStrikes(); ``` + + + +```java title="OptionChain.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.options.OptionsChainRequest; + +try (MarketDataClient client = new MarketDataClient()) { + client.options().chain(OptionsChainRequest.of("AAPL")).values().forEach(System.out::println); +} +``` + + + + +```kotlin title="OptionChain.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.options.OptionsChainRequest + +MarketDataClient().use { client -> + client.options().chain(OptionsChainRequest.of("AAPL")).values().forEach(::println) +} +``` + diff --git a/api/options/expirations.mdx b/api/options/expirations.mdx index 7e30e51..a55b551 100644 --- a/api/options/expirations.mdx +++ b/api/options/expirations.mdx @@ -108,6 +108,30 @@ $expirations = $client->options->expirations("AAPL"); // Display all expiration dates echo $expirations; ``` + + + +```java title="OptionExpirations.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.options.OptionsExpirationsRequest; + +try (MarketDataClient client = new MarketDataClient()) { + client.options().expirations(OptionsExpirationsRequest.of("AAPL")).values().forEach(System.out::println); +} +``` + + + + +```kotlin title="OptionExpirations.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.options.OptionsExpirationsRequest + +MarketDataClient().use { client -> + client.options().expirations(OptionsExpirationsRequest.of("AAPL")).values().forEach(::println) +} +``` + diff --git a/api/options/lookup.mdx b/api/options/lookup.mdx index 8cd8b89..76b3c05 100644 --- a/api/options/lookup.mdx +++ b/api/options/lookup.mdx @@ -100,6 +100,32 @@ $lookup = $client->options->lookup("AAPL 7/28/2023 200 Call"); // Display the OCC symbol echo $lookup; ``` + + + +```java title="OptionLookup.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.options.OptionsLookupRequest; + +try (MarketDataClient client = new MarketDataClient()) { + System.out.println(client.options().lookup(OptionsLookupRequest.of("AAPL 7/28/2023 200 Call")).values()); // "AAPL230728C00200000" +} +``` + + + + +```kotlin title="OptionLookup.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.options.OptionsLookupRequest + +MarketDataClient().use { client -> + val optionSymbol = + client.options().lookup(OptionsLookupRequest.of("AAPL 7/28/2023 200 Call")).values() + println(optionSymbol) // "AAPL230728C00200000" +} +``` + diff --git a/api/options/quotes.mdx b/api/options/quotes.mdx index 43dd4f0..f5e1d3d 100644 --- a/api/options/quotes.mdx +++ b/api/options/quotes.mdx @@ -112,6 +112,30 @@ $quotes = $client->options->quotes("AAPL271217C00250000"); // Display formatted quotes with Greeks echo $quotes; ``` + + + +```java title="OptionQuotes.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.options.OptionsQuoteRequest; + +try (MarketDataClient client = new MarketDataClient()) { + client.options().quote(OptionsQuoteRequest.of("AAPL271217C00250000")).values().forEach(System.out::println); +} +``` + + + + +```kotlin title="OptionQuotes.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.options.OptionsQuoteRequest + +MarketDataClient().use { client -> + client.options().quote(OptionsQuoteRequest.of("AAPL271217C00250000")).values().forEach(::println) +} +``` + diff --git a/api/stocks/bulkcandles.mdx b/api/stocks/bulkcandles.mdx index 11a9c2f..3e1f337 100644 --- a/api/stocks/bulkcandles.mdx +++ b/api/stocks/bulkcandles.mdx @@ -121,6 +121,52 @@ $candles = $client->stocks->bulkCandles( // Display formatted candles summary echo $candles; ``` + + + +:::note +The Java SDK does not yet provide a method for the bulk candles endpoint, so this example uses the JDK's `java.net.http.HttpClient` directly. It will be replaced with a `marketdata-sdk-java` example once bulk support is added. +::: + +```java title="BulkStockCandles.java" +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +HttpClient http = HttpClient.newHttpClient(); +HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("https://api.marketdata.app/v1/stocks/bulkcandles/D/?symbols=AAPL,META,MSFT")) + .header("Authorization", "Bearer " + System.getenv("MARKETDATA_TOKEN")) + .GET() + .build(); +HttpResponse response = http.send(request, HttpResponse.BodyHandlers.ofString()); +System.out.println(response.body()); +``` + + + + +:::note +The Java SDK does not yet provide a method for the bulk candles endpoint, so this example uses the JDK's `java.net.http.HttpClient` directly. It will be replaced with a `marketdata-sdk-java` example once bulk support is added. +::: + +```kotlin title="BulkStockCandles.kt" +import java.net.URI +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpResponse + +val http = HttpClient.newHttpClient() +val request = HttpRequest.newBuilder() + .uri(URI.create("https://api.marketdata.app/v1/stocks/bulkcandles/D/?symbols=AAPL,META,MSFT")) + .header("Authorization", "Bearer ${System.getenv("MARKETDATA_TOKEN")}") + .GET() + .build() +val response = http.send(request, HttpResponse.BodyHandlers.ofString()) +println(response.body()) +``` + diff --git a/api/stocks/candles.mdx b/api/stocks/candles.mdx index 6f1d209..df39758 100644 --- a/api/stocks/candles.mdx +++ b/api/stocks/candles.mdx @@ -113,6 +113,42 @@ $candles = $client->stocks->candles( // Display formatted candles summary echo $candles; ``` + + + +```java title="StockCandles.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockCandlesRequest; +import com.marketdata.sdk.stocks.StockResolution; + +try (MarketDataClient client = new MarketDataClient()) { + // Default resolution is DAILY + var candles = client.stocks().candles( + StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") + .countback(30) + .build()); + candles.values().forEach(System.out::println); +} +``` + + + + +```kotlin title="StockCandles.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockCandlesRequest +import com.marketdata.sdk.stocks.StockResolution + +MarketDataClient().use { client -> + // Default resolution is DAILY + val candles = client.stocks().candles( + StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") + .countback(30) + .build()) + candles.values().forEach(::println) +} +``` + diff --git a/api/stocks/earnings.mdx b/api/stocks/earnings.mdx index 357d923..9a6999c 100644 --- a/api/stocks/earnings.mdx +++ b/api/stocks/earnings.mdx @@ -121,6 +121,30 @@ $earnings = $client->stocks->earnings( // Display formatted earnings summary echo $earnings; ``` + + + +```java title="StockEarnings.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockEarningsRequest; + +try (MarketDataClient client = new MarketDataClient()) { + client.stocks().earnings(StockEarningsRequest.of("AAPL")).values().forEach(System.out::println); +} +``` + + + + +```kotlin title="StockEarnings.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockEarningsRequest + +MarketDataClient().use { client -> + client.stocks().earnings(StockEarningsRequest.of("AAPL")).values().forEach(::println) +} +``` + diff --git a/api/stocks/news.mdx b/api/stocks/news.mdx index cc2381e..b5dcaff 100644 --- a/api/stocks/news.mdx +++ b/api/stocks/news.mdx @@ -119,6 +119,30 @@ $news = $client->stocks->news( // Display formatted news summary echo $news; ``` + + + +```java title="StockNews.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockNewsRequest; + +try (MarketDataClient client = new MarketDataClient()) { + client.stocks().news(StockNewsRequest.of("AAPL")).values().forEach(System.out::println); +} +``` + + + + +```kotlin title="StockNews.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockNewsRequest + +MarketDataClient().use { client -> + client.stocks().news(StockNewsRequest.of("AAPL")).values().forEach(::println) +} +``` + diff --git a/api/stocks/prices.mdx b/api/stocks/prices.mdx index 3ccca6e..e91bbe3 100644 --- a/api/stocks/prices.mdx +++ b/api/stocks/prices.mdx @@ -91,6 +91,30 @@ $prices = $client->stocks->prices("AAPL"); // Display formatted price echo $prices; ``` + + + +```java title="StockPrices.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockPricesRequest; + +try (MarketDataClient client = new MarketDataClient()) { + client.stocks().prices(StockPricesRequest.of("AAPL")).values().forEach(System.out::println); +} +``` + + + + +```kotlin title="StockPrices.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockPricesRequest + +MarketDataClient().use { client -> + client.stocks().prices(StockPricesRequest.of("AAPL")).values().forEach(::println) +} +``` + @@ -162,6 +186,30 @@ $prices = $client->stocks->prices(["AAPL", "META", "MSFT"]); // Display formatted prices summary echo $prices; ``` + + + +```java title="StockPrices.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockPricesRequest; + +try (MarketDataClient client = new MarketDataClient()) { + client.stocks().prices(StockPricesRequest.of("AAPL", "META", "MSFT")).values().forEach(System.out::println); +} +``` + + + + +```kotlin title="StockPrices.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockPricesRequest + +MarketDataClient().use { client -> + client.stocks().prices(StockPricesRequest.of("AAPL", "META", "MSFT")).values().forEach(::println) +} +``` + diff --git a/api/stocks/quotes.mdx b/api/stocks/quotes.mdx index 4185986..38f4a64 100644 --- a/api/stocks/quotes.mdx +++ b/api/stocks/quotes.mdx @@ -110,6 +110,30 @@ $quote = $client->stocks->quote("AAPL"); // Display formatted quote with price, change, bid/ask, volume echo $quote; ``` + + + +```java title="StockQuote.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockQuoteRequest; + +try (MarketDataClient client = new MarketDataClient()) { + client.stocks().quote(StockQuoteRequest.of("AAPL")).values().forEach(System.out::println); +} +``` + + + + +```kotlin title="StockQuote.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockQuoteRequest + +MarketDataClient().use { client -> + client.stocks().quote(StockQuoteRequest.of("AAPL")).values().forEach(::println) +} +``` + @@ -181,6 +205,30 @@ $quotes = $client->stocks->quotes(["AAPL", "META", "MSFT"]); // Display formatted quotes summary echo $quotes; ``` + + + +```java title="StockQuotes.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockQuotesRequest; + +try (MarketDataClient client = new MarketDataClient()) { + client.stocks().quotes(StockQuotesRequest.of("AAPL", "META", "MSFT")).values().forEach(System.out::println); +} +``` + + + + +```kotlin title="StockQuotes.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockQuotesRequest + +MarketDataClient().use { client -> + client.stocks().quotes(StockQuotesRequest.of("AAPL", "META", "MSFT")).values().forEach(::println) +} +``` + diff --git a/api/troubleshooting/logging.mdx b/api/troubleshooting/logging.mdx index 19265a0..7d06bef 100644 --- a/api/troubleshooting/logging.mdx +++ b/api/troubleshooting/logging.mdx @@ -86,6 +86,47 @@ if isinstance(result, MarketDataClientErrorResult): logfile.write(json.dumps(log_data) + "\n") ``` + + + +```java title="Logger.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.exception.MarketDataException; +import com.marketdata.sdk.stocks.StockQuotesRequest; + +// Enable DEBUG logging by setting MARKETDATA_LOGGING_LEVEL=DEBUG in your +// environment. The SDK then logs full request/response diagnostics (including +// the CF-Ray request id) on the "com.marketdata.sdk" java.util.logging logger. +try (MarketDataClient client = new MarketDataClient()) { + var quotes = client.stocks().quotes(StockQuotesRequest.of("AAPL")); + System.out.println(quotes.values()); +} catch (MarketDataException e) { + // The SDK already logged the failing request/response. getSupportInfo() + // bundles the status code, request URL and request id for a bug report. + System.err.println(e.getSupportInfo()); +} +``` + + + + +```kotlin title="Logger.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.exception.MarketDataException +import com.marketdata.sdk.stocks.StockQuotesRequest + +// Enable DEBUG logging via the MARKETDATA_LOGGING_LEVEL=DEBUG environment +// variable; the SDK logs request/response diagnostics on "com.marketdata.sdk". +MarketDataClient().use { client -> + try { + val quotes = client.stocks().quotes(StockQuotesRequest.of("AAPL")) + println(quotes.values()) + } catch (e: MarketDataException) { + System.err.println(e.supportInfo) + } +} +``` + diff --git a/api/troubleshooting/real-time-data.mdx b/api/troubleshooting/real-time-data.mdx index d17e1f3..7229814 100644 --- a/api/troubleshooting/real-time-data.mdx +++ b/api/troubleshooting/real-time-data.mdx @@ -161,6 +161,69 @@ curl -X GET "https://api.marketdata.app/v1/stocks/quotes/AAPL/" \ The `updated` field contains a Unix timestamp. Compare this to the current time to determine if the data is real-time, delayed, or historical. + + + +```java title="CheckDataAge.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.Mode; +import com.marketdata.sdk.stocks.StockQuote; +import com.marketdata.sdk.stocks.StockQuoteRequest; +import java.time.Duration; +import java.time.ZonedDateTime; + +try (MarketDataClient client = new MarketDataClient()) { + StockQuote quote = client.stocks() + .mode(Mode.LIVE) + .quote(StockQuoteRequest.of("AAPL")) + .values().get(0); + + ZonedDateTime updated = quote.updated(); + double ageMinutes = Duration.between(updated, ZonedDateTime.now(updated.getZone())).toSeconds() / 60.0; + + System.out.printf("Data timestamp: %s%n", updated); + System.out.printf("Data age: %.1f minutes%n", ageMinutes); + + if (ageMinutes < 1) { + System.out.println("✅ Real-time data (less than 1 minute old)"); + } else if (ageMinutes < 20) { + System.out.println("⚠️ 15-minute delayed data"); + } else { + System.out.println("❌ Historical data (1 day old or more)"); + } +} +``` + + + + +```kotlin title="CheckDataAge.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.Mode +import com.marketdata.sdk.stocks.StockQuoteRequest +import java.time.Duration +import java.time.ZonedDateTime + +MarketDataClient().use { client -> + val quote = client.stocks() + .mode(Mode.LIVE) + .quote(StockQuoteRequest.of("AAPL")) + .values()[0] + + val updated = quote.updated() ?: error("No timestamp in response") + val ageMinutes = Duration.between(updated, ZonedDateTime.now(updated.zone)).toSeconds() / 60.0 + + println("Data timestamp: $updated") + println("Data age: %.1f minutes".format(ageMinutes)) + + when { + ageMinutes < 1 -> println("✅ Real-time data (less than 1 minute old)") + ageMinutes < 20 -> println("⚠️ 15-minute delayed data") + else -> println("❌ Historical data (1 day old or more)") + } +} +``` + @@ -487,6 +550,77 @@ async function testRealtimeData(symbol: string = "AAPL"): Promise { testRealtimeData("AAPL"); ``` + + + +```java title="TestRealtimeData.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.Mode; +import com.marketdata.sdk.stocks.StockQuote; +import com.marketdata.sdk.stocks.StockQuoteRequest; +import java.time.Duration; +import java.time.ZonedDateTime; + +static boolean testRealtimeData(MarketDataClient client, String symbol) { + StockQuote quote = client.stocks() + .mode(Mode.LIVE) + .quote(StockQuoteRequest.of(symbol)) + .values().get(0); + + ZonedDateTime updated = quote.updated(); + if (updated == null) { + System.out.println("Warning: no timestamp in response"); + return false; + } + + double ageMinutes = Duration.between(updated, ZonedDateTime.now(updated.getZone())).toSeconds() / 60.0; + System.out.printf("%s data age: %.2f minutes%n", symbol, ageMinutes); + + if (ageMinutes < 1) { + System.out.println("✅ Real-time data is working!"); + return true; + } else if (ageMinutes < 20) { + System.out.println("⚠️ Receiving 15-minute delayed data — check the IEX agreement"); + return false; + } else { + System.out.println("❌ Receiving historical data — complete your profile and agreements"); + return false; + } +} +``` + + + + +```kotlin title="TestRealtimeData.kt" +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.Mode +import com.marketdata.sdk.stocks.StockQuoteRequest +import java.time.Duration +import java.time.ZonedDateTime + +fun testRealtimeData(client: MarketDataClient, symbol: String = "AAPL"): Boolean { + val quote = client.stocks() + .mode(Mode.LIVE) + .quote(StockQuoteRequest.of(symbol)) + .values()[0] + + val updated = quote.updated() ?: run { + println("Warning: no timestamp in response") + return false + } + + val ageMinutes = Duration.between(updated, ZonedDateTime.now(updated.zone)).toSeconds() / 60.0 + println("$symbol data age: %.2f minutes".format(ageMinutes)) + + return when { + ageMinutes < 1 -> { println("✅ Real-time data is working!"); true } + ageMinutes < 20 -> { println("⚠️ Receiving 15-minute delayed data — check the IEX agreement"); false } + else -> { println("❌ Receiving historical data — complete your profile and agreements"); false } + } +} +``` + diff --git a/api/troubleshooting/service-outages.mdx b/api/troubleshooting/service-outages.mdx index e95d736..30e8302 100644 --- a/api/troubleshooting/service-outages.mdx +++ b/api/troubleshooting/service-outages.mdx @@ -137,6 +137,45 @@ print(f"Options Chain: {'Online' if options_chain and options_chain['online'] el print(f"Stocks Candles: {'Online' if stocks_candles and stocks_candles['online'] else 'Offline' if stocks_candles else 'Not found'}") ``` + + + +```java title="Status.java" +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.utilities.ServiceStatus; +import java.util.List; + +try (MarketDataClient client = new MarketDataClient()) { + List services = client.utilities().status().values(); + + for (String path : List.of("/v1/stocks/quotes/", "/v1/options/chain/", "/v1/stocks/candles/")) { + String state = services.stream() + .filter(s -> s.service().equals(path)) + .findFirst() + .map(s -> s.online() ? "Online" : "Offline") + .orElse("Not found"); + System.out.println(path + ": " + state); + } +} +``` + + + + +```kotlin title="Status.kt" +import com.marketdata.sdk.MarketDataClient + +MarketDataClient().use { client -> + val services = client.utilities().status().values() + + for (path in listOf("/v1/stocks/quotes/", "/v1/options/chain/", "/v1/stocks/candles/")) { + val svc = services.firstOrNull { it.service() == path } + val state = svc?.let { if (it.online()) "Online" else "Offline" } ?: "Not found" + println("$path: $state") + } +} +``` + diff --git a/api/troubleshooting/too-many-concurrent-requests.mdx b/api/troubleshooting/too-many-concurrent-requests.mdx index 46f3fd5..cada9a0 100644 --- a/api/troubleshooting/too-many-concurrent-requests.mdx +++ b/api/troubleshooting/too-many-concurrent-requests.mdx @@ -175,6 +175,55 @@ function requestWithBackoff(string $url, array $headers, int $maxRetries = 5): a } ``` + + + +```java title="RequestWithBackoff.java" +import com.marketdata.sdk.exception.RateLimitError; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Supplier; + +// The SDK already retries with exponential backoff internally. For a 429 caused +// by too many *concurrent* requests, wrap your call in an outer backoff and +// reduce fan-out. RateLimitError is the SDK's 429; honour getRetryAfter() if set. +static T requestWithBackoff(Supplier call, int maxRetries) throws InterruptedException { + for (int attempt = 0; ; attempt++) { + try { + return call.get(); + } catch (RateLimitError e) { + if (attempt >= maxRetries) throw e; + long backoffMs = (long) (200 * Math.pow(2, attempt)) + + ThreadLocalRandom.current().nextInt(100); + Thread.sleep(e.getRetryAfter().map(d -> d.toMillis()).orElse(backoffMs)); + } + } +} +``` + + + + +```kotlin title="RequestWithBackoff.kt" +import com.marketdata.sdk.exception.RateLimitError +import kotlin.random.Random + +// The SDK already retries internally. For a 429 from too many *concurrent* +// requests, add outer backoff and reduce fan-out. +fun requestWithBackoff(maxRetries: Int = 5, call: () -> T): T { + var attempt = 0 + while (true) { + try { + return call() + } catch (e: RateLimitError) { + if (attempt >= maxRetries) throw e + val backoffMs = (200.0 * Math.pow(2.0, attempt.toDouble())).toLong() + Random.nextInt(100) + Thread.sleep(e.retryAfter.map { it.toMillis() }.orElse(backoffMs)) + attempt++ + } + } +} +``` + diff --git a/api/utilities/headers.mdx b/api/utilities/headers.mdx index 3b2b382..b7a7b40 100644 --- a/api/utilities/headers.mdx +++ b/api/utilities/headers.mdx @@ -87,6 +87,34 @@ $headers = $client->utilities->headers(); // Display all headers echo $headers; ``` + + + +```java title="HeadersCheck.java" +import com.marketdata.sdk.MarketDataClient; + +public class HeadersCheck { + public static void main(String[] args) { + try (MarketDataClient client = new MarketDataClient()) { + System.out.println(client.utilities().headers().values()); + } + } +} +``` + + + + +```kotlin title="HeadersCheck.kt" +import com.marketdata.sdk.MarketDataClient + +fun main() { + MarketDataClient().use { client -> + println(client.utilities().headers().values()) + } +} +``` + diff --git a/api/utilities/status.mdx b/api/utilities/status.mdx index b69f8d8..64f55e7 100644 --- a/api/utilities/status.mdx +++ b/api/utilities/status.mdx @@ -91,6 +91,34 @@ $status = $client->utilities->api_status(); // Display API status for all services echo $status; ``` + + + +```java title="StatusCheck.java" +import com.marketdata.sdk.MarketDataClient; + +public class StatusCheck { + public static void main(String[] args) { + try (MarketDataClient client = new MarketDataClient()) { + client.utilities().status().values().forEach(System.out::println); + } + } +} +``` + + + + +```kotlin title="StatusCheck.kt" +import com.marketdata.sdk.MarketDataClient + +fun main() { + MarketDataClient().use { client -> + client.utilities().status().values().forEach(::println) + } +} +``` + diff --git a/docusaurus.config.js b/docusaurus.config.js index 321203f..3e0e2df 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -274,7 +274,7 @@ const config = { prism: { theme: lightCodeTheme, darkTheme: darkCodeTheme, - additionalLanguages: ['json', 'http', 'php', 'bash', 'excel-formula'], + additionalLanguages: ['json', 'http', 'php', 'bash', 'excel-formula', 'java', 'kotlin', 'groovy'], }, }), }; diff --git a/scripts/export-sdk-docs.js b/scripts/export-sdk-docs.js index 9f8dfd0..6c10a8d 100644 --- a/scripts/export-sdk-docs.js +++ b/scripts/export-sdk-docs.js @@ -15,7 +15,7 @@ const path = require('path'); const { cleanMdx } = require('../lib/mdx-to-md'); const REPO_ROOT = path.resolve(__dirname, '..'); -const SUPPORTED_SDKS = ['js', 'py', 'go', 'php']; +const SUPPORTED_SDKS = ['js', 'py', 'go', 'php', 'java']; function parseArgs(argv) { const args = { sdk: null, out: null }; diff --git a/sdk/index.mdx b/sdk/index.mdx index 9f78413..c26c8b6 100644 --- a/sdk/index.mdx +++ b/sdk/index.mdx @@ -42,6 +42,12 @@ Our SDKs are available for a multitude of programming languages and platforms, s [High performance Market Data SDK integration for enterprise-level backend systems.](/sdk/go) +
+ [![Java SDK Logo](/img/java-logo.svg)](/sdk/java) + ### [Java SDK](/sdk/java) + [Typed Market Data integration for JVM applications, with first-class Kotlin interop.](/sdk/java) +
+ Every SDK is crafted for ease of use, guaranteeing a straightforward setup process with minimal configuration required. diff --git a/sdk/java/authentication.mdx b/sdk/java/authentication.mdx new file mode 100644 index 0000000..b9caa7e --- /dev/null +++ b/sdk/java/authentication.mdx @@ -0,0 +1,142 @@ +--- +title: Authentication +sidebar_position: 2 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +The Market Data API uses a **Bearer Token** for authentication. The token is required for almost every request. Your token should have been e-mailed to you when you first signed up for an account. If you do not have a token or have lost your sign-up email, request a new token from the [Market Data Dashboard](https://www.marketdata.app/dashboard/). + +There are three ways to set your token when using the Java SDK: + +1. Set it from an environment variable _(recommended for production)_ +2. Load it from a `.env` file _(recommended for local development)_ +3. Pass it directly when creating the [client](/sdk/java/client) + +On startup, the SDK looks for the `MARKETDATA_TOKEN` environment variable. If found, it uses that token for all requests. The SDK also loads a `.env` file automatically from the working directory if one is present. + +:::tip +When your code is running in a production environment, we recommend using an environment variable to ensure your token is not stored with your code. This is the most secure way to set your token. +::: + +## How To Set Up The Environment Variable + +### Set The Environment Variable In The Console + + + + +This command sets the environment variable for the current session only. If you open a new terminal or restart your computer, it will not persist. + +```bash +export MARKETDATA_TOKEN="your_api_token" +``` + +#### Make The Variable Persistent + +Add the `export` line to your shell's profile script (`~/.zshrc`, `~/.bashrc`, `~/.bash_profile`, etc.), then restart your terminal or run `source ~/.zshrc` (adjusting for your shell). + + + + +`setx` sets the variable permanently, but it is not available in the current Command Prompt session — open a new one after running it. + +```bash +setx MARKETDATA_TOKEN "your_api_token" +``` + + + + +### Using a .env File + +The SDK automatically loads a `.env` file from your working directory at startup. Create a file named `.env` in your project root: + +```env title=".env" +MARKETDATA_TOKEN=your_api_token +``` + +:::warning +Add `.env` to your `.gitignore` so the token is not committed to source control. +::: + +### Make A Test Request + +Verify your authentication is working by making a test request against `SPY` (or any symbol that requires authentication). Do **not** use `AAPL` to test authentication — `AAPL` is a free test symbol and returns data even when you are not authenticated. + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockQuoteRequest; +import com.marketdata.sdk.exception.AuthenticationError; + +// No need to pass a token here — the SDK reads MARKETDATA_TOKEN automatically. +try (MarketDataClient client = new MarketDataClient()) { + var quote = client.stocks().quote(StockQuoteRequest.of("SPY")).values().get(0); + System.out.println(quote.symbol() + " last=" + quote.last()); +} catch (AuthenticationError e) { + System.out.println("Authentication failed: " + e.getMessage()); +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockQuoteRequest +import com.marketdata.sdk.exception.AuthenticationError + +MarketDataClient().use { client -> + try { + val quote = client.stocks().quote(StockQuoteRequest.of("SPY")).values()[0] + println("${quote.symbol()} last=${quote.last()}") + } catch (e: AuthenticationError) { + println("Authentication failed: ${e.message}") + } +} +``` + + + + +## Passing the Token Directly + +If you prefer to pass the token explicitly (not recommended for production code), use the four-argument constructor `(apiKey, baseUrl, apiVersion, validateOnStartup)`. Pass `null` for any slot you want resolved from the cascade or left at its default. + + + + +```java +import com.marketdata.sdk.MarketDataClient; + +try (MarketDataClient client = + new MarketDataClient("your_token_here", null, null, true)) { + // ... make requests +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient + +MarketDataClient("your_token_here", null, null, true).use { client -> + // ... make requests +} +``` + + + + +:::info Demo mode +If no token is found anywhere in the cascade, the SDK runs in **demo mode** — startup validation is skipped and you can call the free, public endpoints (such as `AAPL` quotes and `utilities().status()`). +::: + +## Next Steps + +After successful authentication, read the overview of how the [client](/sdk/java/client) works, then configure [Settings](/sdk/java/settings) to customize output format, date format, and other universal parameters. diff --git a/sdk/java/client.mdx b/sdk/java/client.mdx new file mode 100644 index 0000000..f24caf6 --- /dev/null +++ b/sdk/java/client.mdx @@ -0,0 +1,399 @@ +--- +title: Client +sidebar_position: 3 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +The `MarketDataClient` is the entry point to the SDK. It handles API requests, response parsing, rate-limit tracking, retries with exponential backoff, and logging. A single client gives you access to all five resources: stocks, options, funds, markets, and utilities. + +The client is immutable, thread-safe, and `AutoCloseable`. Create one per application (it holds a shared HTTP client and a 50-request concurrency pool) and close it when you're done. + +### Get Started Quickly + +1. Review the [authentication documentation](/sdk/java/authentication) to learn how to set your API token. +2. Create a [`MarketDataClient`](#MarketDataClient) and use it to make requests. +3. Reach a resource through `client.stocks()`, `client.options()`, etc. +4. Track your [rate limit](#rate-limits) to see how many API credits remain. +5. Configure [Settings](/sdk/java/settings) to customize output format, date format, and other universal parameters. + + +## MarketDataClient + +```java +public final class MarketDataClient implements AutoCloseable { + + // Production: resolves everything from the configuration cascade and + // validates the token on startup. + public MarketDataClient(); + + // Full control: any null falls back to the cascade / default for that slot. + public MarketDataClient( + @Nullable String apiKey, + @Nullable String baseUrl, // default: https://api.marketdata.app + @Nullable String apiVersion, // default: v1 + boolean validateOnStartup); + + // Resources + public StocksResource stocks(); + public OptionsResource options(); + public FundsResource funds(); + public MarketsResource markets(); + public UtilitiesResource utilities(); + + // Latest client-wide rate-limit snapshot (or null before the first request) + public @Nullable RateLimitSnapshot getRateLimits(); + + // Releases the shared HTTP client + @Override public void close(); +} +``` + +### Constructors + +The **no-arg constructor** is what you'll use in production. It resolves the token and settings from the [configuration cascade](/sdk/java/settings#configuration-cascade) and fires a single `GET /user/` request to validate the token before returning. + +The **four-arg constructor** `(apiKey, baseUrl, apiVersion, validateOnStartup)` lets you set everything explicitly — handy for tests, short-lived runtimes, and serverless platforms where you want to skip the startup round-trip (`validateOnStartup = false`). + + + + +```java +import com.marketdata.sdk.MarketDataClient; + +// Production: token from the cascade, validated on startup. +try (MarketDataClient client = new MarketDataClient()) { + // ... +} + +// Explicit, no startup validation (e.g. on a Lambda cold start): +try (MarketDataClient client = + new MarketDataClient("your_token", null, null, false)) { + // ... +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient + +MarketDataClient().use { client -> + // ... +} + +MarketDataClient("your_token", null, null, false).use { client -> + // ... +} +``` + + + + +:::info Fail-fast configuration +A misconfigured base URL or API version is rejected with an `IllegalArgumentException` **at construction**, not later on the first request. Tokens are never printed in full — the client's `toString()` redacts them (long tokens show only the last four characters; short ones are hidden entirely). +::: + +## Sync and Async + +Every endpoint comes in two variants: a blocking call (e.g. `status()`) and an async one (e.g. `statusAsync()`) returning a `CompletableFuture`. They share the same validation, retry, and rate-limit logic — pick whichever fits your code. Internally the SDK is async-first; the sync method is a thin wrapper that joins the future and unwraps the underlying exception. + +Async pays off when you fan out several requests: total time is about the slowest single call, not the sum. + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.UtilitiesStatusResponse; +import java.util.concurrent.CompletableFuture; + +try (MarketDataClient client = new MarketDataClient()) { + + // Sync — blocks and returns the typed response. + var sync = client.utilities().status(); + System.out.println(sync.values().size() + " services"); + + // Async — returns immediately with a future. Attach a callback, or join(). + CompletableFuture future = client.utilities().statusAsync(); + future.thenAccept(resp -> System.out.println(resp.values().size() + " services")); + + // Fan out several calls in parallel and wait for all of them. + var a = client.utilities().statusAsync(); + var b = client.utilities().statusAsync(); + CompletableFuture.allOf(a, b).join(); +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import java.util.concurrent.CompletableFuture + +MarketDataClient().use { client -> + // Sync + val sync = client.utilities().status() + println("${sync.values().size} services") + + // Async — returns a CompletableFuture (coroutine users can await() it + // via kotlinx-coroutines-jdk8). + client.utilities().statusAsync() + .thenAccept { resp -> println("${resp.values().size} services") } + .join() +} +``` + + + + +### Composing async calls + +The point of the async variants is to **stay non-blocking** — chain off the future instead of calling `join()` in the middle. Use `thenApply` to transform a result, `thenCompose` to chain a dependent call, and fire independent calls together when they don't depend on each other. + + + + +```java +import com.marketdata.sdk.options.OptionsLookupRequest; +import com.marketdata.sdk.options.OptionsQuoteRequest; +import com.marketdata.sdk.stocks.StockQuoteRequest; +import java.util.concurrent.CompletableFuture; + +// thenApply — transform the result without blocking. +CompletableFuture lastPrice = client.stocks() + .quoteAsync(StockQuoteRequest.of("AAPL")) + .thenApply(resp -> resp.values().get(0).last()); + +// thenCompose — chain a dependent async call (resolve a symbol, then quote it). +var optionQuote = client.options() + .lookupAsync(OptionsLookupRequest.of("AAPL 1/16/2026 $200 Call")) + .thenCompose(sym -> client.options().quoteAsync(OptionsQuoteRequest.of(sym.values()))); + +// Independent calls in parallel, then use every result. +var aapl = client.stocks().quoteAsync(StockQuoteRequest.of("AAPL")); +var msft = client.stocks().quoteAsync(StockQuoteRequest.of("MSFT")); +CompletableFuture.allOf(aapl, msft).join(); // wait for both +double spread = aapl.join().values().get(0).last() - msft.join().values().get(0).last(); +``` + + + + +```kotlin +import com.marketdata.sdk.options.OptionsLookupRequest +import com.marketdata.sdk.options.OptionsQuoteRequest +import com.marketdata.sdk.stocks.StockQuoteRequest +import java.util.concurrent.CompletableFuture + +// thenApply — transform the result without blocking. +val lastPrice = client.stocks() + .quoteAsync(StockQuoteRequest.of("AAPL")) + .thenApply { resp -> resp.values()[0].last() } + +// thenCompose — chain a dependent async call. +val optionQuote = client.options() + .lookupAsync(OptionsLookupRequest.of("AAPL 1/16/2026 \$200 Call")) + .thenCompose { sym -> client.options().quoteAsync(OptionsQuoteRequest.of(sym.values())) } + +// Independent calls in parallel, then use every result. +val aapl = client.stocks().quoteAsync(StockQuoteRequest.of("AAPL")) +val msft = client.stocks().quoteAsync(StockQuoteRequest.of("MSFT")) +CompletableFuture.allOf(aapl, msft).join() +val spread = aapl.join().values()[0].last()!! - msft.join().values()[0].last()!! +``` + + + + +### Handling errors in async + +When a request fails, the future completes **exceptionally**. Inside `exceptionally()`, `handle()`, or `whenComplete()`, the throwable you receive is a `java.util.concurrent.CompletionException` — call `getCause()` to reach the underlying `MarketDataException`. (The blocking variants do this unwrapping for you, which is why `try/catch` around a sync call catches the `MarketDataException` directly.) + + + + +```java +import com.marketdata.sdk.exception.RateLimitError; +import com.marketdata.sdk.stocks.StockQuoteRequest; +import java.util.concurrent.CompletionException; + +client.stocks().quoteAsync(StockQuoteRequest.of("AAPL")) + .thenApply(resp -> resp.values().get(0).last()) + .exceptionally(ex -> { + // `ex` is a CompletionException; the real cause is the MarketDataException. + Throwable cause = (ex instanceof CompletionException && ex.getCause() != null) + ? ex.getCause() : ex; + if (cause instanceof RateLimitError) { + System.out.println("Rate limited — backing off"); + } + return null; // fallback value the rest of the chain will see + }) + .join(); + +// handle((value, throwable) -> ...) sees both outcomes in one place; use it when +// you want to map success and failure to the same result type. +``` + + + + +```kotlin +import com.marketdata.sdk.exception.RateLimitError +import com.marketdata.sdk.stocks.StockQuoteRequest +import java.util.concurrent.CompletionException + +client.stocks().quoteAsync(StockQuoteRequest.of("AAPL")) + .thenApply { resp -> resp.values()[0].last() } + .exceptionally { ex -> + val cause = if (ex is CompletionException && ex.cause != null) ex.cause else ex + if (cause is RateLimitError) println("Rate limited — backing off") + null + } + .join() +``` + + + + +:::note `join()` vs `get()` +To block on a future, prefer **`join()`** — it throws an unchecked `CompletionException`. `get()` does the same thing but throws the checked `ExecutionException` (plus `InterruptedException`), forcing a `try/catch`. Both wrap the underlying cause identically, so `join()` is usually the friendlier choice. +::: + +## The Response Object + +Every endpoint returns a typed response that wraps the decoded data plus request metadata, with a uniform surface regardless of endpoint or format. + +```java +public interface MarketDataResponse { + T values(); // the typed payload (e.g. List) + int statusCode(); // 200, 203, or 404 + boolean isNoData(); // true on a 404 "no_data" response + @Nullable String requestId(); // server request id, for support tickets + java.net.URI requestUrl(); // the absolute request URL + @Nullable RateLimitSnapshot rateLimit(); // this request's rate limit + String json(); // the raw response body, as sent + boolean isJson(); + boolean isCsv(); + boolean isHtml(); + void saveToFile(java.nio.file.Path path); // write the raw body verbatim +} +``` + +```java +var response = client.stocks().quote(StockQuoteRequest.of("AAPL")); + +response.values(); // List — the part you usually want +response.statusCode(); // 200 +response.requestId(); // e.g. for a support ticket +response.rateLimit(); // this request's rate-limit snapshot (may be null) +response.saveToFile(Path.of("aapl.json")); // cache the raw body +``` + +## Error Handling + +Everything the SDK throws is a `MarketDataException`. It is a **sealed** hierarchy — the seven subtypes below are the complete set, so you can branch on them exhaustively and the compiler will tell you if a future major version adds one. Each exception carries support context: `getStatusCode()`, `getRequestId()`, `getRequestUrl()`, and `getSupportInfo()`. + +| Subtype | When it's thrown | +|---|---| +| `AuthenticationError` | Missing or invalid token (HTTP 401) | +| `BadRequestError` | Invalid parameters (HTTP 4xx) | +| `NotFoundError` | The resource doesn't exist (HTTP 404) | +| `RateLimitError` | Quota exceeded (HTTP 429); see `getRetryAfter()` | +| `ServerError` | API-side failure (HTTP 5xx) | +| `NetworkError` | Connection failure or timeout | +| `ParseError` | The response could not be decoded | + +Async calls surface the same exceptions through the `CompletableFuture` (wrapped in a `CompletionException`); the sync wrappers unwrap them so you catch the cause directly. + + + + +```java +import com.marketdata.sdk.exception.*; + +try { + var quote = client.stocks().quote(StockQuoteRequest.of("AAPL")); +} catch (AuthenticationError e) { + System.out.println("Check your token"); +} catch (RateLimitError e) { + System.out.println("Rate limited — retry after " + + e.getRetryAfter().map(d -> d.toSeconds() + "s").orElse("a moment")); +} catch (MarketDataException e) { + // Any other case. Attach getSupportInfo() to a bug report. + System.out.println(e.getSupportInfo()); +} +``` + + + + +```java +import com.marketdata.sdk.exception.*; + +// On JDK 21+ the sealed hierarchy enables an exhaustive switch — the compiler +// rejects the code if any subtype is left unhandled. +try { + var quote = client.stocks().quote(StockQuoteRequest.of("AAPL")); +} catch (MarketDataException e) { + String message = switch (e) { + case AuthenticationError a -> "Authentication failed — check your token"; + case BadRequestError b -> "Bad request — check your parameters"; + case NotFoundError n -> "Not found"; + case RateLimitError r -> "Rate limited"; + case ServerError s -> "Server error (HTTP " + s.getStatusCode() + ")"; + case NetworkError n -> "Network problem — is the API reachable?"; + case ParseError p -> "Could not parse the response"; + }; + System.out.println(message); +} +``` + + + + +```kotlin +import com.marketdata.sdk.exception.* + +try { + val quote = client.stocks().quote(StockQuoteRequest.of("AAPL")) +} catch (e: AuthenticationError) { + println("Check your token") +} catch (e: RateLimitError) { + println("Rate limited") +} catch (e: MarketDataException) { + println(e.supportInfo) +} +``` + + + + + +## Rate Limits + +The SDK tracks your rate limit from the `x-api-ratelimit-*` headers on every response. + +- **Per-request:** `response.rateLimit()` reflects the headers of that specific response. +- **Client-wide:** `client.getRateLimits()` returns the latest snapshot seen across all requests. + +```java +public record RateLimitSnapshot(int limit, int remaining, Instant reset, int consumed) {} +``` + +```java +var response = client.stocks().quote(StockQuoteRequest.of("AAPL")); +var rl = response.rateLimit(); +if (rl != null) { + System.out.println(rl.remaining() + " / " + rl.limit() + " requests left"); +} +``` + +The SDK also fails fast: if a prior response reported `remaining = 0` and the reset window has not yet elapsed, the next request raises a `RateLimitError` before hitting the network. + +## Closing the Client + +`MarketDataClient` is `AutoCloseable`. Prefer try-with-resources (Java) or `use {}` (Kotlin) so the underlying HTTP client is released. For a long-lived, application-scoped client, call `close()` on shutdown. diff --git a/sdk/java/funds/candles.mdx b/sdk/java/funds/candles.mdx new file mode 100644 index 0000000..82bc081 --- /dev/null +++ b/sdk/java/funds/candles.mdx @@ -0,0 +1,105 @@ +--- +title: Candles +sidebar_position: 1 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve a mutual fund's NAV (net asset value) OHLC series. + +## Making Requests + +Use the `candles()` method on the `funds` resource, built with `FundCandlesRequest`. Funds report NAV, not traded volume, so there is **no volume column**, and only daily-and-coarser resolutions are available (no intraday). + +```java +FundCandlesResponse candles(FundCandlesRequest request) +CompletableFuture candlesAsync(FundCandlesRequest request) +``` + +### FundCandlesRequest + +```java +FundCandlesRequest.of(FundResolution resolution, String symbol) +FundCandlesRequest.builder(FundResolution resolution, String symbol) + .date(LocalDate date) // a single day + .from(LocalDate from) // window start (inclusive) + .to(LocalDate to) // window end (exclusive) + .countback(int n) // N candles before `to` + .build() +``` + +### FundResolution + +A value type for the candle interval — daily and coarser only: + +```java +FundResolution.DAILY // also WEEKLY, MONTHLY, YEARLY +FundResolution.days(1) +FundResolution.weeks(1) +FundResolution.of("D") // any raw wire token +``` + +#### Returns + +`FundCandlesResponse` wrapping `List`: + +```java +public record FundCandle( + @Nullable ZonedDateTime time, // bar opening moment (America/New_York) + @Nullable Double open, // NAV at open + @Nullable Double high, + @Nullable Double low, + @Nullable Double close) // NAV at close — note: no volume +``` + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.funds.FundCandle; +import com.marketdata.sdk.funds.FundCandlesRequest; +import com.marketdata.sdk.funds.FundResolution; +import java.time.LocalDate; + +try (MarketDataClient client = new MarketDataClient()) { + var candles = client.funds().candles( + FundCandlesRequest.builder(FundResolution.DAILY, "VFINX") + .from(LocalDate.now().minusWeeks(2)) + .to(LocalDate.now()) + .build()); + + for (FundCandle bar : candles.values()) { + System.out.printf("%s O=%.2f H=%.2f L=%.2f C=%.2f%n", + bar.time(), bar.open(), bar.high(), bar.low(), bar.close()); + } +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.funds.FundCandlesRequest +import com.marketdata.sdk.funds.FundResolution +import java.time.LocalDate + +MarketDataClient().use { client -> + val candles = client.funds().candles( + FundCandlesRequest.builder(FundResolution.DAILY, "VFINX") + .from(LocalDate.now().minusWeeks(2)) + .to(LocalDate.now()) + .build()) + + for (bar in candles.values()) { + println("${bar.time()} close=${bar.close()}") + } +} +``` + + + diff --git a/sdk/java/funds/index.mdx b/sdk/java/funds/index.mdx new file mode 100644 index 0000000..d4dc5b5 --- /dev/null +++ b/sdk/java/funds/index.mdx @@ -0,0 +1,16 @@ +--- +title: Funds +slug: /java/funds +sidebar_position: 30 +--- + +The Java SDK from Market Data provides methods to streamline your use of the Funds endpoints. These methods provide a typed interface over the underlying HTTP requests and responses, with both sync and async variants. + +Reach the resource through `client.funds()`. For CSV output use `client.funds().asCsv()`. + +## Funds Endpoints + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from "@docusaurus/theme-common"; + + diff --git a/sdk/java/index.mdx b/sdk/java/index.mdx new file mode 100644 index 0000000..d3dbb12 --- /dev/null +++ b/sdk/java/index.mdx @@ -0,0 +1,77 @@ +--- +title: Java SDK +sidebar_position: 6 +slug: /java +sidebar_custom_props: + badge: n +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Welcome to the Market Data Java SDK documentation. This SDK lets you integrate Market Data services into any JVM application. It ships typed, records-based responses, sync **and** async (`CompletableFuture`) variants for every endpoint, automatic retries and rate-limit tracking, and a sealed exception hierarchy you can branch on exhaustively. + +The SDK is written in Java (JDK 17+) with no Kotlin runtime dependency, but it is designed to be **idiomatic from Kotlin too**: nullability is annotated (no platform types), the client is `AutoCloseable` (so `use {}` works), and async methods return `CompletableFuture`, which Kotlin interops with directly. + +## Quick Start + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockQuoteRequest; + +// The no-arg constructor reads MARKETDATA_TOKEN from the environment (or a .env file) +// and validates it on startup. The client is AutoCloseable. +try (MarketDataClient client = new MarketDataClient()) { + + // A single quote — quote(...) returns a list; a single symbol is row 0. + var quote = client.stocks().quote(StockQuoteRequest.of("AAPL")).values().get(0); + System.out.println(quote.symbol() + " last=" + quote.last()); +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockQuoteRequest + +// `use {}` closes the client when the block ends — the Kotlin equivalent of try-with-resources. +MarketDataClient().use { client -> + val quote = client.stocks().quote(StockQuoteRequest.of("AAPL")).values()[0] + println("${quote.symbol()} last=${quote.last()}") +} +``` + + + + +## Open Source + +The SDK is open source and available on GitHub. Feel free to contribute to the project, report bugs, or request new features. + +- [Java SDK on GitHub](https://github.com/MarketDataApp/sdk-java/) + +## Documentation + +The best source for documentation on the SDK is right here. This documentation is the most up-to-date and accurate source of information on the SDK. + +## Using the SDK + +This SDK is designed to help you get up and running with Market Data's APIs as quickly as possible, providing all the tools you need to access real-time stock and options prices, historical data, and much more. + +### Getting Started + +1. [Install the SDK](/sdk/java/installation) into a Gradle or Maven project. +2. Set up your [authentication token](/sdk/java/authentication) to access the API. +3. Learn about the [client](/sdk/java/client) and how to make your first API requests. +4. Configure [Settings](/sdk/java/settings) to customize output format, date format, and other universal parameters. +5. Explore the available endpoints for [stocks](/sdk/java/stocks), [options](/sdk/java/options), [funds](/sdk/java/funds), [markets](/sdk/java/markets), and [utilities](/sdk/java/utilities). + +### Support + +- If you have any questions or need further assistance, please don't hesitate to open a ticket at our [helpdesk](https://www.marketdata.app/dashboard/). +- If you find a bug you may also [open an issue in our GitHub repository](https://github.com/MarketDataApp/sdk-java/issues). Please only open issues if you find a bug. Use our helpdesk for general questions or implementation assistance. diff --git a/sdk/java/installation.mdx b/sdk/java/installation.mdx new file mode 100644 index 0000000..25faa6a --- /dev/null +++ b/sdk/java/installation.mdx @@ -0,0 +1,84 @@ +--- +title: Installation +sidebar_position: 1 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +This guide will help you install the Market Data Java SDK and configure it for your project. + +## Prerequisites + +- **JDK 17 or newer.** The SDK is compiled for Java 17 and is tested on JDK 17, 21, and 25. +- A build tool: [Gradle](https://gradle.org/) or [Maven](https://maven.apache.org/). + +## Basic Installation + +Add the SDK as a dependency. The published artifact is a single JAR; it does **not** pull in a Kotlin standard library or a third-party HTTP client. + + + + +```kotlin +// build.gradle.kts +dependencies { + implementation("app.marketdata:marketdata-sdk-java:0.1.0") +} +``` + + + + +```groovy +// build.gradle +dependencies { + implementation 'app.marketdata:marketdata-sdk-java:0.1.0' +} +``` + + + + +```xml + + app.marketdata + marketdata-sdk-java + 0.1.0 + +``` + + + + +:::tip +Always check the [latest release](https://github.com/MarketDataApp/sdk-java/releases) for the current version number. +::: + +:::note +The Maven **groupId** is `app.marketdata` (the published Maven Central namespace), while the Java **package** you import from is `com.marketdata.sdk` — for example `import com.marketdata.sdk.MarketDataClient;`. The two are intentionally different. +::: + +## Kotlin Support + +Although the SDK is written in Java, Kotlin consumers are a first-class audience: + +- The public API is null-annotated, so Kotlin sees real nullable/non-null types — not platform types (`String!`). +- `MarketDataClient` is `AutoCloseable`, so you can use `client.use { ... }`. +- Async methods return `java.util.concurrent.CompletableFuture`. If you use coroutines, bridge it with `kotlinx-coroutines-jdk8`'s `await()` — the SDK does **not** depend on coroutines itself. + +## Requirements & Dependencies + +The SDK keeps its dependency footprint small: + +- **No third-party HTTP client.** It uses the JDK's built-in `java.net.http.HttpClient` (HTTP/2). +- **Jackson** (`jackson-databind`) for JSON decoding. +- **JSpecify** nullability annotations — compile-time only, with no runtime cost. + +## Next Steps + +After installation, you'll need to: + +1. Set up your [authentication token](/sdk/java/authentication). +2. Learn about the [client](/sdk/java/client) and how to make your first API requests. +3. Configure [Settings](/sdk/java/settings) to customize output format, date format, and other universal parameters. diff --git a/sdk/java/markets/index.mdx b/sdk/java/markets/index.mdx new file mode 100644 index 0000000..d0785a1 --- /dev/null +++ b/sdk/java/markets/index.mdx @@ -0,0 +1,16 @@ +--- +title: Markets +slug: /java/markets +sidebar_position: 40 +--- + +The Java SDK from Market Data provides methods to streamline your use of the Markets endpoints. These methods provide a typed interface over the underlying HTTP requests and responses, with both sync and async variants. + +Reach the resource through `client.markets()`. For CSV output use `client.markets().asCsv()`. + +## Markets Endpoints + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from "@docusaurus/theme-common"; + + diff --git a/sdk/java/markets/status.mdx b/sdk/java/markets/status.mdx new file mode 100644 index 0000000..dc2973a --- /dev/null +++ b/sdk/java/markets/status.mdx @@ -0,0 +1,95 @@ +--- +title: Status +sidebar_position: 1 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve the exchange open/closed calendar — whether the market was (or will be) open on a given day. + +:::note +This is distinct from [`utilities().status()`](/sdk/java/utilities/status), which reports the Market Data **API's** own service health. +::: + +## Making Requests + +Use the `status()` method on the `markets` resource, built with `MarketStatusRequest`. Every parameter is optional; a bare request returns today's status for the US market. + +```java +MarketStatusResponse status(MarketStatusRequest request) +CompletableFuture statusAsync(MarketStatusRequest request) +``` + +### MarketStatusRequest + +```java +MarketStatusRequest.of() // today, US +MarketStatusRequest.builder() + .country(String country) // ISO 3166, 2-letter (default: US) + .date(LocalDate date) // a single day + .from(LocalDate from) // range start (inclusive) + .to(LocalDate to) // range end (inclusive) + .countback(int n) // N days before `to` + .build() +``` + +#### Returns + +`MarketStatusResponse` wrapping `List`: + +```java +public record MarketStatus( + @Nullable ZonedDateTime date, + @Nullable String status) // "open" / "closed" / null (outside coverage) +// also: boolean isOpen() +``` + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.markets.MarketStatus; +import com.marketdata.sdk.markets.MarketStatusRequest; +import java.time.LocalDate; + +try (MarketDataClient client = new MarketDataClient()) { + var status = client.markets().status( + MarketStatusRequest.builder() + .from(LocalDate.now().minusDays(7)) + .to(LocalDate.now()) + .build()); + + for (MarketStatus day : status.values()) { + System.out.println(day.date().toLocalDate() + " " + day.status() + + (day.isOpen() ? " (trading)" : "")); + } +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.markets.MarketStatusRequest +import java.time.LocalDate + +MarketDataClient().use { client -> + val status = client.markets().status( + MarketStatusRequest.builder() + .from(LocalDate.now().minusDays(7)) + .to(LocalDate.now()) + .build()) + + for (day in status.values()) { + println("${day.date().toLocalDate()} ${day.status()}") + } +} +``` + + + diff --git a/sdk/java/options/chain.mdx b/sdk/java/options/chain.mdx new file mode 100644 index 0000000..99d5ed1 --- /dev/null +++ b/sdk/java/options/chain.mdx @@ -0,0 +1,131 @@ +--- +title: Chain +sidebar_position: 5 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve a full option chain for an underlying, with a rich set of filters. The chain is where most option workflows start. + +## Making Requests + +Use the `chain()` method on the `options` resource, built with `OptionsChainRequest`. + +```java +OptionsChainResponse chain(OptionsChainRequest request) +CompletableFuture chainAsync(OptionsChainRequest request) +``` + +### OptionsChainRequest + +The chain has two mutually-exclusive filter axes modeled as **sealed types**, so the compiler lets you pick exactly one variant of each: + +```java +OptionsChainRequest.builder(String symbol) + + // Expiration selection (sealed ExpirationFilter — pick one): + .expirationFilter(ExpirationFilter.onDate(LocalDate date)) + .expirationFilter(ExpirationFilter.dte(int days)) // days-to-expiration + .expirationFilter(ExpirationFilter.between(LocalDate from, LocalDate to)) + .expirationFilter(ExpirationFilter.all()) // every expiration + // (if omitted, the API narrows to the front month) + + // Strike selection (sealed StrikeFilter — pick one): + .strikeFilter(StrikeFilter.exact(double price)) + .strikeFilter(StrikeFilter.range(double min, double max)) + + // Additive filters: + .side(OptionSide.CALL) // CALL or PUT + .strikeRange(StrikeRange.ITM) // ITM / OTM / ATM + .strikeLimit(int limit) // max strikes per side + .delta(double delta) + .minOpenInterest(long oi) + .minVolume(long vol) + .date(LocalDate date) // historical chain + .build() +``` + + +#### Returns + +`OptionsChainResponse` wrapping `List`: + +```java +public record OptionQuote( + @Nullable String optionSymbol, @Nullable String underlying, + @Nullable ZonedDateTime expiration, @Nullable String side, @Nullable Double strike, + @Nullable ZonedDateTime firstTraded, @Nullable Integer dte, @Nullable ZonedDateTime updated, + @Nullable Double bid, @Nullable Long bidSize, + @Nullable Double mid, @Nullable Double ask, @Nullable Long askSize, + @Nullable Double last, @Nullable Long openInterest, @Nullable Long volume, + @Nullable Boolean inTheMoney, @Nullable Double intrinsicValue, @Nullable Double extrinsicValue, + @Nullable Double underlyingPrice, @Nullable Double iv, + // greeks: + @Nullable Double delta, @Nullable Double gamma, @Nullable Double theta, + @Nullable Double vega, @Nullable Double rho) +``` + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.options.ExpirationFilter; +import com.marketdata.sdk.options.OptionQuote; +import com.marketdata.sdk.options.OptionSide; +import com.marketdata.sdk.options.OptionsChainRequest; +import com.marketdata.sdk.options.StrikeFilter; +import java.util.List; + +try (MarketDataClient client = new MarketDataClient()) { + + // The 5 calls nearest the money. + List chain = client.options().chain( + OptionsChainRequest.builder("AAPL") + .side(OptionSide.CALL) + .strikeLimit(5) + .build()) + .values(); + + for (OptionQuote c : chain) { + System.out.println(c.optionSymbol() + " strike=" + c.strike() + + " bid/ask=" + c.bid() + "/" + c.ask() + " delta=" + c.delta()); + } + + // Sealed filters: contracts within 45 days, strikes between 150 and 250. + var filtered = client.options().chain( + OptionsChainRequest.builder("AAPL") + .expirationFilter(ExpirationFilter.dte(45)) + .strikeFilter(StrikeFilter.range(150, 250)) + .side(OptionSide.CALL) + .build()); +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.options.OptionSide +import com.marketdata.sdk.options.OptionsChainRequest + +MarketDataClient().use { client -> + val chain = client.options().chain( + OptionsChainRequest.builder("AAPL") + .side(OptionSide.CALL) + .strikeLimit(5) + .build()) + .values() + + for (c in chain) { + println("${c.optionSymbol()} strike=${c.strike()} delta=${c.delta()}") + } +} +``` + + + diff --git a/sdk/java/options/expirations.mdx b/sdk/java/options/expirations.mdx new file mode 100644 index 0000000..bb2c820 --- /dev/null +++ b/sdk/java/options/expirations.mdx @@ -0,0 +1,67 @@ +--- +title: Expirations +sidebar_position: 2 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +List the available expiration dates for an underlying. + +## Making Requests + +Use the `expirations()` method on the `options` resource, built with `OptionsExpirationsRequest`. + +```java +OptionsExpirationsResponse expirations(OptionsExpirationsRequest request) +CompletableFuture expirationsAsync(OptionsExpirationsRequest request) +``` + +### OptionsExpirationsRequest + +```java +OptionsExpirationsRequest.of(String symbol) +OptionsExpirationsRequest.builder(String symbol) + .strike(double strike) // only expirations that list this strike + .date(LocalDate date) // historical: the calendar as it stood on this date + .build() +``` + +#### Returns + +`OptionsExpirationsResponse` wrapping `List` — the expiration dates. + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.options.OptionsExpirationsRequest; +import java.time.ZonedDateTime; + +try (MarketDataClient client = new MarketDataClient()) { + var expirations = client.options().expirations(OptionsExpirationsRequest.of("AAPL")); + System.out.println("AAPL has " + expirations.values().size() + " expirations"); + for (ZonedDateTime exp : expirations.values()) { + System.out.println(" " + exp.toLocalDate()); + } +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.options.OptionsExpirationsRequest + +MarketDataClient().use { client -> + val expirations = client.options().expirations(OptionsExpirationsRequest.of("AAPL")) + println("AAPL has ${expirations.values().size} expirations") +} +``` + + + diff --git a/sdk/java/options/index.mdx b/sdk/java/options/index.mdx new file mode 100644 index 0000000..0d732ee --- /dev/null +++ b/sdk/java/options/index.mdx @@ -0,0 +1,16 @@ +--- +title: Options +slug: /java/options +sidebar_position: 20 +--- + +The Java SDK from Market Data provides methods to streamline your use of the Options endpoints. These methods provide a typed interface over the underlying HTTP requests and responses, with both sync and async variants. + +Reach the resource through `client.options()`. For CSV output use `client.options().asCsv()`. + +## Options Endpoints + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from "@docusaurus/theme-common"; + + diff --git a/sdk/java/options/lookup.mdx b/sdk/java/options/lookup.mdx new file mode 100644 index 0000000..eba3486 --- /dev/null +++ b/sdk/java/options/lookup.mdx @@ -0,0 +1,63 @@ +--- +title: Lookup +sidebar_position: 1 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Turn a human-readable option description into a well-formed OCC option symbol. + +## Making Requests + +Use the `lookup()` method on the `options` resource, built with `OptionsLookupRequest`. + +```java +OptionsLookupResponse lookup(OptionsLookupRequest request) +CompletableFuture lookupAsync(OptionsLookupRequest request) +``` + +### OptionsLookupRequest + +```java +OptionsLookupRequest.of(String userInput) +``` + +#### Returns + +`OptionsLookupResponse` wrapping a `String` — the OCC option symbol. Read it with `.values()`. + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.options.OptionsLookupRequest; + +try (MarketDataClient client = new MarketDataClient()) { + String occSymbol = client.options() + .lookup(OptionsLookupRequest.of("AAPL 1/16/2026 $200 Call")) + .values(); + System.out.println("Resolved to: " + occSymbol); // AAPL260116C00200000 +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.options.OptionsLookupRequest + +MarketDataClient().use { client -> + val occSymbol = client.options() + .lookup(OptionsLookupRequest.of("AAPL 1/16/2026 \$200 Call")) + .values() + println("Resolved to: $occSymbol") +} +``` + + + diff --git a/sdk/java/options/quotes.mdx b/sdk/java/options/quotes.mdx new file mode 100644 index 0000000..b9b693f --- /dev/null +++ b/sdk/java/options/quotes.mdx @@ -0,0 +1,96 @@ +--- +title: Quotes +sidebar_position: 4 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve quotes (bid, ask, last, greeks, etc.) for one or more option contracts by OCC symbol. + +## Making Requests + +The `options` resource offers two quote methods: + +- `quote(...)` — a single contract, built with `OptionsQuoteRequest`. +- `quotes(...)` — several contracts. Unlike stocks, options quotes **fan out one request per symbol concurrently**, so the result is a `Map` keyed by OCC symbol (insertion order preserved). + +```java +OptionsQuotesResponse quote(OptionsQuoteRequest request) +CompletableFuture quoteAsync(OptionsQuoteRequest request) + +Map quotes(OptionsQuotesRequest request) +CompletableFuture> quotesAsync(OptionsQuotesRequest request) +``` + +### Request types + +```java +// Single contract +OptionsQuoteRequest.of(String optionSymbol) +OptionsQuoteRequest.builder(String optionSymbol) + .date(LocalDate date) // historical quote + .from(LocalDate from) // date range + .to(LocalDate to) + .countback(int n) + .build() + +// Multiple contracts (fan-out) +OptionsQuotesRequest.of(String first, String... rest) // shortcut: symbols only +OptionsQuotesRequest.builder(String first, String... rest) + .addSymbol(String optionSymbol) + .date(LocalDate date) + .from(LocalDate from) + .to(LocalDate to) + .countback(int n) + .build() +``` + +#### Returns + +`OptionsQuotesResponse` wraps `List`. See [Chain](/sdk/java/options/chain#optionquote) for the full `OptionQuote` record (price fields plus greeks). + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.options.OptionQuote; +import com.marketdata.sdk.options.OptionsQuoteRequest; +import com.marketdata.sdk.options.OptionsQuotesRequest; + +try (MarketDataClient client = new MarketDataClient()) { + + // Single contract. + OptionQuote q = client.options() + .quote(OptionsQuoteRequest.of("AAPL260116C00200000")) + .values().get(0); + System.out.println("last=" + q.last() + " iv=" + q.iv() + " delta=" + q.delta()); + + // Multiple contracts — one request each, keyed by symbol. + var quotes = client.options().quotes( + OptionsQuotesRequest.of("AAPL260116C00200000", "AAPL260116C00210000")); + quotes.forEach((sym, resp) -> + System.out.println(sym + " → " + resp.values().size() + " row(s)")); +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.options.OptionsQuoteRequest + +MarketDataClient().use { client -> + val q = client.options() + .quote(OptionsQuoteRequest.of("AAPL260116C00200000")) + .values()[0] + println("last=${q.last()} iv=${q.iv()} delta=${q.delta()}") +} +``` + + + diff --git a/sdk/java/options/strikes.mdx b/sdk/java/options/strikes.mdx new file mode 100644 index 0000000..9910445 --- /dev/null +++ b/sdk/java/options/strikes.mdx @@ -0,0 +1,72 @@ +--- +title: Strikes +sidebar_position: 3 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +List the available strike prices for an underlying, grouped by expiration. + +## Making Requests + +Use the `strikes()` method on the `options` resource, built with `OptionsStrikesRequest`. + +```java +OptionsStrikesResponse strikes(OptionsStrikesRequest request) +CompletableFuture strikesAsync(OptionsStrikesRequest request) +``` + +### OptionsStrikesRequest + +```java +OptionsStrikesRequest.of(String symbol) +OptionsStrikesRequest.builder(String symbol) + .expiration(LocalDate expiration) // restrict to one expiration + .date(LocalDate date) // historical date + .build() +``` + +#### Returns + +`OptionsStrikesResponse` wrapping `List` — one entry per expiration, each holding its strike prices: + +```java +public record ExpirationStrikes(ZonedDateTime expiration, List strikes) +``` + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.options.ExpirationStrikes; +import com.marketdata.sdk.options.OptionsStrikesRequest; + +try (MarketDataClient client = new MarketDataClient()) { + var strikes = client.options().strikes(OptionsStrikesRequest.of("AAPL")); + for (ExpirationStrikes es : strikes.values()) { + System.out.println(es.expiration().toLocalDate() + ": " + es.strikes().size() + " strikes"); + } +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.options.OptionsStrikesRequest + +MarketDataClient().use { client -> + val strikes = client.options().strikes(OptionsStrikesRequest.of("AAPL")) + for (es in strikes.values()) { + println("${es.expiration().toLocalDate()}: ${es.strikes().size} strikes") + } +} +``` + + + diff --git a/sdk/java/settings.mdx b/sdk/java/settings.mdx new file mode 100644 index 0000000..6eaf3fe --- /dev/null +++ b/sdk/java/settings.mdx @@ -0,0 +1,178 @@ +--- +title: Settings +sidebar_position: 4 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +The Java SDK lets you customize API requests through **universal parameters** — settings such as date format, data mode, column projection, and CSV shaping that apply across endpoints. These are configured per resource, and some can also be set globally through environment variables. + + +## Configuration Cascade + +Client-level configuration (token, base URL, API version) is resolved in priority order, first match wins: + +1. **Explicit constructor argument** (highest priority) +2. **`MARKETDATA_*` environment variable** +3. **`.env` file** in the working directory +4. **Built-in default** (lowest priority) + +```java +// Explicit wins over the environment; null falls through to the cascade. +new MarketDataClient("explicit-token", null, null, true); +``` + +## Universal Parameters + +Universal parameters are set on the **resource**, before you call the endpoint. Each setter returns a configured copy of the resource, so they chain. They apply to every call you make through that configured resource. + + + + +```java +import com.marketdata.sdk.DateFormat; +import com.marketdata.sdk.Mode; +import com.marketdata.sdk.stocks.StockCandlesRequest; +import com.marketdata.sdk.stocks.StockResolution; + +var candles = client.stocks() + .dateFormat(DateFormat.TIMESTAMP) // how dates are sent on the wire + .mode(Mode.DELAYED) // live vs delayed vs cached data + .columns("symbol", "close") // project only the columns you need + .candles(StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") + .from(LocalDate.now().minusMonths(1)) + .to(LocalDate.now()) + .build()); +``` + + + + +```kotlin +import com.marketdata.sdk.DateFormat +import com.marketdata.sdk.Mode +import com.marketdata.sdk.stocks.StockCandlesRequest +import com.marketdata.sdk.stocks.StockResolution + +val candles = client.stocks() + .dateFormat(DateFormat.TIMESTAMP) + .mode(Mode.DELAYED) + .columns("symbol", "close") + .candles( + StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") + .from(LocalDate.now().minusMonths(1)) + .to(LocalDate.now()) + .build()) +``` + + + + +### Date Format + +Controls how dates and times are represented on the wire. + +```java +import com.marketdata.sdk.DateFormat; +// DateFormat.UNIX, DateFormat.TIMESTAMP, DateFormat.SPREADSHEET + +client.stocks().dateFormat(DateFormat.TIMESTAMP); +``` + +| Value | Description | +|---|---| +| `UNIX` | Unix timestamp in seconds (e.g. `1609362000`) | +| `TIMESTAMP` | ISO-8601 timestamp (e.g. `2020-12-30 16:00:00 -05:00`) | +| `SPREADSHEET` | Excel/Sheets serial date number (e.g. `44195.66667`) | + +For more details, see the [API Date Format documentation](/api/universal-parameters/date-format). + +### Data Mode + +Controls whether the API returns live, delayed, or cached data. + +```java +import com.marketdata.sdk.Mode; +// Mode.LIVE, Mode.DELAYED, Mode.CACHED + +client.stocks().mode(Mode.CACHED); +``` + +| Value | Description | +|---|---| +| `LIVE` | Real-time data (paid plans) | +| `DELAYED` | 15+ minute delayed data | +| `CACHED` | Cached data — lower cost per request | + +For more details, see the [API Data Mode documentation](/api/universal-parameters/mode). + +### Columns + +Limits the response to only the columns you need, reducing payload size. Columns you didn't request come back `null` on the typed model — there's no error. + +```java +client.stocks().columns("symbol", "last"); +``` + +For more details, see the [API Columns documentation](/api/universal-parameters/columns). + +### Limit and Offset + +Cap the number of rows returned (`limit`) and skip rows from the start (`offset`). + +```java +client.stocks().limit(100).offset(0); +``` + +## CSV Output + +Every resource exposes an `asCsv()` facet that switches the whole resource to CSV. The response's `csv()` accessor returns the raw CSV text. CSV-only shaping parameters live on this facet: + +- `human(boolean)` — render human-friendly field names and values. +- `headers(boolean)` — include the CSV header row (default `true`). + + + + +```java +import com.marketdata.sdk.stocks.StockQuotesRequest; + +var csv = client.stocks().asCsv() + .columns("symbol", "last") + .human(true) + .headers(true) + .quotes(StockQuotesRequest.of("AAPL", "MSFT")); + +System.out.println(csv.csv()); // raw CSV text +csv.saveToFile(Path.of("quotes.csv")); +``` + + + + +```kotlin +import com.marketdata.sdk.stocks.StockQuotesRequest + +val csv = client.stocks().asCsv() + .columns("symbol", "last") + .human(true) + .headers(true) + .quotes(StockQuotesRequest.of("AAPL", "MSFT")) + +println(csv.csv()) +csv.saveToFile(Path.of("quotes.csv")) +``` + + + + +## Environment Variables Reference + +| Variable | Purpose | Default | +|--------------------------|-------------------------------|------------------------------| +| `MARKETDATA_TOKEN` | API authentication token | _(none)_ | +| `MARKETDATA_BASE_URL` | API base URL | `https://api.marketdata.app` | +| `MARKETDATA_API_VERSION` | API version | `v1` | +| `MARKETDATA_DATE_FORMAT` | Default date format | _(API default)_ | +| `MARKETDATA_LOGGING_LEVEL` | SDK logging level (`java.util.logging`) | _(consumer default)_ | diff --git a/sdk/java/stocks/candles.mdx b/sdk/java/stocks/candles.mdx new file mode 100644 index 0000000..8163580 --- /dev/null +++ b/sdk/java/stocks/candles.mdx @@ -0,0 +1,126 @@ +--- +title: Candles +sidebar_position: 1 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve historical OHLCV (open/high/low/close/volume) candles for a stock symbol. + +## Making Requests + +Use the `candles()` method on the `stocks` resource. Build the request with `StockCandlesRequest`, passing a `StockResolution` and a symbol. For large intraday ranges the SDK automatically splits the request into year-sized chunks, fetches them concurrently, and merges the results. + +```java +StockCandlesResponse candles(StockCandlesRequest request) +CompletableFuture candlesAsync(StockCandlesRequest request) +``` + +### StockCandlesRequest + +```java +// Factory — all-defaults form: +StockCandlesRequest.of(StockResolution resolution, String symbol) + +// Builder — for date windows and adjustments: +StockCandlesRequest.builder(StockResolution resolution, String symbol) + .date(LocalDate date) // a single trading day + .from(LocalDate from) // window start (inclusive) + .to(LocalDate to) // window end (exclusive) + .countback(int n) // N candles before `to` (instead of `from`) + .exchange(String exchange) // disambiguate exchange + .extended(boolean extended) // include extended-hours bars (intraday) + .country(String country) // exchange country (ISO 3166, 2-letter) + .adjustSplits(boolean adjust) // default: true for daily + .adjustDividends(boolean adjust) // default: true for daily + .build() +``` + +### StockResolution + +A value type for the candle interval: + +```java +StockResolution.DAILY // also WEEKLY, MONTHLY, YEARLY +StockResolution.minutes(5) // 5-minute bars +StockResolution.hours(1) // hourly bars +StockResolution.days(1) +StockResolution.of("1H") // any raw wire token +``` + +#### Returns + +`StockCandlesResponse` wrapping `List`: + +```java +public record StockCandle( + @Nullable ZonedDateTime time, // bar opening moment (America/New_York) + @Nullable Double open, + @Nullable Double high, + @Nullable Double low, + @Nullable Double close, + @Nullable Long volume) +``` + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockCandle; +import com.marketdata.sdk.stocks.StockCandlesRequest; +import com.marketdata.sdk.stocks.StockResolution; +import java.time.LocalDate; + +try (MarketDataClient client = new MarketDataClient()) { + + // Daily candles over a date range. + var candles = client.stocks().candles( + StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") + .from(LocalDate.now().minusWeeks(1)) + .to(LocalDate.now()) + .build()); + + for (StockCandle bar : candles.values()) { + System.out.printf("%s O=%.2f H=%.2f L=%.2f C=%.2f V=%d%n", + bar.time(), bar.open(), bar.high(), bar.low(), bar.close(), bar.volume()); + } + + // The last 10 sessions, using countback instead of a left edge. + var lastTen = client.stocks().candles( + StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") + .to(LocalDate.now()) + .countback(10) + .build()); +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockCandlesRequest +import com.marketdata.sdk.stocks.StockResolution +import java.time.LocalDate + +MarketDataClient().use { client -> + val candles = client.stocks().candles( + StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") + .from(LocalDate.now().minusWeeks(1)) + .to(LocalDate.now()) + .build()) + + for (bar in candles.values()) { + println("${bar.time()} O=${bar.open()} C=${bar.close()} V=${bar.volume()}") + } +} +``` + + + + +For CSV output, call `client.stocks().asCsv().candles(...)` and read `.csv()`. See [Settings](/sdk/java/settings#csv-output). diff --git a/sdk/java/stocks/earnings.mdx b/sdk/java/stocks/earnings.mdx new file mode 100644 index 0000000..afa0457 --- /dev/null +++ b/sdk/java/stocks/earnings.mdx @@ -0,0 +1,88 @@ +--- +title: Earnings +sidebar_position: 5 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve historical and upcoming earnings data (EPS actuals, estimates, surprises) for a stock symbol. + +## Making Requests + +Use the `earnings()` method on the `stocks` resource, built with `StockEarningsRequest`. + +```java +StockEarningsResponse earnings(StockEarningsRequest request) +CompletableFuture earningsAsync(StockEarningsRequest request) +``` + +### StockEarningsRequest + +```java +StockEarningsRequest.of(String symbol) +StockEarningsRequest.builder(String symbol) + .date(LocalDate date) // a single report date + .from(LocalDate from) // window start + .to(LocalDate to) // window end + .countback(int n) // N reports before `to` + .report(String report) // "actual" or "estimated" + .build() +``` + +#### Returns + +`StockEarningsResponse` wrapping `List`: + +```java +public record StockEarning( + @Nullable String symbol, + @Nullable Integer fiscalYear, + @Nullable Integer fiscalQuarter, + @Nullable ZonedDateTime date, + @Nullable ZonedDateTime reportDate, + @Nullable String reportTime, // "pre" / "post" + @Nullable String currency, + @Nullable Double reportedEPS, + @Nullable Double estimatedEPS, + @Nullable Double surpriseEPS, + @Nullable Double surpriseEPSpct, + @Nullable ZonedDateTime updated) +``` + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockEarning; +import com.marketdata.sdk.stocks.StockEarningsRequest; + +try (MarketDataClient client = new MarketDataClient()) { + var earnings = client.stocks().earnings(StockEarningsRequest.of("AAPL")); + for (StockEarning e : earnings.values()) { + System.out.printf("%d Q%d reported=%s estimated=%s%n", + e.fiscalYear(), e.fiscalQuarter(), e.reportedEPS(), e.estimatedEPS()); + } +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockEarningsRequest + +MarketDataClient().use { client -> + val earnings = client.stocks().earnings(StockEarningsRequest.of("AAPL")) + for (e in earnings.values()) { + println("${e.fiscalYear()} Q${e.fiscalQuarter()} reported=${e.reportedEPS()}") + } +} +``` + + + diff --git a/sdk/java/stocks/index.mdx b/sdk/java/stocks/index.mdx new file mode 100644 index 0000000..42585a4 --- /dev/null +++ b/sdk/java/stocks/index.mdx @@ -0,0 +1,16 @@ +--- +title: Stocks +slug: /java/stocks +sidebar_position: 10 +--- + +The Java SDK from Market Data provides methods to streamline your use of the Stocks endpoints. These methods provide a typed interface over the underlying HTTP requests and responses, with both sync and async variants. + +Reach the resource through `client.stocks()`. For CSV output use `client.stocks().asCsv()`. + +## Stocks Endpoints + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from "@docusaurus/theme-common"; + + diff --git a/sdk/java/stocks/news.mdx b/sdk/java/stocks/news.mdx new file mode 100644 index 0000000..37829b0 --- /dev/null +++ b/sdk/java/stocks/news.mdx @@ -0,0 +1,95 @@ +--- +title: News +sidebar_position: 4 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve news articles for a stock symbol. + +## Making Requests + +Use the `news()` method on the `stocks` resource, built with `StockNewsRequest`. + +```java +StockNewsResponse news(StockNewsRequest request) +CompletableFuture newsAsync(StockNewsRequest request) +``` + +### StockNewsRequest + +```java +StockNewsRequest.of(String symbol) +StockNewsRequest.builder(String symbol) + .date(LocalDate date) // a single day + .from(LocalDate from) // window start + .to(LocalDate to) // window end + .countback(int n) // N articles before `to` + .build() +``` + +#### Returns + +`StockNewsResponse` wrapping `List`: + +```java +public record StockNewsArticle( + String symbol, + String headline, + String content, + String source, + ZonedDateTime publicationDate) +``` + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockNewsArticle; +import com.marketdata.sdk.stocks.StockNewsRequest; +import java.time.LocalDate; + +try (MarketDataClient client = new MarketDataClient()) { + var news = client.stocks().news( + StockNewsRequest.builder("AAPL") + .from(LocalDate.now().minusDays(7)) + .to(LocalDate.now()) + .build()); + + for (StockNewsArticle a : news.values()) { + System.out.println(a.publicationDate() + " " + a.headline() + " (" + a.source() + ")"); + } +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockNewsRequest +import java.time.LocalDate + +MarketDataClient().use { client -> + val news = client.stocks().news( + StockNewsRequest.builder("AAPL") + .from(LocalDate.now().minusDays(7)) + .to(LocalDate.now()) + .build()) + + for (a in news.values()) { + println("${a.publicationDate()} ${a.headline()} (${a.source()})") + } +} +``` + + + + +:::note +On the typed path, all article fields are populated. For column projection use the CSV facet: `client.stocks().asCsv().news(...)`. +::: diff --git a/sdk/java/stocks/prices.mdx b/sdk/java/stocks/prices.mdx new file mode 100644 index 0000000..b762418 --- /dev/null +++ b/sdk/java/stocks/prices.mdx @@ -0,0 +1,76 @@ +--- +title: Prices +sidebar_position: 3 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve the latest price (mid, change) for one or more stock symbols — a lighter payload than a full quote. + +## Making Requests + +Use the `prices()` method on the `stocks` resource, built with `StockPricesRequest`. + +```java +StockPricesResponse prices(StockPricesRequest request) +CompletableFuture pricesAsync(StockPricesRequest request) +``` + +### StockPricesRequest + +```java +StockPricesRequest.of(String first, String... rest) +StockPricesRequest.builder(String first, String... rest) + .addSymbol(String symbol) + .build() +``` + +#### Returns + +`StockPricesResponse` wrapping `List`: + +```java +public record StockPrice( + @Nullable String symbol, + @Nullable Double mid, + @Nullable Double change, + @Nullable Double changepct, + @Nullable ZonedDateTime updated) +``` + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockPrice; +import com.marketdata.sdk.stocks.StockPricesRequest; + +try (MarketDataClient client = new MarketDataClient()) { + var prices = client.stocks().prices(StockPricesRequest.of("AAPL", "MSFT")); + for (StockPrice p : prices.values()) { + System.out.printf("%-6s mid=%.2f change=%.2f%n", p.symbol(), p.mid(), p.change()); + } +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockPricesRequest + +MarketDataClient().use { client -> + val prices = client.stocks().prices(StockPricesRequest.of("AAPL", "MSFT")) + for (p in prices.values()) { + println("${p.symbol()} mid=${p.mid()} change=${p.change()}") + } +} +``` + + + diff --git a/sdk/java/stocks/quotes.mdx b/sdk/java/stocks/quotes.mdx new file mode 100644 index 0000000..dc51b40 --- /dev/null +++ b/sdk/java/stocks/quotes.mdx @@ -0,0 +1,112 @@ +--- +title: Quotes +sidebar_position: 2 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve real-time quotes (bid, ask, mid, last, volume, etc.) for one or more stock symbols. + +## Making Requests + +The `stocks` resource offers two quote methods: + +- `quote(...)` — a single symbol, built with `StockQuoteRequest`. +- `quotes(...)` — several symbols in **one** request (the stocks backend batches a comma list), built with `StockQuotesRequest`. The result is a single response with one row per symbol. + +```java +StockQuotesResponse quote(StockQuoteRequest request) +CompletableFuture quoteAsync(StockQuoteRequest request) + +StockQuotesResponse quotes(StockQuotesRequest request) +CompletableFuture quotesAsync(StockQuotesRequest request) +``` + +### Request types + +```java +// Single symbol +StockQuoteRequest.of(String symbol) +StockQuoteRequest.builder(String symbol) + .extended(boolean extended) // include extended-session prices + .candle(boolean candle) // add OHLC columns + .week52(boolean week52) // add 52-week high/low + .build() + +// Multiple symbols, one request +StockQuotesRequest.of(String first, String... rest) // shortcut: symbols only +StockQuotesRequest.builder(String first, String... rest) + .addSymbol(String symbol) + .extended(boolean extended) + .candle(boolean candle) + .week52(boolean week52) + .build() +``` + +#### Returns + +`StockQuotesResponse` wrapping `List` (one element for `quote`, one per symbol for `quotes`): + +```java +public record StockQuote( + @Nullable String symbol, + @Nullable Double ask, @Nullable Long askSize, + @Nullable Double bid, @Nullable Long bidSize, + @Nullable Double mid, @Nullable Double last, + @Nullable Double change, @Nullable Double changepct, + @Nullable Long volume, @Nullable ZonedDateTime updated, + // opt-in via .candle(true): + @Nullable Double open, @Nullable Double high, @Nullable Double low, @Nullable Double close, + // opt-in via .week52(true): + @Nullable Double week52High, @Nullable Double week52Low) +``` + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.stocks.StockQuote; +import com.marketdata.sdk.stocks.StockQuoteRequest; +import com.marketdata.sdk.stocks.StockQuotesRequest; + +try (MarketDataClient client = new MarketDataClient()) { + + // A single symbol — row 0 of the list. + StockQuote q = client.stocks().quote(StockQuoteRequest.of("AAPL")).values().get(0); + System.out.printf("%s last=%.2f bid/ask=%.2f/%.2f%n", q.symbol(), q.last(), q.bid(), q.ask()); + + // Several symbols in one request. + var quotes = client.stocks().quotes( + StockQuotesRequest.of("AAPL", "MSFT", "GOOGL")); + for (StockQuote row : quotes.values()) { + System.out.printf("%-6s last=%.2f%n", row.symbol(), row.last()); + } +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.stocks.StockQuoteRequest +import com.marketdata.sdk.stocks.StockQuotesRequest + +MarketDataClient().use { client -> + val q = client.stocks().quote(StockQuoteRequest.of("AAPL")).values()[0] + println("${q.symbol()} last=${q.last()}") + + val quotes = client.stocks().quotes( + StockQuotesRequest.of("AAPL", "MSFT", "GOOGL")) + for (row in quotes.values()) { + println("${row.symbol()} last=${row.last()}") + } +} +``` + + + diff --git a/sdk/java/utilities/headers.mdx b/sdk/java/utilities/headers.mdx new file mode 100644 index 0000000..1302536 --- /dev/null +++ b/sdk/java/utilities/headers.mdx @@ -0,0 +1,51 @@ +--- +title: Headers +sidebar_position: 3 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Echo back the HTTP headers the server received for your request — useful for confirming your `Authorization` header actually reached the API. Sensitive values are redacted in the response. + +## Making Requests + +Use the `headers()` method on the `utilities` resource. It requires a valid token. + +```java +UtilitiesHeadersResponse headers() +CompletableFuture headersAsync() +``` + +#### Returns + +`UtilitiesHeadersResponse` wrapping `Map` — the headers the server received, lower-cased keys to values (sensitive values redacted server-side). + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; + +try (MarketDataClient client = new MarketDataClient()) { + var headers = client.utilities().headers().values(); + System.out.println("Server saw " + headers.size() + " request headers"); +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient + +MarketDataClient().use { client -> + val headers = client.utilities().headers().values() + println("Server saw ${headers.size} request headers") +} +``` + + + diff --git a/sdk/java/utilities/index.mdx b/sdk/java/utilities/index.mdx new file mode 100644 index 0000000..5a957ba --- /dev/null +++ b/sdk/java/utilities/index.mdx @@ -0,0 +1,16 @@ +--- +title: Utilities +slug: /java/utilities +sidebar_position: 50 +--- + +The Java SDK from Market Data provides utility methods for checking API health, inspecting your account quota, and debugging requests. These methods provide a typed interface over the underlying HTTP requests and responses, with both sync and async variants. + +Reach the resource through `client.utilities()`. + +## Utilities Endpoints + +import DocCardList from "@theme/DocCardList"; +import { useCurrentSidebarCategory } from "@docusaurus/theme-common"; + + diff --git a/sdk/java/utilities/status.mdx b/sdk/java/utilities/status.mdx new file mode 100644 index 0000000..597cd35 --- /dev/null +++ b/sdk/java/utilities/status.mdx @@ -0,0 +1,61 @@ +--- +title: Status +sidebar_position: 1 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve the health of the Market Data API's services — a handy liveness check. + +:::note +This reports the **API's** own service health. For the exchange open/closed calendar, see [`markets().status()`](/sdk/java/markets/status). +::: + +## Making Requests + +Use the `status()` method on the `utilities` resource. It takes no parameters and is **public** — it works without a token. + +```java +UtilitiesStatusResponse status() +CompletableFuture statusAsync() +``` + +#### Returns + +`UtilitiesStatusResponse` wrapping `List`. Each `ServiceStatus` exposes the service name, a status string, an `online()` flag, uptime percentiles, and an updated timestamp. + +The server refreshes this data every few minutes, so polling more often than that is wasted work. + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.utilities.ServiceStatus; + +try (MarketDataClient client = new MarketDataClient()) { + var status = client.utilities().status(); + long online = status.values().stream().filter(ServiceStatus::online).count(); + System.out.println(online + " of " + status.values().size() + " services online"); +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient +import com.marketdata.sdk.utilities.ServiceStatus + +MarketDataClient().use { client -> + val status = client.utilities().status() + val online = status.values().count { it.online() } + println("$online of ${status.values().size} services online") +} +``` + + + diff --git a/sdk/java/utilities/user.mdx b/sdk/java/utilities/user.mdx new file mode 100644 index 0000000..a180670 --- /dev/null +++ b/sdk/java/utilities/user.mdx @@ -0,0 +1,53 @@ +--- +title: User +sidebar_position: 2 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +Retrieve your account details — including how much of your daily request quota remains. This is the same endpoint the SDK calls on startup to validate your token. + +## Making Requests + +Use the `user()` method on the `utilities` resource. It requires a valid token. + +```java +UtilitiesUserResponse user() +CompletableFuture userAsync() +``` + +#### Returns + +`UtilitiesUserResponse` wrapping a `User`, which exposes your plan and quota — for example `requestsRemaining()` and `requestsLimit()`. + +## Examples + + + + +```java +import com.marketdata.sdk.MarketDataClient; +import com.marketdata.sdk.utilities.User; + +try (MarketDataClient client = new MarketDataClient()) { + User me = client.utilities().user().values(); + System.out.println("Quota: " + me.requestsRemaining() + " of " + + me.requestsLimit() + " requests left today"); +} +``` + + + + +```kotlin +import com.marketdata.sdk.MarketDataClient + +MarketDataClient().use { client -> + val me = client.utilities().user().values() + println("Quota: ${me.requestsRemaining()} of ${me.requestsLimit()} left today") +} +``` + + + diff --git a/static/img/java-logo.svg b/static/img/java-logo.svg new file mode 100644 index 0000000..bf1a224 --- /dev/null +++ b/static/img/java-logo.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file