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 nancial 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

Lets 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 nal 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 specied below, using the

prescribed function names, variable names, and data structures.

Second, your module will be tested manually by performing a number of predened 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 le (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 les  portfolio0.csv and  portfolio.csv in the same location as the  stocktrader.py file.

The coursework folder also contains several CSV les 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 nancial data that your module can work with. The data for  stocks is located in the coursework, with each le 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 oating 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 oating 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 le 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 le name. So, for example, if  symbol =    'EZJ' then the le to be loaded has the name  fname = symbol + '.csv' .

The stock data CSV les are of the following format:

the rst 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 oating point numbers corresponding to prices and trading volumes. Note that only the rst values are relevant for lling 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 le is of an invalid format, a  ValueError exception should be raised.

Example: loadStock('EZJ') should load the easyJet data from the le  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 le (assumed to be in the same directory as  stocktrader.py ). The function loads the data from the le 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 lename is provided, the name  portfolio.csv should be         assumed.

As the  loadPortfolio(fname) function goes through the list of shares in the CSV le, 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 le is of the following form:

the rst 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 oating 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 le:

2012/1/16

20000

SKY,5

EZJ,8

If the le specified by  fname cannot be opened (as it is not found), a  FileNotFoundError exception should be raised.

If a line in the le 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 le 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 oating 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 oating 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 les 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.