com.mtgox.api.MtGox.java Source code

Java tutorial

Introduction

Here is the source code for com.mtgox.api.MtGox.java

Source

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.mtgox.api;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.HttpsURLConnection;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

/**
 *
 * https://github.com/adv0r/mtgox-apiv2-java
 * @author adv0r <leg@lize.it>
 * MIT License (see LICENSE.md)
 * Implementation of MtGox Api V2
 * Consider donations @ 1N7XxSvek1xVnWEBFGa5sHn1NhtDdMhkA7
 * unofficial documentation by nitrous https://bitbucket.org/nitrous/mtgox-api/overview
 */
public class MtGox implements TradeInterface {

    public enum Currency {
        BTC, USD, GBP, EUR, JPY, AUD, CAD, CHF, CNY, DKK, HKD, PLN, RUB, SEK, SGD, THB
    };

    private ApiKeys keys;

    private final HashMap<Currency, Integer> devisionFactors;

    private final double MIN_ORDER = 0.01; //BTC

    private final String API_BASE_URL = "https://data.mtgox.com/api/2/";

    //Paths
    private final String API_GET_INFO = "MONEY/INFO";

    private final String API_WITHDRAW = "MONEY/BITCOIN/SEND_SIMPLE";
    private final String API_LAG = "MONEY/ORDER/LAG";
    private final String API_ADD_ORDER = "BTCUSD/MONEY/ORDER/ADD";

    private final String SIGN_HASH_FUNCTION = "HmacSHA512";
    private final String ENCODING = "UTF-8";

    private boolean printHttpResponse;

    public MtGox(ApiKeys keys) {
        this.keys = keys;
        printHttpResponse = false;
        // set division Factors
        devisionFactors = new HashMap<Currency, Integer>();
        devisionFactors.put(Currency.BTC, 100000000);
        devisionFactors.put(Currency.USD, 100000);
        devisionFactors.put(Currency.GBP, 100000);
        devisionFactors.put(Currency.EUR, 100000);
        devisionFactors.put(Currency.JPY, 1000);
        devisionFactors.put(Currency.AUD, 100000);
        devisionFactors.put(Currency.CAD, 100000);
        devisionFactors.put(Currency.CHF, 100000);
        devisionFactors.put(Currency.CNY, 100000);
        devisionFactors.put(Currency.DKK, 100000);
        devisionFactors.put(Currency.HKD, 100000);
        devisionFactors.put(Currency.PLN, 100000);
        devisionFactors.put(Currency.RUB, 100000);
        devisionFactors.put(Currency.SEK, 1000);
        devisionFactors.put(Currency.SGD, 100000);
        devisionFactors.put(Currency.THB, 100000);

    }

    public void setPrintHTTPResponse(boolean resp) {
        this.printHttpResponse = resp;
    }

    @Override
    public String getLag() {
        String urlPath = API_LAG;
        HashMap<String, String> query_args = new HashMap<>();
        /*Params
        * 
        */
        String queryResult = query(urlPath, query_args);
        /*Sample result
        * the lag in milliseconds
        */
        JSONParser parser = new JSONParser();
        String lag = "";
        try {
            JSONObject httpAnswerJson = (JSONObject) (parser.parse(queryResult));
            JSONObject dataJson = (JSONObject) httpAnswerJson.get("data");
            lag = (String) dataJson.get("lag_text");
        } catch (ParseException ex) {
            Logger.getLogger(MtGox.class.getName()).log(Level.SEVERE, null, ex);
        }
        return lag;
    }

