Showing posts with label F#. Show all posts
Showing posts with label F#. Show all posts

Wednesday, March 26, 2014

F#: Capture real time bars from InteractiveBrokers

Let's continue our journey of capturing data from InteractiveBrokers. In this blog, we discuss how to capture real time bars. A bar here means a summary of prices over the last 5-second interval. Currently you can choose one of the following 4 types of prices that occurred in the interval to generate bars:

  • TRADES: actual trade prices;
  • BID: bid prices;
  • ASK: ask prices;
  • MIDPOINT: mid prices.

For the type of prices you chose, the information in a bar includes OHLC (open-high-low-close), volume, WAP (weighted average price), and count (the number of trades that occurred, which is applicable to TRADES only). Compared to ad hoc data like tick prices or market depth, real time bars are sent over to you on a regular basis -- you receive an update every 5 seconds.
The code example here is to capture real time bars on TRADES. As in the previous two blog posts, the first thing we need to do is to specify the stock we are interested in. Again, here I chose Infosys listed in Mumbai.

        let contract = tws1.createContract()
        contract.secType <- "STK"
        contract.symbol <- "INFY"
        contract.exchange <- "NSE"
        contract.currency <- "INR"

Then, we use the reqRealTimeBarsEx method to subscribe to real time bar events. This method takes 5 parameters:

  • ticker ID and contract: the same as in the previous two blog posts;
  • bar size: the time length of each bar. Currently it must be 5, as only 5-second bars are supported;
  • price type: TRADES, BID, ASK, or MIDPOINT; 
  • useRTH: if a bar's interval fully or partially falls outside RTH (regular trading hours), this flag controls whether data outside RTH should be included in the bar. 0 means to exclude data outside RTH, and 1 means otherwise.

The following line instructs the broker that we wish to listen for real time bar events for the stock Infosys:

        tws1.reqRealTimeBarsEx(1, contract, 5, "TRADES", 0)

As for to unsubscribe, we just need invoke the cancelRealTimeBars method with the ticker ID for which we want to cancel subscription.

        tws1.cancelRealTimeBars(1)

Finally, below is a simple demo program, which captures real time bar events for 10 minutes and simply prints them out.

---------------------------------------------------------------------------------------
open AxTWSLib
open TWSLib
open System
open System.Drawing
open System.Windows.Forms

type RealTimeBarEvent =
    AxTWSLib._DTwsEvents_realtimeBarEvent

type RealTimeBarEventHandler =
    AxTWSLib._DTwsEvents_realtimeBarEventHandler

let now() = DateTime.Now

let formatTimeString (t:TimeSpan) =
    System.String.Format("{0:D2}:{1:D2}:{2:D2}",
            t.Hours, t.Minutes, t.Seconds)

let processRealTimeBarEvent _ (e:RealTimeBarEvent) =
    // ticker ID
    printf "id=%A," e.tickerId
    // time: the start of the bar interval
    let t = TimeSpan.FromSeconds(float e.time)
    printf "time=%s," (formatTimeString t)
    // open
    printf "o=%A," e.``open``
    // high
    printf "h=%A," e.high
    // low
    printf "l=%A," e.low
    // close
    printf "c=%A," e.close
    // wap
    printf "WAP=%A," e.wAP
    // volume
    printf "v=%A," e.volume
    // count
    printfn "cnt=%A" e.count

[<EntryPoint; STAThread>]
let main _ =
    printfn "start time: %A" (now())
    let form1 = new Form(Text="Dummy Form")
    let tws1 = new AxTws()
    tws1.BeginInit()
    form1.Controls.Add(tws1)
    tws1.EndInit()

    tws1.connect("", 7496, 1)
    printfn "server version = %d" tws1.serverVersion

    if tws1.serverVersion > 0 then
        tws1.realtimeBar.AddHandler(
            new RealTimeBarEventHandler(processRealTimeBarEvent))

        let contract = tws1.createContract()
        contract.secType <- "STK"
        contract.symbol <- "INFY"
        contract.exchange <- "NSE"
        contract.currency <- "INR"
        tws1.reqRealTimeBarsEx(1, contract, 5, "TRADES", 0)

        let timerWorkflow = async {
            do! Async.Sleep (60000 * 10)
            Application.Exit()
            }
        Async.Start timerWorkflow

        Application.Run()
        tws1.cancelRealTimeBars(1)
        tws1.disconnect()
        printfn "Diconnected!"

    printfn "end time: %A" (now())
    0

Tuesday, March 4, 2014

F#: Capture market depth data from InteractiveBrokers

In the last blog post, we discussed how to capture real-time tick data from InteractiveBrokers. Now let's see how we can capture real-time market depth data from InteractiveBrokers. The example code in this post is very similar to the one in the previous post. As far as the code is concerned, the main difference is just that now we subscribe to and process market depth events instead of tick price events. However, to interpret market depth events is slightly more complicated than to interpret tick data events, since an order book is basically a table with multiple entries.

Before we talk about how to process market depth events to maintain an order book, let's first take a look at an example below, which is a snapshot of the order book for the stock "Infosys" listed in Mumbai. The snapshot was taken at some time on 24th Feb 2014. You can see that the depth of this order book is 5, i.e., it shows the best 5 entries for both bid side and ask side, so there are 10 entries in total. Moreover, all entries are sorted by price, so that the entries for bid are in descending order and those for ask are in ascending order. By observing the market depth data, it's not difficult to see that the first bid/ask entry of an order book should be always the same as the tick price data. As such, subscribing to tick price events is equivalent to subscribing to market depth events with depth 1.

bid ask
position price size price size
1
3748.70 14 3749.00 71
2
3748.50 290 3749.40 10
3
3748.45 20 3749.45 1
4
3748.15 25 3749.85 50
5
3748.10 125 3749.90 15

