# 04 · Handling Data(处理数据 · 第一个算法) - **系列**:Full Algorithmic Trading Using Python - **频道**:TradeOptionsWithMe | **本集**:Handling Data(处理数据 · 第一个算法) - **时长**:18 分 1 秒 | **原视频**:https://youtu.be/WQwyKfef80k - **本地视频**:[videos/04-handling-data.mp4](videos/04-handling-data.mp4) ## 🎯 本集要点(中文导读) **写第一个真正的算法**(实战入门):买入并持有 SPY,涨/跌超 10% 就平仓,然后空仓 1 个月再循环。 - **入口**:QuantConnect → Lab → Create New Algorithm → `Exit Builder mode` 得到空白模板。 - **Initialize()**: - `SetStartDate` / `SetEndDate`(不设 end 则取最近)、`SetCash` - `AddEquity("SPY", Resolution.Daily)` 添加数据(分辨率:tick/秒/分/时/日;一般不建议用 tick) - `SetDataNormalizationMode(...)`:数据复权方式——默认 `SplitAndDividendAdjusted`,另有 `Raw`/`SplitAdjusted`/`TotalReturn`(决策权:期权等只支持 Raw) - 保存 `Symbol`、`SetBenchmark(SPY)`(自动生成基准图表) - 券商模型(默认即可,也可选 InteractiveBrokers 等)与账户类型(Cash / Margin,含 PDT 规则) - **OnData(data)**:`data` 是 **Slice** 对象(含 `Bars`/`QuoteBars`/`OptionChains` 等,按 Symbol 索引)。 - 取价三法:`data.Bars[self.spy].Close` / `data[self.spy].Close` / `self.Securities[symbol].Close` - 用 `data.ContainsKey(symbol)` / `in` 判断数据是否就绪 - **下单/平仓**:`self.MarketOrder(symbol, qty)`(负数=卖出)、`self.SetHoldings(symbol, 1.0)`(按比例,1.0=满仓);`self.Liquidate()` 一键清仓。 - **回测**:点 Build → Backtest,查看权益曲线、基准对比、统计指标、Logs / Orders 标签页。 - ⚠️ 作者强调:这只是教学示例,"等 1 个月"等规则是随意的,**不是能实盘的好策略**。 ## 📝 完整文稿(英文 · 自动转写) > 由本地 ASR(faster-whisper)从视频音轨转录,未人工校对,供检索/精读使用。原始讲解以上方视频为准。 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 QuantConnect 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 initialised and on-data method. The on-data 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 on-data method, let's first take care of the initialised method which is unsurprisingly used to initialise your algorithm. First and foremost, we will set the start and end date for backtesting. 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 backtesting purposes. In real-life trading, this summer 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 wanted to trade SPY, we will use Add Equity and the SPY ticker symbol. As a second argument, the Add Equity 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 player, you could use Add Forex. For futures, you would use Add Futures for 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 certain 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 Set Data Normalization Mode 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 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 trade 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 Here Brokerage Model, One-Dab brokerage, FXCM brokerage, Bitfenix 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 three days for equities. Margin accounts however allow you to use leverage from two to four times your capital. You can also use the pattern 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 three 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.entry price which will track the entry price of our SPY position. The second one is self.period which we will set to a time frame of 31 days and lastly self.next entry time 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 initialize method which means we can move on to implementing the on data 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 look ahead bias. The on data 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 the event handler such as the on data 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, spits, 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 rule 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 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 are also quotes 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 delisting, simple 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 on data method, let's start implementing the on data method. Firstly, we will want to save the current price of SBI 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.baras is a dictionary that we can index with self.spy, which is the symbol object for SBI. 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's very little trading activity, your algorithm might not have data for a given symbol yet. To check if data exists, you can use .contains key or Python's in operator while specifying the requested symbol. Since SBI 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 a symbol and then check if we are invested in this security specifically. But since we are only trading SBI, 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 SBI until SBI 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 SBI as we can. We can buy SBI using self.market order, which unsurprisingly sends a market order for the specified symbol and quantity. Note that negative order sizes will be interpreted as cell orders. To calculate the order size, we divide the current cash in our portfolio by the current price of SBI. Since we want this to be an integer, we will cut off any decimal places by casting it to an end. An easy alternative to manually calculating the order size will be to use self.set holdings, which allows you to specify a certain percentage of your portfolio to allocate to a given security. So a one would mean that we would want to allocate 100% of our portfolio to SBI, which is exactly what we want to do. After this, we want to log that with just both SBI 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 SBI 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're good to go. For this, we will use an LF 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 SBI position. Once again, you could accomplish this with self.market order, 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 it simple as an argument. Here, we once again will log that we close our SPI position. Then, we will set the next entry time variable to the current time plus self.tor period, which we initialize to 31 days. This makes sure that for the next 30 days, we will stay in cash. With that, we've now successfully implemented our first simple trading algorithm. What there's left to do now is click on build and then on the back test button in the top right. This will then start a back test, which simulates how this bot would have traded over the specified back test time frame. Depending on the algorithm and time frame, back testing can take anything from a few seconds to hours. However, this algorithm should not take more than a few seconds. After the back test has 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 analyze 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 algorithm send as well as the field 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.