Close Navigation
Learn more about IBKR accounts
Blankly – Python Backtesting Guide

Blankly – Python Backtesting Guide

Posted March 19, 2024
Igor Radovanovic - via AlgoTrading101 Blog
AlgoTrading101

The article “Blankly – Python Backtesting Guide” first appeared on AlgoTrading101 blog.

Excerpt

What is Blankly?

Blankly is an open-source Python backtester that allows algorithmic traders to build, backtest, and run their trading algorithms for stocks, crypto, futures, and forex.

Link: https://github.com/blankly-finance/blankly

What is Blankly used for?

Blankly is mainly used by algorithmic traders for backtesting and running trading strategies in Python.

Why should I use Blankly?

  • Blankly is completely free.
  • Blankly is easy to use and beginner-friendly.
  • Is precise.
  • Has built-in support for exchanges such as Binance, Coinbase, and Alpaca.
  • Allows for backtesting using alternative data
  • Is maintained.

Why shouldn’t I use Blankly?

  • Doesn’t have advanced orders (OCO, straddle, etc.)
  • Doesn’t have advanced position handling.
  • Should have more features.
  • Some parts of the website and documentation are outdated.
  • It seems that the development has slowed down.

Is Blankly free?

Yes, Blankly is completely free and all the code for it is open-sourced. The upcoming premium part of it will feature a cloud-hosted solution.

What are some Blankly alternatives?

Blankly can be replaced with other software that can be more suitable for your needs. Here are some of them:

What exchanges and brokers does Blankly support?

Blankly supports the following exchanges and brokers at the time of writing this article:

How does Blankly work?

Blankly works by running an initialization layer that sets up your algorithm and the overall state that it will be using. Then, there are chains that you can add which can be executed on events such as each bar, each price, and each order.

Once the algorithm backtest is done, or once you stop your live trading it will start a teardown process to gracefully stop. This can be observed from the picture below:

How to get started with Blankly?

To get started with Blankly, all you need to do is to install the Blankly library and initialize the project. We do this by writing the following commands:

pip install blankly
mkdir blankly-project
cd blankly-project
blankly init

Now, you will be presented with different options that will help you customize blankly to fit your project’s needs. It will start you off with a question asking you to choose an exchange to connect to or go without one.

Blankly suggests users pick an exchange to get more accurate data, have the option to get live data, and have the ability to easily switch to a live trading algorithm. I’ll go with Alpaca.

After that, you will be asked to select what type of model you want to use. In our case, we will be creating a Strategy. For the next question, we will select that we don’t need a template, but feel free to select that option if you want to inspect it.

Finally, you can choose to add your API key for Alpaca now or later. I’ll choose later as I want to show you how you can do this by changing a file.

How to set API keys in Blankly?

To set API keys in Blankly, you can navigate to the keys.json file where you will find the supported exchanges and be able to set your API keys by changing the file. You will also see the sandbox parameter which can be set to True to use a paper trading account.

