The Mount Gox Command Line Interface

Standard

What is the Mount Gox Command Line Interface aka mgcli

It is a command line tool to interact with the Bitcoin exchange Mount Gox. The command mgcli sellbtc 3.6 4,55 puts the order of selling 3.6 Bitcoin for the price of 4,5$ per BTC. With mgcli orders it shows you all your orders. The output of mgcli help looks like this:

  Mount Gox Command Line Interface V0.7
 ---------------------------------------

  syntax: mgcli <command> [options]
  available commands:
    help
    ticker
    info
    orders
    cancelorder <oid>
    buybtc <amount> [price]   (amount in BTC, price (optional) in USD)
    sellbtc <amount> [price]   (amount in BTC, price (optional) in USD)
    history <currency=BTC|USD>
    depth (only USD)
    trades (only USD)

  LIABILITY: The author of this program does not assume any liability for any
  effects caused by this program. Use it on your own responsibility.

  Get updates and more information at: mgcli.wordpress.com
  Submit bug reports and feedback at: https://mgcli.wordpress.com/feedback

  If this program is useful to you, consider a Bitcoin donation to:
  1EZ3gMP2VM3iEak7foSLmyeJJVj47NUWbe

You got the point.

What it is not?

It is not a trading bot or a tool to support you decision making. You can just lots of the things you can do at https://mtgox.com fast and easy with a scala-based command line tool.

Development backgrouds

After several years of of abstinence to software development I decided to programm something interesting. One spin off of this project is this hopefully helpful tool. I implemented it in Scala, a new and modern programming language. Is is also a programming exercise around bitcoin, something i am very interested in.

Scala is running at the Java Virtual Machine and can use java libraries. A new and modern language with tons of working libraries.

Advertisements

New Version mgcli V0.8 released

Standard

I made some smaller improvements and bugfixes:

  • The command interpreter is now editable in mgcli.xml. Use the two commandInterpreter tags to define the command interpreter. With this improvement mgcli should run on non-Windows systems.
  • Some smaller corrections and formatting regarding the text output.

Download the new version here.

Source code: requestGox.php

Standard

This script handles the communication with Mout Gox. It is taken from here.

<?php

function mtgox_query($path, $params) {
    // API settings
    $key = getenv("surgekey");
    $secret = getenv("surgesecret");

    $mt = explode(' ', microtime());
    $req = array();
    $req['nonce'] = $mt[1].substr($mt[0], 2, 6);

    // generate the POST data string
    $post_data = http_build_query($req, '', '&');
    if (getenv("surgeargs")) $post_data = $post_data.'&'.getenv("surgeargs");

    // generate the extra headers
    $headers = array(
        'Rest-Key: '.$key,
        'Rest-Sign: '.base64_encode(hash_hmac('sha512', $post_data, base64_decode($secret), true)),
    );

    // our curl handle (initialize if required)
    static $ch = null;
    if (is_null($ch)) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MtGox PHP client; '.php_uname('s').'; PHP/'.phpversion().')');
    }

    curl_setopt($ch, CURLOPT_URL, 'https://mtgox.com/api/'.$path);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); 

    //set debug proxy
    //curl_setopt($ch, CURLOPT_PROXY, '127.0.0.1:8888'); 

    // run the query
    $res = curl_exec($ch);

    if ($res == false) return "error while executing request to MtGox:\t\n".curl_error($ch);
    return $res;
}

//argv[1] should contain uri of mtgox api path like '0/info.php'
echo mtgox_query($argv[1], $argv[2]);

?>

Source Code: MountGoxCommandLineInterface.scala

Standard

Source code of MountGoxCommandLineInterface.scala. Not necessariely the actual version.

/******************************************************************   
  mgcli: main class
 ******************************************************************/

object mgcli {
    val version = "V0.8.2"
    val header1: String = "  Mount Gox Command Line Interface " + version
    val header2: String = " ---------------------------------------"

