Giter Club home page Giter Club logo

alpaca-java's Introduction

Logo

GitHub Repository Maven Central Javadocs GitHub

Overview

This library is a Java client implementation of the Alpaca API. Alpaca lets you trade with algorithms, connect with apps, and build services all with a commission-free trading API for stocks, crypto, and options. This library uses the Alpaca OpenAPI Specifications to generate clients for the REST API with the OkHttp library, but implements the websocket and SSE streaming interface using a custom implementation with the OkHttp library. This library is community developed and if you have any questions, please ask them on Github Discussions, the Alpaca Slack #dev-alpaca-java channel, or on the Alpaca Forums. This library strives to provide the complete Alpaca API as a Java client implementation, so open a new issue or new pull request if you find something missing.

Give this repository a star ⭐ if it helped you build a trading algorithm in Java!

Gradle and Maven Integration

If you are using Gradle as your build tool, add the following dependency to your build.gradle file:

implementation group: "net.jacobpeterson.alpaca", name: "alpaca-java", version: "10.0.1"

If you are using Maven as your build tool, add the following dependency to your pom.xml file:

<dependency>
    <groupId>net.jacobpeterson.alpaca</groupId>
    <artifactId>alpaca-java</artifactId>
    <version>10.0.1</version>
</dependency>

Note that you don't have to use the Maven Central artifacts. Instead, you can clone this repository, build this project, and install the artifacts to your local Maven repository as shown in the Building section.

Logger

For logging, this library uses SLF4j which serves as an interface for various logging frameworks. This enables you to use whatever logging framework you would like. However, if you do not add a logging framework as a dependency in your project, the console will output a message stating that SLF4j is defaulting to a no-operation (NOP) logger implementation. To enable logging, add a logging framework of your choice as a dependency to your project such as Logback, Log4j 2, SLF4j-simple, or Apache Commons Logging.

BigDecimal vs. Double

It is generally considered bad practice to represent currency values in floating-point data types such as float or double because it can lead to rounding errors and precision loss in calculations. However, using floating-point data types can have significant performance benefits compared to using arbitrary-precision number data types, especially in a quantitative finance and algorithmic trading environment. Because of this, alpaca-java uses the Double data type (the double boxed type) when using the Market Data APIs and the BigDecimal data type when using the Trading or Broker APIs. The thinking behind this is that exact decimal quantities are important when placing real trades with real money, but less important when performing calculations of financial indicators. The best solution would be to use the TA4j Num interface so that you can decide what data type to use based on your use case, but that is on the TODO list for now. By the way, using BigDecimal wouldn't matter if Alpaca used floating-point data types in their internal systems or in their REST APIs, but the fact that they use String data types in some of the REST API JSON responses and their Trading and Broker OpenAPI specifications don't use the double data type, this leads me to believe that using BigDecimal does actually matter.

Examples

Note that the examples below are not exhaustive. Refer to the Javadoc for all classes and method signatures.

AlpacaAPI is the main class used to interface with the various Alpaca API endpoints. If you are using the Trading or Market Data APIs for a single Alpaca account or if you are using the Broker API, you will generally only need one instance of this class. However, if you are using the Trading API with OAuth to act on behalf of an Alpaca account, this class is optimized so that it can be instantiated quickly, especially when an existing OkHttpClient is given in the constructor. Additionally, all API endpoint instances are instantiated lazily. This class is thread-safe.

The Alpaca API documentation is located here and the AlpacaAPI Javadoc is located here.

Example usage:

Use this code if you are using the Trading or Market Data APIs for a single Alpaca account:

final String keyID = "<your_key_id>";
final String secretKey = "<your_secret_key>";
final TraderAPIEndpointType endpointType = TraderAPIEndpointType.PAPER; // or 'LIVE'
final MarketDataWebsocketSourceType sourceType = MarketDataWebsocketSourceType.IEX; // or 'SIP'
final AlpacaAPI alpacaAPI = new AlpacaAPI(keyID, secretKey, endpointType, sourceType);

Use this code if you are using the Trading API with OAuth to act on behalf of an Alpaca account:

final String oAuthToken = "<an_oauth_token>";
final TraderAPIEndpointType endpointType = TraderAPIEndpointType.PAPER; // or 'LIVE'
final AlpacaAPI alpacaAPI = new AlpacaAPI(oAuthToken, endpointType);

Use this code if you are using the Broker API:

final String brokerAPIKey = "<your_broker_api_key>";
final String brokerAPISecret = "<your_broker_api_secret>";
final BrokerAPIEndpointType endpointType = BrokerAPIEndpointType.SANDBOX; // or 'PRODUCTION'
final AlpacaAPI alpacaAPI = new AlpacaAPI(brokerAPIKey, brokerAPISecret, endpointType);

Note that this library uses OkHttp as its HTTP client library which creates background threads to service requests via a connection pool. These threads persist even if the main thread exits, so if you want to destroy these threads when you're done using AlpacaAPI, use alpacaAPI.closeOkHttpClient();.

The Trader API is used for placing trades, updating account details, getting open positions, and more. Refer to the Javadoc for a list of all available method signatures.

