Transcript for:
Basic Trading Algorithm Guide

Welcome back to the fourth video of this algorithmic trading series using Python and the QuantConnect platform. So far, we've mainly had theory-based videos covering some of the key concepts of the algorithmic trading development process and the QuantConnect API. In this video, we will change things up a little by writing the very first actual trading algorithm of this series.

While doing this, we will get to know the basics of the QuantConnect API in a practical manner. But before we head over to the QuantConnect platform, let me quickly thank the sponsor of this video series, namely QuantConnect themselves. I'm very glad that they offered to sponsor this series, since in my opinion that's an absolute win-win situation.

So once again, thanks for the support. That said, let me quickly outline the idea behind the trading bot that we will implement in this video. Firstly, I have to say that this will be a very basic trading bot that will hopefully show different aspects of the Quorn Connect API.

So please don't expect to develop a trading bot that you would actually want to fund with real money. The idea behind this trading algorithm will be to buy and hold SPY, which is the ETF tracking the S&P 500 index. To make things a little more interesting, we will want to close our position as soon as it loses more than 10% or achieves a profit greater than 10%.

After this, we stop investing for a predetermined period of time such as one month. Thereafter, we start this cycle all over again by investing in SPY again. With that out of the way, let's now head over to the QuantConnect platform. If you have not yet created a free QuantConnect account, there's a link in the description box below. Feel free to code along.

On the QuantConnect website, you will want to navigate to the Lab tab. There you will want to click on Create New Algorithm, which will bring you to the Strategy Builder view. The Strategy Builder can be very useful for more complex algorithms which is why we will explore this feature later in this series.

For now, we just want to start with a blank template algorithm by clicking on Exit Builder Mode. This will bring you to a simple algorithm template with an algorithm class as well as an initialize and onData method. The onData method is an event handler that is being called every time your algorithm receives new data. More specifically, it is called every time a tick event occurs or a bar reaches its end time.

For a more detailed breakdown of this, make sure to check out the previous video of this series. Before we start implementing the onData method, let's first take care of the initialize method which is unsurprisingly used to initialize your algorithm. First and foremost we will set the start and end date for bag testing.

If you don't specify an end date, the most recent date will be chosen. Next up, we want to specify the starting cash balance for your algo. Once again, this is just for bag testing purposes.

In real life trading, this number will be taken from your brokerage account balance. After that, we will add the data for the security that our algorithm will trade. Since we want it to trade SPY, we will use addEquity and the SPY ticker symbol.

As a second argument, the addEquity method takes a resolution. Here, the lowest resolution that you could specify is tick resolution which can be as low as a tick per millisecond. But note that such a low resolution will lead to a lot of data points which can be very hard to process efficiently. Furthermore, tick data is raw and unfiltered which can lead to some unwanted problems.

So if you aren't completely sure of what you're doing, I wouldn't recommend going lower than minute resolution for now. But to be honest, you won't really need that low of a resolution for most use cases anyway. For this algorithm we will just be using daily resolution. Since QuantConnect also supports a bunch of other asset classes besides equities, you can also add other securities. For instance, to add a forex pair you could use addForex, for futures you would use addFutures, for CFDs add CFDs and so on.

In the next step we will set the data normalization mode for our SPY equity data. QuantConnect supports four different data adjustment modes. The default data mode is split and dividend adjusted. This means that the data is smoothed out so that the stock splits don't look like sudden huge price drops or spikes. This can be very useful since it makes the data easier to handle.

Besides that, QuantConnect also supports raw data which isn't adjusted at all. Here dividends are paid in cash and the stock's price is not adjusted. Certain asset classes such as options only support raw data.

To change the data mode, you can use the setDataNormalizationMode method and specify the data normalization mode. There also exists only split adjusted data and total return data which automatically accounts for reinvested dividends. Note that most of the time you won't have to explicitly change the data normalization mode, since you can just use the default mode.

But nonetheless it is good to know how to do so. For this algorithm I will just use raw data so that you can see how to change the data adjustment mode. Next up, let's save the Symbol object of SPY into a new variable.

If you aren't sure what Symbol objects are or why they are important, I recommend watching the last video of this series. But in short, Symbol objects hold more information than tickers which can help against unwanted ambiguity when referencing a given security. After saving this Symbol object we can set a benchmark for this algo with self.setBenchmark.