So, how do we subscribe to market depth events? It's almost as easy as to subscribe to tick price events. The following code snippet creates an contract object specifying the stock we are interested in. In this case, it's Infosys listed in Mumbai.

        let contract = tws1.createContract()
        contract.secType <- "STK"
        contract.symbol <- "INFY"
        contract.exchange <- "NSE"
        contract.currency <- "INR"

Then, we use the reqMktDepthEx method, which takes 3 parameters: ticker ID, contract, and market depth. The following line instructs the broker that we wish to listen for market depth events up to depth 5.

        tws1.reqMktDepthEx(1, contract, 5)

Please note that though one can specify an arbitrarily large number for market depth, the effective market depth will still be subject to the exchange and your data subscription with InteractiveBrokers. More often than not, market depth data are not free, but there are some exceptions, like NSE in Mumbai. Once we have invoked the reqMktDepthEx method, we immediately receive the initial batch of market depth events, which are meant to initialize a complete snapshot of the order book. After the initial batch, we will receive subsequent market depth events on an ad hoc basis. These subsequent events represent increment changes to the order book. Basically, a market depth event consists of the following properties:
  • id: ticker ID, which is specified by us via the reqMktDepthEx method;
  • side: 0 = ask, 1 = bid;
  • operation: 0 = insert, 1 = update, 2 = delete;
  • position: indicate which position in the order book to update;
  • price: bid/ask price;
  • size: bid/ask  size.

For example, the snapshot example above was constructed by the following initial batch of events:

event sequence
id
side
operation
position
price
size
1
1
1
0
0
3748.7
14
2
1
1
0
1
3748.5
290
3
1
1
0
2
3748.45
20
4
1
1
0
3
3748.15
25
5
1
1
0
4
3748.1
125
6
1
0
0
0
3749
71
7
1
0
0
1
3749.4
10
8
1
0
0
2
3749.45
1
9
1
0
0
3
3749.85
50
10
1
0
0
4
3749.9
15

From the 11th event onward, we receive increment changes. For example, the 11th event was as follows:

event sequence
id
side
operation
position
price
size
11
1
1
1
0
3748.7
17
   
Based on this event, we should update the first bid entry of the order book with price 3748.7 and size 17. So I hope this is clear enough on how to interpret market depth events.

As for to unsubscribe, we just need invoke the cancelMktDepth method with the ticker ID for which we want to cancel the subscription.

        tws1.cancelMktDepth(1)

Finally, below is a simple demo program, which captures market depth events for 10 minutes and simply prints them out. This program does not present an order book visually.

----------------------------------------------------------------------------------------------
open AxTWSLib
open TWSLib
open System
open System.Drawing
open System.Windows.Forms

type MktDepthEvent =
    AxTWSLib._DTwsEvents_updateMktDepthEvent

type MktDepthEventHandler =
    AxTWSLib._DTwsEvents_updateMktDepthEventHandler

let now() = DateTime.Now

let processMktDepthEvent _ (e:MktDepthEvent) =
    // event timestamp 
    printf "%A," (now())
    // event ID, which should match the subscription ID
    printf "id=%A," e.id
    // side, 0=ask, 1=buy
    printf "side=%A," e.side
    // operation, 0=insert, 1=update, 2=delete
    printf "op=%A," e.operation
    // position
    printf "pos=%A," e.position
    // price
    printf "price=%A," e.price
    // size
    printfn "size=%A" e.size

[<EntryPoint; STAThread>]
let main _ = 
    printfn "start time: %A" (now())
    let form1 = new Form(Text="Dummy Form")
    let tws1 = new AxTws()
    tws1.BeginInit()
    form1.Controls.Add(tws1)
    tws1.EndInit()

    tws1.connect("", 7496, 1)
    printfn "server version = %d" tws1.serverVersion

    if tws1.serverVersion > 0 then
        tws1.updateMktDepth.AddHandler(
            new MktDepthEventHandler(processMktDepthEvent))

        let contract = tws1.createContract()
        contract.secType <- "STK"
        contract.symbol <- "INFY"
        contract.exchange <- "NSE"
        contract.currency <- "INR"
        tws1.reqMktDepthEx(1, contract, 5)

        let timerWorkflow = async { 
            do! Async.Sleep 10000
            Application.Exit() 
            }
        Async.Start timerWorkflow

        Application.Run()
        tws1.cancelMktDepth(1)
        tws1.disconnect()
        printfn "Diconnected!"

    printfn "end time: %A" (now())
    0





   

Tuesday, January 21, 2014

F#: Capture tick price data from InteractiveBrokers

In the last blog post, we saw how to set up connectivity to InteractiveBrokers. Let's move on to see how we can capture real-time tick price data from InteractiveBrokers. Before I show the complete example code, let me first go over some key steps.
The first thing is to specify for which asset you want to capture tick prices. We do that by filling in an IContract object. In the code snippet below, tws1 represents the connection to InteractiveBrokers and we use the factory method, createContract(), to create a blank IContract object. If the asset we are interested in is S&P 500 ETF Trust, we can fill in the IContract object as follows:

        let contract = tws1.createContract()
        contract.secType <- "STK"
        contract.symbol <- "SPY"
        contract.exchange <- "SMART"
        contract.currency <- "USD"

Then we can submit the IContract object via the reqMktDataEx method so as to subscribe to prices information of S&P 500 ETF Trust. The reqMktDataEx method takes 4 parameters. Here we need to focus on only the first 2: ticker ID and contract. Ticker ID is an integer, which we can choose any number as long as we assign a unique number to each asset we subscribe to. Below is how we invoke the reqMktDataEx method to submit the IContract object we created.

        tws1.reqMktDataEx(1, contract, "", 0)