Example usage:

// Place a market order to buy one share of Apple
final Order openingOrder = alpacaAPI.trader().orders()
        .postOrder(new PostOrderRequest()
                .symbol("AAPL")
                .qty("1")
                .side(OrderSide.BUY)
                .type(OrderType.MARKET)
                .timeInForce(TimeInForce.GTC));
System.out.println("Opening Apple order: " + openingOrder);

// Wait for massive gains
Thread.sleep(10_000);

// Close the Apple position
final Order closingOrder = alpacaAPI.trader().positions()
        .deleteOpenPosition("AAPL", null, new BigDecimal("100"));
System.out.println("Closing Apple order: " + openingOrder);

// Wait for closing trade to fill
Thread.sleep(10_000);

// Print out PnL
final String openFillPrice = alpacaAPI.trader().orders()
        .getOrderByOrderID(UUID.fromString(openingOrder.getId()), false)
        .getFilledAvgPrice();
final String closeFillPrice = alpacaAPI.trader().orders()
        .getOrderByOrderID(UUID.fromString(closingOrder.getId()), false)
        .getFilledAvgPrice();
System.out.println("PnL from Apple trade: " +
        new BigDecimal(closeFillPrice).subtract(new BigDecimal(openFillPrice)));

The Market Data API is used for getting market data for stocks, cryptocurrencies, options, and more. Refer to the Javadoc for a list of all available method signatures.

Example usage:

// Print out the latest Tesla trade
final StockTrade latestTSLATrade = alpacaAPI.marketData().stock()
        .stockLatestTradeSingle("TSLA", StockFeed.IEX, null).getTrade();
System.out.printf("Latest TSLA trade: price=%s, size=%s\n",
        latestTSLATrade.getP(), latestTSLATrade.getS());

// Print out the highest Bitcoin ask price on the order book
final CryptoOrderbook latestBitcoinOrderBooks = alpacaAPI.marketData().crypto()
        .cryptoLatestOrderbooks(CryptoLoc.US, "BTC/USD").getOrderbooks().get("BTC/USD");
final Double highestBitcoinAskPrice = latestBitcoinOrderBooks.getA().stream()
        .map(CryptoOrderbookEntry::getP)
        .max(Double::compare)
        .orElse(null);
System.out.println("Bitcoin highest ask price: " + highestBitcoinAskPrice);

// Print out the latest Microsoft option trade
final String latestMSFTOptionTrade = alpacaAPI.marketData().option()
        .optionChain("MSFT").getSnapshots().entrySet().stream()
        .filter(entry -> entry.getValue().getLatestTrade() != null)
        .map(entry -> Map.entry(entry.getKey(), entry.getValue().getLatestTrade()))
        .max(Comparator.comparing(entry -> entry.getValue().getT()))
        .map(entry -> String.format("ticker=%s, time=%s, price=%s",
                entry.getKey(), entry.getValue().getT(), entry.getValue().getP()))
        .orElse(null);
System.out.println("Latest Microsoft option trade: " + latestMSFTOptionTrade);

The Broker API is used for creating new Alpaca accounts for your end users, funding their accounts, placing trades, and more. Refer to the Javadoc for a list of all available method signatures.

Example usage:

// Listen to the trade events (SSE) and print them out
final SSERequest sseRequest = alpacaAPI.broker().events()
        .subscribeToTradeV2(null, null, null, null, new SSEListenerAdapter<>());

// Wait for SSE channel to be ready
Thread.sleep(2000);

// Buy one share of GME for an account
alpacaAPI.broker().trading()
        .createOrderForAccount(UUID.fromString("<some_account_uuid>"),
                new CreateOrderRequest()
                        .symbol("GME")
                        .qty(BigDecimal.ONE)
                        .side(OrderSide.SELL)
                        .timeInForce(TimeInForce.GTC)
                        .type(OrderType.MARKET));

// Wait to be filled
Thread.sleep(2000);

// Close the SSE stream and the OkHttpClient to exit cleanly
sseRequest.close();
alpacaAPI.closeOkHttpClient();

The Updates Stream is used for listening to trade updates in realtime. Refer to the Javadoc for a list of all available method signatures.

Example usage:

// Connect to the 'updates' stream and wait until it's authorized
alpacaAPI.updatesStream().connect();
if (!alpacaAPI.updatesStream().waitForAuthorization(5, TimeUnit.SECONDS)) {
    throw new RuntimeException();
}

// Print out trade updates
alpacaAPI.updatesStream().setListener(System.out::println);
alpacaAPI.updatesStream().subscribeToTradeUpdates(true);

// Place a trade
alpacaAPI.trader().orders().postOrder(new PostOrderRequest()
        .symbol("AAPL")
        .qty("1")
        .side(OrderSide.BUY)
        .type(OrderType.MARKET)
        .timeInForce(TimeInForce.GTC));

// Wait a few seconds
Thread.sleep(5000);

// Close the trade
alpacaAPI.trader().positions().deleteAllOpenPositions(true);

// Wait a few seconds
Thread.sleep(5000);

