|         |      1 // Core Part 6 about a really dumb investment strategy | 
|         |      2 //===================================================== | 
|         |      3  | 
|         |      4  | 
|         |      5 // generate jar with | 
|         |      6 //   > scala -d drumb.jar  drumb.scala | 
|         |      7  | 
|         |      8  | 
|         |      9 object CW6b {  | 
|         |     10  | 
|         |     11  | 
|         |     12 //two test portfolios | 
|         |     13  | 
|         |     14 val blchip_portfolio = List("GOOG", "AAPL", "MSFT", "IBM", "FB", "AMZN", "BIDU") | 
|         |     15 val rstate_portfolio = List("PLD", "PSA", "AMT", "AIV", "AVB", "BXP", "CCI",  | 
|         |     16                             "DLR", "EQIX", "EQR", "ESS", "EXR", "FRT", "HCP")  | 
|         |     17  | 
|         |     18 import io.Source | 
|         |     19 import scala.util._ | 
|         |     20  | 
|         |     21 // (1) The function below takes a stock symbol and a year as arguments. | 
|         |     22 //     It should read the corresponding CSV-file and reads the January  | 
|         |     23 //     data from the given year. The data should be collected in a list of | 
|         |     24 //     strings for each line in the CSV-file. | 
|         |     25  | 
|         |     26 def get_january_data(symbol: String, year: Int) : List[String] =  | 
|         |     27   Source.fromFile(symbol ++ ".csv")("ISO-8859-1").getLines().toList.filter(_.startsWith(year.toString)) | 
|         |     28  | 
|         |     29  | 
|         |     30 //test cases | 
|         |     31 //blchip_portfolio.map(get_january_data(_, 2018)) | 
|         |     32 //rstate_portfolio.map(get_january_data(_, 2018)) | 
|         |     33  | 
|         |     34 //get_january_data("GOOG", 1980) | 
|         |     35 //get_january_data("GOOG", 2010) | 
|         |     36 //get_january_data("FB", 2014) | 
|         |     37  | 
|         |     38 //get_january_data("PLD", 1980) | 
|         |     39 //get_january_data("EQIX", 2010) | 
|         |     40 //get_january_data("ESS", 2014) | 
|         |     41  | 
|         |     42  | 
|         |     43 // (2) From the output of the get_january_data function, the next function  | 
|         |     44 //     should extract the first line (if it exists) and the corresponding | 
|         |     45 //     first trading price in that year with type Option[Double]. If no line  | 
|         |     46 //     is generated by get_january_data then the result is None; Some if  | 
|         |     47 //     there is a price. | 
|         |     48  | 
|         |     49 def get_first_price(symbol: String, year: Int) : Option[Double] = { | 
|         |     50   val data = Try(Some(get_january_data(symbol, year).head)) getOrElse None  | 
|         |     51   data.map(_.split(",").toList(1).toDouble) | 
|         |     52 } | 
|         |     53  | 
|         |     54 //test cases | 
|         |     55 //get_first_price("GOOG", 1980) | 
|         |     56 //get_first_price("GOOG", 2010) | 
|         |     57 //get_first_price("FB", 2014) | 
|         |     58  | 
|         |     59 /* | 
|         |     60 for (i <- 1978 to 2018) { | 
|         |     61   println(blchip_portfolio.map(get_first_price(_, i))) | 
|         |     62 } | 
|         |     63  | 
|         |     64 for (i <- 1978 to 2018) { | 
|         |     65   println(rstate_portfolio.map(get_first_price(_, i))) | 
|         |     66 } | 
|         |     67 */  | 
|         |     68  | 
|         |     69  | 
|         |     70 // (3) Complete the function below that obtains all first prices | 
|         |     71 //     for the stock symbols from a portfolio (list of strings) and  | 
|         |     72 //     for the given range of years. The inner lists are for the | 
|         |     73 //     stock symbols and the outer list for the years. | 
|         |     74  | 
|         |     75 def get_prices(portfolio: List[String], years: Range): List[List[Option[Double]]] =  | 
|         |     76   for (year <- years.toList) yield | 
|         |     77     for (symbol <- portfolio) yield get_first_price(symbol, year) | 
|         |     78  | 
|         |     79  | 
|         |     80 //test cases | 
|         |     81  | 
|         |     82 //println("Task 3 data from Google and Apple in 2010 to 2012") | 
|         |     83 //val goog_aapl_prices = get_prices(List("GOOG", "AAPL"), 2010 to 2012) | 
|         |     84 //println(goog_aapl_prices.toString ++ "\n") | 
|         |     85  | 
|         |     86 //val p_fb = get_prices(List("FB"), 2012 to 2014) | 
|         |     87 //val tt = get_prices(List("BIDU"), 2004 to 2008) | 
|         |     88  | 
|         |     89  | 
|         |     90 // (4) The function below calculates the change factor (delta) between | 
|         |     91 //     a price in year n and a price in year n + 1.  | 
|         |     92  | 
|         |     93 def get_delta(price_old: Option[Double], price_new: Option[Double]) : Option[Double] = { | 
|         |     94   (price_old, price_new) match { | 
|         |     95     case (Some(x), Some(y)) => Some((y - x) / x) | 
|         |     96     case _ => None | 
|         |     97   } | 
|         |     98 } | 
|         |     99  | 
|         |    100  | 
|         |    101 // (5) The next function calculates all change factors for all prices (from a  | 
|         |    102 //     portfolio). The input to this function are the nested lists created by  | 
|         |    103 //     get_prices above. | 
|         |    104  | 
|         |    105 def get_deltas(data: List[List[Option[Double]]]):  List[List[Option[Double]]] = | 
|         |    106   for (i <- (0 until (data.length - 1)).toList) yield  | 
|         |    107     for (j <- (0 until (data(0).length)).toList) yield get_delta(data(i)(j), data(i + 1)(j)) | 
|         |    108  | 
|         |    109  | 
|         |    110  | 
|         |    111 // test case using the prices calculated above | 
|         |    112  | 
|         |    113 //println("Task 5 change prices from Google and Apple in 2010 and 2011") | 
|         |    114 //val goog_aapl_deltas = get_deltas(goog_aapl_prices) | 
|         |    115 //println(goog_aapl_deltas.toString ++ "\n") | 
|         |    116  | 
|         |    117 //val ttd = get_deltas(tt) | 
|         |    118  | 
|         |    119  | 
|         |    120 // (6) Write a function that given change factors, a starting balance and an index, | 
|         |    121 //     calculates the yearly yield, i.e. new balance, according to our dumb investment  | 
|         |    122 //     strategy. Index points to a year in the data list. | 
|         |    123  | 
|         |    124 def yearly_yield(data: List[List[Option[Double]]], balance: Long, index: Int): Long = { | 
|         |    125   val somes = data(index).flatten | 
|         |    126   val somes_length = somes.length | 
|         |    127   if (somes_length == 0) balance | 
|         |    128   else { | 
|         |    129     val portion: Double = balance.toDouble / somes_length.toDouble | 
|         |    130     balance + (for (x <- somes) yield (x * portion)).sum.toLong | 
|         |    131   } | 
|         |    132 } | 
|         |    133  | 
|         |    134 // test case using the deltas calculated above | 
|         |    135 //println("Task 6 yield from Google and Apple in 2010 with  balance 100") | 
|         |    136  | 
|         |    137 //val d0 = goog_aapl_deltas(0)(0) | 
|         |    138 //val d1 = goog_aapl_deltas(0)(1) | 
|         |    139 //println(s"50 * ${d0.get} + 50 * ${d1.get} = ${50.toDouble * d0.get + 50.toDouble * d1.get}") | 
|         |    140  | 
|         |    141  | 
|         |    142 //val goog_aapl_yield = yearly_yield(goog_aapl_deltas, 100, 0) | 
|         |    143 //println("Rounded yield: " ++ goog_aapl_yield.toString ++ "\n") | 
|         |    144  | 
|         |    145  | 
|         |    146 //yearly_yield(get_prices(rstate_portfolio, 2016 to 2018), 100, 2)  | 
|         |    147 //get_prices(rstate_portfolio, 2016 to 2018)(2).flatten.sum | 
|         |    148  | 
|         |    149  | 
|         |    150 // (7) Write a function compound_yield that calculates the overall balance for a  | 
|         |    151 //     range of years where in each year the yearly profit is compounded to the new  | 
|         |    152 //     balances and then re-invested into our portfolio. For this use the function and  | 
|         |    153 //     results generated under (6). The function investment calls compound_yield | 
|         |    154 //     with the appropriate deltas and the first index. | 
|         |    155  | 
|         |    156  | 
|         |    157 def compound_yield(data: List[List[Option[Double]]], balance: Long, index: Int): Long = { | 
|         |    158   if (index >= data.length) balance else { | 
|         |    159     val new_balance = yearly_yield(data, balance, index) | 
|         |    160     compound_yield(data, new_balance, index + 1) | 
|         |    161   } | 
|         |    162 } | 
|         |    163  | 
|         |    164 def investment(portfolio: List[String], years: Range, start_balance: Long): Long = { | 
|         |    165   compound_yield(get_deltas(get_prices(portfolio, years)), start_balance, 0) | 
|         |    166 } | 
|         |    167  | 
|         |    168  | 
|         |    169  | 
|         |    170 //test cases for the two portfolios given above | 
|         |    171  | 
|         |    172 //println("Real data: " + investment(rstate_portfolio, 1978 to 2019, 100)) | 
|         |    173 //println("Blue data: " + investment(blchip_portfolio, 1978 to 2019, 100)) | 
|         |    174  | 
|         |    175 } | 
|         |    176  | 
|         |    177  | 
|         |    178  |