Monday, August 22, 2011

Scala stock charts, part 3 - Saving the quotes

Going further in programming the functionality behind the technical analysis tool for stocks made in Scala we are going to save the daily quotes to separate file, we are also going to supply functionality to load the data for a stock, and save and read a price list divided in 3 lists.

The format of the files is a bitt different, the Quotes I will save and read as they are, but the price list will have a different format that will look as jSon and is saved in a way that will be easy to read with pattern matching. For the matter of fact also the Stock-quotes for a single instrument will be read and transformed to a list of Quote's with pattern matching.

FileHandler.scala
/*
 @author Peter Johansson / Ironic programmer 
 */

package scala.ironic.file

object FileHandler {
  import java.io.{File,BufferedWriter,FileWriter,IOException}
  import scala.collection.mutable.{ HashMap => MutableHashMap}
  import scala.collection.immutable.{ HashMap }
  import scala.io.Source
  
  val rootDir = { scala.util.Properties.userDir }
  val filesDir = { rootDir + "/files"}
  val logFile = { rootDir + "/" + "sBors.log"}
  val pricelistsFile = { rootDir + "/files/pricelists.txt"}
    
  def saveDailyQuotes(quotes: Iterator[QuoteItem]) = {
    log("## Saving daily qoutes")
    log("--- rootDir : " + rootDir)
    log("--- filesDir : " + filesDir)
    // Check that dir exista or creat if not
    checkDir(filesDir)
    
    var lists = new MutableHashMap[String,List[Quote]]
    var curList = "?"
    
    quotes foreach { quote =>
      quote match { 
        case q: Quote => { 
                            saveQuoteToFile(q);
                            lists(curList) = List(q) ::: lists(curList)
                          }
        case qList: QuoteList => {
                                    curList = qList.name;
                                    lists += qList.name -> List()
                                 }
        case _ => log("! " + quote)
      }
    }
    
    // Save current lists to file...
    saveStockPriceList(lists)
    
  }
  
  def checkDir(dirName: String){
     val f = new File(dirName)
     if(!f.exists){
       f.mkdirs
       log("!! Created dir at : " + dirName)
     }
  }
  
  def saveQuoteToFile(quote: Quote) = {
    val fileName = scala.util.Properties.userDir + "/files/" + quote.ticker.replace(" ","_") + ".txt"
    val bw = new BufferedWriter(new FileWriter(fileName,true))
    bw.write(quote + "\n")
    bw.close()
  }
  
  def saveStockPriceList(pricelist: MutableHashMap[String,List[Quote]]){
    log("## Saving pricelists")
    val bw = new BufferedWriter(new FileWriter(pricelistsFile,false))
    val sb = new StringBuffer
    sb.append("{ \"pricelists\": [\n")
    for{ (listname,quotes) <- pricelist } {
      sb.append("\t{ \n\t\t\"listname\": \"" + listname + "\",\n")
      sb.append("\t\t\"stocks\": [\n")
      for{ quote <- quotes } {
        sb.append("\t\t\t{ \"ticker\": \""+quote.ticker + "\", \"aktie\": \"" + quote.aktie + "\"},\n")
      }
      sb.append("\t\t],\n")
      sb.append("\t},\n")
    }
    sb.append("}\n")
    
    bw.write(sb.toString)
    bw.close()
  }
  
  def log(msg: String){
    println(msg)
    val bw = new BufferedWriter(new FileWriter(logFile,true))
    bw.write(msg + "\n")
    bw.close()
    
  }
  
  def loadPriceLists : HashMap[String, List[Instrument]] = {
    log("## Loading pricelists")
    val pricelists = new HashMap[String, List[Instrument]]
    val fc = Source.fromFile(pricelistsFile)
    val ListExtractorRE = """.*"listname": "(.+)",.*\s*""".r
    val InstExtractorRE = """.*"ticker": "(.+)", "aktie": "(.+)".*\s*""".r
   
    fc.getLines foreach { line =>
      line match {
        case ListExtractorRE(listname) => println("#### got listname : " + new QuoteList(listname))
        case InstExtractorRE(ticker, aktie) => 
          println("# got instrument : " + new Instrument(ticker, aktie))
        case _ => 
      }
      
    }
    return pricelists
  }
  
  def loadInstrument(ticker: String): List[QuoteItem] = {
    val fn = rootDir + "/files/" + ticker.replace(" ","_") + ".txt"
    val fc = Source.fromFile(fn)
    val QuoteExtractorRE = """Quote\(([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*),([^\,]*).*\)""".r
    println(QuoteExtractorRE)
    val quotelines = fc.getLines// filter( s=> ! s.startsWith("Quote")) 
    val quotes = quotelines map { line =>
      line match {
        case QuoteExtractorRE(date,ticker,aktie,diffKr,diffPerc,buy,sell,last,high,low,returnNo,returnKr) => 
          Quote(date,ticker,aktie,diffKr.toDouble,diffPerc.toDouble,buy.toDouble,sell.toDouble,last.toDouble,high.toDouble,low.toDouble,returnNo.toDouble,returnKr.toDouble)
        case _ => InvalidQuote("?", line)
      } 
    } 
    return quotes.toList
  }
  
  
}

That is a lot of code that I wrote on the fly, but not as much as it would have been to do the same thing in Java. the nice thing is the pattern matching, that saves a lot pain that Java code would bring, for example reading every line in the file would not be more code, but also creating and getting the values to objects would give a lot of more code.

[coming... Java code to do it as a comparison]

checkDir: is a method to create directory structure if it does not exist. Actually the first time loading the quotes there is no /files dir if it is not created manually, to avoid crashing, and open upp for the possibility to later use some config to have the data files in a directory of choice this is to ensure that the supplied configured dir will be created.

saveDailyQuotes: traverse the content of a daily quote file and calls to save single quote and collect the price lists and calls to save these.

saveStockPriceList: saves the current instruments divided in their lists to a file.

saveQuoteToFile: append a single quote to its data file.

Finally the methods for loading price lists and a single instrument

loadPriceLists: loads the pice lists back to a map with a list name and a list of Instruments. (Actually it is note yet finished, since I want to think out a way to avoid having a mutable HashMap, now it only prints out what it gets.)

loadInstrument: loads the file of a single stock to a list of Quote's


I am not completely happy with it, there is some duplication of code, for example I create new Writers in every method that save things. There is certainly things that can improve here. However, refactoring is a later issue, and this does what I want it to do, so for now it will have to do.

Now the interesting part is beginning to appear, next thing is to make functionality to take car of the technical stuff for the charts to be drawn.

0 kommentarer:

Post a Comment