shinybroker banner

ShinyBroker is an ongoing development project at the Duke University FinTech Masters Program and is available for public use on paper accounts. Feature requests and bug reports are welcome on our GitHub page as we continue build up a full-featured platform for use in and outside the classroom.

ShinyBroker lets you build the trading system you want in Shiny and connect it with Interactive Brokers.

This 'hello world' walkthrough shows you how to set up a good development environment, install the ShinyBroker package, connect it to a running instance of TraderWorkstation (TWS) or IB Gateway (IBG), explore a few of the default market data features, and use Shiny to create a functioning user interface. The next article in this series will explain how to fetch, process, and graphically display market data from IBKR.

The ShinyBroker package, and the examples in the documentation, were created with PyCharm. You certainly do not have to use PyCharm, but it is used in this article series to make the material accessible to readers who aren't old hands at dealing with environments, versions, and packages. If you follow this tutorial and use PyCharm, you will not have to worry about any of that; if you don't, then it's assumed you know what you're doing and are comfortable with your own setup.

Use TWS unless you're already comfortable with IBG. ShinyBroker will work fine with IB Gateway, but it's recommended to start out with TraderWorkstation so that you can use its UI and menus to verify/follow along with whatever you're building in ShinyBroker.

Install the following:

Create a new PyCharm Project: We're going to set up a PyCharm project for this tutorial and instruct it to use one and only one dedicated installation of Python. This way, you don't have to deal with all sorts of compatability problems that may otherwise arise between different Python versions, libraries, and so on.

  1. Open up PyCharm and create a new project. The "project" is simply a folder that stores all files related to this tutorial. You can name it whatever you want and store it in a convenient place on your computer. Give it a good, informative name that you like -- it's helpful.
  2. Configure an Interpreter. You want to tell PyCharm to create a dedicated installation of Python only for use by this project, and you can do that by configuring an interpreter. You might have already created an environment for this project when you created a new project; if you did, then you'll see a folder named ".venv" in your project directory, like in the screenshot below:

    venv success

    If your PyCharm project does not have an interpreter configured, then you will probably be prompted to create one via a clickable banner within PyCharm. If you can't find the banner or if you closed it, you can use the interpreter settings menu found under File > Settings. From the interpreter settings window, add a new local interpreter in your project folder. You should now see the ".venv" folder in your project directory like the one in the screenshot above. .venv contains the installation of Python that your project will use.

  3. Verify that your project is using the correct interpreter. You can do that quickly by glancing at or mousing over the path that appears at the bottom right-hand corner of the PyCharm window -- it should display the path to the .venv folder in your project directory.

Word of Advice: If you've followed these steps and are having trouble -- especially if you've just installed everything and set up your project all in one go -- then you might want to reboot your computer, or at least try exiting and restarting PyCharm.

Once your development environment is set up, either by completing Step 0 or your own project setup, you may proceed.

Run the command pip install shinybroker in your project's terminal.

install shinybroker

You'll know it installed correctly and in the right place because you'll be able to find it in your .venv folder:

sb success

Now that ShinyBroker is installed, it's time to make sure your TWS is set up to accept data connections from APIs.

You need to check a few settings in TWS to make sure it will permit a data connection with ShinyBroker.

  1. Launch TWS and sign in to your paper account (or use the demo)
  2. Open the API Configuration menu found at File > Global Configuration > API > Settings.
  3. Check Enable ActiveX and Socket Clients
  4. Un-check Read-only API to allow ShinyBroker to send orders to TWS. We won't be using this in the current tutorial but subsequent articles will need it. Your settings menu should now look like the below:
    api config
  5. Click "Apply" and "OK"
  6. Don't close TWS. You'll need it open and running or else ShinyBroker won't have an IBKR connection to connect to!

