# 06 · Indicators & Historical Data(指标与历史数据) - **系列**:Full Algorithmic Trading Using Python - **频道**:TradeOptionsWithMe | **本集**:Indicators & Historical Data(指标与历史数据) - **时长**:28 分 32 秒 | **原视频**:https://youtu.be/_BHi5Y2Aow4 - **本地视频**:[videos/06-indicators-history.mp4](videos/06-indicators-history.mp4) ## 🎯 本集要点(中文导读) 本集讲 **历史数据请求(History)** 与 **指标(Indicators)**、自定义图表。 - **History 请求**:`self.History(symbol, period_or_barcount, resolution)` 返回 **Pandas DataFrame**。 - 例:`self.History(self.spy, 30, Resolution.Daily)`;第二参可传"天数"或"bar 数量",不传 resolution 则用该标的默认分辨率(否则分钟)。 - 可传 **Symbol 列表** 一次取多个标的;也可对 universe 内所有标的取历史。 - DataFrame 列:Equity 为 close/high/low/open/volume;Forex 默认 QuoteBar(含 bid/ask 各 OHL);crypto/期权/期货默认同 Equity,可切换为 QuoteBar。 - **指标(Indicators)**: - QuantConnect 内置**大量现成指标**(SMA、RSI、MACD、Bollinger 等),可组合使用。 - **快捷方式**:`self.SMA(symbol, period, resolution)` 一行创建并自动更新(`self.` 上的指标辅助方法)。 - **自定义指标**:用 IndicatorBase 等自建;用 `self.RegisterIndicator(symbol, indicator, resolution)` 让指标自动接收数据更新(本集示例注册了自定义指标)。 - 指标每次更新会触发 **Updated 事件**(可挂事件处理器)。 - **自定义图表/Plot**:可在回测图中绘制自定义指标曲线(本集演示了自定义 plot)。 - ⚠️ 注意 **warm-up(预热)**:新注册的指标需要积累数据才"ready",故图表可能回测开始一段时间后才出现;用 history 预热可让曲线从头开始。 - 提示:除非有特殊需求,多数情况直接用内置指标即可,不必自己写。 ## 📝 完整文稿(英文 · 自动转写) > 由本地 ASR(faster-whisper)从视频音轨转录,未人工校对,供检索/精读使用。原始讲解以上方视频为准。 Welcome back to the next video of this algorithmic trading video course. In this course you learn how to develop your own trading algorithms using Python and the QuantConnect algorithmic trading platform. If you are new to this series, I highly recommend going back and starting with the first few videos of this series. In this video you will learn how to perform history requests for different securities, how to use and create indicators inside of QuantConnect, how to create custom plots, and more. As always we will start with a brief theory based part before moving on to writing a simple example trading bot, displaying the learned concepts of this video. First and foremost we will cover how to perform history requests inside of QuantConnect. So far we have only learned how to access the most recent data that your algorithm is receiving. Sometimes you might need the data from a period of time such as the past 30 days. If for instance you want to compare the current price with that of a few days ago or take an average of the price, history requests can be very useful. Before we start getting into the specifics of how to perform history requests inside of your algorithm, let me first give you a rough outline of this feature. A history request can be performed with self.history which normally takes three arguments, namely a symbol, a period or bar count and a resolution. It is also possible to perform history requests without providing a symbol but more on that later. A history request like this one will return a Pandas data frame. If you aren't familiar with Pandas, a data frame basically just is a table that structures the data in an easy to access manner. For equities the return data frame will have a column for the close, high, low, open and volume for each of the past symbols. Since the default data type for forex pairs is quote bars, the data frame for them has a few more columns including close, high, low and open data for both bid and ask bars. For crypto options and futures, the default format is the same as for equities, however it is also possible to change the format to a quote bar format. After requesting this data, you can then access it like you would access any other Pandas data frame and use the data in your algorithm. I will show some examples of how to do this when we start coding later in this video. For now, let's quickly take a look at a few variations of the history request that you can use depending on the use case. Firstly, let's look at the second parameter of the history method. This can either be a period of time or a bar account, so simply passing an integer will be interpreted as a number of bars. For instance, this request would lead to a data frame with 5 rows, one for each of the past 5 one minute bars. If you don't provide a resolution as an argument, the resolution of the data for that symbol will be chosen. If no such resolution can be found, the default resolution is minutely. Instead of passing a bar account, you can also pass a period of time like this. This would get the daily trade bar data for the past 7 days of IBM. It is also possible to pass a list of symbol objects instead of just a single symbol if you want to request the historical data for multiple securities. Besides that, it is even possible to perform history requests for all securities in your algorithms universe. This however would no longer return a Pandas data frame but an array of slice objects instead. For more details and slice objects, I recommend watching the third video of this series. Next to using history requests to access historical data, you can also use them to compare the data points between different securities. Note however that performing many history requests can be quite inefficient. If for instance, your algorithm performs a history request for the past 30 days of pricing data every trading day, it is mostly requesting the same data over and over again since only one day of data actually changes. Due to this in certain cases there are better and more efficient alternatives to history requests. One such alternative is consolidating data or using a rolling window which we will learn more about in the next video. That said, let's now move on to indicators inside of QuantConnect. QuantConnect provides you with over 100 pre-built indicators that you can use with minimal effort. Besides these, you can also create your own indicators as well as combine the pre-existing indicators in various ways. Generally speaking indicators are a great way to gain some additional insights into the markets. This is a great way to complement the decision making of your algorithm. However, usually you would not want to base your entire strategy on one or two indicators and nothing else. If you aren't looking for a super specific indicator, chances are high that QuantConnect already has a shortcut method for your indicator. For an overview of all the available pre-built indicators, check out the link in the description box below. If your indicator is on that list, using it is very easy. Let me demonstrate this using the RSI indicator. The relative strength index indicator takes four arguments. The first is the symbol for which you want to create this indicator and the second is the period for the RSI indicator. Then we have the moving average type that will be used. The most common type of moving average is just a simple, unweighted moving average. Another common moving average is an exponential one which gives more weight to recent price swings. Besides that, there are a bunch of other types of moving averages that you can see in this table. The fourth argument of the RSI indicator helper method is the resolution. Generally, you can only pass resolutions that are equal or higher than the data resolution you set when adding the data. So for example, if you want an indicator with Minitley resolution, the data for that security has to be Minitley, secondly or tick resolution. After initializing an indicator, you usually can't just use it right away. This is the case because an indicator usually needs a certain number of data points to be ready to use. For instance, a simple moving average covering the past 30 days would first hit 30 days of data to calculate the 30 day moving average. There are two main ways to deal with the time it takes for an indicator to get ready. The first and simplest way is to set a warm up time for your algorithm. This can be accomplished with a set warm up method. To check whether your algorithm is currently warming up, you can use the flag self dot is warming up. Inside of your algorithm, you can use the isready property of indicators to check if an indicator is in fact ready. So before using an indicator, you can just use a simple if statement to check this attribute. The disadvantage of this method is that it does not work for more advanced algorithms that use a dynamic universe. Furthermore, you won't be able to use the indicator until enough time has passed. If you don't want to wait, you can also use history requests to pump data into the indicators. For this, you can iterate through the requested historical data and use the update method to update your indicator with the necessary data. I will show you how to do this in the coding part of this video. Once an indicator is ready to use, you can access its current value with current dot value. So if for instance, you want to access the current value of your RSI indicator, you could do so like this. The helper shortcut indicators are very easy to use since everything is done for you automatically. If you want more customizability, there also are other options. For example, most indicators also have the option for you to manually update them and decide which data should be used for this. This can be helpful if for instance you want to use the opening price instead of the closing price for the indicator updates or if you want to use custom time periods. For this, you can't use the helper shortcut methods to create an indicator. All of the pre-built indicators have a shortcut method and a written out version that does not come with automatic updates. To register the non-shortcut versions for automatic updates, you would have to use self dot register indicator. This takes three arguments, namely the symbol for which the indicator will be used, the instance of the indicator and the resolution of the data. Let me show you this with a relative strength index indicator. Instead of using self dot RSI, you would use self dot relative strength index. This takes the same arguments as self dot RSI except for the first symbol argument. To register this for daily updates, you would need this line of code. Self dot register indicator is also what you would use when creating your own custom indicators. I will show you how to do this when we get to the coding part of this video. Besides registering indicators for automatic updates, you can also just manually update them with the update method of the indicator. Last but not least, let me quickly show you how you can combine various indicators. QuantConnect has so-called indicator extensions that you can use to chain different indicators together. The simplest way to show you how this works is by demonstrating it on the identity indicator, which is an indicator that simply tracks the price of a security. Let's say you want to create a Paris trading bot that needs to compare the price spread between two different securities. You could track this spread with a minus indicator extension. This code would track the difference between Pepsi's and Coca-Cola's price in the delta indicator. Besides the minus indicator extension, there also is plus, times, max, min, average and others. This table shows all the possible indicator extensions. Hopefully the example with the identity indicator shows how and why these extensions can be useful. But note that you can also use these on all other indicators and even on custom created ones. So the possibilities of combining various indicators really are endless. That said, I hope this theory part gave you a good introduction to indicators and history requests. Let's now move on to the coding part of this video. Before we head over to the QuantConnect platform and start putting the learn things into practice, let me quickly outline the idea behind the trading bot that we will implement in this video. Just like the last few times, let me start with a short disclaimer. The bot that we will implement is very simple and its purpose is to show you how to apply the just presented concepts. It won't be a very good trading bot that you would actually want to trade with real money. For this video we will once again just be trading SPY, but definitely feel free to try out different securities. The strategy that we will implement will first try to identify whether SPY's price currently is an uptrend or in a downtrend. It will do this with the help of a simple moving average. If SPY's price is above its simple moving average, we consider it to be an uptrend. Otherwise we consider that to be a downtrend. Besides looking at the SMA indicator, we will also look at how close SPY's current price is to its 52-week high and low. If SPY is in an uptrend and close or above its 52-week high, we want to go long. Otherwise if SPY is in a downtrend and close to its 52-week low, we want to go short. In all other cases we don't want an active position. If SPY's price is in an uptrend and near its high we will buy as much as we can. We first exit this position if SPY's price crosses below its simple moving average or its price falls out of reach of its 52-week high. If we do this the other way around for a downtrend near SPY's low. The hypothesis behind our strategy is that combining multiple indicators such as a simple moving average for identifying a trend and a 52-week range breakout might yield better results than just using a single or no indicator at all. That said, let us now head over to the QuantConnect platform and start turning this idea into actual code. If you haven't already, make sure to use the link in the description box below to create your free QuantConnect account. Next to that link you will also find a link to copy all the code from this video. I do however recommend coding along and trying out things yourself since that usually leads to a better learning experience. Inside of QuantConnect we will create a new algorithm in the lab tab. Once again we won't be using the strategy builder for now which is why we click on exit and start with the blank template algorithm. As always we will start by implementing the initialised methods. First, we set the start and end date for backtesting. You can choose whatever values you want to hear. Thereafter I'll just leave the account balance to be 100,000 US dollars as it is right now. In real trading this will be taken from your broker account. Then, we will quickly add the data for SPY. Since I've already covered all these steps in previous videos I won't go over this in any detail here. For this algorithm we will be using the daily resolution. Next up we want to initialise a simple moving average indicator. For this we will use the sma helper shortcut method. First, we specify self.spy to be the security for this indicator. Then we specify 30 days for the length of this moving average and lastly we pass daily resolution as its resolution. Note that you can't use a lower resolution here since we added the SPY data as daily data as well. With this we are already done with the initialise method. I hope this shows you how easy it is to set up indicators with the help of these shortcut methods. After implementing this algorithm I will also show you how you could implement your own custom indicators by creating a custom implementation of a simple moving average indicator. But for now let's move on to the on data method. Instead of the on data method we will want to use the sma indicator for our decision making. The problem here is that it might not be ready yet since it first has enough data for a 30 day moving average after at least 30 days. That's why we have to check if it's ready before trying to use it. If it's not ready we just return. As I already mentioned earlier in this video it is also possible to use history requests to pump data into indicators so that you don't have to wait for them to become ready. Let me show you how to do this for this sma indicator. Since we only want to do this once in the beginning of our algorithm we will do this in the initialise method. The first thing we do for this is perform a history request using self.history. The past arguments are self.spy as the symbol, 30 as the bar account and daily resolution as the resolution. As you hopefully can remember this returns upon us data frame with a column for high, low, open, close and volume information for each of the past 30 bars. We will use the close price for the sma indicator which is why we index this data frame by the close column and save it into the closing prices variable. Next up we want to iterate over this series of time and closing price information. Since the requested data frame is also indexed by symbols we first want to lock onto the spy symbol. We can do this with .lock. Now what is left to do is update the sma indicator with the time and price info of each of these rows. Doing this makes sure that the sma indicator is ready and good to use right from the beginning. In theory this means that it would be unnecessary to check whether the indicator is ready but I leave it in for now nonetheless. The next thing we want to do in the on data method is save the high and low price of the past year. We will once again use a history request for this but before we do so I want to say that there are other much more efficient ways to accomplish the same result. To save the high and low we use the history request for daily resolution and the spy symbol. This time we pass a period of 365 days instead of a bar account. If we would specify 365 as a bar account instead of a time delta the history request would get data for the past 365 trading days. Since this excludes weekends that would be about one and a half years instead of only one year. So if you are creating an indicator or using the bar account and want to cover a time span of one year make sure to use 252 bars for 52 week time frame. As the low we save the minimum value of the low price column and as the high we save the maximum value of the high price column of this 365 day time frame. The reason why such a history request is so inefficient is that we are basically requesting data for the past 365 days every day. Here actually only the first and last day of the data changes so we are basically requesting the same data over and over again. The more efficient alternative would be to use a rolling window which we will learn in the next video. Another alternative would be to use the minimum and maximum indicators. If you want to practice your skills I'd recommend trying to replace this history request using min and max indicators after watching this video. For more details on these and other indicators check out the reference table on the indicators documentation page that I linked below. In the next step we will save SPY's most recent price to the price variable. For this we will access the securities array and index it with the SPY symbol object. After that we can now check if SPY's price is within 5% of its 365 day high and whether the price is above its simple moving average. To do so we have to access the indicators value which we can do with dot current dot value. If these two conditions are fulfilled we want to establish a long position. However we only want to do so if we don't already have such a position which is why we use self.prolio.is long to check this. If we don't have a long SPY position we use self.set holdings to allocate 100% of our buying power to SPY. Now we basically do the exact same thing for going short. So first we check if SPY's price is within 5% of its low as well as in the downtrend. Thereafter we check if we don't already have a short SPY position. If all these conditions are fulfilled we use set holdings to short as much of SPY as we can. In the case that none of the outro if and l if conditions are fulfilled we want to close any open SPY positions since we aren't near a low and in a downtrend and not in an outrend and near a high. We exit any potential open positions by calling self.liquidate. If there aren't any open positions self.liquidate does nothing. Last but not least we want to plot a few things so that we can better understand and analyze what our trading algorithm is doing. So let's use self.plot to plot the high, low and simple moving average value. Self.plot takes three arguments. The first is the name of the chart that we want the plot to be on. The second is the name of the actual plot and the final one is the actual data. We want to plot all our values on the same benchmark chart. The benchmark chart is an already existing chart with a plot of SPY's price on it. So with these three lines we will have a chart with plots for SPY's price, high, low and 30 day moving average all in one place. All that's left to do now is click on build and back test to view this chart as well as the performance of this algorithm over the specified time period. Once again we won't really look at the performance of this bot since that's not the point of this video. We'll do that later in the series when we create more interesting bots with better strategies. When the back test is finished we want to select the benchmark chart in the top right. We can then quickly enlarge in this chart so that we can take a better look at it. As you can see this chart now has four plots. The red plot is SPY's price over this time frame. The green one is the simple moving average and the other two are 365 day high and low. Here we can see that SPY seems to be making quite a few new highs over this time period while the lows stayed constant after the early 2020 dip. Judging from this chart this algorithm should only have established any short positions in the beginning of 2020 since there its price clearly was below the moving average and it was definitely near its low since it was making new lows. To verify that the algorithm was actually doing this let's scroll down and look at the orders that were sent during March of 2020. Here we can see that during March this algorithm exclusively shorted SPY which is exactly what we were expecting. Since SPY's price was very volatile during this time it had multiple days where the price moved more than 5% up from its low which is why the algorithm only had some of these short positions for very few days. Before I end this video let me quickly present one last thing namely how to create custom indicators. Like I mentioned earlier I will implement a custom simple moving average for this. This means that from the functionality this algorithm won't change but instead of using the help with short cut SMA method we will create our own version of the indicator. Hopefully this example will help you learn how to create your own indicators inside of QuantConnect. Firstly we will import DQ from collections. DQ is a double sided Q which allows you to append and remove elements from both sides of the collection. You can think of this like a normal Q but besides appending to the right you can also append to the left the same goes for removing elements. We will need this for the moving average since new price data is coming in and old data is going out. For a custom indicator we will now create our own indicator class. This class has to inherit from the Python indicator class and we have to implement both an initialize and update method. For the initialize method we will just use the standard Python init class method. This constructor will take a name and a period as arguments. In this method we will just initialize 4 helper variables. The first is the name of the indicator which will set to the past name. The second helper will be a variable saving the current time which we initialize with the earliest time possible. Then we have a variable that saves the actual value of our simple moving average indicator. Last but not least we create a variable in which we save a DQ object. The max length of this double sided Q will be the period that we passed. So if we want the 30 day moving average this Q will save up to 30 elements. Now that we have implemented the init method we just have to implement the update method so that the indicator actually can be used. The update method will simply take one input argument. We want this to be a trade bar object for SPY. The first thing we do in here is append the closing price of the past bar to the left of our Q. We want this Q to have the most recent values to the left and the older ones to the right. Then we save the end time of the past bar input to the self.time variable of this indicator class. After that it is time to calculate the actual value of the simple moving average. As you probably know this can be done by simply summing up the safe values and dividing by the total number of elements. We can use the sum method to sum all the elements in our Q and then divide by the length of this Q. Note that if the Q is not yet filled with all values we aren't actually taking the average of the period that we want. In other words this indicator is first ready after receiving at least as many data points as the max length of this Q. We don't actually have to manually set the Israeli attribute of the indicator though. Instead we just return true if it's ready and folds if not. We can do this in one line by returning this Boolean condition. With that we have now successfully implemented a custom version of the SMA indicator. To actually use it we will head back to the initialized method of RQC algorithm class. For this let me quickly comment out the code for the non-custom SMA indicator. After that we will first create an instance of our custom simple moving average class. The first argument of its constructor is just the name and the second is a period for which we will once again use 30. Now we have to register this instance to be an indicator for our algorithm. We can do this with self.register indicator which takes three arguments. First we specify for which security this indicator is. Then we specify what the actual indicator is and lastly we pass the resolution of the data that this indicator will be using. In our case this of course is daily resolution. This makes sure that our indicator will receive automatic daily updates with SPYTradeBarData. Now we are already done. We don't have to adjust anything inside of the on data method. So to double check if everything is working correctly let's build and back test one more time. After the back test is finished let's once again look at the benchmark chart. As you can see here our custom plots actually did not start until about a month after the back test start. This is because we waited for our custom indicator to get ready and did not pump any data into it right from the get go. Let's also why these results are slightly different but if we remove the history warmup for the shortcut helper sma the results should be the same. If you found this custom indicator implementation to be a little overwhelming don't worry too much. QuantConnect already provides you with lots and lots of pre-built indicators that you can use and combine in many different ways. So unless you want something very specific you likely won't have to create too many custom indicators yourself. That said I hope you enjoyed this video and learned some new things. If you want to clone this code there's a link in the description box below. In the next video we will look at consolidating data, scheduling events, rolling windows and more. If you liked this video and are enjoying this series make sure to smash the like button, subscribe and turn on the notification bell for more content like this. Thanks for watching.