I am constantly referring back to the Boot Camp, but it is difficult to access the information in its interactive format. I have copied the text of the lesson here for quick access and review. (Do the boot camp before using this for review.) -GKBuy and Hold / Equities  (#1)

Buy and hold may be the simplest algorithm possible yet covers many aspects of building an algorithm. In this lesson we walk through the steps of initializing an algorithm to run a buy and hold strategy along with some nuances of getting the data you want.

What You'll Learn
  • Initializing algorithms date range and starting portfolio value.
  • Requesting asset data and configuring your data resolution.
  • Differences in data normalization techniques and how to request raw data.
  • Accessing algorithm portfolio properties.
Set Starting Cash
  1. Buy and Hold / Equities
  2. Set Starting Cash
Initializing Algorithms

At the very start of your algorithm we call the Initialize() method to set up your strategy. It is important to set up the state of your algorithm here so it can be restarted easily.

Initializing Cash

The self.SetCash() method sets the starting capital for your strategy. In backtesting we use the value you set to initialize your capital; in live trading we get this value from your brokerage account. By default we use a starting cash of $100,000 in backtesting and paper trading.

Task Objectives CompletedContinue

  1. Set starting cash for the algorithm to $25,000

class BootCampTask(QCAlgorithm):

    def Initialize(self):
        
        self.AddEquity("SPY", Resolution.Daily)
        
        # 1. Set Starting Cash 
        self.SetCash(25000)
        
    def OnData(self, data):
        pass

-----------------------------

Set Date Range
  1. Buy and Hold / Equities
  2. Set Date Range
Initializing Analysis Dates

The date range for your backtest is defined in your Initialize method. You can set this with fixed dates or a datetime object. These methods are called self.SetStartDate() and self.SetEndDate() .

Task Objectives CompletedContinue

  1. Set start date for the backtest to January 1st, 2017
  2. Set ending date for the backtest to October 31st, 2017
The SetStartDate() and SetEndDate() methods both have the same arguments: int yearint month and int day. You should replace the numbers in the algorithm code with the right dates. 

class BootCampTask(QCAlgorithm):

    def Initialize(self):
        
        #1-2. Set Date Range
        self.SetStartDate(2017, 1, 1)
        self.SetEndDate(2017, 10, 31)
        self.AddEquity("SPY", Resolution.Daily)
        
    def OnData(self, data):
        pass
-----------------------

Manually Selecting Data
  1. Buy and Hold / Equities
  2. Manually Selecting Data
Initializing Datafeeds

There are many ways to request data in QuantConnect. To start we're going to demonstrate how to ask for data manually.

Adding Data

The self.AddEquity() method is used for manually requesting assets. It takes a string ticker of the current asset name and the resolution. For more information see the documentation.

The AddEquity (and all other AddAsset methods) return a security object. This gives you a reference to the security object you can use later.

You can control the resolution of the data with the Resolution enum. It has the values TickSecondMinuteHour and Daily. e.g. Resolution.Minute. Not all data is available in all resolutions. You should check the Data Library to make sure your resolution is available.

You can change leverage, request pre-market data and disable fill-forward. You can see the API calls in the documentation. We'll discuss these next.

Task Objectives CompletedContinue

  1. Update the code to request minute resolution data for SPY.
  2. Add minute resolution data for the Russel 2000 ETF (ticker: IWM).
You can use different resolutions together without issue. Just call the AddEquity(string ticker, Resolution resolution) method with the ticker and resolution set. 

class BootCampTask(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2017, 6, 1)
        self.SetEndDate(2017, 6, 15)
        
        # Manually Select Data
        self.spy = self.AddEquity("SPY", Resolution.Minute)
        self.iwm = self.AddEquity("IWM", Resolution.Minute)
        
    def OnData(self, data):
        pass
-------------------------

Set Data Normalization Mode
  1. Buy and Hold / Equities
  2. Set Data Normalization Mode
Data Normalization

In QuantConnect, historical data is adjusted by default. This means the historical values are changed to account for share splits and dividend payments. Historical prices can look a little bit strange if you don't understand why it was done: e.g. instead of $10.23 the price might read $1.21216.

You can control the Data Normalization Mode for each asset individually. This is done with the: security.SetDataNormalizationMode() method. The accepted values are: RawAdjustedSplitAdjusted and TotalReturn.

ibm = self.AddEquity("IBM", Resolution.Minute) ibm.SetDataNormalizationMode(DataNormalizationMode.Adjusted) //DefaultAccessing Securities

Securities in your algorithm can be accessed via the self.Securities[symbol] dictionary. It looks up a security by its symbol object or ticker string. Using this accessor you can look up a security and alter its models or data normalization mode.

//Same as above; accessing securities properties and methods. self.Securities["IBM"].SetDataNormalizationMode(DataNormalizationMode.Adjusted)

Task Objectives CompletedContinue

  1. Change the data normalization mode of SPY to Raw.
  2. Using the security.SetLeverage method; set the leverage for the IWM Equity to 1.0.
Following the IBM example in the content body access the SPY security and call the SetDataNormalizationMode() method.

class BootCampTask(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2017, 6, 1)
        self.SetEndDate(2017, 6, 15)
        
        #1-2. Change the data normalization mode for SPY and set the leverage for the IWM Equity     
        self.spy = self.AddEquity("SPY", Resolution.Daily)
        self.iwm = self.AddEquity("IWM", Resolution.Daily)
    
        self.spy.SetDataNormalizationMode(DataNormalizationMode.Raw)
        
        self.iwm.SetLeverage(1.0)
        
    def OnData(self, data):
        pass

------------------------------------------------------

Checking Holdings
  1. Buy and Hold / Equities
  2. Checking Holdings
Global Portfolio Properties

The algorithm Portfolio dictionary also has helper properties for quick look ups of things like: InvestedTotalUnrealizedProfitTotalPortfolioValueTotalMarginUsed. You can see more properties in the documentation. e.g:

if self.Portfolio["IBM"].Invested: self.Debug("Total unrealized profit: " + str(Portfolio.TotalUnrealizedProfit))Specific Portfolio Items

Individual asset holdings are held in your Portfolio property. This can be accessed via the self.Portfolio[symbol] dictionary. Entries in the Portfolio dictionary are SecurityHolding objects with many properties about your holdings, such as: InvestedQuantityAveragePrice and 

UnrealizedProfit. e.g:

if self.Portfolio["IBM"].Invested: self.Debug("We have IBM shares!")

Task Objectives CompletedContinue

  1. Update the AddEquity command to request IBM data instead of SPY.
  2. Fix the compiler error on the Debug statement to display the quantity of IBM shares you own.

Don't forget to update the AddEquity line to IBM!

Remember you cannot concatenate floats with strings. You will need to wrap the Quantity with the python str() method.

class BootCampTask(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2017, 6, 1)
        self.SetEndDate(2017, 6, 2)
        
        #1. Update the AddEquity command to request IBM data
        self.ibm = self.AddEquity("IBM", Resolution.Daily)
        
    def OnData(self, data):
        
        #2. Display the Quantity of IBM Shares You Own
        self.Debug("Number of IBM Shares: " + str(self.Portfolio["IBM"].Quantity))
    --------------------

Placing Orders
  1. Buy and Hold / Equities
  2. Placing Orders

In this task we're going to pull together everything we've learned so far; requesting data, setting data normalization, using your portfolio and printing a debug statement.

Submitting an Order

Lets place our first trade! There are many ways to submit orders through LEAN but we're going to start with a MarketOrder. It can be used like this:

self.MarketOrder("AAPL", 200)

Market orders are filled immediately when the market is open. If you are using daily data, the order isn't processed until the next morning. Daily bars only arrive at your algorithm after the market has closed.

Fill Prices

The average fill price of your asset is available in the Portfolio class. You can access it like this: Portfolio["IBM"].AveragePrice. In backtesting this is a modelled price. In live trading this is taken from your brokerage fill event.

Task Objectives CompletedContinue

  1. Insert AddEquity in the Initialize() method to request IWM data at minute resolution.
  2. Set IWM to DataNormalizationMode.Raw.
  3. In the OnData() method place a self.MarketOrder() for 100 shares of IWM.
  4. Using self.Debug() print the AveragePrice of IWM to the console.

Use self.Portfolio["IWM"].AveragePrice or self.Portfolio.Invested to only send 1 debug message to the console.

You might be able to use something like this in the OnData method...

if not self.Portfolio.Invested: // ...class BootCampTask(QCAlgorithm):     def Initialize(self):         self.SetStartDate(2017, 6, 1)         self.SetEndDate(2017, 6, 15)         #1,2. Select IWM minute resolution data and set it to Raw normalization mode         self.iwm = self.AddEquity("IWM", Resolution.Minute)         self.iwm.SetDataNormalizationMode(DataNormalizationMode.Raw)     def OnData(self, data):         #3. Place an order for 100 shares of IWM and print the average fill price         #4. Debug the AveragePrice of IWM         if not self.Portfolio.Invested:             self.MarketOrder("IWM", 100)             self.Debug(str(self.Portfolio["IWM"].AveragePrice)) ------------------------  

    

Author