14.6 从Web获取股票价格

完成净资产应用的最后一步是从Web获取股票价格。在之前所见的stocks.xml文件里有份股票代码和股份的列表。对于每支股票的代码,需要获取其收盘价。幸运的是,Yahoo提供了一个web service,用它就可以得到股票数据。比如,为了找到Google股票的最新收盘价,可以访问下面的URL:

  1. 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)文件。

下面是个样例文件:

  1. Date,Open,High,Low,Close,Volume,Adj Close
  2. 2009-04-02,363.31,369.76,360.32,362.50,4488000,362.50
  3. 2009-04-01,343.78,355.24,340.61,354.09,3301200,354.09
  4. 2009-03-31,348.93,353.51,346.18,348.06,3655300,348.06
  5. ...

获得最新的收盘价,要跳过开头标题的一行,最近日期的数据是从第二行开始的。从逗号分隔值里,仅仅抓取第5个元素③(索引为4的元素,从传统的0开始计数)。

③如果要的是收盘价,要抓取第5个元素,如果是调整后的收盘价,则要抓取第7个字段。

下面将Yahoo的服务投入使用。打开stocks.xml文件④,抓取每支股票的代码,获取其最新的收盘价,用获取的收盘价乘以拥有的股份数,得到这支股票的总价。把所有的值加起来,就得到了投资的总值。

④我不会把从控制台读取stocks.xml文件,更新股份的完整例子都放在这里。作为一个有经验的程序员,通过本章迄今所给出的例子,你应该已经知道怎么做了。

下面这个名为StockPriceFinder的单例对象里,包括两段代码,分别是用存在于XML文件里的股票代码和股份组装出一个map,以及从Yahoo服务获取数据。

UsingScala/StockPriceFinder.scala

  1. object StockPriceFinder {
  2. def getLatestClosingPrice(symbol: String) = {
  3. val url = "http://ichart.finance.yahoo.com/table.csv?s=" +
  4. symbol + "&a=00&b=01&c=" + new java.util.Date().getYear
  5. val data = scala.io.Source.fromURL(url).mkString
  6. val mostRecentData = data.split("\n")(1)
  7. val closingPrice = mostRecentData.split(",")(4).toDouble
  8. closingPrice
  9. }
  10. def getTickersAndUnits() = {
  11. val stocksAndUnitsXML = scala.xml.XML.load("stocks.xml")
  12. (Map[String, Int]() /: (stocksAndUnitsXML \ "symbol")) { (map,
  13. symbolNode) =>
  14. val ticker = (symbolNode \ "@ticker").toString
  15. val units = (symbolNode \ "units").text.toInt
  16. map(ticker) = units //Creates and returns a new Map
  17. }
  18. }
  19. }

getLatestClosingPrice()方法里,给定一个股票代码,使用Yahoo服务,获取其价格数据。因为数据是CSV格式的,需要分解数据,提取收盘价。最后从这个方法里返回收盘价。

因为股票代码和股份存在stocks.xml里,getTickersAndUnits()会读取这个文件,创建一个股票代码和股份的map。之前一节,我们了解了如何做到这一点。把同样的代码放到上面的单例对象里。

现在就准备获取数据和计算结果了。代码如下:

UsingScala/FindTotalWorthSequential.scala

  1. val symbolsAndUnits = StockPriceFinder.getTickersAndUnits
  2. println("Today is " + new java.util.Date())
  3. println("Ticker Units Closing Price($) Total Value($)")
  4. val startTime = System.nanoTime()
  5. val netWorth = (0.0 /: symbolsAndUnits) { (worth, symbolAndUnits) =>
  6. val (symbol, units) = symbolAndUnits
  7. val latestClosingPrice = StockPriceFinder getLatestClosingPrice symbol
  8. val value = units * latestClosingPrice
  9. println("%-7s %-5d %-16f %f".format(symbol, units, latestClosingPrice,
  10. value))
  11. worth + value
  12. }
  13. val endTime = System.nanoTime()
  14. println("The total value of your investments is $" + netWorth)
  15. println("Took %f seconds".format((endTime-startTime)/1000000000.0))

上面代码的输出如下:

  1. Today is Fri Apr 03 11:14:21 MDT 2009
  2. Ticker Units Closing Price($) Total Value($)
  3. XRX 240 4.980000 1195.200000
  4. NSM 200 11.250000 2250.000000
  5. SYMC 230 16.020000 3684.600000
  6. ADBE 125 23.280000 2910.000000
  7. VRSN 200 20.070000 4014.000000
  8. CSCO 250 18.140000 4535.000000
  9. TXN 190 16.470000 3129.300000
  10. ALU 150 2.010000 301.500000
  11. IBM 215 100.820000 21676.300000
  12. INTC 160 15.700000 2512.000000
  13. ORCL 200 18.820000 3764.000000
  14. HPQ 225 33.690000 7580.250000
  15. AMD 150 3.160000 474.000000
  16. AAPL 200 112.710000 22542.000000
  17. MSFT 190 19.290000 3665.100000
  18. The total value of your investments is $84233.25
  19. Took 18.146055 seconds

上面的代码里,先从StockPriceFinder里获取股票代码和股份的Map。然后,对每支股票的代码,让StockPriceFinder通过getLatestClosingPrice()方法获取最新的价格。在得到了最新的收盘价之后,就用它乘以股份,算出这支股票的总价。用/:()-foldLeft()方法辅助迭代,同时算出净值。

完成这个任务并不需要太多代码。上面的例子运行一次大约需要18秒。在下一节,我们会让响应更快一些。