// Disconnect the 'updates' stream and exit cleanly
alpacaAPI.updatesStream().disconnect();
alpacaAPI.closeOkHttpClient();

The Market Data Stream is used for listening to stock market data, crypto market data, and news data in realtime. Refer to the Javadocs (stock, crypto, news) for a list of all available method signatures.

Example usage:

// Connect to the 'stock market data' stream and wait until it's authorized
alpacaAPI.stockMarketDataStream().connect();
if (!alpacaAPI.stockMarketDataStream().waitForAuthorization(5, TimeUnit.SECONDS)) {
    throw new RuntimeException();
}

// Print out trade messages
alpacaAPI.stockMarketDataStream().setListener(new StockMarketDataListenerAdapter() {
    @Override
    public void onTrade(StockTradeMessage trade) {
        System.out.println("Received trade: " + trade);
    }
});

// Subscribe to AAPL trades
alpacaAPI.stockMarketDataStream().setTradeSubscriptions(Set.of("AAPL"));
System.out.println("Subscribed to Apple trades.");

// Wait a few seconds
Thread.sleep(5000);

// Unsubscribe from AAPL and subscribe to TSLA and MSFT
alpacaAPI.stockMarketDataStream().setTradeSubscriptions(Set.of("TSLA", "MSFT"));
System.out.println("Subscribed to Tesla and Microsoft trades.");

// Wait a few seconds
Thread.sleep(5000);

// Disconnect the 'stock market data' stream and exit cleanly
alpacaAPI.stockMarketDataStream().disconnect();
alpacaAPI.closeOkHttpClient();

Building

To build this project yourself, clone this repository and run:

./gradlew build

To install the built artifacts to your local Maven repository on your machine (the ~/.m2/ directory), run:

./gradlew publishToMavenLocal

TODO

  • Implement better reconnect logic for Websockets and SSE streaming.
  • Implement Unit Testing for REST API and Websocket streaming (both live and mocked).
  • Use TA4j Num interface instead of Double or BigDecimal for number data types so that users can use either Double or BigDecimal for performance or precision in price data.

Contributing

Contributions are welcome!

Do the following before starting your pull request:

  1. Create a new branch in your forked repository for your feature or bug fix instead of committing directly to the master branch in your fork.
  2. Use the dev branch as the base branch in your pull request.

alpaca-java's People

Contributors

balajikandavel avatar belarusrulez avatar chulsupark avatar cloudygeek avatar darioarena87 avatar dmejer-code avatar dsgraham81 avatar gretard avatar jc-gomes avatar jessethouin avatar jschoeff81690 avatar kurru avatar mainstringargs avatar petersoj avatar robfrank avatar smontiel avatar tamuseanmiller avatar zhemaituk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

alpaca-java's Issues

polygon.domain classes missing from source

I just downloaded the source and the PolygonAPI won't compile because the polygon.domain package is missing...

import io.github.mainstringargs.polygon.domain.SymbolAnalystRatings;
import io.github.mainstringargs.polygon.domain.SymbolDetails;
import io.github.mainstringargs.polygon.domain.SymbolDividend;
import io.github.mainstringargs.polygon.domain.SymbolEarning;
import io.github.mainstringargs.polygon.domain.SymbolEndpoints;
import io.github.mainstringargs.polygon.domain.SymbolFinancial;
import io.github.mainstringargs.polygon.domain.SymbolNews;
import io.github.mainstringargs.polygon.domain.Ticker;

I also noticed other domain package classes missing like...

import io.github.mainstringargs.polygon.domain.StockQuote;
import io.github.mainstringargs.polygon.domain.StockTrade;

Should I this pull this from another repository? Or should it be in this one?

When websocket closes abnormally, alpaca-Java doesn't resubscribe to the previous channels upon reconnect

There is an issue with the polygon websocket closing unexpectedly with the message: "CLOSED_ABNORMALLY"
This, by itself, is a polygon issue. I even get the same thing using wscat: Disconnected (code: 1006, reason: ""). This, unfortunately, is an error the consumer must handle.

Alpaca-java attempts to handle the issue by reconnecting which appears to succeed. However, it doesn't resubscribe to any of the channels from before the abnormal closure. This results in a connection that isn't listening to anything.

I believe that the reconnect logic needs to include resubscribe logic as well.

It appears that the GO alpaca library has gone through a similar issue:
alpacahq/alpaca-trade-api-go#48

MD5 checksum Java 1.8.0_261

I downloaded the Alpaca Java API for use in a programming project today and I have gotten an MD5 checksum that differs from the expected one.

Used Gradle version was 6.6.1 in Git Bash on Windows 10, Version 10.0.19041 Build 19041.

Java: java -version
java version "1.8.0_261"
Java(TM) SE Runtime Environment (build 1.8.0_261-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.261-b12, mixed mode)

MD5 using CertUtil: CertUtil -hashfile alpaca-java-6.0.1.jar md5
MD5 hash of alpaca-java-6.0.1.jar:
96d5a061107f91ab0fbf046e23edeff7
CertUtil: -hashfile command completed successfully.

The program I am building will only use the API to query stock data without making any trades. Can I safely proceed or should I try another Java version to get a matching MD5 checksum?

