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.
net.qtsurfer:api-client · com.github.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 net.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.github.QTSurfer</groupId>
<artifactId>api-client-java</artifactId>
<version>v0.1.2</version>
</dependency>For Gradle:
repositories { maven { url 'https://jitpack.io' } }
dependencies { implementation 'com.github.QTSurfer:api-client-java:v0.1.2' }Once published to Central, the coordinate will be net.qtsurfer:api-client:0.1.2.
import net.qtsurfer.api.client.api.ExchangeApi;
import net.qtsurfer.api.client.invoker.ApiClient;
import net.qtsurfer.api.client.model.Exchange;
import java.util.List;
ApiClient client = new ApiClient();
client.updateBaseUri("https://api.qtsurfer.net/v1");
client.setRequestInterceptor(builder ->
builder.header("Authorization", "Bearer " + System.getenv("JWT_API_TOKEN")));
ExchangeApi exchanges = new ExchangeApi(client);
List<Exchange> result = exchanges.getExchanges();| API class | Methods |
|---|---|
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 net.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 net.qtsurfer.api.client.binary.ExchangeBinaryDownloads;
import net.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.net/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.