    @Override
    public String withdrawBTC(double amount, String dest_address) { //TODO
        String urlPath = API_WITHDRAW;
        HashMap<String, String> query_args = new HashMap<>();
        /*Params
         * address : Target bitcoin address
         * amount_int : Amount of bitcoins to withdraw
         * fee_int : Fee amount to be added to transaction (optional), maximum 0.01 BTC
         * no_instant : Setting this parameter to 1 will prevent transaction from being processed internally, and force usage of the bitcoin blockchain even if receipient is also on the system
         * green : Setting this parameter to 1 will cause the TX to use MtGoxs green address
         */
        query_args.put("amount_int", Long.toString(Math.round(amount * devisionFactors.get(Currency.BTC))));
        query_args.put("address", dest_address);
        String queryResult = query(urlPath, query_args);

        /*Sample result
        * On success, this method will return the transaction id (in offser trx ) which will contain either the bitcoin transaction id as hexadecimal or a UUID value in case of internal transfer.
        */

        JSONParser parser = new JSONParser();
        try {
            JSONObject obj2 = (JSONObject) (parser.parse(queryResult));
            //JSONObject data = (JSONObject)obj2.get("data"); //TODO

        } catch (ParseException ex) {
            Logger.getLogger(MtGox.class.getName()).log(Level.SEVERE, null, ex);
        }

        return ""; //TODO Edit
    }

    @Override
    public String sellBTC(double amount) {
        return placeOrder("sell", Math.round(amount * devisionFactors.get(Currency.BTC)));
    }

    @Override
    public String buyBTC(double amount) {
        return placeOrder("buy", Math.round(amount * devisionFactors.get(Currency.BTC)));
    }

    public String placeOrder(String type, long amount_int) {

        String toReturn = "";
        String result = "";
        String data = "";
        String urlPath = API_ADD_ORDER;
        HashMap<String, String> query_args = new HashMap<>();
        /*Params
         * type : {ask (sell) | bid(buy) }
         * amount_int : amount of BTC to buy or sell, as an integer
         * price_int : The price per bitcoin in the auxiliary currency, as an integer, optional if you wish to trade at the market price
         */
        query_args.put("amount_int", Long.toString(amount_int));
        if (type.equals("sell"))
            query_args.put("type", "ask");
        else
            query_args.put("type", "bid");

        String queryResult = query(urlPath, query_args);
        /*Sample result
        * {"result":"success","data":"abc123-def45-.."} 
        */
        JSONParser parser = new JSONParser();
        try {
            JSONObject obj2 = (JSONObject) (parser.parse(queryResult));
            result = (String) obj2.get("result");
            data = (String) obj2.get("data");

            //lastPriceArray[0] = (Double)obj2.get("last"); //USD

        } catch (ParseException ex) {
            Logger.getLogger(MtGox.class.getName()).log(Level.SEVERE, null, ex);
        }

        if (result.equals("success")) {
            toReturn = "executed : " + data;
        } else
            toReturn = "not executed : " + data; //TODO test this branch

        return toReturn; //TODO change
    }

