Java tutorial
/** * Java implementation for cryptocoin trading. * * Copyright (c) 2014 the authors: * * @author Andreas Rueckert <mail@andreas-rueckert.de> * @author Roland Schumacher <info@geniali.ch> * * 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. */ package de.andreas_rueckert.trade.site.cryptsy.client; import de.andreas_rueckert.MissingAccountDataException; import de.andreas_rueckert.NotYetImplementedException; import de.andreas_rueckert.persistence.PersistentProperty; import de.andreas_rueckert.persistence.PersistentPropertyList; import de.andreas_rueckert.trade.account.TradeSiteAccount; import de.andreas_rueckert.trade.CryptoCoinTrade; import de.andreas_rueckert.trade.account.TradeSiteAccountImpl; import de.andreas_rueckert.trade.currency.Currency; import de.andreas_rueckert.trade.currency.CurrencyImpl; import de.andreas_rueckert.trade.currency.CurrencyNotSupportedException; import de.andreas_rueckert.trade.currency.CurrencyPair; import de.andreas_rueckert.trade.currency.CurrencyPairImpl; import de.andreas_rueckert.trade.currency.CurrencyProvider; import de.andreas_rueckert.trade.Depth; import de.andreas_rueckert.trade.order.DepositOrder; import de.andreas_rueckert.trade.order.OrderStatus; import de.andreas_rueckert.trade.order.OrderType; import de.andreas_rueckert.trade.order.SiteOrder; import de.andreas_rueckert.trade.order.WithdrawOrder; import de.andreas_rueckert.trade.Price; import de.andreas_rueckert.trade.site.TradeSite; import de.andreas_rueckert.trade.site.TradeSiteImpl; import de.andreas_rueckert.trade.site.TradeSiteRequestType; import de.andreas_rueckert.trade.site.TradeSiteUserAccount; import de.andreas_rueckert.trade.Ticker; import de.andreas_rueckert.trade.Trade; import de.andreas_rueckert.trade.TradeDataNotAvailableException; import de.andreas_rueckert.util.HttpUtils; import de.andreas_rueckert.util.LogUtils; import de.andreas_rueckert.util.TimeUtils; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Hex; import net.sf.json.JSON; import net.sf.json.JSONArray; import net.sf.json.JSONException; import net.sf.json.JSONObject; /** * Main class for the cryptsy API. * * @see <a href="https://www.cryptsy.com/pages/api">Cryptsy API</a> * @see <a href="https://github.com/abwaters/cryptsy-api/blob/master/src/com/abwaters/cryptsy/Cryptsy.java">Cryptsy API java implementation</a> */ public class CryptsyClient extends TradeSiteImpl implements TradeSite { // Static variables /** * The domain of the service. */ public static String DOMAIN = "cryptsy.com"; // Instance variables /** * The default user account, if no other account is given for a authenticated request. */ private TradeSiteUserAccount _defaultUserAccount = null; /** * A mapping for currency pairs and ID's. */ private Map<CurrencyPair, String> _marketIDs = null; /** * I'll just use the same code for the nonce as in the btc-e implementation. */ private static long _nonce; /** * The URL for public API calls. */ private String _public_url; // Constructors /** * Create a new connection to the cryptsy trading site. */ public CryptsyClient() { super(); _name = "Cryptsy"; // The name of the trade site. _url = "https://api.cryptsy.com/api"; // The URL for authenticated API calls. _public_url = "http://pubapi2.cryptsy.com/"; // The URL for public API calls. // Create a unixtime nonce for the new API. _nonce = (TimeUtils.getInstance().getCurrentGMTTimeMicros() / 1000000); // We would normally set the supported currency pairs here, // but to request them from the cryptsy exchange (there are too // many to set them manually), we'd need a user account. // So we have to delay this setting a bit and do the actual // request when the pairs are requested from the user. // Update: wrote a version to fetch the currency pairs via public API. //_supportedCurrencyPairs = getSupportedCurrencyPairs(); } // Methods /** * Execute a authenticated query on cryptsy. * * @param method The method to execute. * @param arguments The arguments to pass to the server. * @param userAccount The user account on the exchange, or null if the default account should be used. * @return The returned data as JSON or null, if the request failed. */ private final JSON authenticatedHTTPRequest(String method, Map<String, String> arguments, TradeSiteUserAccount userAccount) { HashMap<String, String> headerLines = new HashMap<String, String>(); // Create a new map for the header lines. Mac mac; SecretKeySpec key = null; String accountKey = null; // The used key of the account. String accountSecret = null; // The used secret of the account. // Try to get an account key and secret for the request. if (userAccount != null) { accountKey = userAccount.getAPIkey(); accountSecret = userAccount.getSecret(); } else if (_defaultUserAccount != null) { // Use the default values from the API implementation. accountKey = _defaultUserAccount.getAPIkey(); accountSecret = _defaultUserAccount.getSecret(); } // Check, if account key and account secret are available for the request. if (accountKey == null) { throw new MissingAccountDataException("Public key not available for authenticated request to " + _name); } if (accountSecret == null) { throw new MissingAccountDataException( "Private key not available for authenticated request to " + _name); } if (arguments == null) { // If the user provided no arguments, just create an empty argument array. arguments = new HashMap<String, String>(); } arguments.put("method", method); // Add the method to the post data. arguments.put("nonce", "" + ++_nonce); // Add the dummy nonce. // Convert the arguments into a string to post them. String postData = ""; for (Iterator argumentIterator = arguments.entrySet().iterator(); argumentIterator.hasNext();) { Map.Entry argument = (Map.Entry) argumentIterator.next(); if (postData.length() > 0) { postData += "&"; } postData += argument.getKey() + "=" + argument.getValue(); } // Create a new secret key try { key = new SecretKeySpec(accountSecret.getBytes("UTF-8"), "HmacSHA512"); } catch (UnsupportedEncodingException uee) { System.err.println("Unsupported encoding exception: " + uee.toString()); return null; } // Create a new mac try { mac = Mac.getInstance("HmacSHA512"); } catch (NoSuchAlgorithmException nsae) { System.err.println("No such algorithm exception: " + nsae.toString()); return null; } // Init mac with key. try { mac.init(key); } catch (InvalidKeyException ike) { System.err.println("Invalid key exception: " + ike.toString()); return null; } // Add the key to the header lines. headerLines.put("Key", accountKey); // Encode the post data by the secret and encode the result as base64. try { headerLines.put("Sign", Hex.encodeHexString(mac.doFinal(postData.getBytes("UTF-8")))); } catch (UnsupportedEncodingException uee) { System.err.println("Unsupported encoding exception: " + uee.toString()); return null; } // Now do the actual request String requestResult = HttpUtils.httpPost(_url, headerLines, postData); if (requestResult != null) { // The request worked try { // Convert the HTTP request return value to JSON to parse further. JSONObject jsonResult = JSONObject.fromObject(requestResult); // Check, if the request was successful int success = jsonResult.getInt("success"); if (success == 0) { // The request failed. String errorMessage = jsonResult.getString("error"); LogUtils.getInstance().getLogger().error(_name + " trade API request failed: " + errorMessage); return null; } else { // Request succeeded! // Try to figure, what the return actually is: json object or json array? // Test, if the return value is an JSONArray. JSONArray arrayReturn = jsonResult.optJSONArray("return"); if (arrayReturn != null) { // Converting the result into a JSON array worked, so return it. return arrayReturn; } // Now test, if the return value is a JSONObject. JSONObject objectReturn = jsonResult.optJSONObject("return"); if (objectReturn != null) { // Converting the result into a JSON object worked, so return it. return objectReturn; } if (!jsonResult.has("return")) { // Has this object no return value? LogUtils.getInstance().getLogger() .error(_name + " trade API request '" + method + "' has no return value."); return null; // No reasonable return value possible. } else { // There is a return value, but it's neither an array or a object, so we cannot convert it. LogUtils.getInstance().getLogger().error(_name + " trade API request '" + method + "' has a return value, that is neither a JSONObject or a JSONArray. Don't know, what to do with it."); return null; // Not much we can do here... } } } catch (JSONException je) { System.err.println("Cannot parse json request result: " + je.toString()); return null; // An error occured... } } return null; // The request failed. } /** * Cancel an order on the trade site. * * @param order The order to cancel. * @return true, if the order was canceled. False otherwise. */ public boolean cancelOrder(SiteOrder order) { throw new NotYetImplementedException("Cancelling an order is not yet implemented for " + this._name); } /** * Execute an order on the trade site. * * @param order The order to execute. * @return The new status of the order. */ public synchronized OrderStatus executeOrder(SiteOrder order) { throw new NotYetImplementedException("Executing an order is not yet implemented for cryptsy"); } /** * Get the current funds of a user. * * @param userAccount The account of the user on the exchange. Null, if the default account should be used. * @return The accounts with the current balance as a collection of Account objects, or null if the request failed. */ public Collection<TradeSiteAccount> getAccounts(TradeSiteUserAccount userAccount) { // Try to get some info on the user (including the current funds). JSONObject jsonResponse = (JSONObject) authenticatedHTTPRequest("getinfo", null, userAccount); if (jsonResponse != null) { JSONObject jsonFunds = jsonResponse.getJSONObject("balances_available_btc"); // An array for the parsed funds. ArrayList<TradeSiteAccount> result = new ArrayList<TradeSiteAccount>(); // Now iterate over all the currencies in the funds. for (Iterator currencyIterator = jsonFunds.keys(); currencyIterator.hasNext();) { String currentCurrency = (String) currencyIterator.next(); // Get the next currency. BigDecimal balance = new BigDecimal(jsonFunds.getString(currentCurrency)); // Get the balance for this currency. result.add(new TradeSiteAccountImpl(balance, CurrencyProvider.getInstance().getCurrencyForCode(currentCurrency.toUpperCase()), this)); } return result; // Return the array with the accounts. } return null; // The request failed. } /** * Get the market depth as a Depth object. * * @param currencyPair The queried currency pair. * @throws TradeDataNotAvailableException if the depth is not available. */ public Depth getDepth(CurrencyPair currencyPair) throws TradeDataNotAvailableException { getSupportedCurrencyPairs(); // If the request for the depth is allowed at the moment if (isRequestAllowed(TradeSiteRequestType.Depth)) { if (!isSupportedCurrencyPair(currencyPair)) { throw new CurrencyNotSupportedException("Currency pair: " + currencyPair.toString() + " is currently not supported on " + this._name); } } // Compute the URL for the orderbook request. // I use the public methods for now, since I hope, that they scale better // than the authenticated methods. String url = _public_url + "api.php?method=singleorderdata&marketid=" + getMarketIdForCurrencyPair(currencyPair); String requestResult = HttpUtils.httpGet(url); // Do the actual request. if (requestResult != null) { // Request sucessful? try { // Convert the HTTP request return value to JSON to parse further. return new CryptsyDepth(JSONObject.fromObject(requestResult), currencyPair, this); } catch (JSONException je) { System.err.println("Cannot parse " + this._name + " depth return: " + je.toString()); throw new TradeDataNotAvailableException("cannot parse data from " + this._name); } } throw new TradeDataNotAvailableException(this._name + " server did not respond to depth request"); } /** * Get the depths for all the currency pairs. They are fetched with a single orderbook request, * so only 1 request is done to the cryptsy exchange. * * @return All the depths as a list. */ public List<Depth> getDepths() { // The URL to fetch all the orderbooks. String url = _public_url + "api.php?method=orderdatav2"; // Do the actual request on vircurex. String requestResult = HttpUtils.httpGet(url); if (requestResult != null) { // Request sucessful? try { // Convert the result to a JSON object. JSONObject resultJSON = JSONObject.fromObject(requestResult); // Check the status value for errors. int successStatus = resultJSON.getInt("success"); if (successStatus != 1) { // If the request was not successful throw new TradeDataNotAvailableException(_name + " returned an error"); } // Get the actual market data. JSONObject marketDataJSON = resultJSON.getJSONObject("return"); // Create a buffer for the results. List<Depth> resultBuffer = new ArrayList<Depth>(); // Loop over the entries of this object. for (JSONObject currentMarketJSON : ((Map<String, JSONObject>) marketDataJSON).values()) { try { // Parse the market data including the currency pair. Depth currentDepth = new CryptsyDepth(currentMarketJSON, this); // Add the depth to the buffer. resultBuffer.add(currentDepth); } catch (CurrencyNotSupportedException cnse) { // The depth already logs the error, so just continue here. continue; // Contiue with the next market. } } // Return the buffer. return resultBuffer; } catch (JSONException je) { LogUtils.getInstance().getLogger() .error("Cannot parse " + _name + " depths return: " + je.toString()); } } // Since fetching the depths failed, just throw an exception, that the trade data are not available. throw new TradeDataNotAvailableException("Fetching the depths from " + this._name + " returned null"); } /** * Get the current market depths sequentially via 1 market call. So the sequence is just 1 call long... :-) * This method is identical to the coins-e method with the same name. * * @param currencyPairs The currency pairs to query. * @return The current market depths for the given currency pairs. * @throws TradeDataNotAvailableException if to many depths are not available. */ public List<Depth> getDepthsSequentially(CurrencyPair[] currencyPairs) throws TradeDataNotAvailableException { // Just get all the depths and then extract the requested depths from there. Since // we will usually request many depths, this should be a fastest solution. List<Depth> fetchedDepths = getDepths(); // Now we have to resort those depths according to the order of the requested currency pairs. // To do so, I convert the depth array to a linked list, and remove any found depth from there, // so the list should get shorter with any found depth. List<Depth> fetchedDepthList = fetchedDepths; // Now create an array for the result, that will be sorted according to the passed currency pair array. List<Depth> result = new ArrayList<Depth>(); int currentIndex = 0; // Loop over the currecy pairs parameter and add the returned depths to the result array. currency_pair_loop: for (CurrencyPair currentCurrencyPair : currencyPairs) { // Search this depth in the list of fetched depths. for (int currentListIndex = 0; currentListIndex < fetchedDepthList.size();) { Depth fetchedDepth = fetchedDepthList.get(currentListIndex); if (fetchedDepth == null) { // If there was no depth returned, remove this entry from the search list. fetchedDepthList.remove(currentListIndex); // If there was actually a depth returned and it's for the currency pair , we are // currently processing.. } else if (fetchedDepth.getCurrencyPair().equals(currentCurrencyPair)) { // Add this depth to the result. result.add(currentIndex++, fetchedDepth); // And remove this depth from the list of searched depths, to make the searching // a bit quicker in the next loop iteration. Currency pairs should never be requested // twice, so this should work. fetchedDepthList.remove(currentListIndex); // Now continue with the next currency pair. continue currency_pair_loop; } else { // Just continue search the list of depth returns. ++currentListIndex; } } // If the requested currency pair was not in the list of returned depths, just set it to null // in the result for now. ToDo: throw a TradeDataNotAvailableException if more than x depths // are not available? result.add(currentIndex++, null); } // Return the array with the result. return result; } /** * Get the fee for an order in the resulting currency. * Synchronize this method, since several users might use this method with different * accounts and therefore different fees via a single API implementation instance. * * @param order The order to use for the fee computation. * @return The fee in the resulting currency (currency value for buy, payment currency value for sell). */ public synchronized Price getFeeForOrder(SiteOrder order) { if (order instanceof WithdrawOrder) { if (order.getCurrencyPair().getCurrency().hasCode("BTC")) { return new Price("0.0"); // No clue what cryptsy charges for withdrawals? } else { // System.out.println( "Compute withdaw fees for currencies other than btc"); throw new CurrencyNotSupportedException("Cannot compute fee for this order: " + order.toString()); } } else if (order instanceof DepositOrder) { Currency depositedCurrency = ((DepositOrder) order).getCurrency(); // Most cryptocoin deposits are free, it seems? if (depositedCurrency.hasCode(new String[] { "BTC", "LTC" })) { return new Price("0.0", depositedCurrency); } else { throw new NotYetImplementedException("Deposit fees are not implemented for trade site " + getName() + " and currency " + depositedCurrency.getCode()); } } else if (order.getOrderType() == OrderType.BUY) { // This seems to be a trade order // Since buy and sell have different fees, I split the trade orders here // in 2 conditions. // According to // @see https://cryptsy.freshdesk.com/support/articles/173970-what-are-cryptsy-s // Cryptsy charges 0.2% for buys and 0.3% for sales. return new Price(order.getAmount().multiply(new BigDecimal("0.002")), order.getCurrencyPair().getCurrency()); } else if (order.getOrderType() == OrderType.SELL) { // Also a trade order. return new Price(order.getAmount().multiply(new BigDecimal("0.003")), order.getCurrencyPair().getCurrency()); } return null; // Should never be reached. } /** * Get the market ID for a given currency pair. * * @param currencyPair The traded currency pair. * @return The market ID as a string. * @throws CurrencyNotSupportedException if there is no ID for this market (= currency pair). */ private final String getMarketIdForCurrencyPair(CurrencyPair currencyPair) throws CurrencyNotSupportedException { // Try to get the market ID from the local map. String marketID = _marketIDs.get(currencyPair); // If this pair is not in the map, throw an exception. if (marketID == null) { throw new CurrencyNotSupportedException( "The currency pair " + currencyPair.toString() + " is not supported at " + this._name); } // If the market ID from the map is not null, return it. return marketID; } /** * Get the shortest allowed requet interval in microseconds. * * @return The shortest allowed request interval in microseconds. */ public long getMinimumRequestInterval() { return getUpdateInterval(); } /** * Get the section name in the global property file. * <p> * The name of the property section as a String. */ public String getPropertySectionName() { return "Cryptsy"; } /** * Get the open orders on this trade site. * * @param userAccount The account of the user on the exchange. Null, if the default account should be used. * @return The open orders as a collection, or null if the request failed. */ public Collection<SiteOrder> getOpenOrders(TradeSiteUserAccount userAccount) { throw new NotYetImplementedException("Getting the open orders is not yet implemented for cryptsy"); } /** * Get the settings of the cryptsy client. * * @return The setting of the cryptsy client as a list. */ public PersistentPropertyList getSettings() { // Get the settings from the base class. PersistentPropertyList result = super.getSettings(); // If there is a default user account yet, get the public API key from it (might be null, though). result.add(new PersistentProperty("Public key", null, _defaultUserAccount != null ? _defaultUserAccount.getAPIkey() : null, 6)); // Add the private key (I use the secret field from the account class to store it). result.add(new PersistentProperty("Private key", null, _defaultUserAccount != null ? _defaultUserAccount.getSecret() : null, 5)); return result; } /** * Get the supported currency pairs of this trading site. * * @return The supported currency pairs of this trading site. */ public CurrencyPair[] getSupportedCurrencyPairs() { // Check, if the available markets were already requested from the cryptsy exchange. if (_marketIDs == null) { // If not, request them from the cryptsy exchange. _marketIDs = requestMarketIDs(); if (_marketIDs != null) { // Get a set with the keys (= the currency pairs). Set<CurrencyPair> currencyPairs = _marketIDs.keySet(); // Convert the set to an array, so we can access the pairs quicker in future requests. _supportedCurrencyPairs = currencyPairs.toArray(new CurrencyPair[currencyPairs.size()]); } } // Return the array with the pairs. return _supportedCurrencyPairs; } /** * Get the current ticker from the cryptsy API. * * @param currencyPair The currency pair to query. * * @return The current cryptsy ticker. * * @throws TradeDataNotAvailableException if the ticker is not available. */ public Ticker getTicker(CurrencyPair currencyPair) throws TradeDataNotAvailableException { throw new NotYetImplementedException("Getting the ticker is not yet implemented for cryptsy"); } /** * Get a list of recent trades. * * @param since_micros The GMT-relative epoch in microseconds. * @param currencyPair The currency pair to query. * @return The trades as a list of Trade objects. */ public List<Trade> getTrades(long since_micros, CurrencyPair currencyPair) throws TradeDataNotAvailableException { throw new NotYetImplementedException("Getting the trades is not yet implemented for cryptsy"); } /** * Get the interval, in which the trade site updates it's depth, ticker etc. * in microseconds. * * @return The update interval in microseconds. */ public long getUpdateInterval() { return 15L * 1000000L; // Just a default value for low volume. } /** * Check, if some request type is allowed at the moment. Most * trade site have limits on the number of request per time interval. * * @param requestType The type of request (trades, depth, ticker, order etc). * @return true, if the given type of request is possible at the moment. */ public boolean isRequestAllowed(TradeSiteRequestType requestType) { return true; // Just a dummy for now... } /** * Request the markets from the website. * * @return A map of currency pairs with their associated market IDs. */ private final Map<CurrencyPair, String> requestMarketIDs() { // Create a new buffer for the result. Map<CurrencyPair, String> resultBuffer = new HashMap<CurrencyPair, String>(); // Request the markets from the website. // The result should automagically be converted to a JSONArray. // ToDo: use the account from the actual current user? JSONArray JSONresult = (JSONArray) authenticatedHTTPRequest("getmarkets", null, null); // Iterate over the array and convert each market from json to a currency pair. for (int i = 0; i < JSONresult.size(); i++) { // Get the current market as a JSON object. JSONObject currentMarket = JSONresult.getJSONObject(i); // Get the market ID for this market. String marketID = "" + currentMarket.getInt("marketid"); // Get the currency of this market. Currency currency = CurrencyProvider.getInstance() .getCurrencyForCode(currentMarket.getString("primary_currency_code").toUpperCase()); // Get the payment currency of this market. Currency paymentCurrency = CurrencyProvider.getInstance() .getCurrencyForCode(currentMarket.getString("secondary_currency_code").toUpperCase()); // Add the currency pair with the ID to the result buffer. resultBuffer.put(new CurrencyPairImpl(currency, paymentCurrency), marketID); } return resultBuffer; // Return the buffer with the result. } /** * Request the markets from the website via the public API. * * @return A map of currency pairs with their associated market IDs. */ private final Map<CurrencyPair, String> requestMarketIDsPublic_() { // Create a new buffer for the result. Map<CurrencyPair, String> resultBuffer = new HashMap<CurrencyPair, String>(); // Create the URL to request some market data. String url = _public_url + "api.php?method=marketdatav2"; String requestResult = HttpUtils.httpGet(url); // Do the actual request. if (requestResult != null) { // Request sucessful? try { // Convert the data to JSON. JSONObject resultJSON = JSONObject.fromObject(requestResult); // Check the status value for errors. int successStatus = resultJSON.getInt("success"); if (successStatus != 1) { // If the request was not successful throw new TradeDataNotAvailableException( _name + " returned an error while requesting the market IDs via public API"); } // Get the actual market data. JSONObject marketDataJSON = resultJSON.getJSONObject("return").getJSONObject("markets"); // Iterate over the keys and convert each market. for (Iterator<String> keys = marketDataJSON.keys(); keys.hasNext();) { // Get the name of the next market as the key. String marketKey = (String) keys.next(); // Get the current market as a JSON object. JSONObject currentMarket = marketDataJSON.getJSONObject(marketKey); // Get the market ID for this market. String marketID = "" + currentMarket.getInt("marketid"); // Get the currency of this market. Currency currency = CurrencyProvider.getInstance() .getCurrencyForCode(currentMarket.getString("primarycode").toUpperCase()); // Get the payment currency of this market. Currency paymentCurrency = CurrencyProvider.getInstance() .getCurrencyForCode(currentMarket.getString("secondarycode").toUpperCase()); // Add the currency pair with the ID to the result buffer. resultBuffer.put(new CurrencyPairImpl(currency, paymentCurrency), marketID); } return resultBuffer; // Return the buffer with the result. } catch (JSONException je) { // Parse error while parsing the markets. LogUtils.getInstance().getLogger() .error("Cannot parse " + _name + " market ID return: " + je.toString()); return null; // Markets not found. } } LogUtils.getInstance().getLogger().error("Cannot fetch " + _name + " market IDs"); return null; // Nothing to return. } /** * Set the default user account for authenticated requests, if no user account is given. * * @param defaultUserAccount The new default user account. */ public void setDefaultUserAccount(TradeSiteUserAccount defaultUserAccount) { // Store the user account in the instance. _defaultUserAccount = defaultUserAccount; } /** * Set new settings for the cryptsy client. * * @param settings The new settings for the cryptsy client. */ public void setSettings(PersistentPropertyList settings) { super.setSettings(settings); String key = settings.getStringProperty("Public key"); if (key != null) { // Get the public API key from the settings and store it in the default user account. if (_defaultUserAccount == null) { // If there is no default user account yet, _defaultUserAccount = new TradeSiteUserAccount(); // create one. } // Store the public API key in the settings. _defaultUserAccount.setAPIkey(key); } String secret = settings.getStringProperty("Private key"); if (secret != null) { // Get the private API key from the settings and store it in the default user account. if (_defaultUserAccount == null) { // If there is no default user account yet, _defaultUserAccount = new TradeSiteUserAccount(); // create one. } // Store the public API key in the settings. _defaultUserAccount.setSecret(secret); } } }