Java tutorial
/* * 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.exchanges.ExchangeFacade; 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.ErrorManager; import com.nubits.nubot.trading.ServiceInterface; import com.nubits.nubot.trading.TradeInterface; 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.client.HttpClient; 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.json.JSONException; 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.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; public class CcexWrapper implements TradeInterface { private ApiKeys keys; protected CcexService service; private Exchange exchange; private String checkConnectionUrl = "https://c-cex.com/"; private static final Logger LOG = LoggerFactory.getLogger(CcexWrapper.class.getName()); //Entry point(s) private final String API_BASE = "https://c-cex.com/t/r.html?"; private String baseUrl; //Errors private ErrorManager errors = new ErrorManager(); private final String TOKEN_ERR = "error"; private final String TOKEN_BAD_RETURN = "No Connection With Exchange"; public CcexWrapper(ApiKeys keys, Exchange exchange) { this.keys = keys; this.exchange = exchange; this.baseUrl = API_BASE + "key=" + keys.getPrivateKey(); service = new CcexService(keys); setupErrors(); } public void initBaseUrl() { this.baseUrl = API_BASE + "key=" + keys.getPrivateKey(); } private void setupErrors() { errors.setExchangeName(exchange); } private ApiResponse getQuery(String url, HashMap<String, String> query_args, boolean needAuth, boolean isGet) { ApiResponse apiResponse = new ApiResponse(); return apiResponse; } @Override public ApiResponse getAvailableBalances(CurrencyPair pair) { return getBalanceImpl(pair, null); } @Override public ApiResponse getAvailableBalance(Currency currency) { return getBalanceImpl(null, currency); } public ApiResponse getBalanceImpl(CurrencyPair pair, Currency currency) { ApiResponse apiResponse = new ApiResponse(); String url = baseUrl + "&a=getbalance"; String queryResult = query(url, "", new HashMap<String, String>(), true, true); if (queryResult.equals(TOKEN_BAD_RETURN)) { apiResponse.setError(errors.nullReturnError); return apiResponse; } if (queryResult.startsWith("Access denied")) { apiResponse.setError(errors.authenticationError); return apiResponse; } /*Sample result * * {"return": * [ * {"usd":0}, * {"btc":0.04666311}, * ... * ] * } * */ JSONParser parser = new JSONParser(); try { JSONObject httpAnswerJson = (JSONObject) (parser.parse(queryResult)); JSONArray balanceArray = (JSONArray) httpAnswerJson.get("return"); if (currency != null) { //looking for a specific currency String lookingFor = currency.getCode().toLowerCase(); boolean found = false; for (int i = 0; i < balanceArray.size(); i++) { JSONObject tempElement = (JSONObject) balanceArray.get(i); if (tempElement.containsKey(lookingFor)) { found = true; double foundBalance = Utils.getDouble(tempElement.get(lookingFor)); apiResponse.setResponseObject(new Amount(foundBalance, currency)); } } if (!found) { //Specific currency not found String errorMessage = "Cannot find a balance for currency " + lookingFor; ApiError apiErr = errors.genericError; apiErr.setDescription(errorMessage); apiResponse.setError(apiErr); return apiResponse; } } else { //get all balances for the pair boolean foundNBTavail = false; boolean foundPEGavail = false; Amount NBTAvail = new Amount(0, pair.getOrderCurrency()), PEGAvail = new Amount(0, pair.getPaymentCurrency()); Amount PEGonOrder = new Amount(0, pair.getPaymentCurrency()), NBTonOrder = new Amount(0, pair.getOrderCurrency()); String NBTcode = pair.getOrderCurrency().getCode().toLowerCase(); String PEGcode = pair.getPaymentCurrency().getCode().toLowerCase(); for (int i = 0; i < balanceArray.size(); i++) { JSONObject tempElement = (JSONObject) balanceArray.get(i); if (tempElement.containsKey(NBTcode)) { double tempAvailablebalance = Utils.getDouble(tempElement.get(NBTcode)); double tempLockedebalance = 0; //Not provided by the API NBTAvail = new Amount(tempAvailablebalance, pair.getOrderCurrency()); NBTonOrder = new Amount(tempLockedebalance, pair.getOrderCurrency()); foundNBTavail = true; } else if (tempElement.containsKey(PEGcode)) { double tempAvailablebalance = Utils.getDouble(tempElement.get(PEGcode)); double tempLockedebalance = 0; //Not provided by the API PEGAvail = new Amount(tempAvailablebalance, pair.getPaymentCurrency()); PEGonOrder = new Amount(tempLockedebalance, pair.getPaymentCurrency()); foundPEGavail = true; } } PairBalance balance = new PairBalance(PEGAvail, NBTAvail, PEGonOrder, NBTonOrder); apiResponse.setResponseObject(balance); if (!foundNBTavail || !foundPEGavail) { LOG.warn("Cannot find a balance for currency with code " + "" + NBTcode + " or " + PEGcode + " in your balance. " + "NuBot assumes that balance is 0"); } } } catch (ParseException ex) { LOG.error("httpresponse: " + queryResult + " \n" + ex.toString()); apiResponse.setError(errors.parseError); return apiResponse; } return apiResponse; } @Override public ApiResponse getLastPrice(CurrencyPair pair) { throw new UnsupportedOperationException("Not supported yet."); } @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 order_id = ""; /*https://c-cex.com/t/r.html?key=Your_API_Key&a=makeorder&pair=Curr1-Curr2&q=Amount&t=[s/b]&r=Rate "t" - order type. t=s - Sell, t=b - Buy */ String typeCode; if (type.equals(Constant.BUY)) { typeCode = "b"; } else { typeCode = "s"; } String url = baseUrl + "&a=makeorder"; url += "&pair=" + (pair.toStringSepSpecial("-")).toLowerCase(); url += "&q=" + amount; url += "&t=" + typeCode; url += "&r=" + rate; String queryResult = query(url, "", new HashMap<String, String>(), true, true); if (queryResult.equals(TOKEN_BAD_RETURN)) { apiResponse.setError(errors.nullReturnError); return apiResponse; } if (queryResult.startsWith("Access denied")) { apiResponse.setError(errors.authenticationError); return apiResponse; } /*Sample result * *{"return":"not enough funds"} * - OR - *{"return":"4677949"} */ JSONParser parser = new JSONParser(); try { JSONObject httpAnswerJson = (JSONObject) (parser.parse(queryResult)); String ret = (String) httpAnswerJson.get("return"); if (ret.contains("not enough funds") || ret.contains(" ")) { ApiError apiErr = errors.apiReturnError; apiErr.setDescription(ret); LOG.error("CCex API returned an error: " + ret); apiResponse.setError(apiErr); return apiResponse; } else { //correct order_id = ret; apiResponse.setResponseObject(order_id); } } catch (ParseException ex) { LOG.error("httpresponse: " + queryResult + " \n" + ex.toString()); apiResponse.setError(errors.parseError); return apiResponse; } return apiResponse; } @Override public ApiResponse getActiveOrders() { return getOrdersImpl(null); } @Override public ApiResponse getActiveOrders(CurrencyPair pair) { return getOrdersImpl(pair); } public ApiResponse getOrdersImpl(CurrencyPair pair) { ApiResponse apiResponse = new ApiResponse(); ArrayList<Order> orderList = new ArrayList<Order>(); String url = baseUrl + "&a=orderlist&self=1"; if (pair != null) { url += "&pair=" + (pair.toStringSepSpecial("-")).toLowerCase(); } String queryResult = query(url, "", new HashMap<String, String>(), true, true); if (queryResult.equals(TOKEN_BAD_RETURN)) { apiResponse.setError(errors.nullReturnError); return apiResponse; } if (queryResult.startsWith("Access denied")) { apiResponse.setError(errors.authenticationError); return apiResponse; } /*Sample result * *{"return": * {"158913": * {"type":"sell","c1":"usd","c2":"btc","amount":0.00064744,"price":520,"self":0}, * "158912": * {...} * } * } * */ try { org.json.JSONObject httpAnswerJson = new org.json.JSONObject(queryResult); //correct org.json.JSONObject dataJson = (org.json.JSONObject) httpAnswerJson.get("return"); if (dataJson.length() == 0) { //empty order list apiResponse.setResponseObject(orderList); return apiResponse; } //Iterate on orders String names[] = org.json.JSONObject.getNames(dataJson); for (int i = 0; i < names.length; i++) { org.json.JSONObject tempJson = dataJson.getJSONObject(names[i]); Order tempOrder = parseOrder(tempJson); tempOrder.setId(names[i]); if (!tempOrder.isCompleted()) //Do not add executed orders { //check if a specific currencypair is set if (pair != null) { if (tempOrder.getPair().equals(pair)) { orderList.add(tempOrder); } } else { orderList.add(tempOrder); } } } apiResponse.setResponseObject(orderList); } catch (JSONException ex) { LOG.error("httpresponse: " + queryResult + " \n" + ex.toString()); apiResponse.setError(errors.parseError); return apiResponse; } return apiResponse; } @Override public ApiResponse getOrderDetail(String orderID) { ApiResponse apiResp = new ApiResponse(); Order order = null; ApiResponse listApiResp = getActiveOrders(); if (listApiResp.isPositive()) { ArrayList<Order> orderList = (ArrayList<Order>) listApiResp.getResponseObject(); boolean found = false; for (int i = 0; i < orderList.size(); i++) { Order tempOrder = orderList.get(i); if (orderID.equals(tempOrder.getId())) { found = true; apiResp.setResponseObject(tempOrder); return apiResp; } } if (!found) { ApiError apiErr = errors.apiReturnError; apiErr.setDescription("Cannot find the order with id " + orderID); apiResp.setError(apiErr); return apiResp; } } else { return listApiResp; } return apiResp; } @Override public ApiResponse cancelOrder(String orderID, CurrencyPair pair) { ApiResponse apiResponse = new ApiResponse(); String order_id = ""; //https://c-cex.com/t/r.html?key=Your_API_Key&a=cancelorder&id=OrderID String url = baseUrl + "&a=cancelorder"; url += "&id=" + orderID; String queryResult = query(url, "", new HashMap<String, String>(), true, true); if (queryResult.equals(TOKEN_BAD_RETURN)) { apiResponse.setError(errors.nullReturnError); return apiResponse; } if (queryResult.startsWith("Access denied")) { apiResponse.setError(errors.authenticationError); return apiResponse; } /*Sample result * * {"return":"Order 4678299 canceled"} * - OR - *{"error":"Order 1228 not found"} */ JSONParser parser = new JSONParser(); try { JSONObject httpAnswerJson = (JSONObject) (parser.parse(queryResult)); if (httpAnswerJson.containsKey("error")) { ApiError apiErr = errors.apiReturnError; apiErr.setDescription((String) httpAnswerJson.get("error")); apiResponse.setError(apiErr); return apiResponse; } else { //correct apiResponse.setResponseObject(true); } } catch (ParseException ex) { LOG.error("httpresponse: " + queryResult + " \n" + ex.toString()); apiResponse.setError(errors.parseError); return apiResponse; } return apiResponse; } @Override public ApiResponse getTxFee() { return new ApiResponse(true, Global.options.getTxFee(), null); } @Override public ApiResponse getTxFee(CurrencyPair pair) { LOG.info("CCex uses global TX fee, currency pair not supprted. \n" + "now calling getTxFee()"); return getTxFee(); } @Override public ApiResponse getLastTrades(CurrencyPair pair) { long now = System.currentTimeMillis(); long yesterday = now - Utils.getOneDayInMillis(); return getTradesImpl(pair, yesterday); } @Override public ApiResponse getLastTrades(CurrencyPair pair, long startTime) { return getTradesImpl(pair, startTime); } private ApiResponse getTradesImpl(CurrencyPair pair, long startTime) { ApiResponse apiResponse = new ApiResponse(); ArrayList<Trade> tradeList = new ArrayList<Trade>(); /* * https://c-cex.com/t/r.html?key=Your_API_Key&a=tradehistory&d1=Date_Time_From&d2=Date_Time_To&pair=Curr1-Curr2 * Example: https://c-cex.com/t/r.html?key=Your_API_Key&a=tradehistory&d1=2014-01-01&d2=2014-02-10&pair=grc-btc */ //Parse the date Date date = new Date(startTime); Calendar myCal = new GregorianCalendar(); myCal.setTime(date); String formattedStartDate = Integer.toString(myCal.get(Calendar.YEAR)) + "-" + (myCal.get(Calendar.MONTH) + 1) + "-" + myCal.get(Calendar.DAY_OF_MONTH); myCal.setTime(new Date()); //now String formattedStopDate = Integer.toString(myCal.get(Calendar.YEAR)) + "-" + (myCal.get(Calendar.MONTH) + 1) + "-" + myCal.get(Calendar.DAY_OF_MONTH); String url = baseUrl + "&a=tradehistory"; url += "&d1=" + formattedStartDate; url += "&d2=" + formattedStopDate; url += "&pair=" + (pair.toStringSepSpecial("-")).toLowerCase(); String queryResult = query(url, "", new HashMap<String, String>(), true, true); if (queryResult.equals(TOKEN_BAD_RETURN)) { apiResponse.setError(errors.nullReturnError); return apiResponse; } if (queryResult.startsWith("Access denied")) { apiResponse.setError(errors.authenticationError); return apiResponse; } /*Sample result *{"return": * [ * {"id":"464351","dt":"2014-10-23 13:04:49","type":"Buy","amount":0.04271662,"rate":369,"backrate":0.00271003} * ] * } */ JSONParser parser = new JSONParser(); try { JSONObject httpAnswerJson = (JSONObject) (parser.parse(queryResult)); JSONArray array; //correct try { array = (JSONArray) httpAnswerJson.get("return"); } catch (Exception e) { apiResponse.setResponseObject(tradeList); return apiResponse; } for (int i = 0; i < array.size(); i++) { tradeList.add(parseTrade((JSONObject) array.get(i), pair)); } apiResponse.setResponseObject(tradeList); } catch (ParseException ex) { LOG.error("httpresponse: " + queryResult + " \n" + ex.toString()); apiResponse.setError(errors.parseError); return apiResponse; } return apiResponse; } @Override public ApiResponse isOrderActive(String id) { ApiResponse existResponse = new ApiResponse(); ApiResponse orderDetailResponse = getOrderDetail(id); if (orderDetailResponse.isPositive()) { Order order = (Order) orderDetailResponse.getResponseObject(); existResponse.setResponseObject(true); } else { ApiError err = orderDetailResponse.getError(); if (err.getDescription().contains("Cannot find the order")) { existResponse.setResponseObject(false); } else { existResponse.setError(err); } } return existResponse; } @Override public ApiResponse clearOrders(CurrencyPair pair) { //Since there is no API entry point for that, this call will iterate over actie ApiResponse toReturn = new ApiResponse(); boolean ok = true; ApiResponse activeOrdersResponse = getActiveOrders(); if (activeOrdersResponse.isPositive()) { ArrayList<Order> orderList = (ArrayList<Order>) activeOrdersResponse.getResponseObject(); for (int i = 0; i < orderList.size(); i++) { Order tempOrder = orderList.get(i); ApiResponse deleteOrderResponse = cancelOrder(tempOrder.getId(), pair); if (deleteOrderResponse.isPositive()) { boolean deleted = (boolean) deleteOrderResponse.getResponseObject(); if (deleted) { LOG.warn("Order " + tempOrder.getId() + " deleted succesfully"); } else { LOG.warn("Could not delete order " + tempOrder.getId() + ""); ok = false; } } else { LOG.error(deleteOrderResponse.getError().toString()); } try { Thread.sleep(500); } catch (InterruptedException ex) { LOG.error(ex.toString()); } } toReturn.setResponseObject(ok); } else { LOG.error(activeOrdersResponse.getError().toString()); toReturn.setError(activeOrdersResponse.getError()); return toReturn; } return toReturn; } @Override public ApiError getErrorByCode(int code) { return null; } @Override public String getUrlConnectionCheck() { return checkConnectionUrl; } @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) { throw new UnsupportedOperationException("Not supported yet."); } private Order parseOrder(org.json.JSONObject orderObject) { Order order = new Order(); try { /* "type" - Order type - by/sell "c1" - Currency 1 "c2" - Currency 2 "amount" - Currency 2 amount "price" - Currency rate "self" - 1 - self older */ String c1 = orderObject.getString("c1"); String c2 = orderObject.getString("c2"); CurrencyPair cp = new CurrencyPair(Currency.createCurrency(c1), Currency.createCurrency(c2)); order.setPair(cp); order.setType((orderObject.getString("type")).toUpperCase()); order.setAmount(new Amount(orderObject.getDouble("amount"), cp.getOrderCurrency())); order.setPrice(new Amount(orderObject.getDouble("price"), cp.getPaymentCurrency())); order.setCompleted(false); order.setInsertedDate(new Date()); //Not provided } catch (JSONException ex) { LOG.error(ex.toString()); } return order; } private Trade parseTrade(JSONObject tradeObj, CurrencyPair pair) { // {"id":"464351","dt":"2014-10-23 13:04:49","type":"Buy","amount":0.04271662,"rate":369,"backrate":0.00271003} Trade trade = new Trade(); trade.setOrder_id((String) tradeObj.get("id")); trade.setExchangeName(ExchangeFacade.CCEX); trade.setPair(pair); trade.setType(((String) tradeObj.get("type")).toUpperCase()); trade.setAmount(new Amount(Utils.getDouble(tradeObj.get("amount")), pair.getPaymentCurrency())); trade.setPrice(new Amount(Utils.getDouble(tradeObj.get("rate")), pair.getOrderCurrency())); trade.setFee(new Amount(0, pair.getPaymentCurrency())); trade.setDate(parseDate((String) tradeObj.get("dt"))); return trade; } private Date parseDate(String dateStr) { Date toRet = null; //Parse the date //Sample 2014-02-19 04:55:44 String datePattern = "yyyy-MM-dd HH:mm:ss"; DateFormat df = new SimpleDateFormat(datePattern, Locale.ENGLISH); try { toRet = df.parse(dateStr); } catch (java.text.ParseException ex) { LOG.error(ex.toString()); toRet = new Date(); } return toRet; } @Override public ApiResponse getOrderBook(CurrencyPair pair) { throw new UnsupportedOperationException("CcexWrapper.getOrderBook() not implemented yet."); } private class CcexService implements ServiceInterface { protected ApiKeys keys; private CcexService() { } private CcexService(ApiKeys keys) { this.keys = keys; } @Override public String executeQuery(String base, String method, AbstractMap<String, String> args, boolean needAuth, boolean isGet) { String answer = ""; String url = base + method; // add header Header[] headers = new Header[1]; headers[0] = new BasicHeader("Content-type", "application/x-www-form-urlencoded"); URL queryUrl; try { queryUrl = new URL(url); } catch (MalformedURLException ex) { LOG.error(ex.toString()); } HttpClient client = HttpClientBuilder.create().build(); HttpPost post = null; HttpGet get = null; HttpResponse response = null; try { get = new HttpGet(url); get.setHeaders(headers); response = client.execute(get); } catch (Exception e) { 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); return null; } catch (IllegalStateException ex) { LOG.error("" + ex); return null; } if (Global.options != null && Global.options.isVerbose()) { LOG.info("\nSending request to URL : " + url + " ; get = " + isGet); if (post != null) { System.out.println("Post parameters : " + post.getEntity()); } LOG.info("Response Code : " + response.getStatusLine().getStatusCode()); LOG.info("Response :" + response); } return answer; } } }