Since we will be trading SPY we will just use SPY as the benchmark. Here you usually want to choose some relevant market index in the traded sector. Setting a benchmark will automatically generate a chart for the benchmark for the specified period when backtesting your algorithm.

This can help when analyzing the performance of your algo. QuantConnect also allows you to set different brokerage models so that your algorithm best possibly accounts for your broker's fee structure and account type. The default brokerage model usually is good enough, but you can also specify a brokerage model such as the Interactive brokerage model, Trade Year brokerage model, Wanda brokerage, XCM, Brokerage, Bitfinex and more. These different models will then adjust the fee structure to the chosen brokerage.

Besides choosing a brokerage model you can also set the account type to a cash or margin account. A cash account does not allow you to use leverage and it has a settlement period of 3 days for equities. Margin accounts however allow you to use leverage from 2 to 4 times your capital.

You can also use the patron day trading margin model to account for the PDT rule which allows you to pattern day trade only if you have more than $25,000 deposited in your account. For this algo, we will just be using the interactive brokerage model as well as a margin account. But note that this algorithm could also easily be used in a cash account. Usually in cash accounts, you just have to be careful to account for the T plus 3 settlement rules. Last but not least, we will quickly create three custom helper variables that we will need for this algorithm.

The first is self.entryPrice which will track the entry price of our SPY position. The second one is self.period which we will set to a timeframe of 31 days. And lastly self.nextEntryTime will track when we should re-enter our long SPY position. We will initialize this to the current time since we want to start investing right away. Now we are finally done with the initialized method which means we can move on to implementing the onData method.

Remember, This is called every time the end time of a bar is reached or a tick event occurs. So basically every time the algorithm receives new data while still accounting for lookahead bias. The onData method has one parameter which is called data. Let's quickly look at the theory behind this. The data parameter is a slice object that arrives at event handlers such as the onData method.

It provides you with a couple of helpers to access the data in a structured manner. The properties of a slice object include option chains, trade bars, quote bars, splits, among others. These are dictionaries that are indexed by symbols.

Let me now briefly break down the most important data type that you can access through such a slice object. All of these data types extend the base data class which provides you with symbol, value and time information. First and foremost you can access tick data. Tick data's most interesting property is the last price.

But note that, like I said earlier, tick data is raw and unfiltered and it might contain some bad ticks. So generally you should be cautious when using tick data. The next important type of data is trade bar data. Since this is bar data it covers a period of time and is passed on to the event handlers at its end time. Trade bars are supported for equities, options and futures.

It provides you with open, high, low, close and volume information. Trade bars are built by consolidating trades from exchanges. Note that we will go over accessing trade bars in a minute. I just first want to quickly cover the most commonly used data types on a conceptual level. Besides trade bars, there also are quote bars which are supported by all asset types, so also by forex, crypto and CFDs.

The main difference between quote bars and trade bars is that quote bars are built by consolidating bid and ask prices from exchanges. Quote bars offer open, high, low, close, bid, ask, last bid size and last ask size properties. Here the bid and ask properties are bar objects themselves which once again include open, high, low and close info.

The open, high, low and close properties of the quote bars are generated midpoints from the bid and ask bars. Other data types include delistings, symbol change events, splits and dividends, but since these aren't used as often I won't cover them in too much detail for now. Now that you hopefully have a much better picture of the theory behind the shape of the data parameter of the onData method, let's start implementing the onData method. Firstly, we will want to save the current price of SPY into a variable named price. There are multiple ways of accomplishing this.

The first would be to access the bars of the data slice object. Data.bars is a dictionary that we can index with self.SPY, which is the symbol object for SPY. Since we want the most recent price, we will save the close price. Note that this will be the close price of the day before, since we don't yet know what today's closing price will be. Another way to accomplish the same would be to directly index the data variable.

This will also return a trade bar object that we can use to find the last close price. A third way to achieve the same result would be to use the self.securities dictionary. We can then index self.securities by the symbol and save the closing price.

When using the slice object to access data, it can sometimes be useful to first check if the requested data does already exist. If you just added the data or there is very little trading activity, your algorithm might not have data for a given symbol yet. To check if data exists, you can use .containsKey or Python's IN operator while specifying the requested symbol. Since SPY is very actively traded and we aren't dynamically adding data, this won't really make a difference for this algorithm. But in other cases it can be very helpful to do so.

