#!/usr/bin/env python # coding: utf-8 # ### MATH20662 Coursework 2018 ([Dr Stefan Güttel](http://guettel.com)) # # 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/04/2018 # ``` # # #
# 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 (without syntactical comparisons). # 2. Your lecturer compares the syntax of all submitted codes (interestingly, with the help of another Python program). Hence, refrain from communicating any code with others. # #
# Note that 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 guidelines:* [University Guidelines Plagiarism and Academic Malpractice](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. # * Further, all module functions will be tested automatically by another Python program (so-called unit testing). This is why it is important that your module "does not do anything" when it is imported into another program, and that you strictly follow the format of the functions specified in the tasks below (otherwise some of the tests may fail and you may lose marks). # * It is also checked whether each function/line of code is free of bugs and the program runs without crashing. Functionality will be the main factor in the assessment. # * It will be tested if your functions react to exceptional inputs in an appropriate manner (using Exceptions). It should be impossible to crash the code. # * Make sure that your module is properly documented, and that all functions have meaningful docstrings. In particular, each function must explain its own inputs and returned values so that there is no room for misinterpretation. # * Further marks will be given on the code efficiency and strategy. # # ### 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 Thursday, May 10th, 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.** # # # ***************** # # **UPDATE: The strict deadline for submitting the resit coursework is Thursday, August 23rd, at 1pm.** # ## 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 "only" 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:** # # ```python # """ # 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@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. # # * In the subfolder `stockdata` you will find ten CSV files containing historic stock prices of different 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 `stockdata` subfolder, 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 (in the `stockdata` subdirectory) 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. # # The CSV files in the `stockdata` subdirectory 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 `stockdata/EZJ.csv` into the dictionary `stocks`, whereas `loadStock('XYZ')` should raise a `FileNotFoundError` exception. # ## Task 3: function `loadPortfolio(fname)` # # Write a function `loadPortfolio(fname)` which takes a input a string `fname` corresponding to the name of a CSV file 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 `portfolio` 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. # # **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`. # ## 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. # ## Task 5: function `addTransaction(trans, verbose)` # # Write a function `addTransaction(trans, verbose)` which takes as input a dictionary `trans` corresponding to a buy/sell transaction on our portfolio and an optional Boolean variable `verbose` (which is `False` by default). The dictionary `trans` has three items as follows: # # * the key `date` whose value is any string accepted by the function normaliseDate() # * the key `symbol` whose value is a string corresponding to the symbol of a stock # * the key `volume` whose value is an integer corresponding to the number of shares to buy or sell. # # **Example:** Here are two valid transaction dictionaries, the first one for selling 5 shares of Sky on 12th of August 2013, and the second for buying 10 shares of easyJet on the 21st of August 2013. # ```python # { 'date' : '2013-08-12', 'symbol' : 'SKY', 'volume' : -5 }, # { 'date' : '21.08.2013', 'symbol' : 'EZJ', 'volume' : 10 } # ``` # # A call to the `addTransaction(trans)` function should # * update the `portfolio` value for `cash` # * insert, update, or delete the number of shares # * update the date of `portfolio` to the date of the transaction # * append `trans` to the list `transactions`. # # To be on the safe side, we always assume to **sell at the daily low price** and **buy at the daily high price**. # # The `addTransaction(trans)` function does not need to return any values as both `portfolio` and `transactions` are available in the outer namespace and therefore accessible to the function. # # If the optional Boolean parameter `verbose=True` the function should print to the console an informative statement about the performed transaction. # # **Example:** The call # ```python # addTransaction({ 'date':'2013-08-12', 'symbol':'SKY', 'volume':-5 }, True) # ``` # should print something like # ``` # > 2013-08-12: Sold 5 shares of SKY for a total of £4182.50 # Available cash: £24182.50 # ``` # # **Exceptions:** The function `addTransaction(trans)` may fail for several reasons, in which case both `portfolio` and `transactions` should remain unchanged and the appropriate exception should be raised: # # * if the `date` of the transaction is earlier than the date of the portfolio, a `DateError` exception should be raised (i.e., one cannot insert any transactions prior to the last one) # * if the `symbol` value of the transaction is not listed in the stocks dictionary, a `ValueError` exception should be raised # * if the `volume` is such that we either do not have enough cash to perform a buying transaction or we do not have enough (or none at all) shares to perform a selling transaction, a `TransactionError` exception should be raised. # ## Task 5.5: Take a break and enjoy # # When you arrive here, it's time to take a break and test your code extensively. You should now be able to use your module to load portfolio and stock data files into your computers memory, print the value of your portfolio, and perform buying and selling transactions. For example, if you create a `test_stocktrader.py` file (or use the one in the coursework.zip folder) the following code should now work: # # ```python # import stocktrader as s # s.loadPortfolio() # val1 = s.valuatePortfolio(verbose=True) # trans = { 'date':'2013-08-12', 'symbol':'SKY', 'volume':-5 } # s.addTransaction(trans,verbose=True) # val2 = s.valuatePortfolio(verbose=True) # print("Hurray, we have increased our portfolio value by £{:.2f}!".format(val2-val1)) # # # ``` # # The console output should be something like this: # ``` # Your portfolio on 2012-01-16: # [* share values based on the lowest price on 2012-01-16] # # Capital type | Volume | Val/Unit* | Value in £* # -----------------------+--------+-----------+------------- # Cash | 1 | 20000.00 | 20000.00 # Shares of SKY | 5 | 677.50 | 3387.50 # Shares of EZJ | 8 | 429.93 | 3439.42 # -----------------------+--------+-----------+------------- # TOTAL VALUE 26826.92 # # # > 2013-08-12: Sold 5 shares of SKY for a total of £4182.50 # Available cash: £24182.50 # # # Your portfolio on 2013-08-12: # [* share values based on the lowest price on 2013-08-12] # # Capital type | Volume | Val/Unit* | Value in £* # -----------------------+--------+-----------+------------- # Cash | 1 | 24182.50 | 24182.50 # Shares of EZJ | 8 | 1327.35 | 10618.80 # -----------------------+--------+-----------+------------- # TOTAL VALUE 34801.30 # # # Hurray, we have increased our portfolio value by £7974.38! # ``` # # Before moving on to the final tasks, make sure that all the functions of Tasks 1-5 work as expected, that all calculations are correct, and that the appropriate exceptions are raised whenever a problem occurs. The following tasks will rely on these core functions. # ## Task 6: function `savePortfolio(fname)` # # Write a function `savePortfolio(fname)` that saves the current dictionary `portfolio` to a CSV file with name `fname` (a string). The file should be saved in the same directory as the `stocktrader.py` module. If no filename is provided, the name `portfolio.csv` should be assumed. # # The function does not need to return anything. # # **Example:** `savePortfolio('portfolio1.csv')` should store the values of the `portfolio` in the file `portfolio1.csv`. # ## Task 7: function `sellAll(date, verbose)` # # Write a function `sellAll(date, verbose)` that sells all shares in the portfolio on a particular date. Here, `date` is an optional string of any format accepted by the function `normaliseDate()` and `verbose` is an optional Boolean variable which is `False` by default. If `verbose=True` all selling transactions are printed to the console. If `date` is not provided, the date of the portfolio is assumed for the sell out. # # **Note:** You should be able to use a simple loop with the function `addTransaction(trans, verbose)` for this task. # ## Task 8: function `loadAllStocks()` # # Write a function `loadAllStocks()` which loads all historic stock data from the stockdata subdirectory 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. # # If the loading of one of the files in the stockdata subdirectory fails, this file should simply be ignored. The coursework.zip folder contains an invalid file `invalidcsv/PPB.csv` which you can use for testing this. # # **Note:** You should be able to use a simple loop with the function `loadStock(symbol)` for this task. You may want to use the [`os` module](https://docs.python.org/3.6/library/os.html) for getting a list of all files in a directory. # ## Task 9: function `tradeStrategy1(verbose)` # # Write a function `tradeStrategy1(verbose)` that goes through all trading days in the dictionary `stocks` and buys and sells shares automatically. The strategy is as follows: # # * The earliest buying decision is either on the date of the portfolio or the tenth available trading day in `stock` (whichever is later) # * At any time, we buy the highest possible volume of a stock given the available cash. # * When shares have been bought, no other shares will be bought until all shares from the previous buying transaction are sold again. # * All shares from a previous buying transaction are sold at once, so it's a simple "buy as much as possible & sell all" procedure. # * If shares a being sold on trading day `j` we will only consider buying new shares on the following trading day, `j+1`. # # Assume that `j` is the index of the current trading day, then we will find the stock to buy as follows: # # * For each stock `s` available in `stocks` evaluate the quotient # ``` # Q_buy(s,j) = 10*H(s,j) / (H(s,j) + H(s,j-1) + H(s,j-2) + ... + H(s,j-9)) # ``` # where `H(s,j)` is the *high price* of stock `s` at the `j`-th trading day. Note that `Q_buy(s,j)` is large when the *high price* of stock `s` on trading day `j` is large compared the average of all previous ten high prices (including the current). This means we might enter a phase of price recovery. # # * Find the maximal quotient `Q_buy(s,j)` among all stocks `s` and buy a largest possible volume `v` of the corresponding stock on trading day `j`. (It might not be possible to buy any as there might not be enough cash left; in this case do nothing on trading day `j` and move to the next. If two or more stocks have exactly the same quotient, take the one whose symbol comes first in lexicographical order.) # # * Note that, as usual, our buying decision is based on the *high price.* # # If we have automatically bought `v` shares of a stock `s` on trading day `j`, then from trading day `k = j+1` onwards we will consider selling all of it as follows: # # * On trading day `k = j+1, j+2, ...` calculate the quotient # ``` # Q_sell(k) = L(s,k) / H(s,j), # ``` # where `L(s,k)` corresponds to the *low price* of stock `s` on trading day `k`. This quotient is high if the current *low value* of the stock is large compared to the *high value* to which we bought it. # # * Sell all `v` shares of `s` on day `k` if `Q_sell(k) < 0.7` (we already lost at least 30%, let's get rid of these shares!) or if `Q_sell(k) > 1.3` (we made a profit of at least 30%, time to cash in!). # # **Notes:** # * For solving this task it might be useful to first extract a list of all trading days from the `stocks` dictionary: # ``` # lst = [ '2012-01-03', '2012-01-04', ..., '2018-03-13' ] # ``` # You can assume that all loaded stocks in the `stocks` dictionary can be traded on exactly the same days, and that there is at least one stock in that dictionary. # * All buying and selling transactions should be performed using the `addTransaction(trans, verbose)` function from Task 5. The `verbose` parameter of `tradeStrategy1(verbose)` can just be handed over to `addTransaction(trans, verbose)`. # # **Example:** The following code loads a portfolio of £20,000 cash (and no shares) on the 1st of January 2012, runs the `tradeStrategy1(verbose=True)` until the end of available data, and valuates the portfolio on the 13th of March 2018. # # ```python # s.loadPortfolio('portfolio0.csv') # s.loadAllStocks() # s.valuatePortfolio(verbose=True) # s.tradeStrategy1(verbose=True) # s.valuatePortfolio('2018-03-13', verbose=True) # ``` # # The console output is as follows: # # ``` # Your portfolio on 2012-01-01: # [* share values based on the lowest price on 2012-01-01] # # Capital type | Volume | Val/Unit* | Value in £* # -----------------------+--------+-----------+------------- # Cash | 1 | 20000.00 | 20000.00 # -----------------------+--------+-----------+------------- # TOTAL VALUE 20000.00 # # > 2012-01-16: Bought 29 shares of PRU for a total of £19517.00 # Remaining cash: £483.00 # > 2012-11-21: Sold 29 shares of PRU for a total of £25520.00 # Available cash: £26003.00 # > 2012-11-22: Bought 37 shares of EZJ for a total of £25696.50 # Remaining cash: £306.50 # > 2013-01-25: Sold 37 shares of EZJ for a total of £33633.00 # Available cash: £33939.50 # > 2013-01-28: Bought 35 shares of EZJ for a total of £33103.00 # Remaining cash: £836.50 # > 2013-05-21: Sold 35 shares of EZJ for a total of £43120.00 # Available cash: £43956.50 # > 2013-05-22: Bought 34 shares of EZJ for a total of £43905.22 # Remaining cash: £51.28 # > 2014-01-22: Sold 34 shares of EZJ for a total of £58208.00 # Available cash: £58259.28 # > 2014-01-23: Bought 18 shares of BATS for a total of £57456.00 # Remaining cash: £803.28 # > 2016-04-08: Sold 18 shares of BATS for a total of £74853.00 # Available cash: £75656.28 # > 2016-04-11: Bought 68 shares of SMIN for a total of £75140.00 # Remaining cash: £516.28 # > 2016-09-29: Sold 68 shares of SMIN for a total of £98532.00 # Available cash: £99048.28 # > 2016-09-30: Bought 108 shares of SKY for a total of £98594.49 # Remaining cash: £453.79 # > 2018-02-27: Sold 108 shares of SKY for a total of £140400.00 # Available cash: £140853.79 # > 2018-02-28: Bought 104 shares of SKY for a total of £140192.00 # Remaining cash: £661.79 # # Your portfolio on 2018-03-13: # [* share values based on the lowest price on 2018-03-13] # # Capital type | Volume | Val/Unit* | Value in £* # -----------------------+--------+-----------+------------- # Cash | 1 | 661.79 | 661.79 # Shares of SKY | 104 | 1316.00 | 136864.00 # -----------------------+--------+-----------+------------- # TOTAL VALUE 137525.79 # ``` # # Not bad! In a bit more than six years we have multiplied our initial investment of £20,000 by a factor of almost seven. I am now quitting my job as a lecturer, but promise to still mark your coursework in my new house on the Cayman Islands... # # ![Bahamas](dagobert.jpg) # ## Task 10 (optional): function `tradeStrategy2(verbose)` # # When you modify the start date of your portfolio, or remove some of the stocks from the available data, you will see that `tradeStrategy1()` is not very robust and we've just been lucky to make so much profit. Also, it is kind of strange that we repeatedly sell and then immediately buy the same stock. This doesn't seem to make much sense. In some cases, this strategy results in big loses. # # Can you write your own `tradeStrategy2(verbose)` function that performs better? # # The conditions are as follows: # # * `tradeStrategy2(verbose)` should only perform transactions via the function `addTransaction(trans,verbose)` # * `tradeStrategy2(verbose)` itself should not modify the dictionaries `portfolio`, `stocks` and neither the list `transactions` # * `tradeStrategy2(verbose)` should work on all valid `stock` and `portfolio` dictionaries, with different companies than the provided ones and over different time ranges # * on any trading day the buy/sell decision is only based on the price data `[ Open, High, Low, Close ]` available up to that day (i.e., no information from the future is used for making decisions); no other (external) data should be used # * `tradeStrategy2(verbose)` can (and probably should) "diversify" to reduce the risk, which means that any time it can decide to have shares of more than one stock in the portfolio, or no shares at all # * `tradeStrategy2(verbose)` is not restricted to a single buying or selling transaction per day, and even allowed to buy and sell shares of a stock on the same day (which would however incur a loss because buying is at the daily high price, and selling at the low price) # * `tradeStrategy2(verbose)` should not use any "randomness", i.e., two calls to the function with exactly the same data should result in an indentical list of transactions # * `tradeStrategy2(verbose)` should run reasonably fast, not longer than a couple of seconds on the provided data. # #
# #
# This task is optional and will not be part of the formal assessment. However, if your module contains a working `tradeStrategy2(verbose)` function, it will enter a *trading competition.* In this competition I will test your strategy on different stock data and over shorter and longer time intervals. The winners with the three best-performing strategies will be announced on the course website and will receive Amazon vouchers sponsored by Sabisu. #
# #
# # **Industry sponsor:** [Sabisu](http://www.sabisu.co/) is a Manchester-based software company that provide an operational # and project intelligence platform for oil & gas and petrochemicals customers (including global leaders like Shell and Sabic). Much of Sabisu's work is related to time series and they use Python for the analytics. Sabisu have a long-standing collaboration with our School of Mathematics and the University in general. # # # # **End of coursework.**