MATH20621 Coursework 2021 RESIT
Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: daixieit
MATH20621 Coursework 2021 RESIT
stocktrader: A Python module for virtual stock trading
Introduction
The aim of this coursework is to code a Python 3 module stocktrader that allows the user to load historical financial data and to simulate the buying and selling of shares on the stock market. Shares represent a fraction of ownership in a company and the total of all shares form the stock of that company. Shares can be bought and sold on the stock market at a price that varies daily. A reasonable investor aims to buy cheap shares and sells them when the price is higher. The actions of buying and selling shares are referred to as transactions. Transactions usually incur transaction fees, but we will not consider these in this project.
Frequently asked questions
What can/cannot be used for this coursework?
The Python knowledge contained in the lecture notes is essentially sufficient to complete this project. While you are allowed to consult the web for getting ideas about how to solve a specific problem, the most straightforward solution may already be in the lecture notes. You are also allowed to use online sources, but you must clearly indicate copy-and-pasted code like so:
# the following 3 lines follow a similar code on
# http://webaddress.org/somecode.htm as retrieved on 24/11/2021
Let’s be clear about what is *not* allowed: You are NOT allowed to send, give, or receive Python code to/from classmates and others. This project is the equivalent of a standard exam and it counts 70% towards your final mark. Consequently, the standard examination rules apply to this project. |
Once your Python module is submitted via the Blackboard system, it will undergo plagiarism tests:
1. The turn-it-in system automatically checks for similarities in the codes among all students.
2. Another Python program compares the syntax of all submitted codes.
Even if you are the originator of the work (and not the one who copied), the University Guidelines Plagiarism and Academic Malpractice (link below) require that you will be equally responsible for this case of academic malpractice and may lose all marks on the coursework (or even be assigned 0 marks for the overall course).
Link to the University guidelines: https://documents.manchester.ac.uk/DocuInfo.aspx?DocID=2870 (https://documents.manchester.ac.uk/DocuInfo.aspx?DocID=2870)
How is this coursework assessed?
There are several factors that enter the assessment:
First, it will be checked whether you have followed the tasks and format specified below, using the
prescribed function names, variable names, and data structures.
Second, your module will be tested manually by performing a number of predefined transactions,
loading/saving a couple of portfolios, and testing trading strategies.
All functions of your module will be tested automatically by another computer program. The outputs are then compared to verified outputs of another implementation. (This also means that if you do not strictly follow the format specified below then some of these automatic tests will fail and you may lose marks.)
Each function/line of code should be bug free. Functionality will be the main factor in the assessment.
Your code should be robust to exceptional user inputs (using Exceptions). It should not be possible to crash the code even when a user provides an unexpected input.
It is required that your module is properly documented, and that every function has a meaningful
docstring. Each function must explain its own input arguments and their types, the returned values and their types, and any exceptions that may be raised. There should be no room for misinterpretation. Check that print(functionname.__doc__) returns a proper docstring. Essentially, each function should be useable from the docstring alone, without reading the code.
Further, marks will be given on code efciency. Have you solved a problem using 100 lines of code,
although we have learned a very straightforward way which would only require 2 lines? You may lose marks in this case.
The rough split (subject to scaling) between the total marks is 65% for testing and manual inspection, 15% for documentation, and 20% for code efficiency.
When and how to submit the coursework?
The coursework can be completed and submitted as a single Python module named stocktrader.py . The submission is via Blackboard and the strict deadline is Friday, September 2nd, at 1pm. You can resubmit your coursework as often as you like, but only the last submission counts. Submissions after the deadline will not be accepted (unless you have a DASS extension).
Task 0: Prepare the module and understand the data
structures
Download the coursework.zip file (coursework.zip) and unzip the folder to a convenient location on your computer (e.g., your Desktop). The folder already contains a template for your stocktrader.py module. Your whole coursework project can be completed using this module. You just need to replace the TODO
comments with the actual code. Make sure that all code is contained in functions so your module "does not do anything" when it is imported into another Python program.
Module template:
"""
stocktrader -- A Python module for virtual stock trading
TODO: Add a description of the module...
Also fill out the personal fields below.
Full name: Peter Pan
StudentId: 123456
Email: peter.pan.123@student.manchester.ac.uk
"""
class TransactionError(Exception):
pass
class DateError(Exception):
pass
stocks = {}
portfolio = {}
transactions = []
def normaliseDate(s):
# TODO
# TODO: All other functions from the tasks go here
def main():
# Test your functions here
# the following allows your module to be run as a program
if __name__ == '__main__ ' or __name__ == 'builtins':
main()
CSV data:
The coursework folder also contains the files portfolio0.csv and portfolio.csv in the same location as the stocktrader.py file.
The coursework folder also contains several CSV files with historic stock prices of diferent companies.
The module stocktrader uses three essential data structures as explained below.
The stocks dictionary
The dictionary stocks stores historic financial data that your module can work with. The data for stocks is located in the coursework, with each file of the form SYMBOL.csv corresponding to a particular company. Every entry in the stocks dictionary is a key-value pair. Each key is a string corresponding to a symbol and the value is again a dictionary.
The dictionaries in stocks contain key-value pairs where the key (a string) corresponds to a date in the form YYYY-MM-DD and the value is a list of floating point numbers [ Open, High, Low, Close ] corresponding to the prices of a stock at that particular date.
Here is an excerpt of a valid stocks dictionary containing data for the symbol EZJ (easyJet plc) and SKY (Sky plc):
stocks = {
'EZJ' : {
'2012-01-03' : [435.273010, 435.273010, 425.050995, 434.835999],
'2012-01-04' : [434.618011, 434.618011, 423.273010, 428.072998],
'2012-01-05' : [430.472992, 430.472992, 417.273010, 418.364014],
#...
},
'SKY' : {
'2012-01-03' : [751.000000, 755.500000, 731.500000, 742.000000],
'2012-01-04' : [740.000000, 741.125000, 718.000000, 730.000000],
'2012-01-05' : [733.500000, 735.500000, 719.500000, 721.000000],
#...
},
}
The interpretation of this data at an example is as follows: on the 4rd of January 2012 the price of a Sky share ranged between £718.00 (the "low") and £741.125 (the "high").
The portfolio dictionary
portfolio is a dictionary that represents our capital at a given date. Our capital is the combination of cash and the shares that you hold. The keys in portfolio are strings date , cash , and arbitrarily many symbols. The respective values are the date of the last transaction performed on the portfolio in the form
YYYY-MM-DD , the cash amount as a floating point number, and the integer number of shares held for each symbol.
Here's an example of a valid portfolio dictionary:
portfolio = {
'date' : '2013-11-27',
'cash' : 12400.45,
'EZJ' : 10
}
The interpretation of this is as follows: on the 27th of November 2013 we have £12,400.45 in cash and we own 10 shares of easyJet. We could now look up in the stocks dictionary that the low price of easyJet on that day is £1426.00. Hence, if we sold all 10 easyJet shares on this day, we'd have £12,400.45 + 10 x £1426.00 = £26,660.45 of cash and no more EZJ shares. In this case the portfolio dictionary would only have two keys, date and cash .
The transactions list
transactions is a list of dictionaries, with each dictionary corresponding to a buy/sell transaction on our portfolio. Here is an example of a valid transactions list:
transactions = [
{ 'date' : '2013-08-11', 'symbol' : 'SKY', 'volume' : -5 },
{ 'date' : '2013-08-21', 'symbol' : 'EZJ', 'volume' : 10 }
]
The interpretation of this is as follows: on 11th of August 2013 we sold 5 shares of Sky (because volume is negative), and on the 21st of August 2013 we bought 10 shares of easyJet (because volume is positive). The value of volume is always an integer, and the date values are chronological: while there can be two or more neighboring list entries in transactions having the same date, the following ones can never have an earlier date. This makes sense as the time order of transactions is important.
Task 1: function normaliseDate(s)
Write a function normaliseDate(s) which takes as input a string s and returns a date string of the form YYYY-MM-DD . The function should accept the following input formats: YYYY-MM-DD , YYYY/MM/DD and DD.MM.YYYY , where DD and MM are integers with one or two digits (the day and/or month can be given
with or without a leading 0 ), and YYYY is a four-digit integer. The function converts all of these formats to
YYYY-MM-DD .
If the conversion of the format fails (i.e., it is not exactly in any of the formats specified above), the function raises a DateError exception.
Note that this function is only about conversion of formats, and there is no need to check whether the date
YYYY-MM-DD actually exists.
Example: Both normaliseDate('08.5.2012') and normaliseDate('2012/05/8') should return the string 2012-05-08 , while normaliseDate('8.5.212') should raise a DateError exception.
Task 2: function loadStock(symbol)
Write a function loadStock(symbol) which takes as input a string symbol and loads the historic stock data from the corresponding CSV file into the dictionary stocks . The function does not need to return anything as the dictionary stocks is in the outer namespace and therefore accessible to the function. Note that symbol only contains the symbol of a stock, not the full file name. So, for example, if symbol = 'EZJ' then the file to be loaded has the name fname = symbol + '.csv' .
The stock data CSV files are of the following format:
the first line is the header and can be ignored
every following line is of the comma-separated form
Date,Open,High,Low,Close,AdjClose,Volume , where Date is in any of the formats accepted by the function normaliseDate() , and all other entries are floating point numbers corresponding to prices and trading volumes. Note that only the first values are relevant for filling the stocks dictionary and AdjClose,Volume can be ignored.
If the file given by symbol cannot be opened (as it is not found), a FileNotFoundError exception should be raised.
If a line in the CSV file is of an invalid format, a ValueError exception should be raised.
Example: loadStock('EZJ') should load the easyJet data from the file EZJ.csv into the dictionary
stocks , whereas loadStock('XYZ') should raise a FileNotFoundError exception.
Fallback: If, for some reason, you struggle to implement this function, you can copy-and-paste the above example stocks dictionary (containing only three days of EZJ and SKY shares) into your stocktrader.py file and use this for testing the other functions.
Task 3: function
Write a function loadPortfolio(fname) which takes as input a string fname corresponding to the name of a CSV file (assumed to be in the same directory as stocktrader.py ). The function loads the data from the file and assigns them to the portfolio dictionary, with all entries of the form described above (including the date!).
Make sure that the portfolio dictionary is emptied before new data is loaded into it, and that the list
transactions is emptied as well.
The function does not need to return anything as the dictionary portfolio is in the outer namespace and therefore accessible to the function. If no filename is provided, the name portfolio.csv should be assumed.
As the loadPortfolio(fname) function goes through the list of shares in the CSV file, it should use the function loadStock(symbol) from Task 2 to load the historic stock data for each symbol it encounters.
A valid portfolio CSV file is of the following form:
the first line contains the date of the portfolio in any of the forms accepted by the function
normaliseDate()
the second line contains the cash in the portfolio, a nonnegative floating point number
the following lines (if present) are of the form symbol,volume . Here, symbol is the symbol of a stock and volume is an integer corresponding to the number of shares.
Here is an example of a portfolio.csv file:
2012/1/16
20000
SKY,5
EZJ,8
If the file specified by fname cannot be opened (as it is not found), a FileNotFoundError exception should be raised.
If a line in the file is of an invalid format, a ValueError exception should be raised. The coursework folder contains a faulty portfolio_faulty.csv file which you can use for testing.
Example: loadPortfolio() should empty the dictionary portfolio and the list transactions , and then load the data from portfolio.csv into the dictionary portfolio , as well as the corresponding stock data into the dictionary stocks .
Fallback: If, for some reason, you struggle to implement this function, you can copy-and-paste the above example portfolio dictionary (containing 10 shares of EZJ ) into your stocktrader.py file and use this for testing the other functions.
Task 4: function valuatePortfolio(date, verbose)
Write a function valuatePortfolio(date, verbose) with two named parameters date and
verbose . The function valuates the portfolio at a given date and returns a floating point number corresponding to its total value. The parameter date is any string accepted by the normaliseDate() function and when it is not provided, the date of the portfolio is used. The parameter verbose is a Boolean value which is False by default. When the function is called with verbose=True it should still return the total value of the portfolio but also print to the console a table of all capital with the current low prices of all shares, as well as the total value.
Example: With the portfolio.csv example given in Task 3, a call to valuatePortfolio('2012-2- 6') should return the floating point number 27465.372072... . When valuatePortfolio('2012-2- 6', True) is called, it should also print a table like this:
Your portfolio on 2012-02-06:
[* share values based on the lowest price on 2012-02-06]
Capital type | Volume | Val/Unit* | Value in £*
-----------------------+--------+-----------+-------------
Cash |
| |
1 |
| |
20000.00 |
| |
20000.00 |
Shares of SKY |
| |
5 |
| |
686.50 |
| |
3432.50 |
Shares of EZJ |
| |
8 |
| |
504.11 |
| |
4032.87 |
-----------------------+--------+-----------+-------------
TOTAL VALUE 27465.37
Note 1: For the valuation we use the low prices of Sky and easyJet on date , in this case the 6th of February 2012. This is to be on the safe side: if we were selling the shares on that day, we would at least get those prices.
Note 2: A call to valuatePortfolio(date) should raise DateError exceptions in two cases:
When date is earlier than the date of the portfolio, there might have been transactions afterwards and we no longer know what was the value back then. For example, valuatePortfolio('2012-1-3') should fail if the portfolio is already dated 2012-02-06 .
When date is not a trading day (e.g., a bank holiday or weekend) the CSV files will not contain any
price for it and hence we cannot look up the values of shares. For example,
valuatePortfolio('2012-2-12') should fail for that reason.
2022-08-15