So now we let ticker ID 1 represent S&P 500 ETF Trust. If later on we no longer want to listen to the prices of S&P 500 ETF Trust, we can pass ticker ID 1 to the cancelMktData method to stop receiving any further updates.

        tws1.cancelMktData(1)

After subscribing via the reqMktDataEx method, any tick price updates will be sent to us via events. As such, we write the event handler, processTickPriceEvent, to process tick price events. The handler is simply just to distinguish different types of tick prices and print them out. (As for the code of the handler, please refer to the complete code at the end of this blog post.) Then we register the handler with tick price events as follows:

        tws1.tickPrice.AddHandler(
            new TickPriceEventHandler(processTickPriceEvent))

After registering with the events, we have to invoke Application.Run() to enter the event loop to start capturing tick prices. My intention here is to capture tick prices for 10 seconds and then quit. To end the event loop after 10 seconds, I set up the following asynchronous workflow.

        let timerWorkflow = async { 
            do! Async.Sleep 10000
            Application.Exit() 
            }
        Async.Start timerWorkflow 

OK, now that we have covered all the major steps, below is the complete code of the program.

/////////////////////////////////////////////////////////////////////////////////////////////////////////
open AxTWSLib
open TWSLib
open System
open System.Drawing
open System.Windows.Forms

type TickPriceEvent = 
    AxTWSLib._DTwsEvents_tickPriceEvent

type TickPriceEventHandler =
    AxTWSLib._DTwsEvents_tickPriceEventHandler

let now() = DateTime.Now

let processTickPriceEvent _ (e:TickPriceEvent) =
    match e.tickType with
    | 1 -> printfn "%A|bid=%A" (now()) e.price
    | 2 -> printfn "%A|ask=%A" (now()) e.price
    | 4 -> printfn "%A|last=%A" (now()) e.price
    | 6 -> printfn "%A|high=%A" (now()) e.price
    | 7 -> printfn "%A|low=%A" (now()) e.price
    | 9 -> printfn "%A|close=%A" (now()) e.price
    | x -> printfn "%A|UNKNOW_TICK_TYPE(%d)=%A" (now()) x e.price

[<EntryPoint; STAThread>]
let main _ = 
    printfn "start time: %A" (now())
    let form1 = new Form(Text="Dummy Form")
    let tws1 = new AxTws()
    tws1.BeginInit()
    form1.Controls.Add(tws1)
    tws1.EndInit()

    tws1.connect("", 7496, 1)
    printfn "server version = %d" tws1.serverVersion

    if tws1.serverVersion > 0 then
        tws1.tickPrice.AddHandler(
            new TickPriceEventHandler(processTickPriceEvent))

        let contract = tws1.createContract()
        contract.secType <- "STK"
        contract.symbol <- "SPY"
        contract.exchange <- "SMART"
        contract.currency <- "USD"
        tws1.reqMktDataEx(1, contract, "", 0)

        let timerWorkflow = async { 
            do! Async.Sleep 10000
            Application.Exit() 
            }
        Async.Start timerWorkflow

        Application.Run()
        tws1.cancelMktData(1)
        tws1.disconnect()
        printfn "Diconnected!"

    printfn "end time: %A" (now())

    0



Sunday, January 5, 2014

F#: Connect to InteractiveBrokers via ActiveX

In this blog post we show how to write a F# console program which runs on Windows and connect to InteractiveBrokers (IB). There are a few different options of programmatic connectivity provided by IB. Here we use the ActiveX control (Tws.ocx) and Trade Workstation (TWS), so the end-to-end communication between our console program and IB goes like this:

F# code <=> Tws.ocx <=> TWS <=> IB server.

Before we begin, you need to have an account with IB, which can be either a real account or a paper account. Once you have the account ready, here is the step-by-step guide to the console program.

Install TWS and API:

  • Trade Workstation: please download and install a standalone version from here. You need to configure TWS to accept ActiveX clients by doing the following:
    • Select Edit->Global Configuration
    • Select API->Settings
    • Check "Enable ActiveX and Socket Clients" and note down the port number specified at "Socket Port". By default, the port number is 7496.
  • API: can be downloaded from here, which contains Tws.ocx.

Generate wrapper DLLs to expose Tws.ocx to .NET:
Assuming that the API has been installed at C:\TWS_API and you have Visual Studio loaded on your machine, you can then run Aximp.exe as follows:

C:\TWS_API\bin\ActiveX>aximp Tws.ocx

And you shall see the following 2 DLLs generated:

Generated Assembly: C:\TWS_API\bin\ActiveX\TWSLib.dll
Generated Assembly: C:\TWS_API\bin\ActiveX\AxTWSLib.dll

To make use of the ActiveX control, we need to add references to these 2 DLLs in the Visual Studio project. (Note that as the only thing we are trying to do here is connecting to and disconnecting from TWS, we need only AxTWSLib.dll. However, in following few blog posts, we will try to capture market data and submit trade orders, which will requires both DLLs.)

Write F# code to connect and disconnect:
Below you can see the code of our console program. There are some remarks about the code I would like to make:

  • Tws.ocx is an ActiveX control which is meant to be added to a GUI container. To get the control initialized properly, a dummy Windows Form is created to host it, as you can see at the beginning of the main function. As our intention here is a console program, we don't display the form.
  • To connect to TWS, we invoke the connect method, which takes 3 parameters: IP address, port number, client ID. 
    • IP address: to indicate where is the machine running TWS. Since I run TWS and the program on the same machine, there's no need to provide an IP address.
    • port number: 7496 by default, unless you change it in the Global Configuration page of TWS.
    • client ID: an integer used to identify this client connection. If you want to connect multiple clients to the same TWS instance at the same time, each client should be assigned a unique ID. 
  • Once TWS receives a connection request, a dialog pops up. Click on yes to accept the connection. 
  • To see whether the connection is successful, we check the serverVersion property of the control. If it's successful, we shall a positive serverVersion. Otherwise, we receive a zero. 


