This notebook demonstrates the integration of roboquant with Interactive Brokers (IBKR).
Disclaimer: roboquant is not affiliated with Interactive Brokers Group, Inc.’s.
There are currently 3 types of integration available:
The setup and integration with IBKR is a more complicated than with other brokers. The two extra steps that are required:
1.You cannot connect directly to an IBKR server. You will always need to run either IB Gateway
or Trader Workstation
, and roboquant will connect to that instance. IB Gateway
is the better option of the two, since it provides nice insights into the called API's and is less heavy weight. If you don't have it yet installed, you can download IB Gateway from here
Because you need a running IB Gateway that is accessible, you won't be able to successfully run this notebook on public infrastructure like MyBinder.org.
The TWS-API client cannot be packaged together with roboquant due to license constraints. So you'll have to download this file yourself, and then make it available to roboquant by uploading it to the notebooks directory.
TwsApi.jar
. This jar can be found at following path IBJts/source/JavaClient/TwsApi.jar
inside the extracted folder.After that, you can run the next cell and if no errors show up you are good to go.
// Make sure this is loaded first
@file:DependsOn("TwsApi.jar")
%use roboquant(version=2.1.0, modules=ibkr)
import org.roboquant.ibkr.*
Welcome()
The first part shows how to retrieve historic prices from IBKR and run a back test. Because IBKR offers data for many different assets in different markets, you need to specify what specific assets you are interested in. A simple symbol name won't be sufficient. For stock type of assets, at least supply the Symbol, Currency and possible the Exchange.
Typically a paid subscription is required to receive market data from IBKR. Also IBKR has strict limitations on how often you can make an API call. So if you require high frequency market data for running your strategy, IBKR market data subscriptions might not be the best fit.
// Use assets that you have a market data subscription for
val symbols = listOf("TSLA", "MSFT", "GOOGL", "JPM")
val assets = symbols.map { Asset(it) }
// Set this property in case you run this notebook in a docker container and the IBKR gateway runs on the host
// Config.setProperty("ibkr.host", "host.docker.internal")
val feed = IBKRHistoricFeed()
feed.retrieve(assets)
feed.waitTillRetrieved()
feed.assets
PriceBarChart(feed, feed.assets.first())
val strategy = EMAStrategy.PERIODS_5_15
val roboquant = Roboquant(strategy, AccountMetric())
roboquant.run(feed)
val account = roboquant.broker.account
account
val metricResults = roboquant.logger.getMetric("account.equity")
TimeSeriesChart(metricResults)
PriceChart(feed, assets.first(), account.trades)
Now we create an instance of the IBKRFeed and subscribe to the same asset. The setup is very much identical as back testing, the only difference is that now we use a live data feed instead of a historic data feed. Of course, outside trading hours there won't be price data and no signals or orders will be generated.
val feed = IBKRLiveFeed()
feed.subscribe(assets)
val strategy = EMAStrategy(3, 5)
val roboquant = Roboquant(strategy, AccountMetric())
We have all the components assembled that we need to start the test. All that remains, is to start the run and evaluate the strategy against the feed. We'll run it for 60 minutes, but you can change this. But remember the EMA Crossover is only going to evaluate after at least three observations.
If the time that is displayed in the progress bar looks off by a few hours, please realize that roboquant uses a timezone independent representation for all internal time processing (same as UTC).
val timeframe = Timeframe.next(60.minutes)
roboquant.run(feed, timeframe)
feed.disconnect()
The run has completed, lets see a few result. Of course if the run was outside trading hours without any price action, there will not be much to see.
val account = roboquant.broker.account
account
account.positions.summary()
Also lets plot two charts. See also the visualization notebook for examples of how to use charts to display results
val logger = roboquant.logger
logger.getMetricNames().summary()
val accountValue = logger.getMetric("account.equity")
TimeSeriesChart(accountValue, useTime = false)
TradeChart(account.trades)
And just as it was the case in the previous sections, most live feeds will only generate data during trading hours. So if you run these code cells outside trading hours, you won't see signals and orders being generated (depending of course on the used strategy).
val broker = IBKRBroker()
Config.exchangeRates = FixedExchangeRates(Currency.USD, Currency.EUR to 1.10)
broker.account
val account = broker.account
account.fullSummary()
Besides using strategies and policies to generate orders, you can also call the broker.place
method directly to place an order. Again, use this only with paper trading accounts.
val asset = account.assets.first()
val order = MarketOrder(asset, 2.0)
broker.place(listOf(order), Event.empty())
// Should show up as an open order initially
println(broker.account.fullSummary())
// Now should show up as an extra closed order (if placed during trading hours)
// Also you might hear TWS say "Order Filled"
Thread.sleep(5000)
val account = broker.account
account.fullSummary()
broker.account.trades
The setup of the feed is exactly the same as before. There is no difference in the feed if you use it for forward testing with the builtin Simulated Broker or paper- and live-trading. Also, this time we'll run it for 60 minutes.
val strategy = EMAStrategy(3, 5)
val roboquant = Roboquant(strategy, AccountMetric(), broker = broker)
val timeframe = Timeframe.next(60.minutes)
roboquant.run(feed, timeframe)
broker.account.positions.summary()
feed.disconnect()
broker.disconnect()