By now our StockLibrary is functional. We can use it in C#, VB.NET or any other languages that Visual Studio supports. Unfortunately, it is now running in sequential and synchronous way. So if we use it to analyze multiple stock symbols it will be slow because GetAnalyzers will only create the StockAnalyzers sequentially. Another bottleneck is the WebRequest IO operation. Since it’s running synchronously, it will hog the thread while waiting for response from Yahoo’s server.
Now we know what to do:
- GetAnalyzers need to run in parallel.
- IO Processes need to run asynchronously.
In F# it is very easy to program in asynchronous. We only need to:
- Enclose the function with async { … } and use return at the end of the function
//... SNIP ... static member loadPrices ticker = async { //... SNIP ... return prices } //... SNIP ...
- Add ‘bangs’/exclamation mark (!) after let and use the asynchronous version of the IO operation.
//... SNIP ... let! resp = req.AsyncGetResponse() //... SNIP ... let! csv = reader.AsyncReadToEnd() //... SNIP ...
If you see how Luca is writing the code, he simply added ‘bangs’ and change the IO method into its Async version. But as I found out, we can’t do this anymore in Visual Studio 2012. In order to make the code complie, we need to do two things.
- Install F# Power Pack and add it into project’s References
- Import Microsoft.FSharp.Control.WebExtensions library.
Alright, back to the code. Since we have changed loadPrices into asynchronous, we also need to change the StockAnalyzer’s factory method to make it compile.
//... SNIP ... static member GetAnalyzers(tickers, days) = tickers |> Seq.map StockLoader.loadPrices //Run every each loadPrices in Parallel |> Async.Parallel |> Async.RunSynchronously |> Seq.map (fun prices -> new StockAnalyzer(prices, days)) //... SNIP ...
Putting everything into context:
namespace StockLibrary open System.IO open System.Net open Microsoft.FSharp.Control.WebExtensions type internal StockLoader = static member loadPrices ticker = async { let url = "http://ichart.finance.yahoo.com/table.csv?s=" + ticker + "&d=8&e=10&f=2012&g=d&a=2&b=13&c=1986&ignore=.csv" let req = WebRequest.Create(url) let! resp = req.AsyncGetResponse() let stream = resp.GetResponseStream() let reader = new StreamReader(stream) let! csv = reader.AsyncReadToEnd() let prices = csv.Split([|'\n'|]) |> Seq.skip 1 |> Seq.map (fun line -> line.Split([|','|])) |> Seq.filter (fun values -> values |> Seq.length = 7) |> Seq.map (fun values -> System.DateTime.Parse(values.[0]), float values.[6]) //return the list of tuples (DateTime, float) return prices } type StockAnalyzer(lprices, days) = let prices = lprices //snd is a function that return the second element of a tuple |> Seq.map snd //return the first N element of the list |> Seq.take days //This is a factory method static member GetAnalyzers(tickers, days) = tickers |> Seq.map StockLoader.loadPrices |> Async.Parallel |> Async.RunSynchronously |> Seq.map (fun prices -> new StockAnalyzer(prices, days)) member s.Return = let lastPrice = prices |> Seq.nth 0 let startPrice = prices |> Seq.nth (days-1) lastPrice/startPrice-1. //Calculate standard-deviation member s.StdDev = let logRets = prices |> Seq.pairwise |> Seq.map (fun (x,y) -> log (x/y)) let mean = logRets |> Seq.average let sqr x = x * x //calculate variance let var = logRets |> Seq.averageBy (fun r -> sqr(r-mean)) //return sqrt(variance) sqrt var
That’s it! We can now create an asynchronous and parallel program in F# with much clarity. I am now very excited to materialize this knowledge to solve problems. What do you think? Do you have any ideas that would be good to be implemented with F#?
loading...
About Hardono
Incoming Search
f#