------------------------------------------------------------------------
open AxTWSLib
open System
open System.Drawing
open System.Windows.Forms

[<EntryPoint; STAThread>]
let main _ = 
    // initialize TWS ActiveX Control tws1
    let form1 = new Form(Text="Dummy Form")
    let tws1 = new AxTws()
    tws1.BeginInit()
    form1.Controls.Add(tws1)
    tws1.EndInit()

    // connect to local TWS
    tws1.connect("", 7496, 1)
    printfn "server version = %d" tws1.serverVersion

    // disconnect from TWS
    if tws1.serverVersion > 0 then 
        tws1.disconnect()

    0
------------------------------------------------------------------------

Friday, January 25, 2013

F#: contributing a chapter to an upcoming book, "F# Deep Dives"

Thanks to Tomas Petricek's invitation, I am honored by being able to write a chapter for an upcoming book - F# Deep Dives. The chapter I am responsible for is Chapter 4: Numerical computing in financial domain. The first draft of the chapter is now available online via Manning's early access program.

Currently the chapter covers the following:
1 Introducing financial derivatives
2 Using probability functions of Math.NET
2.1 Configuring F# Interactive (profiling and floating point formatting)
2.2 Setting up Math.NET Numerics
2.3 Introducing Random Variables, Expectation and Variance
2.4 Generating Normally Distributed Samples
3 Geometric Brownian Motion and Monte Carlo Estimate
3.1 Modeling stock prices using geometric Brownian motion
3.2 Payoff Function, Discounted Payoff, and Monte Carlo estimate
3.3 Analyzing variance of Monte Carlo estimates
3.4 Pricing Path-dependent options (Asian options and barrier options)
3.5 Reducing Variance by antithetic variates

Friday, September 7, 2012

F#: K-S test on final prices of GBM paths

In the previous post, I mentioned that I chose System.Random, because it’s faster than any other random number generators available in Math.NET Numerics. And I also said that I have a minor concern about System.Random, because someone found a problem in the implementation of System.Random and raised this issue to the .NET team in early 2011. And the team admits it’s a problem. However, this issue is still there with .NET Framework 4.5. And it seems to me that the .NET team is not planning to take any immediate action. 

As I know little about the algorithm of System.Random, I’m not sure if it’s worth the efforts to pick up the details and quantify the impact. As such, I decided to take an easy way out, i.e., close my eyes to this issue  do Kolmogrov-Smirnov test (KS test) on only the final prices distribution. If the simulated distribution is statistically indistinguishable from the true, theoretic distribution in terms of shape, I will be fine with System.Random.  At the end of the day, I think what really matters to me is whether System.Random can ultimately give me the correct shape of the final prices distribution I want.

Basically the F# code is pretty much the same as in the previous post except the following:


  • I add a run_stat_test function to run the statistical test and generate a Q-Q plot in Matlab.

  • I change the Monte Carlo parameter N to 1 so that I simulate only one data point, i.e., final price, for each simulated path.


// Monte Carlo parameters
let T = 1.0             // time horizon in years
let M = 100000          // number of simulations
let N = 1               // number of time intervals


You can see that I take the logarithms of all final prices and test them against a normal distribution with mean = (log S0)+(r-0.5*sigma*sigma)*T and standard deviation = sigma*sqrt(T) using KS test. And the output from the test is as follows:

h = false, p = 0.7243765247

And here is the Q-Q plot:

untitled

p-value is about 0.72, which means it’s quite likely that the simulated prices were indeed sampled from the normal distribution, and h=false means that we can’t reject the null hypothesis. Moreover, the Q-Q plot does not show any obvious outliers off the diagonal line. Therefore, it’s ok for me to continue to use System.Random.

If any expert happens to know about the issue of System.Random and is willing to shed some light on it, it will be appreciated.

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

open MathNet.Numerics.Distributions
open MathNet.Numerics.Statistics

// Monte Carlo parameters
let T = 1.0             // time horizon in years
let M = 100000          // number of simulations
let N = 1               // number of time intervals
let p = 0.99            // confidence level

// underlying dynamics
let S0 = 50.0         // spot price
let sigma = 0.2       // annualized vol
let r = 0.01          // risk-free rate

// risk factor dW
let rnd = new System.Random(1)
let get_dW dt =
    let dW = Normal.WithMeanVariance(0.0, dt)
    dW.RandomSource <- rnd
    (fun () -> dW.Sample())

// GBM path generation
let generate_GBM_paths S0 r sigma T M N =
    let dt = T / (float N)
    let drift = (r - 0.5 * (sigma**2.0)) * dt
    let dW = get_dW dt
    Array.init M (fun _ ->
         Array.init N (fun _ -> drift + sigma * dW()) |>
         Array.scan (+) 0.0 |>
         Array.map (fun x -> S0 * exp(x)) )

let run_stat_test (prices:float array) =
    let m = (log S0)+(r-0.5*sigma*sigma)*T
    let s = sigma*sqrt(T)
    let log_prices = Array.map log prices
    let matlab = new MLApp.MLAppClass()
    matlab.PutWorkspaceData("log_prices", "base", log_prices)
    matlab.PutWorkspaceData("m", "base", m)
    matlab.PutWorkspaceData("s", "base", s)
    matlab.Execute("pd = ProbDistUnivParam('normal',[m s])") |> ignore
    matlab.Execute("[h,p] = kstest(log_prices,pd,0.05,0)") |> ignore
    matlab.Execute("qqplot(log_prices,pd)") |> ignore
    let h = matlab.GetVariable("h","base")
    let p = matlab.GetVariable("p","base")
    (h,p)

