14.6 从Web获取股票价格
完成净资产应用的最后一步是从Web获取股票价格。在之前所见的stocks.xml
文件里有份股票代码和股份的列表。对于每支股票的代码,需要获取其收盘价。幸运的是,Yahoo提供了一个web service,用它就可以得到股票数据。比如,为了找到Google股票的最新收盘价,可以访问下面的URL:
http://ichart.finance.yahoo.com/table.csv?s=GOOG&a=00&b=01&c=2009
参数s、a、b和c分别表示股票代码、起始月份(一月是0),起始日期和起始年份。如果你不用参数d、e和f指定截止日期,服务会返回从给定起始日期到最近可用日期所有的价格。访问上面的URL,会下载一个逗号分隔值(comma-separated value,CSV)文件。
下面是个样例文件:
Date,Open,High,Low,Close,Volume,Adj Close
2009-04-02,363.31,369.76,360.32,362.50,4488000,362.50
2009-04-01,343.78,355.24,340.61,354.09,3301200,354.09
2009-03-31,348.93,353.51,346.18,348.06,3655300,348.06
...
获得最新的收盘价,要跳过开头标题的一行,最近日期的数据是从第二行开始的。从逗号分隔值里,仅仅抓取第5个元素③(索引为4
的元素,从传统的0
开始计数)。
③如果要的是收盘价,要抓取第5个元素,如果是调整后的收盘价,则要抓取第7个字段。
下面将Yahoo的服务投入使用。打开stocks.xml
文件④,抓取每支股票的代码,获取其最新的收盘价,用获取的收盘价乘以拥有的股份数,得到这支股票的总价。把所有的值加起来,就得到了投资的总值。
④我不会把从控制台读取
stocks.xml
文件,更新股份的完整例子都放在这里。作为一个有经验的程序员,通过本章迄今所给出的例子,你应该已经知道怎么做了。
下面这个名为StockPriceFinder
的单例对象里,包括两段代码,分别是用存在于XML文件里的股票代码和股份组装出一个map
,以及从Yahoo服务获取数据。
UsingScala/StockPriceFinder.scala
object StockPriceFinder {
def getLatestClosingPrice(symbol: String) = {
val url = "http://ichart.finance.yahoo.com/table.csv?s=" +
symbol + "&a=00&b=01&c=" + new java.util.Date().getYear
val data = scala.io.Source.fromURL(url).mkString
val mostRecentData = data.split("\n")(1)
val closingPrice = mostRecentData.split(",")(4).toDouble
closingPrice
}
def getTickersAndUnits() = {
val stocksAndUnitsXML = scala.xml.XML.load("stocks.xml")
(Map[String, Int]() /: (stocksAndUnitsXML \ "symbol")) { (map,
symbolNode) =>
val ticker = (symbolNode \ "@ticker").toString
val units = (symbolNode \ "units").text.toInt
map(ticker) = units //Creates and returns a new Map
}
}
}
在getLatestClosingPrice()
方法里,给定一个股票代码,使用Yahoo服务,获取其价格数据。因为数据是CSV格式的,需要分解数据,提取收盘价。最后从这个方法里返回收盘价。
因为股票代码和股份存在stocks.xml
里,getTickersAndUnits()
会读取这个文件,创建一个股票代码和股份的map
。之前一节,我们了解了如何做到这一点。把同样的代码放到上面的单例对象里。
现在就准备获取数据和计算结果了。代码如下:
UsingScala/FindTotalWorthSequential.scala
val symbolsAndUnits = StockPriceFinder.getTickersAndUnits
println("Today is " + new java.util.Date())
println("Ticker Units Closing Price($) Total Value($)")
val startTime = System.nanoTime()
val netWorth = (0.0 /: symbolsAndUnits) { (worth, symbolAndUnits) =>
val (symbol, units) = symbolAndUnits
val latestClosingPrice = StockPriceFinder getLatestClosingPrice symbol
val value = units * latestClosingPrice
println("%-7s %-5d %-16f %f".format(symbol, units, latestClosingPrice,
value))
worth + value
}
val endTime = System.nanoTime()
println("The total value of your investments is $" + netWorth)
println("Took %f seconds".format((endTime-startTime)/1000000000.0))
上面代码的输出如下:
Today is Fri Apr 03 11:14:21 MDT 2009
Ticker Units Closing Price($) Total Value($)
XRX 240 4.980000 1195.200000
NSM 200 11.250000 2250.000000
SYMC 230 16.020000 3684.600000
ADBE 125 23.280000 2910.000000
VRSN 200 20.070000 4014.000000
CSCO 250 18.140000 4535.000000
TXN 190 16.470000 3129.300000
ALU 150 2.010000 301.500000
IBM 215 100.820000 21676.300000
INTC 160 15.700000 2512.000000
ORCL 200 18.820000 3764.000000
HPQ 225 33.690000 7580.250000
AMD 150 3.160000 474.000000
AAPL 200 112.710000 22542.000000
MSFT 190 19.290000 3665.100000
The total value of your investments is $84233.25
Took 18.146055 seconds
上面的代码里,先从StockPriceFinder
里获取股票代码和股份的Map
。然后,对每支股票的代码,让StockPriceFinder
通过getLatestClosingPrice()
方法获取最新的价格。在得到了最新的收盘价之后,就用它乘以股份,算出这支股票的总价。用/:()-foldLeft()
方法辅助迭代,同时算出净值。
完成这个任务并不需要太多代码。上面的例子运行一次大约需要18秒。在下一节,我们会让响应更快一些。