java.lang.NoClassDefFoundError: AlpacaWebsocketClient on mvn exec:java

My pom.xml is simply:

        <dependency>
            <groupId>io.github.mainstringargs</groupId>
            <artifactId>alpaca-java</artifactId>
            <version>2.0</version>
            <scope>compile</scope>
        </dependency>

I have checked the jar in my local maven repository and it does contain all the generated classes. I've tested the sample code on version 1.1 and it works great. I might be completely missing something on my end, but I've reinstalled all dependencies from the latest version from maven central and still get this error.

Also, is there an ETA for the PolygonAPI to be released on Maven Central?

Ability to track the socket state and events (isConnected, onConnected(), onDisconnected())

Hi

Not having the possibility to know in what state the socket currently is is leading to unexpected errors.

I often get:

2021-05-05 18:22:02.230  INFO 28323 --- [t@1169015699-48] .j.a.w.c.AbstractWebsocketClientEndpoint : Reconnecting due to closure: CLOSED_ABNORMALLY
2021-05-05 18:22:02.234  INFO 28323 --- [t@1169015699-48] .j.a.w.c.AbstractWebsocketClientEndpoint : Connecting to wss://api.alpaca.markets/stream
2021-05-05 18:22:02.240 ERROR 28323 --- [t@1169015699-44] .j.a.w.c.AbstractWebsocketClientEndpoint : Websocket Error!