// main
let main () =
    let final_prices = generate_GBM_paths S0 r sigma T M N |>
                        Array.map (fun p -> p.[p.Length - 1])
    let forward = final_prices.Mean()
    let std_dev = final_prices.StandardDeviation()
    let std_error = std_dev / sqrt(float M)
    let std_normal = Normal.WithMeanVariance(0.0,1.0)
    let radius = std_normal.InverseCumulativeDistribution(0.5*(1.0+p))
                    * std_error
    let conf_int = (forward - radius, forward + radius)
    let h,p = run_stat_test final_prices
    printfn "h = %A, p = %A" h p
    (forward, std_dev, std_error, conf_int)

// run simulation
main()

// calculate theoretical mean and deviation
let true_forward = S0 * exp(r*T)
let v = sigma*sigma*T
let true_std_dev = sqrt((exp(v)-1.0)*exp(2.0*(log(S0)+r*T-0.5*v*T)+v))

Sunday, September 2, 2012

F#: Simulate entire GBM path

In all the Monte Carlo simulation codes I have done in previous posts, I simulate only the underlying prices at expiry, because European Calls and Puts are dependent on only the final price at expiry and we knew that the final prices follow a lognormal distribution, for which there's a nice analytic expression and hence we can easily simulate. However, if we move further out of the safe zone of European options, it's quite often that we need to simulate entire GBM (Geometric Brownian Motion) paths. Before we really get into dealing with path-dependent options, let's first take a look at my implementation for generating GBM paths based on the Euler method.

In this post, I started using a open-source numerical library, Math.NET Numerics. Don Syme wrote a blog on how to install and use it. Basically I use it in my code for two purposes:

  • using the Normal class to draw normal random numbers. Internally the Normal class also uses the Box Muller transform to generate samples, but it uses the polar form of the transform, which is faster than the basic form I had been using in previous posts. Another good thing about the polar form is that it does not suffer the infinity issue I mentioned in a previous post. 
  • using some handy statistical functions, like mean, standard deviation, and inverse CDF, because I like to compute standard error and confidence interval. 
Before we look into the code, here are the parameters I use:


// Monte Carlo parameters
let T = 1.0             // time horizon in years
let M = 100000          // number of simulations
let N = 252             // number of time intervals
let p = 0.99            // confidence level

// underlying dynamics
let S0 = 50.0         // spot price
let sigma = 0.2       // annualized vol
let r = 0.01          // risk-free rate


Below is the code snippet to construct a function which generates Brownian Motion increments. You can see that I use System.Random as the random source, because it's a few times faster than all other uniform random sources provided by Math.NET Numerics. (I actually have a minor concern about System.Random. I might talk about it later in another post.)

// risk factor dW
let rnd = new System.Random(1)
let get_dW dt =
    let dW = Normal.WithMeanVariance(0.0, dt)
    dW.RandomSource <- rnd
    (fun () -> dW.Sample())

The below function, generate_GBM_paths, is the one that actually simulate all the entire paths. Here I use an array of arrays to keep all the paths simulated. Storing every complete simulated path is memory consuming, but it's convenient for me to do post-simulation analysis. I think this code snippet is a good example to show that F#'s built-in array processing functions are really expressive and handy, compared to other major general-purpose languages.


// GBM path generation based on the Euler method
let generate_GBM_paths S0 r sigma T M N =
    let dt = T / (float N)
    let drift = (r - 0.5 * (sigma**2.0)) * dt
    let dW = get_dW dt
    Array.init M (fun _ -> 
         Array.init N (fun _ -> drift + sigma * dW()) |>
         Array.scan (+) 0.0 |>
         Array.map (fun x -> S0 * exp(x)) )

As you can see from the main function of the code, after generation of GBM paths, I extracted final prices out of all the paths and passed them to:

  • the statistical functions provided by Math.NET Numerics to compute 
    • mean, i.e., the forward price of the underlying
    • standard deviation of the simulated final price distribution
    • standard error of the final price simulation
    • confidence interval
  • Matlab to plot KDE (Kernel Density Estimation) to see if the simulated final price distribution looks like a lognormal distribution. I recently just learnt KDE from the book, "Data Analysis with Open Source Tools," written by Philipp K. Janert. In case you haven't heard about KDE, it's something like an advanced version of histograms. In a KDE, the graph will be smoothed by a density function and you don't need to find out what bin size you should use. If you don't like to run this KDE part, you could simply comment it out.

Finally, here are the results from the main function:


Real: 00:00:07.541, CPU: 00:00:06.817, GC gen0: 201, gen1: 135, gen2: 2
val it : float * float * float * (float * float) =
  (50.47657684, 10.21848436, 0.03231368481, (50.3933423, 50.55981138))

The entries in the above tuple are mean, standard deviation, standard error, and confidence interval. I also calculated theoretical mean and standard deviation as follows:

val true_forward : float = 50.50250835
val true_std_dev : float = 10.20235347

And below is the KDE plot:


///////////////////////////////////////////////////////////////////////////////////////////////////////


open MathNet.Numerics.Distributions
open MathNet.Numerics.Statistics

// Monte Carlo parameters
let T = 1.0             // time horizon in years
let M = 100000          // number of simulations
let N = 252             // number of time intervals
let p = 0.99            // confidence level

// underlying dynamics
let S0 = 50.0         // spot price
let sigma = 0.2       // annualized vol
let r = 0.01          // risk-free rate

// risk factor dW
let rnd = new System.Random(1)
let get_dW dt =
    let dW = Normal.WithMeanVariance(0.0, dt)
    dW.RandomSource <- rnd
    (fun () -> dW.Sample())

// GBM path generation
let generate_GBM_paths S0 r sigma T M N =
    let dt = T / (float N)
    let drift = (r - 0.5 * (sigma**2.0)) * dt
    let dW = get_dW dt
    Array.init M (fun _ -> 
         Array.init N (fun _ -> drift + sigma * dW()) |>
         Array.scan (+) 0.0 |>
         Array.map (fun x -> S0 * exp(x)) )

// plot the simulated distribution using KDE in Matlab
let plot_KDE (prices:float array) =
    let matlab = new MLApp.MLAppClass()
    matlab.PutWorkspaceData("x", "base", prices)
    matlab.Execute("[f,xi] = ksdensity(x);") |> ignore
    matlab.Execute("plot(xi,f);") |> ignore
    ()

// main
let main () =
    let final_prices = generate_GBM_paths S0 r sigma T M N |> 
                        Array.map (fun p -> p.[p.Length - 1])
    let forward = final_prices.Mean()
    let std_dev = final_prices.StandardDeviation()
    let std_error = std_dev / sqrt(float M)
    let std_normal = Normal.WithMeanVariance(0.0,1.0)
    let radius = std_normal.InverseCumulativeDistribution(0.5*(1.0+p)) 
                    * std_error
    let conf_int = (forward - radius, forward + radius)
    plot_KDE final_prices
    (forward, std_dev, std_error, conf_int)

// run simulation
main()

// calculate theoretical mean and deviation
let true_forward = S0 * exp(r*T)
let v = sigma*sigma*T
let true_std_dev = sqrt((exp(v)-1.0)*exp(2.0*(log(S0)+r*T-0.5*v*T)+v))

Friday, August 24, 2012

F#: Running Monte Carlo Simulation on GPU

Multi-threading on CPU is a bit old fashioned. Let's run Monte Carlo Simulation with style on GPU. Here I'm using the Accelerator API from Microsoft Research. Tomas Petricek has a nice and concise introduction about the API. In fact, this post mainly relies on the introduction. If you never use the API, it's a good idea to take a look at the article to pick up some basic concepts on how the API works.

Here are some points I like to mention about the sample code below:
  • Currently Accelerator does not support random number generation. Since I want to time only the execution done on GPU, I put the code for normal random number generation outside the main pricing function (i.e., European_Option_Price), as what I did in my previous posts on Monte Carlo. Inside the the pricing function, I try to put every computation on GPU.
  • As I still use the Box Muller transform, to avoid the issue of generating a normal random number of infinity, I apply the simple transform (i.e., Y = 1-U[0,1)) mentioned in the blog post. Well, if you want to try, you can choose not to apply the transform and set the seed to 388, and then see what your GPU will return. I tried that for fun.
  • Why I set grid_size = 3163 is because the number of simulations will be the square of grid_size, which is slightly over the simulation size (10 millions) I used in my previous posts on Monte Carlo. Easier for me to compare.
  • Obviously, using the API reduces F#'s expressiveness, but with some custom syntactic sugars, like overloaded operators, I believe that it can be mitigated. However, I don't bother to do that at this moment.
After running the code, the output is as follows:

option_price = 10.117122
CPU time = 234ms
Absolute time = 433ms

Wow, in terms of absolute time, this GPU version is almost 4 times faster than my previous champion, the Array version. Though we fix the seed to 1, the reason why the option price given by the GPU version is different from the price by the Array version is mainly because of the simple transform I mentioned above. If you don't apply the transform, you will see both versions produce "almost" the same option price (some very minor difference is still there, due to slightly different number of simulations).

My laptop is 4 years old and equiped with only a lousy Intel 965 Express chipset, because I seldom play PC games and hence I used to look at mainly CPU speed and memory when purchasing a laptop. Now, given this impressive performance result, next time when I buy a new laptop, I will definitely choose one with a high-end NVIDIA graphics chipset.

Why NVIDIA? because I like to try the following:
  • Actually the latest release of Accelerator contains a CUDA target.
  • NVIDIA claims their cuRAND library can generate random numbers at light speed.

////////////////////////////////////////////////////////////
open System
open System.Diagnostics
open Microsoft.ParallelArrays

type PA = Microsoft.ParallelArrays.ParallelArrays
type FPA = Microsoft.ParallelArrays.FloatParallelArray

let grid_size = 3163
let shape = [| grid_size; grid_size |]
let dxTarget = new DX9Target()

let rnd = new Random(1) 
let next_double_pair () =
         (1.0 - rnd.NextDouble(), 1.0 - rnd.NextDouble())
//let rnd = new Random(388)  
//let next_double_pair () = (rnd.NextDouble(), rnd.NextDouble())
let box_muller_transform (u1,u2) =
     sqrt(-2.0*log(u1)) * cos(2.0*Math.PI*u2)
let generator _ _ =
     next_double_pair() |> box_muller_transform |> (float32)
