As for assignment 1, the pre-deadline tests will determine your mark for the first part, corresponding to 70% of the overall marks that are available. Assuming that you have achieved full marks on the first part, the pre-deadline tests will check that the form of
results.yaml is correct, and that it uses the expected student username (i.e., your one) and corresponding time periods; the pre-deadline tests do not check the correctness of the other fields in results.yaml, which will be checked post deadline only if you pass the pre-deadline test for results.yaml. For those other fields, you should use the examples provided (which are in the subdirectory a2_example_yamls).
Part 1: strategy implementation (70%)
The trading strategy that you should implement is a triple moving average (TMA) momentum strategy, which is described in slides 4.7. The specification of the strategy and the functions that it should comprise are given in full detail, so the correctness of your code can and will be checked automatically.
Two template files are provided to get you started:
• strategies/a2_strategy_template.R, which should become the file strategy.R that you eventually submit;
• a2_main_template.R, which uses DATA/A2 and strategies/a2_strategy_template.R.
If you source a2_main_template.R with no edits to these two files you will get an error:
Error in if (store$iter > params$lookbacks$long) { :
argument is of length zero
This is because the strategy requires a parameter called lookbacks that you will need to pass in from a2_main_template.R. Read on to see what form this parameter should take, and, more generally, how you should be editing these two files. a2_strategy_template.R contains 10 incomplete functions that you need to complete. The first 6 functions (checkE01,..., checkE06) are error checks for the inputs to getTMA. These error checks are all one-liners, worth 3% each. They are intentionally meant to be
straightforward to implement. The next three functions compute the moving averages (getTMA), use them to compute the position sign (getPosSignFromTMA), and compute the position size (getPosSize). The final, tenth function, getOrders combines the last three to implement that actual trading strategy. Recall that every strategy in the backtester framework has a getOrders function.
The TMA momentum strategy that you should implement uses three moving averages with different lookbacks (window lengths). The short lookback should be smaller than the medium one, which in turn should be smaller than the long lookback. In every trading period, the strategy will compute the value of these three moving averages (for the series that it trades on, which will be determined by params$series). You will achieve this by completing the implementation of the function getTMA.
The following table indicates the position that the strategy will take depending on the relative values of the three moving averages (MAs). You will compute this position (sign, but not size) by completing the function getPosSignFromTMA. The system is out of the market (i.e., flat) when the relationship between the short MA and the medium MA does not match the relationship between the medium MA and the long MA.
MA MA MA Position
short MA < medium MA < long MA short
short MA > medium MA > long MA long
Example output for checkE01 ... checkE06 and getTMA
The file a2_test_checks_and_getTMA.R is provided to give you guidance on how you can test the six functions, checkE01 ... checkE06. For each one, two tests are provided: for a correct implementation, one test should produce TRUE and the other FALSE. (You don't need to use these tests, as you can also just rely on the tests on CodeGrade.)
To use these tests, first source a2_test_checks_and_getTMA.R and also source the implementations that you would like to test. The tests that should return TRUE are test_checkE01() ... test_checkE06(); for tests that should return FALSE, there is single function, test_pass_all_checks, which takes the function to test as its only argument.
Here's an example of both types of test for E01 (where a correct implementation of checkE01 has been sourced):
> test_checkE01()
[1] TRUE
> test_pass_all_checks(checkE01)
[1] FALSE
The way these tests work is clear from the source code in a2_test_checks_and_getTMA.R:
###############################################################################
# Source the functions that you would like to test, e.g., with
# source('strategies/a2_strategy_template.R') or source('strategies/strategy.R')
###############################################################################
source('framework/data.R'); dataList <- getData(directory="A2")
prices <- dataList[[1]]
prices_19_rows <- dataList[[1]]$Close[1:19]
prices_20_rows <- dataList[[1]]$Close[1:20]
prices_20_rows_renamed <- prices_20_rows
colnames(prices_20_rows_renamed) <- 'Closed'
bad_prices <- c(1,2,3)
lookbacks_no_names <- list(5,10,25) # list elements not named
lookbacks_not_integer <- list(short=5,medium=as.integer(10),long=as.integer(20))
lookbacks_wrong_order <- list(short=as.integer(15),medium=as.integer(10),long=as.integer(20))
lookbacks <- list(short=as.integer(5),medium=as.integer(10),long=as.integer(20))
test_checkE01 <- function()
Part 2: cross-validation (30%)
In this part of the assignment you are asked to do a cross-validated parameter optimization of profit, where you will use an in-sample and out-of-of-sample time period. Every student has their own in-sample and out-of-sample periods based on their MWS username (only the part before the @, e.g., for Rahul Savani, this username is rsjs, rather than the full email form rsjs@liverpool.ac.uk). By having different time periods for different sutdents, there is not one single correct results.yaml.
To get your in-sample and out-of-sample periods, use a2_periods.R as follows. Source it and run the function getPeriods with your MWS username as per the following example (where we use the fake username "x1xxx"). Use startIn, endIn, startOut, and endOut as the start and end of the in-sample and out-of-sample periods respectively.
> source('a2_periods.R')
> getPeriods('x1xxx')
$startIn
[1] 1
$endIn
[1] 884
$startOut
[1] 885
$endOut
[1] 2000
You will do two parameter sweeps. One on your in-sample period, and one on your out-of-sample period (normally one doesn't do a sweep on the out-of-sample period in practice; we do it here to allow detailed cross-period performance analysis). The sweep will be over the following parameters: the short, medium, and long lookbacks, and the subset of series that are traded on.