    def opHelp = {
        println("  syntax: mgcli <command> [options]")
        println("  available commands:")
        println("    help")
        println("    ticker")
        println("    info")
        println("    orders")
        println("    cancelorder <oid>")
        println("    buybtc <amount> [price]   (amount in BTC, price (optional) in USD)")
        println("    sellbtc <amount> [price]   (amount in BTC, price (optional) in USD)")
        println("    history <currency=BTC|USD>")
        println("    depth (only USD)")
        println("    trades (only USD)")
        println
        println("  ")
        println("  LIABILITY: The author of this program does not assume any liability for any")
        println("  effects caused by this program. Use it on your own responsibility. ")
        println("  ")
        println("  Get updates and more information at: mgcli.wordpress.com")
        println("  Submit bug reports and feedback at: https://mgcli.wordpress.com/feedback")
        println("  ")
        println("  If this program is useful to you, consider a Bitcoin donation to:")
        println("  1EZ3gMP2VM3iEak7foSLmyeJJVj47NUWbe")
        println
        println
    }

    def opCancel (oid: String) = {
        val gox: MountGox = new MountGox()
        var res: Tuple2[Boolean, String] = gox.existOrder(oid)
        if (res._1) {
            println(res._2)
            println("  Type 'yes' + <ENTER> to cancel this order ...")

            if (readLine == "yes") {
                println("  Canceling order " + oid + " ...")
                res = gox.cancelOrder(oid)
            }
                else println ("  Cancel order " + oid + " aborted ...")
        }
            else println(res._2)
    }

    def opSellBTC(args: Array[String]) = opTradeBTC(args, GTConsts.SELL)
    def opBuyBTC(args: Array[String]) = opTradeBTC(args, GTConsts.BUY)

    def opTradeBTC(args: Array[String], typ: Int) = {

      def tradeFunc(strAmount: String, strPrice: String): Tuple3[Boolean, String, String] = {
            typ match {
              case GTConsts.SELL => return new MountGox() sellBTC(strAmount, strPrice)
              case GTConsts.BUY =>  return new MountGox() buyBTC(strAmount, strPrice)
          }
      }

      var tradeType = ""
        typ match {
          case GTConsts.SELL => {
              tradeType = "sell"
          }

          case GTConsts.BUY => {
               tradeType = "buy"
          }
        }

        //check if price was given
        val priced: Boolean = (args.length > 3)

        var execute: Boolean = true
        var amount: Float = -1
        var price: Float = -1

        //check if arguments are valid numbers
        try { amount = args(2).toFloat } catch {
            case e => {
                println ("  Amount '" + args(2) + "' is not a valid number string. Aborting ...")
                execute = false
            }
        }        

        if (priced && execute) {
            try { price = args(3).toFloat    } catch {
                case e => {
                    println ("  Price '" + args(3) + "' is not a valid number string. Aborting ...")
                    execute = false
                }
            }        
        }

        // no price given, executing with empty price argument for market price order        
        if (execute && !priced) {
            println("  Do you want to " + tradeType + " " + amount +
                " BTC for the actual market price?")
            println("  Type 'yes' + <ENTER> to continue ...")
            if (readLine == "yes") {
                println("  Executing " +
                    tradeType + "ing " + amount + " BTC for the actual market price ...")
              var res = tradeFunc(amount.toString, "")
                println("  " + res._2 + ": " + res._3)
            }
                else println("  The " + tradeType + "ing of " +
                    amount + " BTC for the actual market price was aborted ...")
        }

        // price given, putting in order
        if (execute && priced) {
            println("  Do you want to " + tradeType + " " + amount + " BTC for " +
                price + " USD per BTC?")
            println("  Type 'yes' + <ENTER> to continue ...")
            if (readLine == "yes") {
                println("  Executing the " + tradeType +
                    "ing of " + amount + " BTC for " + price + " USD per BTC...")
                var res = tradeFunc(amount.toString, price.toString)
                println("  " + res._2 + ": " + res._3)
            }
                else println ("  The " + tradeType + "ing of " + amount +
                    " BTC for " + price + "USD per BTC aborted ...")
        }
    }

  def main(args: Array[String]) {
        goxCfg.envValue = "mgcliconfig"
        goxCfg.read

        println
        println(header1)
        println(header2)
        println
        //println("debug. a0: " + args(0) + "  a1: " +
            //args(1) + "  a2: " + args(2) + "  a3: " + args(3))

        if (args.length > 1) {
            args(1) match {
                case "help" => opHelp
                case "ticker" => new MountGox() printTickerData
                case "info" => new MountGox() printInfoData
                case "orders" => new MountGox() printOrders
                case "depth" => new MountGox() printDepth
                case "trades" => new MountGox() printAllTrades

                case "cancelorder" =>
                    if (args.length > 2) opCancel(args(2))
                        else println("  mgcli cancelorder: missing oid argument")

                case "buybtc" =>
                    if (args.length > 2) opBuyBTC(args)
                        else println("  mgcli buybtc: missing amount and/or price argument")

                case "sellbtc" =>
                    if (args.length > 2) opSellBTC(args)
                        else println(  "mgcli sellbtc: missing amount and/or price argument")

                case "history" =>
                    if ((args.length > 2) && ((args(2) == "BTC") || (args(2) == "USD"))) {
                        new MountGox() printHistory(args(2))
                    }
                        else println("  mgcli history: missing or misspelled currency. use 'USD' or 'BTC'")

                case _ => println("  get help with 'mgcli help'")                
            }
        }
            else println("  get help with 'mgcli help'")

        println
    }
}