let random_normals = Array2D.init grid_size grid_size generator
let European_Option_Price K S0 r vol T =
    let FPA_zero = new FPA(float32 0.0, shape)
    let FPA_N = new FPA(float32 (grid_size * grid_size), [|1|])
    let FPA_discount = new FPA(float32 (exp(-r*T)), [|1|])
    let FPA_K = new FPA(float32 K,shape)
    let FPA_S0 = new FPA(float32 S0,shape)
    let FPA_mean =
                    new FPA(float32 ((r - 0.5*(vol**2.0))*T), shape)
    let FPA_sigma = new FPA(float32 (vol*sqrt(T)), shape)
    let FPA_exp = new FPA(float32 (exp 1.0), shape)
    let FPA_normals = new FPA(random_normals)
    let FPA_S_T = 
                  FPA_S0 *
                  PA.Pow(FPA_exp,
                   (FPA_mean + FPA_sigma * FPA_normals))
    let FPA_payouts = PA.Max(FPA_S_T - FPA_K, FPA_zero)
    let FPA_price = FPA_discount * PA.Sum(FPA_payouts) / FPA_N
    let result = dxTarget.ToArray1D(FPA_price)
    (float) result.[0]

let S0 = 50.0
let K = 40.0
let r = 0.01
let vol = 0.2
let T = 0.25
let price() =
    let price = European_Option_Price K S0 r vol T
    printfn "option_price = %f" price

let time f =
    let proc = Process.GetCurrentProcess()
    let cpu_time_stamp = proc.TotalProcessorTime
    let timer = new Stopwatch()
    timer.Start()
    try
        f()
    finally
        let cpu_time = (proc.TotalProcessorTime -
                        cpu_time_stamp).TotalMilliseconds
        printfn "CPU time = %dms" (int64 cpu_time)
        printfn "Absolute time = %dms" timer.ElapsedMilliseconds

time price
  


Monday, August 20, 2012

F#: Infinity and Infinitesimal-ness

These days many programming languages have the notion of infinity defined explicitly with their float type. F# (or .Net actually) is no exception. In contrast to infinity, Double.Epsilon is the smallest positive float in F#, i.e., anything between Double.Epsilon and 0.0 is viewed as 0.0 in F#. Let's use F# Interactive to do some simple empirical studies on both infinity and Double.Epsilon in F#.

First of all, F# has a built-in float value of infinity, which is equivalent to System.Double.PositiveInfinity in .Net.

> infinity;;
val it : float = infinity
> System.Double.PositiveInfinity;;
val it : float = infinity


Well, negative infinity is also there.

> -infinity;;
val it : float = -infinity
> System.Double.NegativeInfinity;;
val it : float = -infinity


Then let's see how small Double.Epsilon is:

 > System.Double.Epsilon;;
val it : float = 4.940656458e-324


Not sure how you feel about this, but it looks "scary" small to me. By the way, we also have Double.MaxValue and Double.MinValue.

> System.Double.MaxValue;;
val it : float = 1.797693135e+308
> infinity > System.Double.MaxValue;;
val it : bool = true
> System.Double.MinValue;;
val it : float = -1.797693135e+308
> System.Double.MinValue > -infinity;;
val it : bool = true


In financial mathematics, log(x) and exp(x) are used excessively, so let's take a look at how these 2 functions operate with infinity and Double.Epsilon.

> exp(infinity);;
val it : float = infinity
> log(infinity);;
val it : float = infinity

> log(0.0);;
val it : float = -infinity


We know that log(x) goes to negative infinity when x goes to 0.0. What if we evaluate log(x) at x = Double.Epsilon? Can F# properly handle this seemingly extreme case?

> log(System.Double.Epsilon);;
val it : float = -744.4400719
> exp(log(System.Double.Epsilon));;
val it : float = 4.940656458e-324
> exp(log(System.Double.Epsilon)) = System.Double.Epsilon;;
val it : bool = true


OK, log(Double.Epsilon) is only around -744.44, not too bad relative to the infinitesimal-ness of Double.Epsilon, and if you substitute this value into exp(x) you basically recover Double.Epsilon as shown above. If you run similar tests in Matlab, Matlab will return you pretty much the same results.

The above example actually implies a few things about the resolution of log(x) and exp(x) in F#:
  • Other than negative infinity, you won't see that log(x) could ever return any numerical value lower than -744.4400719, i.e., log(Double.Epsilon)
  • For any x smaller than -744.4400719, i.e., log(Double.Epsilon), exp(x) is evaluated as 0.0.
Besides exp(x) and log(x), you might also try how well F# handles sin(x) and cos(x) when x is extremely big or small. You might see something unexpected.

Thursday, August 16, 2012

F#: How could the Monte Calro pricer return option_price = infinity?

If you leave the random seed blank in the Monte Carlo code and let System.Random pick up a time-dependent seed for you (or you deliberately set the seed to 388), you might see a funny pricing result like this:

option_price = Infinity
CPU time = 2636ms
Absolute time = 2435ms


Why you see this funny "infinity" price is because:
  • System.Random.NextDouble() could generate 0.0, though the probability is extremely low. As such, it's possible to generate a tuple of (u1,u2) where u1 = 0.0 and (u2 < 0.25 or u2 > 0.75). This kind of (u1,u2) drives the Box Muller transform to infinity. Then, not surprisingly, we can see that a single sample of infinite payout screws up the Law of Large Numbers, as the integrability condition is not satisfied.
  • The notion of infinity is defined in F# (or actually via Double.PositiveInfinity and Double.NegativeInfinity in .Net). F# knows how to handle log(0.0) and exp(infinity) so that F# elegantly tells you "Sir, the option is literally priceless." rather than returns you a garbage float number. Nevertheless, elegance without correctness means nothing, as infinity is a wrong answer to our option pricing problem .
Well, there's nothing wrong for System.Random to generate a value of 0.0, because this is simply how it's designed. The NextDouble() method is meant to approximate U[0,1), but the possibility of getting a 0.0 value is not desirable here. One workaround is to take a simple transform: Y = 1.0 - U[0,1). Then Y follows U(0,1]. So the code snippet we use to draw random numbers can be modified as follows:

let N = 10000000
let rnd = new Random(388)    
let next_double () = 1.0 - rnd.NextDouble()
let randomPairs = Array.init N (fun i -> (next_double(), next_double()))
let box_muller_transform (u1,u2) = sqrt(-2.0*log(u1)) * cos(2.0*Math.PI*u2)
let randomNormals = randomPairs |> Array.map box_muller_transform


You might argue that mathematically P(U=0) is zero in either U(0,1) or U[0,1) or U(0,1] or U[0,1]. When it comes to implementation of a pseudo-random number generator, at least in the case of System.Random, U[0,1) usually implies P(U=0)>0.

Perhaps you like to know how I find out the seed of 388. For sure, I don't pick up this seed out of thin air. Here is the F# code I use to search for a seed that leads to infinity in our case:

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
open System
open System.Diagnostics


let search_for_0 seed_min seed_max draw_max =
    let found u1 u2 = if (u1 = 0.0 && (u2 < 0.25 || u2 > 0.75)) then true else false

    let rec seed_loop seed =
        let rnd = new Random(seed)
   
        let rec draw_loop draw =
            let (u1,u2) = (rnd.NextDouble(),rnd.NextDouble())
            if found u1 u2 || draw = draw_max then (u1,u2, draw)
            else draw_loop (draw + 1)
   
        let (u1,u2, draw) = draw_loop 1
        if found u1 u2 || seed = seed_max then (u1,u2, draw, seed)
        else seed_loop (seed + 1)
   
    let (u1,u2, draw, seed) = seed_loop seed_min
    printfn "seed_min = %d, seed_max = %d, draw_max = %d" seed_min seed_max draw_max
    if found u1 u2 then

        printfn "found u1 = 0.0 and u2 = %f at draw %d of seed %d" u2 draw seed
    else printfn "couldn't find 0.0 within the range specified."


let time () =
    let proc = Process.GetCurrentProcess()
    let cpu_time_stamp = proc.TotalProcessorTime
    let timer = new Stopwatch()
    timer.Start()
    try
        search_for_0 1 1000 10000000
    finally
        let cpu_time = (proc.TotalProcessorTime-cpu_time_stamp).TotalMilliseconds
        printfn "CPU time = %dms" (int64 cpu_time)
        printfn "Absolute time = %dms" timer.ElapsedMilliseconds

time()

Wednesday, August 15, 2012

F#: Monte Carlo Simulation using Tail Recursion

Previously we used lists or arrays to implement Monte Carlo simulation, where in the beginning of the code we draw a fixed number of random numbers and keep all of them in memory throughout the execution of the code. Needless to say it's very memory-consuming. Indeed, if I increase the number of simulation runs to 100 millions, the code (either the List or Array version) throws  System.OutOfMemoryException on my laptop.

We in fact don't really need to keep all the random numbers in memory, if the option price is the only thing we want to calculate, which is the case in the simple option pricing problem we are handling. At the end of the day, what really matters and hence we have to keep track of is the sum of all simulated option payouts rather than individual simulated payouts. In other words, every time when we generate a random number to simulate an option payout, there's no harm to throw away the random number as long as we have added the simulated payout to a partial sum value we keep track of.

That's where tail recursion can help. We want to use tail recursion with the accumulator pattern to achieve constant space complexity. In the code below,  the sum parameter of the loop function keeps track of the partial sum and plays the role of accumulator.

Well, here is the performance results for 10 million simulation runs on my laptop:
version
option price
CPU time (ms)
absolute time (ms)
List
10.118465
5,335
5,467
Array
10.118465
1,606
1,639
Tail Recursion
10.118465
2,730
2,732

The performance of the tail-recursive version is quite OK relative to List and Array. Anyhow, the following results show the true power of tail recursion when it comes to 100 million runs.

version
option price
CPU time (ms)
absolute time (ms)
List
out of memory
out of memory
out of memory
Array
out of memory
out of memory
out of memory
Tail Recursion
10.118305
26,800
26,898

Perhaps memory consumption is a more important factor to monitor than CPU time or absolute time. If I manually bring up Windows Task Manager, I can see that the memory use of the tail-recursive code remains steadily at the level of 1.4MB while the memory use of either the List or Array version keeps growing up to 1.8GB until it eventually throws out the out-of-memory exception and crashes.

///////////////////////////////////////////////////////////////////////////////
open System
open System.Diagnostics


let N = 10000000
let rnd = new Random(1)


let box_muller_transform (u1,u2) = sqrt(-2.0*log(u1)) * cos(2.0*Math.PI*u2)

let call_payout K S = max (S-K) 0.0

let European_Option_Price (payout:float->float) S0 r vol T =
    let S_T x = S0 * exp((r - 0.5*(vol**2.0))*T + vol*sqrt(T)*x)
    let rec loop n sum =
        if n = N then sum
        else let new_sample = (rnd.NextDouble(),rnd.NextDouble())
                              |> box_muller_transform|> S_T |> payout
             loop (n+1) (sum+new_sample)
    (loop 0 0.0) / (float N) |> (*) (exp(-r*T))


let S0 = 50.0
let K = 40.0
let r = 0.01
let vol = 0.2
let T = 0.25
let price() =
    let price = European_Option_Price (call_payout K) S0 r vol T
    printfn "option_price = %f" price


let time f =
    let proc = Process.GetCurrentProcess()
    let cpu_time_stamp = proc.TotalProcessorTime
    let timer = new Stopwatch()
    timer.Start()
    try
        f()
    finally
        let cpu_time = (proc.TotalProcessorTime-cpu_time_stamp).TotalMilliseconds
        printfn "CPU time = %dms" (int64 cpu_time)
        printfn "Absolute time = %dms" timer.ElapsedMilliseconds


time price