AuthorJing Wu2018-06-03

Introduction

In the last tutorial we introduced logical operations, loops and list comprehension. We will introduce functions and object-oriented programming in this chapter, which will enable us to build complex algorithms in more flexible ways.

Functions

A function is a reusable block of code. We can use a function to output a value, or do anything else we want. We can easily define our own function by using the keyword "def".

def product(x,y):
    return x*y
print(product(2,3))
[out]: 6
print(product(5,10))
[out]: 50

The keyword def is followed by the function name and the parenthesized list of formal parameters. The statements that form the body of the function start at the next line, and must be indented. The product() function above has x and y as its parameters. A function doesn't necessarily have parameters:

def say_hi():
    print('Welcome to QuantConnect')
say_hi()
[out]: Welcome to QuantConnect

Built-in Function

range() is a function that creates a list containing an arithmetic sequence. It's often used in for loops. The arguments must be integers. If the "step" argument is omitted, it defaults to 1.

print(range(10))
[out]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(range(1, 11))
[out]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(range(1, 11, 2))
[out]: [1, 3, 5, 7, 9]

len() is another function used together with range() to create a for loop. This function returns the length of an object. The argument must be a sequence or a collection.

tickers = ['AAPL', 'GOOGL', 'IBM', 'FB', 'F', 'V', 'G', 'GE']
print(f"The number of tickers is {len(tickers)}")
for k in range(len(tickers)):
    print(f"{k + 1} {tickers[k]}")
[out]:
The number of tickers is 8
1 AAPL
2 GOOGL
3 IBM
4 FB
5 F
6 V
7 G
8 GE

Note: If you want to print only the tickers without those numbers, then simply write "for ticker in tickers: print(ticker)"

map() is a function that applies a specific function to every item of a sequence or collection, and returns a list of the results.

tickers = ['AAPL','GOOG','IBM','FB','F','V', 'G', 'GE']
print(list(map(len,tickers)))
[out]: [4, 5, 3, 2, 1, 1, 1, 2]

The lambda operator is a way to create small anonymous functions. These functions are just needed where they have been created. For example:

print(list(map(lambda x: x**2, range(10))))
[out]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

map() can be applied to more than one list. The lists have to have the same length.

print(list(map(lambda x, y: x+y, [1,2,3,4,5], [5,4,3,2,1])))
[out]: [6, 6, 6, 6, 6]

sorted() takes a list or set and returns a new sorted list:

sorted([5,2,3,4,1])
[out]: [1, 2, 3, 4, 5]

We can add a "key" parameter to specify a function to be called on each list element prior to making comparisons. For example:

price_list = [('AAPL', 144.09), ('GOOGL', 911.71), ('MSFT', 69), ('FB', 150), ('WMT', 75.32)]
sorted(price_list, key = lambda x: x[1])
[out]:
[('MSFT', 69), ('WMT', 75.32), ('AAPL', 144.09), ('FB', 150), ('GOOGL', 911.71)]

By default the values are sorted by ascending order. We can change it to descending by adding an optional parameter reverse.

price_list = [('AAPL', 144.09), ('GOOGL', 911.71), ('MSFT', 69), ('FB', 150), ('WMT', 75.32)]
sorted(price_list, key = lambda x: x[1], reverse = True)
[out]:
[('GOOGL', 911.71), ('FB', 150), ('AAPL', 144.09), ('WMT', 75.32), ('MSFT', 69)]

Lists also have a function list.sort(). This function takes the same "key" and "reverse" arguments as sorted(), but it doesn't return a new list.

price_list = [('AAPL', 144.09), ('GOOGL', 911.71), ('MSFT', 69), ('FB', 150), ('WMT', 75.32)]
price_list.sort(key = lambda x: x[1])
print price_list
[out]:
[('MSFT', 69), ('WMT', 75.32), ('AAPL', 144.09), ('FB', 150), ('GOOGL', 911.71)]

Object-Oriented Programming

Python is an object-oriented programming language. It's important to understand the concept of "objects" because almost every kind of data from QuantConnect API is an object.

Class

A class is a type of data, just like a string, float, or list. When we create an object of that data type, we call it an instance of a class.

In Python, everything is an object - everything is an instance of some class. The data stored inside an object are called attributes, and the functions which are associated with the object are called methods.

For example, as mentioned above, a list is an object of the "list" class, and it has a method list.sort().

We can create our own objects by defining a class. We would do this when it's helpful to group certain functions together. For example, we define a class named "Stock" here:

class Stock:
    def __init__(self, ticker, open, close, volume):
        self.ticker = ticker
        self.open = open
        self.close = close
        self.volume = volume
        self.rate_return = float(close)/open - 1

    def update(self, open, close):
        self.open = open
        self.close = close
        self.rate_return = float(self.close)/self.open - 1

    def print_return(self):
        print(self.rate_return)

The Stock class has attributes ticker, open, close, volume and rate_return. Inside the class body, the first method is called __init__, which is a special method. When we create a new instance of the class, the __init__ method is immediately executed with all the parameters that we pass to the Stock object. The purpose of this method is to set up a new Stock object using data we have provided.

Here we create two Stock objects named "apple" and "google".

apple  = Stock('AAPL', 143.69, 144.09, 20109375)
google = Stock('GOOGL', 898.7, 911.7, 1561616)

Stock objects also have two other methods: update() and print_return(). We can access the attribues of a Stock object and call its methods:

apple.ticker
[out]: 'AAPL'
google.print_return()
[out]: 0.0144653388227
google.update(912.8,913.4)
google.print_return()
[out]: 0.000657318141981

By calling the update() function, we updated the open and close prices of a stock. Please note that when we use the attributes or call the methods inside a class, we need to specify them as self.attribute or self.method(), otherwise Python will deem them as global variables and thus raise an error.

We can add an attribute to an object anywhere:

apple.ceo = 'Tim Cook'
apple.ceo
[out]: 'Tim Cook'

We can check what names (i.e. attributes and methods) are defined on an object using the dir() function:

dir(apple)
[out]:
['__doc__',
 '__init__',
 '__module__',
 'ceo',
 'close',
 'open',
 'print_return',
 'rate_return',
 'ticker',
 'update',
 'volume']

Inheritance

Inheritance is a way of arranging classes in a hierarchy from the most general to the most specific. A "child" class is a more specific type of a "parent" class because a child class will inherit all the attribues and methods of its parent. For example, we define a class named "Child" which inherits "Stock":

class Child(Stock):
    def __init__(self, name):
        self.name = name

Then we create an object:

aa = Child('AA')
print(aa.name)
[out]: 'AA'
aa.update(100, 102)
print(aa.open)
[out]: 100
print(aa.close)
[out]: 102
aa.print_return()
[out]: 0.02

As seen above, the new class Child has inherited the methods from Stock.

Summary

In this chapter we have introduced functions and classes. When we write a QuantConnect algorithm, we would define our algorithm as a class (QCAlgorithm). This means our algorithm inherited the QC API methods from QCAlgorithm class.

In the next chapter, we will introduce NumPy and Pandas, which enable us to conduct scientific calculations in Python.



QuantConnect Logo

Try the world leading quantitative analysis platform today

Sign Up

Previous: Python: Logical Operations and Loops Next: NumPy and Basic Pandas