Auto-generated Java client for the QTSurfer API, built from the OpenAPI 3.1 spec with openapi-generator and the JDK's java.net.http.HttpClient.
com.qtsurfer:api-client-java · com.qtsurfer:api-client-java
Intentionally thin: one method per endpoint, 1:1 with the spec. For workflow orchestration (polling, retries, domain objects, unified errors), use com.qtsurfer:sdk.
- Zero HTTP runtime deps —
java.net.http.HttpClient(JDK built-in) + Jackson for JSON. - Spec-driven — generated sources fetched from
QTSurfer/qtsurfer-apion every build. - JDK 17+ — modern language features, long-term support.
- Distributed via JitPack today; Maven Central later.
Add the JitPack repository and the dependency:
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>com.qtsurfer</groupId>
<artifactId>api-client-java</artifactId>
<version>x.x.x</version>
</dependency>For Gradle:
repositories { maven { url 'https://jitpack.io' } }
dependencies { implementation 'com.qtsurfer:api-client-java:x.x.x' }Once published to Central, the coordinate will be com.qtsurfer:api-client-java:x.x.x.
import com.qtsurfer.api.client.api.ExchangeApi;
import com.qtsurfer.api.client.invoker.ApiClient;
import com.qtsurfer.api.client.model.Exchange;
import java.util.List;
ApiClient client = new ApiClient();
client.updateBaseUri("https://api.qtsurfer.com/v1");
client.setRequestInterceptor(builder ->
builder.header("Authorization", "Bearer " + System.getenv("QTSURFER_TOKEN")));
ExchangeApi exchanges = new ExchangeApi(client);
List<Exchange> result = exchanges.getExchanges();Every endpoint above expects a short-lived JWT in Authorization: Bearer ….
Exchange a long-lived API key for one via AuthApi.auth():
import com.qtsurfer.api.client.api.AuthApi;
import com.qtsurfer.api.client.invoker.ApiClient;
import com.qtsurfer.api.client.model.AuthTokenResponse;
ApiClient apikeyClient = new ApiClient();
apikeyClient.updateBaseUri("https://api.qtsurfer.com/v1");
apikeyClient.setRequestInterceptor(builder ->
builder.header("X-API-Key", System.getenv("QTSURFER_APIKEY")));
AuthTokenResponse token = new AuthApi(apikeyClient).auth();
String jwt = token.getAccessToken(); // feed to a Bearer-authed ApiClientFor production use, prefer the com.qtsurfer:sdk
auth(apikey) helper — it returns an AuthenticatedClient that refreshes the
JWT transparently, reads QTSURFER_APIKEY from the environment, and supports
pluggable token stores so callers don't reinvent that plumbing.
| API class | Methods |
|---|---|
AuthApi |
auth() — exchange API key for a short-lived JWT |
ExchangeApi |
getExchanges(), getInstruments(exchangeId) |
ExchangeBinaryDownloads |
getTickersHour(...), getKlinesHour(...) — Lastra/Parquet streams (manual; see note below) |
StrategyApi |
postStrategy(body, xCompileAsync), getStrategyStatus(strategyId) |
BacktestingApi |
prepareBacktesting, getPreparationStatus, executeBacktesting, cancelExecution, getExecutionResult |
All generated model types (Exchange, InstrumentDetail, JobState, BacktestJobResult, ResultMap, ResponseError, …) live under com.qtsurfer.api.client.model.
These endpoints return raw Lastra bytes (default) or Parquet (format=parquet). The auto-generated ExchangeApi.getExchangeTickersHour / getExchangeKlinesHour methods are unusable for binary payloads — openapi-generator's native library decodes the body as UTF-8 and feeds it to Jackson, which corrupts the bytes. Use ExchangeBinaryDownloads instead:
import com.qtsurfer.api.client.binary.ExchangeBinaryDownloads;
import com.qtsurfer.api.client.binary.ExchangeBinaryDownloads.Format;
ExchangeBinaryDownloads downloads = new ExchangeBinaryDownloads(client);
try (var in = downloads.getTickersHour("binance", "BTC", "USDT", "2026-01-15T10")) {
Files.copy(in, Path.of("BTC_USDT_2026-01-15_h10.lastra"));
}
try (var in = downloads.getKlinesHour("binance", "BTC", "USDT", "2026-01-15T10", Format.PARQUET)) {
// feed into Apache Parquet, DuckDB, etc.
}The class reuses the ApiClient's HttpClient and request interceptor, so any Authorization header set at the client level applies automatically.
ApiClient exposes the underlying HttpClient.Builder and an ObjectMapper, plus hooks for request/response interceptors.
client.updateBaseUri("https://api.qtsurfer.com/v1");
client.setRequestInterceptor(builder ->
builder.header("Authorization", "Bearer " + token)
.header("X-Request-Id", UUID.randomUUID().toString()));
client.setResponseInterceptor(response ->
log.debug("HTTP {} {}", response.statusCode(), response.uri()));Generated sources are produced by the openapi-generator-maven-plugin during the generate-sources phase and compiled from target/generated-sources/openapi. To regenerate:
mvn -B clean generate-sourcesThe input spec URL is configured in pom.xml (openapi.spec.url property). Point it to a tag or commit for reproducible builds.
| Command | Description |
|---|---|
mvn verify |
Fetch spec, generate, compile, run tests, build jar + sources + javadoc |
mvn clean |
Remove target/ |
Apache-2.0 — see LICENSE.