com.nubits.nubot.trading.wrappers.AltsTradeWrapper.java Source code

Java tutorial

Introduction

Here is the source code for com.nubits.nubot.trading.wrappers.AltsTradeWrapper.java

Source

/*
 * Copyright (C) 2015 Nu Development Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package com.nubits.nubot.trading.wrappers;

import com.nubits.nubot.bot.Global;
import com.nubits.nubot.exchanges.Exchange;
import com.nubits.nubot.global.Constant;
import com.nubits.nubot.global.Settings;
import com.nubits.nubot.models.*;
import com.nubits.nubot.models.Currency;
import com.nubits.nubot.trading.*;
import com.nubits.nubot.trading.keys.ApiKeys;
import com.nubits.nubot.utils.Utils;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.NoRouteToHostException;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by sammoth on 27/02/15.
 */
public class AltsTradeWrapper implements TradeInterface {

    private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(AltsTradeWrapper.class.getName());
    //Class fields
    private ApiKeys keys;
    protected AltsTradeService service;

    private Exchange exchange;
    private final String SIGN_HASH_FUNCTION = "HmacSHA512";
    private final String ENCODING = "UTF-8";
    private String apiBaseUrl;
    private String checkConnectionUrl;
    //API Paths
    private final String API_BASE_URL = "https://alts.trade/rest_api";
    private final String API_BALANCE = "balance";
    private final String API_TICKER = "ticker";
    private final String API_ORDERS = "orders";
    private final String API_MY = "my";
    private final String API_MY_ALL = "my_all";
    private final String API_CANCEL = "cancel";
    private final String API_MY_HISTORY = "my_history";
    //Errors
    private ErrorManager errors = new ErrorManager();
    private final String TOKEN_ERR = "error";
    private final String TOKEN_BAD_RETURN = "No Connection With Exchange";

    public AltsTradeWrapper(ApiKeys keys, Exchange exchange) {
        this.keys = keys;
        this.exchange = exchange;
        service = new AltsTradeService(keys);

        setupErrors();
    }

    private void setupErrors() {
        errors.setExchangeName(exchange);
    }

    private ApiResponse getQuery(String url, HashMap<String, String> query_args, boolean needAuth, boolean isGet) {
        ApiResponse apiResponse = new ApiResponse();
        String queryResult = query(url, "", query_args, needAuth, isGet);
        if (queryResult == null) {
            apiResponse.setError(errors.nullReturnError);
            return apiResponse;
        }
        if (queryResult.equals(TOKEN_BAD_RETURN)) {
            apiResponse.setError(errors.noConnectionError);
            return apiResponse;
        }

        JSONParser parser = new JSONParser();
        try {
            JSONObject httpAnswerJson = (JSONObject) (parser.parse(queryResult));
            boolean status = (boolean) httpAnswerJson.get("status");
            if (!status) {
                ApiError error = errors.apiReturnError;
                error.setDescription(httpAnswerJson.get("error").toString());
                apiResponse.setError(error);
            } else {
                apiResponse.setResponseObject(httpAnswerJson);
            }
        } catch (ClassCastException cce) {
            //if casting to a JSON object failed, try a JSON Array
            try {
                JSONArray httpAnswerJson = (JSONArray) (parser.parse(queryResult));
                apiResponse.setResponseObject(httpAnswerJson);
            } catch (ParseException pe) {
                // Alts.trade return the order id as an integer
                LOG.error("httpResponse: " + queryResult + " \n" + pe.toString());
                apiResponse.setError(errors.parseError);
            }
        } catch (ParseException pe) {
            LOG.error("httpResponse: " + queryResult + " \n" + pe.toString());
            apiResponse.setError(errors.parseError);
            return apiResponse;
        }
        return apiResponse;
    }

    @Override
    public ApiResponse getAvailableBalances(CurrencyPair pair) {
        return getBalancesImpl(pair, null);
    }

    @Override
    public ApiResponse getAvailableBalance(Currency currency) {
        return getBalancesImpl(null, currency);
    }