    @Override
    public double[] getBalance() {
        String urlPath = API_GET_INFO;
        HashMap<String, String> query_args = new HashMap<>();

        /*Params
         * 
         */
        double[] balanceArray = new double[3];

        String queryResult = query(urlPath, query_args);
        /*Sample result
        * {
        *   "data": {
        *       "Created": "yyyy-mm-dd hh:mm:ss",
        *       "Id": "abc123",
        *       "Index": "123",
        *       "Language": "en_US",
        *       "Last_Login": "yyyy-mm-dd hh:mm:ss",
        *       "Login": "username",
        *       "Monthly_Volume":                   **Currency Object**,
        *       "Trade_Fee": 0.6,
        *       "Rights": ['deposit', 'get_info', 'merchant', 'trade', 'withdraw'],
        *       "Wallets": {
        *           "BTC": {
        *               "Balance":                  **Currency Object**,
        *               "Daily_Withdraw_Limit":     **Currency Object**,
        *               "Max_Withdraw":             **Currency Object**,
        *               "Monthly_Withdraw_Limit": null,
        *               "Open_Orders":              **Currency Object**,
        *               "Operations": 1,
        *           },
        *           "USD": {
        *               "Balance":                  **Currency Object**,
        *               "Daily_Withdraw_Limit":     **Currency Object**,
        *               "Max_Withdraw":             **Currency Object**,
        *               "Monthly_Withdraw_Limit":   **Currency Object**,
        *               "Open_Orders":              **Currency Object**,
        *               "Operations": 0,
        *           },
        *           "JPY":{...}, "EUR":{...},
        *           // etc, depends what wallets you have
        *       },
        *   },
        *   "result": "success"
        * }
        */

        JSONParser parser = new JSONParser();
        try {
            JSONObject httpAnswerJson = (JSONObject) (parser.parse(queryResult));
            JSONObject dataJson = (JSONObject) httpAnswerJson.get("data");
            JSONObject walletsJson = (JSONObject) dataJson.get("Wallets");

            JSONObject BTCwalletJson = (JSONObject) ((JSONObject) walletsJson.get("BTC")).get("Balance");

            String BTCBalance = (String) BTCwalletJson.get("value");

            boolean hasDollars = true;
            boolean hasEuros = true;
            JSONObject USDwalletJson, EURwalletJson;
            String USDBalance = "", EURBalance = "";

            try {
                USDwalletJson = (JSONObject) ((JSONObject) walletsJson.get("USD")).get("Balance");
                USDBalance = (String) USDwalletJson.get("value");
            } catch (Exception e) {
                hasDollars = false;
            }

            try {
                EURwalletJson = (JSONObject) ((JSONObject) walletsJson.get("EUR")).get("Balance");
                EURBalance = (String) EURwalletJson.get("value");
            } catch (Exception e) {
                hasEuros = false;
            }

            balanceArray[0] = Double.parseDouble(BTCBalance); //BTC

            if (hasDollars)
                balanceArray[1] = Double.parseDouble(USDBalance); //USD
            else
                balanceArray[1] = -1; //Account does not have USD wallet

            if (hasEuros)
                balanceArray[2] = Double.parseDouble(EURBalance); //EUR
            else
                balanceArray[2] = -1; //Account does not have EUR wallet

        } catch (ParseException ex) {
            Logger.getLogger(MtGox.class.getName()).log(Level.SEVERE, null, ex);
        }

        return balanceArray;
    }

    public String query(String path, HashMap<String, String> args) {
        GoxService query = new GoxService(path, args, keys);
        String queryResult = query.executeQuery();
        return queryResult;
        //TODO should be done by a different thread ...
    }

    public double getLastPrice(Currency cur) {

        String urlPath = getTickerPath(cur, true);
        long divideFactor = devisionFactors.get(cur);
        HashMap<String, String> query_args = new HashMap<>();

        /*Params :
        * No params required
        */
        String queryResult = query(urlPath, query_args);

        /* Result sample :
        *{
        *   "result":"success",
        *   "data": {
        *       "high":       **Currency Object - USD**,
        *       "low":        **Currency Object - USD**,
        *       "avg":        **Currency Object - USD**,
        *       "vwap":       **Currency Object - USD**,
        *       "vol":        **Currency Object - BTC**,
        *       "last_local": **Currency Object - USD**,
        *       "last_orig":  **Currency Object - ???**,
        *       "last_all":   **Currency Object - USD**,
        *       "last":       **Currency Object - USD**,
        *       "buy":        **Currency Object - USD**,
        *       "sell":       **Currency Object - USD**,
        *       "now":        "1364689759572564"
        *   }
        *}
        */
        JSONParser parser = new JSONParser();
        double last = 0;
        try {
            JSONObject httpAnswerJson = (JSONObject) (parser.parse(queryResult));
            JSONObject dataJson = (JSONObject) httpAnswerJson.get("data");
            JSONObject lastJson = (JSONObject) dataJson.get("last");
            String last_String = (String) lastJson.get("value");
            last = Double.parseDouble(last_String);

        } catch (ParseException ex) {
            Logger.getLogger(MtGox.class.getName()).log(Level.SEVERE, null, ex);
        }
        return last;
    }