{
    "alpaca": {
        "example-portfolio": {
            "API_KEY": "********************",
            "API_SECRET": "********************",
            "sandbox": false
        }
    },

Above, I’ll place my Alpaca API keys and set the sandbox parameter to true.

What are the components of Blankly’s strategies?

The main two components of Blankly’s strategies are the initializer and price data event handler. The initializer is used to lay out the foundation of your algorithm and set the state variables that are to be used.

For example, here is a state that is aware of the traded symbol and constrains the data that will be passed to a deque that can hold a maximum of 150 candles. Once a new candle arrives, the oldest one will be pushed out. It also sets the position to be false.

def init(symbol, state: blankly.StrategyState):
    # This gets the past 150 data points as a deque to reduce memory usage
    state.variables['history'] = state.interface.history(
        symbol, to=150, return_as='deque'
    )['close']
    state.variables['owns_position'] = False

When it comes to the price events, here is an example of a price event that takes in the candles and calculates the RSI upon which it makes a market buy order and sets the position state to True if the RSI is oversold:

def price_event(price, symbol, state: StrategyState):
    state.variables['history'].append(price)
    rsi = blankly.indicators.rsi(state.variables['history'])
    if rsi[-1] < 30 and not state.variables['owns_position']:
        buy = int(state.interface.cash / price) # calculate number of shares
        state.interface.market_order(symbol, side='buy', size=buy)
        state.variables['owns_position'] = True

Now that you have a sense of how Blankly functions, we can go into it a bit deeper and code a pairs trading strategy.

How to create orders with Blankly?

To create orders with Blankly, we can utilise the state to execute orders such as the market, limit, and stop orders. An example of a Blankly order is as follows:

order = interface.market_order('BTC-USD', 'buy', 10)

How to create a pairs trading strategy in Blankly?

To create a pairs trading strategy in Blankly, we will need to build out the main components of the trading strategy that we’ll be using.

In this case, it is a pairs trade strategy that bets that if the two assets diverge – they will likely converge again. As you can see, this strategy relies on a couple of prerequisites such as cointegration between the two assets.

To learn more about pairs trading, read our blog here and if you want to find out how you can obtain plausible pairs with machine learning, check out this blog post.

The assets that we will use for this example are Box (BOX) and Dropbox (DBX) as we already have a sense of their cointegrated nature based on our previous articles and analyses. For this example, we will hold at maximum only one position (1 short and 1 long) at a time and deploy 100% of our cash.

We will enter a position if one stock has moved 5% or more than the other one over the course of the last five days. We will short the top one and long the bottom one until it reverses.

Let’s build out the initializer while thinking about what the primary state variables of our strategy will be.

How to initialise a Blankly strategy?

To initialize a Blankly strategy, we will build out the Blankly initializer with a state that will support the execution of our strategy.

Our state will need to keep track of the following parts:

  1. The close price history of BOX
  2. The close price history of DBX
  3. Percentage move
  4. Position status
  5. Trade status (did we long BOX or DBX)

We will cap the close histories for both prices to hold a maximum of 5 candles as we don’t need more than that. We will also be using the Futures version of the Blankly strategy state as we will be longing and shorting the stocks.

Let’s code out the state that we want to be initialised:

import blankly

def init(symbol, state: blankly.FuturesStrategyState):
    state.variables["dbx_history"] = state.interface.history(
        "BOX", to=5, return_as="deque"
    )["close"]
    state.variables["box_history"] = state.interface.history(
        "DBX", to=5, return_as="deque"
    )["close"]

    state.variables["dbx_change"] = 0
    state.variables["box_change"] = 0

    state.variables["in_position"] = False
    state.variables["dbx_long"] = False
    state.variables["box_long"] = False

    state.variables["dbx_size"] = 0
    state.variables["box_size"] = 0

The next step is to create a price_event that will be intaking the asset data. The issue here, as with most backtesting libraries, is that the initialised state is often localised to a single asset.

In other words, if we passed the price_event in the usually way of attaching each stock separately, we would have issues with:

  • Synchronising the dequeues
  • Sharing a state (would need to build custom solutions or use global variables)
  • Executing orders properly

Thankfully, Blankly offers an arbitrage event which is a wrapper for the usual price_event.

Visit AlgoTrading101 to learn about the Blankly arbitrage event.

Join The Conversation

If you have a general question, it may already be covered in our FAQs. If you have an account-specific question or concern, please reach out to Client Services.

Leave a Reply

Your email address will not be published. Required fields are marked *

Disclosure: Interactive Brokers

Information posted on IBKR Campus that is provided by third-parties does NOT constitute a recommendation that you should contract for the services of that third party. Third-party participants who contribute to IBKR Campus are independent of Interactive Brokers and Interactive Brokers does not make any representations or warranties concerning the services offered, their past or future performance, or the accuracy of the information provided by the third party. Past performance is no guarantee of future results.

This material is from AlgoTrading101 and is being posted with its permission. The views expressed in this material are solely those of the author and/or AlgoTrading101 and Interactive Brokers is not endorsing or recommending any investment or trading discussed in the material. This material is not and should not be construed as an offer to buy or sell any security. It should not be construed as research or investment advice or a recommendation to buy, sell or hold any security or commodity. This material does not and is not intended to take into account the particular financial conditions, investment objectives or requirements of individual customers. Before acting on this material, you should consider whether it is suitable for your particular circumstances and, as necessary, seek professional advice.

Disclosure: Futures Trading

Futures are not suitable for all investors. The amount you may lose may be greater than your initial investment. Before trading futures, please read the CFTC Risk Disclosure. A copy and additional information are available at ibkr.com.

Disclosure: Forex

There is a substantial risk of loss in foreign exchange trading. The settlement date of foreign exchange trades can vary due to time zone differences and bank holidays. When trading across foreign exchange markets, this may necessitate borrowing funds to settle foreign exchange trades. The interest rate on borrowed funds must be considered when computing the cost of trades across multiple markets.

Disclosure: Digital Assets

Trading in digital assets, including cryptocurrencies, is especially risky and is only for individuals with a high risk tolerance and the financial ability to sustain losses. Eligibility to trade in digital asset products may vary based on jurisdiction.

IBKR Campus Newsletters

This website uses cookies to collect usage information in order to offer a better browsing experience. By browsing this site or by clicking on the "ACCEPT COOKIES" button you accept our Cookie Policy.