├── Chapter08 ├── log.sql └── tradehistory.sql ├── Chapter05 ├── Chapter5 │ ├── packages.config │ ├── App.config │ ├── mc1.fsx │ ├── Script2.fsx │ ├── Script3.fsx │ ├── Script1.fsx │ ├── Chapter5.fsproj │ └── chapter5.fsx └── Chapter5.sln ├── Chapter04 ├── Chapter4 │ ├── Script1.fsx │ ├── App.config │ ├── Script4.fsx │ ├── Agent.fs │ ├── GUI.fs │ ├── Script2.fsx │ ├── Script3.fsx │ └── Chapter4.fsproj └── Chapter4.sln ├── README.md ├── Chapter01 ├── program1.fsx ├── program2.fsx └── table.csv ├── Chapter06 ├── Script8.fsx ├── Script5.fsx ├── Script4.fsx ├── volsmile.fsx └── smile_data.csv ├── Chapter07 ├── Quote.fsx ├── OrderType.fsx └── FIX.fs ├── Chapter09 ├── Script1.fsx ├── Script2.fsx └── smile_data.csv ├── Chapter03 └── Chapter3.fsx └── Chapter02 └── Chapter2.fsx /Chapter08/log.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE LOG 2 | ( 3 | log_id int IDENTITY PRIMARY KEY, 4 | log_datetime datetime DEFAULT CURRENT_TIMESTAMP, 5 | log_level nvarchar(12) DEFAULT 'info', 6 | log_msg ntext 7 | ) -------------------------------------------------------------------------------- /Chapter05/Chapter5/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Chapter04/Chapter4/Script1.fsx: -------------------------------------------------------------------------------- 1 | open System.Windows.Forms 2 | 3 | let form = new Form(Text = "First F# form") 4 | let button = new Button(Text = "Click me to close!", Dock = DockStyle.Fill) 5 | 6 | button.Click.Add(fun _ -> Application.Exit() |> ignore) 7 | form.Controls.Add(button) 8 | form.Show() -------------------------------------------------------------------------------- /Chapter08/tradehistory.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE TRADEHISTORY 2 | ( 3 | tradehistory_id int IDENTITY PRIMARY KEY, 4 | tradehistory_datetime datetime DEFAULT CURRENT_TIMESTAMP, 5 | tradehistory_instrument nvarchar(12), 6 | tradehistory_qty int, 7 | tradehistory_type nvarchar(12), 8 | tradehistory_price float 9 | ) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | fsharp-for-quantitative-finance 2 | =============================== 3 | 4 | The example source code for the Packt's "F# for Quantitative Finance" by Johan Astborg 5 | 6 | [More info about the book](http://www.packtpub.com/fsharp-for-quantitative-finance/book) 7 | 8 | # Versions 9 | The code in this repository is kept up to date with errata corrections. 10 | -------------------------------------------------------------------------------- /Chapter04/Chapter4/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Chapter05/Chapter5/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Chapter01/program1.fsx: -------------------------------------------------------------------------------- 1 | /// Open the System.IO namespace 2 | open System.IO 3 | 4 | /// Sample stock data, from Yahoo Finance 5 | let stockData = [ 6 | "2013-06-06,51.15,51.66,50.83,51.52,9848400,51.52"; 7 | "2013-06-05,52.57,52.68,50.91,51.36,14462900,51.36"; 8 | "2013-06-04,53.74,53.75,52.22,52.59,10614700,52.59"; 9 | "2013-06-03,53.86,53.89,52.40,53.41,13127900,53.41"; 10 | "2013-05-31,54.70,54.91,53.99,54.10,12809700,54.10"; 11 | "2013-05-30,55.01,55.69,54.96,55.10,8751200,55.10"; 12 | "2013-05-29,55.15,55.40,54.53,55.05,8693700,55.05" 13 | ] 14 | 15 | /// Split row on commas 16 | let splitCommas (l:string) = 17 | l.Split(',') 18 | 19 | 20 | /// Get the row with lowest trading volume 21 | let lowestVolume = 22 | stockData 23 | |> List.map splitCommas 24 | |> List.minBy (fun x -> (int x.[5])) -------------------------------------------------------------------------------- /Chapter01/program2.fsx: -------------------------------------------------------------------------------- 1 | /// Open the System.IO namespace 2 | open System.IO 3 | 4 | let filePath = "table.csv" 5 | 6 | /// Split row on commas 7 | let splitCommas (l:string) = 8 | l.Split(',') 9 | 10 | /// Read a file into a string array 11 | let openFile (name : string) = 12 | try 13 | let content = File.ReadAllLines(name) 14 | content |> Array.toList 15 | with 16 | | :? System.IO.FileNotFoundException as e -> printfn "Exception! %s " e.Message; ["empty"] 17 | 18 | /// Get the row with lowest trading volume, from file 19 | let lowestVolume = 20 | openFile filePath 21 | |> List.map splitCommas 22 | |> Seq.skip 1 23 | |> Seq.minBy (fun x -> (int x.[5])) 24 | 25 | /// Use printfn with generic formatter, %A 26 | printfn "Lowest volume, found in row: %A" lowestVolume 27 | lowestVolume.[0] 28 | -------------------------------------------------------------------------------- /Chapter04/Chapter4/Script4.fsx: -------------------------------------------------------------------------------- 1 | #r "System.Windows.Forms.DataVisualization.dll" 2 | 3 | // The form 4 | open System 5 | open System.Net 6 | open System.Windows.Forms 7 | open System.Drawing 8 | 9 | let form = new Form(Visible = true, Text = "Data grid #1", 10 | TopMost = true, Size = Drawing.Size(600,600)) 11 | 12 | let textBox = 13 | new RichTextBox(Dock = DockStyle.Fill, Text = "F# Programming is Fun!", 14 | Font = new Font("Lucida Console",16.0f,FontStyle.Bold), 15 | ForeColor = Color.DarkBlue) 16 | 17 | let show x = 18 | textBox.Text <- sprintf "%30A" x 19 | 20 | 21 | form.Controls.Add textBox 22 | 23 | show (1,2) 24 | show [ 0 .. 100 ] 25 | show [ 0.0 .. 2.0 .. 100.0 ] 26 | 27 | (1,2,3) |> show 28 | 29 | [ 0 .. 99 ] |> show 30 | 31 | [ for i in 0 .. 99 -> (i, i*i) ] |> show 32 | -------------------------------------------------------------------------------- /Chapter04/Chapter4.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Chapter4", "Chapter4\Chapter4.fsproj", "{7F1AD7E2-45C6-4685-A5F6-6A2EC8F92769}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {7F1AD7E2-45C6-4685-A5F6-6A2EC8F92769}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {7F1AD7E2-45C6-4685-A5F6-6A2EC8F92769}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {7F1AD7E2-45C6-4685-A5F6-6A2EC8F92769}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {7F1AD7E2-45C6-4685-A5F6-6A2EC8F92769}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /Chapter05/Chapter5.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Chapter5", "Chapter5\Chapter5.fsproj", "{0F38272D-8BC4-4E8E-8168-083E60C0CBF7}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {0F38272D-8BC4-4E8E-8168-083E60C0CBF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {0F38272D-8BC4-4E8E-8168-083E60C0CBF7}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {0F38272D-8BC4-4E8E-8168-083E60C0CBF7}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {0F38272D-8BC4-4E8E-8168-083E60C0CBF7}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /Chapter04/Chapter4/Agent.fs: -------------------------------------------------------------------------------- 1 | namespace Agents 2 | 3 | open System 4 | 5 | // Type for our agent 6 | type Agent<'T> = MailboxProcessor<'T> 7 | 8 | // Control messages to be sent to agent 9 | type CounterMessage = 10 | | Update of float 11 | | Reset 12 | 13 | module Helpers = 14 | let genRandomNumber (n) = 15 | let rnd = new System.Random() 16 | float (rnd.Next(n, 100)) 17 | 18 | module MaxAgent = 19 | // Agent to keep track of max value and update GUI 20 | let sampleAgent = Agent.Start(fun inbox -> 21 | let rec loop max = async { 22 | let! msg = inbox.Receive() 23 | match msg with 24 | | Reset -> 25 | return! loop 0.0 26 | | Update value -> 27 | let max = Math.Max(max, value) 28 | 29 | Console.WriteLine("Max: " + max.ToString()) 30 | 31 | do! Async.Sleep(1000) 32 | return! loop max 33 | } 34 | loop 0.0) -------------------------------------------------------------------------------- /Chapter06/Script8.fsx: -------------------------------------------------------------------------------- 1 | open System 2 | open MathNet.Numerics 3 | open MathNet.Numerics.LinearAlgebra 4 | open MathNet.Numerics.LinearAlgebra.Double 5 | open MathNet.Numerics.Distributions 6 | 7 | /// Sample points 8 | let xdata = [ 0.0; 1.0; 2.0; 3.0; 4.0 ] 9 | let ydata = [ 1.0; 1.4; 1.6; 1.3; 0.9 ] 10 | 11 | let N = xdata.Length 12 | let order = 2 13 | 14 | /// Generating a Vandermonde row given input v 15 | let vandermondeRow v = [for x in [0..order] do yield v ** (float x)] 16 | 17 | /// Creating Vandermonde rows for each element in the list 18 | let vandermonde = xdata |> Seq.map vandermondeRow |> Seq.toList 19 | 20 | /// Create the A Matrix 21 | let A = vandermonde |> DenseMatrix.ofRowsList N (order + 1) 22 | A.Transpose() 23 | 24 | /// Create the Y Matrix 25 | let createYVector order l = [for x in [0..order] do yield l] 26 | let Y = (createYVector order ydata |> DenseMatrix.ofRowsList (order + 1) N).Transpose() 27 | 28 | /// Calculate coefficients using least squares 29 | let coeffs = (A.Transpose() * A).LU().Solve(A.Transpose() * Y).Column(0) 30 | 31 | let calculate x = (vandermondeRow(x) |> DenseVector.ofList) * coeffs 32 | 33 | let fitxs = [(Seq.min xdata).. 0.5 ..(Seq.max xdata)] 34 | let fitys = fitxs |> List.map calculate -------------------------------------------------------------------------------- /Chapter04/Chapter4/GUI.fs: -------------------------------------------------------------------------------- 1 | namespace GUI 2 | 3 | open System 4 | open System.Drawing 5 | open System.Windows.Forms 6 | open Agents 7 | 8 | // User interface form 9 | type public SampleForm() as form = 10 | inherit Form() 11 | 12 | let valueLabel = new Label(Location=new Point(25,15), AutoSize=true) 13 | let sendButton = new Button(Location=new Point(25,75)) 14 | let agent = MaxAgent.sampleAgent 15 | 16 | let initControls = 17 | valueLabel.Text <- "Press button to send value to agent." 18 | sendButton.Text <- "Send value to agent" 19 | do 20 | initControls 21 | 22 | form.Controls.Add(valueLabel) 23 | form.Controls.Add(sendButton) 24 | 25 | form.Text <- "SampleApp F#" 26 | 27 | sendButton.Click.AddHandler(new System.EventHandler 28 | (fun sender e -> form.eventStartButton_Click(sender, e))) 29 | 30 | // Event handlers 31 | member form.eventStartButton_Click(sender:obj, e:EventArgs) = 32 | let random = Helpers.genRandomNumber 5 33 | Console.WriteLine("Sending value to agent: " + random.ToString()) 34 | agent.Post(Update random) 35 | () -------------------------------------------------------------------------------- /Chapter05/Chapter5/mc1.fsx: -------------------------------------------------------------------------------- 1 | /// Monte Carlo implementation 2 | 3 | /// Convert the nr of days to years 4 | let days_to_years d = 5 | (float d) / 365.25 6 | 7 | /// Asset price at maturity for sample rnd 8 | // s: stock price 9 | // t: time to expiration in years 10 | // r: risk free interest rate 11 | // v: volatility 12 | // rnd: sample 13 | let price_for_sample s t r v rnd = 14 | s*exp((r-v*v/2.0)*t+v*rnd*sqrt(t)) 15 | 16 | /// For each sample we run the monte carlo simulation 17 | // s: stock price 18 | // x: strike price of option 19 | // t: time to expiration in years 20 | // r: risk free interest rate 21 | // v: volatility 22 | // samples: random samples as input to simulation 23 | let monte_carlo s x t r v (samples:seq) = 24 | samples 25 | |> Seq.map (fun rnd -> (price_for_sample s t r v rnd) - x) 26 | |> Seq.average 27 | 28 | ///// Generate sample sequence 29 | //let random = new System.Random() 30 | //let rnd() = random.NextDouble() 31 | //let data = [for i in 1 .. 1000 -> rnd() * 1.0] 32 | 33 | /// Monte carlo for call option 34 | //monte_carlo 58.60 60.0 0.5 0.01 0.3 data 35 | 36 | /// Generate sample sequence 37 | let random = new System.Random() 38 | let rnd() = random.NextDouble() 39 | let data = [for i in 1 .. 1000 -> rnd() * 1.0] 40 | 41 | /// Monte carlo for call option 42 | monte_carlo 58.60 60.0 0.5 0.01 0.3 data -------------------------------------------------------------------------------- /Chapter05/Chapter5/Script2.fsx: -------------------------------------------------------------------------------- 1 | #r "System.Windows.Forms.DataVisualization.dll" 2 | 3 | open System 4 | open System.Net 5 | open System.Windows.Forms 6 | open System.Windows.Forms.DataVisualization.Charting 7 | open Microsoft.FSharp.Control.WebExtensions 8 | open MathNet.Numerics.Distributions; 9 | 10 | // A normally distributed random generator 11 | let normd = new Normal(0.0, 1.0) 12 | 13 | // Create chart and form 14 | let chart = new Chart(Dock = DockStyle.Fill) 15 | let area = new ChartArea("Main") 16 | chart.ChartAreas.Add(area) 17 | 18 | let mainForm = new Form(Visible = true, TopMost = true, 19 | Width = 700, Height = 500) 20 | 21 | do mainForm.Text <- "Wiener process in F#" 22 | mainForm.Controls.Add(chart) 23 | 24 | // Create serie for stock price 25 | let wienerProcess = new Series("process") 26 | do wienerProcess.ChartType <- SeriesChartType.Line 27 | do wienerProcess.BorderWidth <- 2 28 | do wienerProcess.Color <- Drawing.Color.Red 29 | chart.Series.Add(wienerProcess) 30 | 31 | let random = new System.Random() 32 | let rnd() = random.NextDouble() 33 | //let data = [for i in 1 .. 10 -> rnd()] 34 | let T = 1.0 35 | let N = 500.0 36 | let dt:float = T / N 37 | 38 | /// Sequences represent infinite number of elements 39 | let W s = 40 | let rec loop x = seq { yield x; yield! loop (x + sqrt(dt)*normd.Sample()*s)} 41 | loop s 42 | 43 | wienerProcess.Points.Clear() 44 | do (Seq.take 100 (W 55.00)) |> Seq.iter (wienerProcess.Points.Add >> ignore) -------------------------------------------------------------------------------- /Chapter04/Chapter4/Script2.fsx: -------------------------------------------------------------------------------- 1 | #r "System.Windows.Forms.DataVisualization.dll" 2 | 3 | open System 4 | open System.Net 5 | open System.Windows.Forms 6 | open System.Windows.Forms.DataVisualization.Charting 7 | open Microsoft.FSharp.Control.WebExtensions 8 | 9 | // Create chart and form 10 | let chart = new Chart(Dock = DockStyle.Fill) 11 | let area = new ChartArea("Main") 12 | chart.ChartAreas.Add(area) 13 | 14 | let mainForm = new Form(Visible = true, TopMost = true, 15 | Width = 700, Height = 500) 16 | 17 | do mainForm.Text <- "Yahoo Finance data in F#" 18 | mainForm.Controls.Add(chart) 19 | 20 | // Create serie for stock price 21 | let stockPrice = new Series("stockPrice") 22 | do stockPrice.ChartType <- SeriesChartType.Line 23 | do stockPrice.BorderWidth <- 2 24 | do stockPrice.Color <- Drawing.Color.Red 25 | chart.Series.Add(stockPrice) 26 | 27 | // Create serie for moving average 28 | let movingAvg = new Series("movingAvg") 29 | do movingAvg.ChartType <- SeriesChartType.Line 30 | do movingAvg.BorderWidth <- 2 31 | do movingAvg.Color <- Drawing.Color.Blue 32 | chart.Series.Add(movingAvg) 33 | 34 | // Syncronous fetching (just one stock here) 35 | let fetchOne() = 36 | let uri = new System.Uri("http://ichart.finance.yahoo.com/table.csv?s=ORCL&d=9&e=23&f=2012&g=d&a=2&b=13&c=1986&ignore=.csv") 37 | let client = new WebClient() 38 | let html = client.DownloadString(uri) 39 | html 40 | 41 | // Parse CSV 42 | let getPrices() = 43 | let data = fetchOne() 44 | data.Split('\n') 45 | |> Seq.skip 1 46 | |> Seq.map (fun s -> s.Split(',')) 47 | |> Seq.map (fun s -> float s.[4]) 48 | |> Seq.truncate 2500 49 | 50 | // Calc moving average 51 | let movingAverage n (prices:seq) = 52 | prices 53 | |> Seq.windowed n 54 | |> Seq.map Array.sum 55 | |> Seq.map (fun a -> a / float n) 56 | 57 | // The plotting 58 | let sp = getPrices() 59 | do sp |> Seq.iter (stockPrice.Points.Add >> ignore) 60 | 61 | let ma = movingAverage 100 sp 62 | do ma |> Seq.iter (movingAvg.Points.Add >> ignore) -------------------------------------------------------------------------------- /Chapter04/Chapter4/Script3.fsx: -------------------------------------------------------------------------------- 1 | #r "System.Windows.Forms.DataVisualization.dll" 2 | 3 | open System 4 | open System.Net 5 | open System.Windows.Forms 6 | open System.Windows.Forms.DataVisualization.Charting 7 | open Microsoft.FSharp.Control.WebExtensions 8 | 9 | // Create chart and form 10 | let chart = new Chart(Dock = DockStyle.Fill) 11 | let area = new ChartArea("Main") 12 | chart.ChartAreas.Add(area) 13 | 14 | let mainForm = new Form(Visible = true, TopMost = true, 15 | Width = 700, Height = 500) 16 | 17 | do mainForm.Text <- "Yahoo Finance data in F#" 18 | mainForm.Controls.Add(chart) 19 | 20 | // Create serie for stock price 21 | let stockPrice = new Series("stockPrice") 22 | do stockPrice.ChartType <- SeriesChartType.Line 23 | do stockPrice.BorderWidth <- 2 24 | do stockPrice.Color <- Drawing.Color.Red 25 | chart.Series.Add(stockPrice) 26 | 27 | // Create serie for moving average 28 | let movingAvg = new Series("movingAvg") 29 | do movingAvg.ChartType <- SeriesChartType.Line 30 | do movingAvg.BorderWidth <- 2 31 | do movingAvg.Color <- Drawing.Color.Blue 32 | chart.Series.Add(movingAvg) 33 | 34 | // Syncronous fetching (just one stock here) 35 | let fetchOne() = 36 | let uri = new System.Uri("http://ichart.finance.yahoo.com/table.csv?s=ORCL&d=9&e=23&f=2012&g=d&a=2&b=13&c=1986&ignore=.csv") 37 | let client = new WebClient() 38 | let html = client.DownloadString(uri) 39 | html 40 | 41 | // Parse CSV 42 | let getPrices() = 43 | let data = fetchOne() 44 | data.Split('\n') 45 | |> Seq.skip 1 46 | |> Seq.map (fun s -> s.Split(',')) 47 | |> Seq.map (fun s -> float s.[4]) 48 | |> Seq.truncate 2500 49 | 50 | // Calc moving average 51 | let movingAverage n (prices:seq) = 52 | prices 53 | |> Seq.windowed n 54 | |> Seq.map Array.sum 55 | |> Seq.map (fun a -> a / float n) 56 | 57 | // The plotting 58 | let sp = getPrices() 59 | do sp |> Seq.iter (stockPrice.Points.Add >> ignore) 60 | 61 | let ma = movingAverage 100 sp 62 | do ma |> Seq.iter (movingAvg.Points.Add >> ignore) -------------------------------------------------------------------------------- /Chapter05/Chapter5/Script3.fsx: -------------------------------------------------------------------------------- 1 | /// Normal distribution 2 | 3 | open System 4 | open System.Net 5 | open System.Windows.Forms 6 | open System.Windows.Forms.DataVisualization.Charting 7 | open Microsoft.FSharp.Control.WebExtensions 8 | open MathNet.Numerics.Distributions; 9 | 10 | let normd = new Normal(0.0, 1.0) 11 | 12 | // Create chart and form 13 | let chart = new Chart(Dock = DockStyle.Fill) 14 | let area = new ChartArea("Main") 15 | chart.ChartAreas.Add(area) 16 | 17 | let mainForm = new Form(Visible = true, TopMost = true, 18 | Width = 700, Height = 500) 19 | 20 | do mainForm.Text <- "VIX-index 2000-01-01 to 2013-11-01" 21 | mainForm.Controls.Add(chart) 22 | 23 | // Create serie for stock price 24 | let stockPrice = new Series("stockPrice") 25 | do stockPrice.ChartType <- SeriesChartType.Line 26 | do stockPrice.BorderWidth <- 2 27 | do stockPrice.Color <- Drawing.Color.Red 28 | chart.Series.Add(stockPrice) 29 | 30 | // Create serie for moving average 31 | let movingAvg = new Series("movingAvg") 32 | do movingAvg.ChartType <- SeriesChartType.Line 33 | do movingAvg.BorderWidth <- 2 34 | do movingAvg.Color <- Drawing.Color.Blue 35 | chart.Series.Add(movingAvg) 36 | 37 | // Syncronous fetching (just one stock here) 38 | let fetchOne() = 39 | let uri = new System.Uri("http://ichart.finance.yahoo.com/table.csv?s=%5EVIX&a=00&b=1&c=2000&d=10&e=1&f=2013&g=d&ignore=.csv") 40 | let client = new WebClient() 41 | let html = client.DownloadString(uri) 42 | html 43 | 44 | // Parse CSV 45 | let getPrices() = 46 | let data = fetchOne() 47 | data.Split('\n') 48 | |> Seq.skip 1 49 | |> Seq.map (fun s -> s.Split(',')) 50 | |> Seq.map (fun s -> float s.[4]) 51 | |> Seq.truncate 2500 52 | 53 | // Calc moving average 54 | let movingAverage n (prices:seq) = 55 | prices 56 | |> Seq.windowed n 57 | |> Seq.map Array.sum 58 | |> Seq.map (fun a -> a / float n) 59 | 60 | // The plotting 61 | let sp = getPrices() 62 | do sp |> Seq.iter (stockPrice.Points.Add >> ignore) 63 | 64 | let ma = movingAverage 100 sp 65 | do ma |> Seq.iter (movingAvg.Points.Add >> ignore) -------------------------------------------------------------------------------- /Chapter07/Quote.fsx: -------------------------------------------------------------------------------- 1 | /// Type to represent market data, bid ask, latest aggregation 2 | type Quote = 3 | { 4 | bid : float 5 | ask : float 6 | } 7 | member this.midpoint() = (this.bid + this.ask) / 2.0 8 | 9 | let q = {bid = 1.40; ask = 1.45} : Quote 10 | q.midpoint() 11 | 12 | type Quote2 = 13 | { 14 | bid : float 15 | ask : float 16 | } 17 | member this.midpoint() = (this.bid + this.ask) / 2.0 18 | member this.spread() = abs(this.bid - this.ask) 19 | 20 | 21 | /// Change 22 | // Often data is just sent from the feed handler, as bid or ask 23 | type LightQuote = 24 | | Bid of float | Ask of float 25 | 26 | 27 | /// Brownian motion / Wiener process 28 | let random = new System.Random() 29 | let rnd() = random.NextDouble() 30 | let data = [for i in 1 .. 10 -> rnd()] 31 | let T = 1.0 32 | let N = 500.0 33 | let dt:float = T / N 34 | 35 | /// Recursion 36 | let primes = 37 | Seq.initInfinite (fun i -> i + 2) //need to skip 0 and 1 for isPrime 38 | |> Seq.map (fun i -> bigint(i)) 39 | 40 | let allEvens = 41 | let rec loop x = seq { yield x; yield! loop (x + 2) } 42 | loop 0;; 43 | 44 | Seq.take 5 allEvens 45 | 46 | /// Sequences represent infinite number of elements 47 | // p -> probability mean 48 | // s -> scaling factor 49 | let W p s = 50 | let rec loop x = seq { yield x; yield! loop (x + sqrt(dt)*(rnd()-(1.0-p))*s)} 51 | loop 0.0;; 52 | 53 | Seq.take 50 (W 0.6 10.0) 54 | 55 | #r "System.Windows.Forms.DataVisualization.dll" 56 | 57 | open System 58 | open System.Net 59 | open System.Windows.Forms 60 | open System.Windows.Forms.DataVisualization.Charting 61 | open Microsoft.FSharp.Control.WebExtensions 62 | 63 | // Create chart and form 64 | let chart = new Chart(Dock = DockStyle.Fill) 65 | let area = new ChartArea("Main") 66 | chart.ChartAreas.Add(area) 67 | 68 | let mainForm = new Form(Visible = true, TopMost = true, 69 | Width = 700, Height = 500) 70 | 71 | do mainForm.Text <- "Wiener process in F#" 72 | mainForm.Controls.Add(chart) 73 | 74 | // Create serie for stock price 75 | let wienerProcess = new Series("process") 76 | do wienerProcess.ChartType <- SeriesChartType.Line 77 | do wienerProcess.BorderWidth <- 2 78 | do wienerProcess.Color <- Drawing.Color.Red 79 | chart.Series.Add(wienerProcess) 80 | 81 | do (Seq.take 100 (W 0.55 100.0)) |> Seq.iter (wienerProcess.Points.Add >> ignore) 82 | 83 | -------------------------------------------------------------------------------- /Chapter09/Script1.fsx: -------------------------------------------------------------------------------- 1 | open System.IO 2 | open FSharp.Charting 3 | open System.Windows.Forms.DataVisualization.Charting 4 | 5 | let mlist = [for x in [0..10] do yield (x, x*2)] 6 | 7 | /// Plot values using FSharpChart 8 | fsi.AddPrinter(fun (ch:FSharp.Charting.ChartTypes.GenericChart) -> ch.ShowChart(); "FSharpChartingSmile") 9 | Chart.Line(mlist) 10 | 11 | //Chart.Line(fits).WithTitle("Volatility Smile") 12 | 13 | /// Payoff for European call option 14 | // s: stock price 15 | // k: strike price of option 16 | let payoffCall k s = 17 | max (s-k) 0.0 18 | 19 | /// Payoff for European Put option 20 | // s: stock price 21 | // k: strike price of option 22 | let payoffPut k s = 23 | max (k-s) 0.0 24 | 25 | // Calculate the payoff of a given option 26 | let payoff payoffFunction = 27 | [ for s in 0.0 .. 10.0 .. 100.0 -> s, payoffFunction s ] 28 | 29 | // Compare the payoff of call and put options 30 | let callPayoff = payoff (payoffCall 50.0) 31 | let putPayoff = payoff (payoffPut 50.0) 32 | 33 | let chart = Chart.Combine [Chart.Line(callPayoff); Chart.Line(putPayoff).WithTitle("Payoff diagram")] 34 | 35 | //Chart.Line(callPayoff).WithTitle("Payoff - Call Option") 36 | Chart.Line(putPayoff).WithTitle("Payoff - Put Option") 37 | 38 | // Display the payoff of European call and put options 39 | let chart = Chart.Combine [ Chart.Line(callPayoff, Name="Call option").WithLegend(); Chart.Line(putPayoff, Name="Put option").WithLegend().WithTitle("Payoff diagram") ] 40 | chart.WithTitle("Payoff diagram") 41 | 42 | //////////// 43 | 44 | /// Payoff for long straddle 45 | // s: stock price 46 | // k: strike price of option 47 | let longStraddle k s = 48 | (payoffCall k s) + 49 | (payoffPut k s) 50 | 51 | /// Payoff for Short straddle 52 | // s: stock price 53 | // k: strike price of option 54 | let shortStraddle k s = 55 | -(payoffCall k s) + 56 | -(payoffPut k s) 57 | 58 | /// Payoff for long butterfly 59 | // s: stock price 60 | // h: high price 61 | // l: low price 62 | let longButterfly l h s = 63 | (payoffCall l s) + 64 | (payoffCall h s) - 65 | 2.0 * (payoffCall ((l + h) / 2.0) s) 66 | 67 | /// Payoff for short butterfly 68 | // s: stock price 69 | // h: high price 70 | // l: low price 71 | let shortButterfly l h s = 72 | -(payoffCall l s) + 73 | -(payoffCall h s) - 74 | 2.0 * -(payoffCall ((l + h) / 2.0) s) 75 | 76 | Chart.Line(payoff (longStraddle 50.0)).WithTitle("Payoff - Long straddle") 77 | 78 | Chart.Line(payoff (shortStraddle 50.0)).WithTitle("Payoff - Short straddle") 79 | 80 | Chart.Line(payoff (longButterfly 20.0 80.0)).WithTitle("Payoff - Long butterfly") 81 | 82 | Chart.Line(payoff (shortButterfly 20.0 80.0)).WithTitle("Payoff - Short butterfly") -------------------------------------------------------------------------------- /Chapter06/Script5.fsx: -------------------------------------------------------------------------------- 1 | open System.IO 2 | open FSharp.Charting 3 | open System.Windows.Forms.DataVisualization.Charting 4 | 5 | open MathNet.Numerics 6 | open MathNet.Numerics.LinearAlgebra 7 | open MathNet.Numerics.LinearAlgebra.Double 8 | open MathNet.Numerics.Distributions 9 | 10 | let filePath = @"smile_data.csv" 11 | 12 | /// Split row on commas 13 | let splitCommas (l:string) = 14 | l.Split(',') 15 | 16 | /// Read a file into a string array 17 | let openFile (name : string) = 18 | try 19 | let content = File.ReadAllLines(name) 20 | content |> Array.toList 21 | with 22 | | :? System.IO.FileNotFoundException as e -> printfn "Exception! %s " e.Message; ["empty"] 23 | 24 | /// Read the data from a CSV file and returns 25 | /// a tuple of strike price and implied volatility% 26 | let readVolatilityData = 27 | openFile filePath 28 | |> List.map splitCommas 29 | |> List.map (fun cols -> (cols.[2], cols.[3])) 30 | 31 | /// 83.2 32 | /// Calculates moneyness and parses strings into numbers 33 | let calcMoneyness spot list = 34 | list 35 | |> List.map (fun (strike, imp) -> (spot / (float strike), (float imp))) 36 | 37 | let list = readVolatilityData 38 | let mlist = calcMoneyness 83.2 list 39 | 40 | /// Plot values using FSharpChart 41 | fsi.AddPrinter(fun (ch:FSharp.Charting.ChartTypes.GenericChart) -> ch.ShowChart(); "FSharpChartingSmile") 42 | Chart.Point(mlist) 43 | 44 | /// Final step - Plot data 45 | 46 | /// Sample points 47 | //let xdata = [ 0.0; 1.0; 2.0; 3.0; 4.0 ] 48 | //let ydata = [ 1.0; 1.4; 1.6; 1.3; 0.9 ] 49 | 50 | let xdata = mlist |> Seq.map (fun (x, _) -> x) |> Seq.toList 51 | let ydata = mlist |> Seq.map (fun (_, y) -> y) |> Seq.toList 52 | 53 | let N = xdata.Length 54 | let order = 2 55 | 56 | /// Generating a Vandermonde row given input v 57 | let vandermondeRow v = [for x in [0..order] do yield v ** (float x)] 58 | 59 | /// Creating Vandermonde rows for each element in the list 60 | let vandermonde = xdata |> Seq.map vandermondeRow |> Seq.toList 61 | 62 | /// Create the A Matrix 63 | let A = vandermonde |> DenseMatrix.ofRowsList N (order + 1) 64 | A.Transpose() 65 | 66 | /// Create the Y Matrix 67 | let createYVector order l = [for x in [0..order] do yield l] 68 | let Y = (createYVector order ydata |> DenseMatrix.ofRowsList (order + 1) N).Transpose() 69 | 70 | /// Calculate coefficients using least squares 71 | let coeffs = (A.Transpose() * A).LU().Solve(A.Transpose() * Y).Column(0) 72 | 73 | let calculate x = (vandermondeRow(x) |> DenseVector.ofList) * coeffs 74 | 75 | let fitxs = [(Seq.min xdata).. 0.01 ..(Seq.max xdata)] 76 | let fitys = fitxs |> List.map calculate 77 | let fits = [for x in [(Seq.min xdata).. 0.01 ..(Seq.max xdata)] do yield (x, calculate x)] 78 | 79 | let chart = Chart.Combine [Chart.Point(mlist); Chart.Line(fits).WithTitle("Volatility Smile")] -------------------------------------------------------------------------------- /Chapter09/Script2.fsx: -------------------------------------------------------------------------------- 1 | open System.IO 2 | open FSharp.Charting 3 | open System.Windows.Forms.DataVisualization.Charting 4 | 5 | open MathNet.Numerics 6 | open MathNet.Numerics.LinearAlgebra 7 | open MathNet.Numerics.LinearAlgebra.Double 8 | open MathNet.Numerics.Distributions 9 | 10 | let filePath = @"smile_data.csv" 11 | 12 | /// Split row on commas 13 | let splitCommas (l:string) = 14 | l.Split(',') 15 | 16 | /// Read a file into a string array 17 | let openFile (name : string) = 18 | try 19 | let content = File.ReadAllLines(name) 20 | content |> Array.toList 21 | with 22 | | :? System.IO.FileNotFoundException as e -> printfn "Exception! %s " e.Message; ["empty"] 23 | 24 | /// Read the data from a CSV file and returns 25 | /// a tuple of strike price and implied volatility% 26 | // Filter for just one expiration date 27 | let readVolatilityData date = 28 | openFile filePath 29 | |> List.map splitCommas 30 | |> List.filter (fun cols -> cols.[1] = date) 31 | |> List.map (fun cols -> (cols.[2], cols.[3])) 32 | 33 | /// 83.2 34 | /// Calculates moneyness and parses strings into numbers 35 | let calcMoneyness spot list = 36 | list 37 | |> List.map (fun (strike, imp) -> (spot / (float strike), (float imp))) 38 | 39 | // Filter out one expiration date -- 2013-12-20 40 | let list = readVolatilityData "2013-12-20" 41 | 42 | // Filter on moneyness, 0.5 to 1.5 43 | let mlist = calcMoneyness 83.2 list |> List.filter (fun (x, y) -> x > 0.5 && x < 1.5) 44 | 45 | /// Plot values using FSharpChart 46 | fsi.AddPrinter(fun (ch:FSharp.Charting.ChartTypes.GenericChart) -> ch.ShowChart(); "FSharpChartingSmile") 47 | Chart.Point(mlist) 48 | 49 | /// Final step - Plot data 50 | 51 | let xdata = mlist |> Seq.map (fun (x, _) -> x) |> Seq.toList 52 | let ydata = mlist |> Seq.map (fun (_, y) -> y) |> Seq.toList 53 | 54 | let N = xdata.Length 55 | let order = 2 56 | 57 | /// Generating a Vandermonde row given input v 58 | let vandermondeRow v = [for x in [0..order] do yield v ** (float x)] 59 | 60 | /// Creating Vandermonde rows for each element in the list 61 | let vandermonde = xdata |> Seq.map vandermondeRow |> Seq.toList 62 | 63 | /// Create the A Matrix 64 | let A = vandermonde |> DenseMatrix.ofRowsList N (order + 1) 65 | A.Transpose() 66 | 67 | /// Create the Y Matrix 68 | let createYVector order l = [for x in [0..order] do yield l] 69 | let Y = (createYVector order ydata |> DenseMatrix.ofRowsList (order + 1) N).Transpose() 70 | 71 | /// Calculate coefficients using least squares 72 | let coeffs = (A.Transpose() * A).LU().Solve(A.Transpose() * Y).Column(0) 73 | 74 | let calculate x = (vandermondeRow(x) |> DenseVector.ofList) * coeffs 75 | 76 | let fitxs = [(Seq.min xdata).. 0.01 ..(Seq.max xdata)] 77 | let fitys = fitxs |> List.map calculate 78 | let fits = [for x in [(Seq.min xdata).. 0.01 ..(Seq.max xdata)] do yield (x, calculate x)] 79 | 80 | let chart = Chart.Combine [Chart.Point(mlist); Chart.Line(fits).WithTitle("Volatility Smile - 2nd degree polynomial")] -------------------------------------------------------------------------------- /Chapter06/Script4.fsx: -------------------------------------------------------------------------------- 1 | open System 2 | open System.Net 3 | 4 | /// Calculate the standard deviation 5 | let stddev(values:seq) = 6 | values 7 | |> Seq.fold (fun acc x -> acc + (1.0 / float (Seq.length values)) * (x - (Seq.average values)) ** 2.0) 0.0 8 | |> sqrt 9 | 10 | /// Calculate logarithmic returns 11 | let calcDailyReturns(prices:seq) = 12 | prices 13 | |> Seq.pairwise 14 | |> Seq.map (fun (x, y) -> log (x / y)) 15 | 16 | /// Annualized volatility 17 | let annualVolatility(returns:seq) = 18 | let sd = stddev(calcDailyReturns(returns)) 19 | let days = Seq.length(returns) 20 | sd * sqrt(float days) 21 | 22 | let formatLeadingZero(number:int):String = 23 | String.Format("{0:00}", number) 24 | 25 | /// Helper function to create the Yahoo-finance URL 26 | let constructURL(symbol, fromDate:DateTime, toDate:DateTime) = 27 | let fm = formatLeadingZero(fromDate.Month-1) 28 | let fd = formatLeadingZero(fromDate.Day) 29 | let fy = formatLeadingZero(fromDate.Year) 30 | let tm = formatLeadingZero(toDate.Month-1) 31 | let td = formatLeadingZero(toDate.Day) 32 | let ty = formatLeadingZero(toDate.Year) 33 | "http://ichart.finance.yahoo.com/table.csv?s=" + symbol + "&d=" + tm + "&e=" + td + "&f=" + ty + "&g=d&a=" + fm + "&b=" + fd + "&c=" + fy + "&ignore=.csv" 34 | 35 | /// Synchronous fetching (just one request) 36 | let fetchOne symbol fromDate toDate = 37 | let url = constructURL(symbol, fromDate, toDate) 38 | let uri = new System.Uri(url) 39 | let client = new WebClient() 40 | let html = client.DownloadString(uri) 41 | html 42 | 43 | /// Parse CSV 44 | let getPrices stock fromDate toDate = 45 | let data = fetchOne stock fromDate toDate 46 | data.Trim().Split('\n') 47 | |> Seq.skip 1 48 | |> Seq.map (fun s -> s.Split(',')) 49 | |> Seq.map (fun s -> float s.[4]) 50 | |> Seq.takeWhile (fun s -> s >= 0.0) 51 | 52 | /// Returns a formatted string with volatility for a stock 53 | let getAnnualizedVol stock fromStr toStr = 54 | let prices = getPrices stock (System.DateTime.Parse fromStr) (System.DateTime.Parse toStr) 55 | let vol = Math.Round(annualVolatility(prices) * 100.0, 2) 56 | sprintf "Volatility for %s is %.2f %%" stock vol 57 | 58 | getAnnualizedVol "MSFT" "2013-01-01" "2013-08-29" 59 | // val it : string = "Volatility for MSFT is 21.30 %" 60 | 61 | getAnnualizedVol "ORCL" "2013-01-01" "2013-08-29" 62 | // val it : string = "Volatility for ORCL is 20.44 %" 63 | 64 | getAnnualizedVol "GOOG" "2013-01-01" "2013-08-29" 65 | // val it : string = "Volatility for GOOG is 14.80 %" 66 | 67 | getAnnualizedVol "EBAY" "2013-01-01" "2013-08-29" 68 | // val it : string = "Volatility for EBAY is 20.82 %" 69 | 70 | getAnnualizedVol "AAPL" "2013-01-01" "2013-08-29" 71 | // val it : string = "Volatility for AAPL is 25.16 %" 72 | 73 | getAnnualizedVol "AMZN" "2013-01-01" "2013-08-29" 74 | // val it : string = "Volatility for AMZN is 21.10 %" 75 | 76 | getAnnualizedVol "^GSPC" "2013-01-01" "2013-08-29" 77 | // val it : string = "Volatility for ^GSPC is 9.15 %" -------------------------------------------------------------------------------- /Chapter05/Chapter5/Script1.fsx: -------------------------------------------------------------------------------- 1 | // Another example with Bollinger Bands 2 | #r "System.Windows.Forms.DataVisualization.dll" 3 | 4 | open System 5 | open System.Net 6 | open System.Windows.Forms 7 | open System.Windows.Forms.DataVisualization.Charting 8 | open Microsoft.FSharp.Control.WebExtensions 9 | 10 | // Create chart and form 11 | let chart = new Chart(Dock = DockStyle.Fill) 12 | let area = new ChartArea("Main") 13 | chart.ChartAreas.Add(area) 14 | 15 | // Add legends 16 | chart.Legends.Add(new Legend()) 17 | 18 | let mainForm = new Form(Visible = true, TopMost = true, 19 | Width = 700, Height = 500) 20 | 21 | do mainForm.Text <- "Yahoo Finance data in F# - Bollinger Bands" 22 | mainForm.Controls.Add(chart) 23 | 24 | // Create serie for stock price 25 | let stockPrice = new Series("stockPrice") 26 | do stockPrice.ChartType <- SeriesChartType.Line 27 | do stockPrice.BorderWidth <- 2 28 | do stockPrice.Color <- Drawing.Color.DarkGray 29 | chart.Series.Add(stockPrice) 30 | 31 | // Create serie for moving average 32 | let movingAvg = new Series("movingAvg") 33 | do movingAvg.ChartType <- SeriesChartType.Line 34 | do movingAvg.BorderWidth <- 2 35 | do movingAvg.Color <- Drawing.Color.Blue 36 | chart.Series.Add(movingAvg) 37 | 38 | // Create serie for upper band 39 | let upperBand = new Series("upperBand") 40 | do upperBand.ChartType <- SeriesChartType.Line 41 | do upperBand.BorderWidth <- 2 42 | do upperBand.Color <- Drawing.Color.Red 43 | chart.Series.Add(upperBand) 44 | 45 | // Create serie for lower band 46 | let lowerBand = new Series("lowerBand") 47 | do lowerBand.ChartType <- SeriesChartType.Line 48 | do lowerBand.BorderWidth <- 2 49 | do lowerBand.Color <- Drawing.Color.Green 50 | chart.Series.Add(lowerBand) 51 | 52 | // Syncronous fetching (just one stock here) 53 | let fetchOne() = 54 | let uri = new System.Uri("http://ichart.finance.yahoo.com/table.csv?s=ORCL&d=9&e=23&f=2012&g=d&a=2&b=13&c=2012&ignore=.csv") 55 | let client = new WebClient() 56 | let html = client.DownloadString(uri) 57 | html 58 | 59 | // Parse CSV 60 | let getPrices() = 61 | let data = fetchOne() 62 | data.Split('\n') 63 | |> Seq.skip 1 64 | |> Seq.map (fun s -> s.Split(',')) 65 | |> Seq.map (fun s -> float s.[4]) 66 | |> Seq.truncate 2500 67 | 68 | // Calc moving average 69 | let movingAverage n (prices:seq) = 70 | prices 71 | |> Seq.windowed n 72 | |> Seq.map Array.sum 73 | |> Seq.map (fun a -> a / float n) 74 | 75 | // Stddev 76 | let stddev2(values:seq) = 77 | let avg = Seq.average values 78 | values 79 | |> Seq.fold (fun acc x -> acc + (1.0 / float (Seq.length values)) * (x - avg) ** 2.0) 0.0 80 | |> sqrt 81 | 82 | let movingStdDev n (prices:seq) = 83 | prices 84 | |> Seq.windowed n 85 | |> Seq.map stddev2 86 | 87 | // The plotting 88 | let sp = getPrices() 89 | do sp |> Seq.iter (stockPrice.Points.Add >> ignore) 90 | 91 | let ma = movingAverage 100 sp 92 | do ma |> Seq.iter (movingAvg.Points.Add >> ignore) 93 | 94 | // Bollinger bands, K = 2.0 95 | let ub = movingStdDev 100 sp 96 | // Upper 97 | Seq.zip ub ma |> Seq.map (fun (a,b) -> b + 2.0 * a) |> Seq.iter (upperBand.Points.Add >> ignore) 98 | // Lower 99 | Seq.zip ub ma |> Seq.map (fun (a,b) -> b - 2.0 * a) |> Seq.iter (lowerBand.Points.Add >> ignore) -------------------------------------------------------------------------------- /Chapter04/Chapter4/Chapter4.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 7f1ad7e2-45c6-4685-a5f6-6a2ec8f92769 9 | Exe 10 | Chapter4 11 | Chapter4 12 | v4.5 13 | Chapter4 14 | 15 | 16 | true 17 | full 18 | false 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | 3 23 | AnyCPU 24 | bin\Debug\Chapter4.XML 25 | true 26 | 27 | 28 | pdbonly 29 | true 30 | true 31 | bin\Release\ 32 | TRACE 33 | 3 34 | AnyCPU 35 | bin\Release\Chapter4.XML 36 | true 37 | 38 | 39 | 40 | 41 | True 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 11 59 | 60 | 61 | 68 | -------------------------------------------------------------------------------- /Chapter05/Chapter5/Chapter5.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 0f38272d-8bc4-4e8e-8168-083e60c0cbf7 9 | Exe 10 | Chapter5 11 | Chapter5 12 | v4.5 13 | Chapter5 14 | 15 | 16 | true 17 | full 18 | false 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | 3 23 | AnyCPU 24 | bin\Debug\Chapter5.XML 25 | true 26 | 27 | 28 | pdbonly 29 | true 30 | true 31 | bin\Release\ 32 | TRACE 33 | 3 34 | AnyCPU 35 | bin\Release\Chapter5.XML 36 | true 37 | 38 | 39 | 40 | ..\packages\MathNet.Numerics.2.6.1\lib\net40\MathNet.Numerics.dll 41 | True 42 | 43 | 44 | ..\packages\MathNet.Numerics.2.6.1\lib\net40\MathNet.Numerics.IO.dll 45 | True 46 | 47 | 48 | 49 | True 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 11 62 | 63 | 64 | 71 | -------------------------------------------------------------------------------- /Chapter05/Chapter5/chapter5.fsx: -------------------------------------------------------------------------------- 1 | // FSChart 2 | #r @"System.Windows.Forms.DataVisualization.dll" 3 | 4 | open System 5 | open System.Net 6 | open FSharp.Charting 7 | open Microsoft.FSharp.Control.WebExtensions 8 | open System.Windows.Forms.DataVisualization.Charting 9 | 10 | module FSharpCharting = 11 | fsi.AddPrinter(fun (ch:FSharp.Charting.ChartTypes.GenericChart) -> ch.ShowChart(); "FSharpCharting") 12 | 13 | // Syncronous fetching (just one stock here) 14 | let fetchOne() = 15 | let uri = new System.Uri("http://ichart.finance.yahoo.com/table.csv?s=ORCL&d=9&e=23&f=2012&g=d&a=2&b=13&c=2012&ignore=.csv") 16 | let client = new WebClient() 17 | let html = client.DownloadString(uri) 18 | html 19 | 20 | // Parse CSV and re-arrange O,H,L,C - > H,L,O,C 21 | let getOHLCPrices() = 22 | let data = fetchOne() 23 | data.Split('\n') 24 | |> Seq.skip 1 25 | |> Seq.map (fun s -> s.Split(',')) 26 | |> Seq.map (fun s -> s.[0], float s.[2], float s.[3], float s.[1], float s.[4]) 27 | |> Seq.truncate 50 28 | 29 | // Candlestick chart price range specified 30 | let ohlcPrices = getOHLCPrices() |> Seq.toList 31 | Chart.Candlestick(ohlcPrices).WithYAxis(Max = 34.0, Min = 30.0) 32 | 33 | /// Histogram 34 | #r @"System.Windows.Forms.DataVisualization.dll" 35 | #r @"C:\Users\Niklas\Documents\Visual Studio 2012\Projects\Chapter5\packages\FSharp.Charting.0.87\lib\net40\FSharp.Charting.dll" 36 | #r @"C:\Users\Niklas\Documents\Visual Studio 2012\Projects\Chapter5\packages\MathNet.Numerics.2.6.1\lib\net40\MathNet.Numerics.dll" 37 | 38 | open System 39 | open MathNet.Numerics 40 | open MathNet.Numerics.Distributions 41 | open MathNet.Numerics.Statistics 42 | open FSharp.Charting 43 | 44 | module FSharpCharting2 = 45 | fsi.AddPrinter(fun (ch:FSharp.Charting.ChartTypes.GenericChart) -> ch.ShowChart(); "FSharpCharting") 46 | 47 | let dist = new Normal(0.0, 1.0) 48 | let samples = dist.Samples() |> Seq.take 10000 |> Seq.toList 49 | let histogram = new Histogram(samples, 35); 50 | 51 | let getValues = 52 | let bucketWidth = Math.Abs(histogram.LowerBound - histogram.UpperBound) / (float histogram.BucketCount) 53 | [0..(histogram.BucketCount-1)] 54 | |> Seq.map (fun i -> (histogram.Item(i).LowerBound + histogram.Item(i).UpperBound)/2.0, histogram.Item(i).Count) 55 | 56 | Chart.Column getValues 57 | 58 | // 59 | type Quote = 60 | { 61 | bid : float 62 | ask : float 63 | } 64 | 65 | let q1 : Quote = {bid = 100.0; ask = 200.0} 66 | 67 | let (didParse, value) = Int32.TryParse("123") 68 | 69 | let (bid, ask) = (100.0, 110.0) 70 | bid 71 | ask 72 | 73 | let (x, y, _) = (3.0, 2.0, 4.0) 74 | 75 | let (|RegexContains|_|) pattern input = 76 | let matches = System.Text.RegularExpressions.Regex.Matches(input, pattern) 77 | if matches.Count > 0 then Some [ for m in matches -> m.Value ] 78 | else None 79 | 80 | let testString = function 81 | | RegexContains "http://\S+" urls -> printfn "Got urls: %A" urls 82 | | RegexContains "[^@]@[^.]+\.\W+" emails -> printfn "Got email address: %A" emails 83 | | RegexContains "\d+" numbers -> printfn "Got numbers: %A" numbers 84 | | _ -> printfn "Didn't find anything." 85 | 86 | let (|RegexNumber|_|) input = 87 | let matches = System.Text.RegularExpressions.Regex.Matches(input, "\d+.\d+") 88 | if matches.Count > 0 then Some [ for m in matches -> m.Value ] 89 | else None 90 | 91 | let testNumbers = function 92 | | RegexNumber "1.21" -> printfn "Got urls: %A" 1 93 | | RegexNumber "not a number" 94 | | _ -> "nothing" 95 | 96 | let (|Integer|_|) str = 97 | match System.Int32.TryParse(str) with 98 | | (true, int) -> Some(int) 99 | | _ -> None 100 | 101 | let (|Double|_|) str = 102 | match System.Double.TryParse(str) with 103 | | (true, num) -> Some(num) 104 | | _ -> None 105 | 106 | let testNumbers a = function 107 | | Double a -> printfn "apa %A" a 108 | | Integer a -> printfn "apa %A" a 109 | | _ -> printfn "nothing" 110 | 111 | testNumbers "1.0" 112 | testNumbers "1" 113 | 114 | // create an active pattern 115 | let (|Integer|_|) str = 116 | match System.Int32.TryParse(str) with 117 | | (true,num) -> Some(num) 118 | | _ -> None 119 | 120 | // create an active pattern 121 | let (|Double|_|) str = 122 | match System.Double.TryParse(str) with 123 | | (true,num) -> Some(num) 124 | | _ -> None 125 | 126 | let testParse numberStr = 127 | match numberStr with 128 | | Integer num -> printfn "Parsed an integer '%A'" num 129 | | Double num -> printfn "Parsed a double '%A'" num 130 | | _ -> printfn "Couldn't parse string: %A" numberStr 131 | 132 | testParse "1.0" 133 | testParse "1" -------------------------------------------------------------------------------- /Chapter03/Chapter3.fsx: -------------------------------------------------------------------------------- 1 | /// Linear regression using least squares 2 | 3 | open System 4 | open MathNet.Numerics 5 | open MathNet.Numerics.LinearAlgebra 6 | open MathNet.Numerics.LinearAlgebra.Double 7 | open MathNet.Numerics.Distributions 8 | 9 | let X = DenseMatrix.ofColumnsList 5 2 [ List.init 5 (fun i -> 1.0); [ 10.0; 20.0; 30.0; 40.0; 50.0 ] ] 10 | X 11 | let y = DenseVector [| 8.0; 21.0; 32.0; 40.0; 49.0 |] 12 | let p = X.QR().Solve(y) 13 | 14 | printfn "X: %A" X 15 | printfn "y: %s" (y.ToString()) 16 | printfn "p: %s" (p.ToString()) 17 | 18 | let (a, b) = (p.[0], p.[1]) 19 | 20 | 21 | ///////////////////////// Statistics 22 | 23 | let random = new System.Random() 24 | let rnd() = random.NextDouble() 25 | let data = [for i in 1 .. 5 -> rnd() * 10.0] 26 | 27 | let avg = data |> Seq.average 28 | let sum = data |> Seq.sum 29 | 30 | let min = data |> Seq.min 31 | let max = data |> Seq.max 32 | 33 | //// 34 | let calcDailyReturns(prices:seq) = 35 | prices 36 | |> Seq.pairwise 37 | |> Seq.map (fun (x, y) -> log (x / y)) 38 | 39 | let variance(values:seq) = 40 | values 41 | |> Seq.map (fun x -> (1.0 / float (Seq.length values)) * (x - (Seq.average values)) ** 2.0) 42 | |> Seq.sum 43 | 44 | variance [1.0 .. 6.0] 45 | 46 | let stddev1(values:seq) = 47 | sqrt(variance(values)) 48 | 49 | let stddev2(values:seq) = 50 | values 51 | |> Seq.fold (fun acc x -> acc + (1.0 / float (Seq.length values)) * (x - (Seq.average values)) ** 2.0) 0.0 52 | |> sqrt 53 | 54 | stddev1 [2.0; 4.0; 4.0; 4.0; 5.0; 5.0; 7.0; 9.0] 55 | stddev2 [2.0; 4.0; 4.0; 4.0; 5.0; 5.0; 7.0; 9.0] 56 | 57 | let random = new System.Random() 58 | let rnd() = random.NextDouble() 59 | let data = [for i in 1 .. 100 -> rnd() * 10.0] 60 | 61 | let var = variance data 62 | let std = stddev2 data 63 | 64 | 65 | ///////////////////////// Regression with noisy data 66 | 67 | open System 68 | open MathNet.Numerics 69 | open MathNet.Numerics.LinearAlgebra 70 | open MathNet.Numerics.LinearAlgebra.Double 71 | open MathNet.Numerics.Distributions 72 | 73 | /// Define our target functions 74 | let f1 x = Math.Sqrt(Math.Exp(x)) 75 | let f2 x = SpecialFunctions.DiGamma(x*x) 76 | 77 | /// Sample points 78 | let xdata = [ 1.0 .. 1.0 .. 10.0 ] 79 | 80 | /// Create data samples, with chosen parameters and with gaussian noise added 81 | let fy (noise:IContinuousDistribution) x = 2.5*f1(x) - 4.0*f2(x) + noise.Sample() 82 | let ydata = xdata |> List.map (fy (Normal.WithMeanVariance(0.0,2.0))) 83 | 84 | /// Build matrix form 85 | let X = 86 | [ 87 | xdata |> List.map f1 88 | xdata |> List.map f2 89 | ] |> DenseMatrix.ofColumnsList 10 2 90 | let y = DenseVector.ofList ydata 91 | 92 | /// Solve 93 | let p = X.QR().Solve(y) 94 | 95 | printfn "X: %A" X 96 | printfn "y: %s" (y.ToString()) 97 | printfn "p: %s" (p.ToString()) 98 | 99 | (p.[0], p.[1]) 100 | 101 | ///////////////////////// Implementing algorithms 102 | 103 | let rec bisect n N (f:float -> float) (a:float) (b:float) (t:float) : float = 104 | if n >= N then -1.0 105 | else 106 | let c = (a + b) / 2.0 107 | if f(c) = 0.0 || (b - a) / 2.0 < t then 108 | // Solution found 109 | c 110 | else 111 | if sign(f(c)) = sign(f(a)) then 112 | bisect (n + 1) N f c b t 113 | else 114 | bisect (n + 1) N f a c t 115 | 116 | let f = (fun x -> (x**2.0 - x - 6.0)) 117 | f(-2.0) 118 | f(3.0) 119 | 120 | let first = bisect 0 25 f -10.0 0.0 0.01 121 | let second = bisect 0 25 f 0.0 10.0 0.01 122 | 123 | first;; 124 | second;; 125 | 126 | /// Secant methods (Updated version) 127 | 128 | let rec secant n N (f:float -> float) (x0:float) (x1:float) (x2:float) : float = 129 | if n >= N then x0 130 | else 131 | let x = x1 - (f(x1))*((x1 - x0)/(f(x1) - f(x0))) 132 | secant (n + 1) N f x x0 x2 133 | 134 | let f = (fun x -> (x**2.0 - 612.0)) 135 | 136 | secant 0 10 f 0.0 10.0 30.0 137 | 138 | ///////////////////////// Statistics 139 | 140 | /// Helpers to generate random numbers 141 | let random = new Random() 142 | let rnd() = random.NextDouble() 143 | let data = [for i in 1 .. 500 -> rnd() * 10.0] 144 | 145 | /// Calculates the variance of a sequence 146 | let variance(values:seq) = 147 | values 148 | |> Seq.map (fun x -> (1.0 / float (Seq.length values)) * (x - (Seq.average values)) ** 2.0) 149 | |> Seq.sum 150 | 151 | /// Calculates the standard deviation of a sequence 152 | let stddev(values:seq) = 153 | values 154 | |> Seq.fold (fun acc x -> acc + (1.0 / float (Seq.length values)) * (x - (Seq.average values)) ** 2.0) 0.0 155 | |> sqrt 156 | 157 | let avg = data |> Seq.average 158 | let sum = data |> Seq.sum 159 | let min = data |> Seq.min 160 | let max = data |> Seq.max 161 | let var = data |> variance 162 | let std = data |> stddev -------------------------------------------------------------------------------- /Chapter01/table.csv: -------------------------------------------------------------------------------- 1 | Date,Open,High,Low,Close,Volume,Adj Close 2 | 2013-06-06,51.15,51.66,50.83,51.52,9848400,51.52 3 | 2013-06-05,52.57,52.68,50.91,51.36,14462900,51.36 4 | 2013-06-04,53.74,53.75,52.22,52.59,10614700,52.59 5 | 2013-06-03,53.86,53.89,52.40,53.41,13127900,53.41 6 | 2013-05-31,54.70,54.91,53.99,54.10,12809700,54.10 7 | 2013-05-30,55.01,55.69,54.96,55.10,8751200,55.10 8 | 2013-05-29,55.15,55.40,54.53,55.05,8693700,55.05 9 | 2013-05-28,55.25,56.25,55.21,55.53,9747600,55.53 10 | 2013-05-24,53.80,54.65,53.57,54.64,7310000,54.64 11 | 2013-05-23,53.69,54.56,53.32,54.48,9468900,54.48 12 | 2013-05-22,55.32,55.82,54.13,54.40,11451600,54.40 13 | 2013-05-21,55.96,56.11,54.94,54.95,13019300,54.95 14 | 2013-05-20,56.56,56.66,55.76,55.95,8759600,55.95 15 | 2013-05-17,56.49,57.11,56.47,56.71,11558600,56.71 16 | 2013-05-16,56.80,56.90,55.72,55.80,8521500,55.80 17 | 2013-05-15,56.27,56.68,56.06,56.65,8568100,56.65 18 | 2013-05-14,55.15,56.41,55.14,56.27,9227100,56.27 19 | 2013-05-13,55.79,55.80,55.09,55.33,7220600,55.33 20 | 2013-05-10,55.39,55.82,55.21,55.65,7125400,55.65 21 | 2013-05-09,54.99,55.99,54.79,55.17,9298100,55.17 22 | 2013-05-08,54.01,54.98,53.77,54.96,7027500,54.96 23 | 2013-05-07,53.93,54.13,53.10,53.94,9680000,53.94 24 | 2013-05-06,54.06,54.82,53.76,54.09,6354000,54.09 25 | 2013-05-03,54.00,54.51,53.60,54.21,7207400,54.21 26 | 2013-05-02,52.42,53.70,52.03,53.55,9885100,53.55 27 | 2013-05-01,52.38,52.87,51.92,52.40,7917200,52.40 28 | 2013-04-30,52.67,53.14,52.13,52.39,9056900,52.39 29 | 2013-04-29,52.61,52.99,52.21,52.84,7171300,52.84 30 | 2013-04-26,52.40,52.86,51.89,52.54,10612400,52.54 31 | 2013-04-25,53.31,53.36,52.07,52.36,11477200,52.36 32 | 2013-04-24,53.00,53.33,52.64,52.97,10227600,52.97 33 | 2013-04-23,51.96,52.77,51.85,52.49,12678600,52.49 34 | 2013-04-22,52.14,52.32,51.16,51.63,13761200,51.63 35 | 2013-04-19,52.77,53.24,51.90,52.39,15600000,52.39 36 | 2013-04-18,53.85,54.24,52.73,52.82,25357100,52.82 37 | 2013-04-17,56.49,56.83,55.65,56.10,14684600,56.10 38 | 2013-04-16,56.19,57.15,56.00,57.01,8629600,57.01 39 | 2013-04-15,56.98,57.48,55.69,55.84,9197900,55.84 40 | 2013-04-12,57.90,57.90,56.81,57.31,6315400,57.31 41 | 2013-04-11,57.42,58.04,57.21,57.78,7291200,57.78 42 | 2013-04-10,56.33,57.58,56.28,57.30,8001300,57.30 43 | 2013-04-09,55.44,56.45,55.32,56.07,6264200,56.07 44 | 2013-04-08,55.42,56.84,55.29,55.87,10187600,55.87 45 | 2013-04-05,54.73,55.59,54.50,55.52,7236300,55.52 46 | 2013-04-04,55.34,55.87,55.10,55.79,5906800,55.79 47 | 2013-04-03,55.81,56.00,55.32,55.50,11496400,55.50 48 | 2013-04-02,56.26,56.79,55.95,56.16,15229100,56.16 49 | 2013-04-01,55.64,56.75,55.44,55.71,20619900,55.71 50 | 2013-03-28,52.04,54.51,51.45,54.22,24210600,54.22 51 | 2013-03-27,51.66,52.58,50.95,52.07,12416500,52.07 52 | 2013-03-26,51.67,52.14,51.10,52.09,10759300,52.09 53 | 2013-03-25,53.40,53.40,51.13,51.31,18177500,51.31 54 | 2013-03-22,53.01,53.46,52.96,53.27,8501200,53.27 55 | 2013-03-21,52.20,53.05,51.94,52.92,14056500,52.92 56 | 2013-03-20,51.67,52.61,51.05,52.42,14632700,52.42 57 | 2013-03-19,51.00,51.38,50.46,51.10,14623700,51.10 58 | 2013-03-18,49.86,50.55,49.55,50.11,14339000,50.11 59 | 2013-03-15,51.72,52.08,50.33,50.41,21767000,50.41 60 | 2013-03-14,51.79,52.90,51.60,51.80,17144800,51.80 61 | 2013-03-13,52.96,53.00,50.62,50.98,19805200,50.98 62 | 2013-03-12,51.70,53.10,50.90,52.96,16123900,52.96 63 | 2013-03-11,53.10,53.34,52.67,52.76,8965000,52.76 64 | 2013-03-08,53.18,53.46,52.26,53.13,13077000,53.13 65 | 2013-03-07,53.69,53.87,52.75,52.98,9450000,52.98 66 | 2013-03-06,54.98,55.00,53.44,53.57,14560300,53.57 67 | 2013-03-05,55.55,56.00,54.76,55.26,9812600,55.26 68 | 2013-03-04,54.34,55.49,54.25,55.48,9750900,55.48 69 | 2013-03-01,54.59,55.10,54.02,54.90,6422900,54.90 70 | 2013-02-28,54.50,55.18,54.26,54.71,9575500,54.71 71 | 2013-02-27,54.02,54.56,53.67,54.15,7662300,54.15 72 | 2013-02-26,53.77,53.97,53.12,53.82,8463000,53.82 73 | 2013-02-25,55.41,55.65,53.60,53.61,7336400,53.61 74 | 2013-02-22,54.96,55.13,54.57,55.02,5087300,55.02 75 | 2013-02-21,55.34,55.58,53.90,54.62,10728400,54.62 76 | 2013-02-20,56.90,57.10,55.48,55.53,7396000,55.53 77 | 2013-02-19,56.86,56.98,56.36,56.68,5702100,56.68 78 | 2013-02-15,56.81,57.15,56.41,56.70,9130200,56.70 79 | 2013-02-14,56.79,57.12,56.63,56.83,7054800,56.83 80 | 2013-02-13,56.86,57.26,56.41,57.05,9096000,57.05 81 | 2013-02-12,56.40,57.18,56.11,56.78,10023700,56.78 82 | 2013-02-11,56.52,56.58,55.75,56.41,5150900,56.41 83 | 2013-02-08,56.46,57.08,56.39,56.62,8066900,56.62 84 | 2013-02-07,56.17,56.39,55.35,56.13,7646600,56.13 85 | 2013-02-06,56.01,56.50,55.72,56.11,7003300,56.11 86 | 2013-02-05,56.03,56.87,55.80,56.42,6184900,56.42 87 | 2013-02-04,56.65,56.73,55.63,55.69,8659200,55.69 88 | 2013-02-01,56.41,57.27,56.13,57.21,8471400,57.21 89 | 2013-01-31,55.36,56.26,55.31,55.97,7946400,55.97 90 | 2013-01-30,55.98,56.50,55.52,55.78,6979300,55.78 91 | 2013-01-29,55.66,55.84,55.29,55.66,7037200,55.66 92 | 2013-01-28,56.19,56.34,55.73,55.95,7876400,55.95 93 | 2013-01-25,56.00,56.66,55.92,56.53,13330200,56.53 94 | 2013-01-24,53.58,55.34,53.57,55.19,12751200,55.19 95 | 2013-01-23,53.39,54.02,53.33,53.38,9036800,53.38 96 | 2013-01-22,54.15,54.32,53.06,53.37,10678200,53.37 97 | 2013-01-18,54.17,54.50,53.70,54.21,10558700,54.21 98 | 2013-01-17,54.13,54.88,53.75,54.17,24458600,54.17 99 | 2013-01-16,52.93,53.46,51.88,52.90,22253100,52.90 100 | 2013-01-15,53.19,53.46,52.36,52.51,9624400,52.51 101 | 2013-01-14,53.58,53.67,52.83,53.18,10600100,53.18 102 | 2013-01-11,53.14,53.83,53.06,53.70,11445200,53.70 103 | 2013-01-10,53.34,53.50,52.43,53.00,7331300,53.00 104 | 2013-01-09,52.82,53.42,52.45,52.76,8099900,52.76 105 | 2013-01-08,53.36,54.20,52.51,52.68,11528200,52.68 106 | 2013-01-07,52.53,53.58,52.06,53.51,10185600,53.51 107 | 2013-01-04,52.46,53.12,52.22,52.78,6486300,52.78 108 | 2013-01-03,53.49,53.65,52.15,52.45,10831400,52.45 109 | 2013-01-02,52.40,53.70,52.04,53.59,13798900,53.59 -------------------------------------------------------------------------------- /Chapter06/volsmile.fsx: -------------------------------------------------------------------------------- 1 | /// Black-Scholes implementation 2 | 3 | /// Helper function 4 | let pow x n = exp(n * log(x)) 5 | 6 | /// Cumulative distribution function 7 | let cnd x = 8 | let a1 = 0.31938153 9 | let a2 = -0.356563782 10 | let a3 = 1.781477937 11 | let a4 = -1.821255978 12 | let a5 = 1.330274429 13 | let pi = 3.141592654 14 | let l = abs(x) 15 | let k = 1.0 / (1.0 + 0.2316419 * l) 16 | let w = ref (1.0-1.0/sqrt(2.0*pi)*exp(-l*l/2.0)*(a1*k+a2*k*k+a3*(pow k 3.0)+a4*(pow k 4.0)+a5*(pow k 5.0))) 17 | if (x < 0.0) then w := 1.0 - !w 18 | !w 19 | 20 | /// Black-Scholes 21 | // call_put_flag: 'c' if call option; otherwise put option 22 | // s: stock price 23 | // x: strike price of option 24 | // t: time to expiration in years 25 | // r: risk free interest rate 26 | // v: volatility 27 | let black_scholes call_put_flag s x t r v = 28 | let d1=(log(s / x) + (r+v*v*0.5)*t)/(v*sqrt(t)) 29 | let d2=d1-v*sqrt(t) 30 | let res = ref 0.0 31 | 32 | if (call_put_flag = 'c') then 33 | res := s*cnd(d1)-x*exp(-r*t)*cnd(d2) 34 | else 35 | res := x*exp(-r*t)*cnd(-d2)-s*cnd(-d1) 36 | !res 37 | 38 | /// Convert the nr of days to years 39 | let days_to_years d = 40 | (float d) / 365.25 41 | 42 | #r @"C:\Users\Niklas\Documents\Visual Studio 2012\Projects\Chapter3\packages\MathNet.Numerics.FSharp.2.5.0\lib\net40\MathNet.Numerics.FSharp.dll" 43 | #r @"C:\Users\Niklas\Documents\Visual Studio 2012\Projects\Chapter5\packages\MathNet.Numerics.2.6.1\lib\net40\MathNet.Numerics.dll" 44 | 45 | open MathNet.Numerics.Distributions; 46 | 47 | /// Normal distribution 48 | let normd = new Normal(0.0, 1.0) 49 | normd.Density(100.0) 50 | 51 | type PutCallFlag = Put | Call 52 | 53 | /// Delta 54 | // call_put_flag: 'c' if call option; otherwise put option 55 | // s: stock price 56 | // x: strike price of option 57 | // t: time to expiration in years 58 | // r: risk free interest rate 59 | // v: volatility 60 | let black_scholes_delta call_put_flag s x t r v = 61 | let d1=(log(s / x) + (r+v*v*0.5)*t)/(v*sqrt(t)) 62 | match call_put_flag with 63 | | Put -> cnd(d1) - 1.0 64 | | Call -> cnd(d1) 65 | 66 | /// Gamma 67 | // s: stock price 68 | // x: strike price of option 69 | // t: time to expiration in years 70 | // r: risk free interest rate 71 | // v: volatility 72 | let black_scholes_gamma s x t r v = 73 | let d1=(log(s / x) + (r+v*v*0.5)*t)/(v*sqrt(t)) 74 | normd.Density(d1) 75 | 76 | /// Vega 77 | // s: stock price 78 | // x: strike price of option 79 | // t: time to expiration in years 80 | // r: risk free interest rate 81 | // v: volatility 82 | let black_scholes_vega s x t r v = 83 | let d1=(log(s / x) + (r+v*v*0.5)*t)/(v*sqrt(t)) 84 | s*normd.Density(d1)*sqrt(t) 85 | 86 | /// Theta 87 | // call_put_flag: 'c' if call option; otherwise put option 88 | // s: stock price 89 | // x: strike price of option 90 | // t: time to expiration in years 91 | // r: risk free interest rate 92 | // v: volatility 93 | let black_scholes_theta call_put_flag s x t r v = 94 | let d1=(log(s / x) + (r+v*v*0.5)*t)/(v*sqrt(t)) 95 | let d2=d1-v*sqrt(t) 96 | let res = ref 0.0 97 | match call_put_flag with 98 | | Put -> -(s*normd.Density(d1)*v)/(2.0*sqrt(t))+r*x*exp(-r*t)*cnd(-d2) 99 | | Call -> -(s*normd.Density(d1)*v)/(2.0*sqrt(t))-r*x*exp(-r*t)*cnd(d2) 100 | 101 | /// Rho 102 | // call_put_flag: 'c' if call option; otherwise put option 103 | // s: stock price 104 | // x: strike price of option 105 | // t: time to expiration in years 106 | // r: risk free interest rate 107 | // v: volatility 108 | let black_scholes_rho call_put_flag s x t r v = 109 | let d1=(log(s / x) + (r+v*v*0.5)*t)/(v*sqrt(t)) 110 | let d2=d1-v*sqrt(t) 111 | let res = ref 0.0 112 | match call_put_flag with 113 | | Put -> -x*t*exp(-r*t)*cnd(-d2) 114 | | Call -> x*t*exp(-r*t)*cnd(d2) 115 | 116 | 117 | /// Plot delta of call option as function of underlying price 118 | #r "System.Windows.Forms.DataVisualization.dll" 119 | 120 | open System 121 | open System.Net 122 | open System.Windows.Forms 123 | open System.Windows.Forms.DataVisualization.Charting 124 | open Microsoft.FSharp.Control.WebExtensions 125 | 126 | /// Create chart and form 127 | let chart = new Chart(Dock = DockStyle.Fill) 128 | let area = new ChartArea("Main") 129 | chart.ChartAreas.Add(area) 130 | chart.Legends.Add(new Legend()) 131 | 132 | let mainForm = new Form(Visible = true, TopMost = true, 133 | Width = 700, Height = 500) 134 | 135 | do mainForm.Text <- "Option delta as a function of underlying price" 136 | mainForm.Controls.Add(chart) 137 | 138 | /// Create serie for call option delta 139 | let optionDeltaCall = new Series("Call option delta") 140 | do optionDeltaCall.ChartType <- SeriesChartType.Line 141 | do optionDeltaCall.BorderWidth <- 2 142 | do optionDeltaCall.Color <- Drawing.Color.Red 143 | chart.Series.Add(optionDeltaCall) 144 | 145 | /// Create serie for call option gamma 146 | let optionGammaCall = new Series("Call option gamma") 147 | do optionGammaCall.ChartType <- SeriesChartType.Line 148 | do optionGammaCall.BorderWidth <- 2 149 | do optionGammaCall.Color <- Drawing.Color.Blue 150 | chart.Series.Add(optionGammaCall) 151 | 152 | /// Create serie for call option theta 153 | let optionThetaCall = new Series("Call option theta") 154 | do optionThetaCall.ChartType <- SeriesChartType.Line 155 | do optionThetaCall.BorderWidth <- 2 156 | do optionThetaCall.Color <- Drawing.Color.Green 157 | chart.Series.Add(optionThetaCall) 158 | 159 | /// Create serie for call option vega 160 | let optionVegaCall = new Series("Call option vega") 161 | do optionVegaCall.ChartType <- SeriesChartType.Line 162 | do optionVegaCall.BorderWidth <- 2 163 | do optionVegaCall.Color <- Drawing.Color.Purple 164 | chart.Series.Add(optionVegaCall) 165 | 166 | /// Calculate and plot call delta 167 | let opd = [for x in [10.0..1.0..70.0] do yield black_scholes_delta Call x 60.0 0.5 0.01 0.3] 168 | do opd |> Seq.iter (optionDeltaCall.Points.Add >> ignore) 169 | 170 | /// Calculate and plot call gamma 171 | let opg = [for x in [10.0..1.0..70.0] do yield black_scholes_gamma x 60.0 0.5 0.01 0.3] 172 | do opg |> Seq.iter (optionGammaCall.Points.Add >> ignore) 173 | 174 | /// Calculate and plot call theta 175 | let opt = [for x in [10.0..1.0..70.0] do yield black_scholes_theta Call x 60.0 0.5 0.01 0.3] 176 | do opt |> Seq.iter (optionThetaCall.Points.Add >> ignore) 177 | 178 | /// Calculate and plot call vega 179 | let opv = [for x in [10.0..1.0..70.0] do yield black_scholes_vega x 60.0 0.1 0.01 0.3] 180 | do opv |> Seq.iter (optionVegaCall.Points.Add >> ignore) 181 | -------------------------------------------------------------------------------- /Chapter06/smile_data.csv: -------------------------------------------------------------------------------- 1 | ERICB3L100,2013-12-20,100,28.5 2 | ERICB3L34,2013-12-20,34,138.6 3 | ERICB3L38,2013-12-20,38,122.3 4 | ERICB3L42,2013-12-20,42,107.6 5 | ERICB3L46,2013-12-20,46,94.3 6 | ERICB3L50,2013-12-20,50,82.1 7 | ERICB3L62.50,2013-12-20,62.5,49.1 8 | ERICB3L65,2013-12-20,65,43.2 9 | ERICB3L67.50,2013-12-20,67.5,37.5 10 | ERICB3L75,2013-12-20,75,24.8 11 | ERICB3L80,2013-12-20,80,20.9 12 | ERICB3L85,2013-12-20,85,19.6 13 | ERICB3L90,2013-12-20,90,21.2 14 | ERICB3L95,2013-12-20,95,23.1 15 | ERICB3X100,2013-12-20,100,34.1 16 | ERICB3X105,2013-12-20,105,33 17 | ERICB3X110,2013-12-20,110,39.1 18 | ERICB3X115,2013-12-20,115,60 19 | ERICB3X120,2013-12-20,120,66.3 20 | ERICB3X130,2013-12-20,130,77.8 21 | ERICB3X140,2013-12-20,140,81.5 22 | ERICB3X50,2013-12-20,50,76.2 23 | ERICB3X60,2013-12-20,60,51.5 24 | ERICB3X62.50,2013-12-20,62.5,40 25 | ERICB3X65,2013-12-20,65,37.6 26 | ERICB3X67.50,2013-12-20,67.5,33 27 | ERICB3X70,2013-12-20,70,30.7 28 | ERICB3X75,2013-12-20,75,23 29 | ERICB3X80,2013-12-20,80,20.2 30 | ERICB3X85,2013-12-20,85,19 31 | ERICB3X90,2013-12-20,90,22.6 32 | ERICB3X95,2013-12-20,95,19.7 33 | ERICB4A100,2014-01-17,100,22.7 34 | ERICB4A105,2014-01-17,105,27.3 35 | ERICB4A110,2014-01-17,110,41.2 36 | ERICB4A115,2014-01-17,115,33.8 37 | ERICB4A120,2014-01-17,120,50.8 38 | ERICB4A34,2014-01-17,34,100.7 39 | ERICB4A38,2014-01-17,38,88.5 40 | ERICB4A42,2014-01-17,42,77.6 41 | ERICB4A46,2014-01-17,46,67.6 42 | ERICB4A50,2014-01-17,50,58.6 43 | ERICB4A62.50,2014-01-17,62.5,34.2 44 | ERICB4A65,2014-01-17,65,35.5 45 | ERICB4A75,2014-01-17,75,21.9 46 | ERICB4A80,2014-01-17,80,20.6 47 | ERICB4A85,2014-01-17,85,19.1 48 | ERICB4A90,2014-01-17,90,19.4 49 | ERICB4A95,2014-01-17,95,21 50 | ERICB4B100,2014-02-21,100,23 51 | ERICB4B105,2014-02-21,105,23.6 52 | ERICB4B62.50,2014-02-21,62.5,34.3 53 | ERICB4B65,2014-02-21,65,30.3 54 | ERICB4B67.50,2014-02-21,67.5,30.9 55 | ERICB4B70,2014-02-21,70,26.7 56 | ERICB4B75,2014-02-21,75,25.6 57 | ERICB4B80,2014-02-21,80,23.5 58 | ERICB4B85,2014-02-21,85,22.6 59 | ERICB4B90,2014-02-21,90,22.3 60 | ERICB4B95,2014-02-21,95,22.4 61 | ERICB4C100,2014-03-21,100,22.3 62 | ERICB4C105,2014-03-21,105,22.9 63 | ERICB4C110,2014-03-21,110,30.6 64 | ERICB4C115,2014-03-21,115,34.2 65 | ERICB4C120,2014-03-21,120,28.1 66 | ERICB4C130,2014-03-21,130,33.2 67 | ERICB4C140,2014-03-21,140,37.8 68 | ERICB4C38,2014-03-21,38,77.8 69 | ERICB4C42,2014-03-21,42,49.1 70 | ERICB4C46,2014-03-21,46,41.6 71 | ERICB4C60,2014-03-21,60,32.9 72 | ERICB4C62.50,2014-03-21,62.5,25.6 73 | ERICB4C65,2014-03-21,65,28.3 74 | ERICB4C67.50,2014-03-21,67.5,26.7 75 | ERICB4C70,2014-03-21,70,26 76 | ERICB4C75,2014-03-21,75,25 77 | ERICB4C80,2014-03-21,80,23.3 78 | ERICB4C85,2014-03-21,85,22.4 79 | ERICB4C90,2014-03-21,90,22 80 | ERICB4C95,2014-03-21,95,22 81 | ERICB4F100,2014-06-19,100,20.7 82 | ERICB4F110,2014-06-19,110,24.2 83 | ERICB4F120,2014-06-19,120,22.4 84 | ERICB4F130,2014-06-19,130,25.3 85 | ERICB4F140,2014-06-19,140,27.5 86 | ERICB4F38,2014-06-19,38,47.1 87 | ERICB4F42,2014-06-19,42,40.3 88 | ERICB4F46,2014-06-19,46,34.1 89 | ERICB4F50,2014-06-19,50,28.3 90 | ERICB4F60,2014-06-19,60,25.2 91 | ERICB4F70,2014-06-19,70,21.8 92 | ERICB4F80,2014-06-19,80,21 93 | ERICB4F85,2014-06-19,85,20.5 94 | ERICB4F90,2014-06-19,90,20.5 95 | ERICB4I100,2014-09-19,100,21 96 | ERICB4I110,2014-09-19,110,21.3 97 | ERICB4I120,2014-09-19,120,22.3 98 | ERICB4I130,2014-09-19,130,23.1 99 | ERICB4I60,2014-09-19,60,21.1 100 | ERICB4I70,2014-09-19,70,20.5 101 | ERICB4I80,2014-09-19,80,20.8 102 | ERICB4I90,2014-09-19,90,20.3 103 | ERICB4L100,2014-12-19,100,21.3 104 | ERICB4L110,2014-12-19,110,21.7 105 | ERICB4L120,2014-12-19,120,22.1 106 | ERICB4L130,2014-12-19,130,23.1 107 | ERICB4L90,2014-12-19,90,21.2 108 | ERICB4M100,2014-01-17,100,23.3 109 | ERICB4M105,2014-01-17,105,28.6 110 | ERICB4M110,2014-01-17,110,41.4 111 | ERICB4M115,2014-01-17,115,46.6 112 | ERICB4M120,2014-01-17,120,51.5 113 | ERICB4M130,2014-01-17,130,60.5 114 | ERICB4M140,2014-01-17,140,68.6 115 | ERICB4M50,2014-01-17,50,56.7 116 | ERICB4M60,2014-01-17,60,36.3 117 | ERICB4M62.50,2014-01-17,62.5,37 118 | ERICB4M65,2014-01-17,65,28.8 119 | ERICB4M67.50,2014-01-17,67.5,26.9 120 | ERICB4M70,2014-01-17,70,24.3 121 | ERICB4M75,2014-01-17,75,21.3 122 | ERICB4M80,2014-01-17,80,19.3 123 | ERICB4M85,2014-01-17,85,18.4 124 | ERICB4M90,2014-01-17,90,18.9 125 | ERICB4M95,2014-01-17,95,23.3 126 | ERICB4N100,2014-02-21,100,23.2 127 | ERICB4N105,2014-02-21,105,28 128 | ERICB4N62.50,2014-02-21,62.5,30.6 129 | ERICB4N65,2014-02-21,65,28.4 130 | ERICB4N67.50,2014-02-21,67.5,26.5 131 | ERICB4N70,2014-02-21,70,25.8 132 | ERICB4N75,2014-02-21,75,24.1 133 | ERICB4N80,2014-02-21,80,22.6 134 | ERICB4N85,2014-02-21,85,21.8 135 | ERICB4N90,2014-02-21,90,21.5 136 | ERICB4N95,2014-02-21,95,21.1 137 | ERICB4O100,2014-03-21,100,21.3 138 | ERICB4O105,2014-03-21,105,25.7 139 | ERICB4O110,2014-03-21,110,27.6 140 | ERICB4O115,2014-03-21,115,33.6 141 | ERICB4O120,2014-03-21,120,37.2 142 | ERICB4O130,2014-03-21,130,44 143 | ERICB4O140,2014-03-21,140,50.1 144 | ERICB4O46,2014-03-21,46,49.4 145 | ERICB4O50,2014-03-21,50,43.2 146 | ERICB4O60,2014-03-21,60,30.6 147 | ERICB4O62.50,2014-03-21,62.5,28.2 148 | ERICB4O65,2014-03-21,65,27.3 149 | ERICB4O67.50,2014-03-21,67.5,26 150 | ERICB4O70,2014-03-21,70,25 151 | ERICB4O75,2014-03-21,75,23.7 152 | ERICB4O80,2014-03-21,80,22.7 153 | ERICB4O85,2014-03-21,85,21.9 154 | ERICB4O90,2014-03-21,90,20.8 155 | ERICB4O95,2014-03-21,95,21.3 156 | ERICB4R100,2014-06-19,100,34 157 | ERICB4R110,2014-06-19,110,40.2 158 | ERICB4R120,2014-06-19,120,46.1 159 | ERICB4R130,2014-06-19,130,51.7 160 | ERICB4R140,2014-06-19,140,55.8 161 | ERICB4R38,2014-06-19,38,60.1 162 | ERICB4R42,2014-06-19,42,53.3 163 | ERICB4R46,2014-06-19,46,47.1 164 | ERICB4R50,2014-06-19,50,41.6 165 | ERICB4R60,2014-06-19,60,29.4 166 | ERICB4R70,2014-06-19,70,27.7 167 | ERICB4R80,2014-06-19,80,27.7 168 | ERICB4R90,2014-06-19,90,29.5 169 | ERICB4U100,2014-09-19,100,30 170 | ERICB4U110,2014-09-19,110,35.6 171 | ERICB4U120,2014-09-19,120,40.8 172 | ERICB4U130,2014-09-19,130,45.2 173 | ERICB4U140,2014-09-19,140,50.1 174 | ERICB4U42,2014-09-19,42,35 175 | ERICB4U46,2014-09-19,46,33.9 176 | ERICB4U50,2014-09-19,50,32.1 177 | ERICB4U60,2014-09-19,60,29.6 178 | ERICB4U70,2014-09-19,70,28 179 | ERICB4U80,2014-09-19,80,27.8 180 | ERICB4U90,2014-09-19,90,28.1 181 | ERICB4X42,2014-12-19,42,34.4 182 | ERICB4X46,2014-12-19,46,33.1 183 | ERICB4X50,2014-12-19,50,32 184 | ERICB4X60,2014-12-19,60,29.3 185 | ERICB4X70,2014-12-19,70,27.8 186 | ERICB4X80,2014-12-19,80,27.3 187 | ERICB5A100,2015-01-16,100,21 188 | ERICB5A110,2015-01-16,110,21.3 189 | ERICB5A120,2015-01-16,120,21.7 190 | ERICB5A130,2015-01-16,130,22.3 191 | ERICB5A140,2015-01-16,140,23.3 192 | ERICB5A90,2015-01-16,90,20.5 193 | ERICB5M38,2015-01-16,38,35.7 194 | ERICB5M42,2015-01-16,42,33.5 195 | ERICB5M46,2015-01-16,46,32 196 | ERICB5M50,2015-01-16,50,31.2 197 | ERICB5M60,2015-01-16,60,29 198 | ERICB5M70,2015-01-16,70,27.8 199 | ERICB5M80,2015-01-16,80,26.5 -------------------------------------------------------------------------------- /Chapter09/smile_data.csv: -------------------------------------------------------------------------------- 1 | ERICB3L100,2013-12-20,100,28.5 2 | ERICB3L34,2013-12-20,34,138.6 3 | ERICB3L38,2013-12-20,38,122.3 4 | ERICB3L42,2013-12-20,42,107.6 5 | ERICB3L46,2013-12-20,46,94.3 6 | ERICB3L50,2013-12-20,50,82.1 7 | ERICB3L62.50,2013-12-20,62.5,49.1 8 | ERICB3L65,2013-12-20,65,43.2 9 | ERICB3L67.50,2013-12-20,67.5,37.5 10 | ERICB3L75,2013-12-20,75,24.8 11 | ERICB3L80,2013-12-20,80,20.9 12 | ERICB3L85,2013-12-20,85,19.6 13 | ERICB3L90,2013-12-20,90,21.2 14 | ERICB3L95,2013-12-20,95,23.1 15 | ERICB3X100,2013-12-20,100,34.1 16 | ERICB3X105,2013-12-20,105,33 17 | ERICB3X110,2013-12-20,110,39.1 18 | ERICB3X115,2013-12-20,115,60 19 | ERICB3X120,2013-12-20,120,66.3 20 | ERICB3X130,2013-12-20,130,77.8 21 | ERICB3X140,2013-12-20,140,81.5 22 | ERICB3X50,2013-12-20,50,76.2 23 | ERICB3X60,2013-12-20,60,51.5 24 | ERICB3X62.50,2013-12-20,62.5,40 25 | ERICB3X65,2013-12-20,65,37.6 26 | ERICB3X67.50,2013-12-20,67.5,33 27 | ERICB3X70,2013-12-20,70,30.7 28 | ERICB3X75,2013-12-20,75,23 29 | ERICB3X80,2013-12-20,80,20.2 30 | ERICB3X85,2013-12-20,85,19 31 | ERICB3X90,2013-12-20,90,22.6 32 | ERICB3X95,2013-12-20,95,19.7 33 | ERICB4A100,2014-01-17,100,22.7 34 | ERICB4A105,2014-01-17,105,27.3 35 | ERICB4A110,2014-01-17,110,41.2 36 | ERICB4A115,2014-01-17,115,33.8 37 | ERICB4A120,2014-01-17,120,50.8 38 | ERICB4A34,2014-01-17,34,100.7 39 | ERICB4A38,2014-01-17,38,88.5 40 | ERICB4A42,2014-01-17,42,77.6 41 | ERICB4A46,2014-01-17,46,67.6 42 | ERICB4A50,2014-01-17,50,58.6 43 | ERICB4A62.50,2014-01-17,62.5,34.2 44 | ERICB4A65,2014-01-17,65,35.5 45 | ERICB4A75,2014-01-17,75,21.9 46 | ERICB4A80,2014-01-17,80,20.6 47 | ERICB4A85,2014-01-17,85,19.1 48 | ERICB4A90,2014-01-17,90,19.4 49 | ERICB4A95,2014-01-17,95,21 50 | ERICB4B100,2014-02-21,100,23 51 | ERICB4B105,2014-02-21,105,23.6 52 | ERICB4B62.50,2014-02-21,62.5,34.3 53 | ERICB4B65,2014-02-21,65,30.3 54 | ERICB4B67.50,2014-02-21,67.5,30.9 55 | ERICB4B70,2014-02-21,70,26.7 56 | ERICB4B75,2014-02-21,75,25.6 57 | ERICB4B80,2014-02-21,80,23.5 58 | ERICB4B85,2014-02-21,85,22.6 59 | ERICB4B90,2014-02-21,90,22.3 60 | ERICB4B95,2014-02-21,95,22.4 61 | ERICB4C100,2014-03-21,100,22.3 62 | ERICB4C105,2014-03-21,105,22.9 63 | ERICB4C110,2014-03-21,110,30.6 64 | ERICB4C115,2014-03-21,115,34.2 65 | ERICB4C120,2014-03-21,120,28.1 66 | ERICB4C130,2014-03-21,130,33.2 67 | ERICB4C140,2014-03-21,140,37.8 68 | ERICB4C38,2014-03-21,38,77.8 69 | ERICB4C42,2014-03-21,42,49.1 70 | ERICB4C46,2014-03-21,46,41.6 71 | ERICB4C60,2014-03-21,60,32.9 72 | ERICB4C62.50,2014-03-21,62.5,25.6 73 | ERICB4C65,2014-03-21,65,28.3 74 | ERICB4C67.50,2014-03-21,67.5,26.7 75 | ERICB4C70,2014-03-21,70,26 76 | ERICB4C75,2014-03-21,75,25 77 | ERICB4C80,2014-03-21,80,23.3 78 | ERICB4C85,2014-03-21,85,22.4 79 | ERICB4C90,2014-03-21,90,22 80 | ERICB4C95,2014-03-21,95,22 81 | ERICB4F100,2014-06-19,100,20.7 82 | ERICB4F110,2014-06-19,110,24.2 83 | ERICB4F120,2014-06-19,120,22.4 84 | ERICB4F130,2014-06-19,130,25.3 85 | ERICB4F140,2014-06-19,140,27.5 86 | ERICB4F38,2014-06-19,38,47.1 87 | ERICB4F42,2014-06-19,42,40.3 88 | ERICB4F46,2014-06-19,46,34.1 89 | ERICB4F50,2014-06-19,50,28.3 90 | ERICB4F60,2014-06-19,60,25.2 91 | ERICB4F70,2014-06-19,70,21.8 92 | ERICB4F80,2014-06-19,80,21 93 | ERICB4F85,2014-06-19,85,20.5 94 | ERICB4F90,2014-06-19,90,20.5 95 | ERICB4I100,2014-09-19,100,21 96 | ERICB4I110,2014-09-19,110,21.3 97 | ERICB4I120,2014-09-19,120,22.3 98 | ERICB4I130,2014-09-19,130,23.1 99 | ERICB4I60,2014-09-19,60,21.1 100 | ERICB4I70,2014-09-19,70,20.5 101 | ERICB4I80,2014-09-19,80,20.8 102 | ERICB4I90,2014-09-19,90,20.3 103 | ERICB4L100,2014-12-19,100,21.3 104 | ERICB4L110,2014-12-19,110,21.7 105 | ERICB4L120,2014-12-19,120,22.1 106 | ERICB4L130,2014-12-19,130,23.1 107 | ERICB4L90,2014-12-19,90,21.2 108 | ERICB4M100,2014-01-17,100,23.3 109 | ERICB4M105,2014-01-17,105,28.6 110 | ERICB4M110,2014-01-17,110,41.4 111 | ERICB4M115,2014-01-17,115,46.6 112 | ERICB4M120,2014-01-17,120,51.5 113 | ERICB4M130,2014-01-17,130,60.5 114 | ERICB4M140,2014-01-17,140,68.6 115 | ERICB4M50,2014-01-17,50,56.7 116 | ERICB4M60,2014-01-17,60,36.3 117 | ERICB4M62.50,2014-01-17,62.5,37 118 | ERICB4M65,2014-01-17,65,28.8 119 | ERICB4M67.50,2014-01-17,67.5,26.9 120 | ERICB4M70,2014-01-17,70,24.3 121 | ERICB4M75,2014-01-17,75,21.3 122 | ERICB4M80,2014-01-17,80,19.3 123 | ERICB4M85,2014-01-17,85,18.4 124 | ERICB4M90,2014-01-17,90,18.9 125 | ERICB4M95,2014-01-17,95,23.3 126 | ERICB4N100,2014-02-21,100,23.2 127 | ERICB4N105,2014-02-21,105,28 128 | ERICB4N62.50,2014-02-21,62.5,30.6 129 | ERICB4N65,2014-02-21,65,28.4 130 | ERICB4N67.50,2014-02-21,67.5,26.5 131 | ERICB4N70,2014-02-21,70,25.8 132 | ERICB4N75,2014-02-21,75,24.1 133 | ERICB4N80,2014-02-21,80,22.6 134 | ERICB4N85,2014-02-21,85,21.8 135 | ERICB4N90,2014-02-21,90,21.5 136 | ERICB4N95,2014-02-21,95,21.1 137 | ERICB4O100,2014-03-21,100,21.3 138 | ERICB4O105,2014-03-21,105,25.7 139 | ERICB4O110,2014-03-21,110,27.6 140 | ERICB4O115,2014-03-21,115,33.6 141 | ERICB4O120,2014-03-21,120,37.2 142 | ERICB4O130,2014-03-21,130,44 143 | ERICB4O140,2014-03-21,140,50.1 144 | ERICB4O46,2014-03-21,46,49.4 145 | ERICB4O50,2014-03-21,50,43.2 146 | ERICB4O60,2014-03-21,60,30.6 147 | ERICB4O62.50,2014-03-21,62.5,28.2 148 | ERICB4O65,2014-03-21,65,27.3 149 | ERICB4O67.50,2014-03-21,67.5,26 150 | ERICB4O70,2014-03-21,70,25 151 | ERICB4O75,2014-03-21,75,23.7 152 | ERICB4O80,2014-03-21,80,22.7 153 | ERICB4O85,2014-03-21,85,21.9 154 | ERICB4O90,2014-03-21,90,20.8 155 | ERICB4O95,2014-03-21,95,21.3 156 | ERICB4R100,2014-06-19,100,34 157 | ERICB4R110,2014-06-19,110,40.2 158 | ERICB4R120,2014-06-19,120,46.1 159 | ERICB4R130,2014-06-19,130,51.7 160 | ERICB4R140,2014-06-19,140,55.8 161 | ERICB4R38,2014-06-19,38,60.1 162 | ERICB4R42,2014-06-19,42,53.3 163 | ERICB4R46,2014-06-19,46,47.1 164 | ERICB4R50,2014-06-19,50,41.6 165 | ERICB4R60,2014-06-19,60,29.4 166 | ERICB4R70,2014-06-19,70,27.7 167 | ERICB4R80,2014-06-19,80,27.7 168 | ERICB4R90,2014-06-19,90,29.5 169 | ERICB4U100,2014-09-19,100,30 170 | ERICB4U110,2014-09-19,110,35.6 171 | ERICB4U120,2014-09-19,120,40.8 172 | ERICB4U130,2014-09-19,130,45.2 173 | ERICB4U140,2014-09-19,140,50.1 174 | ERICB4U42,2014-09-19,42,35 175 | ERICB4U46,2014-09-19,46,33.9 176 | ERICB4U50,2014-09-19,50,32.1 177 | ERICB4U60,2014-09-19,60,29.6 178 | ERICB4U70,2014-09-19,70,28 179 | ERICB4U80,2014-09-19,80,27.8 180 | ERICB4U90,2014-09-19,90,28.1 181 | ERICB4X42,2014-12-19,42,34.4 182 | ERICB4X46,2014-12-19,46,33.1 183 | ERICB4X50,2014-12-19,50,32 184 | ERICB4X60,2014-12-19,60,29.3 185 | ERICB4X70,2014-12-19,70,27.8 186 | ERICB4X80,2014-12-19,80,27.3 187 | ERICB5A100,2015-01-16,100,21 188 | ERICB5A110,2015-01-16,110,21.3 189 | ERICB5A120,2015-01-16,120,21.7 190 | ERICB5A130,2015-01-16,130,22.3 191 | ERICB5A140,2015-01-16,140,23.3 192 | ERICB5A90,2015-01-16,90,20.5 193 | ERICB5M38,2015-01-16,38,35.7 194 | ERICB5M42,2015-01-16,42,33.5 195 | ERICB5M46,2015-01-16,46,32 196 | ERICB5M50,2015-01-16,50,31.2 197 | ERICB5M60,2015-01-16,60,29 198 | ERICB5M70,2015-01-16,70,27.8 199 | ERICB5M80,2015-01-16,80,26.5 -------------------------------------------------------------------------------- /Chapter07/OrderType.fsx: -------------------------------------------------------------------------------- 1 | 2 | /// Order side 3 | type OrderSide = 4 | Buy | Sell | Sellshort 5 | 6 | /// Order type 7 | type OrderType = 8 | Market | Limit | Stop | StopLimit 9 | 10 | /// Order status, according to FIX standard 4.2 11 | type OrderStatus = 12 | Created | New | Filled | PartiallyFilled | DoneForDay | Cancelled | Replaced | PendingCancel | Stopped | Rejected | Suspended | PendingNew | Calculated | Expired 13 | 14 | /// Time in force 15 | type Tif = 16 | GoodForDay | GoodTilCancelled | ImmediateOrCancel | FillorKill 17 | 18 | /// Order class 19 | type Order(side: OrderSide, t: OrderType, p: float, tif: Tif, q: int, i: string, sp: float) = 20 | // Init order with status created 21 | let mutable St = OrderStatus.Created 22 | let mutable S = side 23 | member private this.Ts = System.DateTime.Now 24 | member private this.T = t 25 | member private this.P = p 26 | member private this.tif = tif 27 | member private this.Q = q 28 | member private this.I = i 29 | member private this.Sp = sp 30 | 31 | member this.Status 32 | with get() = St 33 | and set(st) = St <- st 34 | 35 | member this.Side 36 | with get() = S 37 | and set(s) = S <- s 38 | 39 | member this.Timestamp 40 | with get() = this.Ts 41 | 42 | member this.Type 43 | with get() = this.T 44 | 45 | member this.Qty 46 | with get() = this.Q 47 | 48 | member this.Price 49 | with get() = this.P 50 | 51 | member this.Tif 52 | with get() = this.tif 53 | 54 | member this.Instrument 55 | with get() = this.I 56 | 57 | member this.StopPrice 58 | with get() = this.Sp 59 | 60 | member this.toggleOrderSide() = 61 | S <- this.toggleOrderSide(S) 62 | 63 | member private this.toggleOrderSide(s: OrderSide) = 64 | match s with 65 | | Buy -> Sell 66 | | Sell -> Buy 67 | | Sellshort -> Sellshort 68 | 69 | static member (~-) (o : Order) = 70 | Order(o.toggleOrderSide(o.Side), o.Type, o.Price, o.tif, o.Q, o.I, o.Sp) 71 | 72 | /// Validation result, ok or failed with message 73 | type Result = Valid of Order | Error of string 74 | 75 | /// Validates an order for illustrative purposes 76 | let validateOrder (result:Result) : Result = 77 | match result with 78 | | Error s -> Error s 79 | | Valid order -> 80 | let orderType = order.Type 81 | let orderPrice = order.Price 82 | let stopPrice = order.StopPrice 83 | match orderType with 84 | | OrderType.Limit -> 85 | match orderPrice with 86 | | p when p > 0.0 -> Valid order 87 | | _ -> Error "Limit orders must have a price > 0" 88 | | OrderType.Market -> Valid order 89 | | OrderType.Stop -> 90 | match stopPrice with 91 | | p when p > 0.0 -> Valid order 92 | | _ -> Error "Stop orders must have price > 0" 93 | | OrderType.StopLimit -> 94 | match stopPrice with 95 | | p when p > 0.0 && orderPrice > 0.0 -> Valid order 96 | | _ -> Error "Stop limit orders must both price > 0 and stop price > 0" 97 | 98 | // Limit buy order 99 | let buyOrder = Order(OrderSide.Buy, OrderType.Limit, 54.50, Tif.FillorKill, 100, "MSFT", 0.0) 100 | 101 | // Limit buy order, no price 102 | let buyOrderNoPrice = Order(OrderSide.Buy, OrderType.Limit, 0.0, Tif.FillorKill, 100, "MSFT", 0.0) 103 | 104 | // Stop order that will be converted to limit order, no limit price 105 | let stopLimitNoPrice = Order(OrderSide.Buy, OrderType.StopLimit, 0.0, Tif.FillorKill, 100, "MSFT", 45.50) 106 | 107 | // Stop order that will be converted to market order 108 | let stopNoPrice = Order(OrderSide.Buy, OrderType.Stop, 0.0, Tif.FillorKill, 100, "MSFT", 45.50) 109 | 110 | // Stop order that will be converted to market order 111 | let stopNoPriceNoInstrument = Order(OrderSide.Buy, OrderType.Stop, 0.0, Tif.FillorKill, 100, "", 45.50) 112 | 113 | let buyOrderExceetsPreTradeRisk = Order(OrderSide.Sell, OrderType.Limit, 26.50, Tif.GoodForDay, 1000, "MSFT", 0.0) 114 | let buyOrderBelowPricePreTradeRisk = Order(OrderSide.Sell, OrderType.Limit, 26.50, Tif.GoodForDay, 500, "MSFT", 0.0) 115 | 116 | // Validate sample orders 117 | validateOrder (Valid buyOrder) // Ok 118 | validateOrder (Valid buyOrderNoPrice) // Failed 119 | validateOrder (Valid stopLimitNoPrice) // Failed 120 | validateOrder (Valid stopNoPrice) // Ok 121 | 122 | /// Modify above to work with function composition 123 | 124 | let validateInstrument (result:Result) : Result = 125 | match result with 126 | | Error l -> Error l 127 | | Valid order -> 128 | let orderInstrument = order.Instrument 129 | match orderInstrument.Length with 130 | | l when l > 0 -> Valid order 131 | | _ -> Error "Must specify order Instrument" 132 | 133 | 134 | validateInstrument (Valid stopNoPriceNoInstrument) 135 | 136 | // Add check for specified Instrument 137 | let validateOrderAndInstrument = validateOrder >> validateInstrument 138 | 139 | validateOrderAndInstrument (Valid stopNoPriceNoInstrument) 140 | 141 | let orderValueMax = 25000.0; // Order value max of $25,000 142 | 143 | /// Add simple pre trade risk 144 | let preTradeRiskRuleOne (result:Result) : Result = 145 | match result with 146 | | Error l -> Error l 147 | | Valid order -> 148 | let orderValue = (float order.Qty) * order.Price 149 | match orderValue with 150 | | v when orderValue > orderValueMax -> Error "Order value exceeded limit" 151 | | _ -> Valid order 152 | 153 | // Using currying 154 | let preTradeRiskRuleTwo (marketPrice:float) (result:Result) : Result = 155 | match result with 156 | | Error l -> Error l 157 | | Valid order -> 158 | let orderLimit = (float order.Qty) * order.Price 159 | match orderLimit with 160 | | v when orderLimit < marketPrice && order.Side = OrderSide.Buy -> Error "Order limit price below market price" 161 | | v when orderLimit > marketPrice && order.Side = OrderSide.Sell -> Error "Order limit price above market price" 162 | | _ -> Valid order 163 | 164 | let validateOrderAndInstrumentAndPreTradeRisk = validateOrderAndInstrument >> preTradeRiskRuleOne 165 | let validateOrderAndInstrumentAndPreTradeRisk2 marketPrice = validateOrderAndInstrument >> preTradeRiskRuleOne >> (preTradeRiskRuleTwo marketPrice) 166 | 167 | validateOrderAndInstrumentAndPreTradeRisk (Valid stopNoPriceNoInstrument) 168 | validateOrderAndInstrumentAndPreTradeRisk (Valid buyOrderExceetsPreTradeRisk) 169 | validateOrderAndInstrumentAndPreTradeRisk2 25.0 (Valid buyOrderBelowPricePreTradeRisk) 170 | 171 | /// Chain using List.reduce 172 | let preTradeRiskRules marketPrice = [ 173 | preTradeRiskRuleOne 174 | (preTradeRiskRuleTwo marketPrice) 175 | ] 176 | 177 | /// Create function composition using reduce, >>, composite operator 178 | let preTradeComposite = List.reduce (>>) (preTradeRiskRules 25.0) 179 | 180 | preTradeComposite (Valid buyOrderExceetsPreTradeRisk) 181 | preTradeComposite (Valid buyOrderBelowPricePreTradeRisk) -------------------------------------------------------------------------------- /Chapter07/FIX.fs: -------------------------------------------------------------------------------- 1 | namespace TradingSystem 2 | 3 | (* 4 | If used in F# Interactive 5 | #r @"PATH_TO\QuickFix.dll" 6 | *) 7 | 8 | open QuickFix 9 | open QuickFix.Transport 10 | open QuickFix.FIX42 11 | open System.Globalization 12 | open System 13 | open Orders 14 | open QuickFix.Fields 15 | open System.ComponentModel 16 | 17 | module FIX = 18 | type ClientInitiator(orders:BindingList) = 19 | member this.findOrder str = 20 | try 21 | Some (orders |> Seq.find (fun o -> o.Timestamp = str)) 22 | with | _ as ex -> printfn "Exception: %A" ex.Message; None 23 | interface IApplication with 24 | member this.OnCreate(sessionID : SessionID) : unit = printfn "OnCreate" 25 | member this.ToAdmin(msg : QuickFix.Message, sessionID : SessionID) : unit = printfn "ToAdmin %A" msg 26 | member this.FromAdmin(msg : QuickFix.Message, sessionID : SessionID) : unit = printfn "FromAdmin %A" msg 27 | member this.ToApp(msg : QuickFix.Message, sessionID : SessionID) : unit = printfn "FromAdmin - Report: %A" msg 28 | member this.FromApp(msg : QuickFix.Message, sessionID : QuickFix.SessionID) : unit = 29 | match msg with 30 | | :? ExecutionReport as report -> 31 | let qty = report.CumQty 32 | let avg = report.AvgPx 33 | let sta = report.OrdStatus 34 | let oid = report.ClOrdID 35 | let lqty = report.LeavesQty 36 | let eqty = report.CumQty 37 | let debug = fun str -> printfn "ExecutionReport (%s) # avg price: %s | qty: %s | status: %s | orderId: %s" str (avg.ToString()) (qty.ToString()) (sta.ToString()) (oid.ToString()) 38 | match sta.getValue() with 39 | | OrdStatus.NEW -> 40 | match this.findOrder(oid.ToString()) with 41 | | Some(o) -> 42 | o.Status <- OrderStatus.New 43 | | _ -> printfn "ERROR: The order was not found in OMS" 44 | debug "NEW" 45 | | OrdStatus.FILLED -> 46 | /// Update avg price, open price, ex price 47 | match this.findOrder(oid.ToString()) with 48 | | Some(o) -> 49 | o.Status <- OrderStatus.Filled 50 | o.AveragePrice <- double (avg.getValue()) 51 | o.OpenQty <- int (lqty.getValue()) 52 | o.ExecutedQty <- int (eqty.getValue()) 53 | | _ -> printfn "ERROR: The order was not found in OMS" 54 | debug "FILLED" 55 | | OrdStatus.PARTIALLY_FILLED -> 56 | /// Update avg price, open price, ex price 57 | match this.findOrder(oid.ToString()) with 58 | | Some(o) -> 59 | o.Status <- OrderStatus.PartiallyFilled 60 | o.AveragePrice <- double (avg.getValue()) 61 | o.OpenQty <- int (lqty.getValue()) 62 | o.ExecutedQty <- int (eqty.getValue()) 63 | | _ -> printfn "ERROR: The order was not found in OMS" 64 | debug "PARTIALLY_FILLED" 65 | | OrdStatus.CANCELED -> 66 | match this.findOrder(oid.ToString()) with 67 | | Some(o) -> 68 | o.Status <- OrderStatus.Cancelled 69 | | _ -> printfn "ERROR: The order was not found in OMS" 70 | debug "CANCELED" 71 | | OrdStatus.REJECTED -> 72 | match this.findOrder(oid.ToString()) with 73 | | Some(o) -> 74 | o.Status <- OrderStatus.Rejected 75 | | _ -> printfn "ERROR: The order was not found in OMS" 76 | debug "REJECTED" 77 | | OrdStatus.REPLACED -> 78 | match this.findOrder(oid.ToString()) with 79 | | Some(o) -> 80 | o.Status <- OrderStatus.Replaced 81 | | _ -> printfn "ERROR: The order was not found in OMS" 82 | debug "REPLACED" 83 | | OrdStatus.EXPIRED -> 84 | printfn "ExecutionReport (EXPIRED) %A" report 85 | | _ -> printfn "ExecutionReport (other) %A" report 86 | | _ -> () 87 | 88 | member this.OnLogout(sessionID : SessionID) : unit = printf "OnLogout" 89 | member this.OnLogon(sessionID : SessionID) : unit = printf "OnLogon" 90 | 91 | type ConsoleLog() = 92 | interface ILog with 93 | member this.Clear() : unit = printf "hello" 94 | member this.OnEvent(str : string) : unit = printfn "%s" str 95 | member this.OnIncoming(str : string) : unit = printfn "%s" str 96 | member this.OnOutgoing(str : string) : unit = printfn "%s" str 97 | 98 | type ConsoleLogFactory(settings : SessionSettings) = 99 | interface ILogFactory with 100 | member this.Create(sessionID : SessionID) : ILog = new NullLog() :> ILog 101 | 102 | type FIXEngine(orders:BindingList) = 103 | let settings = new SessionSettings(@"conf\config.cfg") 104 | let application = new ClientInitiator(orders) 105 | let storeFactory = FileStoreFactory(settings) 106 | let logFactory = new ConsoleLogFactory(settings) 107 | let messageFactory = new MessageFactory() 108 | let initiator = new SocketInitiator(application, storeFactory, settings) 109 | let currentID = initiator.GetSessionIDs() |> Seq.head 110 | let orders = orders 111 | member this.init() : unit = 112 | () 113 | member this.start() : unit = 114 | initiator.Start() 115 | member this.stop() : unit = 116 | initiator.Stop() 117 | member this.sendOrder(order:Order) : unit = 118 | let fixOrder = new NewOrderSingle() 119 | // Convert to Order to NewOrderSingle 120 | fixOrder.Symbol <- new Symbol(order.Instrument) 121 | fixOrder.ClOrdID <- new ClOrdID(order.Timestamp) 122 | fixOrder.OrderQty <- new OrderQty(decimal order.Qty) 123 | fixOrder.OrdType <- new OrdType('2'); // Limit order 124 | fixOrder.Side <- new Side('1'); 125 | fixOrder.Price <- new Price(decimal order.Price); 126 | fixOrder.TransactTime <- new TransactTime(); 127 | fixOrder.HandlInst <- new HandlInst('2'); 128 | fixOrder.SecurityType <- new SecurityType("OPT"); // Option 129 | fixOrder.Currency <- new Currency("USD"); 130 | // Add to OMS 131 | orders.Add(order) 132 | // Send order to target 133 | Session.SendToTarget(fixOrder, currentID) |> ignore 134 | 135 | module Apa = 136 | // Use a list of NewOrderSingle as first step 137 | let orders = new BindingList() 138 | let fixEngine = new FIX.FIXEngine(orders) 139 | fixEngine.init() 140 | fixEngine.start() 141 | let buyOrder1 = new Order(OrderSide.Buy, OrderType.Limit, 24.50, Tif.GoodForDay, 100, "ERICB4A115", 0.0) 142 | let buyOrder2 = new Order(OrderSide.Buy, OrderType.Limit, 34.50, Tif.GoodForDay, 100, "ERICB4A116", 0.0) 143 | let buyOrder3 = new Order(OrderSide.Buy, OrderType.Limit, 44.50, Tif.GoodForDay, 100, "ERICB4A117", 0.0) 144 | fixEngine.sendOrder(buyOrder1) 145 | fixEngine.sendOrder(buyOrder2) 146 | fixEngine.sendOrder(buyOrder3) -------------------------------------------------------------------------------- /Chapter02/Chapter2.fsx: -------------------------------------------------------------------------------- 1 | /// Background workers (1) 2 | 3 | open System.Threading 4 | open System.ComponentModel 5 | 6 | let worker = new BackgroundWorker() 7 | worker.DoWork.Add(fun args -> 8 | for i in 1 .. 50 do 9 | // Simulates heavy calculation 10 | Thread.Sleep(1000) 11 | printfn "%A" i 12 | ) 13 | 14 | worker.RunWorkerCompleted.Add(fun args -> 15 | printfn "Completed..." 16 | ) 17 | 18 | worker.RunWorkerAsync() 19 | 20 | /// Background workers (2) 21 | 22 | open System.Threading 23 | open System.ComponentModel 24 | 25 | let worker = new BackgroundWorker() 26 | worker.DoWork.Add(fun args -> 27 | for i in 1 .. 50 do 28 | // Simulates heavy calculation 29 | Thread.Sleep(1000) 30 | printfn "A: %A" i 31 | ) 32 | 33 | worker.DoWork.Add(fun args -> 34 | for i in 1 .. 10 do 35 | // Simulates heavy calculation 36 | Thread.Sleep(500) 37 | printfn "B: %A" i 38 | ) 39 | 40 | worker.RunWorkerCompleted.Add(fun args -> 41 | printfn "Completed..." 42 | ) 43 | 44 | worker.RunWorkerAsync() 45 | 46 | /// Background workers (3) 47 | open System.ComponentModel 48 | 49 | let workerCancel = new BackgroundWorker(WorkerSupportsCancellation = true) 50 | workerCancel.DoWork.Add(fun args -> 51 | printfn "apan %A" args 52 | for i in 1 .. 50 do 53 | if (workerCancel.CancellationPending = false) then 54 | Thread.Sleep(1000) 55 | printfn "%A" i 56 | ) 57 | 58 | workerCancel.RunWorkerCompleted.Add(fun args -> 59 | printfn "Completed..." 60 | ) 61 | 62 | workerCancel.RunWorkerAsync() 63 | workerCancel.CancelAsync() 64 | 65 | /// Threads (1) 66 | open System.Threading 67 | 68 | let runMe() = 69 | for i in 1 .. 10 do 70 | try 71 | Thread.Sleep(1000) 72 | with 73 | | :? System.Threading.ThreadAbortException as ex -> printfn "Exception %A" ex 74 | printfn "I'm still running..." 75 | 76 | let thread = new Thread(runMe) 77 | thread.Start() 78 | 79 | /// Threads (2) 80 | open System.Threading 81 | 82 | let runMe() = 83 | for i in 1 .. 10 do 84 | try 85 | Thread.Sleep(1000) 86 | with 87 | | :? System.Threading.ThreadAbortException as ex -> printfn "Exception %A" ex 88 | printfn "I'm still running..." 89 | 90 | let createThread() = 91 | let thread = new Thread(runMe) 92 | thread.Start() 93 | 94 | createThread() 95 | createThread() 96 | 97 | /// Thread pools (1) 98 | open System.Threading 99 | 100 | let runMe(arg:obj) = 101 | for i in 1 .. 10 do 102 | try 103 | Thread.Sleep(1000) 104 | with 105 | | :? System.Threading.ThreadAbortException as ex -> printfn "Exception %A" ex 106 | printfn "%A still running..." arg 107 | 108 | ThreadPool.QueueUserWorkItem(new WaitCallback(runMe), "One") 109 | ThreadPool.QueueUserWorkItem(new WaitCallback(runMe), "Two") 110 | ThreadPool.QueueUserWorkItem(new WaitCallback(runMe), "Three") 111 | 112 | /// Async example (1) 113 | open System.Net 114 | open Microsoft.FSharp.Control.WebExtensions 115 | 116 | /// Stock symbol and URL to Yahoo finance 117 | let urlList = [ "MSFT", "http://ichart.finance.yahoo.com/table.csv?s=MSFT&d=6&e=6&f=2013&g=d&a=1&b=1&c=2010&ignore=.csv" 118 | "GOOG", "http://ichart.finance.yahoo.com/table.csv?s=GOOG&d=6&e=6&f=2013&g=d&a=1&b=1&c=2010&ignore=.csv" 119 | "EBAY", "http://ichart.finance.yahoo.com/table.csv?s=EBAY&d=6&e=6&f=2013&g=d&a=1&b=1&c=2010&ignore=.csv" 120 | "AAPL", "http://ichart.finance.yahoo.com/table.csv?s=AAPL&d=6&e=6&f=2013&g=d&a=1&b=1&c=2010&ignore=.csv" 121 | "ADBE", "http://ichart.finance.yahoo.com/table.csv?s=ADBE&d=6&e=6&f=2013&g=d&a=1&b=1&c=2010&ignore=.csv" 122 | ] 123 | 124 | /// Async fetch of CSV data 125 | let fetchAsync(name, url:string) = 126 | async { 127 | try 128 | let uri = new System.Uri(url) 129 | let webClient = new WebClient() 130 | let! html = webClient.AsyncDownloadString(uri) 131 | printfn "Downloaded historical data for %s, recieved %d characters" name html.Length 132 | with 133 | | ex -> printfn "Exception: %s" ex.Message 134 | } 135 | 136 | /// Helper function to run in async parallel 137 | let runAll() = 138 | urlList 139 | |> Seq.map fetchAsync 140 | |> Async.Parallel 141 | |> Async.RunSynchronously 142 | |> ignore 143 | 144 | /// Get max closing price from 2010-01-01 for each stock 145 | runAll() 146 | 147 | /// Async example (2) 148 | open System.Net 149 | open Microsoft.FSharp.Control.WebExtensions 150 | 151 | /// Stock symbol and URL to Yahoo finance 152 | let urlList = [ "MSFT", "http://ichart.finance.yahoo.com/table.csv?s=MSFT&d=6&e=6&f=2013&g=d&a=1&b=1&c=2010&ignore=.csv" 153 | "GOOG", "http://ichart.finance.yahoo.com/table.csv?s=GOOG&d=6&e=6&f=2013&g=d&a=1&b=1&c=2010&ignore=.csv" 154 | "EBAY", "http://ichart.finance.yahoo.com/table.csv?s=EBAY&d=6&e=6&f=2013&g=d&a=1&b=1&c=2010&ignore=.csv" 155 | "AAPL", "http://ichart.finance.yahoo.com/table.csv?s=AAPL&d=6&e=6&f=2013&g=d&a=1&b=1&c=2010&ignore=.csv" 156 | "ADBE", "http://ichart.finance.yahoo.com/table.csv?s=ADBE&d=6&e=6&f=2013&g=d&a=1&b=1&c=2010&ignore=.csv" 157 | ] 158 | 159 | /// Parse CSV and extract max price 160 | let getMaxPrice(data:string) = 161 | let rows = data.Split('\n') 162 | rows 163 | |> Seq.skip 1 164 | |> Seq.map (fun s -> s.Split(',')) 165 | |> Seq.map (fun s -> float s.[4]) 166 | |> Seq.take (rows.Length - 2) 167 | |> Seq.max 168 | 169 | /// Async fetch of CSV data 170 | let fetchAsync(name, url:string) = 171 | async { 172 | try 173 | let uri = new System.Uri(url) 174 | let webClient = new WebClient() 175 | let! html = webClient.AsyncDownloadString(uri) 176 | let maxprice = (getMaxPrice(html.ToString())) 177 | printfn "Downloaded historical data for %s, max closing price since 2010-01-01: %f" name maxprice 178 | with 179 | | ex -> printfn "Exception: %s" ex.Message 180 | } 181 | 182 | /// Helper function to run in async parallel 183 | let runAll() = 184 | urlList 185 | |> Seq.map fetchAsync 186 | |> Async.Parallel 187 | |> Async.RunSynchronously 188 | |> ignore 189 | 190 | /// Get max closing price from 2010-01-01 for each stock 191 | runAll() 192 | 193 | /// MailboxProcessor (1) - Max agent 194 | open System 195 | 196 | // Type for our agent 197 | type Agent<'T> = MailboxProcessor<'T> 198 | 199 | // Control messages to be sent to agent 200 | type CounterMessage = 201 | | Update of float 202 | | Reset 203 | 204 | module Helpers = 205 | let genRandomNumber (n) = 206 | let rnd = new System.Random() 207 | float (rnd.Next(n, 100)) 208 | 209 | module MaxAgent = 210 | // Agent to keep track of max value and update GUI 211 | let sampleAgent = Agent.Start(fun inbox -> 212 | let rec loop max = async { 213 | let! msg = inbox.Receive() 214 | match msg with 215 | | Reset -> 216 | return! loop 0.0 217 | | Update value -> 218 | let max = Math.Max(max, value) 219 | 220 | Console.WriteLine("Max: " + max.ToString()) 221 | 222 | do! Async.Sleep(1000) 223 | return! loop max 224 | } 225 | loop 0.0) 226 | 227 | let agent = MaxAgent.sampleAgent 228 | let random = Helpers.genRandomNumber 5 229 | agent.Post(Update random) 230 | 231 | /// OOP (1) 232 | type Order(s: OrderSide, t: OrderType, p: float) = 233 | let mutable S = s 234 | member this.T = t 235 | member this.P = p 236 | 237 | member this.Side 238 | with get() = S 239 | and set(s) = S <- s 240 | 241 | member this.Type 242 | with get() = this.T 243 | 244 | member this.Price 245 | with get() = this.P 246 | 247 | member this.toggleOrderSide() = 248 | match S with 249 | | Buy -> S <- Sell 250 | | Sell -> S <- Buy 251 | 252 | /// OOP - Overloaded operators (1) 253 | type Order(s: OrderSide, t: OrderType, p: float) = 254 | let mutable S = s 255 | member this.T = t 256 | member this.P = p 257 | 258 | member this.Side 259 | with get() = S 260 | and set(s) = S <- s 261 | 262 | member this.Type 263 | with get() = this.T 264 | 265 | member this.Price 266 | with get() = this.P 267 | 268 | member this.toggleOrderSide() = 269 | S <- this.toggleOrderSide(S) 270 | 271 | member private this.toggleOrderSide(s: OrderSide) = 272 | match s with 273 | | Buy -> Sell 274 | | Sell -> Buy 275 | 276 | static member (~-) (o : Order) = 277 | Order(o.toggleOrderSide(o.Side), o.Type, o.Price) --------------------------------------------------------------------------------