Tag Archives: Source Code

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
    }
}