51 changed files with 2697 additions and 42 deletions
			
			
		@ -0,0 +1,42 @@ | 
				
			|||||||
 | 
					# Java Gradle CircleCI 2.0 configuration file | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# Check https://circleci.com/docs/2.0/language-java/ for more details | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					version: 2 | 
				
			||||||
 | 
					jobs: | 
				
			||||||
 | 
					  build: | 
				
			||||||
 | 
					    docker: | 
				
			||||||
 | 
					      # specify the version you desire here | 
				
			||||||
 | 
					      - image: circleci/openjdk:8-jdk | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # Specify service dependencies here if necessary | 
				
			||||||
 | 
					      # CircleCI maintains a library of pre-built images | 
				
			||||||
 | 
					      # documented at https://circleci.com/docs/2.0/circleci-images/ | 
				
			||||||
 | 
					      # - image: circleci/postgres:9.4 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    working_directory: ~/repo | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    environment: | 
				
			||||||
 | 
					      # Customize the JVM maximum heap limit | 
				
			||||||
 | 
					      JVM_OPTS: -Xmx3200m | 
				
			||||||
 | 
					      TERM: dumb | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    steps: | 
				
			||||||
 | 
					      - checkout | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # Download and cache dependencies | 
				
			||||||
 | 
					      - restore_cache: | 
				
			||||||
 | 
					          keys: | 
				
			||||||
 | 
					          - v1-dependencies-{{ checksum "build.gradle" }} | 
				
			||||||
 | 
					          # fallback to using the latest cache if no exact match is found | 
				
			||||||
 | 
					          - v1-dependencies- | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - run: gradle dependencies | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - save_cache: | 
				
			||||||
 | 
					          paths: | 
				
			||||||
 | 
					            - ~/.gradle | 
				
			||||||
 | 
					          key: v1-dependencies-{{ checksum "build.gradle" }} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # run tests! | 
				
			||||||
 | 
					      - run: gradle test | 
				
			||||||
@ -0,0 +1,32 @@ | 
				
			|||||||
 | 
					Your issue may already be reported! | 
				
			||||||
 | 
					Please search on the [issue track](../) before creating one. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Expected Behavior | 
				
			||||||
 | 
					<!--- If you're describing a bug, tell us what should happen --> | 
				
			||||||
 | 
					<!--- If you're suggesting a change/improvement, tell us how it should work --> | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Current Behavior | 
				
			||||||
 | 
					<!--- If describing a bug, tell us what happens instead of the expected behavior --> | 
				
			||||||
 | 
					<!--- If suggesting a change/improvement, explain the difference from current behavior --> | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Possible Solution | 
				
			||||||
 | 
					<!--- Not obligatory, but suggest a fix/reason for the bug, --> | 
				
			||||||
 | 
					<!--- or ideas how to implement the addition or change --> | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Steps to Reproduce (for bugs) | 
				
			||||||
 | 
					<!--- Provide a link to a live example, or an unambiguous set of steps to --> | 
				
			||||||
 | 
					<!--- reproduce this bug. Include code to reproduce, if relevant --> | 
				
			||||||
 | 
					1. | 
				
			||||||
 | 
					2. | 
				
			||||||
 | 
					3. | 
				
			||||||
 | 
					4. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Context | 
				
			||||||
 | 
					<!--- How has this issue affected you? What are you trying to accomplish? --> | 
				
			||||||
 | 
					<!--- Providing context helps us come up with a solution that is most useful in the real world --> | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Your Environment | 
				
			||||||
 | 
					<!--- Include as many relevant details about the environment you experienced the bug in --> | 
				
			||||||
 | 
					* Version used: | 
				
			||||||
 | 
					* Operating System and version (desktop or mobile): | 
				
			||||||
 | 
					* Link to your project: | 
				
			||||||