java.io.IOException: Broken pipe
        at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.flush(SslConnection.java:1086) ~[jetty-io-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.io.WriteFlusher.flush(WriteFlusher.java:422) ~[jetty-io-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.io.WriteFlusher.write(WriteFlusher.java:277) ~[jetty-io-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.io.AbstractEndPoint.write(AbstractEndPoint.java:381) ~[jetty-io-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.websocket.common.io.FrameFlusher.flush(FrameFlusher.java:264) ~[websocket-common-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.websocket.common.io.FrameFlusher.process(FrameFlusher.java:193) ~[websocket-common-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:241) ~[jetty-util-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.util.IteratingCallback.iterate(IteratingCallback.java:223) ~[jetty-util-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.outgoingFrame(AbstractWebSocketConnection.java:581) ~[websocket-common-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.websocket.client.io.WebSocketClientConnection.outgoingFrame(WebSocketClientConnection.java:58) ~[websocket-client-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.close(AbstractWebSocketConnection.java:181) ~[websocket-common-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:510) ~[websocket-common-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:440) ~[websocket-common-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) ~[jetty-io-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) ~[jetty-io-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.onFillable(SslConnection.java:540) ~[jetty-io-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:395) ~[jetty-io-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.io.ssl.SslConnection$2.succeeded(SslConnection.java:161) ~[jetty-io-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) ~[jetty-io-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104) ~[jetty-io-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336) ~[jetty-util-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313) ~[jetty-util-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171) ~[jetty-util-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:129) ~[jetty-util-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:383) ~[jetty-util-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:882) ~[jetty-util-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1036) ~[jetty-util-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]

2021-05-05 18:22:17.263 ERROR 28323 --- [d26-scheduler-1] .j.a.w.c.AbstractWebsocketClientEndpoint : Websocket Error!

java.net.SocketTimeoutException: Connect Timeout
        at org.eclipse.jetty.io.ManagedSelector$Connect.run(ManagedSelector.java:955) ~[jetty-io-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[na:na]
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
        at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
        at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]

2021-05-05 18:22:17.264 ERROR 28323 --- [t@1169015699-48] .j.a.w.c.AbstractWebsocketClientEndpoint : Could not reconnect!

java.net.SocketTimeoutException: Connect Timeout
        at org.eclipse.jetty.io.ManagedSelector$Connect.run(ManagedSelector.java:955) ~[jetty-io-9.4.38.v20210224.jar!/:9.4.38.v20210224]
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[na:na]
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
        at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
        at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]

I can see that the method to do that is public, but not the accessor to the client object.

private final AlpacaWebsocketClient alpacaWebSocketClient;

Would you mind implementing a simple:

@FunctionalInterface
public interface OnSocketConnected {

    public void onConnected();

}
import javax.websocket.CloseReason; // Not sure as to expose it directly

@FunctionalInterface
public interface OnSocketDisconnected {

    public void onDisconnected(CloseReason reason);

}
public class AlpacaWebsocketClient {

    /* ... */

    public boolean isConnected() {
        return alpacaWebSocketClient.isConnected();
    }
    
    /* ... */

}

Thanks in advance

Could not parse message: {"stream":"T.CNHI","data":{"ev":"T","T":"CNHI","i":"52983525296441","x":3,"p":10.79,"s":300,"t":1605711806562000000,"c":[14,41],"z":1}}: NumberFormatException: For input string: "52983525296441"

I am getting the below error since today morning. May be its just me or any one else facing the issue?

Nov 18 2020 08:03:27 AM MST - Could not parse message: {"stream":"T.CNHI","data":{"ev":"T","T":"CNHI","i":"52983525296441","x":3,"p":10.79,"s":300,"t":1605711806562000000,"c":[14,41],"z":1}}
com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: For input string: "52983525296441"
at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:228)
at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:218)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
at com.google.gson.Gson.fromJson(Gson.java:932)
at com.google.gson.Gson.fromJson(Gson.java:1003)
at com.google.gson.Gson.fromJson(Gson.java:975)
at net.jacobpeterson.alpaca.websocket.marketdata.client.MarketDataWebsocketClient.handleWebsocketMessage(MarketDataWebsocketClient.java:246)
at net.jacobpeterson.abstracts.websocket.client.AbstractWebsocketClientEndpoint.lambda$onMessage$1(AbstractWebsocketClientEndpoint.java:148)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)

alpaca.properties not loading for v3.0.0

There now seems to be an issue with loading the alpaca.properties file. When using the no-parameter overloaded constructor (to instantiate AlpacaAPI with properties file), the KEY_ID_VALUE and SECRET_VALUE in the AlpacaProperties class stay as "PLACEHOLDER". This issue did not exist with 2.0 or 1.1. Why does the commit 16397a5 modify the way property/resource files are opened/streamed? Simply using the default class loader rather than the system class loader seemed to work great.

StreamSocket issue

Hi,

Perhaps this is not a software issue but i swear i cannot see how i could add a stream listener for watching the price of stocks. I know it's possible to do it since it's documented on the official API but could not find how here. Is it even implemented?

Thank you

Undefined classes

Classes:
AlpacaStreamMessage
TradeUpdateMessage
AccountUpdateMessage
AlpacaStreamMessage

are not defined...

build fails: missing PolygonNatsClient

/home/stephen/git/alpaca-java/src/main/java/io/github/mainstringargs/polygon/PolygonAPI.java:7: error: cannot find symbol
import io.github.mainstringargs.polygon.nats.PolygonNatsClient;
                                            ^
  symbol:   class PolygonNatsClient
  location: package io.github.mainstringargs.polygon.nats
/home/stephen/git/alpaca-java/src/main/java/io/github/mainstringargs/polygon/PolygonAPI.java:18: error: cannot find symbol
  private final PolygonNatsClient polygonNatsClient;
                ^
  symbol:   class PolygonNatsClient
  location: class PolygonAPI
2 errors


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileJava'.
> Compilation failed; see the compiler error output for details.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

* Get more help at https://help.gradle.org

BUILD FAILED in 47s
3 actionable tasks: 3 executed

Limit Order Bad Request

Order limitOrderTSLA = alpacaAPI.requestNewLimitOrder(
		            "TSLA",
		            100,
		            OrderSide.BUY,
		            OrderTimeInForce.DAY,
		            600.00,
		            false);

- Exception : net.jacobpeterson.alpaca.rest.exception.AlpacaAPIRequestException: Alpaca API Request Exception! : Status Code = 400, Status Text = "Bad Request", API Response Code = 40010000, API Response Message = "request body format is invalid"
at net.jacobpeterson.alpaca.AlpacaAPI.requestNewOrder(AlpacaAPI.java:500)

error when using requestNewStopLimitBracketOrder

public Order requestNewStopLimitBracketOrder(String symbol, Integer quantity, OrderSide side,
        OrderTimeInForce timeInForce, Double limitPrice, Double stopPrice, Boolean extendedHours,
        Double takeProfitLimitPrice, Double stopLossStopPrice, Double stopLossLimitPrice)
        throws AlpacaAPIRequestException {
    return requestNewOrder(symbol, quantity, side, OrderType.STOP_LIMIT, timeInForce, limitPrice, stopPrice,
            extendedHours, null, OrderClass.BRACKET, takeProfitLimitPrice, stopLossStopPrice, stopLossLimitPrice);
}

Order order = alpacaAPI.requestNewStopLimitBracketOrder(symbol, 1, OrderSide.BUY, OrderTimeInForce.DAY, barPlusList.get(1).getC(),
barPlusList.get(1).getC() * .10, false, barPlusList.get(1).getC() * .10, barPlusList.get(0).getL(), barPlusList.get(0).getL());

io.github.mainstringargs.alpaca.rest.exception.AlpacaAPIRequestException: Alpaca API Request Exception! : Status Code = 422, Status Text = "Unprocessable Entity", API Response Code = 40010001, API Response Message = "bracket orders must be market or limit orders"
at io.github.mainstringargs.alpaca.AlpacaAPI.requestNewOrder(AlpacaAPI.java:466)
at io.github.mainstringargs.alpaca.AlpacaAPI.requestNewStopLimitBracketOrder(AlpacaAPI.java:677)
at com.boivin.TradingApplication.openStopLimitBracketOrder(TradingApplication.java:190)
at com.boivin.TradingApplication.trade(TradingApplication.java:154)
at com.boivin.TradingApplication.lambda$null$0(TradingApplication.java:120)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at com.boivin.TradingApplication.lambda$main$2(TradingApplication.java:106)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at com.boivin.TradingApplication.main(TradingApplication.java:71)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)

No ability to request last stock price

I've written a very basic method and added it to the AlpacaAPI.java file on my local machine, but I wonder if you can add it to the official api.
My code:

/**
 * The Last Quote API provides last quote details for a symbol.
 * @param symbol the symbol for which you'd like to get a quote
 * @return The last stock price in a Json format like:
 * {
 *   "status": "success",
 *   "symbol": "SPY",
 *   "last": {
 *     "askprice": 279.1,
 *     "asksize": 1,
 *     "askexchange": 15,
 *     "bidprice": 287.05,
 *     "bidsize": 10,
 *     "bidexchange": 17,
 *     "timestamp": 1588770424970329400
 *   }
 * }
 */
public JsonElement getLastQuote(String symbol){

    AlpacaRequestBuilder urlBuilder = new AlpacaRequestBuilder(baseDataUrl, AlpacaConstants.VERSION_1_ENDPOINT,
        AlpacaConstants.LASTQUOTE_ENDPOINT+ "/" + AlpacaConstants.STOCKS + "/" + symbol);

    HttpResponse<InputStream> response = alpacaRequest.invokeGet(urlBuilder);

    return alpacaRequest.getResponseJSON(response);
}

it's ugly, I know, but it works - I'd love to change it if it means that it will now be exposed...

Thanks,
Elad

Broken pipe in PolygonWebsocketClientEndpoint

got "broken pipe" error as below. Seems like this only happens when subscribing a lot of symbols - like SP100.

2020-05-04 13:28:58.808:WARN:oejwja.JsrEvents:WebSocketClient@2092801316-67: Unable to report throwable to websocket (no @onerror handler declared): io.github.mainstringargs.polygon.websocket.client.PolygonWebsocketClientEndpoint
java.io.IOException: Broken pipe
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.flush(SslConnection.java:1089)
at org.eclipse.jetty.io.WriteFlusher.flush(WriteFlusher.java:422)
at org.eclipse.jetty.io.WriteFlusher.write(WriteFlusher.java:277)
at org.eclipse.jetty.io.AbstractEndPoint.write(AbstractEndPoint.java:381)
at org.eclipse.jetty.websocket.common.io.FrameFlusher.flush(FrameFlusher.java:264)
at org.eclipse.jetty.websocket.common.io.FrameFlusher.process(FrameFlusher.java:193)
at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:241)
at org.eclipse.jetty.util.IteratingCallback.iterate(IteratingCallback.java:223)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.outgoingFrame(AbstractWebSocketConnection.java:584)
at org.eclipse.jetty.websocket.client.io.WebSocketClientConnection.outgoingFrame(WebSocketClientConnection.java:58)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.close(AbstractWebSocketConnection.java:181)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:511)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:441)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.onFillable(SslConnection.java:543)
at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:398)
at org.eclipse.jetty.io.ssl.SslConnection$2.succeeded(SslConnection.java:161)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:129)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:375)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938)
at java.lang.Thread.run(Thread.java:748)