    private ApiResponse getBalancesImpl(CurrencyPair pair, Currency currency) {
        ApiResponse apiResponse = new ApiResponse();
        String url = API_BASE_URL + "/" + API_BALANCE;
        HashMap<String, String> args = new HashMap<>();
        boolean isGet = false;

        ApiResponse response = getQuery(url, args, true, isGet);
        if (response.isPositive()) {
            JSONArray httpAnswerJson = (JSONArray) response.getResponseObject();
            if (currency != null) { //get just one currency balance
                Amount balance = new Amount(0, currency);
                for (Iterator<JSONObject> wallet = httpAnswerJson.iterator(); wallet.hasNext();) {
                    JSONObject thisWallet = wallet.next();
                    if (thisWallet.get("code").equals(currency.getCode().toUpperCase())) {
                        balance.setQuantity(Utils.getDouble(thisWallet.get("balance")));
                    }
                }
                apiResponse.setResponseObject(balance);
            } else { // get the full pair balances
                Amount PEGAvail = new Amount(0, pair.getPaymentCurrency());
                Amount NBTAvail = new Amount(0, pair.getOrderCurrency());
                Amount PEGonOrder = new Amount(0, pair.getPaymentCurrency());
                Amount NBTonOrder = new Amount(0, pair.getOrderCurrency());
                for (Iterator<JSONObject> wallet = httpAnswerJson.iterator(); wallet.hasNext();) {
                    JSONObject thisWallet = wallet.next();
                    if (thisWallet.get("code").equals(pair.getOrderCurrency().getCode().toUpperCase())) {
                        NBTAvail.setQuantity(Utils.getDouble(thisWallet.get("balance")));
                        NBTonOrder.setQuantity(Utils.getDouble(thisWallet.get("held_for_orders")));
                    }
                    if (thisWallet.get("code").equals(pair.getPaymentCurrency().getCode().toUpperCase())) {
                        PEGAvail.setQuantity(Utils.getDouble(thisWallet.get("balance")));
                        PEGonOrder.setQuantity(Utils.getDouble(thisWallet.get("held_for_orders")));
                    }
                }
                PairBalance balance = new PairBalance(PEGAvail, NBTAvail, PEGonOrder, NBTonOrder);
                apiResponse.setResponseObject(balance);
            }
        } else {
            apiResponse = response;
        }

        return apiResponse;

    }

    @Override
    public ApiResponse getLastPrice(CurrencyPair pair) {
        ApiResponse apiResponse = new ApiResponse();
        String url = API_BASE_URL + "/" + API_TICKER + "/" + pair.toStringSepSpecial("/");
        //https://alts.trade/rest_api/ticker/{market_name}
        HashMap<String, String> args = new HashMap<>();
        boolean isGet = true;

        double last = -1;
        double ask = -1;
        double bid = -1;
        Ticker ticker = new Ticker();

        ApiResponse response = getQuery(url, args, false, isGet);
        if (response.isPositive()) {
            JSONObject httpAnswerJson = (JSONObject) response.getResponseObject();
            JSONObject result = (JSONObject) httpAnswerJson.get("result");
            last = Utils.getDouble(result.get("last"));
            ask = Utils.getDouble(result.get("low"));
            bid = Utils.getDouble(result.get("high"));
            ticker.setAsk(ask);
            ticker.setLast(last);
            ticker.setBid(bid);
            apiResponse.setResponseObject(ticker);
        } else {
            apiResponse = response;
        }
        return apiResponse;
    }

    @Override
    public ApiResponse sell(CurrencyPair pair, double amount, double rate) {
        return enterOrder(Constant.SELL, pair, amount, rate);
    }

    @Override
    public ApiResponse buy(CurrencyPair pair, double amount, double rate) {
        return enterOrder(Constant.BUY, pair, amount, rate);
    }

    private ApiResponse enterOrder(String type, CurrencyPair pair, double amount, double rate) {
        ApiResponse apiResponse = new ApiResponse();
        String url = API_BASE_URL + "/" + API_ORDERS;
        HashMap<String, String> args = new HashMap<>();
        boolean isGet = false;

        args.put("amount", Objects.toString(amount));
        args.put("market", pair.toStringSepSpecial("/").toUpperCase());
        args.put("price", Objects.toString(rate));
        args.put("action", type);

        ApiResponse response = getQuery(url, args, true, isGet);
        if (response.isPositive()) {
            ApiResponse getOpenOrders = getActiveOrders(pair);
            if (getOpenOrders.isPositive()) {
                ArrayList<Order> orders = (ArrayList) getOpenOrders.getResponseObject();
                Date checkDate = null;
                String order_id = null;
                for (Iterator<Order> order = orders.iterator(); order.hasNext();) {
                    Order thisOrder = order.next();
                    if (checkDate == null || thisOrder.getInsertedDate().getTime() > checkDate.getTime()) {
                        checkDate = thisOrder.getInsertedDate();
                        order_id = thisOrder.getId();
                    }
                }
                apiResponse.setResponseObject(order_id);
            } else {
                apiResponse = getOpenOrders;
            }
        } else {
            apiResponse = response;
        }
        return apiResponse;
    }