Source Code: MarketHandling.scala

Standard

Source code of MarketHandling.scala. Not necessariely the actual version.

/******************************************************************   
  Classes to handle everything regarding the bitcoin exchange

 ******************************************************************/
import scala.xml._

import java.util.GregorianCalendar
import java.util.ArrayList
import java.util.Locale
import java.util.TimeZone
import java.text.SimpleDateFormat
import java.io._

import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

import com.google.gson._

import org.apache.http._
import org.apache.commons.codec.binary.Base64
import org.apache.commons.io.IOUtils

import org.apache.http.client.utils.URLEncodedUtils
import org.apache.http.message.BasicNameValuePair;

import scala.util.parsing.combinator._

/******************************************************************   
  object TrConsts:
      contains constants relevant for trading at Mt. Gox

 ******************************************************************/

object TrConsts {
    val BUY = 1
    val SELL = 2

    val fBTC = 1E8
    val fUSD = 1E5
}

/******************************************************************   
  abstract classes
      not really implemented yet    

 ******************************************************************/

abstract class MarketHandling {
    var marketName: String = "default name"
    var marketDescription: String = "default description"
}

abstract class TickerData {
    var ident: String = "default ticker data"
}

/******************************************************************   
  Mount Gox: object goxCfg
                       object to handle configuraiotn information

 ******************************************************************/

object goxCfg {

    var envValue = ""
    var xml: Elem = null

    // access to Mt. Gox
    var key = ""
    var secret = ""    

    //required for php execution
    var phpExec: String = ""
    var requestScript = ""
    var cmdInter = ""
    var cmdInterArgs = ""

    // inititalize configuration values
    def read {
        try {
            xml = scala.xml.XML.loadFile(System.getenv(envValue))

            phpExec = (xml \ "gox" \ "request" \ "phpExec").text
            requestScript = (xml \ "gox" \ "request" \ "phpScript").text
            cmdInter = (xml \ "gox" \ "request" \ "commandInterpreter").text
            cmdInterArgs = (xml \ "gox" \ "request" \ "commandInterpreterArguments").text

            key = (xml \ "gox" \ "login" \ "key").text
            secret = (xml \ "gox" \ "login" \ "secret").text
        } catch {
                case e => {
                    println("  Error handling configuration file:")
                    println(e.getMessage)
                    sys.exit
                }
        }
    }
}

/******************************************************************   
  Mount Gox: class MountGox
                       manages everything regarding Mount Gox
 ******************************************************************/

class MountGox extends MarketHandling {
    marketName = "Mount Gox"
    marketDescription = "Momentary the main Bitcoin exchange in the world ..."

    def printTickerData = new GoxTicker() print
    def printInfoData = new GoxInfo() print
    def printOrders = new GoxOrders() print
    def printDepth = new GoxDepth() print
    def printAllTrades = new GoxAllTrades() print
    def printHistory(currency: String) = new GoxHistory(currency) print

    def    existOrder(oid: String) = new GoxOrders() exists(oid)
    def    cancelOrder(oid: String) = new GoxOrders() cancel(oid)
    def getOrder(oid: String) = new GoxOrders() getOrder(oid)
    def cancelAllOrders = new GoxOrders() cancelAllOrders

    def sellBTC(amount: String, price:String) = new GoxTrades() sellBTC(amount, price)
    def buyBTC(amount: String, price:String) = new GoxTrades() buyBTC(amount, price)

    //gives history data as nested string list
    def getHistory(currency: String) = new GoxHistory(currency) getHistory
}

/******************************************************************   
  Mount Gox: object GoxUtils

 ******************************************************************/

object GoxUtils { 