Polygon Websocket Not Connecting on JDK 11, 12, or 13.

When I try to connect to the Polygon web socket I get an error that reads "Handshake response not received." When I use the REST api it connects fine and I get no issues. I know that it isn't on the server side because when I run "wscat -c wss://socket.polygon.io/stocks" I connect fine and am authorized each time. I am running JDK 13 and have tried this on JDK 11 and it didn't work. I noticed however that when I run on JDK 8 it works fine. I believe it might be a version compatibility issue.

I know that the web socket library in use is tyrus, and I found out that the repository is no longer supported. On the web I have found that others have had similar issues with later versions of Java and that JDK 8 is the last known working version of Java with tyrus for certain websockets. I don't know enough to understand why, but the alpaca web socket works great on JDK 13. I will attach some photos for reference.
Capture

PolygonAPI Subscribing to wildcard ("*") does not work.

According to the Polygon API, you can stream all tickers on the available channels using the wildcard (e.g. T.* or Q.*), but when I do so in the code below, there are no stream updates and the connection just hangs.

        PolygonAPI polygonAPI = new PolygonAPI();
        polygonAPI.addPolygonStreamListener(new PolygonStreamListener() {
            @Override
            public Map<String, Set<ChannelType>> getStockChannelTypes() {
                Map<String, Set<ChannelType>> channelTypes = new HashMap<>();
                channelTypes.put("*", Sets.newHashSet(ChannelType.QUOTES));
                return channelTypes;
            }

            @Override
            public void streamUpdate(String s, ChannelType channelType, ChannelMessage channelMessage) {
                System.out.println("===> streamUpdate " + s + " " + channelType + " " + channelMessage);
            }
        });

I spoke with a Polygon representative and supposedly the Alpaca Live Account API Key does allow for wildcards when subscribing to various channels. It may be a limitation Polygon put on the NATS connections specifically for Alpaca, but I'm not sure.

getDailyOpenClose returning null

The method getDailyOpenClose in PolygonAPI seems to always return null, I didn't spend too much time on it but I couldn't tell why it was happening. I haven't tested in any environment but my own but thought I would bring it up in case others get the issue.

Just an example call...
polygonAPI.getDailyOpenClose("AMD", lastOpenDate);

Could not find method buildScan() for arguments

I ran...

gradlew build