    @Override
    public ApiResponse getActiveOrders() {
        ApiResponse apiResponse = new ApiResponse();
        String url = API_BASE_URL + "/" + API_ORDERS + "/" + API_MY_ALL;
        HashMap<String, String> args = new HashMap<>();
        boolean isGet = false;
        ArrayList<Order> orderList = new ArrayList<>();

        ApiResponse response = getQuery(url, args, true, isGet);
        if (response.isPositive()) {
            JSONObject httpAnswerJson = (JSONObject) response.getResponseObject();
            JSONArray orders = (JSONArray) httpAnswerJson.get("orders");
            for (Iterator<JSONObject> order = orders.iterator(); order.hasNext();) {
                orderList.add(parseOrder(order.next(), null));
            }
            apiResponse.setResponseObject(orderList);
        } else {
            apiResponse = response;
        }
        return apiResponse;
    }

    @Override
    public ApiResponse getActiveOrders(CurrencyPair pair) {
        ApiResponse apiResponse = new ApiResponse();
        String url = API_BASE_URL + "/" + API_ORDERS + "/" + API_MY;
        HashMap<String, String> args = new HashMap<>();
        boolean isGet = false;
        ArrayList<Order> orderList = new ArrayList<>();

        args.put("market", pair.toStringSepSpecial("/").toUpperCase());

        ApiResponse response = getQuery(url, args, true, isGet);
        if (response.isPositive()) {
            JSONObject httpAnswerJson = (JSONObject) response.getResponseObject();
            JSONArray orders = (JSONArray) httpAnswerJson.get("orders");
            for (Iterator<JSONObject> order = orders.iterator(); order.hasNext();) {
                orderList.add(parseOrder(order.next(), pair));
            }
            apiResponse.setResponseObject(orderList);
        } else {
            apiResponse = response;
        }

        return apiResponse;
    }

    private Order parseOrder(JSONObject in, CurrencyPair pair) {
        Order out = new Order();

        out.setId(in.get("order_id").toString());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = null;
        try {
            date = sdf.parse(in.get("date").toString());
        } catch (java.text.ParseException pe) {
            LOG.error(pe.toString());
        }
        if (date != null) {
            out.setInsertedDate(date);
        }
        if (pair == null) {
            pair = CurrencyPair.getCurrencyPairFromString(in.get("market").toString(), "/");
        }
        out.setPair(pair);
        Amount price = new Amount(Utils.getDouble(in.get("price")), pair.getPaymentCurrency());
        out.setPrice(price);
        Amount amount = new Amount(Utils.getDouble(in.get("amount")), pair.getOrderCurrency());
        out.setAmount(amount);
        out.setType(in.get("action").toString() == "SELL" ? Constant.SELL : Constant.BUY);
        return out;
    }

    @Override
    public ApiResponse getOrderDetail(String orderID) {
        ApiResponse apiResponse = new ApiResponse();

        ApiResponse getAllOrders = getActiveOrders();
        if (getAllOrders.isPositive()) {
            ArrayList<Order> orders = (ArrayList) getAllOrders.getResponseObject();
            for (Iterator<Order> order = orders.iterator(); order.hasNext();) {
                Order thisOrder = order.next();
                if (thisOrder.getId().equals(orderID)) {
                    apiResponse.setResponseObject(thisOrder);
                }
            }
        } else {
            apiResponse = getAllOrders;
        }
        return apiResponse;
    }

    @Override
    public ApiResponse cancelOrder(String orderID, CurrencyPair pair) {
        ApiResponse apiResponse = new ApiResponse();
        String url = API_BASE_URL + "/" + API_ORDERS + "/" + API_CANCEL;
        HashMap<String, String> args = new HashMap<>();
        boolean isGet = false;

        args.put("order_id", orderID);

        ApiResponse response = getQuery(url, args, true, isGet);
        if (response.isPositive()) {
            apiResponse.setResponseObject(true);
        } else {
            apiResponse = response;
        }
        return apiResponse;
    }

    @Override
    public ApiResponse getTxFee() {
        ApiResponse apiResponse = new ApiResponse();
        apiResponse.setResponseObject(0);
        return apiResponse;
    }

    @Override
    public ApiResponse getTxFee(CurrencyPair pair) {
        ApiResponse apiResponse = new ApiResponse();
        apiResponse.setResponseObject(0);
        return apiResponse;
    }

    @Override
    public ApiResponse getLastTrades(CurrencyPair pair) {
        return getLastTradesImpl(pair, 0);
    }