Next we'll run a bare-bones ShinyBroker app to check that it runs and can connect with TWS. Reminder that TWS needs to be running in order for ShinyBroker to work, so if you shut it down after the previous step, you'll need to launch it again before going on to the next.

  1. Create a new Python script in PyCharm with File > New, then select "Python File" and give it an informative name (the example uses "hello_world_1.py").
  2. Paste the code below into your script:
    
    import shinybroker as sb
    
    # Create an instance of a ShinyBroker App object using the default ui and server
    app = sb.sb_app(
        host='127.0.0.1',  # localhost TWS is being served on your local machine
        port=7497,         # make this match the port in your API Settings config
        client_id=10742    # picked at random, choose another Client ID if preferred
    )
    
    # Run the app
    app.run()
  3. Run your Python script. There are many ways to do so, but one quick and easy way is to highlight all of the code in your script with "select all", right-click the selection, and choose "Execute Selection in Python Console". It's even faster with keyboard shortcuts, e.g., for a Mac: command-A to select all, then alt-shift-E to execute. You can find those commands, and their shortcuts, by right-clicking on highlighted code in PyCharm.
  4. After you execute the code PyCharm will launch a new Python console to run the code you told it to run. Your screen should look something like the below. You can see what the code you ran did: it created a ShinyBroker app object and told it where to find an IBKR connection by specifying host, port, and client_id. By itself, that app object does nothing but sit there in variable space; to make it run, you have to call its run method, which we did in the final line of the sample code when we called app.run(). That command is the one that started the continuously-running Python process that you see in your Console. That process serves your ShinyBroker app at the blue hyperlink appearing in the command line output. The app will continue to run until you tell it to stop (e.g., by clicking the red square in the Python Console in PyCharm).
    code execution success
  5. To view your app, click the hyperlink in the command line output. Doing so will open your first ShinyBroker App in your default browser! You can also copy-paste the url into a browser of your choice. (And if you're thinking can I serve the app on one computer and access the app via browser from another computer/phone in a different location the answer is "yes, you can", but that's a later topic)

Interact with the app and make note of a few important points.

When you navigate a browser to the url at which your app is being served, you should be greeted with an app that looks something like the screen snip below.

app is running

Take a few minutes to poke around in the app and try out some of the default tabs & tools provided for tasks involving market data, contract matching, socket messages from IBKR, and so on.

Especially important to note is the section on the home page, which currently reads no ui passed to sb_ui(). That space is special -- it is reserved for you, the trader, to put UI elements and controls for your trading system. Since no ui was specified when we created this simple app object in this hello world example, ShinyBroker is telling you that it's empty.

We'll conclude this tutorial in the next step by injecting a little bit of ui into that space.

When you're finished exploring the app and ready to move on, stop the app from running by clicking the red square button in the console tab in PyCharm.

You're now ready for the final step in this tutorial in which you'll add a bit of live, functional UI to your ShinyBroker app.

If you're new to Shiny and reactive programming it is very highly recommended that you brush up on the basics by reviewing some of the very accessible and informative videos that the team at Posit has created to teach this topic. The video tutorial below, by Winston Chang of Posit, is an excellent place to start. As you watch, remember that anything you can build in Shiny, you can use in ShinyBroker.

Now it's time for the real learning & takeaway part of this turtorial. Let's take a minute to understand and appreciate that all user interfaces must necessarily operate on the same basic principle:

  1. Some kind of new information becomes available as input; for example: user keystrokes, the market price of an asset changes, an order fills, etc. No matter what its form or purpose, the point is that some kind of change has taken place meaning that there is new, or, equivalently, updated data available that is of importance to you, the trader. Information like this shows up asynchronously, meaning that there is no way to tell when or if it will become available. Therefore a system like ShinyBroker must always be on the ball and ready to handle incoming data as it comes in from the brokerage connection.
  2. Incoming data is collected by a server function, whose job it is to process the data into a useful return value and pass that value to the appropriate place in your app. For example, the input data might be a time-indexed list containing the past hour's prices for a stock at 1-minute intervals, and a server function's job might be to accept that list as input and return the timestamp of the highest (or lowest, etc.) price during the period.
  3. The server function may pass its return value to just about anything the trader can dream up but its ultimate fate is to be displayed to the trader as some sort of output that is understandable to the trader; for example: a plot, an updated forecast calculation, or a text box.

This tutorial culminates in the next step, which will show you how to implement this process using the magic of Shiny, which was written to greatly simplify the entire input -> processing -> output loop, making these kinds of systems much easier for the trader to build.

Our newly added ui will cause the ShinyBroker app to operate as follows:

  1. The user inputs a string to an input text box
  2. A server function watches the contents of the text box and when it changes, it appends the string "You entered '" to the beginning of the input's text, and appends "'." to the end; therefore, the resulting string is "You entered '{string that you entered}'.".
  3. The return string is passed to an output text box where you, the trader, can read it.

To accomplish this feat we'll need to add 3 new elements to our ShinyBroker app:

  1. A text input box so that the user has something to type into
  2. A server function that performs the string appending steps
  3. A text output that displays the final string

You can build just about anything you want starting from this humble example simply by keeping the "input -> processing -> output" data flow in your mind and making intellegent use of the buttons, widgets, plots, and other Python tools that are readily available in Shiny and other Python packages.

Please try the next step now.

