Created
July 12, 2014 09:37
-
-
Save fbettag/1cca7db7c36c4a706709 to your computer and use it in GitHub Desktop.
Currency Converter in Scala (no rounding yet) based on Yahoo Finance and Twitter Finagle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.concurrent.atomic.AtomicReference | |
import com.twitter.finagle.{ http, Service } | |
import com.twitter.finagle.builder.ClientBuilder | |
import com.twitter.util.{ Promise, Future } | |
import org.jboss.netty.handler.codec.http._ | |
import org.jboss.netty.util.CharsetUtil | |
import net.liftweb.util.Schedule | |
import net.liftweb.json._ | |
import net.liftweb.common.Logger | |
import net.liftweb.util.Helpers._ | |
import java.math.{ BigDecimal => BigDec } | |
object Currencies extends Enumeration with Logger { | |
val EUR, CHF, USD, GBP, AUD, NZD, CAD = Value | |
private val baseCurrency = USD | |
val uri = "http://finance.yahoo.com/webservice/v1/symbols/allcurrencies/quote?format=json&view=basic" | |
private val client: Service[HttpRequest, HttpResponse] = ClientBuilder() | |
.codec(http.Http()) | |
.hosts("finance.yahoo.com:80") | |
.hostConnectionLimit(1) | |
//.tlsWithoutValidation() | |
.build() | |
private def fetch() { | |
val request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri) | |
request.headers().add("Host", "finance.yahoo.com") | |
debug("Requesting " + uri) | |
val response: Future[HttpResponse] = client(request) | |
response.onFailure(x => error(x)) | |
response.onSuccess { response => | |
val respStr = response.getContent.toString(CharsetUtil.UTF_8) | |
val json = JsonParser.parseOpt(respStr) | |
json.map(store) getOrElse { | |
error("Invalid response: " + respStr) | |
} | |
} | |
Schedule(() => fetch(), 15.minutes) | |
} | |
private def store(quotes: JValue) { | |
debug("Storing") | |
var quotesM = Map[String, BigDec]() | |
quotes match { | |
case jo: JObject => | |
jo \ "list" \ "resources" match { | |
case JArray(resources) => | |
resources.map { | |
case res: JObject => | |
val fields = res \ "resource" \ "fields" | |
val quote = for { | |
name <- (fields \ "name").extractOpt[String] | |
price <- (fields \ "price").extractOpt[String].map(new BigDec(_)) | |
if name.matches(".../...") | |
} yield (name, price) | |
quote match { | |
case Some((name, price)) => quotesM ++= Map(name -> price) | |
case _ => warn("Did not contain a quote!") | |
} | |
case _ => warn("Did not contain a resource") | |
} | |
case _ => warn("Did not contain a resource!") | |
} | |
case _ => warn("Did not contain resources!") | |
} | |
if (quotesM.nonEmpty) cachedQuotes.set(quotesM) | |
} | |
private val cachedQuotes = new AtomicReference[Map[String, BigDec]](Map()) | |
def convert(amount: Double, from: this.Value, to: this.Value): Option[Double] = { | |
if (from == to) Some(amount) | |
else if (from == baseCurrency) { | |
val pair = from.toString.toUpperCase + "/" + to.toString.toUpperCase | |
cachedQuotes.get.get(pair).map(x => amount * x.doubleValue()) | |
} else if (to == baseCurrency) { | |
val pair = to.toString.toUpperCase + "/" + from.toString.toUpperCase | |
cachedQuotes.get.get(pair).map(x => amount / x.doubleValue()) | |
} else { | |
val pair1 = baseCurrency.toString.toUpperCase + "/" + from.toString.toUpperCase | |
val pair2 = baseCurrency.toString.toUpperCase + "/" + to.toString.toUpperCase | |
for { | |
fromQuote <- cachedQuotes.get.get(pair1) | |
toQuote <- cachedQuotes.get.get(pair2) | |
} yield amount / fromQuote.doubleValue() * toQuote.doubleValue() | |
} | |
} | |
def init() { | |
fetch() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
See also: https://github.com/snowplow/scala-forex/