    @Override
    public ApiResponse getLastTrades(CurrencyPair pair, long startTime) {
        return getLastTradesImpl(pair, startTime);
    }

    private ApiResponse getLastTradesImpl(CurrencyPair pair, long startTime) {
        ApiResponse apiResponse = new ApiResponse();
        String url = API_BASE_URL + "/" + API_ORDERS + "/" + API_MY_HISTORY;
        HashMap<String, String> args = new HashMap<>();
        boolean isGet = false;
        ArrayList<Trade> tradeList = new ArrayList<>();

        args.put("market", pair.toStringSepSpecial("/"));

        ApiResponse response = getQuery(url, args, true, isGet);
        if (response.isPositive()) {
            JSONObject httpAnswerJson = (JSONObject) response.getResponseObject();
            JSONArray history = (JSONArray) httpAnswerJson.get("history");
            for (Iterator<JSONObject> trade = history.iterator(); trade.hasNext();) {
                Trade thisTrade = parseTrade(trade.next(), pair);
                if (startTime > 0 && thisTrade.getDate().getTime() < startTime)
                    continue;
                tradeList.add(thisTrade);
            }
            apiResponse.setResponseObject(tradeList);
        } else {
            apiResponse = response;
        }
        return apiResponse;
    }

    private Trade parseTrade(JSONObject in, CurrencyPair pair) {
        Trade out = new Trade();

        out.setExchangeName(exchange.getName());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = null;
        try {
            date = sdf.parse(in.get("date").toString());
        } catch (java.text.ParseException pe) {
            LOG.error(pe.toString());
        }
        if (date != null) {
            out.setDate(date);
        }
        Amount amount = new Amount(Utils.getDouble(in.get("amount")), pair.getOrderCurrency());
        out.setAmount(amount);
        Amount price = new Amount(Utils.getDouble(in.get("price")), pair.getPaymentCurrency());
        out.setPrice(price);
        out.setType(in.get("action").toString().equals("SELL") ? Constant.SELL : Constant.BUY);

        return out;
    }

    @Override
    public ApiResponse isOrderActive(String id) {
        ApiResponse apiResponse = new ApiResponse();

        ApiResponse getActiveOrders = getActiveOrders();
        if (getActiveOrders.isPositive()) {
            ArrayList<Order> orders = (ArrayList) getActiveOrders.getResponseObject();
            boolean isActive = false;
            for (Iterator<Order> order = orders.iterator(); order.hasNext();) {
                if (order.next().getId().equals(id)) {
                    isActive = true;
                }
            }
            apiResponse.setResponseObject(isActive);
        } else {
            apiResponse = getActiveOrders;
        }
        return apiResponse;
    }

    @Override
    public ApiResponse getOrderBook(CurrencyPair pair) {
        return null;
    }

    @Override
    public ApiResponse clearOrders(CurrencyPair pair) {
        ApiResponse apiResponse = new ApiResponse();
        ApiResponse getOrders = getActiveOrders(pair);
        if (getOrders.isPositive()) {
            ArrayList<Order> orders = (ArrayList) getOrders.getResponseObject();
            boolean allClear = true;
            for (Iterator<Order> order = orders.iterator(); order.hasNext();) {
                ApiResponse cancel = cancelOrder(order.next().getId(), pair);
                if (!cancel.isPositive()) {
                    allClear = false;
                }
            }
            apiResponse.setResponseObject(allClear);
        } else {
            apiResponse = getOrders;
        }
        return apiResponse;
    }

    @Override
    public ApiError getErrorByCode(int code) {
        return null;
    }

    @Override
    public String getUrlConnectionCheck() {
        return API_BASE_URL;
    }

    @Override
    public String query(String base, String method, AbstractMap<String, String> args, boolean needAuth,
            boolean isGet) {
        String queryResult = TOKEN_BAD_RETURN; //Will return this string in case it fails
        if (exchange.getLiveData().isConnected()) {
            if (exchange.isFree()) {
                exchange.setBusy();
                queryResult = service.executeQuery(base, method, args, needAuth, isGet);
                exchange.setFree();
            } else {
                //Another thread is probably executing a query. Init the retry procedure
                long sleeptime = Settings.RETRY_SLEEP_INCREMENT * 1;
                int counter = 0;
                long startTimeStamp = System.currentTimeMillis();
                LOG.debug(method + " blocked, another call is being processed ");
                boolean exit = false;
                do {
                    counter++;
                    sleeptime = counter * Settings.RETRY_SLEEP_INCREMENT; //Increase sleep time
                    sleeptime += (int) (Math.random() * 200) - 100;// Add +- 100 ms random to facilitate competition
                    LOG.debug(
                            "Retrying for the " + counter + " time. Sleep for " + sleeptime + "; Method=" + method);
                    try {
                        Thread.sleep(sleeptime);
                    } catch (InterruptedException e) {
                        LOG.error(e.toString());
                    }

                    //Try executing the call
                    if (exchange.isFree()) {
                        LOG.debug("Finally the exchange is free, executing query after " + counter
                                + " attempt. Method=" + method);
                        exchange.setBusy();
                        queryResult = service.executeQuery(base, method, args, needAuth, isGet);
                        exchange.setFree();
                        break; //Exit loop
                    } else {
                        LOG.debug("Exchange still busy : " + counter + " .Will retry soon; Method=" + method);
                        exit = false;
                    }
                    if (System.currentTimeMillis() - startTimeStamp >= Settings.TIMEOUT_QUERY_RETRY) {
                        exit = true;
                        LOG.error(
                                "Method=" + method + " failed too many times and timed out. attempts = " + counter);
                    }
                } while (!exit);
            }
        } else {
            LOG.error("The bot will not execute the query, there is no connection with" + exchange.getName());
            queryResult = TOKEN_BAD_RETURN;
        }
        return queryResult;
    }

    @Override
    public void setKeys(ApiKeys keys) {
        this.keys = keys;
    }

    @Override
    public void setExchange(Exchange exchange) {
        this.exchange = exchange;
    }

    @Override
    public void setApiBaseUrl(String apiBaseUrl) {

    }

    class AltsTradeService implements ServiceInterface {

        protected ApiKeys keys;

        public AltsTradeService(ApiKeys keys) {
            this.keys = keys;
        }

        @Override
        public String executeQuery(String base, String method, AbstractMap<String, String> args, boolean needAuth,
                boolean isGet) {
            String answer = null;
            String signature = "";
            String post_data = "";
            String url = base + method;
            args.put("nonce", Objects.toString(System.currentTimeMillis()));

            List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();

            for (Iterator<Map.Entry<String, String>> argumentIterator = args.entrySet().iterator(); argumentIterator
                    .hasNext();) {
                Map.Entry<String, String> argument = argumentIterator.next();
                urlParameters.add(new BasicNameValuePair(argument.getKey(), argument.getValue()));
            }

            post_data = TradeUtils.buildQueryString(args, ENCODING);

            signature = TradeUtils.signRequest(keys.getPrivateKey(), post_data, SIGN_HASH_FUNCTION, ENCODING);

            // add header
            Header[] headers = new Header[3];
            headers[0] = new BasicHeader("Rest-Key", keys.getApiKey());
            headers[1] = new BasicHeader("Rest-Sign", signature);
            headers[2] = new BasicHeader("Content-type", "application/x-www-form-urlencoded");

            HttpClient client = HttpClientBuilder.create().build();
            HttpPost post = null;
            HttpGet get = null;
            HttpResponse response = null;

            try {
                if (!isGet) {
                    post = new HttpPost(url);
                    post.setEntity(new UrlEncodedFormEntity(urlParameters, ENCODING));
                    post.setHeaders(headers);
                    response = client.execute(post);
                } else {
                    get = new HttpGet(url);
                    get.setHeaders(headers);
                    response = client.execute(get);
                }
            } catch (NoRouteToHostException e) {
                if (!isGet) {
                    post.abort();
                } else {
                    get.abort();
                }
                LOG.error(e.toString());
                return null;
            } catch (SocketException e) {
                if (!isGet) {
                    post.abort();
                } else {
                    get.abort();
                }
                LOG.error(e.toString());
                return null;
            } catch (Exception e) {
                if (!isGet) {
                    post.abort();
                } else {
                    get.abort();
                }
                LOG.error(e.toString());
                return null;
            }
            BufferedReader rd;

            try {
                rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));

                StringBuffer buffer = new StringBuffer();
                String line = "";
                while ((line = rd.readLine()) != null) {
                    buffer.append(line);
                }

                answer = buffer.toString();
            } catch (IOException ex) {

                LOG.error(ex.toString());
                return null;
            } catch (IllegalStateException ex) {

                LOG.error(ex.toString());
                return null;
            }
            if (Global.options != null && Global.options.isVerbose()) {

                LOG.trace("\nSending request to URL : " + base + " ; get = " + isGet);
                if (post != null) {
                    System.out.println("Post parameters : " + post.getEntity());
                }
                LOG.trace("Response Code : " + response.getStatusLine().getStatusCode());
                LOG.trace("Response :" + response);

            }
            return answer;

        }

    }
}