Saturday, September 24, 2011

Scala stock charts, part 4 - Woking with the GUI - 1

I have been busy the last couple of weeks, or better saying, too lazy to sit home with my Scala Technical Analysis tool. Anyway, this weekend I decided to continue and this time to start from the GUI and start using the functionality done so far from a GUI.

I have also done som restructuring in the packages which now is:

package me.ironic.scabors.app
package me.ironic.scabors.service
package me.ironic.scabors.model
package me.ironic.scabors.file

even if I have not reworked the messy file-package. I will do that later. however, I finished up with loading the price list, and that code now look like this:

def loadPriceLists : MutableHashMap[String, List[Instrument]] = {
    log("## Loading pricelists")
    var pricelists = new MutableHashMap[String, List[Instrument]]
    val fc = Source.fromFile(pricelistsFile)
    val ListExtractorRE = """.*"listname": "(.+)",.*\s*""".r
    val InstExtractorRE = """.*"ticker": "(.+)", "aktie": "(.+)".*\s*""".r
   
    /*
     * When hitting a PriceList, a new list begins
     */
    var curList = "?"
    fc.getLines foreach { line =>
      line match {
        case ListExtractorRE(listname) => val pList = new PriceList(listname)
          curList = pList.name
          pricelists += pList.name -> List()
        case InstExtractorRE(ticker, aktie) => val inst = new Instrument(ticker, aktie) 
          pricelists(curList) = List(inst) ::: pricelists(curList)
        case _ => None
      }
      
    } 
    return pricelists
  }

I also broke out the model classes from the file package, and put it in another file called PriceModel.scala, you can also see that I am now using "Price" which is a better name than "Quote" so all references to Quote is now replaced with Price. The content of that file is:

PriceModel.scala
package me.ironic.scabors.model

// To be able to handle Iterator[PriceItem]
trait PriceItem 

case class InvalidPrice(date: String, content:String) extends PriceItem
case class PriceList(name:String) extends PriceItem
case class Price(date: String, ticker :String, aktie : String,
    diffKr : Double, diffPerc : Double, buy : Double,
    sell : Double, last : Double, high : Double,
    low : Double, returnNo : Double, returnKr : Double
  ) extends PriceItem
case class Instrument(ticker:String, aktie: String)  


I also made a Service object so that the GUI does not need to know or depend on anything from the file package. So far as I have gotten with the GUI it just contains two methods, one to get all price lists and one to get the data for a chosen stock. Here is the code:

PriceService.scala
package me.ironic.scabors.service

object PriceService {
  import me.ironic.scabors.file.FileHandler
  val pricelists = {
    println("# loading pricelists")
    FileHandler.loadPriceLists
  }
  
  def getPriceLists = {
     // using already loaded pricelists, i.e loading is done in val...
     pricelists
  }
  
  def getInstrument(inst: String) = {
    FileHandler.loadInstrument(inst)
  }
}

GUI programming done in Scala

I decided to leave the idea of using Java FX that I had from the beginning and that I wrote in the beginning post. Instead I decided to use Scala Swing. Not so easy as it turned out, because first of all, it has been a couple of years since I last did any Java Swing programming and second of all, it was not very similar and not easy to transform Java Swing thinking to Scala Swing thinking. Well, it is less code, but surly, at this stage it looks really messy. Maybe it takes a while for my brain to melt Scala Swing programming and the fact that google got warm from all searching and few good examples does not help.

I am to tired to explain most of the code, maybe in a later post, but here is what the purpose if the GUI is so far.
  • Provide a text field to filter search for stocks "Instrument"
  • Provide a button to perform the search
  • Provide a ComboBox to present the filtered result from the search and to select stock to draw
  • Provide Panel for drawing a Bar Chart of selected stock

I have implemented and it works as I wand, except that the tedious job to provide the functionality to draw a bar chart is not done. Thea is a later exercise and a future post to describe. I let you see the messy code for this. 
ScaBorsApp.scala
package me.ironic.scabors.app

import swing._
import java.awt.Dimension
import me.ironic.scabors.service._
import me.ironic.scabors.model._
object ScaBorsApp extends SimpleSwingApplication{
      
    def top = new MainFrame {
      title = "Technical Analysis for Stocks"
      val searchField = new TextField {
          text = "a"
          columns = 20
        }
       /*
       * Search button, will get the pricelist and do filter and sort to present in combo box
       */
      val searchBtn = new Button("Search") {
          action = Action("Search"){ 
            val pricelists = PriceService.getPriceLists map { kv => kv._2 } flatMap { 
                case i: Instrument => i 
                case x => x
              } filter {
                i: Instrument => i.aktie.toLowerCase().startsWith(searchField.text.toLowerCase)
              }
            
            val items = pricelists.toList sortWith {(i1,i2) => i1.aktie.toLowerCase < i2.aktie.toLowerCase}
            combo.peer.setModel(
              ComboBox.newConstantModel(
                  for{p <- items} yield p.ticker + ": " + p.aktie
              )
            )
          }
        }
    
      /*
       * ComboBox to present filtered search for stocks, see searchButton action code how search is performed
       */
      import event._
      val combo = new ComboBox(List("-- Search to be able to select --")) {
        preferredSize = new Dimension(300,20)
        listenTo(selection)
        reactions += {
          case SelectionChanged(c) =>
            curInstrument = new Instrument(selection.item.split(":")(0),selection.item.split(":")(1).trim)
            curPriceList = PriceService.getInstrument(curInstrument.ticker)
            barchartPanel.updateList(curPriceList)

        }
      }
      
      var curInstrument: Instrument = Instrument("---","Not choosen")
      var curPriceList: List[PriceItem] = Nil
      
      /*
       * Top menu panel.
       */
      val menu = new FlowPanel {
        contents += new Label("Stock")
        contents += searchField
        contents += searchBtn
        contents += combo
      }
      
    val barchartPanel = new BarChartPanel(curPriceList)
      val scrollPane = new ScrollPane {
        preferredSize = new Dimension(1180,700)
        contents = barchartPanel
      }
      

      contents = new BorderPanel() {
        add(menu, BorderPanel.Position.North) 
        add(scrollPane, BorderPanel.Position.Center)
      }
     
      preferredSize = new Dimension(1200,800)
    }
}

class BarChartPanel(var list: List[PriceItem]) extends Panel {
  import java.awt.Color
  background = Color.white
  preferredSize = new Dimension(1180,680)
  override protected def paintComponent(g: Graphics2D) {
    super.paintComponent(g)
    
  } 
  
  def updateList(newList: List[PriceItem]) = {
    list = newList
    println("Updating list : " + list)
  }
}


Yeah, thats right, I also gave up SBT, I am now using gradle to build. here is the code for that. build.gradle
build.gradle
apply plugin: 'scala'

repositories {
    mavenCentral()
}

dependencies {
    // Libraries needed to run the scala tools
    scalaTools 'org.scala-lang:scala-compiler:2.9.1'
    scalaTools 'org.scala-lang:scala-library:2.9.1'

    // Libraries needed for scala api
    compile 'org.scala-lang:scala-library:2.9.1'

    // Libraries needed for scala.swing
//    scalaTools 'org.scala-lang:scala-swing:2.9.1'
    compile 'org.scala-lang:scala-swing:2.9.1'
}


task createSourceDirs << {
  println "# Creating source dirs"
  ['src/main/scala','src/test/scala','src/main/java','src/test/java'].each { 
    println " - $it"
    new File(it).mkdirs()
  }
}

Until later..... ciao!

0 kommentarer:

Post a Comment