@ -0,0 +1,27 @@ | 
				
			|||||||
 | 
					<!--- Provide a general summary of your changes in the Title above --> | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Description | 
				
			||||||
 | 
					<!--- Describe your changes in detail --> | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Motivation and Context | 
				
			||||||
 | 
					<!--- Why is this change required? What problem does it solve? --> | 
				
			||||||
 | 
					<!--- If it fixes an open issue, please link to the issue here. --> | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## How Has This Been Tested? | 
				
			||||||
 | 
					<!--- Please describe in detail how you tested your changes. --> | 
				
			||||||
 | 
					<!--- Include details of your testing environment, tests ran to see how --> | 
				
			||||||
 | 
					<!--- your change affects other areas of the code, etc. --> | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Types of changes | 
				
			||||||
 | 
					<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> | 
				
			||||||
 | 
					- [ ] Bug fix (non-breaking change which fixes an issue) | 
				
			||||||
 | 
					- [ ] New feature (non-breaking change which adds functionality) | 
				
			||||||
 | 
					- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Checklist: | 
				
			||||||
 | 
					<!--- Go over all the following points, and put an `x` in all the boxes that apply. --> | 
				
			||||||
 | 
					<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> | 
				
			||||||
 | 
					- [ ] My code follows the code style of this project. | 
				
			||||||
 | 
					- [ ] My change requires a change to the documentation. | 
				
			||||||
 | 
					- [ ] I have updated the documentation accordingly. | 
				
			||||||
 | 
					- [ ] I have updated the version of the library as required per the specifications of [Semantic Versioning](https://semver.org/) | 
				
			||||||
@ -0,0 +1,86 @@ | 
				
			|||||||
 | 
					# Contributing to the BinanceJ Project | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Welcome to BinanceJ! | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The goal of this project is to provide the de-facto standard Binance API in Java. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- | 
				
			||||||
 | 
					#### Sections | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> * [Getting Started](#getting-started) | 
				
			||||||
 | 
					> * [Testing](#testing) | 
				
			||||||
 | 
					> * [Code Standards](#code-standards) | 
				
			||||||
 | 
					> * [Versioning](#versioning) | 
				
			||||||
 | 
					> * [Submitting Pull Requests](#submitting-pull-requests) | 
				
			||||||
 | 
					> * [Submitting Issues](#submitting-issues) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Getting Started | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Overall Requirements | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Java 8  | 
				
			||||||
 | 
					* Gradle | 
				
			||||||
 | 
					* [Sonarlint](https://www.sonarlint.org/) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It is highly suggested you use something like Intellij IDEA when developing this project though | 
				
			||||||
 | 
					there is no strict enforcement on that. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To get started with the project, do as follows:  | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh | 
				
			||||||
 | 
					git clone git@github.com:angrygoats/binancej.git | 
				
			||||||
 | 
					cd binancej | 
				
			||||||
 | 
					gradle clean && gradle build | 
				
			||||||
 | 
					``` | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Testing | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We operate a strict TDD shop here. As a result, any PR submitted without tests for anything but _the most trivial_ of | 
				
			||||||
 | 
					features will be rejected immediately. 100% code coverage is not necessary (but desirable where possible). You should | 
				
			||||||
 | 
					aim to cover all major conditions your code can go through thoroughly. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The tools we primarily use are Junit4 and Mockito. Since the API is so simple this all we really need. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To run the tests, simple run: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh | 
				
			||||||
 | 
					gradle test | 
				
			||||||
 | 
					```  | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from the root of the project. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Code Standards | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Clean Java is good Java. As all of us know Java can get unnecessarily verbose at times, and in order to keep readability | 
				
			||||||
 | 
					in view at all times good code standards should be followed: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Run Sonarlint and follow it's suggestions | 
				
			||||||
 | 
					2. 4 spaces (not tabs) | 
				
			||||||
 | 
					3. Keep a space between each member of a class for readability | 
				
			||||||
 | 
					4. Javadoc every function you write with the exception of already documented functions you are `@Override`'ing   | 
				
			||||||
 | 
					5. Javadoc classes that are not obvious from their name | 
				
			||||||
 | 
					6. __Absolutely no wild card importing__ | 
				
			||||||
 | 
					7. Clean up your imports - do not leave unused imports in any code you write | 
				
			||||||
 | 
					8. You should make an effort to clean up all code you touch if the clean up will not take too long | 
				
			||||||
 | 
					    * Any thing that will take long should be made into an issue | 
				
			||||||
 | 
					9. Make use of Lombok wherever you can to reduce boilerplate as much as possible | 
				
			||||||
 | 
					10. Take advantage of Java 8 features such as streams to simplify your code where possible | 
				
			||||||
 | 
					11. If you are using IntelliJ, take advantage of code formatting under `Code -> Reformat Code` | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Versioning | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The BinanceJ project uses [Semantic Versioning](https://semver.org/). In the `build.gradle` you will find a line | 
				
			||||||
 | 
					to adjust the semantic versioning of the library. | 
				
			||||||
 | 
					     | 
				
			||||||
 | 
					 ## Submitting Pull Requests | 
				
			||||||
 | 
					  | 
				
			||||||
 | 
					 Please be sure to follow the supplied pull request template. PRs will be rejected if they lack tests or the test | 
				
			||||||
 | 
					 coverage isn't sufficient. | 
				
			||||||
 | 
					  | 
				
			||||||
 | 
					 ## Submitting Issues | 
				
			||||||
 | 
					  | 
				
			||||||
 | 
					 Please be sure to follow the supplied issue template. If the template does not contain fields you need, feel free | 
				
			||||||
 | 
					 to add them if they provide more context to the issue you are experiencing. | 
				
			||||||
@ -0,0 +1,21 @@ | 
				
			|||||||
 | 
					MIT License | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Copyright (c) 2018 Taylor Bockman | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Permission is hereby granted, free of charge, to any person obtaining a copy | 
				
			||||||
 | 
					of this software and associated documentation files (the "Software"), to deal | 
				
			||||||
 | 
					in the Software without restriction, including without limitation the rights | 
				
			||||||
 | 
					to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
				
			||||||
 | 
					copies of the Software, and to permit persons to whom the Software is | 
				
			||||||
 | 
					furnished to do so, subject to the following conditions: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The above copyright notice and this permission notice shall be included in all | 
				
			||||||
 | 
					copies or substantial portions of the Software. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			||||||
 | 
					IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			||||||
 | 
					FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
				
			||||||
 | 
					AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
				
			||||||
 | 
					LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
				
			||||||
 | 
					OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
				
			||||||
 | 
					SOFTWARE. | 
				
			||||||
@ -1,17 +0,0 @@ | 
				
			|||||||
package com.sigmaflare; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class Binance { | 
					 | 
				
			||||||
    private final String apiKey; | 
					 | 
				
			||||||
    private final String secretKey; | 
					 | 
				
			||||||
    private static final String BASE_ENDPOINT = "https://api.binance.com"; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public Binance(String apiKey, String secretKey) { | 
					 | 
				
			||||||
       this.apiKey = apiKey; | 
					 | 
				
			||||||
       this.secretKey = secretKey; | 
					 | 
				
			||||||
    } | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public boolean someLibraryMethod() { | 
					 | 
				
			||||||
        return true; | 
					 | 
				
			||||||
    } | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
@ -0,0 +1,38 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.ObjectMapper; | 
				
			||||||
 | 
					import org.apache.http.impl.client.CloseableHttpClient; | 
				
			||||||
 | 
					import org.apache.http.impl.client.HttpClientBuilder; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public abstract class BaseBinanceApi { | 
				
			||||||
 | 
					    protected final String apiKey; | 
				
			||||||
 | 
					    protected final String secretKey; | 
				
			||||||
 | 
					    protected final CloseableHttpClient closeableHttpClient; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected static final ObjectMapper mapper = Helpers.objectMapperBuilder(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public BaseBinanceApi(String apiKey, String secretKey) { | 
				
			||||||
 | 
					        this.apiKey = apiKey; | 
				
			||||||
 | 
					        this.secretKey = secretKey; | 
				
			||||||
 | 
					        this.closeableHttpClient = HttpClientBuilder.create().build(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public BaseBinanceApi(String apiKey, String secretKey, CloseableHttpClient closeableHttpClient) { | 
				
			||||||
 | 
					        this.apiKey = apiKey; | 
				
			||||||
 | 
					        this.secretKey = secretKey; | 
				
			||||||
 | 
					        this.closeableHttpClient = closeableHttpClient; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void close() throws IOException { | 
				
			||||||
 | 
					        closeableHttpClient.close(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    protected void finalize() throws IOException { | 
				
			||||||
 | 
					        close(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,11 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					final class Constant { | 
				
			||||||
 | 
					    private Constant() {} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static final String BASE_ENDPOINT = "https://api.binance.com"; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static final String NO_RESPONSE_TEXT = "No response returned from {}"; | 
				
			||||||
 | 
					    static final String NO_RESPONSE_TEXT_FORMATTED = "No response returned from %s"; | 
				
			||||||
 | 
					    static final String SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED = "Symbol and interval must be supplied"; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,148 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.codepoetics.ambivalence.Either; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.ExchangeInfo; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.Ping; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.ServiceError; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.Time; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException; | 
				
			||||||
 | 
					import lombok.Builder; | 
				
			||||||
 | 
					import lombok.extern.slf4j.Slf4j; | 
				
			||||||
 | 
					import org.apache.http.HttpEntity; | 
				
			||||||
 | 
					import org.apache.http.StatusLine; | 
				
			||||||
 | 
					import org.apache.http.client.methods.CloseableHttpResponse; | 
				
			||||||
 | 
					import org.apache.http.client.methods.HttpGet; | 
				
			||||||
 | 
					import org.apache.http.impl.client.CloseableHttpClient; | 
				
			||||||
 | 
					import org.apache.http.util.EntityUtils; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT; | 
				
			||||||
 | 
					import static com.sigmaflare.binancej.Constant.NO_RESPONSE_TEXT; | 
				
			||||||
 | 
					import static com.sigmaflare.binancej.Constant.NO_RESPONSE_TEXT_FORMATTED; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * GeneralUtilities is a container class for methods that interact with the infrastructure | 
				
			||||||
 | 
					 * on the Binance side. Methods like server health and exchange information are found here. | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					@Slf4j | 
				
			||||||
 | 
					public class GeneralUtilities extends BaseBinanceApi { | 
				
			||||||
 | 
					    private static final String PING_URL = "/api/v1/ping"; | 
				
			||||||
 | 
					    private static final String TIME_URL = "/api/v1/time"; | 
				
			||||||
 | 
					    private static final String EXCHANGE_INFO_URL = "/api/v1/exchangeInfo"; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Builder | 
				
			||||||
 | 
					    public GeneralUtilities(String apiKey, String secretKey) { | 
				
			||||||
 | 
					       super(apiKey, secretKey); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    GeneralUtilities(String apiKey, String secretKey, CloseableHttpClient closeableHttpClient) { | 
				
			||||||
 | 
					        super(apiKey, secretKey, closeableHttpClient); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Hits the ping endpoint to check if the service is alive. | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @return empty Ping object if it returned 200, otherwise ServiceError | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public Either<ServiceError, Ping> ping() throws BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        final String url = String.format("%s%s", BASE_ENDPOINT, PING_URL); | 
				
			||||||
 | 
					        final HttpGet request = Helpers.getBuilder(url, apiKey); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try { | 
				
			||||||
 | 
					            try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) { | 
				
			||||||
 | 
					                StatusLine sl = closeableHttpResponse.getStatusLine(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                HttpEntity httpEntity = closeableHttpResponse.getEntity(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (httpEntity == null) { | 
				
			||||||
 | 
					                    log.error(NO_RESPONSE_TEXT, url); | 
				
			||||||
 | 
					                    throw new BinanceServiceUnreachableException( | 
				
			||||||
 | 
					                        String.format(NO_RESPONSE_TEXT_FORMATTED, url), null); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                String response = EntityUtils.toString(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!Helpers.statusCodeIsOk(sl.getStatusCode())) { | 
				
			||||||
 | 
					                    return Either.ofLeft(mapper.readValue(response, ServiceError.class)); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return Either.ofRight(new Ping()); | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } catch (IOException e) { | 
				
			||||||
 | 
					            throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause()); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Gets the current server time on Binance's servers | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @return A Time object if successful, otherwise an ServiceError object | 
				
			||||||
 | 
					     * @throws BinanceServiceUnreachableException Throws when the request fails | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public Either<ServiceError, Time> getServerTime() throws BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        final String url = String.format("%s%s", BASE_ENDPOINT, TIME_URL); | 
				
			||||||
 | 
					        final HttpGet request = Helpers.getBuilder(url, apiKey); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try { | 
				
			||||||
 | 
					            try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) { | 
				
			||||||
 | 
					                StatusLine sl = closeableHttpResponse.getStatusLine(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                HttpEntity httpEntity = closeableHttpResponse.getEntity(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (httpEntity == null) { | 
				
			||||||
 | 
					                    log.error(NO_RESPONSE_TEXT, url); | 
				
			||||||
 | 
					                    throw new BinanceServiceUnreachableException( | 
				
			||||||
 | 
					                        String.format(NO_RESPONSE_TEXT_FORMATTED, url), null); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                String response = EntityUtils.toString(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!Helpers.statusCodeIsOk(sl.getStatusCode())) { | 
				
			||||||
 | 
					                    return Either.ofLeft(mapper.readValue(response, ServiceError.class)); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return Either.ofRight(mapper.readValue(response, Time.class)); | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } catch (IOException e) { | 
				
			||||||
 | 
					            throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause()); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Retrieves exchange information | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @return An ExchangeInfo when successful, otherwise an ServiceError | 
				
			||||||
 | 
					     * @throws BinanceServiceUnreachableException If the service cannot be reached | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public Either<ServiceError, ExchangeInfo> getExchangeInfo() throws BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        final String url = String.format("%s%s", BASE_ENDPOINT, EXCHANGE_INFO_URL); | 
				
			||||||
 | 
					        final HttpGet request = Helpers.getBuilder(url, apiKey); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try { | 
				
			||||||
 | 
					            try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) { | 
				
			||||||
 | 
					                StatusLine sl = closeableHttpResponse.getStatusLine(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                HttpEntity httpEntity = closeableHttpResponse.getEntity(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (httpEntity == null) { | 
				
			||||||
 | 
					                    log.error(NO_RESPONSE_TEXT, url); | 
				
			||||||
 | 
					                    throw new BinanceServiceUnreachableException( | 
				
			||||||
 | 
					                        String.format(NO_RESPONSE_TEXT_FORMATTED, url), null); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                String response = EntityUtils.toString(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!Helpers.statusCodeIsOk(sl.getStatusCode())) { | 
				
			||||||
 | 
					                    return Either.ofLeft(mapper.readValue(response, ServiceError.class)); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return Either.ofRight(mapper.readValue(response, ExchangeInfo.class)); | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } catch (IOException e) { | 
				
			||||||
 | 
					            throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause()); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,87 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.codepoetics.ambivalence.LeftProjection; | 
				
			||||||
 | 
					import com.codepoetics.ambivalence.RightProjection; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.ObjectMapper; | 
				
			||||||
 | 
					import org.apache.http.client.methods.HttpGet; | 
				
			||||||
 | 
					import org.apache.http.client.methods.HttpPost; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.function.Function; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public final class Helpers { | 
				
			||||||
 | 
					    private Helpers() { | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Checks to insure the status code is ok. | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param statusCode The status code | 
				
			||||||
 | 
					     * @return True of the status code is acceptable (200-399), and false otherwise | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public static boolean statusCodeIsOk(int statusCode) { | 
				
			||||||
 | 
					        return statusCode >= 200 && statusCode < 400; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Builds a configured HttpGet | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param url    The full URL to GET to | 
				
			||||||
 | 
					     * @param apiKey The API key | 
				
			||||||
 | 
					     * @return A configured HttpGet object | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public static HttpGet getBuilder(String url, String apiKey) { | 
				
			||||||
 | 
					        HttpGet httpGet = new HttpGet(url); | 
				
			||||||
 | 
					        httpGet.setHeader("X-MBX-APIKEY", apiKey); | 
				
			||||||
 | 
					        return httpGet; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Builds a configured HttpPost | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param url    The full URL to POST to | 
				
			||||||
 | 
					     * @param apiKey The API key | 
				
			||||||
 | 
					     * @return A configured HttpPost object | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public static HttpPost postBuilder(String url, String apiKey) { | 
				
			||||||
 | 
					        HttpPost httpPost = new HttpPost(url); | 
				
			||||||
 | 
					        httpPost.setHeader("X-MBX-APIKEY", apiKey); | 
				
			||||||
 | 
					        return httpPost; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Builds a fully configured ObjectMapper | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @return A fully configured ObjectMapper | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public static ObjectMapper objectMapperBuilder() { | 
				
			||||||
 | 
					        return new ObjectMapper().findAndRegisterModules(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Safely extracts the value from a LeftProjection of an Either | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param val The LeftProjection to perform the extraction on | 
				
			||||||
 | 
					     * @param <L> The Left type | 
				
			||||||
 | 
					     * @param <R> The Right type | 
				
			||||||
 | 
					     * @return The unwrapped L type object | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked") | 
				
			||||||
 | 
					    public static <L, R> L extractEitherValueSafely(LeftProjection<L, R> val) { | 
				
			||||||
 | 
					        return (L) val.join(Function.identity(), Function.identity()); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Safely extracts the value from a RightProjection of an Either | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param val The RightProjection to perform the extraction on | 
				
			||||||
 | 
					     * @param <L> The Left type | 
				
			||||||
 | 
					     * @param <R> The Right type | 
				
			||||||
 | 
					     * @return The unwrapped R type object | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    @SuppressWarnings("unchecked") | 
				
			||||||
 | 
					    public static <L, R> R extractEitherValueSafely(RightProjection<L, R> val) { | 
				
			||||||
 | 
					        return (R) val.join(Function.identity(), Function.identity()); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,382 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.codepoetics.ambivalence.Either; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.JavaType; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.Candlestick; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.Interval; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.OrderBookDepth; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.ServiceError; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.TickerPrice; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException; | 
				
			||||||
 | 
					import lombok.Builder; | 
				
			||||||
 | 
					import lombok.extern.slf4j.Slf4j; | 
				
			||||||
 | 
					import org.apache.http.HttpEntity; | 
				
			||||||
 | 
					import org.apache.http.StatusLine; | 
				
			||||||
 | 
					import org.apache.http.client.methods.CloseableHttpResponse; | 
				
			||||||
 | 
					import org.apache.http.client.methods.HttpGet; | 
				
			||||||
 | 
					import org.apache.http.impl.client.CloseableHttpClient; | 
				
			||||||
 | 
					import org.apache.http.util.EntityUtils; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException; | 
				
			||||||
 | 
					import java.util.List; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT; | 
				
			||||||
 | 
					import static com.sigmaflare.binancej.Constant.NO_RESPONSE_TEXT; | 
				
			||||||
 | 
					import static com.sigmaflare.binancej.Constant.NO_RESPONSE_TEXT_FORMATTED; | 
				
			||||||
 | 
					import static com.sigmaflare.binancej.Constant.SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Slf4j | 
				
			||||||
 | 
					public class MarketData extends BaseBinanceApi { | 
				
			||||||
 | 
					    private static final String ORDER_BOOK_URL = "/api/v1/depth"; | 
				
			||||||
 | 
					    private static final String CANDLESTICK_URL = "/api/v1/klines"; | 
				
			||||||
 | 
					    private static final String TICKER_PRICE_URL = "/api/v3/ticker/price"; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Builder | 
				
			||||||
 | 
					    MarketData(String apiKey, String secretKey) { | 
				
			||||||
 | 
					        super(apiKey, secretKey); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MarketData(String apiKey, String secretKey, CloseableHttpClient closeableHttpClient) { | 
				
			||||||
 | 
					        super(apiKey, secretKey, closeableHttpClient); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Overloaded version of getOrderBookDepth that uses the default limit of 100 | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param symbol The symbol | 
				
			||||||
 | 
					     * @return A populated OrderBookDepth if successful, otherwise an ServiceError | 
				
			||||||
 | 
					     * @throws BinanceServiceUnreachableException In the case the service is unreachable | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public Either<ServiceError, OrderBookDepth> getOrderBookDepth(String symbol) | 
				
			||||||
 | 
					        throws BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        return getOrderBookDepth(symbol, 100); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Retrieves orderbook depth information | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param symbol The symbol | 
				
			||||||
 | 
					     * @param limit  The record limit (default: 100, maximum 1000) | 
				
			||||||
 | 
					     * @return A populated OrderBookDepth if successful, otherwise an ServiceError | 
				
			||||||
 | 
					     * @throws BinanceServiceUnreachableException In the case the service is unreachable | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public Either<ServiceError, OrderBookDepth> getOrderBookDepth(String symbol, int limit) | 
				
			||||||
 | 
					        throws BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        String url = String.format("%s%s?symbol=%s&limit=%d", BASE_ENDPOINT, ORDER_BOOK_URL, symbol, limit); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final HttpGet request = Helpers.getBuilder(url, apiKey); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try { | 
				
			||||||
 | 
					            try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) { | 
				
			||||||
 | 
					                StatusLine sl = closeableHttpResponse.getStatusLine(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                HttpEntity httpEntity = closeableHttpResponse.getEntity(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (httpEntity == null) { | 
				
			||||||
 | 
					                    log.error(NO_RESPONSE_TEXT, url); | 
				
			||||||
 | 
					                    throw new BinanceServiceUnreachableException(String.format(NO_RESPONSE_TEXT_FORMATTED, url), null); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                String response = EntityUtils.toString(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!Helpers.statusCodeIsOk(sl.getStatusCode())) { | 
				
			||||||
 | 
					                    return Either.ofLeft(mapper.readValue(response, ServiceError.class)); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return Either.ofRight(mapper.readValue(response, OrderBookDepth.class)); | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } catch (IOException e) { | 
				
			||||||
 | 
					            throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause()); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Retrieves candlestick data for the supplied symbol and interval | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param symbol   The symbol | 
				
			||||||
 | 
					     * @param interval The interval | 
				
			||||||
 | 
					     * @return A list of candlesticks if successful, otherwise an ServiceError | 
				
			||||||
 | 
					     * @throws BinanceServiceUnreachableException If the service is unreachable | 
				
			||||||
 | 
					     * @throws IllegalArgumentException           If the required arguments symbol and interval are not supplied | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public Either<ServiceError, List<Candlestick>> getCandleStickData(String symbol, Interval interval) | 
				
			||||||
 | 
					        throws BinanceServiceUnreachableException { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (symbol == null || interval == null) { | 
				
			||||||
 | 
					            throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String url = String.format( | 
				
			||||||
 | 
					            "%s%s?symbol=%s&interval=%s", | 
				
			||||||
 | 
					            BASE_ENDPOINT, | 
				
			||||||
 | 
					            CANDLESTICK_URL, | 
				
			||||||
 | 
					            symbol, | 
				
			||||||
 | 
					            interval.toString()); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return getCandleStickDataFromUrl(url); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Retrieves candlestick data for the supplied symbol and interval | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param symbol   The symbol | 
				
			||||||
 | 
					     * @param interval The interval | 
				
			||||||
 | 
					     * @param limit    The output limit | 
				
			||||||
 | 
					     * @return A list of candlesticks if successful, otherwise an ServiceError | 
				
			||||||
 | 
					     * @throws BinanceServiceUnreachableException If the service is unreachable | 
				
			||||||
 | 
					     * @throws IllegalArgumentException           If the required arguments symbol and interval are not supplied | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public Either<ServiceError, List<Candlestick>> getCandleStickData(String symbol, Interval interval, int limit) | 
				
			||||||
 | 
					        throws BinanceServiceUnreachableException { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (symbol == null || interval == null) { | 
				
			||||||
 | 
					            throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String url = String.format( | 
				
			||||||
 | 
					            "%s%s?symbol=%s&interval=%s&limit=%d", | 
				
			||||||
 | 
					            BASE_ENDPOINT, | 
				
			||||||
 | 
					            CANDLESTICK_URL, | 
				
			||||||
 | 
					            symbol, | 
				
			||||||
 | 
					            interval.toString(), | 
				
			||||||
 | 
					            limit); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return getCandleStickDataFromUrl(url); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Retrieves candlestick data for the supplied symbol and interval | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param symbol      The symbol | 
				
			||||||
 | 
					     * @param interval    The interval | 
				
			||||||
 | 
					     * @param time        The start/end time | 
				
			||||||
 | 
					     * @param isStartTime Indicates whether the time is a start or end time | 
				
			||||||
 | 
					     * @return A list of candlesticks if successful, otherwise an ServiceError | 
				
			||||||
 | 
					     * @throws BinanceServiceUnreachableException If the service is unreachable | 
				
			||||||
 | 
					     * @throws IllegalArgumentException           If the required arguments symbol and interval are not supplied | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public Either<ServiceError, List<Candlestick>> | 
				
			||||||
 | 
					    getCandleStickData(String symbol, Interval interval, long time, boolean isStartTime) | 
				
			||||||
 | 
					        throws BinanceServiceUnreachableException { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (symbol == null || interval == null) { | 
				
			||||||
 | 
					            throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String url; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (isStartTime) { | 
				
			||||||
 | 
					            url = String.format( | 
				
			||||||
 | 
					                "%s%s?symbol=%s&interval=%s&startTime=%d", | 
				
			||||||
 | 
					                BASE_ENDPOINT, | 
				
			||||||
 | 
					                CANDLESTICK_URL, | 
				
			||||||
 | 
					                symbol, | 
				
			||||||
 | 
					                interval.toString(), | 
				
			||||||
 | 
					                time); | 
				
			||||||
 | 
					        } else { | 
				
			||||||
 | 
					            url = String.format( | 
				
			||||||
 | 
					                "%s%s?symbol=%s&interval=%s&endTime=%d", | 
				
			||||||
 | 
					                BASE_ENDPOINT, | 
				
			||||||
 | 
					                CANDLESTICK_URL, | 
				
			||||||
 | 
					                symbol, | 
				
			||||||
 | 
					                interval.toString(), | 
				
			||||||
 | 
					                time); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return getCandleStickDataFromUrl(url); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Retrieves candlestick data for the supplied symbol and interval | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param symbol      The symbol | 
				
			||||||
 | 
					     * @param interval    The interval | 
				
			||||||
 | 
					     * @param limit       The output limit | 
				
			||||||
 | 
					     * @param time        The timeframe | 
				
			||||||
 | 
					     * @param isStartTime indicates whether time is a startTime (true) or endTime (false) | 
				
			||||||
 | 
					     * @return A list of candlesticks if successful, otherwise an ServiceError | 
				
			||||||
 | 
					     * @throws BinanceServiceUnreachableException If the service is unreachable | 
				
			||||||
 | 
					     * @throws IllegalArgumentException           If the required arguments symbol and interval are not supplied | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public Either<ServiceError, List<Candlestick>> | 
				
			||||||
 | 
					    getCandleStickData(String symbol, Interval interval, int limit, long time, boolean isStartTime) | 
				
			||||||
 | 
					        throws BinanceServiceUnreachableException { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (symbol == null || interval == null) { | 
				
			||||||
 | 
					            throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String url; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (isStartTime) { | 
				
			||||||
 | 
					            url = String.format( | 
				
			||||||
 | 
					                "%s%s?symbol=%s&interval=%s&limit=%d&startTime=%d", | 
				
			||||||
 | 
					                BASE_ENDPOINT, | 
				
			||||||
 | 
					                CANDLESTICK_URL, | 
				
			||||||
 | 
					                symbol, | 
				
			||||||
 | 
					                interval.toString(), | 
				
			||||||
 | 
					                limit, | 
				
			||||||
 | 
					                time); | 
				
			||||||
 | 
					        } else { | 
				
			||||||
 | 
					            url = String.format( | 
				
			||||||
 | 
					                "%s%s?symbol=%s&interval=%s&limit=%d&endTime=%d", | 
				
			||||||
 | 
					                BASE_ENDPOINT, | 
				
			||||||
 | 
					                CANDLESTICK_URL, | 
				
			||||||
 | 
					                symbol, | 
				
			||||||
 | 
					                interval.toString(), | 
				
			||||||
 | 
					                limit, | 
				
			||||||
 | 
					                time); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return getCandleStickDataFromUrl(url); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Retrieves candlestick data for the supplied symbol and interval | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param symbol    The symbol | 
				
			||||||
 | 
					     * @param interval  The interval | 
				
			||||||
 | 
					     * @param limit     The output limit | 
				
			||||||
 | 
					     * @param startTime The start timeframe | 
				
			||||||
 | 
					     * @param endTime   The end timeframe | 
				
			||||||
 | 
					     * @return A list of candlesticks if successful, otherwise an ServiceError | 
				
			||||||
 | 
					     * @throws BinanceServiceUnreachableException If the service is unreachable | 
				
			||||||
 | 
					     * @throws IllegalArgumentException           If the required arguments symbol and interval are not supplied | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public Either<ServiceError, List<Candlestick>> | 
				
			||||||
 | 
					    getCandleStickData(String symbol, Interval interval, int limit, long startTime, long endTime) | 
				
			||||||
 | 
					        throws BinanceServiceUnreachableException { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (symbol == null || interval == null) { | 
				
			||||||
 | 
					            throw new IllegalArgumentException(SYMBOL_AND_INTERVAL_MUST_BE_SUPPLIED); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String url; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        url = String.format( | 
				
			||||||
 | 
					            "%s%s?symbol=%s&interval=%s&limit=%d&startTime=%d&endTime=%d", | 
				
			||||||
 | 
					            BASE_ENDPOINT, | 
				
			||||||
 | 
					            CANDLESTICK_URL, | 
				
			||||||
 | 
					            symbol, | 
				
			||||||
 | 
					            interval.toString(), | 
				
			||||||
 | 
					            limit, | 
				
			||||||
 | 
					            startTime, | 
				
			||||||
 | 
					            endTime); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return getCandleStickDataFromUrl(url); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Retrieves the current ticker price for the specified symbol | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param symbol The symbol | 
				
			||||||
 | 
					     * @return A TickerPrice if successful, otherwise an ServiceError | 
				
			||||||
 | 
					     * @throws BinanceServiceUnreachableException If the service is unreachable | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public Either<ServiceError, TickerPrice> getTickerPriceForSymbol(String symbol) | 
				
			||||||
 | 
					        throws BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        if (symbol == null) { | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Symbol must not be null"); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        String url = String.format("%s%s?symbol=%s", BASE_ENDPOINT, TICKER_PRICE_URL, symbol); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final HttpGet request = Helpers.getBuilder(url, apiKey); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try { | 
				
			||||||
 | 
					            try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) { | 
				
			||||||
 | 
					                StatusLine sl = closeableHttpResponse.getStatusLine(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                HttpEntity httpEntity = closeableHttpResponse.getEntity(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (httpEntity == null) { | 
				
			||||||
 | 
					                    log.error(NO_RESPONSE_TEXT, url); | 
				
			||||||
 | 
					                    throw new BinanceServiceUnreachableException(String.format(NO_RESPONSE_TEXT_FORMATTED, url), null); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                String response = EntityUtils.toString(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!Helpers.statusCodeIsOk(sl.getStatusCode())) { | 
				
			||||||
 | 
					                    return Either.ofLeft(mapper.readValue(response, ServiceError.class)); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return Either.ofRight(mapper.readValue(response, TickerPrice.class)); | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } catch (IOException e) { | 
				
			||||||
 | 
					            throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause()); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Retrieves ticker prices for all supported Binance symbols. This is good to use in terms of cost if you need | 
				
			||||||
 | 
					     * more than one symbol's current ticker price. | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @return Either a list of TickerPrice objects if successful, or an ServiceError | 
				
			||||||
 | 
					     * @throws BinanceServiceUnreachableException If the service is unreachable | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public Either<ServiceError, List<TickerPrice>> getTickerPrices() throws BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        String url = String.format("%s%s", BASE_ENDPOINT, TICKER_PRICE_URL); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        final HttpGet request = Helpers.getBuilder(url, apiKey); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try { | 
				
			||||||
 | 
					            try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) { | 
				
			||||||
 | 
					                StatusLine sl = closeableHttpResponse.getStatusLine(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                HttpEntity httpEntity = closeableHttpResponse.getEntity(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (httpEntity == null) { | 
				
			||||||
 | 
					                    log.error(NO_RESPONSE_TEXT, url); | 
				
			||||||
 | 
					                    throw new BinanceServiceUnreachableException(String.format(NO_RESPONSE_TEXT_FORMATTED, url), null); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                String response = EntityUtils.toString(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!Helpers.statusCodeIsOk(sl.getStatusCode())) { | 
				
			||||||
 | 
					                    return Either.ofLeft(mapper.readValue(response, ServiceError.class)); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, TickerPrice.class); | 
				
			||||||
 | 
					                return Either.ofRight(mapper.readValue(response, type)); | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } catch (IOException e) { | 
				
			||||||
 | 
					            throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause()); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Gets candlestick data from the provided URL, allowing all of the getCandleStickData functions to act | 
				
			||||||
 | 
					     * as glorified URL builders. | 
				
			||||||
 | 
					     * | 
				
			||||||
 | 
					     * @param url The URL to use | 
				
			||||||
 | 
					     * @return A list of candlesticks if successful, otherwise an ServiceError | 
				
			||||||
 | 
					     * @throws BinanceServiceUnreachableException If the service is unreachable | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    private Either<ServiceError, List<Candlestick>> | 
				
			||||||
 | 
					    getCandleStickDataFromUrl(String url) throws BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        final HttpGet request = Helpers.getBuilder(url, apiKey); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try { | 
				
			||||||
 | 
					            try (CloseableHttpResponse closeableHttpResponse = closeableHttpClient.execute(request)) { | 
				
			||||||
 | 
					                StatusLine sl = closeableHttpResponse.getStatusLine(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                HttpEntity httpEntity = closeableHttpResponse.getEntity(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (httpEntity == null) { | 
				
			||||||
 | 
					                    log.error(NO_RESPONSE_TEXT, url); | 
				
			||||||
 | 
					                    throw new BinanceServiceUnreachableException(String.format(NO_RESPONSE_TEXT_FORMATTED, url), null); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                String response = EntityUtils.toString(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!Helpers.statusCodeIsOk(sl.getStatusCode())) { | 
				
			||||||
 | 
					                    return Either.ofLeft(mapper.readValue(response, ServiceError.class)); | 
				
			||||||
 | 
					                } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, Candlestick.class); | 
				
			||||||
 | 
					                return Either.ofRight(mapper.readValue(response, type)); | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } catch (IOException e) { | 
				
			||||||
 | 
					            throw new BinanceServiceUnreachableException(e.getMessage(), e.getCause()); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,43 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.annotation.JsonDeserialize; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.transform.CandlestickDeserializer; | 
				
			||||||
 | 
					import lombok.AllArgsConstructor; | 
				
			||||||
 | 
					import lombok.Builder; | 
				
			||||||
 | 
					import lombok.Data; | 
				
			||||||
 | 
					import lombok.NoArgsConstructor; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.math.BigDecimal; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * Represents a single candlestick/kline. No JsonProperty information is stored because we use a | 
				
			||||||
 | 
					 * custom serializer to clean up the data that's given to us from the endpoint. | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					@Data | 
				
			||||||
 | 
					@Builder | 
				
			||||||
 | 
					@AllArgsConstructor | 
				
			||||||
 | 
					@NoArgsConstructor | 
				
			||||||
 | 
					@JsonDeserialize(using = CandlestickDeserializer.class) | 
				
			||||||
 | 
					public class Candlestick { | 
				
			||||||
 | 
					   private long openTime; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   private BigDecimal open; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   private BigDecimal high; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   private BigDecimal low; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   private BigDecimal close; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   private BigDecimal volume; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   private long closeTime; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   private BigDecimal quoteAssetVolume; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   private long numberOfTrades; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   private BigDecimal takerBuyBaseAssetVolume; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   private BigDecimal takerBuyQuoteAssetVolume; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,7 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * The documents do not make it clear what is in this object so it is left blank. | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					public class ExchangeFilter { | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,37 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonProperty; | 
				
			||||||
 | 
					import lombok.AllArgsConstructor; | 
				
			||||||
 | 
					import lombok.Builder; | 
				
			||||||
 | 
					import lombok.Data; | 
				
			||||||
 | 
					import lombok.NoArgsConstructor; | 
				
			||||||
 | 
					import lombok.NonNull; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.HashSet; | 
				
			||||||
 | 
					import java.util.List; | 
				
			||||||
 | 
					import java.util.Set; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Data | 
				
			||||||
 | 
					@Builder | 
				
			||||||
 | 
					@AllArgsConstructor | 
				
			||||||
 | 
					@NoArgsConstructor | 
				
			||||||
 | 
					public class ExchangeInfo { | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("timezone") | 
				
			||||||
 | 
					    private String timezone; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @JsonProperty("serverTime") | 
				
			||||||
 | 
					    private Long serverTime; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("rateLimits") | 
				
			||||||
 | 
					    private Set<RateLimit> rateLimits; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("exchangeFilters") | 
				
			||||||
 | 
					    private Set<ExchangeFilter> exchangeFilters; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("symbols") | 
				
			||||||
 | 
					    private List<Symbol> symbols; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,34 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public enum Interval { | 
				
			||||||
 | 
					    ONE_MINUTE("1m"), | 
				
			||||||
 | 
					    THREE_MINUTES("3m"), | 
				
			||||||
 | 
					    FIVE_MINUTES("5m"), | 
				
			||||||
 | 
					    FIFTEEN_MINUTES("15m"), | 
				
			||||||
 | 
					    THIRTY_MINUTES("30m"), | 
				
			||||||
 | 
					    ONE_HOUR("1h"), | 
				
			||||||
 | 
					    TWO_HOURS("2h"), | 
				
			||||||
 | 
					    FOUR_HOURS("4h"), | 
				
			||||||
 | 
					    SIX_HOURS("6h"), | 
				
			||||||
 | 
					    EIGHT_HOURS("8h"), | 
				
			||||||
 | 
					    TWELVE_HOURS("12h"), | 
				
			||||||
 | 
					    ONE_DAY("1d"), | 
				
			||||||
 | 
					    THREE_DAYS("3d"), | 
				
			||||||
 | 
					    ONE_WEEK("1w"), | 
				
			||||||
 | 
					    ONE_MONTH("1M"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final String representation; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Constructor | 
				
			||||||
 | 
					     * @param representation The string representation of the time interval to construct | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    Interval(String representation) { | 
				
			||||||
 | 
					        this.representation = representation; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public String toString() { | 
				
			||||||
 | 
					        return representation; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,30 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonProperty; | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonTypeName; | 
				
			||||||
 | 
					import lombok.AllArgsConstructor; | 
				
			||||||
 | 
					import lombok.Builder; | 
				
			||||||
 | 
					import lombok.Data; | 
				
			||||||
 | 
					import lombok.EqualsAndHashCode; | 
				
			||||||
 | 
					import lombok.NoArgsConstructor; | 
				
			||||||
 | 
					import lombok.NonNull; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Data | 
				
			||||||
 | 
					@Builder | 
				
			||||||
 | 
					@AllArgsConstructor | 
				
			||||||
 | 
					@NoArgsConstructor | 
				
			||||||
 | 
					@EqualsAndHashCode(callSuper = false) | 
				
			||||||
 | 
					@JsonTypeName("LOT_SIZE") | 
				
			||||||
 | 
					public class LotSizeFilter extends SymbolFilter { | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("minQty") | 
				
			||||||
 | 
					    private String minQty; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("maxQty") | 
				
			||||||
 | 
					    private String maxQty; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("stepSize") | 
				
			||||||
 | 
					    private String stepSize; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,22 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonProperty; | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonTypeName; | 
				
			||||||
 | 
					import lombok.AllArgsConstructor; | 
				
			||||||
 | 
					import lombok.Builder; | 
				
			||||||
 | 
					import lombok.Data; | 
				
			||||||
 | 
					import lombok.EqualsAndHashCode; | 
				
			||||||
 | 
					import lombok.NoArgsConstructor; | 
				
			||||||
 | 
					import lombok.NonNull; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Data | 
				
			||||||
 | 
					@Builder | 
				
			||||||
 | 
					@AllArgsConstructor | 
				
			||||||
 | 
					@NoArgsConstructor | 
				
			||||||
 | 
					@JsonTypeName("MIN_NOTIONAL") | 
				
			||||||
 | 
					@EqualsAndHashCode(callSuper = false) | 
				
			||||||
 | 
					public class MinNotionalFilter extends SymbolFilter { | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("minNotional") | 
				
			||||||
 | 
					    private String minNotional; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,29 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.annotation.JsonDeserialize; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.transform.OrderBookDepthResponseDeserializer; | 
				
			||||||
 | 
					import lombok.AllArgsConstructor; | 
				
			||||||
 | 
					import lombok.Builder; | 
				
			||||||
 | 
					import lombok.Data; | 
				
			||||||
 | 
					import lombok.NoArgsConstructor; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * Represents the Order book depth for Binance's market. | 
				
			||||||
 | 
					 * | 
				
			||||||
 | 
					 * Order book depth is a mess coming from Binance's API, so this is handled purely with | 
				
			||||||
 | 
					 * a custom deserializer to make using the resulting POJOs easier | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					@Data | 
				
			||||||
 | 
					@Builder | 
				
			||||||
 | 
					@AllArgsConstructor | 
				
			||||||
 | 
					@NoArgsConstructor | 
				
			||||||
 | 
					@JsonDeserialize(using = OrderBookDepthResponseDeserializer.class) | 
				
			||||||
 | 
					public class OrderBookDepth { | 
				
			||||||
 | 
					    private Long lastUpdateId; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private List<OrderBookPricing> bids; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private List<OrderBookPricing> asks; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,18 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.AllArgsConstructor; | 
				
			||||||
 | 
					import lombok.Builder; | 
				
			||||||
 | 
					import lombok.Data; | 
				
			||||||
 | 
					import lombok.NoArgsConstructor; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.math.BigDecimal; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Data | 
				
			||||||
 | 
					@Builder | 
				
			||||||
 | 
					@AllArgsConstructor | 
				
			||||||
 | 
					@NoArgsConstructor | 
				
			||||||
 | 
					public class OrderBookPricing { | 
				
			||||||
 | 
					    private BigDecimal price; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private BigDecimal quantity; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,11 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public enum OrderType { | 
				
			||||||
 | 
					    LIMIT, | 
				
			||||||
 | 
					    MARKET, | 
				
			||||||
 | 
					    STOP_LOSS, | 
				
			||||||
 | 
					    STOP_LOSS_LIMIT, | 
				
			||||||
 | 
					    TAKE_PROFIT, | 
				
			||||||
 | 
					    TAKE_PROFIT_LIMIT, | 
				
			||||||
 | 
					    LIMIT_MAKER | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,7 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * Ping doesn't return anything, but we need to have this object to keep the API consistent. | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					public class Ping { | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,30 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonProperty; | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonTypeName; | 
				
			||||||
 | 
					import lombok.AllArgsConstructor; | 
				
			||||||
 | 
					import lombok.Builder; | 
				
			||||||
 | 
					import lombok.Data; | 
				
			||||||
 | 
					import lombok.EqualsAndHashCode; | 
				
			||||||
 | 
					import lombok.NoArgsConstructor; | 
				
			||||||
 | 
					import lombok.NonNull; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Data | 
				
			||||||
 | 
					@Builder | 
				
			||||||
 | 
					@AllArgsConstructor | 
				
			||||||
 | 
					@NoArgsConstructor | 
				
			||||||
 | 
					@JsonTypeName("PRICE_FILTER") | 
				
			||||||
 | 
					@EqualsAndHashCode(callSuper = false) | 
				
			||||||
 | 
					public class PriceFilter extends SymbolFilter { | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("minPrice") | 
				
			||||||
 | 
					    private String minPrice; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("maxPrice") | 
				
			||||||
 | 
					    private String maxPrice; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("tickSize") | 
				
			||||||
 | 
					    private String tickSize; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,23 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonProperty; | 
				
			||||||
 | 
					import lombok.AllArgsConstructor; | 
				
			||||||
 | 
					import lombok.Data; | 
				
			||||||
 | 
					import lombok.NoArgsConstructor; | 
				
			||||||
 | 
					import lombok.NonNull; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Data | 
				
			||||||
 | 
					@AllArgsConstructor | 
				
			||||||
 | 
					@NoArgsConstructor | 
				
			||||||
 | 
					public class RateLimit { | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("rateLimitType") | 
				
			||||||
 | 
					    private RateLimitType rateLimitType; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("interval") | 
				
			||||||
 | 
					    private RateLimitInterval rateLimitInterval; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @JsonProperty("limit") | 
				
			||||||
 | 
					    private Long limit; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,7 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public enum RateLimitInterval { | 
				
			||||||
 | 
					    SECOND, | 
				
			||||||
 | 
					    MINUTE, | 
				
			||||||
 | 
					    DAY | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,6 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public enum RateLimitType { | 
				
			||||||
 | 
					    REQUESTS, | 
				
			||||||
 | 
					    ORDERS | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,21 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonProperty; | 
				
			||||||
 | 
					import lombok.AllArgsConstructor; | 
				
			||||||
 | 
					import lombok.Builder; | 
				
			||||||
 | 
					import lombok.Data; | 
				
			||||||
 | 
					import lombok.NoArgsConstructor; | 
				
			||||||
 | 
					import lombok.NonNull; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Data | 
				
			||||||
 | 
					@Builder | 
				
			||||||
 | 
					@AllArgsConstructor | 
				
			||||||
 | 
					@NoArgsConstructor | 
				
			||||||
 | 
					public class ServiceError { | 
				
			||||||
 | 
					    @JsonProperty("code") | 
				
			||||||
 | 
					    public int code; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("msg") | 
				
			||||||
 | 
					    public String message; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,51 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonProperty; | 
				
			||||||
 | 
					import lombok.AllArgsConstructor; | 
				
			||||||
 | 
					import lombok.Builder; | 
				
			||||||
 | 
					import lombok.Data; | 
				
			||||||
 | 
					import lombok.NoArgsConstructor; | 
				
			||||||
 | 
					import lombok.NonNull; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.HashSet; | 
				
			||||||
 | 
					import java.util.List; | 
				
			||||||
 | 
					import java.util.Set; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Data | 
				
			||||||
 | 
					@Builder | 
				
			||||||
 | 
					@AllArgsConstructor | 
				
			||||||
 | 
					@NoArgsConstructor | 
				
			||||||
 | 
					public class Symbol { | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("symbol") | 
				
			||||||
 | 
					    private String ticker; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("status") | 
				
			||||||
 | 
					    private SymbolStatus symbolStatus; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("baseAsset") | 
				
			||||||
 | 
					    private String baseAsset; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @JsonProperty("baseAssetPrecision") | 
				
			||||||
 | 
					    private int baseAssetPrecision; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("quoteAsset") | 
				
			||||||
 | 
					    private String quoteAsset; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @JsonProperty("quotePrecision") | 
				
			||||||
 | 
					    private int quotePrecision; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("orderTypes") | 
				
			||||||
 | 
					    private Set<OrderType> orderTypes; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @JsonProperty("icebergAllowed") | 
				
			||||||
 | 
					    private boolean icebergAllowed; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("filters") | 
				
			||||||
 | 
					    private List<SymbolFilter> symbolFilters; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,22 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonProperty; | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonSubTypes; | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonTypeInfo; | 
				
			||||||
 | 
					import lombok.NonNull; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The "property" here refers to an identifying feature of the JSON that will be used to
 | 
				
			||||||
 | 
					// match to a @JsonTypeName
 | 
				
			||||||
 | 
					@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "filterType") | 
				
			||||||
 | 
					@JsonSubTypes({ | 
				
			||||||
 | 
					    @JsonSubTypes.Type(value = PriceFilter.class, name = "PRICE_FILTER"), | 
				
			||||||
 | 
					    @JsonSubTypes.Type(value = LotSizeFilter.class, name = "LOT_SIZE"), | 
				
			||||||
 | 
					    @JsonSubTypes.Type(value = MinNotionalFilter.class, name = "MIN_NOTIONAL") | 
				
			||||||
 | 
					}) | 
				
			||||||
 | 
					public abstract class SymbolFilter { | 
				
			||||||
 | 
					    // This seems like an enum, but the entire space of values is not enumerated
 | 
				
			||||||
 | 
					    // so it is left as a string for now
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("filterType") | 
				
			||||||
 | 
					    private String filterType; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,11 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public enum SymbolStatus { | 
				
			||||||
 | 
					    PRE_TRADING, | 
				
			||||||
 | 
					    TRADING, | 
				
			||||||
 | 
					    POST_TRADING, | 
				
			||||||
 | 
					    END_OF_DAY, | 
				
			||||||
 | 
					    HALT, | 
				
			||||||
 | 
					    AUCTION_MATCH, | 
				
			||||||
 | 
					    BREAK | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,24 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonProperty; | 
				
			||||||
 | 
					import lombok.AllArgsConstructor; | 
				
			||||||
 | 
					import lombok.Builder; | 
				
			||||||
 | 
					import lombok.Data; | 
				
			||||||
 | 
					import lombok.NoArgsConstructor; | 
				
			||||||
 | 
					import lombok.NonNull; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.math.BigDecimal; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Data | 
				
			||||||
 | 
					@Builder | 
				
			||||||
 | 
					@AllArgsConstructor | 
				
			||||||
 | 
					@NoArgsConstructor | 
				
			||||||
 | 
					public class TickerPrice { | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("symbol") | 
				
			||||||
 | 
					    private String symbol; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @NonNull | 
				
			||||||
 | 
					    @JsonProperty("price") | 
				
			||||||
 | 
					    private BigDecimal price; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,16 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.annotation.JsonProperty; | 
				
			||||||
 | 
					import lombok.AllArgsConstructor; | 
				
			||||||
 | 
					import lombok.Builder; | 
				
			||||||
 | 
					import lombok.Data; | 
				
			||||||
 | 
					import lombok.NoArgsConstructor; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Data | 
				
			||||||
 | 
					@Builder | 
				
			||||||
 | 
					@AllArgsConstructor | 
				
			||||||
 | 
					@NoArgsConstructor | 
				
			||||||
 | 
					public class Time { | 
				
			||||||
 | 
					    @JsonProperty("serverTime") | 
				
			||||||
 | 
					    private long serverTime; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,46 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities.transform; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.core.JsonParser; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.DeserializationContext; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.JsonNode; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.deser.std.StdDeserializer; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.Candlestick; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException; | 
				
			||||||
 | 
					import java.math.BigDecimal; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * A custom deserializer for OrderBookDepth. Binance returns some non-standard looking data | 
				
			||||||
 | 
					 * in it's array, so we convert the entire thing into our own representation here to make | 
				
			||||||
 | 
					 * it easier to use. | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					public class CandlestickDeserializer extends StdDeserializer<Candlestick> { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public CandlestickDeserializer() { | 
				
			||||||
 | 
					        this(null); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public CandlestickDeserializer(Class<?> vc) { | 
				
			||||||
 | 
					        super(vc); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public Candlestick deserialize(JsonParser jp, DeserializationContext context) throws IOException { | 
				
			||||||
 | 
					        JsonNode n = jp.getCodec().readTree(jp); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Candlestick | 
				
			||||||
 | 
					                    .builder() | 
				
			||||||
 | 
					                    .openTime(n.get(0).asLong()) | 
				
			||||||
 | 
					                    .open(new BigDecimal(n.get(1).asText())) | 
				
			||||||
 | 
					                    .high(new BigDecimal(n.get(2).asText())) | 
				
			||||||
 | 
					                    .low(new BigDecimal(n.get(3).asText())) | 
				
			||||||
 | 
					                    .close(new BigDecimal(n.get(4).asText())) | 
				
			||||||
 | 
					                    .volume(new BigDecimal(n.get(5).asText())) | 
				
			||||||
 | 
					                    .closeTime(n.get(6).asLong()) | 
				
			||||||
 | 
					                    .quoteAssetVolume(new BigDecimal(n.get(7).asText())) | 
				
			||||||
 | 
					                    .numberOfTrades(n.get(8).asLong()) | 
				
			||||||
 | 
					                    .takerBuyBaseAssetVolume(new BigDecimal(n.get(9).asText())) | 
				
			||||||
 | 
					                    .takerBuyQuoteAssetVolume(new BigDecimal(n.get(10).asText())) | 
				
			||||||
 | 
					                    .build(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,66 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.entities.transform; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.fasterxml.jackson.core.JsonParser; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.DeserializationContext; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.JsonNode; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.deser.std.StdDeserializer; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.OrderBookDepth; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.OrderBookPricing; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException; | 
				
			||||||
 | 
					import java.math.BigDecimal; | 
				
			||||||
 | 
					import java.util.ArrayList; | 
				
			||||||
 | 
					import java.util.List; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** | 
				
			||||||
 | 
					 * A custom deserializer for OrderBookDepth. Binance returns some non-standard looking data | 
				
			||||||
 | 
					 * in it's array, so we convert the entire thing into our own representation here to make | 
				
			||||||
 | 
					 * it easier to use. | 
				
			||||||
 | 
					 */ | 
				
			||||||
 | 
					public class OrderBookDepthResponseDeserializer extends StdDeserializer<OrderBookDepth> { | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public OrderBookDepthResponseDeserializer() { | 
				
			||||||
 | 
					        this(null); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public OrderBookDepthResponseDeserializer(Class<?> vc) { | 
				
			||||||
 | 
					        super(vc); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override | 
				
			||||||
 | 
					    public OrderBookDepth deserialize(JsonParser jp, DeserializationContext context) throws IOException { | 
				
			||||||
 | 
					        JsonNode node = jp.getCodec().readTree(jp); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Long lastUpdateId = node.get("lastUpdateId").asLong(); | 
				
			||||||
 | 
					        List<OrderBookPricing> bids = new ArrayList<>(); | 
				
			||||||
 | 
					        List<OrderBookPricing> asks = new ArrayList<>(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // NOTE: Jackson nodes don't seem to support conversion to streams
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (JsonNode n : node.get("bids")) { | 
				
			||||||
 | 
					            bids.add(OrderBookPricing | 
				
			||||||
 | 
					                .builder() | 
				
			||||||
 | 
					                .price(new BigDecimal(n.get(0).asText())) | 
				
			||||||
 | 
					                .quantity(new BigDecimal(n.get(1).asText())) | 
				
			||||||
 | 
					                .build() | 
				
			||||||
 | 
					            ); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (JsonNode n : node.get("asks")) { | 
				
			||||||
 | 
					            asks.add(OrderBookPricing | 
				
			||||||
 | 
					                .builder() | 
				
			||||||
 | 
					                .price(new BigDecimal(n.get(0).asText())) | 
				
			||||||
 | 
					                .quantity(new BigDecimal(n.get(1).asText())) | 
				
			||||||
 | 
					                .build() | 
				
			||||||
 | 
					            ); | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return OrderBookDepth | 
				
			||||||
 | 
					            .builder() | 
				
			||||||
 | 
					            .lastUpdateId(lastUpdateId) | 
				
			||||||
 | 
					            .bids(bids) | 
				
			||||||
 | 
					            .asks(asks) | 
				
			||||||
 | 
					            .build(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,12 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.exceptions; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class BinanceServiceException extends Exception { | 
				
			||||||
 | 
					    public BinanceServiceException() { super(); } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public BinanceServiceException(String message) { super(message); } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public BinanceServiceException(String message, Throwable cause) { super(message, cause); } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public BinanceServiceException(Throwable cause) { super(cause); } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,11 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.exceptions; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.AllArgsConstructor; | 
				
			||||||
 | 
					import lombok.Getter; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Getter | 
				
			||||||
 | 
					@AllArgsConstructor | 
				
			||||||
 | 
					public class BinanceServiceUnreachableException extends BinanceServiceException { | 
				
			||||||
 | 
					    public String message; | 
				
			||||||
 | 
					    public Throwable cause; | 
				
			||||||
 | 
					} | 
				
			||||||
@ -1,12 +0,0 @@ | 
				
			|||||||
/* | 
					 | 
				
			||||||
 * This Java source file was generated by the Gradle 'init' task. | 
					 | 
				
			||||||
 */ | 
					 | 
				
			||||||
import org.junit.Test; | 
					 | 
				
			||||||
import static org.junit.Assert.*; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class LibraryTest { | 
					 | 
				
			||||||
    @Test public void testSomeLibraryMethod() { | 
					 | 
				
			||||||
        Library classUnderTest = new Library(); | 
					 | 
				
			||||||
        assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod()); | 
					 | 
				
			||||||
    } | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
@ -0,0 +1,289 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.codepoetics.ambivalence.Either; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.JavaType; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.ObjectMapper; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.Candlestick; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.ServiceError; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.Interval; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.matchers.GetMatcher; | 
				
			||||||
 | 
					import org.apache.commons.io.FileUtils; | 
				
			||||||
 | 
					import org.apache.http.ProtocolVersion; | 
				
			||||||
 | 
					import org.apache.http.client.methods.CloseableHttpResponse; | 
				
			||||||
 | 
					import org.apache.http.entity.BasicHttpEntity; | 
				
			||||||
 | 
					import org.apache.http.impl.client.CloseableHttpClient; | 
				
			||||||
 | 
					import org.apache.http.message.BasicStatusLine; | 
				
			||||||
 | 
					import org.junit.Before; | 
				
			||||||
 | 
					import org.junit.Test; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream; | 
				
			||||||
 | 
					import java.io.File; | 
				
			||||||
 | 
					import java.io.IOException; | 
				
			||||||
 | 
					import java.math.BigDecimal; | 
				
			||||||
 | 
					import java.nio.charset.StandardCharsets; | 
				
			||||||
 | 
					import java.util.List; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT; | 
				
			||||||
 | 
					import static org.junit.Assert.assertEquals; | 
				
			||||||
 | 
					import static org.junit.Assert.assertTrue; | 
				
			||||||
 | 
					import static org.mockito.ArgumentMatchers.argThat; | 
				
			||||||
 | 
					import static org.mockito.Mockito.mock; | 
				
			||||||
 | 
					import static org.mockito.Mockito.when; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class CandlestickMethodTests { | 
				
			||||||
 | 
					    private static ObjectMapper mapper = Helpers.objectMapperBuilder(); | 
				
			||||||
 | 
					    private final String url = String.format("%s%s", BASE_ENDPOINT, "/api/v1/klines?symbol=TEST&interval=8h"); | 
				
			||||||
 | 
					    private final String urlWithLimit = String.format("%s%s", url, "&limit=1000"); | 
				
			||||||
 | 
					    private final String urlWithStartTime = String.format("%s%s", url, "&startTime=1234"); | 
				
			||||||
 | 
					    private final String urlWithEndTime = String.format("%s%s", url, "&endTime=1234"); | 
				
			||||||
 | 
					    private final String urlWithLimitAndStartTime = String.format("%s%s", urlWithLimit, "&startTime=1234"); | 
				
			||||||
 | 
					    private final String urlWithLimitAndEndTime = String.format("%s%s", urlWithLimit, "&endTime=1234"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private List<Candlestick> candlesticks; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // This will just make mocking easier on us...
 | 
				
			||||||
 | 
					    private String data; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Before | 
				
			||||||
 | 
					    public void setUp() throws IOException { | 
				
			||||||
 | 
					        ClassLoader classLoader = ExchangeInfoMethodTests.class.getClassLoader(); | 
				
			||||||
 | 
					        File file = new File(classLoader.getResource("candlestick_response_sample.json").getFile()); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, Candlestick.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = FileUtils.readFileToString(file, "UTF-8"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        candlesticks = mapper.readValue(data, type); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * testFieldsAreCorrect is a test method to insure field alignment. Since a custom deserializer | 
				
			||||||
 | 
					     * is used to convert an array into a Candlestick object, it's important that field alignment | 
				
			||||||
 | 
					     * is checked so numbers are guaranteed. | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void testFieldsAreCorrect() { | 
				
			||||||
 | 
					        Candlestick c = candlesticks.get(0); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertEquals(1499040000000L, c.getOpenTime()); | 
				
			||||||
 | 
					        assertEquals(new BigDecimal("0.01634790"), c.getOpen()); | 
				
			||||||
 | 
					        assertEquals(new BigDecimal("0.80000000"), c.getHigh()); | 
				
			||||||
 | 
					        assertEquals(new BigDecimal("0.01575800"), c.getLow()); | 
				
			||||||
 | 
					        assertEquals(new BigDecimal("0.01577100"), c.getClose()); | 
				
			||||||
 | 
					        assertEquals(new BigDecimal("148976.11427815"), c.getVolume()); | 
				
			||||||
 | 
					        assertEquals(1499644799999L, c.getCloseTime()); | 
				
			||||||
 | 
					        assertEquals(new BigDecimal("2434.19055334"), c.getQuoteAssetVolume()); | 
				
			||||||
 | 
					        assertEquals(308L, c.getNumberOfTrades()); | 
				
			||||||
 | 
					        assertEquals(new BigDecimal("1756.87402397"), c.getTakerBuyBaseAssetVolume()); | 
				
			||||||
 | 
					        assertEquals(new BigDecimal("28.46694368"), c.getTakerBuyQuoteAssetVolume()); | 
				
			||||||
 | 
					        // There's a field marked ignore after TakerBuyQuoteAssetVolume...
 | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void testCandlestickServiceReturnsSuccessfully() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, List<Candlestick>> res = | 
				
			||||||
 | 
					            marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isRight()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.right()), candlesticks); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void testCandlestickServiceWithLimitReturnsSuccessfully() | 
				
			||||||
 | 
					        throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(urlWithLimit, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, List<Candlestick>> res = | 
				
			||||||
 | 
					            marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 1000); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isRight()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.right()), candlesticks); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void testCandlestickServiceWithLimitAndStartTimeReturnsSuccessfully() | 
				
			||||||
 | 
					        throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(urlWithLimitAndStartTime, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, List<Candlestick>> res = | 
				
			||||||
 | 
					            marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 1000, 1234L,true); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isRight()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.right()), candlesticks); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void testCandlestickServiceWithLimitAndEndTimeReturnsSuccessfully() | 
				
			||||||
 | 
					        throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(urlWithLimitAndEndTime, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, List<Candlestick>> res = | 
				
			||||||
 | 
					            marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 1000, 1234L,false); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isRight()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.right()), candlesticks); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void testCandlestickServiceWithStartTimeReturnsSuccessfully() | 
				
			||||||
 | 
					        throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(urlWithStartTime, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, List<Candlestick>> res = | 
				
			||||||
 | 
					            marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 1234L,true); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isRight()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.right()), candlesticks); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void testCandlestickServiceWithEndTimeReturnsSuccessfully() | 
				
			||||||
 | 
					        throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(urlWithEndTime, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, List<Candlestick>> res = | 
				
			||||||
 | 
					            marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS, 1234L,false); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isRight()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.right()), candlesticks); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BinanceServiceUnreachableException.class) | 
				
			||||||
 | 
					    public void testCandlestickServiceWithoutHttpEntityThrows() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void TestCandlestickServiceBadApiKeyReturns400() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                400, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ServiceError serviceError = ServiceError.builder().code(400).message("Bad API Key").build(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent( | 
				
			||||||
 | 
					            new ByteArrayInputStream(mapper.writeValueAsString(serviceError).getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, List<Candlestick>> res = | 
				
			||||||
 | 
					            marketData.getCandleStickData("TEST", Interval.EIGHT_HOURS); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isLeft()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.left()), serviceError); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,120 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.codepoetics.ambivalence.Either; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.ObjectMapper; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.ExchangeInfo; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.ServiceError; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.matchers.GetMatcher; | 
				
			||||||
 | 
					import org.apache.commons.io.FileUtils; | 
				
			||||||
 | 
					import org.apache.http.ProtocolVersion; | 
				
			||||||
 | 
					import org.apache.http.client.methods.CloseableHttpResponse; | 
				
			||||||
 | 
					import org.apache.http.entity.BasicHttpEntity; | 
				
			||||||
 | 
					import org.apache.http.impl.client.CloseableHttpClient; | 
				
			||||||
 | 
					import org.apache.http.message.BasicStatusLine; | 
				
			||||||
 | 
					import org.junit.Before; | 
				
			||||||
 | 
					import org.junit.Test; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream; | 
				
			||||||
 | 
					import java.io.File; | 
				
			||||||
 | 
					import java.io.IOException; | 
				
			||||||
 | 
					import java.nio.charset.StandardCharsets; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT; | 
				
			||||||
 | 
					import static org.junit.Assert.assertEquals; | 
				
			||||||
 | 
					import static org.junit.Assert.assertTrue; | 
				
			||||||
 | 
					import static org.mockito.ArgumentMatchers.argThat; | 
				
			||||||
 | 
					import static org.mockito.Mockito.mock; | 
				
			||||||
 | 
					import static org.mockito.Mockito.when; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ExchangeInfoMethodTests { | 
				
			||||||
 | 
					    private static ObjectMapper mapper = Helpers.objectMapperBuilder(); | 
				
			||||||
 | 
					    private String url = String.format("%s%s", BASE_ENDPOINT, "/api/v1/exchangeInfo"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ExchangeInfo exchangeInfo; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Before | 
				
			||||||
 | 
					    public void setUp() throws IOException { | 
				
			||||||
 | 
					        ClassLoader classLoader = ExchangeInfoMethodTests.class.getClassLoader(); | 
				
			||||||
 | 
					        File file = new File(classLoader.getResource("exchange_info_response_sample.json").getFile()); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        exchangeInfo = | 
				
			||||||
 | 
					            mapper.readValue(FileUtils.readFileToString(file, "UTF-8"), ExchangeInfo.class); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void TestExchangeInfoServiceReturnsSuccessfully() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent(new ByteArrayInputStream( | 
				
			||||||
 | 
					            mapper.writeValueAsString(exchangeInfo).getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        GeneralUtilities generalUtilities = | 
				
			||||||
 | 
					            new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, ExchangeInfo> res = generalUtilities.getExchangeInfo(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isRight()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.right()), exchangeInfo); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BinanceServiceUnreachableException.class) | 
				
			||||||
 | 
					    public void TestExchangeInfoServiceWithNoHttpEntityFails() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        GeneralUtilities generalUtilities = | 
				
			||||||
 | 
					            new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        generalUtilities.getExchangeInfo(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void TestExchangeInfoServiceBadApiKeyReturns400() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                400, "Bad API Key")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ServiceError serviceError = ServiceError.builder().code(400).message("Bad API Key").build(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent( | 
				
			||||||
 | 
					            new ByteArrayInputStream(mapper.writeValueAsString(serviceError).getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        GeneralUtilities generalUtilities = | 
				
			||||||
 | 
					            new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, ExchangeInfo> res = generalUtilities.getExchangeInfo(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isLeft()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.left()), serviceError); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,124 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.codepoetics.ambivalence.Either; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.ObjectMapper; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.OrderBookDepth; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.ServiceError; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.matchers.GetMatcher; | 
				
			||||||
 | 
					import org.apache.commons.io.FileUtils; | 
				
			||||||
 | 
					import org.apache.http.ProtocolVersion; | 
				
			||||||
 | 
					import org.apache.http.client.methods.CloseableHttpResponse; | 
				
			||||||
 | 
					import org.apache.http.entity.BasicHttpEntity; | 
				
			||||||
 | 
					import org.apache.http.impl.client.CloseableHttpClient; | 
				
			||||||
 | 
					import org.apache.http.message.BasicStatusLine; | 
				
			||||||
 | 
					import org.junit.Before; | 
				
			||||||
 | 
					import org.junit.Test; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream; | 
				
			||||||
 | 
					import java.io.File; | 
				
			||||||
 | 
					import java.io.IOException; | 
				
			||||||
 | 
					import java.nio.charset.StandardCharsets; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT; | 
				
			||||||
 | 
					import static org.junit.Assert.assertEquals; | 
				
			||||||
 | 
					import static org.junit.Assert.assertTrue; | 
				
			||||||
 | 
					import static org.mockito.ArgumentMatchers.argThat; | 
				
			||||||
 | 
					import static org.mockito.Mockito.mock; | 
				
			||||||
 | 
					import static org.mockito.Mockito.when; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class OrderBookDepthTests { | 
				
			||||||
 | 
					    private static ObjectMapper mapper = Helpers.objectMapperBuilder(); | 
				
			||||||
 | 
					    private final String url = String.format("%s%s", BASE_ENDPOINT, "/api/v1/depth?symbol=ETHBTC&limit=100"); | 
				
			||||||
 | 
					    private final String urlWithLimit = String.format("%s%s", BASE_ENDPOINT, "/api/v1/depth?symbol=ETHBTC&limit=1000"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private OrderBookDepth orderBookDepth; | 
				
			||||||
 | 
					    private String orderBookDepthJson; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Before | 
				
			||||||
 | 
					    public void setUp() throws IOException { | 
				
			||||||
 | 
					        ClassLoader classLoader = ExchangeInfoMethodTests.class.getClassLoader(); | 
				
			||||||
 | 
					        File file = new File(classLoader.getResource("orderbook_depth_sample.json").getFile()); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        orderBookDepthJson = FileUtils.readFileToString(file, "UTF-8"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        orderBookDepth = | 
				
			||||||
 | 
					            mapper.readValue(orderBookDepthJson, OrderBookDepth.class); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void TestOrderBookDepthReturnsSuccessfully() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // This changes from the usual way we do this because we do post-processing in the deserializer and the JSON
 | 
				
			||||||
 | 
					        // will no longer match the stringified OrderBookDepth.
 | 
				
			||||||
 | 
					        httpEntity.setContent(new ByteArrayInputStream(orderBookDepthJson.getBytes())); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, OrderBookDepth> res = marketData.getOrderBookDepth("ETHBTC"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isRight()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.right()), orderBookDepth); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BinanceServiceUnreachableException.class) | 
				
			||||||
 | 
					    public void TestOrderBookDepthWithNoHttpEntityFails() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(urlWithLimit, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        marketData.getOrderBookDepth("ETHBTC", 1000); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void TestOrderBookDepthWithBadApiKeyReturns400() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                400, "Bad API Key")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(urlWithLimit, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ServiceError serviceError = ServiceError.builder().code(400).message("Bad API Key").build(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent(new ByteArrayInputStream( | 
				
			||||||
 | 
					            mapper.writeValueAsString(serviceError).getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, OrderBookDepth> res = marketData.getOrderBookDepth("ETHBTC", 1000); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isLeft()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.left()), serviceError); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,105 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.codepoetics.ambivalence.Either; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.ObjectMapper; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.ServiceError; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.Ping; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.matchers.GetMatcher; | 
				
			||||||
 | 
					import org.apache.http.ProtocolVersion; | 
				
			||||||
 | 
					import org.apache.http.client.methods.CloseableHttpResponse; | 
				
			||||||
 | 
					import org.apache.http.entity.BasicHttpEntity; | 
				
			||||||
 | 
					import org.apache.http.impl.client.CloseableHttpClient; | 
				
			||||||
 | 
					import org.apache.http.message.BasicStatusLine; | 
				
			||||||
 | 
					import org.junit.Test; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream; | 
				
			||||||
 | 
					import java.io.IOException; | 
				
			||||||
 | 
					import java.nio.charset.StandardCharsets; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT; | 
				
			||||||
 | 
					import static org.junit.Assert.assertEquals; | 
				
			||||||
 | 
					import static org.junit.Assert.assertTrue; | 
				
			||||||
 | 
					import static org.mockito.ArgumentMatchers.argThat; | 
				
			||||||
 | 
					import static org.mockito.Mockito.mock; | 
				
			||||||
 | 
					import static org.mockito.Mockito.when; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class PingMethodTests { | 
				
			||||||
 | 
					    private static ObjectMapper mapper = new ObjectMapper().findAndRegisterModules(); | 
				
			||||||
 | 
					    private final String url = String.format("%s%s", BASE_ENDPOINT, "/api/v1/ping"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void TestServiceAliveReturnsSuccess() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent(new ByteArrayInputStream("{}".getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        GeneralUtilities generalUtilities = | 
				
			||||||
 | 
					            new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, Ping> res = generalUtilities.ping(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isRight()); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BinanceServiceUnreachableException.class) | 
				
			||||||
 | 
					    public void TestServiceFailsToReturnHttpEntity() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        GeneralUtilities generalUtilities = | 
				
			||||||
 | 
					            new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        generalUtilities.ping(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void TestServiceBadApiKeyReturns400() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                400, "Bad API Key")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ServiceError serviceError = ServiceError.builder().code(400).message("Bad API Key").build(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent( | 
				
			||||||
 | 
					            new ByteArrayInputStream(mapper.writeValueAsString(serviceError).getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        GeneralUtilities generalUtilities | 
				
			||||||
 | 
					            = new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, Ping> res = generalUtilities.ping(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isLeft()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.left()), serviceError); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,207 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.codepoetics.ambivalence.Either; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.JavaType; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.ObjectMapper; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.ServiceError; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.TickerPrice; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.matchers.GetMatcher; | 
				
			||||||
 | 
					import org.apache.commons.io.FileUtils; | 
				
			||||||
 | 
					import org.apache.http.ProtocolVersion; | 
				
			||||||
 | 
					import org.apache.http.client.methods.CloseableHttpResponse; | 
				
			||||||
 | 
					import org.apache.http.entity.BasicHttpEntity; | 
				
			||||||
 | 
					import org.apache.http.impl.client.CloseableHttpClient; | 
				
			||||||
 | 
					import org.apache.http.message.BasicStatusLine; | 
				
			||||||
 | 
					import org.junit.Before; | 
				
			||||||
 | 
					import org.junit.Test; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream; | 
				
			||||||
 | 
					import java.io.File; | 
				
			||||||
 | 
					import java.io.IOException; | 
				
			||||||
 | 
					import java.nio.charset.StandardCharsets; | 
				
			||||||
 | 
					import java.util.List; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT; | 
				
			||||||
 | 
					import static org.junit.Assert.assertEquals; | 
				
			||||||
 | 
					import static org.junit.Assert.assertTrue; | 
				
			||||||
 | 
					import static org.mockito.ArgumentMatchers.argThat; | 
				
			||||||
 | 
					import static org.mockito.Mockito.mock; | 
				
			||||||
 | 
					import static org.mockito.Mockito.when; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class TickerPriceTests { | 
				
			||||||
 | 
					    private static final ObjectMapper mapper = Helpers.objectMapperBuilder(); | 
				
			||||||
 | 
					    private final String url = String.format("%s%s", BASE_ENDPOINT, "/api/v3/ticker/price"); | 
				
			||||||
 | 
					    private final String specificUrl = String.format("%s%s", url, "?symbol=TEST"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private String singleTickerPriceJson; | 
				
			||||||
 | 
					    private String multipleTickerPricesJson; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private TickerPrice tickerPrice; | 
				
			||||||
 | 
					    private List<TickerPrice> tickerPrices; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Before | 
				
			||||||
 | 
					    public void setUp() throws IOException { | 
				
			||||||
 | 
					        ClassLoader classLoader = ExchangeInfoMethodTests.class.getClassLoader(); | 
				
			||||||
 | 
					        File singleTickerPriceFile = | 
				
			||||||
 | 
					            new File(classLoader.getResource("single_ticker_price_response.json").getFile()); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        File multipleTickerPriceFile = | 
				
			||||||
 | 
					            new File(classLoader.getResource("multiple_ticker_prices_response.json").getFile()); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, TickerPrice.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        singleTickerPriceJson = FileUtils.readFileToString(singleTickerPriceFile, "UTF-8"); | 
				
			||||||
 | 
					        multipleTickerPricesJson = FileUtils.readFileToString(multipleTickerPriceFile, "UTF-8"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tickerPrice = mapper.readValue(singleTickerPriceJson, TickerPrice.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tickerPrices = mapper.readValue(multipleTickerPricesJson, type); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void testTickerPriceForSymbolReturnsSuccessfully() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(specificUrl, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent(new ByteArrayInputStream(singleTickerPriceJson.getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, TickerPrice> res = marketData.getTickerPriceForSymbol("TEST"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isRight()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.right()), tickerPrice); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void testTickerPricesSuccessfully() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent(new ByteArrayInputStream(multipleTickerPricesJson.getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, List<TickerPrice>> res = marketData.getTickerPrices(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isRight()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.right()), tickerPrices); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BinanceServiceUnreachableException.class) | 
				
			||||||
 | 
					    public void testTickerPriceForSymbolWithoutHttpEntityThrows() | 
				
			||||||
 | 
					        throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(specificUrl, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        marketData.getTickerPriceForSymbol("TEST"); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BinanceServiceUnreachableException.class) | 
				
			||||||
 | 
					    public void testTickerPricesWithoutHttpEntityThrows() | 
				
			||||||
 | 
					        throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        marketData.getTickerPrices(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void testTickerPriceForSymbolWithBadApiKeyReturns400() | 
				
			||||||
 | 
					        throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                400, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(specificUrl, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ServiceError serviceError = ServiceError.builder().code(400).message("Bad API Key").build(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent( | 
				
			||||||
 | 
					            new ByteArrayInputStream(mapper.writeValueAsString(serviceError).getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, TickerPrice> res = marketData.getTickerPriceForSymbol("TEST"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isLeft()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.left()), serviceError); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void testTickerPricesBadApiKeyReturns400() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                400, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ServiceError serviceError = ServiceError.builder().code(400).message("Bad API Key").build(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent( | 
				
			||||||
 | 
					            new ByteArrayInputStream(mapper.writeValueAsString(serviceError).getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MarketData marketData = new MarketData("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, List<TickerPrice>> res = marketData.getTickerPrices(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isLeft()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.left()), serviceError); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,108 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.codepoetics.ambivalence.Either; | 
				
			||||||
 | 
					import com.fasterxml.jackson.databind.ObjectMapper; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.ServiceError; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.entities.Time; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.exceptions.BinanceServiceUnreachableException; | 
				
			||||||
 | 
					import com.sigmaflare.binancej.matchers.GetMatcher; | 
				
			||||||
 | 
					import org.apache.http.ProtocolVersion; | 
				
			||||||
 | 
					import org.apache.http.client.methods.CloseableHttpResponse; | 
				
			||||||
 | 
					import org.apache.http.entity.BasicHttpEntity; | 
				
			||||||
 | 
					import org.apache.http.impl.client.CloseableHttpClient; | 
				
			||||||
 | 
					import org.apache.http.message.BasicStatusLine; | 
				
			||||||
 | 
					import org.junit.Test; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream; | 
				
			||||||
 | 
					import java.io.IOException; | 
				
			||||||
 | 
					import java.nio.charset.StandardCharsets; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.sigmaflare.binancej.Constant.BASE_ENDPOINT; | 
				
			||||||
 | 
					import static org.junit.Assert.assertEquals; | 
				
			||||||
 | 
					import static org.junit.Assert.assertTrue; | 
				
			||||||
 | 
					import static org.mockito.ArgumentMatchers.argThat; | 
				
			||||||
 | 
					import static org.mockito.Mockito.mock; | 
				
			||||||
 | 
					import static org.mockito.Mockito.when; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class TimeMethodTests { | 
				
			||||||
 | 
					    private static ObjectMapper mapper = new ObjectMapper().findAndRegisterModules(); | 
				
			||||||
 | 
					    private final String url = String.format("%s%s", BASE_ENDPOINT, "/api/v1/time"); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void TestTimeReturnsSuccessfully() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Time time = Time.builder().serverTime(System.currentTimeMillis() / 1000L).build(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent(new ByteArrayInputStream( | 
				
			||||||
 | 
					            mapper.writeValueAsString(time).getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        GeneralUtilities generalUtilities = | 
				
			||||||
 | 
					            new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, Time> res = generalUtilities.getServerTime(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isRight()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.right()), time); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = BinanceServiceUnreachableException.class) | 
				
			||||||
 | 
					    public void TestTimeNoEntityReturnedFails() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                200, "test")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        GeneralUtilities generalUtilities = | 
				
			||||||
 | 
					            new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        generalUtilities.getServerTime(); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test | 
				
			||||||
 | 
					    public void TestTimeBadApiKeyReturns400() throws IOException, BinanceServiceUnreachableException { | 
				
			||||||
 | 
					        CloseableHttpClient mockedCloseableHttpClient = mock(CloseableHttpClient.class); | 
				
			||||||
 | 
					        CloseableHttpResponse mockedCloseableHttpResponse = mock(CloseableHttpResponse.class); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getStatusLine()) | 
				
			||||||
 | 
					            .thenReturn(new BasicStatusLine(new ProtocolVersion("TEST", 1, 0), | 
				
			||||||
 | 
					                400, "Bad API Key")); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpClient.execute(argThat(new GetMatcher(url, "1234")))) | 
				
			||||||
 | 
					            .thenReturn(mockedCloseableHttpResponse); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ServiceError serviceError = ServiceError.builder().code(400).message("Bad API Key").build(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        BasicHttpEntity httpEntity = new BasicHttpEntity(); | 
				
			||||||
 | 
					        httpEntity.setContent(new ByteArrayInputStream( | 
				
			||||||
 | 
					            mapper.writeValueAsString(serviceError).getBytes(StandardCharsets.UTF_8))); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        when(mockedCloseableHttpResponse.getEntity()).thenReturn(httpEntity); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        GeneralUtilities generalUtilities = | 
				
			||||||
 | 
					            new GeneralUtilities("1234", "abcd", mockedCloseableHttpClient); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Either<ServiceError, Time> res = generalUtilities.getServerTime(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue(res.isLeft()); | 
				
			||||||
 | 
					        assertEquals(Helpers.extractEitherValueSafely(res.left()), serviceError); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,29 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.matchers; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.apache.http.client.methods.HttpGet; | 
				
			||||||
 | 
					import org.mockito.ArgumentMatcher; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class GetMatcher implements ArgumentMatcher<HttpGet> { | 
				
			||||||
 | 
					    private String url; | 
				
			||||||
 | 
					    private String apiKey; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public GetMatcher(String url, String apiKey) { | 
				
			||||||
 | 
					        this.url = url; | 
				
			||||||
 | 
					        this.apiKey = apiKey; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Matches HttpGet objects based on "loose matching". We really only care that the URL and associated | 
				
			||||||
 | 
					     * headers we care about are correct. | 
				
			||||||
 | 
					     * @param httpGet The HttpGet object under test | 
				
			||||||
 | 
					     * @return True if they are equal by our definition, false otherwise | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public boolean matches(HttpGet httpGet) { | 
				
			||||||
 | 
					        final String url = httpGet.getURI().toASCIIString(); | 
				
			||||||
 | 
					        final String apiKeyHeaderValue = httpGet.getFirstHeader("X-MBX-APIKEY").getValue(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return url.equals(this.url) && apiKeyHeaderValue != null && apiKeyHeaderValue.equals(apiKey); | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,37 @@ | 
				
			|||||||
 | 
					package com.sigmaflare.binancej.matchers; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.apache.commons.io.IOUtils; | 
				
			||||||
 | 
					import org.apache.http.client.methods.HttpPost; | 
				
			||||||
 | 
					import org.mockito.ArgumentMatcher; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class PostMatcher implements ArgumentMatcher<HttpPost> { | 
				
			||||||
 | 
					    private String url; | 
				
			||||||
 | 
					    private String apiKey; | 
				
			||||||
 | 
					    private String requestBody; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public PostMatcher(String url, String apiKey, String requestBody) { | 
				
			||||||
 | 
					        this.url = url; | 
				
			||||||
 | 
					        this.apiKey = apiKey; | 
				
			||||||
 | 
					        this.requestBody = requestBody; | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** | 
				
			||||||
 | 
					     * Matches HttpPost objects based on "loose matching". We really only care that the URL and associated | 
				
			||||||
 | 
					     * headers we care about are correct. | 
				
			||||||
 | 
					     * @param httpPost The HttpPost object under test | 
				
			||||||
 | 
					     * @return True if they are equal by our definition, false otherwise | 
				
			||||||
 | 
					     */ | 
				
			||||||
 | 
					    public boolean matches(HttpPost httpPost) { | 
				
			||||||
 | 
					        final String url = httpPost.getURI().toASCIIString(); | 
				
			||||||
 | 
					        final String apiKeyHeaderValue = httpPost.getFirstHeader("X-MBX-APIKEY").getValue(); | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try { | 
				
			||||||
 | 
					            String testBody = IOUtils.toString(httpPost.getEntity().getContent(), "UTF-8"); | 
				
			||||||
 | 
					            return this.url.equals(url) && apiKey.equals(apiKeyHeaderValue) && requestBody.equals(testBody); | 
				
			||||||
 | 
					        } catch(IOException e) { | 
				
			||||||
 | 
					            return false; | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,16 @@ | 
				
			|||||||
 | 
					[ | 
				
			||||||
 | 
					  [ | 
				
			||||||
 | 
					    1499040000000, | 
				
			||||||
 | 
					    "0.01634790", | 
				
			||||||
 | 
					    "0.80000000", | 
				
			||||||
 | 
					    "0.01575800", | 
				
			||||||
 | 
					    "0.01577100", | 
				
			||||||
 | 
					    "148976.11427815", | 
				
			||||||
 | 
					    1499644799999, | 
				
			||||||
 | 
					    "2434.19055334", | 
				
			||||||
 | 
					    308, | 
				
			||||||
 | 
					    "1756.87402397", | 
				
			||||||
 | 
					    "28.46694368", | 
				
			||||||
 | 
					    "17928899.62484339" | 
				
			||||||
 | 
					  ] | 
				
			||||||
 | 
					] | 
				
			||||||
@ -0,0 +1,45 @@ | 
				
			|||||||
 | 
					{ | 
				
			||||||
 | 
					  "timezone": "UTC", | 
				
			||||||
 | 
					  "serverTime": 1508631584636, | 
				
			||||||
 | 
					  "rateLimits": [{ | 
				
			||||||
 | 
					    "rateLimitType": "REQUESTS", | 
				
			||||||
 | 
					    "interval": "MINUTE", | 
				
			||||||
 | 
					    "limit": 1200 | 
				
			||||||
 | 
					  }, | 
				
			||||||
 | 
					    { | 
				
			||||||
 | 
					      "rateLimitType": "ORDERS", | 
				
			||||||
 | 
					      "interval": "SECOND", | 
				
			||||||
 | 
					      "limit": 10 | 
				
			||||||
 | 
					    }, | 
				
			||||||
 | 
					    { | 
				
			||||||
 | 
					      "rateLimitType": "ORDERS", | 
				
			||||||
 | 
					      "interval": "DAY", | 
				
			||||||
 | 
					      "limit": 100000 | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					  ], | 
				
			||||||
 | 
					  "exchangeFilters": [], | 
				
			||||||
 | 
					  "symbols": [{ | 
				
			||||||
 | 
					    "symbol": "ETHBTC", | 
				
			||||||
 | 
					    "status": "TRADING", | 
				
			||||||
 | 
					    "baseAsset": "ETH", | 
				
			||||||
 | 
					    "baseAssetPrecision": 8, | 
				
			||||||
 | 
					    "quoteAsset": "BTC", | 
				
			||||||
 | 
					    "quotePrecision": 8, | 
				
			||||||
 | 
					    "orderTypes": ["LIMIT", "MARKET"], | 
				
			||||||
 | 
					    "icebergAllowed": false, | 
				
			||||||
 | 
					    "filters": [{ | 
				
			||||||
 | 
					      "filterType": "PRICE_FILTER", | 
				
			||||||
 | 
					      "minPrice": "0.00000100", | 
				
			||||||
 | 
					      "maxPrice": "100000.00000000", | 
				
			||||||
 | 
					      "tickSize": "0.00000100" | 
				
			||||||
 | 
					    }, { | 
				
			||||||
 | 
					      "filterType": "LOT_SIZE", | 
				
			||||||
 | 
					      "minQty": "0.00100000", | 
				
			||||||
 | 
					      "maxQty": "100000.00000000", | 
				
			||||||
 | 
					      "stepSize": "0.00100000" | 
				
			||||||
 | 
					    }, { | 
				
			||||||
 | 
					      "filterType": "MIN_NOTIONAL", | 
				
			||||||
 | 
					      "minNotional": "0.00100000" | 
				
			||||||
 | 
					    }] | 
				
			||||||
 | 
					  }] | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,10 @@ | 
				
			|||||||
 | 
					[ | 
				
			||||||
 | 
					  { | 
				
			||||||
 | 
					    "symbol": "LTCBTC", | 
				
			||||||
 | 
					    "price": "4.00000200" | 
				
			||||||
 | 
					  }, | 
				
			||||||
 | 
					  { | 
				
			||||||
 | 
					    "symbol": "ETHBTC", | 
				
			||||||
 | 
					    "price": "0.07946600" | 
				
			||||||
 | 
					  } | 
				
			||||||
 | 
					] | 
				
			||||||
@ -0,0 +1,17 @@ | 
				
			|||||||
 | 
					{ | 
				
			||||||
 | 
					  "lastUpdateId": 1027024, | 
				
			||||||
 | 
					  "bids": [ | 
				
			||||||
 | 
					    [ | 
				
			||||||
 | 
					      "4.00000000", | 
				
			||||||
 | 
					      "431.00000000", | 
				
			||||||
 | 
					      [] | 
				
			||||||
 | 
					    ] | 
				
			||||||
 | 
					  ], | 
				
			||||||
 | 
					  "asks": [ | 
				
			||||||
 | 
					    [ | 
				
			||||||
 | 
					      "4.00000200", | 
				
			||||||
 | 
					      "12.00000000", | 
				
			||||||
 | 
					      [] | 
				
			||||||
 | 
					    ] | 
				
			||||||
 | 
					  ] | 
				
			||||||
 | 
					} | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue