Qstk Homework 3-10

Part of Georgia Tech-Coursera's Computational Investing Course

Homework 1:

Part 2:Write a Python function that can simulate and assess the performance of a 4 stock portfolio.

Inputs to the function include:

  • Start date
  • End date
  • Symbols for for equities (e.g., GOOG, AAPL, GLD, XOM)
  • Allocations to the equities at the beginning of the simulation (e.g., 0.2, 0.3, 0.4, 0.1)

The function should return:

  • Standard deviation of daily returns of the total portfolio
  • Average daily return of the total portfolio
  • Sharpe ratio (Always assume you have 252 trading days in an year. And risk free rate = 0) of the total portfolio
  • Cumulative return of the total portfolio

An example of how you might call the function in your program:

vol, daily_ret, sharpe, cum_ret = simulate(startdate, enddate, ['GOOG','AAPL','GLD','XOM'], [0.2,0.3,0.4,0.1])

Some assumptions:

  • Allocate some amount of value to each equity on the first day. You then "hold" those investments for the entire year.
  • Use adjusted close data. In QSTK, this is 'close'
  • Report statistics for the entire portfolio

Part 2.5: Make sure your simulate() function gives correct output. Check it against the examples below.

Part 3: Use your function to create a portfolio optimizer!

Create a for loop (or nested for loop) that enables you to test every "legal" set of allocations to the 4 stocks. Keep track of the "best" portfolio, and print it out at the end.

  • "Legal" set of allocations means: The allocations sum to 1.0. The allocations are in 10% increments.
    • Example legal allocations: [1.0, 0.0, 0.0, 0.0], [0.1, 0.1, 0.1, 0.7]
  • "Best" portfolio means: Highest Sharpe Ratio.

ImplementationEdit

See https://gist.github.com/theedkay/5215968

