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



2 comments:

  1. Very interesting blog articles. I'm working on an FSharp Actor library with practical applications for streaming data across persistent client server connections and so I've created a demo application that simulates a stock price server with multiple clients. I am going to attempt to use your application here to allow the server to distribute quotes incoming from IB.

    ReplyDelete
    Replies
    1. Hi Jesse,

      Glad to see this blog helps. Is the stock price simulation server freely available? If so, I would like to try it.

      Delete