First, we'll need the two pieces of UI that will appear on the web page: an input for the user to type into, and an output to see the result. We'll use the input_text and output_code objects made available by the ui object in Shiny, and store them together in a div, with the input on top of the output. All sorts of different components are available both in Shiny and in other packages like shinywidgets, or you can build your own. In this example, however, we'll only need two components: input_text and output_code. In the code below we use these components to create a new piece of ui and assign to it the appropriate name a_piece_of_new_ui.


from shiny import ui

# Some UI to add to the app
a_piece_of_new_ui = ui.div(
    ui.input_text(
        id='sb_example_text_in',
        label='Example Input. Type something!'
    ),
    ui.output_code('sb_example_text_out')
)

Now we need some logic: a function that recognizes the input text box (which we named sb_example_text_in above) as an input that it needs to watch. It should also possess the additional properties such that, whenever the text in the input changes, the function executes, calculates the new string that we want for the text output, and sends that value to the output object that we named sb_example_text_out. ShinyBroker uses the Shiny framework (see the video above) to simplify all of the 'watching and updating' tasks in order to make it easy to write a function that does the job.

In Shiny-speak, what we want to write is called a rendering function, a special type of function that was created for exactly the kind of 'watching and updating' functionality that we need. A suitable rendering function is provided below but if you're new to Shiny, consider the following bullet points to help you understand rendering functions and why the code is written the way it is so that you can be better equipped to write your own.

Taking all that into account, a render function that suits our purposes might look like the one below:


    @render.code
    def sb_example_text_out():
        return f"You entered '{input.sb_example_text_in()}'."

All that's left is to put that function into an app! Fortunately, ShinyBroker makes that easy. In practice, when making a real trading app, you will probably have tons of different functions required to make the app work, perhaps stored in different files, doing tasks like calculating your models, updating your charts, outputs, and so on. No matter how many you have, you can include all your functions in your app simply by wrapping them into one overall server function that you can pass to sb.sb_app() when you build your ShinyBroker app. It does not matter what you name your server function; the only requirement is that its signature contains the five parameters input: Inputs, output: Outputs, session: Session, ib_socket, sb_rvs. The part of your app that you, the the trader, will write will not always, or even usually, make use all five parameters explicitly, but they need to be there in your server function so that ShinyBroker can keep track of session information in its internals.

Below, we do exactly that: wrap the render function we just wrote into a new server function named a_server_function.


from shiny import Inputs, Outputs, Session, ui, render

# Server to support the new UI
# Signature must always contain the following five parameters:
#   input, output, session, ib_socket, and sb_rvs
def a_server_function(
        input: Inputs, output: Outputs, session: Session, ib_socket, sb_rvs
):
    @render.code
    def sb_example_text_out():
        return f"You entered '{input.sb_example_text_in()}'."

And finally... here comes the magic of ShinyBroker. We can create a new ShinyBroker app by passing our ui and server functions as arguments to sb_app:


import shinybroker as sb

# Create a ShinyBroker app with the new ui and server
app = sb.sb_app(
    a_piece_of_new_ui,
    a_server_function,
    host='127.0.0.1',
    port=7497,
    client_id=10742
)

And that's it! Altogether, the fully completed code example, including app.run(), can be found below:


import shinybroker as sb
from shiny import Inputs, Outputs, Session, ui, render


# Some UI to add to the app
a_piece_of_new_ui = ui.div(
    ui.input_text(
        id='sb_example_text_in',
        label='Example Input. Type something!'
    ),
    ui.output_code('sb_example_text_out')
)


# Server to support the new UI
# Signature must always contain the following five parameters:
#   input, output, session, ib_socket, and sb_rvs
def a_server_function(
        input: Inputs, output: Outputs, session: Session, ib_socket, sb_rvs
):
    @render.code
    def sb_example_text_out():
        return f"You entered '{input.sb_example_text_in()}'."


# Create a ShinyBroker app with the new ui and server
app = sb.sb_app(
    a_piece_of_new_ui,
    a_server_function,
    host='127.0.0.1',
    port=7497,
    client_id=10742
)

app.run()
                        

Run your app and view it in a browser like you did in Step 4. You should see something like the below in your Home tab, and the output should update with whatever you type into the input:

hello world 2

This concludes the first ShinyBroker Tutorial! To recap, we:

  1. Performed our installation and setup
  2. Ran a test app
  3. Demonstrated how the UI and Server functions that you write in your Shiny apps can be very easily integrated into IBKR's trading system with ShinyBroker

In the next installment, we'll be fetching, analyzing, and charting market data.