    def formatGoxDate(d: Long): String = {
        var cal: GregorianCalendar = new GregorianCalendar(Locale.US)
        cal.setTimeInMillis(d * 1000)

        var sdf: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss")
        sdf.setTimeZone(TimeZone.getDefault())

        return sdf.format(cal.getTime())
    }

    def getFactor(currency: String): Double = {
        currency match {
          case "BTC" => return TrConsts.fBTC
          case "USD" => return TrConsts.fUSD
      }
    }
}

/******************************************************************   
  Mount Gox: class GoxInfo
                       data structures for info
 ******************************************************************/

class GoxInfo {

    //result [goxUser, BTC asFloat, USD asFloat, BTC asLong, USD asLong]
    def getData: Tuple5[String, Float, Float, Long, Long] = {
        requestData
        return(
            reqUser,
            reqAsFloat("BTC"),
            reqAsFloat("USD"),
            reqAsLong("BTC"),
            reqAsLong("USD")
        )
    }

    def print {
        requestData
        println("  Wallet")
        println
        println("  user: " + reqUser)
        println("  BTC: " + reqAsFloat("BTC") + " USD: " + reqAsFloat("USD"))
    }

    private def reqUser: String = {
        return data.getAsJsonObject.get("Login").getAsString }

    private def reqAsFloat(arg: String): Float = {
        return data.getAsJsonObject.get("Wallets").
            getAsJsonObject.get(arg).getAsJsonObject.get("Balance").
            getAsJsonObject.get("value") getAsFloat
    }

    private def reqAsLong(arg: String): Long = {
        return data.getAsJsonObject.get("Wallets").
            getAsJsonObject.get(arg).getAsJsonObject.get("Balance").
            getAsJsonObject.get("value_int") getAsLong
    }

    private var data: JsonElement = null
    private def requestData {data = new JsonParser parse(GoxRequests.Info)}
}

/******************************************************************   
  Mount Gox: class GoxHistory
                       data structures for history
 ******************************************************************/

class GoxHistoryEntry (
    val Currency: String,
    val Index: Int,
    val DateAsString: String,
    val Type: String,
    val Info: String,
    val ValueAsString: String,
    val BalanceAsString: String) {

    def DateAsLong: Long = {
        var sdf = new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss")
        return sdf.parse(DateAsString).getTime / 1000
    }

    def ValueAsLong = (ValueAsDouble * GoxUtils.getFactor(Currency)).toLong
    def ValueAsDouble = ValueAsString.toDouble
    def BalanceAsLong = (BalanceAsDouble * GoxUtils.getFactor(Currency)).toLong
    def BalanceAsDouble = BalanceAsString.toDouble

    def print {
        println("  Index: " + String.format("%4s", Index.toString) + "  " +
            DateAsString + "  Type: " + Type)
        println("    " + String.format("%-50s", Info))
        println("    Value (" + Currency + "): " +
            String.format("%10s", ValueAsString) + "     Balance (" +
            Currency + "): " +  String.format("%10s", BalanceAsString))
        println
    }
}

class GoxHistory(val currency: String) {

    def getHistory: List[GoxHistoryEntry] = buildList

    def print {
        val list = buildList
        for (i <- 1 until (list.length - 1)) list(i).print
    }

    private def requestData: String = GoxRequests.History(currency)

    private def createListEntry(l: List[String]) =
        new GoxHistoryEntry(currency, l(0).toInt, l(1), l(2), l(3), l(4), l(5))

    private def buildList: List[GoxHistoryEntry] = {
        val parseList = CSV.parse(requestData)
        var resList: List[GoxHistoryEntry] = Nil

        for (i <- 1 until (parseList.length - 1))
            resList = resList ::: List(createListEntry(parseList(i)))

        return resList
    }

    /* credits to:    
    stackoverflow.com/questions/5063022/use-scala-parser-combinator-to-parse-csv-files */
    object CSV extends RegexParsers {
      override val skipWhitespace = false   // meaningful spaces in CSV

      def COMMA   = ","
      def DQUOTE  = "\""
      def DQUOTE2 = "\"\"" ^^ { case _ => "\"" }  // combine 2 dquotes into 1
      def CRLF    = "\r\n" | "\n"
      def TXT     = "[^\",\r\n]".r
      def SPACES  = "[ \t]+".r

      def file: Parser[List[List[String]]] = repsep(record, CRLF) <~ (CRLF?)

      def record: Parser[List[String]] = repsep(field, COMMA)

