Python Installation and Spyder IDE Guide
Python Installation and Spyder IDE Guide
I
@author: Amir Akbari
AR
1 Starting with Python
1.1 Installing Python
Please search for Download Anaconda Python on Google, choose your Operating System (Win-
dows, iOS, Linux), and follow the instructions to install Python version 3 via Anaconda. Anaconda
is a distributor of Python and provides Python Interpreter (engine), Sypder IDE (editor), Jupyter
(notebook), . . . . This makes the Python configuration on your computer seamless, however, some
KB
advanced users may prefer to install Python components separately, and for example work with a
different editor such as PyCharm.
Note: To prevent potential glitches, avoid naming your folders with special characters. Likewise,
it is also recommend to avoid a space in folder and file names. You may use the underscore key,
_, instead. While this is not an issue for installing Python or Anaconda, it is an issue for IPython
which looks in c:\user\username\.ipython for configuration files. The solution is to define the
HOME variable before launching IPython to a path that has only ASCII characters.
A
I use a Windows laptop and present the shortcut keys for Windows. On Mac, often there is a similar
shortcut key and functionality using the command key instead of Ctrl. Of course, Mac users can
search for the shortcut on the web too. The current Python version on my device is 3.8 and I work
with Spyder version 5. If you use a different version (Python 3 and above) you may notice small
visual differences but the functionality would be them same.
A.
Note: You may change the background color (aka theme) or font in the Editor and other windows
in Spyder from Tools -> Preferences -> Appearance
1
Spyder has three windows:
I
AR
KB
Figure 1. Spyder Windows. An example Screenshot
This is where you write your codes before evaluating it. In the editor, your codes are color-coded for
better readability. The executable codes are shown in black font. The reserved words (see Section
4. Variable Names and Types below for details) are in blue. The text input values (those between
A
" " or ' ') and notes are in green. The gray font lines are comments. Notes and comments are
similar concepts. These are lines which will not be executed but help to document, add clarification
comments, and organize your code. Use # at the beginning of your text to comment out a line and
use """ to open and close a few lines of notes.
Spyder has the tab completion feature: - After entering 1 or more characters, pressing the TAB
A.
button on your keyboard will bring up a list of functions, packages, and variables that match the
typed text. This will reduce the chance of a typo in the syntax and is the recommended approach
in coding, especially because Python is case sensitive (Python is also whitespace sensitive. So
indentation, either spaces or tabs, affects how Python interprets files. We will talk about these
more later). If the list of matches is large, pressing the TAB button again allows the arrow keys can
be used to browse and select a completion. Also, when you write the function names in Spyder a
window will pop up which shows its syntax, its input variables, and further information. A similar
feature is in MS-Excel.
2
The editor comes with a line number area, shown in the left side bar (see Figure 1). If Spyder spots
an error in a line of your code it will show warnings and syntax errors, with a cross in a red circle
beside that line. They can help you to detect potential problems before running your code. For
instance, there is an error in line 16 in Figure 1 above. If I hover my mouse on the alert sign (the
cross in a red circle) an window will pop up that reads Code Analysis Undefined name "MIN".
This occurs because Python is case sensitive and differentiates between min and MIN or Min. It does
not know what MIN is so it gives the error.
I
Once you are done with your code, you can save it similar to an MS-Excel file and open it next
time. Your codes will have a .py type. A Python program (aka scripts) is a collection of functions
AR
that can be run stand alone. You can open as many files you want in Spyder. They will be shown
as a new tab beside each other. In Figure 1, I have two files open: TeachingNote_Python.py and
[Link]. In Spyder, if you modify your codes and your file is not saved afterwards, you will
see an asterisk (*) beside its name. In Figure 1, I have modified [Link] file but did not save
it, so Spyder added a * to indicate that.
2. IPython Console: where you run your code and see the outputs
This is where you can run Python code, either from the Editor or interactively. To run your code
KB
you have several options. The most commonly used ones are:
• you can execute your whole script by choosing Run from the Run drop menu in the toolbar (See
Figure 2). Alternatively, you may press the green triangle icon (its shortcut key on Windows
is F5).
• if you want to run one part of your script, first create a cell by adding #%% before and after
that part. Then choose Run Cell from the Run drop menu. This will execute the codes in
the active cell, the one that your mouse has last clicked in. Alternatively, click the icon with
the green triangle in a rectangle (its shortcut key is Ctrl+Enter). If you want to serially run
A
cells, one after another, you can use Run cell and advance or the icon with a double green
triangle in a rectangle (its shortcut is Shift+Enter).
• if you want to run a few lines of your code, you can select them and choose Run selection
or current line from the Run drop menu, or use the icon with a green triangle beside I (its
shortcut key is F9). Similarly, you can write part of your code in the IPython Console window
A.
and press the Enter key to run only that part. For example, you can use it as a calculator.
Try it!
3
I
AR
Figure 2. Run command
The output of your code will be shown in the IPython Console. For instance, when I execute
print("Hello, World!"), which asks Python to display the text “Hello World!”, we see this text
displayed in the IPython Console window (Figure 1). An input counter number beside each of
your commands will be displayed too, e.g. In [1] before print ("Hello, World!") in Figure 1. The
counter for the next command increases serially. Also, if your code has an error, it will be shown
KB
here. For example, run x2 = MIN(5,-1,100) to see a NameError.
You may access the history of all the codes you executed (ran) in History tab in this window too.
(double click on it) and press Ctrl+I. This will call the help on that function and show it
in the Source window. You can also type help(FUNCTION_NAME) in the console or search
for the function in the HELP pane.
• PLOTS: if your code generates a plot, it will be shown here (see Section 5.7. Matplotlib
for details on plotting in Python).
4
2 Working with Python
Python is an interpreted high-level programming language for general-purpose programming. For
finance related purposes, Python allows us to process data with complex formulas, similar to what
VBA does in MS-Excel, but with fewer limitations and more access to predefined functions. In
addition, it allows us to work with large tables and databases, that are difficult to manage with
MS-Excel. Moreover, it helps us to run our analysis repeatedly on different databases automatically.
Python has an extensive set of built-in functions (similar to those of MS-Excel), but you can also
work with more sophisticated functions that others have written/developed, via importing those
RI
specific Python Libraries (will talk about them shortly). Of course, you can define your own functions
as well.
In this teaching note, for pedagogical reasons, I do my best to draw analogies between Python and
MS-Excel. This undersells the vast capabilities in Python, but I believe will help you to better
adapt to this programming language.
BA
Functions are called similar to MS-Excel: FUNCTION_NAME( input_variables). For example, the
print function prints the value of the input in the IPython Console:
Hello world!
AK
More generally, functions can have multiple input variables (separated with commas) and can gen-
erate multiple output variables. In general, the syntax is:
In the example below, we are giving (parsing) 3 input variables to the min (minimum) function:
[2]: -1
A.
To access the help and documentation of each function, in the IPython console or in the Editor,
select a function (double click on it) and press Ctrl+I. This will call the help on that function and
show it in the Source window. You can also type help(FUNCTION_NAME) in the console. For example
for the min function:
[3]: help(min)
min(...)
min(iterable, *[, default=obj, key=func]) -> value
5
min(arg1, arg2, *args, *[, key=func]) -> value
I
You can also search for documentation for the function in the search bar in the HELP window.
Lastly, help can also be shown automatically after writing a left parenthesis next to an object. You
AR
can activate this behavior in Preferences > Help.
Note that Python is case sensitive and if you type Min, you will receive a NameError, which means
python cannot recognize this function.
The full details of these are easier to comprehend when we face an application. Below, I just list
some important pointers so that you can start working with Python.
A
In MS-Excel, each cell is a variable (e.g. A2 or B17). In Python, we directly give a name to the
variables we use (this can also be done in MS-Excel). For instance:
[5]: A = 3
A.
B = 5
C = A+B
print(A,B,C)
3 5 8
You can also just type the name of the variable and run it. e.g.
6
[6]: C
[6]: 8
In this document, I use both of these approaches to display the output values.
• They can only contain numbers, letters (both upper and lower), and underscores. They cannot
I
have special characters such as $.
• Also variable names cannot have space in them (use underscore instead).
AR
• Names must begin with a letter or an underscore. They cannot start, for instance, with a
number.
• Names are CaSe SeNsItIve. That is, variables A and a are different variables.
• Reserved Words: The following names are Python keywords. They
have special meaning and shouldn’t be used as variable names:
as, or, in, is, if, and, del, not, try, for, def, from, elif, with, else, exec,
pass, while, yield, break, print,class, raise, global, assert, except, import,
lambda, return, continue, finally
KB
If you define/use them in your code as a new variable, you’ll get a syntax error. Therefore, the
following codes will not execute:
x: = 1.0
1X = 1
X-1 = 1
for = 1
A
Some of the most commonly used variables are (we will see the more complex ones later in the
course):
Caution: Notice that large numbers never include commas. We can ask Python to show them with
commas (e.g. 123,456,789) but we do not input them with commas (e.g. 123456789). In MS-Excel,
if you input 123,456,789 in a cell, Excel guesses that this is a number and automatically stores it
as a number without asking the user. See the subsection for printing numbers to learn about how
to ask python to print numbers with commas use as below
7
[7]: a = 123456789
print(a)
123456789
Real numbers are numbers with decimal values. You can ask Python to show only a few decimals
of the number (similar to the MS-Excel Data Type). See subsection printing numbers.
I
[8]: a = 13.56798
AR
print(a)
13.56798
Strings are similar to the TEXT data type in MS-Excel. In order to make a string we may use a
single quote sign ' or double quotations ". Either works equally well. But if the string contains
one, we need to use the other. For example
If you need both a ' and a " in your string, you can use the escape character which tells Python
that the following character is to be taken as the literal character and is not a quote to delimit the
string. See it in action escaping the ” below:
Recall, the syntax for each function is FUNCTION_NAME(in1,in2,...). There are no commas be-
A.
tween parentheses. So Python assumes there is one input. The input is not a number (integer or
float) nor a string (it is not in " or '). So, Python concludes that you want to print the values of
variable x.
Note: Going back to our initial example, print("Hello world!"), the input to the print function
is the string “Hello world!”. If we do not put this string in ", we receive a syntax error
8
File "<ipython-input-345-01a3c6a17cb8>", line 1 print(Hello world!)
ˆ SyntaxError: invalid syntax
The error message is pointing to w in world. Why? Because you are not parsing a number or a
string. So, Python concludes that you are parsing a variable to be printed but the name of this
variable has space in it (before world), and spaces are not allowed for names. Ok, let’s substitute
space with an underscore print(Hello_world!)
I
[11]: #print(Hello_world!)
AR
ˆ SyntaxError: invalid syntax
Again, we get a syntax error but this time it is pointing to !. Recall, variable names cannot have
special characters such as !. Ok, let’s remove it, print(Hello_world)
[12]: #print(Hello_world)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
KB
<ipython-input-692-fabb9b810891> in <module> ----> 1 print(Hello_world) NameError:
name 'Hello_world' is not defined
And now we get a Name Error, suggesting that we want to work with (print) a variable that we
have not defined it before.
List allows you to have a group of variables in one variable. This is similar to selecting a series of
cells (in a row or column) in MS-Excel. For example
A
Hampshire",1323459],["Rhode Island",1051511],["Vermont",626630]]
[13]: x1 = True
y1 = False
9
2.2 Working with Numbers
2.2.1 Basic arithmetic operations on numbers
I
[14]: a = 5
b = 2
AR
x1 = a + b
x2 = a * b
x3 = a ** b
x4 = a / b
x5 = a // b
x6 = a % b
print("x1=",x1, "; x2=", x2, "; x3=",x3, "; x4=",x4, "; x5=",x5, "; x6=",x6)
KB
x1= 7 ; x2= 10 ; x3= 25 ; x4= 2.5 ; x5= 2 ; x6= 1
The usual laws of arithmetic hold with respect to the priority of the operations. For example,
[15]: x7 = x1 + x2*x3**x4/x5
print(x7)
A
15632.0
x
x2 x3 4
x7 = x1 + x5
10 252.5
=7+ 2
10 3125
=7+ 2
A.
= 7 + 15625
x7 = 15632
[16]: x7 = x1 + ((x2*(x3**x4))/x5)
print(x7)
15632.0
10
2.2.3 Printing Numbers
The print() function allows you to modify the format of the printed values. The general syntax is
to write f'{Variable_name:Desired_Format}
For instance, if you want to show numbers with commas use as below:
[17]: a = 123456789
print(a)
I
print(f'{a:,}') # to change the format (f) of the displayed number (a) to␣
comma-separated (,)
,→
AR
123456789
123,456,789
If you want to print numbers in with several decimals, say 3, use as below:
[18]: a = 13.56798
print(a)
print(f'{a: .3f}') # to change the format (f) of the displayed number (a) to␣
float (f) with (rounded) 3 decimals
KB
,→
13.56798
13.568
If you want to print numbers in percentage with the % sign, and no decimals, use as below:
[19]: a = 0.57134545
print(a)
print(f'{a*100:.0f}','%') # multiply a by 100, and add \% sign
A
0.57134545
57 %
If you want to show numbers in accounting format, with 2 decimals and $ sign, use as below:
[20]: a = 8712345.6789
print(a)
A.
print('$',f'{a:,.2f}')
8712345.6789
$ 8,712,345.68
11
2.3 Working with Lists
2.3.1 Accessing items of a list
To get the ith element of the list, we use the following syntax: List_name[i-1]. In Python, the
index of a list (and array and matrix variables that we will see later) starts from 0. Example:
RI
[22]: print ("The 1st item of Alphabet is: ", Alphabet[0])
print ("The 2nd item of Alphabet is: ", Alphabet[1])
print ("The Last item of Alphabet is: ", Alphabet[-1])
BA
This is equivalent to the INDEX(array, row_num, [column_num]) function in MS-Excel, where
you can access a cell by giving the row and column number of a cell to access it. For example,
INDEX(A4:C10, 2, 3) returns cell C5.
[24]: x = []
• The len() function gives the length of a list variable (i.e. the number of items in the list).
This is similar to the COUNT() function in MS-Excel, which helps us get the length of an array
of cells. e.g.
[25]: x = len(Alphabet)
print("Alphabet has", x, "items.")
12
Alphabet has 6 items.
• One can insert an item in the List, at a certain place with the .insert feature,
• add an item to the end of the list with the .append feature
• If you want to remove an item from the list, use the .remove feature of the list:
[26]: [Link](2, 'Z') # add character `Z` as the 3rd item of Alphabet
[Link]('B') # add character `B` at the end of Alphabet
I
[Link]('f') # remove character `f` from Alphabet
AR
print(Alphabet)
• If you are interested to find the index of an item on the list, use the .index feature of the list:
[27]: print(Alphabet)
[Link]('c')
Similarly in MS-Excel, we can use the ROW() and COLUMN() functions to get the row and column
indexes of a cell.
[28]: A = [3,5,-1,2,0,10,11,0]
[Link]()
A
print("After sorting, A is:", A)
Note, .sort permanently change the order of your list. If you want to keep the original ordering
of the list but you need to sort it, say for just printing, you can use the sorted() function:
A.
[29]: numlist = [67, 54, 39, 47, 38, 23, 99, 91, 91, 70]
print("sorted numlist is:",sorted(numlist))
print("But numlist stays as-is:",numlist)
sorted numlist is: [23, 38, 39, 47, 54, 67, 70, 91, 91, 99]
But numlist stays as-is: [67, 54, 39, 47, 38, 23, 99, 91, 91, 70]
13
• use in feature to check if an item belongs to a list. For example, running this statement will
return True, because Alphabet includes “a”.
[30]: True
I
"r" in Alphabet
[31]: False
AR
2.3.3 Copying Lists
Lists are mutable and so assignment does not create a simple copy. After the assignment the changes
to either variable affect both.
[32]: x = [1, 2, 3]
y = x
y[0] = 10
KB
print("y = ", y)
print("x = ", x)
y = [10, 2, 3]
x = [10, 2, 3]
This is because of the memory management of Python. Setting y = x, also sets the memory pointer
A
of y equal to that of the x. Therefore, if you change y, you are also changing x. This makes data
management more efficient by Python (but we don’t care about it in this class).
However, slicing a list creates a copy of the list and any immutable types in the list – but not
mutable elements in the list. This helps you preserve the value of x. In this way, simply copy the
content of x to y:
A.
[33]: x = [1, 2, 3]
y = x[:]
y[0] = 10
print("y = ",y)
print("x = ",x)
y = [10, 2, 3]
x = [1, 2, 3]
14
2.4 Converting Variable Types
Similar to MS-Excel. Sometimes variables are stored in the wrong format and we need to change
them. You can
[34]: type(Alphabet)
I
[34]: list
[35]: x1 = 40
AR
type(x1)
[35]: int
[36]: x2 = "30"
print("type of the variable x2 is", type(x2))
KB
type of the variable x2 is <class 'str'>
[37]: x3 = str(x1)
print("type of the variable x3 is",type(x3))
[38]: x4 = int(x2)
print("type of the variable x4 is",type(x4))
A.
[39]: x5 = float(x2)
print("type of the variable x5 is",type(x5))
Knowing the data type of the variables you are working with is important. For example since x2
and x3 are stored as a string (character) then x2+x3 is ‘3040’ (not 70)!
15
[40]: print(x2+x3)
3040
[41]: x = int(x2)+int(x3)
print(x)
I
70
AR
A KB
A.
16
3 Conditional Statement
3.1 IF statement
The concept of the IF statement is similar to the one in MS-Excel. There are three versions of the
IF conditions: (1) if - then, (2)if-then-else, (3)if-then-elseif-then-else.
In Python, the statement after the condition is considered the “then” statement of the IF statement
and there is no need to include the “then” argument. Simply add 4 spaces or 1 tab to open the
I
“then” arguments.
AR
1. Simple if-then statement: the syntax is as follows
IF CONDITION :
For example:
[42]: x = 5
if x > 0:
print("x equals to", x)
KB
print("x is positive")
x equals to 5
x is positive
IF CONDITION :
ELSE:
For example:
[43]: y = 0
A.
if y > 0:
print("y is positive")
else:
print("y is not positive")
y is not positive
3. if- then- else if- then, . . . Nested if statement can be implemented with elif command
(shorten version of else if). The syntax is as follows
17
IF CONDITION_1 :
ELIF CONDITION_2 :
ELSE:
I
WHAT TO DO IF CONDITION_1 & CONDITION_2 DO NOT HOLD
AR
[44]: z = 0
if z > 0:
print("z is positive")
elif z < 0:
print("z is negative")
else:
print("z must be 0")
KB
z must be 0
Note: Python uses = for assignment and == for testing equality. Whereas, in MS-Excel, you can
use = in the IF statement for testing equality. There you can use <> to test for the not equal
condition.
A.
• logical_and(): Both logical expressions are True. Alternatively use and or & between con-
ditions
• logical_or() : Either of the logical expressions is True. Alternatively use or between con-
ditions
• logical_not(): Not True. Alternatively, use not or ~ between conditions
18
There are equivalent logical operators in MS-Excel such as AND(), OR(), and NOT() functions that
can be used in the IF statement.
• Special case: for lists you can also use the in feature to check if an item belongs to it.
I
There is no "i" in "Team"!
AR
You can achieve similar functionality in MS-Excel too but it is less straightforward. You can use
the contain search option in a column that is already Filtered.
FOR DEFINED_RANGE :
For example, below, we ask Python to print “Financial Modeling is FUN with Python!” 5 times.
We use the RANGE function to specify the number of iterations:
[46]: # Example 1
A
for i in range(5):
print("Financial Modeling is FUN with Python!" )
The RANGE(a,b,j) function is similar to the SEQUENCE() function in MS Excel and uses a start
number a, a stop number b and a step size j. When not specified, a=0 and j=1. For example,
range(10) is equivalent of range(0,10,1) and range(2,10) is equivalent of range(2,10,1).
19
[47]: # Example 2
for i in range(2,10):
print(i,end=' ')
# the `end=' '` is used to avoid the enter at the end of each print.
# therefore the output is printed in one line:
2 3 4 5 6 7 8 9
I
[48]: # Example 3
for ct in range(2,9,2):
AR
print(ct,end=' ')
2 4 6 8
[49]: # Example 4
for ct in range(22,5,-3):
print(ct,end=' ')
22 19 16 13 10 7
KB
We can define a specific range by using iterating in a list:
[50]: # Example 5
for names in ["Sarah", "Ben","Moe"]:
print(names)
Sarah
Ben
Moe
A
for i in range(len(Companies)):
print('CEO of', Companies[i],'is',CEOs[i],'.' )
A.
Note that this is identical to the example below, where I use the enumerate function to also get the
index number in the CEOs list and assign it to the variable i:
20
[52]: # a more advanced example of 5
CEOs = ['Bharat Masrani','David I. McKay','Lloyd Blankfein']
I
CEO of RBC is David I. McKay .
CEO of Goldman Sachs is Lloyd Blankfein .
AR
Loops can also be nested (a FOR loop inside another FOR loop). Pay attention to the spacing at the
beginning of each line, in each loose
[53]: # Example 6
count = 0
x = range(10)
y = range(10)
KB
for i in x:
for j in y:
count = count + j
print(count)
450
Important: The iterable variable (in the above example x and y) should not be reassigned once
inside the loop.
A
A loop can be terminated early using break. The break command is usually used after an if
statement to terminate the loop prematurely if some condition has been met.
[54]: # Example 7
x = range(10)
for i in x:
A.
print(i,end=' ')
if i > 5:
break
0 1 2 3 4 5 6
21
A more readable implementation of the above example is with the use of while loops:
WHILE CONDITION_HOLDS:
Contrast the for statement with the while loop, we check a condition for each iteration. For
example:
[55]: i = 0
I
while i < 5:
print("Financial Modeling is FUN with Python!" )
AR
i = i+1
22
4 Define a Function:
In Python, you can define your own function, in a separate file for example, and call it. This will
help you with code-readability and debugging your code. You can test each function separately and
then merge them all.
DEF FUNCTION_NAME(INPUT_VARIABLE):
I
PROCESS INPUT_VARIABLE
AR
RETURN OUTPUT_VARIABLE (optional)
After defining your function, you should load it to Python. This way, Python learns about your
newly defined function. Then you can parse any input variable you like to the function, as many
times as you like. There are two ways to do so 1. Simple: copy the function you wrote in the
console and run it. 2. Complete: save it in a .py file (e.g. [Link]). Then load it similar to
a library (see the next section).
KB
Example: Write a function that calculates the total grade of a student in Commerce-3FD3 Finan-
cial Modeling, from his/her grade in 5 weekly assignments (10% in total), 2 midterm exams (40%
in total), final exam (40% in total), and class participation (10% in total)
return Total_Grade
Now that we have this function, we can run the above function to get the grade of any student. For
example
23
[57]: # Sarah's marks:
Total_Grade_3FD3([100,0,0,100,50],[80, 65],78, 80)
[57]: 73.2
I
[58]: 86.2
AR
I can now give the data for the whole class and get their total grade.
student_i_totalGrade =␣
Total_Grade_3FD3(student_i[1],student_i[2],student_i[3],student_i[4])
,→
student_i_totalGrade =␣
Total_Grade_3FD3(student_i[1],student_i[2],student_i[3],student_i[4])
,→
24
Section_4_totalGrades.append(student_i_totalGrade)
I
Furthermore, you can call a function inside another function.
AR
Example: Write a function to calculate Effective Annual rate of Return of a multi-period invest-
ment. Your function will receive the each year’s rate of return in a list and the output of your
function is the overall rate of return for the investment, annualized.
F V = P V (1 + R)
Therefore, starting with $ 1 investment, after t years, the wealth of the investor is:
KB
W ealtht = 1 × (1 + r1 ) × (1 + r2 ) × · · · × (1 + rt )
F V = P V (1 + EAR)T
1
T
EAR = W ealthT −1
#Input:
# Returns= n-list of annual rate of returns for n years
#Output:
# EAR = float
# Initialization
25
Wealth = 1 # This is the initial wealth of $1
I
EAR = Wealth ** (1/len(Returns)) - 1
AR
return EAR
0.2602976156686305
4.34%
A
A.
26
5 Libraries
In Python, many tools (functions) are not automatically loaded and are in modules called libraries.
To be able to use the functions in a library, you first need to load the library. It is recommended to
only load libraries that you need in each of your codes. In this way, you keep your programs smaller
when they aren’t needed.
There are a large number of libraries available for Python. Often you need to search online to find
I
the proper library and the proper functions in that library to get the desired job done. For the
purpose of this class, I shortly introduce a set of widely used libraries and their functions that help us
AR
with financial modeling, such as NumPy, Pandas, MatPlotLib and SciPy. These libraries are already
installed on your devices once you install Python with Anaconda. There are two other libraries
that we use frequently and are not already installed: NumPy-Financial and Pandas_DataReader.
Below, I show how to install a library on Python.
Similarly, type pip install pandas_datareader to install this library. Update Recently, Yahoo
made changes to their API that broke compatibility with previous pandas datareader versions.
As a short-term solution, I use functions in yfinance library (for more on this, see the section
related to pandas datareader below). Therefore, we need to also install this library. In Anaconda
Prompt(Anaconda3) type pip install yfinance to install this library.
27
5.2 Working with libraries
To get the documentation for a library, go to [Link]/3/ and search for the name of that
library (say NumPy). Go to that library and review the functions there. You can alternatively, go to
Help>Python Documentation in Spyder and search NumPy in the index. You see the same thing.
[64]: import numpy_financial # which will load the library named NumPy-Financial. or
I
import numpy_financial as npFin # you can assign a short name for the library␣
,→ too.
AR
Then you can call the functions inside the library (here the NumPy-Financial library) by
LIBRARY_NAME.FUNCTION_NAME(INPUT). This way, Python will not mix two functions with simi-
lar names that are in different libraries.
You can use the dir() command to get the list of functions in each library. For example, after
importing the library (say NumPy-Financial), in the IPython console type dir(NumPy_Financial)
to get the list of functions in the Numpy library. ‘ For example
KB
[65]: import numpy_financial as npFin
dir(npFin)
[65]: ['__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
A
'__package__',
'__path__',
'__spec__',
'__version__',
'_financial',
'fv',
A.
'ipmt',
'irr',
'mirr',
'nper',
'npv',
'pmt',
'ppmt',
28
'pv',
'rate']
These functions are equivalent to MS-Excel functions for time value of money. I will discuss them
in the section for the NumPy-Financial library.
Below, I will introduce NumPy (which offers a series of mathematical calculation functions),
NumPy-Financial (for its time value of money functions), Pandas (for data processing in the spread-
I
sheet format), Pandas_DataReader (for downloading financial data from the web), Matplotlib (to
plot graphs), SciPy (for optimization functions), and Statsmodels (for statistical analysis). This
AR
is a basic introduction so that you can start working with these libraries. You may need to consult
their documentation webpages or online forums for a more indepth understanding.
A KB
A.
29
5.3 NumPy Library
NumPy is a powerful library that allows data processing on large, multi-dimensional arrays and
matrices. Moreover, it provides a large collection of high-level mathematical functions to operate
in these arrays. Most other libraries use NumPy functions in the background. So, I start with it.
NumPy works exclusively with numbers (integer and float). There is also NaN, or Not a Number,
which happens if the input data has missing observations, similar to the blank cells in MS-Excel, or
RI
whenever a function produces a result that cannot be clearly evaluated to produce a number. There
are also inf, which represents infinity, and -inf, which represents the negative infinity. These might
happen, for instance, if you divide by a very very small number, closed to 0.
NumPy provides the core data types for numerical analysis – arrays and matrices, which are both
similar to lists, but arrays and matrices, unlike lists, are always rectangular so that all dimensions
• Matrices always have 2 dimensions, whereas Arrays can have 1, 2, 3 or more dimensions
(although in this course we mostly use 2-dimensional arrays to be consistent with the MS-
Excel examples)
AK
– This means that a 1 by n vector stored as an array has 1 dimension and n elements, while
the same vector stored as a matrix has 2-dimensions where the sizes of the dimensions
are 1 and n (in either order).
• Matrices follow the rules of linear algebra for multiplication.
– Standard mathematical operators on arrays operate element-by-element, similar to MS-
Excel. This is not the case for matrices, where multiplication (*) follows the rules of
linear algebra (this makes coding more concise).
• The best practice is to use arrays and to use the @ symbol for matrix (linear algebra) multi-
plication.
A.
30
An example of a 1-row-5-column vector (1x5):
[0. 1. 2. 3. 4.]
RI
[5. 6. 7. 8. 9.]]
You can find the dimension of your array/ matrix with the shape() function:
[68]: print([Link](Y))
(2, 5)
Y is an array of 2 by 5
[70]: X = [Link]([[1.0,2.5],[3.4,4.6]])
Y = [Link]([[5.0,6.0],[7.1,8.9]])
Z1 = [Link]((X,Y),axis = 0) # stack x on top of y
print("Z1 = ", Z1)
Z1 = [[1. 2.5]
A.
[3.4 4.6]
[5. 6. ]
[7.1 8.9]]
31
Z = [[ 1. 2.5 3. 7.5 -2. -5. ]
[ 3.4 4.6 10.2 13.8 -6.8 -9.2]
[ 5. 6. 15. 18. -10. -12. ]
[ 7.1 8.9 21.3 26.7 -14.2 -17.8]]
In MS-Excel, you cannot automatically concatenate arrays or matrices. You can manually copy and
paste the cells you want. This could be a very time consuming process, if you are working with
several sheets. Also you might make mistakes.
I
5.3.4 Accessing elements of Arrays
AR
Similar to Lists, you can access each item of an array by indexing:
10.2
[[5. 6. ]
KB
[7.1 8.9]]
You can reshape your array using the reshape() function, as follows:
A
[76]: W = [Link](Z,(2,12))
print("W is a reshaped matrix of Z, which is", [Link](W)[0], \
"by",[Link](W)[1] )
# I used \ sign to break a long line of code into two shorter ones.
print(W)
A.
Basic Math Operations These operators in NumPy, listed below, behave similar to the core
Python, when both x and y are scalars (i.e., numbers):
32
• +, -, *, /, These add, subtract, multiply, divide.
When one of x or y is an array and the other is a scalar the operation is element by scalar. When
one of x or y is a vector array (1 dimentional) and the other is a matrix array (2 dimentional) the
operation is element by element per column. In this case, x and y should be the same number of
RI
columns. When both x and y are arrays the operation is element by element. In this case, x and y
should be the same size. See below for each example:
[77]: X = [Link]([[1,2],[3,4]])
Y = 5
V = X * Y
print("X =",X)
print("Y =",Y)
print("V = X * Y")
print(" = ",V)
BA
AK
X = [[1 2]
[3 4]]
Y = 5
V = X * Y
= [[ 5 10]
[15 20]]
Example: Multiply each element of a vector array by the elements of another array
[78]: X = [Link]([[1,2,3],[5,3,4]])
A.
Y = [Link]([7,8,9])
V = X * Y
print("X =",X)
print("Y =",Y)
print("V = X * Y")
print(" = ",V)
X = [[1 2 3]
[5 3 4]]
Y = [7 8 9]
33
V = X * Y
= [[ 7 16 27]
[35 24 36]]
[79]: X = [Link]([[1,2,3],[5,3,4]])
Y = [Link]([[7,8,9],[0,6,3]])
I
V = X * Y
print("X =",X)
AR
print("Y =",Y)
print("V = X * Y")
print(" = ",V)
X = [[1 2 3]
[5 3 4]]
Y = [[7 8 9]
[0 6 3]]
V = X * Y
KB
= [[ 7 16 27]
[ 0 18 12]]
[80]: V = X @ [Link](Y)
print("X =", X)
A
X = [[1 2 3]
A.
[5 3 4]]
Y transposed = [[7 0]
[8 6]
[9 3]]
V = X Matrix-multiply Y
= [[50 21]
[95 30]]
34
Recall Matrix multiplication is the sumproduct of each row of X by each column of Y. For example,
RI
etc
Sum, Product and Difference of an Array Similar to MS-Excel, in NumPy we have sum()
and product() functions that allow us to get the sum/product of all elements of an array. Let’s see
some examples:
Z =
[
[
[
[[
3.4
5.
7.1
1.
4.6
6.
8.9
2.5
10.2
15.
21.3
3.
13.8
18.
7.5
-6.8
-10.
-2. BA
-9.2]
-12. ]
26.7 -14.2 -17.8]]
-5. ]
AK
[82]: print([Link](Z)) # sum all elements of the array (output = 1 element)
77.0
[83]: print([Link](Z, axis = 0)) # sum the elements of each column, (output = 4␣
,→ elements)
[84]: print([Link](Z, axis = 1)) # sum the elements of each row (output = 3 elements)
A.
Numpy also has a function to create the cumulative sum, called cumsum. This is very useful for
financial applications, where each row is added with the above ones. You need to tell NumPy on
which axis you want it to run the summation:
35
[[ 1. 2.5 3. 7.5 -2. -5. ]
[ 4.4 7.1 13.2 21.3 -8.8 -14.2]
[ 9.4 13.1 28.2 39.3 -18.8 -26.2]
[ 16.5 22. 49.5 66. -33. -44. ]]
RI
[ 3.4 8. 18.2 32. 25.2 16. ]
[ 5. 11. 26. 44. 34. 22. ]
[ 7.1 16. 37.3 64. 49.8 32. ]]
prod() and cumprod() behave similarly to sum() and cumsum() except that the product and cu-
mulative product are returned.
Numpy does not have a similar function to MS-Excel’s sumproduct. There are several alternative
X = [[1 2 3]
[5 3 4]]
Y = [[7 8 9]
[0 6 3]]
The sumproduct of X and Y = [50 30]
This is a useful feature for portfolio constructions. See the example after this section
diff() generates the difference between elements of an array. This is useful when you want to get
A.
how much a company has grown from the previous year, for example, or compute the stock returns.
For instance,
Q= [Link](Z, 1, axis=0) will give the 1st difference between elements of array Z. In this case,
first row of Q is equal to the second row of Z minus the first row of Z. i.e.: Q[0, ] = Z[1, ] − Z[0, ]
and so on
36
[[ 2.4 2.1 7.2 6.3 -4.8 -4.2]
[ 1.6 1.4 4.8 4.2 -3.2 -2.8]
[ 2.1 2.9 6.3 8.7 -4.2 -5.8]]
print([Link](Z, 2, axis=0))
I
[[-0.8 -0.7 -2.4 -2.1 1.6 1.4]
[ 0.5 1.5 1.5 4.5 -1. -3. ]]
AR
Other Simple Math Functions for Array Similar to MS-Excel:
The stock price of the Coca-Cola company (KO) from 2016 to 2019, from Yahoo Finance, is shown
below. How much is its annual stock return in this period?
Pt + Dt − Pt−1
Rt =
Pt−1
A.
where, Pt and Dt are the closing price and dividends of the stock on period t, respectively. Note that
the above equation is the re-organized form of the time value of money equation F V = P V (1 + R)T ,
where the future value is the sum of the price of stock and its dividends and PV is the last period
price of the stock.
In Yahoo Finance data, the Adj Close column incorporates the effect of dividends and other dis-
tributions. It is also incorporates the effects of share splits. Therefore, we can simply use that
column.
37
For example, the rate of return in 2017 (i.e., from December 2016 to December 2017), we have
42.98 − 37.58
R2017 =
37.58
= 0.1437
Instead of calculating this, year by year, you can calculate this simply by replicating the diff
functionality in numpy:
I
[90]: Price_KO = [Link]([37.58,42.98,45.90,55.35])
AR
Return_KO = [Link](Price_KO)/Price_KO[:-1]
print("KO returns(in %) = ",[Link](Return_KO*100,2))
This approach can be used with multiple stocks. Simply store stock prices in different columns of
a matrix and use the same lines of code.
Log-returns have some convenient theoretical and computational characteristics but do not precisely
depict period-to-period performance, especially in longer horizons, such as a year. We can calculate
them in python using the diff and logfunctionalities of numpy:
A portfolio is a collection (basket) of financial investments like stocks, and bonds. It is commonly
identified by its ingredients (what is in the basket) and weight of them (portion of each item in the
A.
basket).
For instance, suppose you construct a portfolio with initial wealth of $ 4967.80 in December 2017.
You decide to buy 5 shares of IBM Corp., 100 shares of Tesla Inc., 30 shares of Apple Inc., and 10
shares of Microsoft Corp. stocks.
The price of these stocks in December 2017 were, respectively $ 153.42, $ 20.76, $ 42.31, $ 85.54.
Meaning your portfolio (P) is:
38
[92]: initial_wealth = 4967.80
prices = [Link]([153.42,20.76,42.31,85.54])
nb_shares = [Link]([5,100,30,10])
weights = nb_shares * prices / initial_wealth
print([Link](weights*100,2))
I
That is, you invested about 15% of your portfolio money in IBM Corp., 41% in Tesla Inc., 25% in
Apple Inc., and 17% in Microsoft Corp. stocks.
AR
n o
P = (IBM ; 15.44%) , (T SLA; 41.79%) , (AAP L; 25.55%) , (M SF T ; 17.22%)
[93]: print([Link](weights))
1.0
The returns on these stocks, including dividends and other distributions, in years 2018, 2019, 2020,
KB
and 2021 are shown below. What is the return on your portfolio in each year?
Recall, the return of a portfolio (Rp ) is the weighted average of the returns of the stocks (Ri ) in
that portfolio.
X
Rp = wi × Ri
i
So, the portfolio return, for example, for year 2018 is:
39
Instead of calculating this, year by year, you can calculate this simply by replicating the sumproduct
functionality:
−0.22 0.07 −0.05 0.21
I
0.24 0.26 0.89 0.58
R=
−0.01
7.43 0.82 0.43
AR
0.17 0.50 0.35 0.52
The performance of the portfolio (in %) for years 2018, 2019, 2020, and 2021 was
[ 1.87 47.3 338.69 41.42]
Of course, an alternative to approach is to calculate the returns of a portfolio from its value. That
is, first calculate the value of the find at the end of each period, then find its rate of return, period
over period.
X
F undV alue = N bSharesi × (P ricei + Dividendi )
A
i
F undV aluet − F undV aluet−1
Rp,t =
F undV aluet−1
Python has max(), min(), sum() functions to generate some basic descriptive statistics. For other
A.
descriptive statistics, such as mean, median, standard deviation , variance, . . . we can use functions
in NumPy Library:
These functions, similar to the sum() function allow you to choose the Axis or axes along which the
40
statistics are computed. The default is to compute the flattened array.
Example:
RI
print("the maximum of nlist is",max(nlist))
print("the minimum of nlist is",min(nlist))
print("sorted nlist is:",sorted(nlist), \
", the min is the first item:",sorted(nlist)[0],\
", and the max is the last item of the list:", sorted(nlist)[-1])
print("the sum of nlist is",sum(nlist))
avg_rlist = [Link](rlist)
print("The average of rlist is ",avg_rlist)
41
The standard deviation of rlist is 19.27059523772752
The variance of rlist is 371.3558408163265
System of Equations
Example Consider the following problem where we have 3 equations and 3 unknowns (a, b, c)
I
11 = a1+b2+c4
AR
12.5 = a 3.4 + b 4.4 + c 9.1
3.3 = a 2.2 + b 1.9 − c 0.5
To solve this, first define arrays Y , as vector of left-hand side yi variables, and X as the matrix of
right-hand side xij variables.
y1 = a x11 + b x21 + c x31
y2 = a x21 + b x22 + c x23
y3 = a x31 + b x32 + c x33
KB
where,
11
Y = 12.5
3.3
1 2 4
X = 3.4 4.4 9.1
A
2.2 1.9 −0.5
Then use the solve function from the linear algebra package of NumPy ([Link]). As numerical,
below I solve the above system of equations with the following values for Y and X:
[99]: X = [Link]([[1,2,4],[3.4,4.4,9.1],[2.2,1.9,-0.5]])
Y = [Link]([[11],[12.5],[3.3]])
A.
[Link](X,Y)
[99]: array([[-9.47696477],
[12.42276423],
[-1.09214092]])
42
5.3.9 Random Variables
In Python, you can generate random values with different distributions using the functions in the
NumPy library ( for more advanced random number generators you may want to use the functions
in the RANDOM library.)
I
This is the case if you want to generate a random number in a range (between a and b) with equal
probability. The function rand() generates uniform random variables between [0, 1), including 0
AR
and excluding 1, each time you call it. Example:
[100]: [Link]()
[100]: 0.2362809224088045
You may create several random numbers by calling this function once. The function can create an
array of the given shape, for example: a 3-by-2 matrix
KB
[101]: [Link](3,2)
This is the case if you want to generate a random INTEGER number in a range (between a and
b) with equal probability. One way to generate such numbers is to use the randint function. For
A
43
You can modify the chance (probability) of the Head or Tail events. Below I set the probability of
a Head = 70% (therefore we get more Head outcomes)
I
• Normal distribution (bell curve)
Most random variables in the world are normally distributed (following central limit theorem). A
AR
normally distributed random variable is defined with its mean (the center of the bell curve) and
its standard deviation (the dispersion around the mean of the bell curve). The randn() function
generates a standard normal random value with mean 0 and standard deviation 1. The output of
this function ranges from -inf. to +inf.
[105]: [Link]()
[105]: -0.4241437027639983
KB
Similar to the rand() function, you can generate an array of random normal variables:
[106]: [Link](3,2)
Example: Let’s experiment with random generating functions, as an example. The following
A
example builds a sentence using various parts of speech. It randomly chooses words from a list by
using [Link](). We have used a method of the string data type to capitalize the first
letter of the sentence.
adverbs =["handily","sweetly","sourly","gingerly","forcefully","meekly"]
articles=["a","the","that","this"]
def sentence():
article = [Link](articles)
noun = [Link](nouns)
verb = [Link](verbs)
44
adverb = [Link](adverbs)
our_sentence = article + " " + noun + " " + verb + " " + adverb + "."
our_sentence = our_sentence.capitalize()
print(our_sentence)
I
[108]: # call the function to print the random sentence:
sentence()
AR
That baby faints handily.
Note: The numbers that these functions generate are from a pseudorandom sequence of numbers.
These numbers appears to be statistically random, despite having been produced by a completely
deterministic and repeatable process. You may set the sequence, using [Link](n) where
n is an integer number of your choice, to get the same number each time you run the random
generator function. This way, the results of your code can be reproduce at a later time.
Example: Without setting the seed generate a random number from a normal distribution. Repeat
KB
this experiment one more time and print the results. The two numbers are not identical (each look
random). Now, set the seed to an arbitrary number, say n = 5. Then draw a random number from
the same distribution for one more times. Repeat this for another time (set the seed and draw the
random number). The two numbers are identical (although each is a random number)
[109]: print([Link]())
print([Link]())
0.26556179713890227
A
-0.7231357148553637
[110]: [Link](5)
print([Link]())
[Link](5)
print([Link]())
A.
0.44122748688504143
0.44122748688504143
45
5.4 NumPy-Financial
The NumPy-Financial Python package is a collection of Time Value of Money functions. These
functions were copied to this package from version 1.17 of NumPy.
You need to first install this library as it is not included in the main package during the installation.
Launch Anaconda Prompt (from the Start menu of Windows or the Mac equivalent) then type/run:
pip install numpy-financial. It will download and install it on your machine.
I
The functions in this library are:
AR
dir(npFin)
[111]: ['__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
KB
'__package__',
'__path__',
'__spec__',
'__version__',
'_financial',
'fv',
'ipmt',
'irr',
A
'mirr',
'nper',
'npv',
'pmt',
'ppmt',
'pv',
A.
'rate']
These are similar to the time value of money functions in MS-Excel. Let’s see some examples:
• npv(rate, values)
46
manually subtract the initial cash flow to calculate the net present value
Example suppose you are asked to calculate the NPV of a project with initial investment of $ 10
Million. The cash flows in next 5 years are -2,1,3,10,20 million dollars. Your required rate of return
is at least 10%:
I
NPV = [Link](DiscountRate,CashFlow)
AR
print('Net Prsent Value is ${:,.2f}'.format(NPV))
# Notice that I am using a formatting command
# this changes the formatting of the output to currency, with 2 decimals.
Similarly, pv() calculates the present value of future cash flows. The cash flows could be an annuity
KB
payments (pmt) that are paid at certain dates (such as loan payments), or could be a lump sum
payment (fv) paid at maturity (such as the principal of a zero coupon bond), or could be both (such
as corporate bonds).
Similar to the MS-Excel PV function, the argument when defines if it is an Annuity due, where
the payments are at begins of each period (such as rent and insurance payments), or Ordinary
Annuity, where payments are paid at the end of each period (such as mortgage and loan payments).
Choose when = {'begin', 1} or when = {'end', 0} depending the application. The default value
is end. The default value of fv is 0.
A
Example Suppose you are leasing a car. The cash value of the car is $ 25,000 (after tax and fees).
The car dealer offers you the following: monthly payments of $ 400 for 3 years, with an interest rate
of 1.99% APR. After 3 years, you have the option to pay $ 15,000 and own the car. Is this a good
deal?
A.
That is, if you accept the dealer’s offer, including the time value of money, you are paying more
than $ 28,000 for a car which is worth only $ 25,000‘. So this is a bad deal.
47
• fv(rate, nper, pmt, pv, when='end') calculates the future value of a stream of cash flows.
• rate(nper, pmt, pv, fv, when='end', guess=None, tol=None, maxiter=100), finds the
required rate of return. For this class, you can ignore the following arguments: guess, tol,
and maxiter.
• nper(rate, pmt, pv, fv=0, when='end') computes the number of periodic payments of an
annuity.
I
• pmt(rate, nper, pv, fv=0, when='end') computes the payment against loan principal plus
interest. Similarly, ipmt() computes the interest portion of a payment and ppmt() computes
AR
the payment against loan principal.
• irr(CashFlows) calculates the internal rate of return for a stream of future cash flows (the
first element is the initial investment with a negative sign). mirr() is for modified IRR
calculation. KB
A
A.
48
5.5 Pandas Library
Pandas library is an increasingly important component of the Python scientific stack. It is a high-
performance package that provides a comprehensive set of data structures for working with data.
pandas also provides high-performance, robust methods for importing from and exporting to a wide
range of formats, such as xlsx and csv files. Data structures in Pandas allow for labeling columns
and rows, which is not available in NumPy. Moreover, it allows for relational-database operations
(such as join), which are provided in MS-Access.
I
[114]: import pandas as pd
AR
5.5.1 Data Structures
The most important data structures in pandas are Series and DataFrames. Series are the equivalent
of 1-dimensional arrays. DataFrames are collections of Series and so are 2-dimensional (like MS-
Excel spreadsheets).
Series Series are the primary building block of the data structures in pandas, and in many ways
a Series behaves similarly to list and can have both number and text.
KB
[115]: # Panda Series
s = [Link]([5.4, 6.6, 53, 'book', 4.5,"apple",1])
s
[115]: 0 5.4
1 6.6
2 53
3 book
A
4 4.5
5 apple
6 1
dtype: object
First, notice that the data type of s is shown as object. This means that the pandas series has
A.
elements with a type of string. Second, as we see in the printed values in this example, pandas has
automatically generated a label (index) for the series (the first column above) using the sequence 0,
1, . . . . This is similar to the row number in MS-Excel. You can use these indexes when printing
or accessing the data. for example, print items with index 0 and 3:
[116]: s[[0,3]]
49
[116]: 0 5.4
3 book
dtype: object
DataFrame DataFrames collect multiple series in the same way that a spreadsheet collects mul-
tiple columns of data. A DataFrame is composed of Series and each Series has its own data type,
and so not all DataFrames are representable as homogeneous NumPy arrays.
I
A number of methods are available to initialize a DataFrame. The simplest method uses a homo-
geneous NumPy array.
AR
[117]: a = [Link]([[7.0,8,9],[3,4,5]])
df = [Link](a)
df
[117]: 0 1 2
0 7.0 8.0 9.0
1 3.0 4.0 5.0
KB
A DataFrame contains column labels and row labels (similar to row number and column headings
in MS-Excel). When none are provided, the numeric sequence 0, 1, . . . is used. In the above
example, we have three columns that are labeled 0, 1 and 2. There are also two rows labeled as 0
and 1. Labels are useful when working with large DataFrames, where you do not need to remember
which column stores what values. We can assign column and row names by columns and index
commands:
Example Going back to our example for the grades in Section 4 of Commerce 3FD3:
A
[118]: Section_4_marks = [
[100,0,0,100,50,80, 65,78, 80],
[100,100,100,100,100,80, 75,88, 100],
[100,0,0,100,50,80, 65,78, 80],
[0,0,0,100,50,50, 45,38, 20]
]
A.
df
50
[118]: A_1 A_2 A_3 A_4 A_5 Mid_1 Mid_2 Final Participation
Sarah 100 0 0 100 50 80 65 78 80
Rose 100 100 100 100 100 80 75 88 100
Ben 100 0 0 100 50 80 65 78 80
John 0 0 0 100 50 50 45 38 20
Then it is easier to access columns or rows of the DataFrame. You can select a column by giving
its name in [], for example:
I
[119]: df['Mid_1']
AR
[119]: Sarah 80
Rose 80
Ben 80
John 50
Name: Mid_1, dtype: int64
If you want to access a list of columns, say both Mid_1 and Mid_2, put them in a list. e.g:
KB
[120]: df[['Mid_1','Mid_2']]
[121]: [Link]['Sarah']
A_5 50
Mid_1 80
Mid_2 65
Final 78
Participation 80
Name: Sarah, dtype: int64
51
[122]: [Link]['Sarah','Final']
[122]: 78
If you don’t know the index of an item, you can also access it via its row or column number via the
.iloc[] feature:
[123]: [Link][1,0]
I
[123]: 100
AR
or alternatively
[124]: [Link][1]['A_1']
[124]: 100
You can define a new column for the DataFrame using ['new column name']. For example, below
I calculate the total grade for each student:
[125]: A_1 A_2 A_3 A_4 A_5 Mid_1 Mid_2 Final Participation total_grade
Sarah 100 0 0 100 50 80 65 78 80 73.2
Rose 100 100 100 100 100 80 75 88 100 86.2
A
Ben 100 0 0 100 50 80 65 78 80 73.2
John 0 0 0 100 50 50 45 38 20 39.2
As we see above, you may simply add/subtract columns or multiply with a constant number. You
may multiply/divide Series or dataFrame similar to numpy arrays; These operations will be executed
element by element, but considering the index/column names. If you are multiply two pandas Series,
they should have the same indexes. For instance, if we want to multiply grades of assignment 5 and
A.
[126]: df['A_5']*df['Mid_2']
52
dtype: int64
Here, both df['A_5'] and df['Mid_2'] are pandas Series and have the same index labels (which
are the student names). Now, let’s define a new Series and call it S2, using the same values of
column Mid_2 but without defining the index labels. E.g.,
[127]: S2 = [Link]([65,75,65,45])
print(S2)
I
df['A_5']*S2
AR
0 65
1 75
2 65
3 45
dtype: int64
[127]: 0 NaN
1 NaN
2 NaN
KB
3 NaN
Ben NaN
John NaN
Rose NaN
Sarah NaN
dtype: float64
As we see, this multiplication for Pandas is ambiguous; the two Series do not share index labels.
A
Therefore, the output is not the element by element multiplications but a combination of the index
labels. In this case, you may convert S2 to a numpy array and then multiply.
[128]: df['A_5']*S2.to_numpy()
Ben 3250
John 2250
Name: A_5, dtype: int64
When multiplying/dividing Pandas DataFrame, they have to have the same column names. For
instance, let’s scale the grade of the class with respect to Rose’s marks:
[129]: df/[Link]['Rose']
53
[129]: A_1 A_2 A_3 A_4 A_5 Mid_1 Mid_2 Final Participation total_grade
Sarah 1.0 0.0 0.0 1.0 0.5 1.000 0.866667 0.886364 0.8 0.849188
Rose 1.0 1.0 1.0 1.0 1.0 1.000 1.000000 1.000000 1.0 1.000000
Ben 1.0 0.0 0.0 1.0 0.5 1.000 0.866667 0.886364 0.8 0.849188
John 0.0 0.0 0.0 1.0 0.5 0.625 0.600000 0.431818 0.2 0.454756
There are also sub(),add(), div(), and mul() functions in pandas, for subtraction, addition, divi-
sion, and multiplication, where you would have more flexibility in executing these operations.
I
If you want to access the names of the columns:
AR
[130]: [Link]
And if you want to access the names of the rows (i.e. indexes)
[131]: [Link]
KB
[131]: Index(['Sarah', 'Rose', 'Ben', 'John'], dtype='object')
A
A.
54
Merge and Join DataFrames Pandas’ merge() and join() fucntions provide SQL-like opera-
tions for merging the DataFrames using row labels or the contents of columns. In MS-Excel, you
can use the XLOOKUP function (or other similar lookup functions) for merging.
The primary difference between the two is that merge() by default uses column contents while
join() defaults to using index labels. Both commands take a large number of optional inputs. The
important keyword arguments are:
• how, which must be one of ‘left’, ‘right’, ‘outer’, ‘inner’ describes which set of indices to use
when performing the join.
RI
– ‘left’ uses the indices of the DataFrame that is used to call the method and ‘right’ uses
the DataFrame input into merge or join.
– ‘outer’ uses a union of all indices from both DataFrames and ‘inner’ uses an intersection
from the two DataFrames.
• on is a single column name or list of column names to use in the merge. on assumes the names
BA
are common. If no value is given for on or left_on/right_on, then the common column
names are used.
– left_on and right_on allow for a merge using columns with different names. When
left_on and right_on contain the same column names, the behavior is the same as on.
• left_index and right_index indicate that the index labels are the join key for the left and
AK
right DataFrames.
Below, I try to explain each of these merges. First, I define two arbitrary DataFrames, called
df_left and df_right.
df_right = [Link]([['Sarah',80],['Rose',30],['John',100]],
columns=['Name','M_2'])
print('df_right:')
print(df_right)
df_left:
Name M_1
0 Sarah 100
1 Rose 50
2 Ben 75
55
3 Moe 25
df_right:
Name M_2
0 Sarah 80
1 Rose 30
2 John 100
Let’s merge the two DataFrames, by putting the values of df_right beside the values of df_left,
I
where the values of items in column Name of the two DataFrame are the same (since column Name
is shared, it will not be repeated). This is called the inner join:
AR
[133]: df_left.merge(df_right,on='Name', how='inner') # the default value for how =␣
'inner'. you may exclude this term too
,→
Notice that by the inner join, we lost rows 2 and 3 of df_left and row 2 of df_right, because
KB
their values in column Name are not shared.
left join allows us to keep the left DataFrame as-is and only add the values of df_right where
there is a match. For missing values pandas puts NaN if cannot find the match
2 Ben 75 NaN
3 Moe 25 NaN
Similarly, with the right join, we keep the values of df_right and add the values of df_left if
there is a match.
A.
Lastly, we have the outer join, which merges all values and puts NaN if cannot find the match
56
[136]: df_left.merge(df_right,on='Name', how='outer')
I
[Link]() and [Link]() are very similar. With [Link]() function merging is done on index
AR
of the dataFrames. This is very useful for merging timeseries dataFrames.
df_right = [Link]([80,30,100],
columns=['M_2'],index = ['Sarah','Rose','John'])
KB
print('df_right:')
print(df_right)
df_left:
M_1
Sarah 100
Rose 50
Ben 75
A
df_right:
M_2
Sarah 80
Rose 30
John 100
A.
57
5.5.2 Importing and Exporting Data via Panda
All of the data readers in pandas load data into a pandas DataFrame, which if need be you can
transfer to a NumPy array. In practice, the DataFrame is much more useful since it includes useful
information such as column names read from the data source.
Import Excel files Excel files, both 97/2003 (xls) and 2007/10/13 (xlsx), can be imported
using read_excel(). Two inputs are required to use read_excel(), the FILE_NAME and the
I
SHEET_NAME containing the data. In this example, pandas makes use of the information in the
Excel workbook that the first column contains dates and converts these to datetimes.
AR
[139]: TSLA_prices = pd.read_excel('VOO_TSLA_BTC_prices.xlsx',sheet_name='TSLA')
TSLA_prices
• header, an integer indicating which row to use for the column labels. The default is 0 (top)
row, and if skiprows is used, this value is relative.
• skiprows, typically an integer indicating the number of rows at the top of the sheet to skip
before reading the file. The default is 0.
• skip_footer, typically an integer indicating the number of rows at the bottom of the sheet
A.
Note: If the file that you want to import (read in) to Python is not located in
your working directory, you need to give the full path of the location of the file, e.g.,
C://Users//akbara23//Desktop//VOO_TSLA_BTC_prices.xlsx
58
Import CSV and other formatted text files Comma-separated value (CSV) files can be read
using read_csv(). CSV files are text-based files and do not have Excel meta data (formatting
embedded in cells; for example, they cannot have several sheets or cell formatting). Because of this,
for many software programs they are easier to connect to, compared to Excel. In Windows, CSV
files can be opened by NotePad or MS-Excel. With the latter, CSV files might look like an excel
file but note that unlike excel files, CSV files cannot have multiple sheets.
I
Export using Pandas Writing data from a Series or DataFrame is much simpler since the starting
AR
point (the Series or the DataFrame) is well understood by pandas. While the file writing methods
all have a number of options, most can safely be ignored.
[141]: # df.to_excel('FinancialModeling_marks.xlsx')
# df.to_excel('FinancialModeling_marks.xlsx', sheet_name='Section_4')
# df.to_csv('FinancialModeling_marks.csv')
These commands create a new excel file. If you want to write in several sheets of an excel file, you
need to take a few extra steps. First, you need to create an empty excel file and open it in order
KB
to write in it. Then you write your desired DataFrames in each excel sheet, as shown above. Then
you save and close the excel file.
df.to_excel(writer, sheet_name='Section_4')
TSLA_csv_data.to_excel(writer, sheet_name='TSLA_csv_data')
A
[Link]()
Summary If you want to have a preview of your data you can use head() or tail() functions
which return the first and last 5 rows of a DataFrame, respectively. This gives you a sense of the
data at a glance and usually is the first step:
59
[143]: Date Open High Low Close Adj Close Volume
0 2010-06-29 3.800 5.000 3.508 4.778 4.778 93831500
1 2010-06-30 5.158 6.084 4.660 4.766 4.766 85935500
2 2010-07-01 5.000 5.184 4.054 4.392 4.392 41094000
3 2010-07-02 4.600 4.620 3.742 3.840 3.840 25699000
4 2010-07-06 4.000 4.000 3.166 3.222 3.222 34334500
Similarly, you can use the tail() function to get the last 5 observations of a DataFrame.
I
Another useful function to get familiar with your datasets is the dtype feature; it gives the type of
AR
each column of the DataFrame. For example:
[144]: TSLA_prices.dtypes
Statistical Functions Simple statistical functions such as sum(), mean(), std(), var(), prod(),
median(), quantile(), abs(), cumsum() (cumulative sum), and cumprod() (cumulative product)
are available for Series and DataFrame. DataFrame also supports cov() and corr() – the keyword
argument axis determines the direction of the operation (0 for down columns, 1 for across rows).
A
describe() returns a simple set of summary statistics (number of non-missing observations, average,
standard deviation, . . . ). The output is a series where the index contains the names of the statistics
computed.
60
75% 63.509998 4.081550e+07
max 883.090027 3.046940e+08
Notice that we can get each of the above separately too. For instance, to get the average values,
you can use the mean() function:
[146]: [Link]()
I
[146]: Adj Close 9.237774e+01
Volume 3.173395e+07
AR
dtype: float64
Similarly, use std() to get the standard deviations, min() and max() to get minimum and max-
imum values of your DataFrame. count() returns the number of non-null values – that is, those
which are not NaN or another null value such as None or NaT (not a time, for datetimes). Use
quantile([0.25,0.50,0.75]) to get the 25th , 50th , and 75th percentile values.
Most NumPy functions are available in Pandas, but if needed you can convert a Pandas DataFrame
to a NumPy Array, using to_numpy() function. Then manipulate the data using NumPy functions
KB
[147]: ar = df.to_numpy()
print(ar)
[[4.77800000e+00 9.38315000e+07]
[4.76600000e+00 8.59355000e+07]
[4.39200000e+00 4.10940000e+07]
...
[6.46219971e+02 2.12971000e+07]
A
[6.60500000e+02 1.54427000e+07]
[6.55289978e+02 1.39108000e+07]]
Aggregation The simplest application of groupby is to aggregate statistics such as group means
or extrema. All aggregation functions will return a single value per column for each group. Many
common aggregation functions have optimized versions provided by pandas. For example, mean,
A.
sum, min, std, and count can all be directly used on a DataFrame GroupBy object.
Example let’s find the average price of Tesla’s stocks per year in our sample:
First, create a column for the year (more on the date functions later)
If the DATE column was stored as a string (str) in your DataFrame, df, you can change it to
Pandas Time-Date series, using pd.to_datetime([Link]). Then you can work with the above
61
dt() functions (more on dates below).
Second, Select the columns you are interested, and use the groupby function to group the data
[150]: grouped_data.mean()
I
[150]: Open High Low Close
AR
Year
2010 4.681600 4.815446 4.529215 4.668369
2011 5.364397 5.476048 5.238833 5.360952
2012 6.240624 6.361832 6.109176 6.233720
2013 20.883286 21.370603 20.355976 20.880246
2014 44.683079 45.489976 43.841222 44.665817
2015 45.966389 46.676333 45.254127 46.008580
2016 42.011690 42.686699 41.257230 41.953452
2017 62.859243 63.690119 61.937394 62.863259
KB
2018 63.436693 64.738725 62.110462 63.461984
2019 54.605627 55.529960 53.722508 54.706040
2020 289.108428 297.288412 280.697937 289.997067
2021 698.336739 712.457824 681.491378 697.165724
You can combine functions in DataFrame. For example, we can write second and thirds steps above
in one line. Below, I do so, and compute the total volume of traded Tesla stocks in each year:
[151]: TSLA_prices[['Year','Volume']].groupby('Year').sum()
A
[151]: Volume
Year
2010 1026845500
2011 1626175500
2012 1537245000
A.
2013 10593729500
2014 8711606500
2015 5441089000
2016 5811808500
2017 7950157000
2018 10808194000
2019 11540242000
62
2020 19052912400
2021 4279034100
I
TSLA_prices_2.tail()
AR
2780 2021-07-15 658.390015 666.140015 650.599976 650.599976 20209600 2021
2781 2021-07-16 654.679993 656.700012 644.219971 644.219971 16339800 2021
2782 2021-07-19 629.890015 647.200012 646.219971 646.219971 21297100 2021
2783 2021-07-20 651.989990 662.390015 660.500000 660.500000 15442700 2021
2784 2021-07-21 659.609985 664.859985 655.289978 655.289978 13910800 2021
drop(labels) drops rows based on the row labels in a label or list labels. For example, drop row
4 (California):
KB
[153]: TSLA_prices_3 = TSLA_prices_2.drop(2783, axis = 0) # the default axis = 0, so␣
,→ you may exclude this if you want to.
TSLA_prices_3.tail()
Similarly, you may use the dropna() function to drop missing observations.
Finally, drop_duplicates() removes rows which are duplicates or other rows, and is used with
the keyword argument drop_duplicates(cols=col_list) to only consider a subset of all columns
when checking for duplicates.
A.
df =
63
[154]: Firm_1 Firm_2 Firm_3
Region_1 70 80 5
Region_2 90 65 1
Region_3 74 95 3
Region_4 70 80 4
I
unique values of the first column of df = [70 90 74]
AR
[156]: df_drop_duplicates = df.drop_duplicates(['Firm_1','Firm_2'])
print('drop rows with duplicate values in the first and second columns: ')
df_drop_duplicates
drop rows with duplicate values in the first and second columns:
Sort sort_values() function sorts the contents of the DataFrame along either axis using the
contents of a single column or row. Passing a list of columns names or index values implements
lexicographic search. sort_index() function will sort a DataFrame by the values in the index.
Both support the keyword argument ascending to determine the direction of the sort (ascending
by default). ascending can be used with a list to allow sorting in different directions for different
sort variables.
A
Example Sort TSLA_prices by Volume and print the top 5 high volume days. Also, do not print
the following columns: Open, High, Low, Close.
[157]: TSLA_prices.drop(['Open','High','Low','Close'],axis=1).sort_values(by='Volume',␣
,→ascending=False).head()
64
5.5.4 Time-series Data
A TimeSeries data is basically a series where the index contains datetimes index values (more
formally the class TimeSeries inherits from Series), and the Series constructor will automatically
promote a Series with datetime index values to a TimeSeries.
TimeSeries have some useful indexing tricks. For instance, you can easily access all of the data for
a particular year using DataFrame_NAME.loc['yyyy-mm'] syntax where yyyy is the year and mm is
I
the month.
Example Show the closing stock prices and Volume of trades for Tesla in Feb. 2020:
AR
[158]: TSLA_prices.index = TSLA_prices['Date'] # use Date column as the index
TSLA_prices.drop('Date',axis=1,inplace=True)# drop the Date column. we don't␣
need it any more now
,→
Similarly, you can access to a year, month, a date, or a period of time of a TimeSeries, for example:
65
• The data for year 2020: TSLA_prices.loc['2020']
• The data for the month Feb., 2020:TSLA_prices.loc['2020-02']
• The data for teh day Feb. 27th, 20200:TSLA_prices.loc['2020-02-27']
• The data for the days in between Feb. 25th, 20200 and Mar. 20th, 20200:
TSLA_prices.loc['2020-02-25':'2020-03-20']
Once you have the time-series index, you can get the other date values. For example:
I
[159]: TSLA_prices['Year'] = TSLA_prices.[Link]
TSLA_prices['Quarter'] = TSLA_prices.[Link]
AR
TSLA_prices['Month'] = TSLA_prices.[Link]
TSLA_prices['Day'] = TSLA_prices.[Link]
TSLA_prices.head()
[142]: Open High Low Close Adj Close Volume Year Quarter Month Day
Date
2010-06-29 3.800 5.000 3.508 4.778 4.778 93831500 2010 2 6 29
2010-06-30 5.158 6.084 4.660 4.766 4.766 85935500 2010 2 6 30
KB
2010-07-01 5.000 5.184 4.054 4.392 4.392 41094000 2010 3 7 1
2010-07-02 4.600 4.620 3.742 3.840 3.840 25699000 2010 3 7 2
2010-07-06 4.000 4.000 3.166 3.222 3.222 34334500 2010 3 7 6
If you are importing an excel file into a pandas DataFrame and the DATE column in excel is stored
as a Date variable, the [Link]() function usually correctly detects the type of the DATE
column and stores it as date variable in the output DataFrame. However, if you are importing
from a text or csv files (in which all variables are stored as plain text) the DATE column will
also be stored as text (object) in the output DataFrame. So, if the DATE column was stored
A
as a string (str) in your DataFrame, df, you can change it to Pandas Time-Date series, using
pd.to_datetime([Link]). Then you can work with the above dt() functions.
Pandas’ to_datetime() guesses the date format in df but you may also provide the date format to
this function. For example, if the dates in the [Link] are stored as ‘YYYYMMDD’ you can use
the argument format=‘%Y%m%d’. See below
A.
See Python’s webpage on “datetime — Basic date and time types”, here
[Link] for more
information on date formats.
66
Rate of Returns A more useful application of TimeSeries data is for financial time series,
where we are interested in the growth rates (or rate of return) in a period. For example, in the
dataset above, we can calculate the daily returns of stock price. Growth rates are computed using
pct_change().
Pt + Dt − Pt−1
Rt =
Pt−1
where, Pt and Dt are the closing price and dividends of the stock on period t, respectively. In Yahoo
Finance data, the Adj Close column incorporates the effect of dividends and other distributions.
I
[161]: TSLA_prices['daily_returns'] = TSLA_prices['Adj Close'].pct_change()
AR
TSLA_prices.head().
drop(['Open','High','Low','Close','Volume','Year','Quarter','Month','Day'],axis=1)
Notice that our stock price data starts from June 29, 2010. So, we do not have the return data for
that date.
The pct_change() function can also calculate growth rates over a longer period. The keyword
argument periods constructs overlapping growth rates which are useful when using seasonal data.
For example, in our dataset, which is at the daily frequency, you can get the annual growth rate,
with respect to the 252 business days (the default value is 1)
A
[162]: Date
2021-07-15 1.104126
A.
2021-07-16 1.146484
2021-07-19 1.152861
2021-07-20 1.010043
2021-07-21 1.089093
Name: Adj Close, dtype: float64
Alternatively, you can re-sample the daily price data to annual data (end of year) and then compute
the annual returns:
67
[163]: price_annual = TSLA_prices.resample("1y").bfill()
price_annual['Adj Close'].pct_change().tail()
[163]: Date
2017-12-31 0.477165
2018-12-31 0.038280
2019-12-31 0.257001
2020-12-31 7.434370
2021-12-31 0.000000
RI
Freq: A-DEC, Name: Adj Close, dtype: float64
A number of string aliases are given to useful common time series frequencies. We will refer to these
aliases as offset aliases. For instance,
For the resample() function to operate correctly we need to specify to backward fill (.bfill())
or forward fill (.ffill()) the missing observations. See the examples below, where the daily data
ends in the middle of September. ffill() function uses the last observation per month (15th of
September) and uses it for the last day of the month. bfill() function does a backward fill and
since there is no observation for last day of the month, it gives NaN for the missing observation.
[164]: df = TSLA_prices.loc['2019-07-15':'2019-09-15']
A.
df1 = df['Close'].resample("1m").bfill()
df2 = df['Close'].resample("1m").ffill()
print('df1 = ', df1)
print('df2 = ', df2)
df1 = Date
2019-07-31 48.321999
2019-08-31 45.001999
2019-09-30 NaN
Freq: M, Name: Close, dtype: float64
68
df2 = Date
2019-07-31 48.321999
2019-08-31 45.122002
2019-09-30 49.040001
Freq: M, Name: Close, dtype: float64
The pct_change() function is very useful for analyzing the stock returns. Most financial databases
provide a time series for the price of a company and you can use this function to get the holding
I
period returns.
AR
The TimeSeries is also a useful data structure for plotting financial data. In the next sections, I
will explain the basics of plotting with Python.
The return of a portfolio (Rp ) is the weighted average of the returns of the stocks (Ri ) in that
portfolio.
KB X
Rp = wi × Ri
i
For instance, suppose you construct a portfolio by investing 80% of your money in VOO and 20% in
TSLA. Let’s find the annual return of your portfolio in 2018, 2019, 2020, and 2021.
,→ DataFrame
[Link] = df['Date']
# drop the Data columns except adj colose. we don't need them anymore now
[Link](['Date'],axis=1,inplace=True)
# find annual price data and then annual returns and drop the missing␣
,→ observations for the first year
69
df_annual = [Link]("1y").ffill().pct_change().dropna()
I
# # Then find the portfolio returns:
AR
# # Recall: Rp = sum (w_i * R_i)
df_annual['Rp'] = (df_annual * weights).sum(axis=1)
70
5.6 Pandas_datareader
This library allows you to download data from several websites such as Yahoo Finance (yahoo), US
Federal Reserve Economic Data (fred), Bank of Canada, World Bank, and Kenneth French’s data
library (famafrench). Check the documentation of the library for more details here: [Link]
[Link]/en/latest/remote_data.html
You need to first install this library as it is not included in the main package during the installation.
I
Launch Anaconda Prompt (from the Start menu of Windows or the Mac equivalent) then type/run:
pip install pandas_datareader. It will download and install it on your machine.
AR
The main function in this library is called DataReader() and its general syntax is:
DataReader(Symbol, Data_Source, start_date, end_date). * Symbol identifies what you want
to download, * Data_Source identifies the label (name) of the data vendor. See the library’s docu-
mentation for the full list of vendors that are compatible with Pandas_datareader. * start_date
and end_date identify the data period to be downloaded. If not identified, it will be the last 5
years.
In this class, we mostly use this library to get stock prices from Yahoo Finance website. But for
financial modeling you may also want to download Macroeconomic data (such as GDP) from the
KB
central banks’ website. Below, I list two examples:
Stock Prices We can use Yahoo Finance website to get the stock prices of companies using their
Ticker Symbols. The example below shows how to download the closing price for three companies:
# Define the instruments to download. We would like to see Apple, Microsoft and␣
the S&P500 index.
A
,→
71
panel_data = pdr.get_data_yahoo(tickers, start_date, end_date)
# Yahoo Finance gives 'High', 'Low', 'Open', 'Close', 'Volume', 'Adj Close'.
Close_Price = panel_data['Close']
Close_Price.head()
I
[*********************100%%**********************] 3 of 3 completed
AR
Date
2023-01-03 125.070000 239.580002 3824.139893
2023-01-04 126.360001 229.100006 3852.969971
2023-01-05 125.019997 222.309998 3808.100098
2023-01-06 129.619995 224.929993 3895.080078
2023-01-09 130.149994 227.119995 3892.090088
This will download data from Yahoo Finance, at the daily frequency. To get monthly data (prices
KB
at the end of each month), you need to re-sample from daily data every 1 month:
Note: Yahoo Finance recently made changes to their API that broke compatibility with previous
pandas datareader versions. As a solution, here I am using yfinance library to override on the
functions of pandas_datareader. You may directly use yfinance but since the issue might be
A.
Close_Price = panel_data['Close']
72
Close_Price.head()
[*********************100%%**********************] 3 of 3 completed
I
2023-01-05 125.019997 222.309998 3808.100098
2023-01-06 129.619995 224.929993 3895.080078
AR
2023-01-09 130.149994 227.119995 3892.090088
Economics Data We can use the website of the Federal Reserve Bank of [Link] (FRED), the
Bank of Canada, or World Bank. For example, below we can download US GDP data:
2021-07-01 23828.973
2021-10-01 24654.603
Portfolio Data from Fama French We can access the datasets on the Fama/French Data
Library ([Link] via Pan-
das_datareader. There are many useful portfolios and asset pricing factors in this data library
A.
(it has more than 250 datasets). We will work with some of these in this class.
The get the name of all datasets in this website, use .get_available_datasets() function. Below,
we can see the 5 first datasets.
73
get_available_datasets()[:4]
[170]: ['F-F_Research_Data_Factors',
'F-F_Research_Data_Factors_weekly',
'F-F_Research_Data_Factors_daily',
'F-F_Research_Data_5_Factors_2x3']
Let’s dig in more deeply and choose ‘5_Industry_Portfolios’ to get the data for the top 5 sectors
I
in the US: Consumers, Manufacturing, High Tech., Health care, and others.
AR
[171]: Industry_Portfolios = pdr.get_data_famafrench('5_Industry_Portfolios')
In the Variable Explorer tab click on Industry_Portfolios and click on DECR to get the description
of the data that you just downloaded.
This file was created by CMPT_IND_RETS using the 2021-June CRSP database (CRSP dataset
is accessible from the WRDS portal- if you have not done so, you may get your credentials from
[Link] and your MAC emails). It contains the value- and equal-
weighted returns for 5 industry portfolios. The portfolios are constructed at the end of June.
KB
The annual returns are from January to December. Missing data are indicated by -99.99 or -999.
Copyright 2021 Kenneth R. French
To compute the value-weighted average portfolio returns (index 0), the dataset uses the market
capitalization of each firm as a weight
A.
X
Rvw = wi Ri
74
[172]: Industry_Portfolios[0].head()
I
2019-02 1.09 4.16 5.39 3.30 2.96
AR
A KB
A.
75
5.7 Matplotlib
This is a complete plotting library capable of high-quality graphics. Here I cover the basics of produc-
ing plots. Further information is available on the matplotlib website ([Link]
Another useful graphics library is seaborn, which is a Python data visualization library based on
matplotlib. It provides a high-level interface for drawing attractive and informative statistical
graphics. Refer to its documentation page ([Link] for more information.
I
[173]: import [Link] as plt
AR
5.7.1 Line Plot
The most basic, and often most useful 2D graphic is a line plot. Basic line plots are produced using
plot(INPUT_VARIABLE) using a single input containing a 1-dimensional array.
Example plot the monthly returns for the High-Tech sector (‘HiTec’ in the Kenneth French Data
Library datasets)
[174]: df = Industry_Portfolios[0]
y = df['HiTec'].to_numpy() # for teaching purposes, I convert it first to a␣
KB
NumPy Array object
,→
[Link](y)
[Link]()
A
A.
76
You may need to also call show() function to see the generated plot, in the last step of your plotting,
in interactive Python editors such as Jupyter.
You can modify the line color, line style or the marker on the graph by adding the Format strings,
which may contain any of the following elements:
I
g for Green -- for Dashed o for Circle (•)
r for Red -. for Dash-dot s for Square (■)
AR
c for Cyan : for Dotted d for Thin Diamond (♦)
m for Magenta x for Cross (×)
y for Yellow + for Plus (+)
k for Black * for Star (⋆)
w for White
For example:
KB
[175]: # plot y in (r), use square markers (s), in dotted lines (:)
[Link](y,'r*:')
# Add a title
[Link]("High Tech. Sector", color='g')
# Add y-labes
[Link]('HiTec monthly returns', color='b')
A
# Add gridlines
[Link]()
A.
77
I
AR
KB
Note In Spyder you need to run simultaneously all commends related to one graph, otherwise each
line would generate a separate graph. The purpose of the show() function in this case is to inform
the Python interpreter that you are done editing the graph and you want to observe the output.
[176]: x = Industry_Portfolios[0].to_numpy()
A
# Add legend
[Link](loc = 'lower right', frameon = True)
[Link]()
78
I
AR
KB
Alternatively, you can plot each line in separate graphs. See the example below, in which we plot
5 graphs in 2-by-3 row-column panels. The function figure() creates a new empty image. The
function subplot(NMj) identifies how many panels are in each row (N) and in each column (M). The
last number, j, identifies the location of the current panel in the panels.
[177]: [Link]()
[Link]['[Link]'] = [10, 10] # sets the size of the plots
A
[Link](231)
[Link](x[:,0],'b', label = 'Cnsmr')
[Link](loc = 'lower right', frameon = True)
[Link](232)
[Link](x[:,1],'g.', label = 'Manuf')
A.
[Link](233)
[Link](x[:,2],'r:',label = 'HiTec')
[Link](loc = 'lower right', frameon = True)
[Link](234)
79
[Link](x[:,3],'k-.', label = 'Hlth ')
[Link](loc = 'lower right', frameon = True)
[Link](235)
[Link](x[:,4],'y--',label = 'other')
[Link](loc = 'lower right', frameon = True)
I
[Link]()
AR
A KB
You may create different plotting objects for each panel and work with them. See this example,
where we create a 1-by-2 row-column panels. Each item of the object ax identifies each panel (ax[0]
and ax[1]). Then we can add a title or y-label for each panel.
fig, ax = [Link](1,2)
ax[0].plot(x[:,0],'b')
ax[0].set_ylabel('Return %')
ax[0].title.set_text('Cnsmr')
ax[1].plot(x[:,3],'k-.')
80
ax[1].title.set_text('Hlth')
[Link]()
I
AR
KB
5.7.2 Scatter Plots
Similarly, we can draw scatter plots with the Matplotlib library. This is when you want to plot
the variable y in the vertical axis with respect to the variable x in the horizontal axis (you can also
use [Link](x,y)). For instance, below I plot the monthly return of the High-tech sector, in the
A
y-axis, with respect to the Manufacturing sector, in the x-axis
[179]: df = Industry_Portfolios[0]
#plot y versus x, in (r), use square markers (s), in dotted lines (:)
[Link](x=df['Manuf'],y=df['HiTec'])
A.
# Add a title
[Link]("High Tech. vs. Manufacturing Sectors")
# Add y-labes
[Link]('HiTec monthly returns')
81
[Link]('Manuf monthly returns')
# Add gridlines
[Link]()
I
AR
A KB
5.7.3 Bar Charts
bar() function produces bar charts using two 1-dimensional arrays. The first specifies the left edge
of the bars and the second the bar heights.
Example Plot the average monthly return of the High Tech. sector per year in the last 5 years.
df_yr = [Link]('Year').mean()
[Link](df_yr.index,df_yr['HiTec'])
[Link]()
82
I
5.7.4 Pie Charts
AR
KB
pie() function produces pie charts using a 1-dimensional array of data. The data can have any
values, and does not need to sum to 1. However, most often we use the pie chart to show how
different portions of the data are grouped. In these cases the numbers are summed to 1.
Example: For the High Tech. sector, what percentage of the monthly returns are above 5% (large
gain), between 0 and 5% (moderate gain), between 5% and 0 (moderate loss), and less than -5%
(large loss)? Show with a pie chart
First, construct a column, indicating the observations with returns in each category:
A
,→ gain'
[Link]()
83
2023-06 8.98 7.96 5.83 4.66 7.44 2023 large gain
2023-07 2.36 4.16 4.10 -0.12 5.24 2023 moderate gain
I
[182]: HiTec
AR
HiTec_Return_category
large gain 20
large loss 12
moderate gain 18
moderate loss 8
Lastly, plot the series. Also, add the relative size of each year’s observation as a auto_percentage
84
Pie charts can be modified using a large number of keyword arguments, including labels and custom
colors. Exploded views of a pie chart can be produced by providing a vector of distances to the
keyword argument explode. Note that autopct = '%2.0f' is using an old style format string to
format the numeric labels.
5.7.5 Histograms
Histograms can be produced using hist() function. Histograms show the frequency of repeated
I
values in your data. See the example below, where, we plot the histogram for the monthly returns
of the High Tech. sector. The height of each bin shows the number of months (observations) where
AR
the return falls in that bin. We can set the number of bins used in producing the histogram using
the keyword argument bins.
Histograms can be further modified using keyword arguments. In the next example,
A.
85
I
5.7.6 Plots with Pandas
AR
KB
matplotlib also sits on pandas library and you can directly call it from a DataFrame object.
[186]: df = Industry_Portfolios[0]
df['HiTec'].plot()
[Link]()
A
A.
86
Note Above, I’m plotting a pandas DataFrame data (not NumPy Array), therefore I am able to
use the time index as the x-axis.
You can have primary and secondary y-axises. This is a useful feature especially when the variables
do not have the same scale and their order of magnitudes differ significantly.
Example Plot Apple’s stock prices and monthly returns for the last 5 years. For this, we first
download stock price data with the pandas_datareader library at the daily frequency. Then we
re-sample at the monthly frequency to calculate monthly returns. In order to have primary and
I
secondary y-axises, we need to define two (sub)plots, and tell python to share their x-axis. See
AR
below, where I define a plot and call it ax1 and then create a twin copy of it and call it ax2. Once
we have these, we can plot our graphs in each plot and Python will merge them.
87
#2.5. Add legends
[Link](loc = 'upper left') # you can type the location
[Link](loc = 1) # or you can use the index number for the place␣
of the legend location
,→
I
[Link]('AAPL_DailyClosePrice_MonthlyReturns.pdf')
[Link]('AAPL_DailyClosePrice_MonthlyReturns.jpg')
AR
[Link]()
[*********************100%%**********************] 1 of 1 completed
A KB
Example It is a common practice to also impose the 25, 50, 75 percentile on the histogram (see
below). Here, I use the plot function with kind = 'hist' for histogram and I used histype =
'step' in order not to have filled bins. The function axvlines help us to add lines on the graph,
A.
[188]: df['HiTec'].plot(kind='hist',histtype='step',bins=20)
[Link](df['HiTec'].mean(),c='C1')
[Link](df['HiTec'].median(),c='C1',linestyle='--')
[Link](df['HiTec'].quantile(0.25),c='C1',linestyle=':')
[Link](df['HiTec'].quantile(0.75),c='C1',linestyle=':')
88
[Link]('Histogram for monthly returns of the HiTec Sector')
[Link]()
I
AR
KB
You may also draw the probability distribution function (pdf) on the histogram, which is the smooth
histogram, scaled to have an integral of 1.
89
A.
A
90
KB
AR
I
5.8 Statsmodels
statsmodels library provides classes and functions for the estimation of many different statistical
models, tests, data exploration. In this class, we use this library for linear regression applica-
tion, using the ordinary least-square, or OLS, estimator. The online documentation is hosted at
[Link].
Example 1 In the example below, we use this library to find the relationship between monthly
I
returns of the High-Tech. and Manufacturing sectors. Let’s first plot these series in a scatter graph,
where for each month, on the y-axis we show the High-Tech sector’s return and the x-axis we show
AR
the Manufacturing sector’s return on the same date. We also fit a linear trendline on the graph,
using the first order polynomial function (polyfit) from the numpy library.
Industry_Portfolios = pdr.get_data_famafrench('5_Industry_Portfolios',␣
,→ start_date,end_date)
df = Industry_Portfolios[0]
A
x=df['Manuf']
y=df['HiTec']
#plot y versus x, in (r), use square markers (s), in dotted lines (:)
[Link](x,y)
beta,alpha = [Link](x, y, 1)
91
Formula_text = 'y = ' + str(round(alpha,2))+ ' + ' + str(round(beta,2)) + 'x'
I
[Link]()
AR
A KB
The function polyfit() is the equivalent to the TRENDLINE in MS-Excel, in which you can choose
the order of the polynomial you fit to the data. Polynomial of order 1 is a linear line.
Now, let estimate the beta and alpha with the OLS() function from the statmodel library. This
function requires y and x inputs. If you want to also include an intercept in the regression you can
do so with the add_constant() function. By default, the fitted line is assumed to go through the
A.
[191]: x2 = sm.add_constant(x)
results = [Link](y,x2 ).fit()
alpha = [Link][0]
beta = [Link][1]
92
print('The intercept of the fitted line = ', round(alpha,2))
print('The slope of the fitted line = ', round(beta,2))
Example 2 In the example below, we use this library to find the three-year monthly market beta
for the High Tech. sector. In the first step, we import the libraries we need and then download the
I
data:
[192]: # initialize
AR
import pandas as pd
import [Link] as sm
import pandas_datareader.data as pdr
# data download
start_date = '2018-01-01'
end_date = '2021-01-01'
KB
Industry_Portfolios = pdr.
get_data_famafrench('5_Industry_Portfolios',start_date,end_date)
,→
Market_Factors = pdr.
get_data_famafrench('F-F_Research_Data_Factors',start_date,end_date)
,→
Industry_Portfolios dataset from famafrench data library contains several forms of the five sector
portfolios. The value-weighted portfolios are stored in item [0]. Market_Factors dataset includes
both monthly and annual data for the market, as well as the size and value factors. It also provides
A
data for the risk free rate of returns. The monthly data is stored in item [0].
93
According to the capital asset pricing model (CAPM), the expected excess asset return is linearly
related to the market premium:
E[Ri − Rf ] = βi E[Rm − Rf ]
Therefore, we can estimate market beta for asset i, denoted by βi , using linear regressions:
RI
where, we include an intercept, αi to capture the time-invariant effect of other (missing) factors. If
CAPM holds in the data, the estimated αi will be insignificant. The term ei,t denotes the asset-
specific noise, which is zero on average.
In this model, the left-hand side variable (Y-axis variable) is the monthly returns of the Hight Tech.
sector (HiTec) in excess of the risk free rate. The righ-hand side variables (the X-axis variable) are
a constant (const) and the excess market return (Mkt-RF).
94
==============================================================================
Omnibus: 0.656 Durbin-Watson: 1.994
Prob(Omnibus): 0.720 Jarque-Bera (JB): 0.754
Skew: -0.257 Prob(JB): 0.686
Kurtosis: 2.525 Cond. No. 5.88
==============================================================================
I
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly
AR
specified.
The above table, presents the analysis of variance information too. It reports the R-squared,
estimated slope and intercept values (coef) and their 5% confidence intervals ([0.025 0.975]).
If you want to access each of these separately you can do so as follows:
95
5.9 SciPy
SciPy library contains a number of routines to the find extremum (minimum or maximum) of a
user-supplied objective function located in [Link]. SciPy has more useful functions but
for the sake of this class we only focus on the optimization and solver functions.
The steps are similar to the Solver add-on in MS-Excel. First we need to define an (objective)
I
function that we want to minimize. For example, consider finding the minimum of the function
f (x) = x2 . A function which allows the optimizer to work correctly has the form
AR
[198]: def optim_target1(x):
return x**2
Once an optimization target has been specified, the next step is to use one of the optimizers functions
in SciPy to find the minimum values of this objective function. Optimizers are categorized into two
main types: unconstrained and constrained optimizers. If the parameters in mind are limited in a
range (for instance, only positive numbers), we use constrained optimizers. On the other hand, if
the parameters of the objective function can take any value then use unconstrained optimizers.
KB
An important application of optimizers in financial modeling is to find the optimal portfolio weights
for an investor.
The basic structure of all of the unconstrained optimizers is optimizer(f, x0) where the optimizer
is one of the fmin_bfgs, fmin_cg, fmin_ncg or fmin_powell functions in the SciPy library. f is
your objective function and x0 is a set of initial values used to start the algorithm. For this course,
A
we mostly use fmin_bfgs, which is the simplest optimizer. Back to our example above:
Function evaluations: 6
Gradient evaluations: 3
[199]: array([-7.45036449e-09])
The optimizer fmin_bfgs finds that at x = -7.45036449e-09 our objective function (f (x) = x2 )
reached to its minimum value, which is Current function value: 0.000000. Notice that the
96
correct theoretical answer is x=0, but numerically Python finds x = -0.000000000745036449, is
accurate to 9 decimal places.
We can modify our objective function to solve equations. Say we want to know at what value of
x the objective function becomes 4. The idea is to define a new objective function and find the
minimum distance of the initial objective function: i.e. f (x) = x2 − 4
I
return abs(x**2 - 4)
AR
X_star = opt.fmin_bfgs(optim_target2, x0=1)
X_star
The optimizer finds that at X_star = 1.99999999 our objective function ($f(x) = xˆ2-4 $) equals
to its minimum value (i.e.0.000000). Notice that the correct theoretical answer is x=2, and the
numerical solution is accurate to 8 decimal places. Notice that x = -2 is also a correct theoretical
answer. The choice of the initial value (x0=1) in case of several minimum values becomes important.
Function evaluations: 50
Gradient evaluations: 25
[201]: array([-2.00000001])
More Complex Cases: Imagine the case where the objective function has multiple parameters (a
parameter vector). In that case, define the objective function in the following form
97
[202]: def optim_target3(params):
x, y = params
return x**2 - 3*x + 3 + y*x -3*y + y**2
Another case, would be to have an objective function that requires additional inputs, which are not
parameters of interest. In this case, define the objective function in the following form:
I
x, y = params
c1, c2, c3 = hyperparams
AR
return x**2 + c1*x + c2 + y*x + c3*y + y**2
This form is especially useful when optimization targets require both parameters and data. Ad-
ditional inputs can be passed to the optimization target using the keyword argument args and a
tuple containing the input arguments in the correct order. In the above example, we have a single
additional input, therefore the comma is necessary in (hyperparams,) to let Python know that this
is a tuple. E.g:
Example: Find the optimal portfolio Assume that there are only five eligible stocks (avail-
able/approved for investment by your company): The Walt Disney Company (DIS), International
Business Machines Corporation (IBM), The Coca-Cola Company (KO), Ford Motor Company (F),
A.
The Procter & Gamble Company (PG). You want to find the weights of these stocks in a portfolio
with maximum Sharpe ratio. You use the last 10 years of data, at the monthly frequency to infer
assets expected return, as well as their volatility and cross-correlations. As usual, let’s import the
libraries we need
98
import pandas_datareader.data as pdr # for downloading data directly from Python
To do this unconstrained optimization, we first write a function to calculate the Sharpe ratio of a
portfolio (call it SharpeRatio(). This function receives stocks’ weights in the portfolio and their
I
monthly returns, as well the monthly risk free rate. In the next step, we find the portfolio weights
that would result in the maximum Sharpe ratio of our portfolio. We do this by minimizing the
AR
negative value of the portfolio’s Sharpe ratio (hence the negative sign in SR_portfolio formula in
the function below). Notice that there is only one constraint here, which is that the sum of the
weights is 100%. We manually make sure this will hold. That is, in the optimization, we find the
weights for the first 4 stocks and calculate the weight for the last one.
return SR_portfolio
Then we will use the fmin_bfgs() function from the SciPy library to find the optimal weights. This
function will require an initial guess for the weights (weights0 ). Other inputs to this function are
the rest of the inputs to the SharpeRatio() function (other than portfolio weights).
,→
"""
# # # First Download Stock Price data
tickers = ['DIS', 'IBM', 'KO','F','PG']
start_date = '2011-01-01'
end_date = '2020-12-31'
panel_data = pdr.get_data_yahoo(tickers, start_date, end_date)
99
Returns = panel_data['Adj Close'].resample("1m").ffill().pct_change().
,→ dropna()# first observation is NaN, so I drop it
I
,→ start_date, end_date)
# # the first part of the output (index 0) is for the monthly factors,
AR
# # these values are reported
# # in percentage so we change it to decimal values, and
# # the time index is period so we need convert it to days
FF_3Factor = FF_3Factor_All[0]/100
FF_3Factor.index = FF_3Factor.index.to_timestamp()+ MonthEnd(1) # make the␣
,→ periodIndex to end of month date
# # initial values
# # we use equal weights as the inital values. This will also make sure that the␣
,→ sum of weights = 1
weights_0 = [Link](len(tickers)-1)/len(tickers)
A
[*********************100%%**********************] 5 of 5 completed
# # output
optimal_weights_all = [Link](optimal_weights, 1-sum(optimal_weights) )
optimal_weights_all = [Link](optimal_weights_all, index = tickers)
optimal_SharpeRatio = -1 * SharpeRatio(optimal_weights, Returns[tickers],Rf)
100
print('The Sharpe Ratio of the optimal portfolio is: {:.2f}'.
,→format(optimal_SharpeRatio))
I
Gradient evaluations: 14
For the optimal Sharpe Ratio portfolio includes investing:
AR
DIS 57.68
IBM -17.95
KO 15.09
F -29.98
PG 75.15
dtype: float64
The Sharpe Ratio of the optimal portfolio is: 1.15
g(θ) = 0 (equality)
h(θ) ≥ 0 (inequality)
θL ≤ θ ≤ θH (bounds)
A.
where the f is the objective function, g and h are the set of equations for the equality and inequality
constraints on the parameters.
In portfolio optimization, f could be the Sharpe ratio of the portfolio or expected returns adjusted for
risk. The equality constraint (θ) is usually used to ensure the sum of the weights of the ingredients
of a portfolio equals to one. The inequality or bounds constraints are used to set a limit on portfolio
weights. For instance, to impose the no short selling or to impose a balance weight across industries.
The function fmin_slsqp is the most general constrained optimizer and allows for equality, inequal-
101
ity and bounds constraints. Constraints (here the functions g and h) are provided either as a list of
callable functions or as a single function which returns an array. The latter is simpler if there are
multiple constraints, especially if the constraints can be easily calculated using linear algebra.
Example: Consider the same portfolio optimization problem, but with some constraints. Say you
do not intend to short sell a stock (no negative portfolio weight. Also, you do not intend to over
invest in a stock (no more than 49% in one stock). As before, the sum of the portfolio weights needs
to be 100%
I
We first need to specify the objective function (SharpeRatio()). This is going to be very similar
AR
to the one we had in the unconstrained optimization, but we will remove the manual check for the
sum of the portfolios.
[209]: # # objective function: minimize the negative value of the Sharpe Ratio
def SharpeRatio(weights, Ret,Rf):
Ret_portfolio = (weights * Ret).sum(axis=1)
SR_portfolio = -1 * (Ret_portfolio.mean() - Rf)*12/Ret_portfolio.std()/np.
sqrt(12) #I minimize the negative value of the Sharpe Ratio
,→
return SR_portfolio
KB
Then we need to define the 3 constraints (sum of portfolio weights and two limits on portfolio
weights)
[210]: # # # Nb. 1
def equal_condition(x, *args):
return sum(x) - 1.0
weights_0 = [Link](len(tickers))/len(tickers)
A
print(weights_0)
# # # Nb. 2. and 3.
lowerBound = 0.00
upperBound = 0.49
weights_bounds = [(lowerBound, upperBound) for n in tickers]
A.
print(weights_bounds)
We will use the same input data and initial weights as before. Let’s find the
102
[211]: # # constrained minimization
optimal_weights = opt.fmin_slsqp(func = SharpeRatio,
x0 = weights_0,
bounds = weights_bounds,
eqcons = [equal_condition],
args = (Returns[tickers],Rf))
I
Current function value: -1.008872112324576
Iterations: 5
AR
Function evaluations: 30
Gradient evaluations: 5
[212]: # # output
optimal_weights_all = [Link](optimal_weights, index = tickers)
F 0.00
PG 49.00
dtype: float64
The Sharpe Ratio of the optimal portfolio is: 1.01
That means the optimal portfolio invests 24% less in DIS (from 57% to 33%) and 26% less in PG
A.
(75% to 49%). It does not invest in IBM and F (previously it was shorting them). But slightly
invests more in KO by 2% (from 15% to 17%).
Notice that the Sharpe ratio of the unconstrained optimization is higher (1.16 vs 1.01). By imposing
constraints we deviate from the optimal allocation.
103
6 Python Coding Conventions
There are a number of common practices which can be adopted to produce Python code which looks
more like code found in other modules:
1. Use self-explanatory names for the variables and DataFrame. Something that you would
quickly understand when you look at your codes
2. Try to add sufficient comments and annotations to your codes so that you can understand
I
your work later.
AR
3. Use 4 spaces to indent blocks – avoid using tab, except when an editor automatically converts
tabs to 4 spaces
5. Limit lines to 79 characters. The \ symbol can be used to break long lines
6. Use two blank lines to separate functions, and one to separate logical sections in a function.
10. Follow the NumPy guidelines for documenting functions More suggestions can be found in
PEP8 Style Guide for Python Code ([Link]
A
A.
104
7 References and Python Resources
Python is an increasingly popular programming language. You can find numerous useful tutorials
on it on the web. For this document, I personally consulted the following materials frequently:
I
Data Analysis” course by Kevin Sheppard
AR
• On [Link]/learning, the “Python: Data Analysis” and “Python Statistics Essential
Training” courses, by Michele Valisneri
7.1 Best Python Libraries/Packages for Finance and Financial Data Scientists
An article from Majid AliAkbar ([Link]
KB
finance-financial-data-majid-aliakbar/), Published on March 28, 2017.
Finance professionals involved in data analytics and data science make use of R, Python and other
programming languages to perform analysis on a variety of data sets. Python has been gathering
a lot of interest and is becoming a language of choice for data analysis. Python also has a very
active community which does not shy from contributing to the growth of python libraries. If you
search on Github, a popular code hosting platform, you will see that there is a python package to
do almost anything you want. This article provides a list of the best python packages and libraries
used by finance professionals, quants, and financial data scientists.
A
– numpy – NumPy is the fundamental package for scientific computing with Python. It is
a first-rate library for numerical programming and is widely used in academia, finance,
and industry. NumPy specializes in basic array operations.
– scipy – SciPy supplements the popular Numeric module, Numpy. It is a Python-based
A.
105
instruments.
– statistics – This is a built-in Python library for all basic statistical calculations
• Financial Instruments
– pyfin – Pyfin is a python library for performing basic options pricing in python
– vollib – vollib is a python library for calculating option prices, implied volatility and
greeks using Black, Black-Scholes, and Black-Scholes-Merton. vollib implements both
analytical and numerical greeks for each of the three pricing formulae.
I
– QuantPy – A framework for quantitative finance In python. Some current capabilities:
AR
Portfolio class that can import daily returns from Yahoo, Calculation of optimal weights
for Sharpe ratio and efficient frontier, and event profiler
– ffn – A financial function library for Python. ffn is a library that contains many useful
functions for those who work in quantitative finance. It stands on the shoulders of giants
(Pandas, Numpy, Scipy, etc.) and provides a vast array of utilities, from performance
measurement and evaluation to graphing and common data transformations.
– pynance – PyNance is open-source software for retrieving, analyzing and visualizing data
from stock and derivatives markets. It includes tools for generating features and labels
for machine learning algorithms.
KB
– tia – TIA is a toolkit that provides Bloomberg data access, easier pdf generation, back-
testing functionality, technical analysis functionality, return analysis and few windows
utils.
trade app works like a service. The user informs the items he has in stock and a series of
subsequent occurrences (purchases, sales, whatsoever) with those or other items. trade
then calculates the effects of those occurrences and gives back the new amounts and costs
of the items in stock.
– zipline – Zipline is a Pythonic algorithmic trading library. It is an event-driven system
that supports both backtesting and live trading.
A.
106
strategies.
– backtrader – Python Backtesting library for trading strategies
– pybacktest – Vectorized backtesting framework in Python / pandas, designed to make
your backtesting easier. It allows users to specify trading strategies using full power of
pandas, at the same time hiding all boring things like manually calculating trades, equity,
performance statistics and creating visualizations. Resulting strategy code is usable both
in research and production setting.
I
– pyalgotrade – PyAlgoTrade is an event driven algorithmic trading Python library. Al-
though the initial focus was on backtesting, paper trading is now possible
AR
– tradingWithPython – A collection of functions and classes for Quantitative trading
– pandas_talib – A Python Pandas implementation of technical analysis indicators
– algobroker – This is an execution engine for algo trading. The idea is that this python
server gets requests from clients and then forwards them to the broker API.
– finmarketpy – finmarketpy is a Python based library that enables you to analyze market
data and also to backtest trading strategies using a simple to use API, which has prebuilt
templates for you to define backtest.
• Risk Analysis
KB
– pyfolio – pyfolio is a Python library for performance and risk analysis of financial
portfolios. It works well with the Zipline open source backtesting library.
– empyrical – Common financial risk and performance metrics. Used by zipline and
pyfolio.
– finance – Financial Risk Calculations. Optimized for ease of use through class construc-
tion and operator overload.
– qfrm – Quantitative Financial Risk Management: awesome OOP tools for measuring,
managing and visualizing risk of financial instruments and portfolios.
A
• Time Series
– statsmodels – Python module that allows users to explore data, estimate statistical
models, and perform statistical tests.
– dynts – A statistic package for python with emphasis on time series analysis. Built
around numpy, it provides several back-end time series classes including R-based objects
via rpy2.
107