    private class GoxService {
        protected String path;
        protected HashMap args;
        protected ApiKeys keys;

        public GoxService(String path, HashMap<String, String> args, ApiKeys keys) {
            this.path = path;
            this.args = args;
            this.keys = keys;
        }

        //Build the query string given a set of query parameters
        private String buildQueryString(HashMap<String, String> args) {
            String result = new String();
            for (String hashkey : args.keySet()) {
                if (result.length() > 0)
                    result += '&';
                try {
                    result += URLEncoder.encode(hashkey, ENCODING) + "="
                            + URLEncoder.encode(args.get(hashkey), ENCODING);
                } catch (Exception ex) {
                    Logger.getLogger(MtGox.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            return result;
        }

        private String signRequest(String secret, String hash_data) {
            String signature = "";
            try {
                Mac mac = Mac.getInstance(SIGN_HASH_FUNCTION);
                SecretKeySpec secret_spec = new SecretKeySpec(Base64.decodeBase64(secret), SIGN_HASH_FUNCTION);
                mac.init(secret_spec);
                signature = Base64.encodeBase64String(mac.doFinal(hash_data.getBytes()));
            } catch (NoSuchAlgorithmException | InvalidKeyException e) {
                Logger.getLogger(MtGox.class.getName()).log(Level.SEVERE, null, e);
            }
            return signature;
        }

        private String executeQuery() {
            String answer = "";
            boolean httpError = false;
            HttpsURLConnection connection = null;
            String nonce = String.valueOf(System.currentTimeMillis()) + "000";
            try {
                // add nonce and build arg list
                args.put("nonce", nonce);
                String post_data = buildQueryString(args);
                String hash_data = path + "\0" + post_data; //Should be correct

                // args signature with apache cryptografic tools
                String signature = signRequest(keys.getPrivateKey(), hash_data);

                // build URL
                URL queryUrl = new URL(API_BASE_URL + path);
                // create and setup a HTTP connection
                connection = (HttpsURLConnection) queryUrl.openConnection();

                connection.setRequestMethod("POST");

                connection.setRequestProperty("User-Agent", "Advanced-java-client API v2");
                connection.setRequestProperty("Rest-Key", keys.getApiKey());
                connection.setRequestProperty("Rest-Sign", signature.replaceAll("\n", ""));

                connection.setDoOutput(true);
                connection.setDoInput(true);

                //Read the response

                DataOutputStream os = new DataOutputStream(connection.getOutputStream());
                os.writeBytes(post_data);
                os.close();

                BufferedReader br = null;
                boolean toLog = false;
                if (connection.getResponseCode() >= 400) {
                    httpError = true;//TODO , if HTTP error, do something else with output!
                    br = new BufferedReader(new InputStreamReader((connection.getErrorStream())));
                    toLog = true;
                } else
                    br = new BufferedReader(new InputStreamReader((connection.getInputStream())));

                String output;

                if (httpError)
                    System.err.println("Post Data: " + post_data);
                if (printHttpResponse)
                    System.out.println("Query to :" + path + " , HTTP response : \n"); //do not log unless is error > 400
                while ((output = br.readLine()) != null) {
                    if (printHttpResponse)
                        System.out.println(output);
                    answer += output;
                }
            }

            //Capture Exceptions
            catch (IllegalStateException ex) {
                System.err.println(ex);
            } catch (IOException ex) {
                System.err.println(ex);
            } finally {
                //close the connection, set all objects to null
                connection.disconnect();
                connection = null;
            }
            return answer;
        }
    }

    /**
    *
    * returns the string used to get the Ticker
    * @return the string you're searching for;)
    */
    private String getTickerPath(Currency cur, boolean fast) {
        return "BTC" + cur.toString() + "/MONEY/TICKER" + (fast ? "_FAST" : "");
    }

}