And got...

Could not find method buildScan() for arguments

I edited the build.gradle and added the if statement below to get it to work...

if (hasProperty('buildScan')) {
	buildScan { licenseAgreementUrl = 'https://gradle.com/terms-of-service'; licenseAgree = 'yes' }
}

Might want to update the codebase.

Ask or Bid price gives me unreasonable numbers

Hi;

I use MarketDataListener to read the QUOTE for stocks in real-time such as ask or bid price. I realized that either ask or bid gives quite a different price from what is happening in the market. Please take a look at the video I recorded; it shows how suddenly ask or bid price changes. It can create many problems if a user wants to create their strategy based on the real data generated by the stream listeners as it does not reflect the real numbers in the market. For now, I use my paper account as I am testing the library and its classes.

https://www.dropbox.com/s/5c0rckrvy667owq/2021-03-09%2012-49-59.mp4?dl=0

Here is my POM for Alpaca:

        <dependency>
            <groupId>net.jacobpeterson</groupId>
            <artifactId>alpaca-java</artifactId>
            <version>7.0</version>
            <scope>compile</scope>
        </dependency>

Unit Testing + Coverage Setup

Add Unit Testing + Mocking libraries to Gradle configuration
Add Unit Testing + Coverage output to CI configuration
Make results available on github

Watchlist

When running the example program, you get

io.github.mainstringargs.alpaca.rest.exception.AlpacaAPIRequestException: Alpaca API Request Exception! : Status Code = 422, Status Text = "Unprocessable Entity", API Response Code = 40010001, API Response Message = "watchlist name must be unique" at io.github.mainstringargs.alpaca.AlpacaAPI.createWatchlist(AlpacaAPI.java:1121) at com.lavirz.alpaca.AlpacaExample.main(AlpacaExample.java:69)

WebSocket Polygon support

Transition from using NATS for Polygon to using WebSockets for polygon. NATS is supported currently, but they may switch completely over to WebSockets

Requesting a New Market Order is always Throwing the 422 Unprocessable Entity Status Code

I'm a few commits behind, but I don't think anything was changed on this front and was hoping for some help. Here's the logcat from the event though I don't think it is particularly useful:

2020-10-26 10:48:05.771 3147-3147/com.bedefined.alpaca_dashboard W/System.err: net.jacobpeterson.alpaca.rest.exception.AlpacaAPIRequestException: Alpaca API Request Exception! : Status Code = 422, Status Text = "Unprocessable Entity", API Response Code = 40010001, API Response Message = "only type="limit" is allowed for extended hours orders" 2020-10-26 10:48:05.771 3147-3147/com.bedefined.alpaca_dashboard W/System.err: at net.jacobpeterson.alpaca.AlpacaAPI.requestNewOrder(AlpacaAPI.java:481) 2020-10-26 10:48:05.771 3147-3147/com.bedefined.alpaca_dashboard W/System.err: at net.jacobpeterson.alpaca.AlpacaAPI.requestNewMarketOrder(AlpacaAPI.java:507) 2020-10-26 10:48:05.771 3147-3147/com.bedefined.alpaca_dashboard W/System.err: at com.bedefined.alpaca_dashboard.PlaceOrderFragment.lambda$null$4$PlaceOrderFragment(PlaceOrderFragment.java:51) 2020-10-26 10:48:05.771 3147-3147/com.bedefined.alpaca_dashboard W/System.err: at com.bedefined.alpaca_dashboard.-$$Lambda$PlaceOrderFragment$Usfum6cYPNAIO_-730mJUFgVpDU.onClick(Unknown Source:10) 2020-10-26 10:48:05.771 3147-3147/com.bedefined.alpaca_dashboard W/System.err: at androidx.appcompat.app.AlertController$ButtonHandler.handleMessage(AlertController.java:167) 2020-10-26 10:48:05.771 3147-3147/com.bedefined.alpaca_dashboard W/System.err: at android.os.Handler.dispatchMessage(Handler.java:106) 2020-10-26 10:48:05.771 3147-3147/com.bedefined.alpaca_dashboard W/System.err: at android.os.Looper.loop(Looper.java:223) 2020-10-26 10:48:05.771 3147-3147/com.bedefined.alpaca_dashboard W/System.err: at android.app.ActivityThread.main(ActivityThread.java:7656) 2020-10-26 10:48:05.771 3147-3147/com.bedefined.alpaca_dashboard W/System.err: at java.lang.reflect.Method.invoke(Native Method) 2020-10-26 10:48:05.771 3147-3147/com.bedefined.alpaca_dashboard W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 2020-10-26 10:48:05.771 3147-3147/com.bedefined.alpaca_dashboard W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

This is an example command that I have tried to run:
alpacaAPI.requestNewMarketOrder("AMD", 1, OrderSide.BUY, OrderTimeInForce.DAY, true)

It's just throwing the AlpacaAPIRequestException and tossing a null value
image

Anything else I can add in to help let me know, this is a weird one.

AlpacaAPI.getBars 7.0

Hello. I'm upgrading from version 6.1 to version 7.0 and for some reason I keep getting zero results when I call AlpacaAPI.getBars().