Next up, Let's finally implement the trade logic of this bot. Firstly, we want to check if our bot is already invested. We can do this with self.portfolio.invested which will return a boolean. It is also possible to index self.portfolio with the symbol and then check if we are invested in this security specifically. But since we are only trading SPY, this won't be necessary here.

Besides checking if we are invested, the portfolio property also allows you to check the available cash, remaining margin, total fees and other relevant information about your portfolio. If we aren't invested, we want to check if it's time to invest. As you hopefully can remember, this bot is supposed to buy and hold SPY until SPY drops or rises more than a certain amount.

Thereafter, we will stay in cash for one month, where after we will buy and hold again. To account for this one month waiting time, we can use self.time to access the current time and then check if this is greater or equal to the next entry time. If this is fulfilled, we want to buy as much SPY as we can.

We can buy SPY using self.marketOrder, which unsurprisingly sends a market order for the specified symbol and quantity. Note that negative order sizes will be interpreted as sell orders. To calculate the order size, we divide the current cash in our portfolio by the current price of SPY.

Since we want this to be an integer, we will cut off any decimal places by casting it to an int. An easy alternative to manually calculating the order size would be to use self.setHoldings which allows you to specify a certain percentage of your portfolio to allocate to a given security. So a 1 would mean that we would want to allocate 100% of our portfolio to SPY which is exactly what we want to do.

After this we want a log that we just bought SPY at the current price. Logging important actions can be very helpful when reviewing and debugging your algorithm. Instead of using self.log you can also use self.debug which will print things to the console. But more on this in the next video.

Next up we want to save the current price of SPY to the entry price variable since we will need this for the exit condition. Note that this save price isn't actually the exact entry price since a market order's fill price can very well deviate from the price of when the order was sent. But we will disregard that for now.

Now All that's left to do is implement the exit process and then we are good to go. For this we will use an elif condition which is reached when our portfolio currently is invested. Here we check the condition whether the entry price either is 10% below or above the current price. In either case we want to exit the SPY position.

Once again you could accomplish this with self.marketOrder but a far easier solution would be to use self.liquidate. which just liquidates all positions in your portfolio. Alternatively, you could also specify to only liquidate the holdings in a certain security by passing its symbol as an argument.

Here we once again will log that we closed our SPY position. Then we will set the next entry time variable to the current time plus self.period which we initialized to 31 days. This makes sure that for the next 30 days we will stay in cache.

With that we have now successfully implemented our first simple trading algorithm. What there is left to do now is click on build and then on the backtest button in the top right. This will then start a backtest which simulates how this bot would have traded over the specified backtest time frame. Depending on the algorithm and time frame backtesting can take anything from a few seconds to hours.

However this algorithm should not take more than a few seconds. After the backtest is finished up, A performance overview like this one will be generated. At the top you can see an equity chart showing your strategy's performance. On this chart you can clearly see the periods where our algorithm held a break from investing.

Above this chart there are some stats such as total profit, total fees, returns and more. In the top right corner you can select which chart should be displayed. For us the benchmark chart is probably one of the most interesting ones since it allows us to compare the performance of our bot to SPY. As you can see, the two seem to be highly correlated which shouldn't be very surprising.

This means that this bot would likely perform very poorly in bear markets. At the bottom, there are a bunch of other useful stats that can be used to analyse your strategy's performance. I will cover all of this more in depth in a later lesson. At the bottom, you can also navigate to the logs tab which shows you all the generated logs from your algorithm. Here you can get a rough overview of when and at what prices we bought and sold.

For a more detailed order overview you can go to the orders tab. Here you can see all the orders that your algo sent as well as the fill prices and other relevant info. Before I end this video I want to emphasize that this is not a good trading strategy or a good trading bot.

Waiting for one month after a 10% move is quite arbitrary and not really based on anything. The purpose of this bot is to give you an introduction to creating your first trading bot and not to make as much money possible. If you want to copy the code from this video, you can do so by using the link in the description box below. That said, I hope you enjoyed this video and learned something new. In the next video, we will start working on a more dynamic trading bot and we will cover topics such as saving and updating orders, using and creating indicators, and much more.

If you enjoyed the video, make sure to subscribe Turn on the notification bell and smash the like button for more content like this. Thanks for watching.