|
1 | 1 | # Market Data Java SDK |
2 | 2 |
|
3 | 3 | Java SDK for the [Market Data API](https://www.marketdata.app/). **Pre-release** |
4 | | -— the `utilities` and `options` resources are implemented; `stocks`, `funds`, |
| 4 | +— the `utilities`, `options`, and `stocks` resources are implemented; `funds` |
5 | 5 | and `markets` are forthcoming. The build, package layout, configuration cascade, |
6 | 6 | exception taxonomy, and Kotlin-interop foundations are in place. |
7 | 7 |
|
@@ -48,8 +48,10 @@ MarketDataClient().use { client -> |
48 | 48 |
|
49 | 49 | Every response implements `MarketDataResponse<T>`: `values()` returns the typed payload |
50 | 50 | (typed per endpoint — a `List`, a scalar `String`, …), and the same metadata accessors |
51 | | -(`statusCode()`, `isNoData()`, `requestId()`, `json()`, `saveToFile(path)`) are available on |
52 | | -every response, on every resource. |
| 51 | +(`statusCode()`, `isNoData()`, `requestId()`, `rateLimit()`, `json()`, `saveToFile(path)`) are |
| 52 | +available on every response, on every resource. `rateLimit()` returns the rate-limit snapshot |
| 53 | +parsed from *that* response's headers (request-scoped), distinct from the client-level |
| 54 | +`client.getRateLimits()`. |
53 | 55 |
|
54 | 56 | ## Options |
55 | 57 |
|
@@ -149,6 +151,92 @@ With `columns`, a field you didn't request decodes to `null` (no error); a **req |
149 | 151 | you *did* request (or didn't project away) that the API omits raises a `ParseError` — so a |
150 | 152 | `null` never silently hides a dropped field. |
151 | 153 |
|
| 154 | +## Stocks |
| 155 | + |
| 156 | +Reached via `client.stocks()`. Same conventions as `options`: every endpoint has a |
| 157 | +synchronous method and an `…Async` variant, takes a Builder-based request object, and returns |
| 158 | +a typed `MarketDataResponse` (payload via `values()`). The universal-parameter setters |
| 159 | +(`dateFormat`/`mode`/`limit`/`offset`/`columns`) and the `asCsv()` facet work identically. |
| 160 | + |
| 161 | +| Method | Purpose | |
| 162 | +|--------|---------| |
| 163 | +| `candles` | Historical OHLCV candles for a symbol at a given `StockResolution` | |
| 164 | +| `quote` | Real-time quote for a single symbol | |
| 165 | +| `quotes` | Quotes for many symbols — batched in **one** request, one row per symbol | |
| 166 | +| `prices` | Lightweight price snapshot (mid/change) for many symbols | |
| 167 | +| `news` | Recent news articles for a symbol | |
| 168 | +| `earnings` | EPS history and the forward earnings calendar | |
| 169 | + |
| 170 | +> Unlike `options.quotes` (which fans out one request per contract and returns a per-symbol |
| 171 | +> map), the stocks backend accepts a comma list in a single request — so `stocks.quotes` and |
| 172 | +> `stocks.prices` return a single response with one row per symbol. |
| 173 | +
|
| 174 | +### Candles |
| 175 | + |
| 176 | +`StockResolution` is a value type, not an enum — the API accepts an open-ended family of |
| 177 | +resolutions, so use the factories (`DAILY`, `minutes(15)`, `hours(1)`, `days(2)`, …). An |
| 178 | +**intraday** request spanning more than ~one year is automatically split into year-sized |
| 179 | +sub-requests, fetched concurrently and merged into one response — transparent to the caller: |
| 180 | + |
| 181 | +#### Java |
| 182 | + |
| 183 | +```java |
| 184 | +try (var client = new MarketDataClient()) { |
| 185 | + var resp = client.stocks().candles( |
| 186 | + StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") |
| 187 | + .from(LocalDate.now().minusMonths(1)) |
| 188 | + .to(LocalDate.now()) |
| 189 | + .build()); |
| 190 | + |
| 191 | + for (StockCandle c : resp.values()) { // values() is a List<StockCandle> |
| 192 | + System.out.printf("%s O=%s H=%s L=%s C=%s V=%s%n", |
| 193 | + c.time(), c.open(), c.high(), c.low(), c.close(), c.volume()); |
| 194 | + } |
| 195 | +} |
| 196 | +``` |
| 197 | + |
| 198 | +#### Kotlin |
| 199 | + |
| 200 | +```kotlin |
| 201 | +MarketDataClient().use { client -> |
| 202 | + val resp = client.stocks().candles( |
| 203 | + StockCandlesRequest.builder(StockResolution.DAILY, "AAPL") |
| 204 | + .from(LocalDate.now().minusMonths(1)) |
| 205 | + .to(LocalDate.now()) |
| 206 | + .build() |
| 207 | + ) |
| 208 | + resp.values().forEach { c -> |
| 209 | + println("${c.time} O=${c.open} H=${c.high} L=${c.low} C=${c.close} V=${c.volume}") |
| 210 | + } |
| 211 | +} |
| 212 | +``` |
| 213 | + |
| 214 | +### Quotes, prices, news, earnings |
| 215 | + |
| 216 | +```java |
| 217 | +// Multi-symbol quote — one batched request, one row per symbol: |
| 218 | +StockQuotesResponse q = client.stocks().quotes( |
| 219 | + StockQuotesRequest.builder("AAPL", "MSFT") |
| 220 | + .candle(true) // opt-in OHLC columns |
| 221 | + .week52(true) // opt-in 52-week high/low columns |
| 222 | + .build()); |
| 223 | + |
| 224 | +// News — articles plus the feed's latest-update time as a scalar off the response: |
| 225 | +StockNewsResponse news = client.stocks().news(StockNewsRequest.of("AAPL")); |
| 226 | +news.values().forEach(a -> System.out.println(a.publicationDate() + " " + a.headline())); |
| 227 | +System.out.println("feed updated: " + news.updated()); |
| 228 | + |
| 229 | +// Earnings — fundamentals and report fields are nullable on forward-quarter rows: |
| 230 | +client.stocks().earnings(StockEarningsRequest.of("AAPL")) |
| 231 | + .values() |
| 232 | + .forEach(e -> System.out.println("FY" + e.fiscalYear() + " Q" + e.fiscalQuarter() |
| 233 | + + " reportedEPS=" + e.reportedEPS())); |
| 234 | +``` |
| 235 | + |
| 236 | +Stock quote/price numeric fields are `@Nullable` (the backend nulls them for a closed or |
| 237 | +illiquid market), and the OHLC / 52-week columns appear only when opted in via `candle` / |
| 238 | +`week52`. |
| 239 | + |
152 | 240 | ## Configuration |
153 | 241 |
|
154 | 242 | Values are resolved through this cascade (highest priority first): |
@@ -177,7 +265,7 @@ Values are resolved through this cascade (highest priority first): |
177 | 265 |
|
178 | 266 | The corresponding per-call setters — `dateFormat`/`limit`/`offset`/`mode`/`columns` on the |
179 | 267 | resource, plus `human`/`headers` and `asCsv()` on the CSV facet — are exposed on `options` |
180 | | -today (and on every resource as it lands). Auto-applying these env-var values as request |
| 268 | +and `stocks` today (and on every resource as it lands). Auto-applying these env-var values as request |
181 | 269 | *defaults* (`DATE_FORMAT`, `COLUMNS`, `ADD_HEADERS`, `USE_HUMAN_READABLE`, `MODE`, |
182 | 270 | `OUTPUT_FORMAT`) is still reserved. |
183 | 271 |
|
@@ -244,14 +332,18 @@ isn't an exact match is rejected before any request is made. |
244 | 332 |
|
245 | 333 | ``` |
246 | 334 | com.marketdata.sdk # MarketDataClient, RateLimits, the resource façades |
247 | | - # (UtilitiesResource, OptionsResource, OptionsCsvResource), |
248 | | - # and MarketDataResponse<T> + the named response types |
249 | | - # (OptionsChainResponse, CsvResponse, …) — public; |
250 | | - # Configuration, EnvVars, Tokens, Version are |
| 335 | + # (UtilitiesResource, OptionsResource, StocksResource, |
| 336 | + # OptionsCsvResource, StocksCsvResource), and |
| 337 | + # MarketDataResponse<T> + the named response types |
| 338 | + # (OptionsChainResponse, StockCandlesResponse, CsvResponse, …) |
| 339 | + # — public; Configuration, EnvVars, Tokens, Version are |
251 | 340 | # package-private and not part of the API |
252 | 341 | com.marketdata.sdk.options # Options request builders + row records |
253 | 342 | # (OptionsChainRequest, OptionQuote, sealed |
254 | 343 | # ExpirationFilter / StrikeFilter, Greek, …) |
| 344 | +com.marketdata.sdk.stocks # Stocks request builders + row records |
| 345 | + # (StockCandlesRequest, StockCandle, StockQuote, |
| 346 | + # StockEarning, StockResolution, …) |
255 | 347 | com.marketdata.sdk.exception # Sealed MarketDataException hierarchy + ErrorContext |
256 | 348 | ``` |
257 | 349 |
|
|
0 commit comments