val symbol = "QQQ";
val alpaca = new AlpacaAPI();
val start = ZonedDateTime.of(2021, 3, 16, 9, 30, 0, 0, ZoneId.of("America/New_York"));
val end = ZonedDateTime.of(2021, 3, 16, 16, 0, 0, 0, ZoneId.of("America/New_York"));
val map = alpaca.getBars(symbol, start, end, null, null, BarsTimeFrame.MINUTE);
val bars = map.getBars();
System.out.println(bars.size()); // prints 0

I tried various inputs but no luck. Any ideas?

Ability to add or remove Websocket ticker listeners

If we need to add or remove a specific ticker from the alpaca and/or polygon, we have to remove the whole listener, reinitialize it with the updated ticker list, and then listen for it again.

Any way we can just add or remove tickers from the listener itself? @Petersoj

WebSocketException when disconnecting only open market data stream

I believe the issue is .getContainer() on the userSession returns null after the userSession is closed in AbstractWebsocketClientEndpoint.

I'm using your example code in the README to reproduce this issue.

	at net.jacobpeterson.alpaca.websocket.marketdata.client.MarketDataWebsocketClient.removeListener(MarketDataWebsocketClient.java:150) ~[alpaca-java-6.1.jar:na]
	at net.jacobpeterson.alpaca.AlpacaAPI.removeMarketDataStreamListener(AlpacaAPI.java:1735) ~[alpaca-java-6.1.jar:na]

jsonschema2pojo causes exception failed: trying to create the same field twice: t

I did some research on this and I don't think you could do anything about this, but I thought I would just make you aware of it.

Failed to execute goal org.jsonschema2pojo:jsonschema2pojo-maven-plugin:1.0.1:generate (default) on project alpaca-java: Execution default of goal org.jsonschema2pojo:jsonschema2pojo-maven-plugin:1.0.1:generate failed: trying to create the same field twice: t -> [Help 1]

PolygonAPI.getGroupedDaily all or nothing failure

If a single record from grouped daily is bad or unparseable then you get no records at all.

example: if you pull data from 1998-01-20, there is a record that causes a number format exception in the gson deserialization.

wouldn't it be better to return data that was successfully parsed rather than return nothing?

Configurable property configurations

Any plans on making AlpacaProperties able to load from a specified resource? I need to do both live and paper trading at once using two instances of AlpacaAPI. I can make a pull request if you're not planning on doing that change yourself any time soon.

Thanks!

Alpaca.properties Question

What is the user_agent property? I'm familiar with the key_id and secret_key, but where do I find user_agent_key??

Thanks

Use enum more if possible instead of String

Hi

Is there a way to use a Java enum instead of a String field in such cases? (see below)

"event": {
"existingJavaType": "java.lang.String",
"title": "The event type"
},

Here is what I was using for replacement:

public enum AlpacaEvent {
	
	/** Sent when an order has been routed to exchanges for execution. */
	NEW,
	
	/** Sent when your order has been completely filled. */
	FILL,
	
	/** Sent when a number of shares less than the total remaining quantity on your order has been filled. */
	PARTIAL_FILL,
	
	/** Sent when your requested cancelation of an order is processed. */
	CANCELED,
	
	/** Sent when an order has reached the end of its lifespan, as determined by the order’s time in force value. **/
	EXPIRED,
	
	/** Sent when the order is done executing for the day, and will not receive further updates until the next trading day. */
	DONE_FOR_DAY,
	
	/** Sent when your requested replacement of an order is processed. */
	REPLACED,
	
	/** Sent when your order has been rejected. */
	REJECTED,
	
	/** Sent when the order has been received by Alpaca and routed to the exchanges, but has not yet been accepted for execution. */
	PENDING_NEW,
	
	/** Sent when your order has been stopped, and a trade is guaranteed for the order, usually at a stated price or better, but has not yet occurred. */
	STOPPED,
	
	/** Sent when the order is awaiting cancelation. Most cancelations will occur without the order entering this state. */
	PENDING_CANCEL,
	
	/** Sent when the order is awaiting replacement. */
	PENDING_REPLACE,
	
	/** Sent when the order has been completed for the day - it is either “filled” or “done_for_day” - but remaining settlement calculations are still pending. */
	CALCULATED,
	
	/** Sent when the order has been suspended and is not eligible for trading. */
	SUSPENDED,
	
	/** Sent when the order replace has been rejected. */
	ORDER_REPLACE_REJECTED,
	
	/** Sent when the order cancel has been rejected. */
	ORDER_CANCEL_REJECTED;
	
	public static AlpacaEvent of(String text) {
		try {
			return valueOf(text.toUpperCase());
		} catch (IllegalArgumentException exception) {
			return null;
		}
	}
	
}

Since the enum fields returned by Alpaca's API are in lowercase, I see a big usage of

/**
* The {@link APIName} interface is for enums classes to define enum members with API names.
*/
public interface APIName {

while you can use @SerializedName from GSON?
(i am a Jackson user, so i can't really tell you is this the correct way to do it, but take a look at this: https://stackoverflow.com/a/18343576/7292958)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.