#imports # QSTK Imports import QSTK.qstkutil.qsdateutil as du import QSTK.qstkutil.tsutil as tsu import QSTK.qstkutil.DataAccess as da # Third Party Imports import datetime as dt import matplotlib.pyplot as plt import pandas as pd import numpy as np import time from scipy.optimize import minimize ' Reads data from Yahoo Finance ' @param li_startDate: start date in list structure: [year,month,day] e.g. [2012,1,28] ' @param li_endDate: end date in list structure: [year,month,day] e.g. [2012,12,31] ' @param ls_symbols: list of symbols: e.g. ['GOOG','AAPL','GLD','XOM'] ' @returns d_data: dictionary structure of data mapped to keys e.g. 'open', 'close' def readData(li_startDate, li_endDate, ls_symbols): #Create datetime objects for Start and End dates (STL) dt_start = dt.datetime(li_startDate[0], li_startDate[1], li_startDate[2]); dt_end = dt.datetime(li_endDate[0], li_endDate[1], li_endDate[2]); #Initialize daily timestamp: closing prices, so timestamp should be hours=16 (STL) dt_timeofday = dt.timedelta(hours=16); #Get a list of trading days between the start and end dates (QSTK) ldt_timestamps = du.getNYSEdays(dt_start, dt_end, dt_timeofday); #Create an object of the QSTK-dataaccess class with Yahoo as the source (QSTK) c_dataobj = da.DataAccess('Yahoo', cachestalltime=0); #Keys to be read from the data ls_keys = ['open', 'high', 'low', 'close', 'volume', 'actual_close']; #Read the data and map it to ls_keys via dict() (i.e. Hash Table structure) ldf_data = c_dataobj.get_data(ldt_timestamps, ls_symbols, ls_keys); d_data = dict(zip(ls_keys, ldf_data)); return [d_data, dt_start, dt_end, dt_timeofday, ldt_timestamps]; ' Calculate Portfolio Statistics ' @param na_normalized_price: NumPy Array for normalized prices (starts at 1) ' @param lf_allocations: allocation list ' @return list of statistics: ' (Volatility, Average Return, Sharpe, Cumulative Return) def calcStats(na_normalized_price, lf_allocations): #Calculate cumulative daily portfolio value #row-wise multiplication by weights na_weighted_price = na_normalized_price * lf_allocations; #row-wise sum na_portf_value = na_weighted_price.copy().sum(axis=1); #Calculate daily returns on portfolio na_portf_rets = na_portf_value.copy() tsu.returnize0(na_portf_rets); #Calculate volatility (stdev) of daily returns of portfolio f_portf_volatility = np.std(na_portf_rets); #Calculate average daily returns of portfolio f_portf_avgret = np.mean(na_portf_rets); #Calculate portfolio sharpe ratio (avg portfolio return / portfolio stdev) * sqrt(252) f_portf_sharpe = (f_portf_avgret / f_portf_volatility) * np.sqrt(250); #Calculate cumulative daily return #...using recursive function def cumret(t, lf_returns): #base-case if t==0: return (1 + lf_returns[0]); #continuation return (cumret(t-1, lf_returns) * (1 + lf_returns[t])); f_portf_cumrets = cumret(na_portf_rets.size - 1, na_portf_rets); return [f_portf_volatility, f_portf_avgret, f_portf_sharpe, f_portf_cumrets, na_portf_value]; ' Simulate and assess performance of multi-stock portfolio ' @param li_startDate: start date in list structure: [year,month,day] e.g. [2012,1,28] ' @param li_endDate: end date in list structure: [year,month,day] e.g. [2012,12,31] ' @param ls_symbols: list of symbols: e.g. ['GOOG','AAPL','GLD','XOM'] ' @param lf_allocations: list of allocations: e.g. [0.2,0.3,0.4,0.1] ' @param b_print: print results (True/False) def simulate(li_startDate, li_endDate, ls_symbols, lf_allocations, b_print): start = time.time(); #Check if ls_symbols and lf_allocations have same length if len(ls_symbols) != len(lf_allocations): print "ERROR: Make sure symbol and allocation lists have same number of elements."; return; #Check if lf_allocations adds up to 1 sumAllocations = 0; for x in lf_allocations: sumAllocations += x; if sumAllocations != 1: print "ERROR: Make sure allocations add up to 1."; return; #Prepare data for statistics d_data = readData(li_startDate, li_endDate, ls_symbols)[0]; #Get numpy ndarray of close prices (numPy) na_price = d_data['close'].values; #Normalize prices to start at 1 (if we do not do this, then portfolio value #must be calculated by weight*Budget/startPriceOfStock) na_normalized_price = na_price / na_price[0,:]; lf_Stats = calcStats(na_normalized_price, lf_allocations); #Print results if b_print: print "Start Date: ", li_startDate; print "End Date: ", li_endDate; print "Symbols: ", ls_symbols; print "Volatility (stdev daily returns): " , lf_Stats[0]; print "Average daily returns: " , lf_Stats[1]; print "Sharpe ratio: " , lf_Stats[2]; print "Cumulative daily return: " , lf_Stats[3]; print "Run in: " , (time.time() - start) , " seconds."; #Return list: [Volatility, Average Returns, Sharpe Ratio, Cumulative Return] return lf_Stats[0:3]; ' Optimize portfolio allocations to maximise Sharpe ratio ' @param li_startDate: start date in list structure: [year,month,day] e.g. [2012,1,28] ' @param li_endDate: end date in list structure: [year,month,day] e.g. [2012,12,31] ' @param ls_symbols: list of symbols: e.g. ['GOOG','AAPL','GLD','XOM'] ' @param s_precision: true - precise optimization; false - 10% increments & positive weights def optimize(li_startDate, li_endDate, ls_symbols, b_precision): start = time.time(); #Prepare data for statistics ld_alldata = readData(li_startDate, li_endDate, ls_symbols); d_data = ld_alldata[0]; #Get numpy ndarray of close prices (numPy) na_price = d_data['close'].values; #Normalize prices to start at 1 (if we do not do this, then portfolio value #must be calculated by weight*Budget/startPriceOfStock) na_normalized_price = na_price / na_price[0,:]; if b_precision: #Precise optimization: #Define objective function (sharpe ratio) def objective_sharpe(x): return simulate(li_startDate, li_endDate, ls_symbols, x)[2]; #Work on this later... else: #Imprecise optimization (required in Homework 1) #Using backtracking and permutation #Permutation function def all_perms(elements): if len(elements) <=1: yield elements; else: for perm in all_perms(elements[1:]): for i in range(len(elements)): #nb elements[0:1] works in both string and list contexts yield perm[:i] + elements[0:1] + perm[i:]; #Backtracking function results in list of integers that sum to 10 global li_sol, li_valid, i_sum, i_numEls; TARGET = 10; li_sol = [0] * len(ls_symbols); #li_sol = [0] * TARGET; li_valid = []; i_sum = 0; i_numEls = 0; def back(lastEl): global li_sol, li_valid, i_sum, i_numEls; #base-case if i_numEls >= len(ls_symbols): if i_sum == TARGET: li_valid.extend(list(all_perms(li_sol))); return; #continuation for i in range(lastEl, TARGET + 1 - i_sum): i_sum += i; li_sol[i_numEls] = i; i_numEls += 1; back(i); #undo i_sum -= i; i_numEls -= 1; return; back(0); #Convert to float array that sum to 1 global lf_valid; lf_valid = []; for i in li_valid: lf_valid.append([j/10.0 for j in i]); #Calculate Sharpe ratio for each valid allocation f_CurrMaxSharpe = 0.0; for allocation in lf_valid: t_Stats = calcStats(na_normalized_price, allocation); if t_Stats[2] > f_CurrMaxSharpe: lf_CurrStats = t_Stats f_CurrMaxSharpe = t_Stats[2]; lf_CurrEffAllocation = allocation; #Plot portfolio daily values over time period #Obtain benchmark $SPX data d_spx = readData(li_startDate, li_endDate, ["$SPX"])[0]; na_spxprice = d_spx['close'].values; na_spxnormalized_price = na_spxprice / na_spxprice[0,:]; lf_spxStats = calcStats(na_spxnormalized_price, [1]); #Plot plt.clf(); plt.plot(ld_alldata[4], lf_spxStats[4]); #SPX plt.plot(ld_alldata[4], lf_CurrStats[4]); #Portfolio plt.axhline(y=0, color='r'); plt.legend(['$SPX', 'Portfolio']); plt.ylabel('Daily Value'); plt.xlabel('Date'); plt.savefig('chart.pdf', format='pdf'); #Print results: print "Start Date: ", li_startDate; print "End Date: ", li_endDate; print "Symbols: ", ls_symbols; print "Optimal Allocations: ", lf_CurrEffAllocation; print "Volatility (stdev daily returns): " , lf_CurrStats[0]; print "Average daily returns: " , lf_CurrStats[1]; print "Sharpe ratio: " , lf_CurrStats[2]; print "Cumulative daily return: " , lf_CurrStats[3]; print "Run in: " , (time.time() - start) , " seconds."; Actual Implementation Starts: startDate = [2011,1,1]; endDate = [2011,12,31]; simulate(startDate,endDate,['AAPL', GLD', 'GOOG', XOM'], [0.4, 0.4, 0.0, 0.2], True); optimize(startDate,endDate,['AAPL', 'GLD', 'GOOG', 'XOM'], False);

ResultsEdit

Start Date:  [2011, 1, 1]

End Date:  [2011, 12, 31]

Symbols:  ['AAPL', 'GLD', 'GOOG', 'XOM']

Optimal Allocations:  [0.4, 0.4, 0.0, 0.2]

Volatility (stdev daily returns):  0.0101467067654

Average daily returns:  0.000657261102001

Sharpe ratio:  1.0241954103

Cumulative daily return:  1.16487261965

Run in:  2.82899999619  seconds.

ExplanationEdit

Overview

In this project you will create a basic market simulator that accepts trading orders and keeps track of a portfolio's value and saves it to a file. You will also create another program that assesses the performance of that portfolio.

To Do

Part 1: Create a market simulation tool, marketsim.py that takes a command line like this:

python marketsim.py 1000000 orders.csv values.csv

Where the number represents starting cash and orders.csv is a file of orders organized like this:

  • Year
  • Month
  • Day
  • Symbol
  • BUY or SELL
  • Number of Shares

For example:

2008, 12, 3, AAPL, BUY, 130 2008, 12, 8, AAPL, SELL, 130 2008, 12, 5, IBM, BUY, 50

Your simulator should calculate the total value of the portfolio for each day using adjusted closing prices (cash plus value of equities) and print the result to the file values.csv. The contents of the values.csv file should look something like this:

2008, 12, 3, 1000000 2008, 12, 4, 1000010 2008, 12, 5, 1000250 ...

Part 2: Create a portfolio analysis tool, analyze.py, that takes a command line like this:

python analyze.py values.csv \$SPX

The tool should read in the daily values (cumulative portfolio value) from values.csv and plot them. It should use the symbol on the command line as a benchmark for comparison (in this case $SPX). Using this information, analyze.py should:

  • Plot the price history over the trading period.
  • Your program should also output:
    • Standard deviation of daily returns of the total portfolio
    • Average daily return of the total portfolio
    • Sharpe ratio (Always assume you have 252 trading days in an year. And risk free rate = 0) of the total portfolio
    • Cumulative return of the total portfolio

