{"id":21117,"date":"2023-04-20T19:52:28","date_gmt":"2023-04-20T19:52:28","guid":{"rendered":"https:\/\/algotrading101.com\/learn\/?p=21117"},"modified":"2024-03-24T16:05:55","modified_gmt":"2024-03-24T16:05:55","slug":"blankly-backtesting-guide","status":"publish","type":"post","link":"https:\/\/algotrading101.com\/learn\/blankly-backtesting-guide\/","title":{"rendered":"Blankly &#8211; Python Backtesting Guide"},"content":{"rendered":"\n<figure class=\"wp-block-image size-full\"><img fetchpriority=\"high\" decoding=\"async\" width=\"807\" height=\"221\" src=\"https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/Blankly-logo.webp\" alt=\"\" class=\"wp-image-21254\" srcset=\"https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/Blankly-logo.webp 807w, https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/Blankly-logo-300x82.webp 300w, https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/Blankly-logo-768x210.webp 768w\" sizes=\"(max-width: 807px) 100vw, 807px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Table of contents:<\/h3>\n\n\n\n<ol>\n<li><a href=\"#what-is-blankly\">What is Blankly?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-use\">What is Blankly used for?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-pros\">Why should I use Blankly?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-cons\">Why shouldn\u2019t I use Blankly?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-free\">Is Blankly free?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-alternatives\">What are some Blankly alternatives?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-exchanges-and-brokers\">What exchanges and brokers does Blankly support?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-work\">How does Blankly work?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-start\">How to get started with Blankly?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-API-keys\">How to set API keys in Blankly?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-components\">What are the components of Blankly\u2019s strategies?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-orders\">How to create orders with Blankly?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-pairs-trading\">How to create a pairs trading strategy in Blankly?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-initialise\">How to initialise a Blankly strategy?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-arbitrage-event\">What is the Blankly arbitrage event?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-price-event\">How to create price events with Blankly?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-backtest\">How to backtest your trading strategy with Blankly?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-backtest-results\">How to obtain backtesting metrics with Blankly?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-custom-data\">How to create a custom data source with Blankly?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-technical-indicators\">How to use technical indicators with Blankly?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-learn-more\">Where can I learn more about Blankly?<\/a><\/li>\n\n\n\n<li><a href=\"#blankly-full-code\">Full code<\/a><\/li>\n<\/ol>\n\n\n\n<a name=\"what-is-blankly\">\n\n\n\n<h2 class=\"wp-block-heading\">What is Blankly?<\/h2>\n\n\n\n<p>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.<\/p>\n\n\n\n<p>Link: <a href=\"https:\/\/github.com\/blankly-finance\/blankly\">https:\/\/github.com\/blankly-finance\/blankly<\/a><\/p>\n\n\n\n<a name=\"blankly-use\">\n\n\n\n<h2 class=\"wp-block-heading\">What is Blankly used for?<\/h2>\n\n\n\n<p>Blankly is mainly used by algorithmic traders for backtesting and running trading strategies in Python. <\/p>\n\n\n\n<a name=\"blankly-pros\">\n\n\n\n<h2 class=\"wp-block-heading\">Why should I use Blankly?<\/h2>\n\n\n\n<ul>\n<li>Blankly is completely free.<\/li>\n\n\n\n<li>Blankly is easy to use and beginner-friendly.<\/li>\n\n\n\n<li>Is precise.<\/li>\n\n\n\n<li>Has built-in support for exchanges such as Binance, Coinbase, and Alpaca.<\/li>\n\n\n\n<li>Allows for backtesting using alternative data<\/li>\n\n\n\n<li>Is maintained.<\/li>\n<\/ul>\n\n\n\n<a name=\"blankly-cons\">\n\n\n\n<h2 class=\"wp-block-heading\">Why shouldn&#8217;t I use Blankly?<\/h2>\n\n\n\n<ul>\n<li>Doesn&#8217;t have advanced orders (OCO, straddle, etc.)<\/li>\n\n\n\n<li>Doesn&#8217;t have advanced position handling.<\/li>\n\n\n\n<li>Should have more features.<\/li>\n\n\n\n<li>Some parts of the website and documentation are outdated.<\/li>\n\n\n\n<li>It seems that the development has slowed down.<\/li>\n<\/ul>\n\n\n\n<a name=\"blankly-free\">\n\n\n\n<h2 class=\"wp-block-heading\">Is Blankly free?<\/h2>\n\n\n\n<p>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.<\/p>\n\n\n\n<a name=\"blankly-alternatives\">\n\n\n\n<h2 class=\"wp-block-heading\">What are some Blankly alternatives?<\/h2>\n\n\n\n<p>Blankly can be replaced with other software that can be more suitable for your needs. Here are some of them:<\/p>\n\n\n\n<ul>\n<li><a href=\"https:\/\/algotrading101.com\/learn\/quantconnect-guide\/\">QuantConnect<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/algotrading101.com\/learn\/pine-script-tradingview-guide\/\">TradingView (Pine Script)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/algotrading101.com\/learn\/vectorbt-guide\/\">VectorBT<\/a><\/li>\n\n\n\n<li>Trality<\/li>\n\n\n\n<li>QuantRocket<\/li>\n\n\n\n<li>cTrader<\/li>\n<\/ul>\n\n\n\n<a name=\"blankly-exchanges-and-brokers\">\n\n\n\n<h2 class=\"wp-block-heading\">What exchanges and brokers does Blankly support?<\/h2>\n\n\n\n<p>Blankly supports the following exchanges and brokers at the time of writing this article:<\/p>\n\n\n\n<ul>\n<li><a href=\"https:\/\/algotrading101.com\/learn\/alpaca-trading-review\/\">Alpaca<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/algotrading101.com\/learn\/binance-python-api-guide\/\">Binance<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/algotrading101.com\/learn\/coinbase-pro-api-guide\/\">Coinbase Pro<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/algotrading101.com\/learn\/kucoin-api-guide\/\">Kucoin<\/a><\/li>\n\n\n\n<li>Oanda<\/li>\n<\/ul>\n\n\n\n<a name=\"blankly-work\">\n\n\n\n<h2 class=\"wp-block-heading\">How does Blankly work?<\/h2>\n\n\n\n<p>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.<\/p>\n\n\n\n<p>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:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"657\" src=\"https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/blankly-strategy-1024x657.webp\" alt=\"\" class=\"wp-image-21260\" srcset=\"https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/blankly-strategy-1024x657.webp 1024w, https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/blankly-strategy-300x193.webp 300w, https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/blankly-strategy-768x493.webp 768w, https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/blankly-strategy-1536x986.webp 1536w, https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/blankly-strategy-2048x1315.webp 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<a name=\"blankly-start\">\n\n\n\n<h2 class=\"wp-block-heading\">How to get started with Blankly?<\/h2>\n\n\n\n<p>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:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>pip install blankly\nmkdir blankly-project\ncd blankly-project\nblankly init<\/code><\/pre><\/div>\n\n\n\n<p>Now, you will be presented with different options that will help you customize blankly to fit your project&#8217;s needs. It will start you off with a question asking you to choose an exchange to connect to or go without one.<\/p>\n\n\n\n<p>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&#8217;ll go with <a href=\"https:\/\/algotrading101.com\/learn\/alpaca-trading-review\/\">Alpaca<\/a>. <\/p>\n\n\n\n<p>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&#8217;t need a template, but feel free to select that option if you want to inspect it.<\/p>\n\n\n\n<p>Finally, you can choose to add your API key for Alpaca now or later. I&#8217;ll choose later as I want to show you how you can do this by changing a file.<\/p>\n\n\n\n<a name=\"blankly-API-keys\">\n\n\n\n<h2 class=\"wp-block-heading\">How to set API keys in Blankly?<\/h2>\n\n\n\n<p>To set API keys in Blankly, you can navigate to the <code>keys.json<\/code> 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 <code>sandbox<\/code> parameter which can be set to True to use a paper trading account.<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-json\" data-lang=\"JSON\"><code>{\n    &quot;alpaca&quot;: {\n        &quot;example-portfolio&quot;: {\n            &quot;API_KEY&quot;: &quot;********************&quot;,\n            &quot;API_SECRET&quot;: &quot;********************&quot;,\n            &quot;sandbox&quot;: false\n        }\n    },<\/code><\/pre><\/div>\n\n\n\n<p>Above, I&#8217;ll place my Alpaca API keys and set the sandbox parameter to <code>true<\/code>.<\/p>\n\n\n\n<a name=\"blankly-components\">\n\n\n\n<h2 class=\"wp-block-heading\">What are the components of Blankly\u2019s strategies?<\/h2>\n\n\n\n<p>The main two components of Blankly&#8217;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.<\/p>\n\n\n\n<p>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.<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>def init(symbol, state: blankly.StrategyState):\n    # This gets the past 150 data points as a deque to reduce memory usage\n    state.variables[&#39;history&#39;] = state.interface.history(\n        symbol, to=150, return_as=&#39;deque&#39;\n    )[&#39;close&#39;]\n    state.variables[&#39;owns_position&#39;] = False<\/code><\/pre><\/div>\n\n\n\n<p>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 <code>True<\/code> if the RSI is oversold:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>def price_event(price, symbol, state: StrategyState):\n    state.variables[&#39;history&#39;].append(price)\n    rsi = blankly.indicators.rsi(state.variables[&#39;history&#39;])\n    if rsi[-1] &lt; 30 and not state.variables[&#39;owns_position&#39;]:\n        buy = int(state.interface.cash \/ price) # calculate number of shares\n        state.interface.market_order(symbol, side=&#39;buy&#39;, size=buy)\n        state.variables[&#39;owns_position&#39;] = True<\/code><\/pre><\/div>\n\n\n\n<p>Now that you have a sense of how Blankly functions, we can go into it a bit deeper and code a pairs trading strategy.<\/p>\n\n\n\n<a name=\"blankly-orders\">\n\n\n\n<h2 class=\"wp-block-heading\">How to create orders with Blankly?<\/h2>\n\n\n\n<p>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:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code>order = interface.market_order(&#39;BTC-USD&#39;, &#39;buy&#39;, 10)<\/code><\/pre><\/div>\n\n\n\n<a name=\"blankly-pairs-trading\">\n\n\n\n<h2 class=\"wp-block-heading\">How to create a pairs trading strategy in Blankly?<\/h2>\n\n\n\n<p>To create a pairs trading strategy in Blankly, we will need to build out the main components of the trading strategy that we&#8217;ll be using. <\/p>\n\n\n\n<p>In this case, it is a pairs trade strategy that bets that if the two assets diverge &#8211; they will likely converge again. As you can see, this strategy relies on a couple of prerequisites such as cointegration between the two assets.<\/p>\n\n\n\n<p>To learn more about pairs trading, read our blog <a href=\"https:\/\/algotrading101.com\/learn\/pairs-trading-guide\/\">here<\/a> and if you want to find out how you can obtain plausible pairs with machine learning, check out this <a href=\"https:\/\/algotrading101.com\/learn\/cluster-analysis-guide\/\">blog post<\/a>. <\/p>\n\n\n\n<p>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.<\/p>\n\n\n\n<p>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.<\/p>\n\n\n\n<p>Let&#8217;s build out the initializer while thinking about what the primary state variables of our strategy will be.<\/p>\n\n\n\n<a name=\"blankly-initialise\">\n\n\n\n<h2 class=\"wp-block-heading\">How to initialise a Blankly strategy?<\/h2>\n\n\n\n<p>To initialize a Blankly strategy, we will build out the Blankly initializer with a state that will support the execution of our strategy.<\/p>\n\n\n\n<p>Our state will need to keep track of the following parts:<\/p>\n\n\n\n<ol>\n<li>The close price history of BOX<\/li>\n\n\n\n<li>The close price history of DBX<\/li>\n\n\n\n<li>Percentage move<\/li>\n\n\n\n<li>Position status<\/li>\n\n\n\n<li>Trade status (did we long BOX or DBX)<\/li>\n<\/ol>\n\n\n\n<p>We will cap the close histories for both prices to hold a maximum of 5 candles as we don&#8217;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.<\/p>\n\n\n\n<p>Let&#8217;s code out the state that we want to be initialised:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code>import blankly\n\ndef init(symbol, state: blankly.FuturesStrategyState):\n    state.variables[&quot;dbx_history&quot;] = state.interface.history(\n        &quot;BOX&quot;, to=5, return_as=&quot;deque&quot;\n    )[&quot;close&quot;]\n    state.variables[&quot;box_history&quot;] = state.interface.history(\n        &quot;DBX&quot;, to=5, return_as=&quot;deque&quot;\n    )[&quot;close&quot;]\n\n    state.variables[&quot;dbx_change&quot;] = 0\n    state.variables[&quot;box_change&quot;] = 0\n\n    state.variables[&quot;in_position&quot;] = False\n    state.variables[&quot;dbx_long&quot;] = False\n    state.variables[&quot;box_long&quot;] = False\n\n    state.variables[&quot;dbx_size&quot;] = 0\n    state.variables[&quot;box_size&quot;] = 0<\/code><\/pre><\/div>\n\n\n\n<p>The next step is to create a <code>price_event<\/code> 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. <\/p>\n\n\n\n<p>In other words, if we passed the price_event in the usually way of attaching each stock separately, we would have issues with:<\/p>\n\n\n\n<ul>\n<li>Synchronising the dequeues<\/li>\n\n\n\n<li>Sharing a state (would need to build custom solutions or use global variables)<\/li>\n\n\n\n<li>Executing orders properly<\/li>\n<\/ul>\n\n\n\n<p>Thankfully, Blankly offers an arbitrage event which is a wrapper for the usual <code>price_event<\/code>.<\/p>\n\n\n\n<a name=\"blankly-arbitrage-event\">\n\n\n\n<h2 class=\"wp-block-heading\">What is the Blankly arbitrage event?<\/h2>\n\n\n\n<p>The Blankly arbitrage event is a wrapper that intakes a price event, resolution of asset data, the initialisation state, and multiple assets to obtain the data for. An example of this event is the following:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code>add_arbitrage_event(\n    callback: typing.Callable,\n    symbols: list, \n    resolution: typing.Union[str, float],\n    init: typing.Callable = None,\n    **kwargs\n)<\/code><\/pre><\/div>\n\n\n\n<p>Now that we know that this is the solution to a shared-state issue, we can move on to creating our price event that will be passed as the callback parameter to the arbitrage event.<\/p>\n\n\n\n<a name=\"blankly-price-event\">\n\n\n\n<h2 class=\"wp-block-heading\">How to create price events with Blankly?<\/h2>\n\n\n\n<p>To create price events with Blankly, all you need to do is to pass the price data, symbol and the state that the price event will be using. In our pairs trade example, we expect both symbols being passed at the same time to the price event.<\/p>\n\n\n\n<p>Now, let&#8217;s set up the logic of the trade inside our price event. We will collect the data and check if the dequeue has been filled with 5 days worth of price data. If it has, we can calculate the percentage moves for each stock over the last 5 days.<\/p>\n\n\n\n<p>Then, we can compare the difference between the two percentage moves. If the difference is more than 5% we will short the over-performing stock and long the under-performing one, and vice-versa. <\/p>\n\n\n\n<p>We will only stay in one position at a time and will deploy our whole trading portfolio by shorting and longing each asset using 50% of the portfolio.<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code>def price_event(price, symbol, state: blankly.FuturesStrategyState):\n    # Add the new price to the history\n    state.variables[&quot;dbx_history&quot;].append(price[&quot;DBX&quot;])\n    state.variables[&quot;box_history&quot;].append(price[&quot;BOX&quot;])\n\n    # Check if we have enough data to calculate the percentage change\n    if (\n        len(state.variables[&quot;dbx_history&quot;]) == 5\n        and len(state.variables[&quot;box_history&quot;]) == 5\n    ):\n        # Calculate the percentage change\n        state.variables[&quot;dbx_change&quot;] = (\n            state.variables[&quot;dbx_history&quot;][-1] - state.variables[&quot;dbx_history&quot;][0]\n        ) \/ state.variables[&quot;dbx_history&quot;][0]\n        state.variables[&quot;box_change&quot;] = (\n            state.variables[&quot;box_history&quot;][-1] - state.variables[&quot;box_history&quot;][0]\n        ) \/ state.variables[&quot;box_history&quot;][0]\n\n        # calculate the difference between the two stocks\n        diff = state.variables[&quot;dbx_change&quot;] - state.variables[&quot;box_change&quot;]\n\n        # If the difference is greater than 5% we will enter a position\n        # We long the stock that is underperforming and short the stock that is overperforming\n        if diff &gt; 0.05 and not state.variables[&quot;in_position&quot;]:\n            # Calculate the size of the position\n            # We allocate 40% of our portfolio to each position\n            cash = state.interface.cash\n            state.variables[&quot;dbx_size&quot;] = blankly.trunc(\n                (cash * 0.5 \/ state.interface.get_price(&quot;DBX&quot;)), 2\n            )\n            state.variables[&quot;box_size&quot;] = blankly.trunc(\n                (cash * 0.5 \/ state.interface.get_price(&quot;BOX&quot;)), 2\n            )\n\n            # Long BOX and short DBX\n            try:\n                state.interface.market_order(\n                    &quot;DBX&quot;, side=&quot;sell&quot;, size=state.variables[&quot;dbx_size&quot;]\n                )\n                state.interface.market_order(\n                    &quot;BOX&quot;, side=&quot;buy&quot;, size=state.variables[&quot;box_size&quot;]\n                )\n            except Exception as e:\n                print(e)\n                return\n\n            state.variables[&quot;in_position&quot;] = True\n            state.variables[&quot;dbx_long&quot;] = False\n            state.variables[&quot;box_long&quot;] = True\n\n        # If the difference is less than -5% we will enter a position\n        # We long the stock that is underperforming and short the stock that is overperforming\n        elif diff &lt; -0.05 and not state.variables[&quot;in_position&quot;]:\n            # We allocate 50% of our portfolio to each position\n            cash = state.interface.cash\n            state.variables[&quot;dbx_size&quot;] = blankly.trunc(\n                (cash * 0.5 \/ state.interface.get_price(&quot;DBX&quot;)), 2\n            )\n            state.variables[&quot;box_size&quot;] = blankly.trunc(\n                (cash * 0.5 \/ state.interface.get_price(&quot;BOX&quot;)), 2\n            )\n\n            # Short BOX and long DBX\n            try:\n                state.interface.market_order(\n                    &quot;BOX&quot;, side=&quot;sell&quot;, size=state.variables[&quot;box_size&quot;]\n                )\n                state.interface.market_order(\n                    &quot;DBX&quot;, side=&quot;buy&quot;, size=state.variables[&quot;dbx_size&quot;]\n                )\n            except Exception as e:\n                print(e)\n                return\n\n            state.variables[&quot;in_position&quot;] = True\n            state.variables[&quot;dbx_long&quot;] = True\n            state.variables[&quot;box_long&quot;] = False<\/code><\/pre><\/div>\n\n\n\n<p>Now that we have the logic in place when it comes to entering a trade, we can create logic when the tables turn and we want to reverse our position:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code>        # If the position has reversed we reverse our position\n        elif diff &gt; 0.05 and state.variables[&quot;dbx_long&quot;]:\n            # Short DBX and long BOX\n            try:\n                state.interface.market_order(\n                    &quot;DBX&quot;, side=&quot;sell&quot;, size=state.variables[&quot;dbx_size&quot;]\n                )\n                state.interface.market_order(\n                    &quot;BOX&quot;, side=&quot;buy&quot;, size=state.variables[&quot;box_size&quot;]\n                )\n            except Exception as e:\n                print(e)\n                return\n\n            state.variables[&quot;in_position&quot;] = False\n            state.variables[&quot;dbx_long&quot;] = False\n            state.variables[&quot;box_long&quot;] = False\n\n        elif diff &lt; -0.05 and state.variables[&quot;box_long&quot;]:\n            # Long DBX and short BOX\n            try:\n                state.interface.market_order(\n                    &quot;BOX&quot;, side=&quot;sell&quot;, size=state.variables[&quot;box_size&quot;]\n                )\n                state.interface.market_order(\n                    &quot;DBX&quot;, side=&quot;buy&quot;, size=state.variables[&quot;dbx_size&quot;]\n                )\n            except Exception as e:\n                print(e)\n                return\n\n            state.variables[&quot;in_position&quot;] = False\n            state.variables[&quot;dbx_long&quot;] = False\n            state.variables[&quot;box_long&quot;] = False<\/code><\/pre><\/div>\n\n\n\n<p>Notice how I placed the trades inside a try and except block, this is done as I&#8217;d like to know if something is off with particular trades during backtesting. Now that we have the execution logic in place, we can move onto backtesting our strategy.<\/p>\n\n\n\n<a name=\"blankly-backtest\">\n\n\n\n<h2 class=\"wp-block-heading\">How to backtest your trading strategy with Blankly?<\/h2>\n\n\n\n<p>To backtest your trading strategy with Blankly, you will need to initialise the exchange object, the type of strategy that you want to use, and pass the events to the strategy with adequate parameters. After that, you can run the backtest.<\/p>\n\n\n\n<p>For our example, we&#8217;ll be using the Alpaca exchange and the Futures strategy type. When it comes to the price data and events, we will wrap the price event into an arbitrage event and pass both stocks while requesting daly stock data. <\/p>\n\n\n\n<p>Finally, we will issue a backtest on the past 3 years of data:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code>if __name__ == &quot;__main__&quot;:\n    exchange = blankly.Alpaca()\n    strategy = blankly.FuturesStrategy(exchange)\n\n    strategy.add_arbitrage_event(\n        price_event, symbols=[&quot;BOX&quot;, &quot;DBX&quot;], resolution=&quot;1d&quot;, init=init\n    )\n    backtest_result = strategy.backtest(initial_values={&quot;USD&quot;: 10000}, to=&quot;3y&quot;)\n    print(backtest_result)<\/code><\/pre><\/div>\n\n\n\n<a name=\"blankly-backtest-results\">\n\n\n\n<h2 class=\"wp-block-heading\">How to obtain backtesting metrics with Blankly?<\/h2>\n\n\n\n<p>To obtain backtesting metrics with Blankly, all you need to do is to print the contents of the backtesting result. For example, these are the metrics of our pairs trading strategy:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code>Blankly Metrics: \nCalmar Ratio:                      0.14\nCompound Annual Growth Rate (%):   4.0%\nConditional Value-at-Risk:         11.21\nCumulative Returns (%):            14.000000000000002%\nMax Drawdown (%):                  28.999999999999996%\nResampled Time:                    86400.0\nRisk Free Return Rate:             0.0\nSharpe Ratio:                      0.27\nSortino Ratio:                     0.3\nValue-at-Risk:                     141.2\nVariance (%):                      2.4%\nVolatility:                        0.15<\/code><\/pre><\/div>\n\n\n\n<p>Another thing that Blankly does is that it generates an HTML Bokeh report of the strategy that you can observe. Here is how ours looks like:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"984\" src=\"https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/Screenshot-2023-04-09-at-12.57.41-1024x984.webp\" alt=\"\" class=\"wp-image-21241\" srcset=\"https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/Screenshot-2023-04-09-at-12.57.41-1024x984.webp 1024w, https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/Screenshot-2023-04-09-at-12.57.41-300x288.webp 300w, https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/Screenshot-2023-04-09-at-12.57.41-768x738.webp 768w, https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/Screenshot-2023-04-09-at-12.57.41-1536x1476.webp 1536w, https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/Screenshot-2023-04-09-at-12.57.41.webp 1952w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<a name=\"blankly-custom-data\">\n\n\n\n<h2 class=\"wp-block-heading\">How to create a custom data source with Blankly?<\/h2>\n\n\n\n<p>Instead of only downloading data from your exchange, you can use any custom data that contains the columns&nbsp;<code>open<\/code>,&nbsp;<code>high<\/code>,&nbsp;<code>low<\/code>,&nbsp;<code>close<\/code>&nbsp;<code>volume<\/code>&nbsp;and&nbsp;<code>time<\/code>. Then, all you need to do is to pass the data to the Blankly reader:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code>if __name__ == &quot;__main__&quot;:\n    # Use this\n      exchange = blankly.Alpaca()\n    model = Strategy(exchange)\n    reader = blankly.data.PriceReader(&#39;.\/custom.csv&#39;, &#39;CUSTOM-USD&#39;)\n    model.backtester.add_custom_prices(reader)\n    \n    model.backtest(args={}, initial_values={\n      &quot;USD&quot;: 10000\n    })<\/code><\/pre><\/div>\n\n\n\n<p>In addition to custom data sources, Blanky allows you to backtest using alternative data such as tweets. See more <a href=\"https:\/\/docs.blankly.finance\/examples\/model-framework\/\">here<\/a>.<\/p>\n\n\n\n<a name=\"blankly-technical-indicators\">\n\n\n\n<h2 class=\"wp-block-heading\">How to use technical indicators with Blankly?<\/h2>\n\n\n\n<p>To use technical indicators with Blankly, you have three approaches. The first one is to utilise their built-in indicators, the second approach is to use another library to calculate them, and the third option is to build you own.<\/p>\n\n\n\n<p>Here is an example of how one can use a built-in Blankly indicator:<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-python\" data-lang=\"Python\"><code>rsi = blankly.indicators.rsi(state.variables[&#39;history&#39;])<\/code><\/pre><\/div>\n\n\n\n<p>You can read more about the implemented indicators <a href=\"https:\/\/docs.blankly.finance\/metrics\/indicators\">here<\/a>.<\/p>\n\n\n\n<a name=\"blankly-learn-more\">\n\n\n\n<h2 class=\"wp-block-heading\">Where can I learn more about Blankly?<\/h2>\n\n\n\n<p>You can learn more about blankly by accessing their <a href=\"https:\/\/github.com\/blankly-finance\/blankly\">GitHub repo<\/a> and checking out their <a href=\"https:\/\/docs.blankly.finance\">documentation<\/a>.<\/p>\n\n\n\n<a name=\"blankly-full-code\">\n\n\n\n<h2 class=\"wp-block-heading\">Full code<\/h2>\n\n\n\n<p><a href=\"https:\/\/github.com\/AlgoTrading101\/Blankly-AlgoTrading101\">GitHub repository<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>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.<\/p>\n","protected":false},"author":14,"featured_media":22506,"comment_status":"closed","ping_status":"closed","sticky":true,"template":"","format":"standard","meta":{"_lmt_disableupdate":"no","_lmt_disable":"","_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[3,2],"tags":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v20.7 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Blankly - Python Backtesting Guide - AlgoTrading101 Blog<\/title>\n<meta name=\"description\" content=\"Blankly is an open-source Python backtester that allows algorithmic traders to build, backtest, and run their trading algorithms.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/algotrading101.com\/learn\/blankly-backtesting-guide\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Blankly - Python Backtesting Guide - AlgoTrading101 Blog\" \/>\n<meta property=\"og:description\" content=\"Blankly is an open-source Python backtester that allows algorithmic traders to build, backtest, and run their trading algorithms.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/algotrading101.com\/learn\/blankly-backtesting-guide\/\" \/>\n<meta property=\"og:site_name\" content=\"Quantitative Trading Ideas and Guides - AlgoTrading101 Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-04-20T19:52:28+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-03-24T16:05:55+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/blanky.png\" \/>\n\t<meta property=\"og:image:width\" content=\"594\" \/>\n\t<meta property=\"og:image:height\" content=\"408\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Igor Radovanovic\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Igor Radovanovic\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Blankly - Python Backtesting Guide - AlgoTrading101 Blog","description":"Blankly is an open-source Python backtester that allows algorithmic traders to build, backtest, and run their trading algorithms.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/algotrading101.com\/learn\/blankly-backtesting-guide\/","og_locale":"en_US","og_type":"article","og_title":"Blankly - Python Backtesting Guide - AlgoTrading101 Blog","og_description":"Blankly is an open-source Python backtester that allows algorithmic traders to build, backtest, and run their trading algorithms.","og_url":"https:\/\/algotrading101.com\/learn\/blankly-backtesting-guide\/","og_site_name":"Quantitative Trading Ideas and Guides - AlgoTrading101 Blog","article_published_time":"2023-04-20T19:52:28+00:00","article_modified_time":"2024-03-24T16:05:55+00:00","og_image":[{"width":594,"height":408,"url":"http:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2023\/04\/blanky.png","type":"image\/png"}],"author":"Igor Radovanovic","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Igor Radovanovic","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/algotrading101.com\/learn\/blankly-backtesting-guide\/#article","isPartOf":{"@id":"https:\/\/algotrading101.com\/learn\/blankly-backtesting-guide\/"},"author":{"name":"Igor Radovanovic","@id":"https:\/\/algotrading101.com\/learn\/#\/schema\/person\/a7ae60c112a73b7c3fe14ac56726a0ae"},"headline":"Blankly &#8211; Python Backtesting Guide","datePublished":"2023-04-20T19:52:28+00:00","dateModified":"2024-03-24T16:05:55+00:00","mainEntityOfPage":{"@id":"https:\/\/algotrading101.com\/learn\/blankly-backtesting-guide\/"},"wordCount":1959,"publisher":{"@id":"https:\/\/algotrading101.com\/learn\/#organization"},"articleSection":["Programming","Trading"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/algotrading101.com\/learn\/blankly-backtesting-guide\/","url":"https:\/\/algotrading101.com\/learn\/blankly-backtesting-guide\/","name":"Blankly - Python Backtesting Guide - AlgoTrading101 Blog","isPartOf":{"@id":"https:\/\/algotrading101.com\/learn\/#website"},"datePublished":"2023-04-20T19:52:28+00:00","dateModified":"2024-03-24T16:05:55+00:00","description":"Blankly is an open-source Python backtester that allows algorithmic traders to build, backtest, and run their trading algorithms.","breadcrumb":{"@id":"https:\/\/algotrading101.com\/learn\/blankly-backtesting-guide\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/algotrading101.com\/learn\/blankly-backtesting-guide\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/algotrading101.com\/learn\/blankly-backtesting-guide\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/algotrading101.com\/learn\/"},{"@type":"ListItem","position":2,"name":"Blankly &#8211; Python Backtesting Guide"}]},{"@type":"WebSite","@id":"https:\/\/algotrading101.com\/learn\/#website","url":"https:\/\/algotrading101.com\/learn\/","name":"Quantitative Trading Ideas and Guides - AlgoTrading101 Blog","description":"Authentic Stories about Algorithmic trading, coding and life.","publisher":{"@id":"https:\/\/algotrading101.com\/learn\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/algotrading101.com\/learn\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/algotrading101.com\/learn\/#organization","name":"AlgoTrading101","url":"https:\/\/algotrading101.com\/learn\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/algotrading101.com\/learn\/#\/schema\/logo\/image\/","url":"https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2020\/11\/AlgoTrading101-Lucas-Liew.jpg","contentUrl":"https:\/\/algotrading101.com\/learn\/wp-content\/uploads\/2020\/11\/AlgoTrading101-Lucas-Liew.jpg","width":1200,"height":627,"caption":"AlgoTrading101"},"image":{"@id":"https:\/\/algotrading101.com\/learn\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/algotrading101.com\/learn\/#\/schema\/person\/a7ae60c112a73b7c3fe14ac56726a0ae","name":"Igor Radovanovic","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/algotrading101.com\/learn\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d46175c509b3ee240a1e2bbe735a4d1e?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d46175c509b3ee240a1e2bbe735a4d1e?s=96&d=mm&r=g","caption":"Igor Radovanovic"},"sameAs":["https:\/\/igorradovanovic.com","https:\/\/www.linkedin.com\/in\/igor-radovanovic-profile"],"url":"https:\/\/algotrading101.com\/learn\/author\/igor\/"}]}},"modified_by":"Lucas Liew","_links":{"self":[{"href":"https:\/\/algotrading101.com\/learn\/wp-json\/wp\/v2\/posts\/21117"}],"collection":[{"href":"https:\/\/algotrading101.com\/learn\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/algotrading101.com\/learn\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/algotrading101.com\/learn\/wp-json\/wp\/v2\/users\/14"}],"replies":[{"embeddable":true,"href":"https:\/\/algotrading101.com\/learn\/wp-json\/wp\/v2\/comments?post=21117"}],"version-history":[{"count":55,"href":"https:\/\/algotrading101.com\/learn\/wp-json\/wp\/v2\/posts\/21117\/revisions"}],"predecessor-version":[{"id":22636,"href":"https:\/\/algotrading101.com\/learn\/wp-json\/wp\/v2\/posts\/21117\/revisions\/22636"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/algotrading101.com\/learn\/wp-json\/wp\/v2\/media\/22506"}],"wp:attachment":[{"href":"https:\/\/algotrading101.com\/learn\/wp-json\/wp\/v2\/media?parent=21117"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/algotrading101.com\/learn\/wp-json\/wp\/v2\/categories?post=21117"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/algotrading101.com\/learn\/wp-json\/wp\/v2\/tags?post=21117"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}