      def field: Parser[String] = escaped|nonescaped

      def escaped: Parser[String] = {
        ((SPACES?)~>DQUOTE~>((TXT|COMMA|CRLF|DQUOTE2)*)<~DQUOTE<~(SPACES?)) ^^ {
          case ls => ls.mkString("")
        }
      }

      def nonescaped: Parser[String] = (TXT*) ^^ { case ls => ls.mkString("") }

      def parse(s: String) = parseAll(file, s) match {
        case Success(res, _) => res
        case e => throw new Exception(e.toString)
      }
    }
}

/******************************************************************   
  Mount Gox: class GoxTicker
                       data structures for ticker
 ******************************************************************/

class GoxTicker extends TickerData {

    def getData: Tuple5[Float, Float, Float, Int, Float] = {
        requestData
        return(
            reqAsFloat("last"),
            reqAsFloat("high"),
            reqAsFloat("low"),
            reqAsInt("vol"),
            reqAsFloat("vwap")
        )
    }

    def print {
        requestData

        println("  Mount Gox Ticker:")
        println
        println("  Last price: $" + reqAsFloat("last") +
            "  |  High: $" + reqAsFloat("high") + "  |  Low: $" + reqAsFloat("low"))
        println("  Volume: BTC " + reqAsInt("vol") + "  |  Weighted Avg.: $" + reqAsFloat("vwap"))
        println
    }

    private var data: JsonElement = null

    private def requestData {data = new JsonParser parse(GoxRequests.Ticker)}

    private def reqAsFloat(arg: String): Float = {
            return data.getAsJsonObject.get("ticker").getAsJsonObject.get(arg).getAsFloat    }

    private def reqAsInt(arg: String): Int = {
            return data.getAsJsonObject.get("ticker").getAsJsonObject.get(arg).getAsInt    }
}

/******************************************************************   
  Mount Gox: class GoxDepth
                       data structures for depth
 ******************************************************************/

class GoxDepth {

    def print {

        def max(x: Int, y: Int): Int = if (x < y) y else x

        //format output line
        def formatOutput(index: Int, size: Int, maxSize: Int, array: JsonArray): String = {

            if (index >= (maxSize - size)) {
                var ja = array.get(size - maxSize + index).getAsJsonArray
                var price = ja.get(0).toString
                var vol = ja.get(1).toString
                return String.format("%-10s", price) + " " + String.format("%-10s", vol)

            }
                else return ""
        }

        println("  Requesting depth data from Mount Gox. Please wait ...")
        val data: JsonElement = new JsonParser parse(GoxRequests.Depth)
        val jo: JsonObject = data.getAsJsonObject

        //reverse asks array for screen represantation
        val ta: JsonArray = jo get("asks") getAsJsonArray
        val asks = new JsonArray()
        for (i <- 0 until ta.size) asks.add(ta.get(ta.size - 1 - i))

        val bids: JsonArray = jo get("bids") getAsJsonArray

        for (i <- 0 until max(asks size, bids size)) {

            if ((i % 18) == 0) {
                println
                println("  " +     
                    String.format("%-27s","Asks (Price, Volume)") + "  " +     
                    String.format("%-27s","Bids (Price, Volume)"))
                println(" -------------------------------------------------------")
          }

            println("  " +     
                String.format("%-27s", formatOutput(i, asks size, max(asks size, bids size), asks)) +
                "  " +     
                String.format("%-27s", formatOutput(i, bids size, max(asks size, bids size),bids)))  
        }
    }
}

/******************************************************************   
  Mount Gox: class GoxAllTrades
                       data structures for trades
 ******************************************************************/

class GoxAllTrades {

    def print {

        def printSingleTrade(jo: JsonObject) {
            println( "  " +
                String.format("%-5s",jo.get("trade_type").getAsString) +
                String.format("%-22s",GoxUtils.formatGoxDate(jo.get("date").getAsLong)) +
                String.format("%-10s",jo.get("price").getAsString) +
                String.format("%-14s",jo.get("amount").getAsString) +
                String.format("%-10s",jo.get("price_currency").getAsString))
        }

        def printHeader {
             println
            println("  " +     
                String.format("%-5s","Type") +
                String.format("%-22s","Date") +
                String.format("%-10s","Price") +
                String.format("%-14s","Amount") +
                String.format("%-10s","Currency") )
            println(" -------------------------------------------------------------")
        }

        println("  Requesting trade data from Mount Gox. Please wait ...")

        val je: JsonElement = new JsonParser parse(GoxRequests.allTrades)
        val data = je.getAsJsonArray

        val iter: java.util.Iterator[JsonElement] = data.iterator()
        var i: Int = 0
        while (iter.hasNext) {
            if ((i % 18) == 0) printHeader
            printSingleTrade(iter.next().getAsJsonObject)
            i += 1            
        }
        println
    }
}

/******************************************************************   
  Mount Gox: class GoxOrder
                       data structures for orders
 ******************************************************************/

class GoxOrder(data: JsonObject) {

    val oid = data.get("oid").getAsString
    val date = data.get("date").getAsLong
    val status = data.get("real_status").getAsString
    val typ = data.get("type").getAsInt
    val amount = data.get("amount").getAsFloat
    val price = data.get("price").getAsFloat
    val amountLong = data.get("amount_int").getAsLong
    val priceLong = data.get("price_int").getAsLong

    def print {        

        def orderPrintType(t: Int): String = {
            t match {
                case 1 => return "SELL(Ask)"
                case 2 => return "BUY(Bid) "
                case _ => return "unknown"
            }
        }

        def orderStatus(stat: String): String = {
            stat match {
                case "invalid" => return "invalid"
                case "open"      => return "open   "
                case _         => return "unknown"
            }
        }

        println("  Order ID: " + oid)
        println("  " + GoxUtils.formatGoxDate(date) + "  " +
            orderStatus(status) + "  " + orderPrintType(typ) + "  " + amount +
            " BTC" +    " for " + price + " USD")
        println
    }
}

/******************************************************************   
  Mount Gox: class GoxOrders
                       data structures for orders
 ******************************************************************/

class GoxOrders {

    var orders: Orders = retrieveData(GoxRequests.Orders)

    /*** parse Json result to Orders (list of Order) ***/
    def retrieveData(input: String): Orders = {
        val data: JsonElement = new JsonParser parse(input)
        return new Orders(data.getAsJsonObject.get("orders").getAsJsonArray)
    } 

    /*** retrieve one order ***/
    def getOrder(oid: String): GoxOrder = orders getOrder(oid)

    /*** list of all orders ***/
    class Orders(data: JsonArray) {

        var orderList: List[GoxOrder] = Nil

        private var iter: java.util.Iterator[JsonElement] = data.iterator()
        while (iter.hasNext) {
            orderList = orderList ::: List(new GoxOrder(iter.next().getAsJsonObject))
        }

        def exists(oid: String): Tuple2[Boolean, String]    = {
            val tl: List[GoxOrder] = orderList filter((o: GoxOrder) => o.oid == oid)

            if (tl.length == 1) return (true, "Order " + oid + " exists.")
                else return (false, "Order " + oid + " does not exist.")
        }

        def getOrder(oid: String): GoxOrder = {
            val tl: List[GoxOrder] = orderList filter((o: GoxOrder) => o.oid == oid)

            if (tl.length == 1) return tl(0)
                else return null
        }

        def printAll {
            if (orderList.length > 0) {
                println("  Open Orders:"); println;
                orderList map (o => o.print)
            }
                else println("  No open orders ...")
        }
    }

    /*** does order with id exists? ***/
    def exists(oid: String): Tuple2[Boolean, String] = orders exists(oid)

    /*** cancelOrder handling ***/
    def cancel(oid: String): Tuple2[Boolean, String] = {
        if (orders.exists(oid)._1) {
            var tl: List[GoxOrder] = orders.orderList filter((o: GoxOrder) => o.oid == oid)
            val os: Orders = retrieveData(GoxRequests.Cancel(oid, tl(0).typ.toString))
            if (os.exists(oid)._1) return (false, "Canceling order " + oid + " failed.")
                else return (true, "Order " + oid + " canceled.")
        }
            else return (false, "order " + oid + " does not exist. Operation aborted ...")
    }

    /*** cancel all orders ***/
    def cancelAllOrders {orders.orderList.map (o => cancel(o.oid))}

    /*** print handling ***/
    def print {
        orders.printAll
    }
}

/******************************************************************   
  Mount Gox: class GoxTrades
                       data structures for trades
 ******************************************************************/

object GTConsts {
    val BUY  = 1
    val SELL = 2
}

class GoxTrades {

    def sellBTC(amount: String, price: String) = tradeBTC(amount, price, GoxRequests.sellBTC)
    def buyBTC(amount: String, price: String) = tradeBTC(amount, price, GoxRequests.buyBTC) 

    def tradeBTC(amount: String, price: String,
        reqFunc: (String, String) => String): Tuple3[Boolean, String, String] = {

        val data: JsonObject = new JsonParser parse(reqFunc(amount, price)) getAsJsonObject

        var oid = ""
        var status = ""
        var ok = false

        if (data.has("oid")) {
            oid = data.get("oid").getAsJsonPrimitive.getAsString
            status = data.get("status").getAsJsonPrimitive.getAsString
            ok = true
        }
            else status = data.get("status").getAsJsonPrimitive.getAsString

        return (ok, oid, status)
    }
}

/******************************************************************   
  Mount Gox: object GoxRequests
                       sending and receiving data     
 ******************************************************************/

object GoxRequests {

    /*** public methods ***/
    def Ticker: String = return request("0/ticker.php", new ArrayList[NameValuePair])
    def Info: String = return request("0/info.php", new ArrayList[NameValuePair])
    def Orders: String = return request("0/getOrders.php", new ArrayList[NameValuePair])
    def History(currency:String): String = {
        return request("0/history_" + currency + ".csv", new ArrayList[NameValuePair])
    }

    def Cancel(oid: String, typ: String): String = {

        def getType(typ: String): String = {
            typ match {    case "sell" => return "1"
                                    case "buy"  => return "2"
                                    case _      => return "-1"
                                }    
        }

        var p: ArrayList[NameValuePair] = new ArrayList[NameValuePair]
    p.add(new BasicNameValuePair("oid", oid))              
    p.add(new BasicNameValuePair("type", getType(typ)))              
        return request("0/cancelOrder.php", p)
    }

    def buyBTC(amount: String, price: String): String = {
        var p: ArrayList[NameValuePair] = new ArrayList[NameValuePair]
    p.add(new BasicNameValuePair("amount", amount))
    if (price != "") p.add(new BasicNameValuePair("price", price))              
        return request("0/buyBTC.php", p)    
    }

    def sellBTC(amount: String, price: String): String = {
        var p: ArrayList[NameValuePair] = new ArrayList[NameValuePair]
    p.add(new BasicNameValuePair("amount", amount))
    if (price != "") p.add(new BasicNameValuePair("price", price))             
        return request("0/sellBTC.php", p)
    }

    def Depth: String = {
        var p: ArrayList[NameValuePair] = new ArrayList[NameValuePair]
    p.add(new BasicNameValuePair("Currency", "USD"))
        return request("0/data/getDepth.php", p)
    }

    def allTrades: String = {
        var p: ArrayList[NameValuePair] = new ArrayList[NameValuePair]
    p.add(new BasicNameValuePair("Currency", "USD"))
        return request("0/data/getTrades.php", p)
    }

    /*** private methods ***/

    //composing the request
    private def request(apiurl: String, p: ArrayList[NameValuePair]): String = {
        return requestGoxByPHP(apiurl, URLEncodedUtils.format(p, "UTF-8")) }

    //method to sign request string with secret and SHA-512, not used in the moment
    private def signPostData(toSign: String): String = {
        var secret: String = new String(Base64.decodeBase64(goxCfg.secret))
        var mac: Mac = Mac.getInstance("HmacSHA512", "SunJCE")
    var secKey: SecretKeySpec = new SecretKeySpec(secret.getBytes(),"HmacSHA512")

    mac.init(secKey)
    var digest: Array[Byte] = mac.doFinal(toSign.getBytes())
    return Base64.encodeBase64String(digest)
    }

    //execute httppost to mtgox with php script
    private def requestGoxByPHP(apiurl: String, args: String): String = {

        val cmd: Array[String] = Array(goxCfg.cmdInter, goxCfg.cmdInterArgs,
          goxCfg.phpExec + " " + goxCfg.requestScript + " " + apiurl)

        var pb: ProcessBuilder = new ProcessBuilder(cmd(0), cmd(1), cmd(2))
        pb.environment().put("surgekey", goxCfg.key)
        pb.environment().put("surgesecret", goxCfg.secret)
        pb.environment().put("surgeargs", args)

        val p: Process = pb.start()
        val ts: String = IOUtils.toString(p.getInputStream(), "UTF-8");

        p.destroy
        return ts
    }
}