Orders files to run your code on

Grab this zip file to get the input files to run your code against: media:orders-files.zip

Short example to check your code

Here is a very very short example that you can use to check your code. Assuming a 1,000,000 starting cash and the orders file orders-short.csv:

The orders file:

2011,1,05,AAPL,Buy,1500, 2011,1,20,AAPL,Sell,1500,

The daily value of the portfolio (spaces added to help things line up):

2011, 1, 5, 1000000 2011, 1, 6, 999595 2011, 1, 7, 1003165 2011, 1, 10, 1012630 2011, 1, 11, 1011415 2011, 1, 12, 1015570 2011, 1, 13, 1017445 2011, 1, 14, 1021630 2011, 1, 18, 1009930 2011, 1, 19, 1007230 2011, 1, 20, 998035

For reference, here are the adjusted close values for AAPL on the relevant days:

2011-01-05 16:00:00 332.57 2011-01-06 16:00:00 332.30 2011-01-07 16:00:00 334.68 2011-01-10 16:00:00 340.99 2011-01-11 16:00:00 340.18 2011-01-12 16:00:00 342.95 2011-01-13 16:00:00 344.20 2011-01-14 16:00:00 346.99 2011-01-18 16:00:00 339.19 2011-01-19 16:00:00 337.39 2011-01-20 16:00:00 331.26

The full results:

Details of the Performance of the portfolio : Data Range : 2011-01-05 16:00:00 to 2011-01-20 16:00:00 Sharpe Ratio of Fund : -0.449182051041 Sharpe Ratio of $SPX : 0.88647463107 Total Return of Fund : 0.998035 Total Return of $SPX : 1.00289841449 Standard Deviation of Fund : 0.00573613516299 Standard Deviation of $SPX : 0.00492987789459 Average Daily Return of Fund : -0.000162308588036 Average Daily Return of $SPX : 0.000275297459588

More comprehensive examples

We provide an example, orders.csv that you can use to test your code, and compare with others. All of these runs assume a starting portfolio of 1000000 ($1M).

The final value of the portfolio using the sample file is -- 2011,12,20,1133860 Details of the Performance of the portfolio : Data Range : 2011-01-10 16:00:00 to 2011-12-20 16:00:00 Sharpe Ratio of Fund : 1.21540462111 Sharpe Ratio of $SPX : 0.0183391412227 Total Return of Fund : 1.13386 Total Return of $SPX : 0.97759401457 Standard Deviation of Fund : 0.00717514512699 Standard Deviation of $SPX : 0.0149090969828 Average Daily Return of Fund : 0.000549352749569 Average Daily Return of $SPX : 1.72238432443e-05


The other sample file is orders2.csv that you can use to test your code, and compare with others.

The final value of the portfolio using the sample file is -- 2011,12,14, 1078753 Details of the Performance of the portfolio Data Range : 2011-01-14 16:00:00 to 2011-12-14 16:00:00 Sharpe Ratio of Fund : 0.788985460132 Sharpe Ratio of $SPX : -0.177204632551 Total Return of Fund : 1.0787526 Total Return of $SPX : 0.937041848381 Standard Deviation of Fund : 0.00708034136287 Standard Deviation of $SPX : 0.0149914504972 Average Daily Return of Fund : 0.000351902965125 Average Daily Return of $SPX : -0.000167347202139

Implementation suggestions & assumptions

In terms of execution prices, you should assume you get the adjusted close price for the day of the trade.

Here are some hints on how to build it: media:marketsim-guidelines.pdf

What to expect when you turn in your assignment (Coursera)

Once you create the tools described above, you will be asked to run specific orders files through your code and then to run the results through your analyze tool to report on various measures such as Sharpe Ratio and Cumulative Return.

Deliverables for on campus GT students

To do: Run your code for the two files orders.csv and orders2.csv. Generate charts for the two runs.

To turn in:

  • The code for your two programs: marketsim.py, analyze.py
  • A report, report.pdf that includes:
    • The 2 charts for the two orders files.
    • Text output of your analysis code.
alt Example chart. $DJI (green) is the benchmark blue is the fund.
Categories: 1

0 Replies to “Qstk Homework 3-10”

Leave a comment

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *