├── .gitignore ├── 01_capm └── capm.py ├── 02a_market_sim ├── .gitignore ├── Portfolio_SPX_normalized_prices.png ├── analysis.py ├── df_trades.png ├── df_trades_bollinger.png ├── grade_marketsim.py ├── grading.py ├── grading_util.py ├── marketsim.py ├── orders.png ├── orders │ ├── orders-01.csv │ ├── orders-02.csv │ ├── orders-03.csv │ ├── orders-04.csv │ ├── orders-05.csv │ ├── orders-06.csv │ ├── orders-07.csv │ ├── orders-08.csv │ ├── orders-09.csv │ ├── orders-10.csv │ ├── orders-11.csv │ ├── orders-12.csv │ ├── orders-short.csv │ ├── orders.csv │ └── orders2.csv ├── portval_manual.ods └── test_marketsim.py ├── 02b_event_analyzer ├── GOOG_Bollinger.png ├── Manual_calculation_for_AES&.ods ├── bollinger_event_chart.pdf ├── df_trades.csv ├── df_trades_bollinger.csv ├── event_analyzer.py ├── event_analyzer_bollinger.py └── event_chart.pdf ├── README.md └── util.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.pyc 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | env/ 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # Jupyter Notebook 72 | .ipynb_checkpoints 73 | 74 | # pyenv 75 | .python-version 76 | 77 | # celery beat schedule file 78 | celerybeat-schedule 79 | 80 | # SageMath parsed files 81 | *.sage.py 82 | 83 | # dotenv 84 | .env 85 | 86 | # virtualenv 87 | .venv 88 | venv/ 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | .spyproject 94 | 95 | # Rope project settings 96 | .ropeproject 97 | 98 | # mkdocs documentation 99 | /site 100 | 101 | # mypy 102 | .mypy_cache/ 103 | -------------------------------------------------------------------------------- /01_capm/capm.py: -------------------------------------------------------------------------------- 1 | """An example of how to use Capital Asset Pricing Model (CAPM)""" 2 | 3 | import numpy as np 4 | from scipy.optimize import fsolve 5 | 6 | 7 | def compute_stock_return(alpha, beta, market_return): 8 | """ 9 | Compute stock return using the CAPM equation 10 | stock_return = beta * market_return + alpha 11 | 12 | Parameters: 13 | alpha: A measure of the active return on a stock 14 | beta: A measure of the risk arising from exposure to general market (e.g. SP500) movements 15 | market_return: Return rate of the market (e.g. SP500) on a particular day 16 | 17 | Returns: 18 | stock_return: Return rate of the stock on a particular day 19 | """ 20 | stock_return = beta * market_return + alpha 21 | return stock_return 22 | 23 | 24 | def compute_portfolio_return(weights, alphas, betas, market_return): 25 | """ 26 | Compute portfolio return using the CAPM equation 27 | portfolio_return = sum([(stock_beta * market_return + stock_alpha) * stock_weight for each stock in portfolio]) 28 | 29 | Parameters: 30 | weights: A list of weights for stocks in the portfolio 31 | alphas: A list of betas for stocks in the portfolio 32 | betas: A list of betas for stocks in the portfolio 33 | market_return: Return rate of the market (e.g. SP500) on a particular day 34 | 35 | Returns: 36 | portfolio_return: Return rate of the stock on a particular day 37 | """ 38 | portfolio_return = sum([(betas[i] * market_return + alphas[i]) * weights[i] for i in range(len(weights))]) 39 | return portfolio_return 40 | 41 | 42 | def compute_portfolio_return2(weights, stock_returns): 43 | """ 44 | Compute portfolio return when having weights and stock returns 45 | portfolio_return = sum([stock_return * stock_weight for each stock in portfolio]) 46 | 47 | Parameters: 48 | weights: A list of weights for stocks in the portfolio 49 | stock_returns: A list of returns for stocks in the portfolio 50 | 51 | Returns: 52 | portfolio_return: Return rate of the stock on a particular day 53 | """ 54 | portfolio_return = sum([weights[i] * stock_returns[i] for i in range(len(weights))]) 55 | return portfolio_return 56 | 57 | 58 | def weight_function_for_min_risk(weight_variables, betas): 59 | """ 60 | A function of weights of stocks in a portfolio to remove market risk, i.e. 61 | weight_variables * betas = 0 62 | 63 | Parameters: 64 | weight_variables: A list of unknown weight variables to be solved 65 | betas: A list of betas for stocks in the portfolio 66 | 67 | Returns: 68 | weights: A list of equations for weights, assuming 2 equations 69 | """ 70 | 71 | # sum of weight_variables * betas must be 0 72 | first_eq = sum([w * b for w, b in zip(weight_variables, betas)]) 73 | 74 | # Absolute values of weights must add up to 1 75 | second_eq = sum([abs(w) for w in weight_variables]) - 1.0 76 | return [first_eq, second_eq] 77 | 78 | 79 | def test_run(): 80 | """ Example of a portfolio with two stocks 81 | stock_A: predict +1% over market, i.e. alpha_A == 1% 82 | beta_A == 1.0 83 | $50 long position in stock A 84 | 85 | stock_B: predict -1% below market, i.e. alpha_B == -1% 86 | beta_A == 2.0 87 | $50 short position in stock B 88 | """ 89 | alpha_A = 0.01 90 | beta_A = 1.0 91 | position_A = 50.0 92 | 93 | alpha_B = -0.01 94 | beta_B = 2.0 95 | position_B = -50.0 96 | 97 | market_return = 0.1 98 | 99 | # Calculate stock A return 100 | return_A = compute_stock_return(alpha_A, beta_A, market_return) 101 | return_B = compute_stock_return(alpha_B, beta_B, market_return) 102 | return_A_dollar = return_A * position_A 103 | return_B_dollar = return_B * position_B 104 | 105 | portfolio_return = compute_portfolio_return([0.5, -0.5], [alpha_A, alpha_B], [beta_A, beta_B], market_return) 106 | try: 107 | assert (portfolio_return == compute_portfolio_return2([0.5, -0.5], [return_A, return_B])) 108 | except AssertionError: 109 | print ("We have a problem with compute_stock_return") 110 | 111 | print ("Stock A's return in % = {:.0%}, in dollars = {}".format(return_A, return_A_dollar)) 112 | print ("Stock B's return in % = {:.0%}, in dollars = {}".format(return_B, return_B_dollar)) 113 | print ("Portfolio return in % = {:.0%}, in dollars = {}".format(portfolio_return, return_A_dollar + return_B_dollar)) 114 | 115 | # Compute weights that minimize market risks 116 | weight_sols = fsolve(weight_function_for_min_risk, (0.5, 0.5), args=([beta_A, beta_B],)) 117 | print ("Weights that minimize market risks: stock A's weight = {:.2f}, stock B's weight = {:.2f}".format(weight_sols[0], weight_sols[1])) 118 | 119 | 120 | if __name__ == "__main__": 121 | test_run() 122 | -------------------------------------------------------------------------------- /02a_market_sim/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.pyc 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | env/ 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # Jupyter Notebook 72 | .ipynb_checkpoints 73 | 74 | # pyenv 75 | .python-version 76 | 77 | # celery beat schedule file 78 | celerybeat-schedule 79 | 80 | # SageMath parsed files 81 | *.sage.py 82 | 83 | # dotenv 84 | .env 85 | 86 | # virtualenv 87 | .venv 88 | venv/ 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | .spyproject 94 | 95 | # Rope project settings 96 | .ropeproject 97 | 98 | # mkdocs documentation 99 | /site 100 | 101 | # mypy 102 | .mypy_cache/ 103 | 104 | # others 105 | comments.txt 106 | points.txt 107 | -------------------------------------------------------------------------------- /02a_market_sim/Portfolio_SPX_normalized_prices.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntrang086/computational_investing/ae8f6267b76c02a3fef502ff31ebafc235fc8e2c/02a_market_sim/Portfolio_SPX_normalized_prices.png -------------------------------------------------------------------------------- /02a_market_sim/analysis.py: -------------------------------------------------------------------------------- 1 | """Analyze a portfolio""" 2 | 3 | import pandas as pd 4 | import matplotlib.pyplot as plt 5 | import numpy as np 6 | import datetime as dt 7 | import sys 8 | # Append the path of the directory one level above the current directory to import util 9 | sys.path.append('../') 10 | from util import * 11 | 12 | 13 | def assess_portfolio(sd = dt.datetime(2008,1,1), ed = dt.datetime(2009,1,1), \ 14 | syms = ["GOOG","AAPL","GLD","XOM"], \ 15 | allocs=[0.1,0.2,0.3,0.4], \ 16 | sv=1000000, rfr=0.0, sf=252.0, \ 17 | gen_plot=False): 18 | 19 | """Assess a portfolio by computing statistics 20 | 21 | Parameters: 22 | sd: A datetime object that represents the start date 23 | ed: A datetime object that represents the end date 24 | syms: A list of symbols that make up the portfolio 25 | allocs: A list of allocations to the stocks, must sum to 1.0 26 | sv: Start value of the portfolio 27 | rfr: The risk free return per sample period for the entire date range, assuming it does not change 28 | sf: Sampling frequency per year 29 | gen_plot: If True, create a plot named plot.png 30 | 31 | Returns: 32 | cr: Cumulative return 33 | adr: Average period return (if sf == 252 this is daily return) 34 | sddr: Standard deviation of daily return 35 | sr: Sharpe ratio 36 | ev: End value of portfolio 37 | """ 38 | 39 | # Read in adjusted closing prices for given symbols, date range 40 | dates = pd.date_range(sd, ed) 41 | prices_all = get_data(syms, dates) # automatically adds SPY 42 | prices = prices_all[syms] # only portfolio symbols 43 | prices_SPY = prices_all["SPY"] # only SPY, for comparison later 44 | 45 | # Get daily portfolio value 46 | port_val = get_portfolio_value(prices, allocs, sv) 47 | 48 | # Get portfolio statistics (sddr == volatility) 49 | cr, adr, sddr, sr = get_portfolio_stats(port_val, rfr, sf) 50 | 51 | # Compare daily portfolio value with SPY using a normalized plot 52 | if gen_plot: 53 | # Create a temporary dataframe with both the SPY and Portfolio 54 | df_temp = pd.concat([port_val, prices_SPY], keys=["Portfolio", "SPY"], axis=1) 55 | plot_normalized_data(df_temp, title="Daily portfolio and SPY", xlabel="Date", ylabel="Normalized price") 56 | 57 | # Compute end value 58 | ev = port_val.ix[-1, 0] 59 | 60 | return cr, adr, sddr, sr, ev 61 | 62 | 63 | def get_portfolio_value(prices, allocs, sv): 64 | """Helper function to compute portfolio value 65 | 66 | Parameters: 67 | prices: Adjusted closing prices for portfolio symbols 68 | allocs: A list of allocations to the stocks, must sum to 1.0 69 | sv: Start value of the portfolio 70 | 71 | Returns: 72 | port_val: A dataframe object showing the portfolio value for each day 73 | """ 74 | 75 | # Normalize the prices according to the first day 76 | norm_prices = normalize_data(prices) 77 | 78 | # Compute prices based on the allocations 79 | alloc_prices = norm_prices * allocs 80 | 81 | # Calculate position values 82 | pos_vals = alloc_prices * sv 83 | 84 | # Get daily portfolio value 85 | port_val = pos_vals.sum(axis=1).to_frame() 86 | 87 | return port_val 88 | 89 | 90 | def get_portfolio_stats(port_val, daily_rf, samples_per_year): 91 | """Helper function to compute portfolio statistics 92 | 93 | Parameters: 94 | port_val: A dataframe object showing the portfolio value for each day 95 | daily_rf: Daily risk-free rate, assuming it does not change 96 | samples_per_year: Sampling frequency per year 97 | 98 | Returns: 99 | cr: Cumulative return 100 | adr: Average daily return 101 | sddr: Standard deviation of daily return 102 | sr: Sharpe ratio 103 | """ 104 | cr = port_val.iloc[-1, 0]/port_val.iloc[0, 0] - 1 105 | 106 | daily_returns = compute_daily_returns(port_val)[1:] 107 | adr = daily_returns.iloc[:, 0].mean() 108 | sddr = daily_returns.iloc[:, 0].std() 109 | sr = compute_sharpe_ratio(np.sqrt(samples_per_year), adr, daily_rf, sddr) 110 | 111 | return cr, adr, sddr, sr 112 | 113 | 114 | def plot_normalized_data(df, title, xlabel, ylabel, save_fig=False, fig_name="plot.png"): 115 | """Helper function to normalize and plot data""" 116 | 117 | # Normalize the data 118 | df = normalize_data(df) 119 | 120 | # Plot the normalized data 121 | plot_data(df, title=title, xlabel=xlabel, ylabel=ylabel, save_fig=save_fig, fig_name=fig_name) 122 | 123 | 124 | def test_code(): 125 | # Define input parameters 126 | start_date = dt.datetime(2010,1,1) 127 | end_date = dt.datetime(2010,12,31) 128 | symbols = ["GOOG", "AAPL", "GLD", "XOM"] 129 | allocations = [0.2, 0.3, 0.4, 0.1] 130 | start_val = 1000000 131 | risk_free_rate = 0.0 132 | sample_freq = 252 133 | 134 | # Assess the portfolio 135 | cr, adr, sddr, sr, ev = assess_portfolio(sd = start_date, ed = end_date,\ 136 | syms = symbols, \ 137 | allocs = allocations,\ 138 | sv = start_val, \ 139 | gen_plot = True) 140 | 141 | # Print statistics 142 | print ("Start Date:", start_date) 143 | print ("End Date:", end_date) 144 | print ("Symbols:", symbols) 145 | print ("Allocations:", allocations) 146 | print ("Sharpe Ratio:", sr) 147 | print ("Volatility (stdev of daily returns):", sddr) 148 | print ("Average Daily Return:", adr) 149 | print ("Cumulative Return:", cr) 150 | print ("End value:", ev) 151 | 152 | if __name__ == "__main__": 153 | test_code() 154 | -------------------------------------------------------------------------------- /02a_market_sim/df_trades.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntrang086/computational_investing/ae8f6267b76c02a3fef502ff31ebafc235fc8e2c/02a_market_sim/df_trades.png -------------------------------------------------------------------------------- /02a_market_sim/df_trades_bollinger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ntrang086/computational_investing/ae8f6267b76c02a3fef502ff31ebafc235fc8e2c/02a_market_sim/df_trades_bollinger.png -------------------------------------------------------------------------------- /02a_market_sim/grade_marketsim.py: -------------------------------------------------------------------------------- 1 | """MC2-P1: Market simulator - grading script. 2 | 3 | Usage: 4 | - Switch to a student feedback directory first (will write "points.txt" and "comments.txt" in pwd). 5 | - Run this script with both ml4t/ and student solution in PYTHONPATH, e.g.: 6 | PYTHONPATH=ml4t:MC1-P2/jdoe7 python ml4t/mc2_p1_grading/grade_marketsim.py 7 | 8 | Copyright 2017, Georgia Tech Research Corporation 9 | Atlanta, Georgia 30332-0415 10 | All Rights Reserved 11 | """ 12 | 13 | import pytest 14 | from grading import grader, GradeResult, time_limit, run_with_timeout, IncorrectOutput 15 | 16 | import os 17 | import sys 18 | import traceback as tb 19 | 20 | import numpy as np 21 | import pandas as pd 22 | from collections import namedtuple 23 | 24 | from grading_util import get_data 25 | from grading_util import get_orders_data_file 26 | 27 | # Student code 28 | main_code = "marketsim" # module name to import 29 | 30 | # Test cases 31 | MarketsimTestCase = namedtuple('MarketsimTestCase', ['description', 'group', 'inputs', 'outputs']) 32 | marketsim_test_cases = [ 33 | MarketsimTestCase( 34 | description="Orders 1", 35 | group='basic', 36 | inputs=dict( 37 | orders_file='orders-01.csv', 38 | start_val=1000000, 39 | commission=0., 40 | impact=0. 41 | ), 42 | outputs=dict( 43 | num_days = 245 , 44 | last_day_portval = 1115569.2, 45 | sharpe_ratio = 0.612340613407 , 46 | avg_daily_ret = 0.00055037432146 47 | ) 48 | ), 49 | MarketsimTestCase( 50 | description="Orders 2", 51 | group='basic', 52 | inputs=dict( 53 | orders_file='orders-02.csv', 54 | start_val=1000000, 55 | commission=0., 56 | impact=0. 57 | ), 58 | outputs=dict( 59 | num_days = 245 , 60 | last_day_portval = 1095003.35, 61 | sharpe_ratio = 1.01613520942, 62 | avg_daily_ret = 0.000390534819609 63 | ) 64 | ), 65 | MarketsimTestCase( 66 | description="Orders 3", 67 | group='basic', 68 | inputs=dict( 69 | orders_file='orders-03.csv', 70 | start_val=1000000, 71 | commission=0., 72 | impact=0. 73 | ), 74 | outputs=dict( 75 | num_days = 240, 76 | last_day_portval = 857616.0, 77 | sharpe_ratio = -0.759896272199, 78 | avg_daily_ret = -0.000571326189931 79 | ) 80 | ), 81 | MarketsimTestCase( 82 | description="Orders 4", 83 | group='basic', 84 | inputs=dict( 85 | orders_file='orders-04.csv', 86 | start_val=1000000, 87 | commission=0., 88 | impact=0. 89 | ), 90 | outputs=dict( 91 | num_days = 233, 92 | last_day_portval = 923545.4, 93 | sharpe_ratio = -0.266030146916, 94 | avg_daily_ret = -0.000240200768212 95 | ) 96 | ), 97 | MarketsimTestCase( 98 | description="Orders 5", 99 | group='basic', 100 | inputs=dict( 101 | orders_file='orders-05.csv', 102 | start_val=1000000, 103 | commission=0., 104 | impact=0. 105 | ), 106 | outputs=dict( 107 | num_days = 296, 108 | last_day_portval = 1415563.0, 109 | sharpe_ratio = 2.19591520826, 110 | avg_daily_ret = 0.00121733290744 111 | ) 112 | ), 113 | MarketsimTestCase( 114 | description="Orders 6", 115 | group='basic', 116 | inputs=dict( 117 | orders_file='orders-06.csv', 118 | start_val=1000000, 119 | commission=0., 120 | impact=0. 121 | ), 122 | outputs=dict( 123 | num_days = 210, 124 | last_day_portval = 894604.3, 125 | sharpe_ratio = -1.23463930987, 126 | avg_daily_ret = -0.000511281541086 127 | ) 128 | ), 129 | MarketsimTestCase( 130 | description="Orders 7", 131 | group='basic', 132 | inputs=dict( 133 | orders_file='orders-07.csv', 134 | start_val=1000000, 135 | commission=0., 136 | impact=0. 137 | ), 138 | outputs=dict( 139 | num_days = 237, 140 | last_day_portval = 1106563.3, 141 | sharpe_ratio = 2.10356512897, 142 | avg_daily_ret = 0.0004345040621 143 | ) 144 | ), 145 | MarketsimTestCase( 146 | description="Orders 8", 147 | group='basic', 148 | inputs=dict( 149 | orders_file='orders-08.csv', 150 | start_val=1000000, 151 | commission=0., 152 | impact=0. 153 | ), 154 | outputs=dict( 155 | num_days = 229, 156 | last_day_portval = 1074884.1, 157 | sharpe_ratio = 0.941858298061, 158 | avg_daily_ret = 0.000332404156893 159 | ) 160 | ), 161 | MarketsimTestCase( 162 | description="Orders 9", 163 | group='basic', 164 | inputs=dict( 165 | orders_file='orders-09.csv', 166 | start_val=1000000, 167 | commission=0., 168 | impact=0. 169 | ), 170 | outputs=dict( 171 | num_days = 37, 172 | last_day_portval = 1067710.0, 173 | sharpe_ratio = 2.90848480553, 174 | avg_daily_ret = 0.00187252252117 175 | ) 176 | ), 177 | ######################### 178 | # Commission and impact # 179 | ######################### 180 | MarketsimTestCase( 181 | description="Orders 11, commission", 182 | group='commission', 183 | inputs=dict( 184 | orders_file='orders-11.csv', 185 | start_val=1000000, 186 | commission=9.95, 187 | impact=0. 188 | ), 189 | outputs=dict( 190 | num_days = 37, 191 | last_day_portval = 1045700.45, 192 | sharpe_ratio = 1.80327835983, 193 | avg_daily_ret = 0.00130745837411 194 | ) 195 | ), 196 | MarketsimTestCase( 197 | description="Orders 12, impact", 198 | group='impact', 199 | inputs=dict( 200 | orders_file='orders-12.csv', 201 | start_val=1000000, 202 | commission=0., 203 | impact=0.005 204 | ), 205 | outputs=dict( 206 | num_days = 240, 207 | last_day_portval = 1705686.6665, 208 | sharpe_ratio = 1.26427802107, 209 | avg_daily_ret = 0.00290246139389 210 | ) 211 | ), 212 | MarketsimTestCase( 213 | description="Orders 10, impact and commission", 214 | group='both', 215 | inputs=dict( 216 | orders_file='orders-10.csv', 217 | start_val=1000000, 218 | commission=9.95, 219 | impact=0.005 220 | ), 221 | outputs=dict( 222 | num_days = 141, 223 | last_day_portval = 1026658.3265, 224 | sharpe_ratio = 0.627643575702, 225 | avg_daily_ret = 0.000222013722594 226 | ) 227 | ) 228 | ####################### 229 | # Withheld test cases # 230 | ####################### 231 | ] 232 | 233 | seconds_per_test_case = 10 # execution time limit 234 | 235 | # Grading parameters (picked up by module-level grading fixtures) 236 | max_points = 100.0 # 9.5 * 10 + 2.5 * 2 + 1 secret point 237 | html_pre_block = True # surround comments with HTML
 tag (for T-Square comments field)
238 | 
239 | # Test functon(s)
240 | @pytest.mark.parametrize("description,group,inputs,outputs", marketsim_test_cases)
241 | def test_marketsim(description, group, inputs, outputs, grader):
242 |     """Test compute_portvals() returns correct daily portfolio values.
243 | 
244 |     Requires test description, test case group, inputs, expected outputs, and a grader fixture.
245 |     """
246 | 
247 |     points_earned = 0.0  # initialize points for this test case
248 |     try:
249 |         # Try to import student code (only once)
250 |         if not main_code in globals():
251 |             import importlib
252 |             # * Import module
253 |             mod = importlib.import_module(main_code)
254 |             globals()[main_code] = mod
255 |             # * Import methods to test
256 |             for m in ['compute_portvals']:
257 |                 globals()[m] = getattr(mod, m)
258 | 
259 |         incorrect = False
260 |         msgs = []
261 | 
262 |         if group == 'author':
263 |             try:
264 |                 auth_string = run_with_timeout(marketsim.author,seconds_per_test_case,(),{})
265 |                 if auth_string == 'tb34':
266 |                     incorrect = True
267 |                     msgs.append("   Incorrect author name (tb34)")
268 |                     points_earned = -10
269 |                 elif auth_string == '':
270 |                     incorrect = True
271 |                     msgs.append("   Empty author name")
272 |                     points_earned = -10
273 |             except Exception as e:
274 |                 incorrect = True
275 |                 msgs.append("   Exception occured when calling author() method: {}".format(e))
276 |                 points_earned = -10
277 |         else:
278 |             # Unpack test case
279 |             orders_file = inputs['orders_file']
280 |             start_val = inputs['start_val']
281 |             impct = inputs['impact']
282 |             commish = inputs['commission']
283 | 
284 |             portvals = None
285 |             fullpath_orders_file = get_orders_data_file(orders_file)
286 |             portvals = run_with_timeout(compute_portvals,seconds_per_test_case,(),{'orders_file':fullpath_orders_file,'start_val':start_val,'commission':commish,'impact':impct})
287 | 
288 |             # * Check return type is correct, coax into Series
289 |             assert (type(portvals) == pd.Series) or (type(portvals) == pd.DataFrame and len(portvals.columns) == 1), "You must return a Series or single-column DataFrame!"
290 |             if type(portvals) == pd.DataFrame:
291 |                 portvals = portvals[portvals.columns[0]]  # convert single-column DataFrame to Series
292 |             if group == 'basic':
293 |                 if len(portvals) != outputs['num_days']:
294 |                     incorrect=True
295 |                     msgs.append("   Incorrect number of days: {}, expected {}".format(len(portvals), outputs['num_days']))
296 |                 else: 
297 |                     points_earned += 2.0
298 |                 if abs(portvals[-1]-outputs['last_day_portval']) > (0.001*outputs['last_day_portval']):
299 |                     incorrect=True
300 |                     msgs.append("   Incorrect final value: {}, expected {}".format(portvals[-1],outputs['last_day_portval']))
301 |                 else:
302 |                     points_earned += 5.0
303 |                 adr,sr = get_stats(portvals)
304 |                 if abs(sr-outputs['sharpe_ratio']) > abs(0.001*outputs['sharpe_ratio']):
305 |                     incorrect=True
306 |                     msgs.append("   Incorrect sharpe ratio: {}, expected {}".format(sr,outputs['sharpe_ratio']))
307 |                 else:
308 |                     points_earned += 1.0
309 |                 if abs(adr-outputs['avg_daily_ret']) > abs(0.001*outputs['avg_daily_ret']):
310 |                     incorrect=True
311 |                     msgs.append("   Incorrect avg daily return: {}, expected {}".format(adr,outputs['avg_daily_ret']))
312 |                 else:
313 |                     points_earned += 1.0
314 |             elif group=='commission' or group=='impact' or group=='both':
315 |                 if abs(portvals[-1]-outputs['last_day_portval']) > 0.001:
316 |                     incorrect = True
317 |                     msgs.append("   Incorrect final value: {}, expected {}".format(portvals[-1],outputs['last_day_portval']))
318 |                 else:
319 |                     points_earned += 2.0
320 |         if incorrect:
321 |             raise (IncorrectOutput)
322 |     except Exception as e:
323 |         # Test result: failed
324 |         msg = "Test case description: {}\n".format(description)
325 |         
326 |         # Generate a filtered stacktrace, only showing erroneous lines in student file(s)
327 |         
328 |         tb_list = tb.extract_tb(sys.exc_info()[2])
329 |         if 'grading_traceback' in dir(e):
330 |             tb_list = e.grading_traceback
331 |         for i in range(len(tb_list)):
332 |             row = tb_list[i]
333 |             tb_list[i] = (os.path.basename(row[0]), row[1], row[2], row[3])  # show only filename instead of long absolute path
334 |         tb_list = [row for row in tb_list if row[0] == 'marketsim.py']
335 |         if tb_list:
336 |             msg += "Traceback:\n"
337 |             msg += ''.join(tb.format_list(tb_list))  # contains newlines
338 |         msg += "{}: {}".format(e.__class__.__name__, str(e))
339 | 
340 |         # Report failure result to grader, with stacktrace
341 |         grader.add_result(GradeResult(outcome='failed', points=max(points_earned,0), msg=msg))
342 |         raise
343 |     else:
344 |         # Test result: passed (no exceptions)
345 |         grader.add_result(GradeResult(outcome='passed', points=points_earned, msg=None))
346 | 
347 | def get_stats(port_val):
348 |     daily_rets = (port_val / port_val.shift(1)) - 1
349 |     daily_rets = daily_rets[1:]
350 |     avg_daily_ret = daily_rets.mean()
351 |     std_daily_ret = daily_rets.std()
352 |     sharpe_ratio = np.sqrt(252) * daily_rets.mean() / std_daily_ret
353 |     return avg_daily_ret, sharpe_ratio
354 | 
355 | if __name__ == "__main__":
356 |     pytest.main(["-s", __file__])
357 | 


--------------------------------------------------------------------------------
/02a_market_sim/grading.py:
--------------------------------------------------------------------------------
  1 | """MLT - Grading components (based on pytest fixtures).
  2 | 
  3 | Note: Writes results to "comments.txt" in current working directory.
  4 | 
  5 | Copyright 2017, Georgia Tech Research Corporation
  6 | Atlanta, Georgia 30332-0415
  7 | All Rights Reserved
  8 | """
  9 | 
 10 | import pytest
 11 | import signal
 12 | from collections import namedtuple
 13 | from contextlib import contextmanager
 14 | import multiprocessing
 15 | import sys,traceback
 16 | 
 17 | timeout_manager = multiprocessing.Manager()
 18 | 
 19 | GradeResult = namedtuple('GradeResult', ['outcome', 'points', 'msg'])
 20 | 
 21 | class IncorrectOutput(Exception): pass
 22 | 
 23 | class TimeoutException(Exception): pass
 24 | 
 25 | class Grader(object):
 26 |     """Main grader class; an instance of this is passed in through a pytest fixture."""
 27 | 
 28 |     def __init__(self, max_points=None, html_pre_block=False):
 29 |         self.max_points = max_points
 30 |         self.html_pre_block = html_pre_block
 31 |         self.total_points = 0.0
 32 |         self.results = []
 33 |         self.performance = None
 34 | 
 35 |     def add_result(self, result):
 36 |         self.results.append(result)
 37 |         self.add_points(result.points)
 38 | 
 39 |     def add_points(self, points):
 40 |         self.total_points += points
 41 | 
 42 |     def add_performance(self,perf):
 43 |         if self.performance is None:
 44 |             self.performance = perf
 45 |         else:
 46 |             self.performance = self.performance + perf
 47 | 
 48 |     def summary(self):
 49 |         num_tests = len(self.results)
 50 |         max_points = self.max_points if self.max_points is not None else float(num_tests)
 51 |         tests_passed = len([result for result in self.results if result.outcome == 'passed'])
 52 |         # (BPH) return "Total points: {} out of {}\nTests passed: {} out of {}".format(self.total_points, max_points, tests_passed, num_tests)
 53 |         # (BPH) Removing points earned from comments.txt output as part
 54 |         # (BPH) of the autograder -> "test suite" move
 55 |         return "Tests passed: {} out of {}".format(tests_passed, num_tests)
 56 | 
 57 |     def details(self):
 58 |         # (BPH) return "\n".join("Test #{}: {}, points earned: {}{}".format(i, self.results[i].outcome, self.results[i].points, (("\n" + self.results[i].msg + "\n") if self.results[i].msg is not None else "")) for i in range(len(self.results)))
 59 |         # (BPH) Removing point's earned from comments.txt output as part
 60 |         # (BPH) of the autograder->"test suite" move
 61 |         return "\n".join("Test #{}: {} {}".format(i, self.results[i].outcome, (("\n" + self.results[i].msg + "\n") if self.results[i].msg is not None else "")) for i in range(len(self.results)))
 62 | 
 63 |     def write_points(self, filename="points.txt"):
 64 |         print ("[GRADER] Writing points to \"{}\"...".format(filename))  # [debug]
 65 |         with open(filename, "w") as f:
 66 |             f.write("{}\n".format(self.total_points))
 67 |     def write_performance(self,filename="performance.txt"):
 68 |         if self.performance is None:
 69 |             print ("No performance metric collected, skipping")
 70 |         else:
 71 |             print ("[GRADER] Writing performance to \"{}\"...".format(filename))
 72 |             with open(filename,"w") as f:
 73 |                 f.write("{}\n".format(self.performance))
 74 |     def write_comments(self, filename="comments.txt"):
 75 |         # Build comments string
 76 |         print ("[GRADER] Writing comments to \"{}\"...".format(filename))  # [debug]
 77 |         comments = "--- Summary ---\n" + self.summary() + "\n"
 78 |         details = self.details()
 79 |         if details:
 80 |             comments += "\n--- Details ---\n" + details + "\n"
 81 |         print ("\n{}".format(comments))  # [debug]
 82 | 
 83 |         # Write to file
 84 |         with open(filename, "w") as f:
 85 |             if self.html_pre_block:
 86 |                 f.write("
")
 87 |             f.write(comments)
 88 |             if self.html_pre_block:
 89 |                 f.write("
\n") 90 | 91 | def __str__(self): 92 | return "<{} at {:x}: total_points: {}, #results: {}>".format(self.__class__.__name__, id(self), self.total_points, len(self.results)) 93 | 94 | 95 | @contextmanager 96 | def time_limit(seconds, msg="Exceeded time limit!"): 97 | """A contextmanager that raises a TimeoutException if execution takes longer than specified time. 98 | 99 | Usage: 100 | with time_limit(1): 101 | # do stuff within 1 second 102 | 103 | Note: seconds must be an integer. 104 | Based on: http://stackoverflow.com/a/601168 105 | """ 106 | def signal_handler(signum, frame): 107 | raise (TimeoutException, msg) 108 | signal.signal(signal.SIGALRM, signal_handler) 109 | signal.alarm(seconds) 110 | try: 111 | yield 112 | finally: 113 | signal.alarm(0) 114 | 115 | 116 | def proc_wrapper(func,rv,pos_args,keyword_args): 117 | try: 118 | rv['output'] = func(*pos_args,**keyword_args) 119 | except Exception as e: 120 | rv['exception'] = e 121 | rv['traceback'] = traceback.extract_tb(sys.exc_info()[2]) 122 | 123 | def run_with_timeout(func,timeout_seconds,pos_args,keyword_args): 124 | rv_dict = timeout_manager.dict() 125 | p = multiprocessing.Process(target=proc_wrapper,args=(func,rv_dict,pos_args,keyword_args)) 126 | p.start() 127 | p.join(timeout_seconds) 128 | if p.is_alive(): 129 | p.terminate() 130 | raise TimeoutException("Exceeded time limit!") 131 | if not('output' in rv_dict): 132 | if 'exception' in rv_dict: 133 | e = rv_dict['exception'] 134 | e.grading_traceback=None 135 | if 'traceback' in rv_dict: 136 | e.grading_traceback = rv_dict['traceback'] 137 | raise e 138 | raise Exception('Unknown Exception') 139 | return rv_dict['output'] 140 | 141 | # Test fixtures 142 | @pytest.fixture(scope="module") 143 | def grader(request): 144 | """A module-level grading fixture.""" 145 | max_points = getattr(request.module, "max_points", None) # picked up from test module, if defined 146 | html_pre_block = getattr(request.module, "html_pre_block", False) # surround with HTML
 tag?
147 |     #print "[GRADER] max_points: {}".format(max_points)  # [debug]
148 |     _grader = Grader(max_points=max_points, html_pre_block=html_pre_block)  # singleton
149 |     def fin():
150 |         _grader.write_points()
151 |         _grader.write_comments()
152 |         _grader.write_performance()
153 |         print ("[GRADER] Done!")  # [debug]
154 |     request.addfinalizer(fin)
155 |     return _grader
156 | 


--------------------------------------------------------------------------------
/02a_market_sim/grading_util.py:
--------------------------------------------------------------------------------
 1 | """MLT: Utility code.
 2 | 
 3 | Copyright 2017, Georgia Tech Research Corporation
 4 | Atlanta, Georgia 30332-0415
 5 | All Rights Reserved
 6 | """
 7 | 
 8 | import os
 9 | import pandas as pd
10 | 
11 | def symbol_to_path(symbol, base_dir=None):
12 |     """Return CSV file path given ticker symbol."""
13 |     if base_dir is None:
14 |         base_dir = os.environ.get("MARKET_DATA_DIR", '../data/')
15 |     return os.path.join(base_dir, "{}.csv".format(str(symbol)))
16 | 
17 | def get_data(symbols, dates, addSPY=True, colname = 'Adj Close'):
18 |     """Read stock data (adjusted close) for given symbols from CSV files."""
19 |     df = pd.DataFrame(index=dates)
20 |     if addSPY and 'SPY' not in symbols:  # add SPY for reference, if absent
21 |         symbols = ['SPY'] + symbols
22 | 
23 |     for symbol in symbols:
24 |         df_temp = pd.read_csv(symbol_to_path(symbol), index_col='Date',
25 |                 parse_dates=True, usecols=['Date', colname], na_values=['nan'])
26 |         df_temp = df_temp.rename(columns={colname: symbol})
27 |         df = df.join(df_temp)
28 |         if symbol == 'SPY':  # drop dates SPY did not trade
29 |             df = df.dropna(subset=["SPY"])
30 | 
31 |     return df
32 | 
33 | def plot_data(df, title="Stock prices", xlabel="Date", ylabel="Price"):
34 |     import matplotlib.pyplot as plt
35 |     """Plot stock prices with a custom title and meaningful axis labels."""
36 |     ax = df.plot(title=title, fontsize=12)
37 |     ax.set_xlabel(xlabel)
38 |     ax.set_ylabel(ylabel)
39 |     plt.show()
40 | 
41 | def get_orders_data_file(basefilename):
42 |     return open(os.path.join(os.environ.get("ORDERS_DATA_DIR",'orders/'),basefilename))
43 | 
44 | def get_learner_data_file(basefilename):
45 |     return open(os.path.join(os.environ.get("LEARNER_DATA_DIR",'Data/'),basefilename),'r')
46 | 
47 | def get_robot_world_file(basefilename):
48 |     return open(os.path.join(os.environ.get("ROBOT_WORLDS_DIR",'testworlds/'),basefilename))
49 | 


--------------------------------------------------------------------------------
/02a_market_sim/marketsim.py:
--------------------------------------------------------------------------------
  1 | """Market simulator"""
  2 | 
  3 | import pandas as pd
  4 | import numpy as np
  5 | import datetime as dt
  6 | import os
  7 | from analysis import get_portfolio_value, get_portfolio_stats, plot_normalized_data
  8 | import sys
  9 | # Append the path of the directory one level above the current directory to import util
 10 | sys.path.append('../')
 11 | from util import *
 12 | 
 13 | 
 14 | def compute_portvals(orders_file = "./orders/orders.csv", start_val = 1000000, commission=9.95, impact=0.005):
 15 |     """
 16 |     Parameters:
 17 |     orders_file: The name of a file from which to read orders; may be a string, or a file object
 18 |     start_val: The starting value of the portfolio (initial cash available)
 19 |     commission: The fixed amount in dollars charged for each transaction (both entry and exit)
 20 |     impact: The amount the price moves against the trader compared to the historical data at each transaction
 21 |     
 22 |     Returns:
 23 |     portvals: A dataframe with one column containing the value of the portfolio for each trading day
 24 |     """
 25 | 
 26 |     # Read in the orders_file and sort it by date
 27 |     orders_df = pd.read_csv(orders_file, index_col='Date', parse_dates=True, na_values=['nan'])
 28 |     orders_df.sort_index(ascending=True, inplace=True)
 29 |     
 30 |     # Get the start and end dates and symbols
 31 |     start_date = orders_df.index.min()
 32 |     end_date = orders_df.index.max()
 33 |     symbols = orders_df.Symbol.unique().tolist()
 34 | 
 35 |     # Create a dataframe with adjusted close prices for the symbols and for cash
 36 |     df_prices = get_data(symbols, pd.date_range(start_date, end_date), addSPY=True)
 37 |     del df_prices["SPY"]
 38 |     df_prices["cash"] = 1.0
 39 | 
 40 |     # Fill NAN values if any
 41 |     df_prices.fillna(method="ffill", inplace=True)
 42 |     df_prices.fillna(method="bfill", inplace=True)
 43 |     df_prices.fillna(1.0, inplace=True)
 44 | 
 45 |     # Create a dataframe that represents changes in the number of shares by day for each asset. 
 46 |     # It has the same structure as df_prices, and is initially filled with zeros
 47 |     df_trades = pd.DataFrame(np.zeros((df_prices.shape)), df_prices.index, df_prices.columns)
 48 |     for index, row in orders_df.iterrows():
 49 |         # Total value of shares purchased or sold
 50 |         traded_share_value = df_prices.loc[index, row["Symbol"]] * row["Shares"]
 51 |         # Transaction cost 
 52 |         transaction_cost = commission + impact * traded_share_value
 53 | 
 54 |         # Update the number of shares and cash based on the type of transaction done
 55 |         # Note: The same asset may be traded more than once on a particular day
 56 |         if row["Order"] == "BUY":
 57 |             df_trades.loc[index, row["Symbol"]] = df_trades.loc[index, row["Symbol"]] + row["Shares"]
 58 |             df_trades.loc[index, "cash"] = df_trades.loc[index, "cash"] + traded_share_value * (-1.0) - transaction_cost
 59 |         else:
 60 |             df_trades.loc[index, row["Symbol"]] = df_trades.loc[index, row["Symbol"]] -row["Shares"]
 61 |             df_trades.loc[index, "cash"] = df_trades.loc[index, "cash"] + traded_share_value - transaction_cost
 62 | 
 63 |     # Create a dataframe that represents on each particular day how much of each asset in the portfolio
 64 |     # It has the same structure as df_prices, and is initially filled with zeros
 65 |     df_holdings = pd.DataFrame(np.zeros((df_prices.shape)), df_prices.index, df_prices.columns)
 66 |     for row_count in range(len(df_holdings)):
 67 |         # In the first row, the shares are the same as in df_trades, but start_val must be added to cash
 68 |         if row_count == 0:
 69 |             df_holdings.iloc[0, :-1] = df_trades.iloc[0, :-1].copy()
 70 |             df_holdings.iloc[0, -1] = df_trades.iloc[0, -1] + start_val
 71 |         # The rest of the rows show cumulative values
 72 |         else:
 73 |             df_holdings.iloc[row_count] = df_holdings.iloc[row_count-1] + df_trades.iloc[row_count]
 74 |         row_count += 1
 75 | 
 76 |     # Create a dataframe that represents the monetary value of each asset in the portfolio
 77 |     df_value = df_prices * df_holdings
 78 |     
 79 |     # Create portvals dataframe
 80 |     portvals = pd.DataFrame(df_value.sum(axis=1), df_value.index, ["port_val"])
 81 |     return portvals
 82 | 
 83 | 
 84 | def market_simulator(orders_file, start_val=1000000, daily_rf=0.0, samples_per_year=252.0, 
 85 |     save_fig=False, fig_name="plot.png"):
 86 |     """
 87 |     This function takes in an orders file and execute trades based on the file
 88 | 
 89 |     Parameters:
 90 |     orders_file: The file whose orders will be execute
 91 |     start_val: The starting cash in dollars
 92 |     daily_rf: Daily risk-free rate, assuming it does not change
 93 |     samples_per_year: Sampling frequency per year
 94 | 
 95 |     Returns:
 96 |     Print out final portfolio value of the portfolio, as well as Sharpe ratio, 
 97 |     cumulative return, average daily return and standard deviation of the portfolio and $SPX.
 98 |     Plot a chart of the portfolio and $SPX performances
 99 | 
100 |     """
101 |     
102 |     # Process orders
103 |     portvals = compute_portvals(orders_file=orders_file, start_val=start_val)
104 |     if not isinstance(portvals, pd.DataFrame):
105 |         print ("warning, code did not return a DataFrame")
106 |     
107 |     # Get portfolio stats
108 |     cum_ret, avg_daily_ret, std_daily_ret, sharpe_ratio = get_portfolio_stats(portvals,
109 |      daily_rf=daily_rf, samples_per_year=samples_per_year)
110 |     
111 |     # Get the stats for $SPX for the same date range for comparison
112 |     start_date = portvals.index.min()
113 |     end_date = portvals.index.max()
114 |     SPX_prices = get_data(["$SPX"], pd.date_range(start_date, end_date), addSPY=False).dropna()
115 |     cum_ret_SPX, avg_daily_ret_SPX, std_daily_ret_SPX, sharpe_ratio_SPX = \
116 |     get_portfolio_stats(SPX_prices, daily_rf=daily_rf, samples_per_year=samples_per_year)
117 | 
118 |     # Compare portfolio against $SPX
119 |     print ("Date Range: {} to {}".format(start_date, end_date))
120 |     print ()
121 |     print ("Sharpe Ratio of Fund: {}".format(sharpe_ratio))
122 |     print ("Sharpe Ratio of $SPX : {}".format(sharpe_ratio_SPX))
123 |     print ()
124 |     print ("Cumulative Return of Fund: {}".format(cum_ret))
125 |     print ("Cumulative Return of $SPX : {}".format(cum_ret_SPX))
126 |     print ()
127 |     print ("Standard Deviation of Fund: {}".format(std_daily_ret))
128 |     print ("Standard Deviation of $SPX : {}".format(std_daily_ret_SPX))
129 |     print ()
130 |     print ("Average Daily Return of Fund: {}".format(avg_daily_ret))
131 |     print ("Average Daily Return of $SPX : {}".format(avg_daily_ret_SPX))
132 |     print ()
133 |     print ("Final Portfolio Value: {}".format(portvals.iloc[-1, -1]))
134 | 
135 |     # Plot the data
136 |     plot_normalized_data(SPX_prices.join(portvals), "Portfolio vs. SPX", "Date", "Normalized prices",
137 |         save_fig=save_fig, fig_name=fig_name)
138 | 
139 | 
140 | if __name__ == "__main__":
141 |     market_simulator(orders_file="./orders/orders.csv", save_fig=True, fig_name="orders.png")
142 |     market_simulator(orders_file="../02b_event_analyzer/df_trades.csv", 
143 |         save_fig=True, fig_name="df_trades.png")
144 |     market_simulator(orders_file="../02b_event_analyzer/df_trades_bollinger.csv", 
145 |         save_fig=True, fig_name="df_trades_bollinger.png")


--------------------------------------------------------------------------------
/02a_market_sim/orders.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntrang086/computational_investing/ae8f6267b76c02a3fef502ff31ebafc235fc8e2c/02a_market_sim/orders.png


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders-01.csv:
--------------------------------------------------------------------------------
 1 | Date,Symbol,Order,Shares
 2 | 2011-01-10,AAPL,BUY,1500
 3 | 2011-01-13,AAPL,SELL,1500
 4 | 2011-01-13,IBM,BUY,4000
 5 | 2011-01-26,GOOG,BUY,1000
 6 | 2011-02-02,XOM,SELL,4000
 7 | 2011-02-10,XOM,BUY,4000
 8 | 2011-03-03,GOOG,SELL,1000
 9 | 2011-03-03,GOOG,SELL,2200
10 | 2011-06-03,IBM,SELL,3300
11 | 2011-05-03,IBM,BUY,1500
12 | 2011-06-10,AAPL,BUY,1200
13 | 2011-08-01,GOOG,BUY,55
14 | 2011-08-01,GOOG,SELL,55
15 | 2011-12-20,AAPL,SELL,1200
16 | 2011-12-21,AAPL,BUY,20
17 | 2011-12-27,GOOG,BUY,2200
18 | 2011-12-28,IBM,SELL,2200
19 | 


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders-02.csv:
--------------------------------------------------------------------------------
 1 | Date,Symbol,Order,Shares
 2 | 2011-01-10,AAPL,BUY,1500
 3 | 2011-01-10,AAPL,SELL,1500
 4 | 2011-01-13,AAPL,SELL,1500
 5 | 2011-01-13,IBM,BUY,4000
 6 | 2011-01-26,GOOG,BUY,1000
 7 | 2011-02-02,XOM,SELL,4000
 8 | 2011-02-10,XOM,BUY,4000
 9 | 2011-03-03,GOOG,SELL,1000
10 | 2011-06-03,IBM,SELL,3300
11 | 2011-05-03,IBM,BUY,1500
12 | 2011-06-10,AAPL,BUY,1200
13 | 2011-08-01,GOOG,BUY,55
14 | 2011-08-01,GOOG,SELL,55
15 | 2011-08-01,GOOG,BUY,55
16 | 2011-12-20,AAPL,SELL,1200
17 | 2011-12-21,AAPL,BUY,20
18 | 2011-12-28,IBM,SELL,2200
19 | 


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders-03.csv:
--------------------------------------------------------------------------------
 1 | Date,Symbol,Order,Shares
 2 | 2011-01-10,AAPL,BUY,1500
 3 | 2011-01-13,AAPL,BUY,1500
 4 | 2011-01-26,GOOG,BUY,1000
 5 | 2011-02-02,XOM,SELL,4000
 6 | 2011-02-10,XOM,BUY,4000
 7 | 2011-03-03,GOOG,SELL,1000
 8 | 2011-03-03,IBM,SELL,1200
 9 | 2011-06-03,IBM,SELL,3300
10 | 2011-05-03,IBM,BUY,1500
11 | 2011-06-10,AAPL,SELL,3000
12 | 2011-08-01,GOOG,BUY,55
13 | 2011-08-01,GOOG,BUY,55
14 | 2011-12-20,GOOG,SELL,110
15 | 


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders-04.csv:
--------------------------------------------------------------------------------
 1 | Date,Symbol,Order,Shares
 2 | 2008-01-14,GOOG,SELL,1000
 3 | 2008-01-17,GOOG,BUY,1000
 4 | 2008-03-19,AAPL,SELL,3000
 5 | 2008-03-31,IBM,SELL,2000
 6 | 2008-05-02,BAC,BUY,5000
 7 | 2008-05-09,BAC,SELL,5000
 8 | 2008-06-02,IBM,BUY,2000
 9 | 2008-06-02,AAPL,BUY,3100
10 | 2008-06-02,AAPL,BUY,4300
11 | 2008-07-23,AAPL,SELL,3500
12 | 2008-07-10,GOOG,SELL,1000
13 | 2008-08-07,IBM,SELL,55
14 | 2008-08-11,IBM,BUY,55
15 | 2008-12-12,GOOG,BUY,1000
16 | 


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders-05.csv:
--------------------------------------------------------------------------------
 1 | Date,Symbol,Order,Shares
 2 | 2009-05-04,GLD,BUY,3000
 3 | 2009-06-08,AAPL,BUY,1500
 4 | 2009-07-07,IBM,BUY,4000
 5 | 2009-07-07,GOOG,BUY,1000
 6 | 2009-10-19,IBM,SELL,4000
 7 | 2010-01-13,XOM,BUY,3000
 8 | 2010-02-26,GOOG,SELL,1000
 9 | 2010-03-09,XOM,SELL,900
10 | 2010-04-20,GLD,SELL,3000
11 | 2010-05-03,BAC,BUY,1500
12 | 2010-05-27,BAC,SELL,1500
13 | 2010-06-29,XOM,SELL,2100
14 | 2010-07-02,AAPL,SELL,300
15 | 2010-07-06,AAPL,SELL,1200
16 | 


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders-06.csv:
--------------------------------------------------------------------------------
 1 | Date,Symbol,Order,Shares
 2 | 2007-01-10,AAPL,BUY,2500
 3 | 2007-01-17,AAPL,SELL,1500
 4 | 2007-01-19,IBM,BUY,400
 5 | 2007-01-26,GOOG,BUY,1000
 6 | 2007-02-02,XOM,SELL,4000
 7 | 2007-02-16,XOM,BUY,700
 8 | 2007-03-05,GOOG,SELL,550
 9 | 2007-03-05,IBM,SELL,2200
10 | 2007-05-14,XOM,SELL,880
11 | 2007-05-15,XOM,BUY,550
12 | 2007-09-13,GOOG,SELL,1000
13 | 2007-10-10,XOM,BUY,100
14 | 2007-10-12,XOM,SELL,3000
15 | 2007-11-07,XOM,BUY,400
16 | 


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders-07.csv:
--------------------------------------------------------------------------------
 1 | Date,Symbol,Order,Shares
 2 | 2009-01-14,AAPL,BUY,150
 3 | 2009-01-21,AAPL,SELL,150
 4 | 2009-01-21,IBM,BUY,400
 5 | 2009-01-30,GOOG,BUY,100
 6 | 2009-02-02,XOM,SELL,400
 7 | 2009-02-10,XOM,BUY,400
 8 | 2009-03-03,GOOG,SELL,100
 9 | 2009-03-03,IBM,SELL,220
10 | 2009-06-03,IBM,SELL,330
11 | 2009-05-08,IBM,BUY,1500
12 | 2009-06-10,AAPL,BUY,1200
13 | 2009-08-03,GOOG,BUY,550
14 | 2009-08-03,GOOG,SELL,550
15 | 2009-12-21,AAPL,SELL,1200
16 | 


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders-08.csv:
--------------------------------------------------------------------------------
 1 | Date,Symbol,Order,Shares
 2 | 2010-01-12,AAPL,BUY,150
 3 | 2010-01-21,IBM,BUY,400
 4 | 2010-01-21,GOOG,BUY,100
 5 | 2010-01-27,AAPL,SELL,150
 6 | 2010-02-05,XOM,SELL,400
 7 | 2010-02-11,GOOG,SELL,100
 8 | 2010-03-10,IBM,SELL,220
 9 | 2010-04-15,XOM,BUY,400
10 | 2010-05-20,GOOG,BUY,550
11 | 2010-06-03,IBM,SELL,330
12 | 2010-06-24,AAPL,BUY,1200
13 | 2010-08-18,IBM,BUY,1500
14 | 2010-09-15,GOOG,SELL,550
15 | 2010-12-07,AAPL,SELL,1200
16 | 


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders-09.csv:
--------------------------------------------------------------------------------
 1 | Date,Symbol,Order,Shares
 2 | 2011-01-10,AAPL,BUY,2500
 3 | 2011-01-10,AAPL,BUY,2500
 4 | 2011-01-13,AAPL,SELL,5000
 5 | 2011-01-13,IBM,BUY,4000
 6 | 2011-01-26,GOOG,BUY,1000
 7 | 2011-02-02,XOM,SELL,4000
 8 | 2011-02-10,XOM,BUY,4000
 9 | 2011-03-03,GOOG,SELL,1000
10 | 2011-03-03,IBM,SELL,4000
11 | 


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders-10.csv:
--------------------------------------------------------------------------------
 1 | Date,Symbol,Order,Shares
 2 | 2011-01-10,AAPL,BUY,1500
 3 | 2011-01-13,AAPL,SELL,1500
 4 | 2011-01-13,IBM,BUY,4000
 5 | 2011-01-26,GOOG,BUY,1000
 6 | 2011-02-02,XOM,SELL,4000
 7 | 2011-02-10,XOM,BUY,4000
 8 | 2011-03-03,GOOG,SELL,1000
 9 | 2011-03-03,IBM,SELL,2200
10 | 2011-06-03,IBM,SELL,3300
11 | 2011-05-03,IBM,BUY,1500
12 | 2011-08-01,GOOG,BUY,55
13 | 2011-08-01,GOOG,SELL,55
14 | 


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders-11.csv:
--------------------------------------------------------------------------------
 1 | Date,Symbol,Order,Shares
 2 | 2011-01-10,AAPL,BUY,2500
 3 | 2011-01-10,AAPL,BUY,2500
 4 | 2011-01-13,AAPL,SELL,5000
 5 | 2011-01-13,IBM,BUY,4000
 6 | 2011-01-26,GOOG,BUY,1000
 7 | 2011-02-02,XOM,SELL,12000
 8 | 2011-02-10,XOM,BUY,4000
 9 | 2011-03-03,GOOG,SELL,1000
10 | 2011-03-03,IBM,SELL,4000
11 | 


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders-12.csv:
--------------------------------------------------------------------------------
 1 | Date,Symbol,Order,Shares
 2 | 2011-01-10,AAPL,BUY,1500
 3 | 2011-01-13,AAPL,SELL,1500
 4 | 2011-01-13,IBM,BUY,4000
 5 | 2011-01-26,GOOG,BUY,1000
 6 | 2011-02-02,XOM,SELL,4000
 7 | 2011-02-10,XOM,BUY,4000
 8 | 2011-03-03,GOOG,SELL,1000
 9 | 2011-03-03,IBM,SELL,2200
10 | 2011-06-03,IBM,SELL,3300
11 | 2011-05-03,IBM,BUY,1500
12 | 2011-06-10,AAPL,BUY,10000
13 | 2011-08-01,GOOG,BUY,55
14 | 2011-08-01,GOOG,SELL,55
15 | 2011-12-20,AAPL,SELL,1200
16 | 


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders-short.csv:
--------------------------------------------------------------------------------
1 | Date,Symbol,Order,Shares
2 | 2011-01-05,AAPL,BUY,1500
3 | 2011-01-20,AAPL,SELL,1500
4 | 


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders.csv:
--------------------------------------------------------------------------------
 1 | Date,Symbol,Order,Shares
 2 | 2011-01-10,AAPL,BUY,1500
 3 | 2011-01-13,AAPL,SELL,1500
 4 | 2011-01-13,IBM,BUY,4000
 5 | 2011-01-26,GOOG,BUY,1000
 6 | 2011-02-02,XOM,SELL,4000
 7 | 2011-02-10,XOM,BUY,4000
 8 | 2011-03-03,GOOG,SELL,1000
 9 | 2011-03-03,IBM,SELL,2200
10 | 2011-06-03,IBM,SELL,3300
11 | 2011-05-03,IBM,BUY,1500
12 | 2011-06-10,AAPL,BUY,1200
13 | 2011-08-01,GOOG,BUY,55
14 | 2011-08-01,GOOG,SELL,55
15 | 2011-12-20,AAPL,SELL,1200
16 | 


--------------------------------------------------------------------------------
/02a_market_sim/orders/orders2.csv:
--------------------------------------------------------------------------------
 1 | Date,Symbol,Order,Shares
 2 | 2011-01-14,AAPL,BUY,1500
 3 | 2011-01-19,AAPL,SELL,1500
 4 | 2011-01-19,IBM,BUY,4000
 5 | 2011-01-31,GOOG,BUY,1000
 6 | 2011-02-04,XOM,SELL,4000
 7 | 2011-02-11,XOM,BUY,4000
 8 | 2011-03-02,GOOG,SELL,1000
 9 | 2011-03-02,IBM,SELL,2200
10 | 2011-06-02,IBM,SELL,3300
11 | 2011-05-23,IBM,BUY,1500
12 | 2011-06-10,AAPL,BUY,1200
13 | 2011-08-09,GOOG,BUY,55
14 | 2011-08-11,GOOG,SELL,55
15 | 2011-12-14,AAPL,SELL,1200
16 | 


--------------------------------------------------------------------------------
/02a_market_sim/portval_manual.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntrang086/computational_investing/ae8f6267b76c02a3fef502ff31ebafc235fc8e2c/02a_market_sim/portval_manual.ods


--------------------------------------------------------------------------------
/02a_market_sim/test_marketsim.py:
--------------------------------------------------------------------------------
 1 | """Test for optimization.py"""
 2 | 
 3 | 
 4 | from marketsim import compute_portvals
 5 | import unittest
 6 | import math
 7 | import pandas as pd
 8 | from analysis import get_portfolio_stats
 9 | 
10 | 
11 | class TestMarketSimWithOrders(unittest.TestCase):
12 | 
13 |     def test_compute_portvals(self):
14 |         orders_file = "./orders/orders.csv"
15 |         portvals = compute_portvals(orders_file)
16 | 
17 |         # Check if portvals is a dataframe
18 |         self.assertTrue(isinstance(portvals, pd.DataFrame))
19 | 
20 |         # Test portfolio stats
21 |         cum_ret, avg_daily_ret, std_daily_ret, sharpe_ratio = get_portfolio_stats(portvals, daily_rf=0.0, samples_per_year=252.0)
22 |         self.assertTrue(math.isclose(cum_ret, 0.108872698544, rel_tol=0.02), "Cumulative return is incorrect")
23 |         self.assertTrue(math.isclose(avg_daily_ret, 0.000459098655493, rel_tol=0.02), "Average daily return is incorrect")
24 |         self.assertTrue(math.isclose(std_daily_ret, 0.00730509916835, rel_tol=0.02), "Standard deviation is incorrect")
25 |         self.assertTrue(math.isclose(sharpe_ratio, 0.997654521878, rel_tol=0.02), "Sharpe ratio is incorrect")
26 |         self.assertTrue(math.isclose(portvals.iloc[-1, -1], 1106025.8065, rel_tol=0.02), "Portfolio value is incorrect")
27 | 
28 | 
29 | class TestMarketSimWithOrders2(unittest.TestCase):
30 | 
31 |     def test_compute_portvals(self):
32 |         orders_file = "./orders/orders2.csv"
33 |         portvals = compute_portvals(orders_file)
34 | 
35 |         # Check if portvals is a dataframe
36 |         self.assertTrue(isinstance(portvals, pd.DataFrame))
37 | 
38 |         # Test portfolio stats
39 |         cum_ret, avg_daily_ret, std_daily_ret, sharpe_ratio = get_portfolio_stats(portvals, daily_rf=0.0, samples_per_year=252.0)
40 |         self.assertTrue(math.isclose(cum_ret, 0.0538411196951, rel_tol=0.02), "Cumulative return is incorrect")
41 |         self.assertTrue(math.isclose(avg_daily_ret, 0.000253483085898, rel_tol=0.02), "Average daily return is incorrect")
42 |         self.assertTrue(math.isclose(std_daily_ret, 0.00728172910323, rel_tol=0.02), "Standard deviation is incorrect")
43 |         self.assertTrue(math.isclose(sharpe_ratio, 0.552604907987, rel_tol=0.02), "Sharpe ratio is incorrect")
44 |         self.assertTrue(math.isclose(portvals.iloc[-1, -1], 1051088.0915, rel_tol=0.02), "Portfolio value is incorrect")    
45 |     
46 | 
47 | if __name__ == '__main__':
48 |     unittest.main()


--------------------------------------------------------------------------------
/02b_event_analyzer/GOOG_Bollinger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntrang086/computational_investing/ae8f6267b76c02a3fef502ff31ebafc235fc8e2c/02b_event_analyzer/GOOG_Bollinger.png


--------------------------------------------------------------------------------
/02b_event_analyzer/Manual_calculation_for_AES&.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntrang086/computational_investing/ae8f6267b76c02a3fef502ff31ebafc235fc8e2c/02b_event_analyzer/Manual_calculation_for_AES&.ods


--------------------------------------------------------------------------------
/02b_event_analyzer/bollinger_event_chart.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntrang086/computational_investing/ae8f6267b76c02a3fef502ff31ebafc235fc8e2c/02b_event_analyzer/bollinger_event_chart.pdf


--------------------------------------------------------------------------------
/02b_event_analyzer/df_trades.csv:
--------------------------------------------------------------------------------
  1 | Date,Symbol,Order,Shares
  2 | 2008-03-11,CI,BUY,100
  3 | 2008-03-11,CVH,BUY,100
  4 | 2008-03-11,S,BUY,100
  5 | 2008-03-11,UNH,BUY,100
  6 | 2008-03-11,HUM,BUY,100
  7 | 2008-03-11,AET,BUY,100
  8 | 2008-03-11,WLP,BUY,100
  9 | 2008-03-18,HUM,SELL,100
 10 | 2008-03-18,CI,SELL,100
 11 | 2008-03-18,CVH,SELL,100
 12 | 2008-03-18,WLP,SELL,100
 13 | 2008-03-18,UNH,SELL,100
 14 | 2008-03-18,AET,SELL,100
 15 | 2008-03-18,S,SELL,100
 16 | 2008-04-01,TDC,BUY,100
 17 | 2008-04-08,TDC,SELL,100
 18 | 2008-09-19,CTAS,BUY,100
 19 | 2008-09-19,PCL,BUY,100
 20 | 2008-09-19,PLD,BUY,100
 21 | 2008-09-19,HST,BUY,100
 22 | 2008-09-19,MHP,BUY,100
 23 | 2008-09-26,PCL,SELL,100
 24 | 2008-09-26,PLD,SELL,100
 25 | 2008-09-26,CTAS,SELL,100
 26 | 2008-09-26,HST,SELL,100
 27 | 2008-09-26,MHP,SELL,100
 28 | 2008-09-30,FDO,BUY,100
 29 | 2008-09-30,HIG,BUY,100
 30 | 2008-10-07,FDO,SELL,100
 31 | 2008-10-07,HIG,SELL,100
 32 | 2008-10-13,HBAN,BUY,100
 33 | 2008-10-16,SUN,BUY,100
 34 | 2008-10-16,GCI,BUY,100
 35 | 2008-10-16,HIG,BUY,100
 36 | 2008-10-16,PRU,BUY,100
 37 | 2008-10-16,UNM,BUY,100
 38 | 2008-10-16,LNC,BUY,100
 39 | 2008-10-16,MWV,BUY,100
 40 | 2008-10-16,PBI,BUY,100
 41 | 2008-10-16,AMP,BUY,100
 42 | 2008-10-20,HRL,BUY,100
 43 | 2008-10-20,KIM,BUY,100
 44 | 2008-10-20,WYNN,BUY,100
 45 | 2008-10-20,SNDK,BUY,100
 46 | 2008-10-20,HBAN,SELL,100
 47 | 2008-10-20,WYN,BUY,100
 48 | 2008-10-23,LNC,SELL,100
 49 | 2008-10-23,SUN,SELL,100
 50 | 2008-10-23,AMP,SELL,100
 51 | 2008-10-23,GCI,SELL,100
 52 | 2008-10-23,PRU,SELL,100
 53 | 2008-10-23,HIG,SELL,100
 54 | 2008-10-23,UNM,SELL,100
 55 | 2008-10-23,MWV,SELL,100
 56 | 2008-10-23,PBI,SELL,100
 57 | 2008-10-27,WYN,SELL,100
 58 | 2008-10-27,HRL,SELL,100
 59 | 2008-10-27,SNDK,SELL,100
 60 | 2008-10-27,WYNN,SELL,100
 61 | 2008-10-27,KIM,SELL,100
 62 | 2008-10-28,ETFC,BUY,100
 63 | 2008-10-28,LM,BUY,100
 64 | 2008-10-28,WHR,BUY,100
 65 | 2008-10-30,LNC,BUY,100
 66 | 2008-10-30,SHLD,BUY,100
 67 | 2008-10-30,GNW,BUY,100
 68 | 2008-10-30,FITB,BUY,100
 69 | 2008-10-30,GS,BUY,100
 70 | 2008-10-30,EXPE,BUY,100
 71 | 2008-10-30,CMG,BUY,100
 72 | 2008-10-30,IFF,BUY,100
 73 | 2008-10-30,MSI,BUY,100
 74 | 2008-10-30,AMP,BUY,100
 75 | 2008-10-30,CTL,BUY,100
 76 | 2008-10-30,URBN,BUY,100
 77 | 2008-10-30,SYMC,BUY,100
 78 | 2008-10-30,HIG,BUY,100
 79 | 2008-10-30,AVP,BUY,100
 80 | 2008-10-30,TGT,BUY,100
 81 | 2008-10-30,WIN,BUY,100
 82 | 2008-10-30,TE,BUY,100
 83 | 2008-10-30,JDSU,BUY,100
 84 | 2008-10-30,PRU,BUY,100
 85 | 2008-10-30,CI,BUY,100
 86 | 2008-10-30,AIZ,BUY,100
 87 | 2008-11-04,CI,BUY,100
 88 | 2008-11-04,LM,SELL,100
 89 | 2008-11-04,PPL,BUY,100
 90 | 2008-11-04,AET,BUY,100
 91 | 2008-11-04,THC,BUY,100
 92 | 2008-11-04,DF,BUY,100
 93 | 2008-11-04,WHR,SELL,100
 94 | 2008-11-04,ETFC,SELL,100
 95 | 2008-11-06,JDSU,SELL,100
 96 | 2008-11-06,HIG,SELL,100
 97 | 2008-11-06,SYMC,SELL,100
 98 | 2008-11-06,AVP,SELL,100
 99 | 2008-11-06,AMP,SELL,100
100 | 2008-11-06,CI,SELL,100
101 | 2008-11-06,CTL,SELL,100
102 | 2008-11-06,PRU,SELL,100
103 | 2008-11-06,AIZ,SELL,100
104 | 2008-11-06,URBN,SELL,100
105 | 2008-11-06,MSI,SELL,100
106 | 2008-11-06,GS,SELL,100
107 | 2008-11-06,EXPE,SELL,100
108 | 2008-11-06,CMG,SELL,100
109 | 2008-11-06,TGT,SELL,100
110 | 2008-11-06,WIN,SELL,100
111 | 2008-11-06,GNW,SELL,100
112 | 2008-11-06,FITB,SELL,100
113 | 2008-11-06,SHLD,SELL,100
114 | 2008-11-06,LNC,SELL,100
115 | 2008-11-06,IFF,SELL,100
116 | 2008-11-06,TE,SELL,100
117 | 2008-11-07,YHOO,BUY,100
118 | 2008-11-07,LEN,BUY,100
119 | 2008-11-07,PHM,BUY,100
120 | 2008-11-07,ETFC,BUY,100
121 | 2008-11-07,WYN,BUY,100
122 | 2008-11-07,GNW,BUY,100
123 | 2008-11-07,ANF,BUY,100
124 | 2008-11-07,FTI,BUY,100
125 | 2008-11-07,S,BUY,100
126 | 2008-11-07,DHI,BUY,100
127 | 2008-11-11,PPL,SELL,100
128 | 2008-11-11,AET,SELL,100
129 | 2008-11-11,CI,SELL,100
130 | 2008-11-11,THC,SELL,100
131 | 2008-11-11,DF,SELL,100
132 | 2008-11-13,PRU,BUY,100
133 | 2008-11-13,DPS,BUY,100
134 | 2008-11-14,WYN,SELL,100
135 | 2008-11-14,ANF,SELL,100
136 | 2008-11-14,LEN,SELL,100
137 | 2008-11-14,YHOO,SELL,100
138 | 2008-11-14,GNW,SELL,100
139 | 2008-11-14,PHM,SELL,100
140 | 2008-11-14,ETFC,SELL,100
141 | 2008-11-14,FTI,SELL,100
142 | 2008-11-14,DHI,SELL,100
143 | 2008-11-14,S,SELL,100
144 | 2008-11-20,PRU,SELL,100
145 | 2008-11-20,DPS,SELL,100
146 | 2008-11-21,JWN,BUY,100
147 | 2008-11-21,ADSK,BUY,100
148 | 2008-11-21,GNW,BUY,100
149 | 2008-11-21,ALXN,BUY,100
150 | 2008-11-21,DF,BUY,100
151 | 2008-11-21,CELG,BUY,100
152 | 2008-11-21,STI,BUY,100
153 | 2008-11-21,DELL,BUY,100
154 | 2008-11-21,KEY,BUY,100
155 | 2008-11-21,HIG,BUY,100
156 | 2008-11-21,C,BUY,100
157 | 2008-11-24,CPB,BUY,100
158 | 2008-11-24,CBE,BUY,100
159 | 2008-11-26,HRL,BUY,100
160 | 2008-12-01,HIG,SELL,100
161 | 2008-12-01,C,SELL,100
162 | 2008-12-01,CELG,SELL,100
163 | 2008-12-01,DELL,SELL,100
164 | 2008-12-01,DF,SELL,100
165 | 2008-12-01,STI,SELL,100
166 | 2008-12-01,GNW,SELL,100
167 | 2008-12-01,ALXN,SELL,100
168 | 2008-12-01,JWN,SELL,100
169 | 2008-12-01,KEY,SELL,100
170 | 2008-12-01,ADSK,SELL,100
171 | 2008-12-02,CBE,SELL,100
172 | 2008-12-02,CPB,SELL,100
173 | 2008-12-02,DO,BUY,100
174 | 2008-12-02,EMN,BUY,100
175 | 2008-12-02,MET,BUY,100
176 | 2008-12-02,MU,BUY,100
177 | 2008-12-04,HRL,SELL,100
178 | 2008-12-05,AES,BUY,100
179 | 2008-12-08,ACE,BUY,100
180 | 2008-12-08,LNC,BUY,100
181 | 2008-12-08,LSI,BUY,100
182 | 2008-12-08,PFG,BUY,100
183 | 2008-12-08,UNM,BUY,100
184 | 2008-12-08,DV,BUY,100
185 | 2008-12-09,EMN,SELL,100
186 | 2008-12-09,MET,SELL,100
187 | 2008-12-09,MU,SELL,100
188 | 2008-12-09,DO,SELL,100
189 | 2008-12-12,AES,SELL,100
190 | 2008-12-15,UNM,SELL,100
191 | 2008-12-15,DV,SELL,100
192 | 2008-12-15,LSI,SELL,100
193 | 2008-12-15,LNC,SELL,100
194 | 2008-12-15,PFG,SELL,100
195 | 2008-12-15,ACE,SELL,100
196 | 2008-12-16,STX,BUY,100
197 | 2008-12-23,STX,SELL,100
198 | 2009-01-02,HCN,BUY,100
199 | 2009-01-02,HCP,BUY,100
200 | 2009-01-02,EQR,BUY,100
201 | 2009-01-09,HCP,SELL,100
202 | 2009-01-09,EQR,SELL,100
203 | 2009-01-09,HCN,SELL,100
204 | 2009-01-21,JCI,BUY,100
205 | 2009-01-21,FITB,BUY,100
206 | 2009-01-21,COH,BUY,100
207 | 2009-01-21,ATI,BUY,100
208 | 2009-01-28,DV,BUY,100
209 | 2009-01-28,COH,SELL,100
210 | 2009-01-28,FITB,SELL,100
211 | 2009-01-28,BDX,BUY,100
212 | 2009-01-28,ATI,SELL,100
213 | 2009-01-28,JCI,SELL,100
214 | 2009-01-28,LM,BUY,100
215 | 2009-01-28,NEM,BUY,100
216 | 2009-02-04,DV,SELL,100
217 | 2009-02-04,NEM,SELL,100
218 | 2009-02-04,BDX,SELL,100
219 | 2009-02-04,LM,SELL,100
220 | 2009-02-24,AIG,BUY,100
221 | 2009-02-24,NEM,BUY,100
222 | 2009-03-03,AIG,SELL,100
223 | 2009-03-03,NEM,SELL,100
224 | 2009-03-10,NEM,BUY,100
225 | 2009-03-10,JCI,BUY,100
226 | 2009-03-10,BF.B,BUY,100
227 | 2009-03-12,PLL,BUY,100
228 | 2009-03-12,MOS,BUY,100
229 | 2009-03-17,FSLR,BUY,100
230 | 2009-03-17,JCI,SELL,100
231 | 2009-03-17,NUE,BUY,100
232 | 2009-03-17,NEM,SELL,100
233 | 2009-03-17,BF.B,SELL,100
234 | 2009-03-17,AA,BUY,100
235 | 2009-03-19,PLL,SELL,100
236 | 2009-03-19,MOS,SELL,100
237 | 2009-03-23,HRB,BUY,100
238 | 2009-03-23,HRS,BUY,100
239 | 2009-03-24,FSLR,SELL,100
240 | 2009-03-24,NUE,SELL,100
241 | 2009-03-24,AA,SELL,100
242 | 2009-03-30,HRB,SELL,100
243 | 2009-03-30,HRS,SELL,100
244 | 


--------------------------------------------------------------------------------
/02b_event_analyzer/df_trades_bollinger.csv:
--------------------------------------------------------------------------------
  1 | Date,Symbol,Order,Shares
  2 | 2008-02-25,NU,BUY,100
  3 | 2008-02-26,GOOG,BUY,100
  4 | 2008-02-27,CMS,BUY,100
  5 | 2008-02-27,ADSK,BUY,100
  6 | 2008-02-27,HSP,BUY,100
  7 | 2008-03-03,NU,SELL,100
  8 | 2008-03-04,GOOG,SELL,100
  9 | 2008-03-05,HSP,SELL,100
 10 | 2008-03-05,ADSK,SELL,100
 11 | 2008-03-05,CMS,SELL,100
 12 | 2008-04-01,TDC,BUY,100
 13 | 2008-04-07,BMC,BUY,100
 14 | 2008-04-08,HAR,BUY,100
 15 | 2008-04-08,TDC,SELL,100
 16 | 2008-04-08,CTL,BUY,100
 17 | 2008-04-08,FII,BUY,100
 18 | 2008-04-14,BMC,SELL,100
 19 | 2008-04-15,HAR,SELL,100
 20 | 2008-04-15,FII,SELL,100
 21 | 2008-04-15,CTL,SELL,100
 22 | 2008-04-16,MNST,BUY,100
 23 | 2008-04-16,SWY,BUY,100
 24 | 2008-04-16,STX,BUY,100
 25 | 2008-04-17,PG,BUY,100
 26 | 2008-04-17,BDX,BUY,100
 27 | 2008-04-18,ISRG,BUY,100
 28 | 2008-04-21,LLY,BUY,100
 29 | 2008-04-21,PEP,BUY,100
 30 | 2008-04-21,SHLD,BUY,100
 31 | 2008-04-22,PFE,BUY,100
 32 | 2008-04-22,WIN,BUY,100
 33 | 2008-04-22,AGN,BUY,100
 34 | 2008-04-22,XRX,BUY,100
 35 | 2008-04-22,LUV,BUY,100
 36 | 2008-04-22,CL,BUY,100
 37 | 2008-04-22,CI,BUY,100
 38 | 2008-04-22,MAT,BUY,100
 39 | 2008-04-22,NFLX,BUY,100
 40 | 2008-04-23,STX,SELL,100
 41 | 2008-04-23,MNST,SELL,100
 42 | 2008-04-23,SWY,SELL,100
 43 | 2008-04-24,YHOO,BUY,100
 44 | 2008-04-24,CCE,BUY,100
 45 | 2008-04-24,SBUX,BUY,100
 46 | 2008-04-24,CMG,BUY,100
 47 | 2008-04-24,WHR,BUY,100
 48 | 2008-04-24,NWL,BUY,100
 49 | 2008-04-24,PG,SELL,100
 50 | 2008-04-24,BDX,SELL,100
 51 | 2008-04-25,ISRG,SELL,100
 52 | 2008-04-25,KO,BUY,100
 53 | 2008-04-28,KFT,BUY,100
 54 | 2008-04-28,NWL,BUY,100
 55 | 2008-04-28,MMM,BUY,100
 56 | 2008-04-28,PEP,SELL,100
 57 | 2008-04-28,SUN,BUY,100
 58 | 2008-04-28,LLY,SELL,100
 59 | 2008-04-28,SHLD,SELL,100
 60 | 2008-04-29,MRK,BUY,100
 61 | 2008-04-29,MO,BUY,100
 62 | 2008-04-29,MON,BUY,100
 63 | 2008-04-29,PNW,BUY,100
 64 | 2008-04-29,MAT,SELL,100
 65 | 2008-04-29,CI,SELL,100
 66 | 2008-04-29,WIN,SELL,100
 67 | 2008-04-29,CL,SELL,100
 68 | 2008-04-29,BIIB,BUY,100
 69 | 2008-04-29,SIAL,BUY,100
 70 | 2008-04-29,XRX,SELL,100
 71 | 2008-04-29,LUV,SELL,100
 72 | 2008-04-29,NFLX,SELL,100
 73 | 2008-04-29,RDC,BUY,100
 74 | 2008-04-29,AGN,SELL,100
 75 | 2008-04-29,PFE,SELL,100
 76 | 2008-05-01,DNR,BUY,100
 77 | 2008-05-01,SBUX,SELL,100
 78 | 2008-05-01,OI,BUY,100
 79 | 2008-05-01,NWL,SELL,100
 80 | 2008-05-01,CCE,SELL,100
 81 | 2008-05-01,JDSU,BUY,100
 82 | 2008-05-01,CMG,SELL,100
 83 | 2008-05-01,RDC,BUY,100
 84 | 2008-05-01,IFF,BUY,100
 85 | 2008-05-01,YHOO,SELL,100
 86 | 2008-05-01,WHR,SELL,100
 87 | 2008-05-02,SEE,BUY,100
 88 | 2008-05-02,WPO,BUY,100
 89 | 2008-05-02,STJ,BUY,100
 90 | 2008-05-02,SIAL,BUY,100
 91 | 2008-05-02,KO,SELL,100
 92 | 2008-05-02,IP,BUY,100
 93 | 2008-05-05,YHOO,BUY,100
 94 | 2008-05-05,MKC,BUY,100
 95 | 2008-05-05,SYK,BUY,100
 96 | 2008-05-05,SUN,SELL,100
 97 | 2008-05-05,KFT,SELL,100
 98 | 2008-05-05,GME,BUY,100
 99 | 2008-05-05,NWL,SELL,100
100 | 2008-05-05,MMM,SELL,100
101 | 2008-05-06,MRK,SELL,100
102 | 2008-05-06,VMC,BUY,100
103 | 2008-05-06,PRGO,BUY,100
104 | 2008-05-06,PNW,SELL,100
105 | 2008-05-06,RDC,SELL,100
106 | 2008-05-06,SIAL,SELL,100
107 | 2008-05-06,BIIB,SELL,100
108 | 2008-05-06,MO,SELL,100
109 | 2008-05-06,MON,SELL,100
110 | 2008-05-08,IFF,SELL,100
111 | 2008-05-08,OI,SELL,100
112 | 2008-05-08,JDSU,SELL,100
113 | 2008-05-08,RDC,SELL,100
114 | 2008-05-08,DNR,SELL,100
115 | 2008-05-09,SEE,SELL,100
116 | 2008-05-09,WPO,SELL,100
117 | 2008-05-09,STJ,SELL,100
118 | 2008-05-09,SIAL,SELL,100
119 | 2008-05-09,IP,SELL,100
120 | 2008-05-12,GME,SELL,100
121 | 2008-05-12,YHOO,SELL,100
122 | 2008-05-12,SYK,SELL,100
123 | 2008-05-12,MKC,SELL,100
124 | 2008-05-13,PRGO,SELL,100
125 | 2008-05-13,VMC,SELL,100
126 | 2008-05-15,AEP,BUY,100
127 | 2008-05-15,CELG,BUY,100
128 | 2008-05-16,EA,BUY,100
129 | 2008-05-16,URBN,BUY,100
130 | 2008-05-19,EXPE,BUY,100
131 | 2008-05-19,RF,BUY,100
132 | 2008-05-19,BDX,BUY,100
133 | 2008-05-19,CPB,BUY,100
134 | 2008-05-22,CELG,SELL,100
135 | 2008-05-22,AEP,SELL,100
136 | 2008-05-23,EA,SELL,100
137 | 2008-05-23,URBN,SELL,100
138 | 2008-05-27,RF,SELL,100
139 | 2008-05-27,CPB,SELL,100
140 | 2008-05-27,EXPE,SELL,100
141 | 2008-05-27,BDX,SELL,100
142 | 2008-07-30,CBG,BUY,100
143 | 2008-08-05,DNR,BUY,100
144 | 2008-08-05,ADM,BUY,100
145 | 2008-08-05,NEM,BUY,100
146 | 2008-08-05,FLS,BUY,100
147 | 2008-08-05,TAP,BUY,100
148 | 2008-08-06,MOLX,BUY,100
149 | 2008-08-06,WFM,BUY,100
150 | 2008-08-06,CBG,SELL,100
151 | 2008-08-06,S,BUY,100
152 | 2008-08-08,OKE,BUY,100
153 | 2008-08-11,CF,BUY,100
154 | 2008-08-11,DO,BUY,100
155 | 2008-08-11,APD,BUY,100
156 | 2008-08-11,NEM,BUY,100
157 | 2008-08-11,MOS,BUY,100
158 | 2008-08-11,FSLR,BUY,100
159 | 2008-08-12,NEM,SELL,100
160 | 2008-08-12,TAP,SELL,100
161 | 2008-08-12,DNR,SELL,100
162 | 2008-08-12,FLS,SELL,100
163 | 2008-08-12,EIX,BUY,100
164 | 2008-08-12,ADM,SELL,100
165 | 2008-08-12,GS,BUY,100
166 | 2008-08-12,SE,BUY,100
167 | 2008-08-13,S,SELL,100
168 | 2008-08-13,WFM,SELL,100
169 | 2008-08-13,MOLX,SELL,100
170 | 2008-08-14,STJ,BUY,100
171 | 2008-08-14,WM,BUY,100
172 | 2008-08-15,HAR,BUY,100
173 | 2008-08-15,OKE,SELL,100
174 | 2008-08-15,OKE,BUY,100
175 | 2008-08-15,DV,BUY,100
176 | 2008-08-18,DO,SELL,100
177 | 2008-08-18,APD,SELL,100
178 | 2008-08-18,NEM,SELL,100
179 | 2008-08-18,CF,SELL,100
180 | 2008-08-18,MOS,SELL,100
181 | 2008-08-18,FSLR,SELL,100
182 | 2008-08-19,GS,SELL,100
183 | 2008-08-19,EIX,SELL,100
184 | 2008-08-19,SE,SELL,100
185 | 2008-08-21,STJ,SELL,100
186 | 2008-08-21,WM,SELL,100
187 | 2008-08-22,DV,SELL,100
188 | 2008-08-22,HAR,SELL,100
189 | 2008-08-22,OKE,SELL,100
190 | 2008-11-04,THC,BUY,100
191 | 2008-11-11,THC,SELL,100
192 | 2008-12-08,CLX,BUY,100
193 | 2008-12-10,FLIR,BUY,100
194 | 2008-12-15,CLX,SELL,100
195 | 2008-12-16,LO,BUY,100
196 | 2008-12-17,FLIR,SELL,100
197 | 2008-12-23,LO,SELL,100
198 | 2008-12-31,ESRX,BUY,100
199 | 2009-01-06,ICE,BUY,100
200 | 2009-01-06,RAI,BUY,100
201 | 2009-01-06,PRGO,BUY,100
202 | 2009-01-08,ESRX,SELL,100
203 | 2009-01-13,RAI,SELL,100
204 | 2009-01-13,PRGO,SELL,100
205 | 2009-01-13,ICE,SELL,100
206 | 2009-02-06,AIV,BUY,100
207 | 2009-02-13,AIV,SELL,100
208 | 2009-03-18,GIS,BUY,100
209 | 2009-03-24,HRB,BUY,100
210 | 2009-03-25,GIS,SELL,100
211 | 2009-03-27,ESRX,BUY,100
212 | 2009-03-27,STZ,BUY,100
213 | 2009-03-31,HRB,SELL,100
214 | 2009-04-03,STZ,SELL,100
215 | 2009-04-03,ABT,BUY,100
216 | 2009-04-03,ESRX,SELL,100
217 | 2009-04-03,BAX,BUY,100
218 | 2009-04-13,ABT,SELL,100
219 | 2009-04-13,BAX,SELL,100
220 | 2009-04-13,TDC,BUY,100
221 | 2009-04-14,ETR,BUY,100
222 | 2009-04-14,NU,BUY,100
223 | 2009-04-15,ALXN,BUY,100
224 | 2009-04-16,NEM,BUY,100
225 | 2009-04-17,PBCT,BUY,100
226 | 2009-04-20,TDC,SELL,100
227 | 2009-04-21,ETR,SELL,100
228 | 2009-04-21,NU,SELL,100
229 | 2009-04-22,ALXN,SELL,100
230 | 2009-04-23,NEM,SELL,100
231 | 2009-04-24,CPB,BUY,100
232 | 2009-04-24,PBCT,SELL,100
233 | 2009-04-24,HRL,BUY,100
234 | 2009-04-24,GIS,BUY,100
235 | 2009-04-29,AET,BUY,100
236 | 2009-04-29,VFC,BUY,100
237 | 2009-04-29,TSS,BUY,100
238 | 2009-04-29,SRCL,BUY,100
239 | 2009-04-30,STJ,BUY,100
240 | 2009-04-30,MYL,BUY,100
241 | 2009-04-30,MCD,BUY,100
242 | 2009-04-30,JEC,BUY,100
243 | 2009-04-30,BLL,BUY,100
244 | 2009-05-01,AON,BUY,100
245 | 2009-05-01,KSS,BUY,100
246 | 2009-05-01,HRL,SELL,100
247 | 2009-05-01,IFF,BUY,100
248 | 2009-05-01,AGN,BUY,100
249 | 2009-05-01,DLTR,BUY,100
250 | 2009-05-01,GIS,SELL,100
251 | 2009-05-01,CPB,SELL,100
252 | 2009-05-04,WPO,BUY,100
253 | 2009-05-04,AMT,BUY,100
254 | 2009-05-04,DV,BUY,100
255 | 2009-05-05,DF,BUY,100
256 | 2009-05-06,VFC,SELL,100
257 | 2009-05-06,SRCL,SELL,100
258 | 2009-05-06,AET,SELL,100
259 | 2009-05-06,GME,BUY,100
260 | 2009-05-06,TSS,SELL,100
261 | 2009-05-07,XLNX,BUY,100
262 | 2009-05-07,SYMC,BUY,100
263 | 2009-05-07,JEC,SELL,100
264 | 2009-05-07,FDO,BUY,100
265 | 2009-05-07,VZ,BUY,100
266 | 2009-05-07,MCD,SELL,100
267 | 2009-05-07,MYL,SELL,100
268 | 2009-05-07,STJ,SELL,100
269 | 2009-05-07,BBBY,BUY,100
270 | 2009-05-07,BLL,SELL,100
271 | 2009-05-07,NFLX,BUY,100
272 | 2009-05-07,PBI,BUY,100
273 | 2009-05-07,TRV,BUY,100
274 | 2009-05-07,ROST,BUY,100
275 | 2009-05-07,ORCL,BUY,100
276 | 2009-05-08,DLTR,SELL,100
277 | 2009-05-08,AGN,SELL,100
278 | 2009-05-08,AON,SELL,100
279 | 2009-05-08,ORLY,BUY,100
280 | 2009-05-08,WEC,BUY,100
281 | 2009-05-08,AMT,BUY,100
282 | 2009-05-08,KSS,SELL,100
283 | 2009-05-08,IFF,SELL,100
284 | 2009-05-08,AZO,BUY,100
285 | 2009-05-08,NVDA,BUY,100
286 | 2009-05-11,MCHP,BUY,100
287 | 2009-05-11,LOW,BUY,100
288 | 2009-05-11,HD,BUY,100
289 | 2009-05-11,DV,SELL,100
290 | 2009-05-11,WPO,SELL,100
291 | 2009-05-11,AMT,SELL,100
292 | 2009-05-12,SNDK,BUY,100
293 | 2009-05-12,DF,SELL,100
294 | 2009-05-12,NKE,BUY,100
295 | 2009-05-12,HAS,BUY,100
296 | 2009-05-12,AZO,BUY,100
297 | 2009-05-13,GME,SELL,100
298 | 2009-05-14,ORCL,SELL,100
299 | 2009-05-14,VZ,SELL,100
300 | 2009-05-14,PBI,SELL,100
301 | 2009-05-14,FDO,SELL,100
302 | 2009-05-14,BBBY,SELL,100
303 | 2009-05-14,TRV,SELL,100
304 | 2009-05-14,ROST,SELL,100
305 | 2009-05-14,NFLX,SELL,100
306 | 2009-05-14,SYMC,SELL,100
307 | 2009-05-14,XLNX,SELL,100
308 | 2009-05-15,AMT,SELL,100
309 | 2009-05-15,NVDA,SELL,100
310 | 2009-05-15,ORLY,SELL,100
311 | 2009-05-15,AZO,SELL,100
312 | 2009-05-15,WEC,SELL,100
313 | 2009-05-18,HD,SELL,100
314 | 2009-05-18,LOW,SELL,100
315 | 2009-05-18,MCHP,SELL,100
316 | 2009-05-19,NKE,SELL,100
317 | 2009-05-19,SNDK,SELL,100
318 | 2009-05-19,AZO,SELL,100
319 | 2009-05-19,HAS,SELL,100
320 | 2009-05-29,CTAS,BUY,100
321 | 2009-05-29,FLIR,BUY,100
322 | 2009-05-29,MNST,BUY,100
323 | 2009-05-29,BF.B,BUY,100
324 | 2009-06-02,CAH,BUY,100
325 | 2009-06-03,PCS,BUY,100
326 | 2009-06-03,AIG,BUY,100
327 | 2009-06-03,TSO,BUY,100
328 | 2009-06-03,VLO,BUY,100
329 | 2009-06-04,BAX,BUY,100
330 | 2009-06-05,FLIR,SELL,100
331 | 2009-06-05,MNST,SELL,100
332 | 2009-06-05,MNST,BUY,100
333 | 2009-06-05,BF.B,SELL,100
334 | 2009-06-05,DTV,BUY,100
335 | 2009-06-05,CTAS,SELL,100
336 | 2009-06-05,EFX,BUY,100
337 | 2009-06-08,VRSN,BUY,100
338 | 2009-06-08,HUM,BUY,100
339 | 2009-06-08,BAX,BUY,100
340 | 2009-06-08,UNH,BUY,100
341 | 2009-06-08,AET,BUY,100
342 | 2009-06-09,PRGO,BUY,100
343 | 2009-06-09,CAH,SELL,100
344 | 2009-06-09,PFE,BUY,100
345 | 2009-06-10,AIG,SELL,100
346 | 2009-06-10,PCS,SELL,100
347 | 2009-06-10,TSO,SELL,100
348 | 2009-06-10,RAI,BUY,100
349 | 2009-06-10,VLO,SELL,100
350 | 2009-06-11,AET,BUY,100
351 | 2009-06-11,CVH,BUY,100
352 | 2009-06-11,HUM,BUY,100
353 | 2009-06-11,LEN,BUY,100
354 | 2009-06-11,BAX,SELL,100
355 | 2009-06-12,EFX,SELL,100
356 | 2009-06-12,HIG,BUY,100
357 | 2009-06-12,DTV,SELL,100
358 | 2009-06-12,AMP,BUY,100
359 | 2009-06-12,MNST,SELL,100
360 | 2009-06-15,BAX,SELL,100
361 | 2009-06-15,VRSN,SELL,100
362 | 2009-06-15,HUM,SELL,100
363 | 2009-06-15,AET,SELL,100
364 | 2009-06-15,UNH,SELL,100
365 | 2009-06-16,PRGO,SELL,100
366 | 2009-06-16,PFE,SELL,100
367 | 2009-06-17,RAI,SELL,100
368 | 2009-06-18,LEN,SELL,100
369 | 2009-06-18,HUM,SELL,100
370 | 2009-06-18,CVH,SELL,100
371 | 2009-06-18,AET,SELL,100
372 | 2009-06-19,AMP,SELL,100
373 | 2009-06-19,HIG,SELL,100
374 | 2009-07-20,TSN,BUY,100
375 | 2009-07-21,RF,BUY,100
376 | 2009-07-21,KEY,BUY,100
377 | 2009-07-21,LMT,BUY,100
378 | 2009-07-22,STJ,BUY,100
379 | 2009-07-22,ATI,BUY,100
380 | 2009-07-23,BCR,BUY,100
381 | 2009-07-23,MCD,BUY,100
382 | 2009-07-23,SWY,BUY,100
383 | 2009-07-24,LMT,BUY,100
384 | 2009-07-24,PNC,BUY,100
385 | 2009-07-27,TSN,SELL,100
386 | 2009-07-27,BCR,BUY,100
387 | 2009-07-28,LMT,SELL,100
388 | 2009-07-28,KEY,SELL,100
389 | 2009-07-28,DGX,BUY,100
390 | 2009-07-28,RF,SELL,100
391 | 2009-07-29,ATI,SELL,100
392 | 2009-07-29,S,BUY,100
393 | 2009-07-29,STJ,SELL,100
394 | 2009-07-30,BCR,SELL,100
395 | 2009-07-30,AKAM,BUY,100
396 | 2009-07-30,MCO,BUY,100
397 | 2009-07-30,MCD,SELL,100
398 | 2009-07-30,PCS,BUY,100
399 | 2009-07-30,SWY,SELL,100
400 | 2009-07-31,LMT,SELL,100
401 | 2009-07-31,MKC,BUY,100
402 | 2009-07-31,PNC,SELL,100
403 | 2009-07-31,DNB,BUY,100
404 | 2009-07-31,BDX,BUY,100
405 | 2009-08-03,K,BUY,100
406 | 2009-08-03,BCR,SELL,100
407 | 2009-08-03,AZO,BUY,100
408 | 2009-08-04,PPL,BUY,100
409 | 2009-08-04,DGX,SELL,100
410 | 2009-08-05,S,SELL,100
411 | 2009-08-05,DGX,BUY,100
412 | 2009-08-05,EA,BUY,100
413 | 2009-08-05,EW,BUY,100
414 | 2009-08-06,AKAM,SELL,100
415 | 2009-08-06,PG,BUY,100
416 | 2009-08-06,DF,BUY,100
417 | 2009-08-06,PCS,SELL,100
418 | 2009-08-06,PCS,BUY,100
419 | 2009-08-06,ORLY,BUY,100
420 | 2009-08-06,MKC,BUY,100
421 | 2009-08-06,MCO,SELL,100
422 | 2009-08-07,DNB,SELL,100
423 | 2009-08-07,DRI,BUY,100
424 | 2009-08-07,GIS,BUY,100
425 | 2009-08-07,MKC,SELL,100
426 | 2009-08-07,PRGO,BUY,100
427 | 2009-08-07,K,BUY,100
428 | 2009-08-07,BDX,SELL,100
429 | 2009-08-10,AZO,SELL,100
430 | 2009-08-10,K,SELL,100
431 | 2009-08-11,PPL,SELL,100
432 | 2009-08-12,DRI,BUY,100
433 | 2009-08-12,DGX,SELL,100
434 | 2009-08-12,EA,SELL,100
435 | 2009-08-12,EW,SELL,100
436 | 2009-08-13,DF,SELL,100
437 | 2009-08-13,PCS,SELL,100
438 | 2009-08-13,MKC,SELL,100
439 | 2009-08-13,ORLY,SELL,100
440 | 2009-08-13,PG,SELL,100
441 | 2009-08-13,KO,BUY,100
442 | 2009-08-13,FDO,BUY,100
443 | 2009-08-14,DRI,SELL,100
444 | 2009-08-14,PRGO,SELL,100
445 | 2009-08-14,K,SELL,100
446 | 2009-08-14,GIS,SELL,100
447 | 2009-08-19,DRI,SELL,100
448 | 2009-08-20,KO,SELL,100
449 | 2009-08-20,FDO,SELL,100
450 | 2009-08-20,NTAP,BUY,100
451 | 2009-08-21,INTU,BUY,100
452 | 2009-08-25,HCBK,BUY,100
453 | 2009-08-27,NTAP,SELL,100
454 | 2009-08-28,EW,BUY,100
455 | 2009-08-28,ACN,BUY,100
456 | 2009-08-28,INTU,SELL,100
457 | 2009-08-28,NTRS,BUY,100
458 | 2009-08-28,PM,BUY,100
459 | 2009-08-28,GT,BUY,100
460 | 2009-08-28,MOS,BUY,100
461 | 2009-09-01,HCBK,SELL,100
462 | 2009-09-04,EW,SELL,100
463 | 2009-09-04,MOS,SELL,100
464 | 2009-09-04,NTRS,SELL,100
465 | 2009-09-04,PM,SELL,100
466 | 2009-09-04,GT,SELL,100
467 | 2009-09-04,ACN,SELL,100
468 | 2009-09-10,SHW,BUY,100
469 | 2009-09-10,AEE,BUY,100
470 | 2009-09-10,MON,BUY,100
471 | 2009-09-11,ORLY,BUY,100
472 | 2009-09-11,NEE,BUY,100
473 | 2009-09-14,WMT,BUY,100
474 | 2009-09-15,KR,BUY,100
475 | 2009-09-16,DGX,BUY,100
476 | 2009-09-16,LH,BUY,100
477 | 2009-09-17,AEE,SELL,100
478 | 2009-09-17,SHW,SELL,100
479 | 2009-09-17,MON,SELL,100
480 | 2009-09-17,VZ,BUY,100
481 | 2009-09-17,F,BUY,100
482 | 2009-09-17,TXN,BUY,100
483 | 2009-09-17,CLX,BUY,100
484 | 2009-09-18,ORLY,SELL,100
485 | 2009-09-18,CVH,BUY,100
486 | 2009-09-18,NEE,SELL,100
487 | 2009-09-21,WMT,SELL,100
488 | 2009-09-21,KR,BUY,100
489 | 2009-09-21,MCO,BUY,100
490 | 2009-09-21,AMAT,BUY,100
491 | 2009-09-22,KR,SELL,100
492 | 2009-09-22,ACE,BUY,100
493 | 2009-09-23,LH,SELL,100
494 | 2009-09-23,DGX,SELL,100
495 | 2009-09-24,VZ,SELL,100
496 | 2009-09-24,F,SELL,100
497 | 2009-09-24,CLX,SELL,100
498 | 2009-09-24,TXN,SELL,100
499 | 2009-09-25,CVH,SELL,100
500 | 2009-09-28,KR,SELL,100
501 | 2009-09-28,MCO,SELL,100
502 | 2009-09-28,AMAT,SELL,100
503 | 2009-09-29,ACE,SELL,100
504 | 2009-10-09,ROST,BUY,100
505 | 2009-10-09,T,BUY,100
506 | 2009-10-13,S,BUY,100
507 | 2009-10-15,BAX,BUY,100
508 | 2009-10-16,DFS,BUY,100
509 | 2009-10-16,T,SELL,100
510 | 2009-10-16,ROST,SELL,100
511 | 2009-10-16,MSI,BUY,100
512 | 2009-10-19,AIG,BUY,100
513 | 2009-10-20,STT,BUY,100
514 | 2009-10-20,LMT,BUY,100
515 | 2009-10-20,BSX,BUY,100
516 | 2009-10-20,CCE,BUY,100
517 | 2009-10-20,S,SELL,100
518 | 2009-10-20,STI,BUY,100
519 | 2009-10-20,AZO,BUY,100
520 | 2009-10-20,DVA,BUY,100
521 | 2009-10-20,BCR,BUY,100
522 | 2009-10-20,IPG,BUY,100
523 | 2009-10-22,AMGN,BUY,100
524 | 2009-10-22,BAX,SELL,100
525 | 2009-10-23,DFS,SELL,100
526 | 2009-10-23,MSI,SELL,100
527 | 2009-10-26,AIG,SELL,100
528 | 2009-10-27,STI,SELL,100
529 | 2009-10-27,BSX,SELL,100
530 | 2009-10-27,BCR,SELL,100
531 | 2009-10-27,DVA,SELL,100
532 | 2009-10-27,AZO,SELL,100
533 | 2009-10-27,LMT,SELL,100
534 | 2009-10-27,CCE,SELL,100
535 | 2009-10-27,IPG,SELL,100
536 | 2009-10-27,STT,SELL,100
537 | 2009-10-29,AMGN,SELL,100
538 | 2009-11-09,DV,BUY,100
539 | 2009-11-11,KSS,BUY,100
540 | 2009-11-11,DV,BUY,100
541 | 2009-11-16,DV,SELL,100
542 | 2009-11-17,JEC,BUY,100
543 | 2009-11-18,ADSK,BUY,100
544 | 2009-11-18,KR,BUY,100
545 | 2009-11-18,KSS,SELL,100
546 | 2009-11-18,DV,SELL,100
547 | 2009-11-23,HSY,BUY,100
548 | 2009-11-24,LTD,BUY,100
549 | 2009-11-24,JEC,SELL,100
550 | 2009-11-24,YHOO,BUY,100
551 | 2009-11-25,HSY,BUY,100
552 | 2009-11-25,KR,SELL,100
553 | 2009-11-25,ADSK,SELL,100
554 | 2009-12-01,HSY,SELL,100
555 | 2009-12-02,YHOO,SELL,100
556 | 2009-12-02,LTD,SELL,100
557 | 2009-12-03,HSY,SELL,100
558 | 2009-12-14,XOM,BUY,100
559 | 2009-12-14,C,BUY,100
560 | 2009-12-14,TAP,BUY,100
561 | 2009-12-16,CELG,BUY,100
562 | 2009-12-16,WMT,BUY,100
563 | 2009-12-16,AMGN,BUY,100
564 | 2009-12-16,USB,BUY,100
565 | 2009-12-16,ESRX,BUY,100
566 | 2009-12-16,STZ,BUY,100
567 | 2009-12-21,XOM,SELL,100
568 | 2009-12-21,APD,BUY,100
569 | 2009-12-21,SIAL,BUY,100
570 | 2009-12-21,CAG,BUY,100
571 | 2009-12-21,PG,BUY,100
572 | 2009-12-21,NEM,BUY,100
573 | 2009-12-21,TAP,SELL,100
574 | 2009-12-21,C,SELL,100
575 | 2009-12-23,STZ,SELL,100
576 | 2009-12-23,ESRX,SELL,100
577 | 2009-12-23,USB,SELL,100
578 | 2009-12-23,TWC,BUY,100
579 | 2009-12-23,AMGN,SELL,100
580 | 2009-12-23,WMT,SELL,100
581 | 2009-12-23,CTAS,BUY,100
582 | 2009-12-23,CELG,SELL,100
583 | 2009-12-28,HAR,BUY,100
584 | 2009-12-29,APD,SELL,100
585 | 2009-12-29,NEM,SELL,100
586 | 2009-12-29,CAG,SELL,100
587 | 2009-12-29,PG,SELL,100
588 | 2009-12-29,SIAL,SELL,100
589 | 2009-12-31,TWC,SELL,100
590 | 2009-12-31,HAR,SELL,100
591 | 2009-12-31,CTAS,SELL,100
592 | 


--------------------------------------------------------------------------------
/02b_event_analyzer/event_analyzer.py:
--------------------------------------------------------------------------------
  1 | import os
  2 | import pandas as pd
  3 | import numpy as np
  4 | import math
  5 | import copy
  6 | import pickle
  7 | import datetime as dt
  8 | import matplotlib.pyplot as plt
  9 | import sys
 10 | # Append the path of the directory one level above the current directory to import util
 11 | sys.path.append('../')
 12 | from util import *
 13 | 
 14 | 
 15 | def detect_return_diff(symbols, data_dict, symbol_change=-0.05, market_change=0.03):
 16 |     """ 
 17 |     Create the event dataframe. Here we are only interested in the opposite movements of 
 18 |     symbol and market, i.e. symbol_change and market_change are of opposite signs
 19 | 
 20 |     Parameters:
 21 |     symbols: A list of symbols of interest
 22 |     data_dict: A dictionary whose keys are types of data, e.g. Adj Close, Volume, etc. and 
 23 |     values are dataframes with dates as indices and symbols as columns
 24 |     symbol_change: Min.(if positive) or max. (if negative) change in the return of symbol
 25 |     market_change: Max.(if negative) or min. (if positive) change in the return of market
 26 |     
 27 |     Returns:
 28 |     df_events: A dataframe filled with either 1's for detected events or NAN's for no events
 29 |     """
 30 | 
 31 |     df_close = data_dict["Adj Close"]
 32 |     market_close = df_close["SPY"]
 33 | 
 34 |     # Create a dataframe filled with NAN's
 35 |     df_events = copy.deepcopy(df_close)
 36 |     df_events = df_events * np.NAN
 37 | 
 38 |     # Dates for the event range
 39 |     dates = df_close.index
 40 | 
 41 |     for symbol in symbols:
 42 |         for i in range(1, len(dates)):
 43 |             # Calculate the returns for this date
 44 |             symbol_return = (df_close[symbol].loc[dates[i]] / df_close[symbol].loc[dates[i - 1]]) - 1
 45 |             market_return = (market_close.loc[dates[i]] / market_close.loc[dates[i - 1]]) - 1
 46 | 
 47 |             # Event is found if both the symbol and market cross their respective thresholds
 48 |             if symbol_change < 0 and market_change > 0:
 49 |                 if symbol_return <= symbol_change and market_return >= market_change:
 50 |                     df_events[symbol].loc[dates[i]] = 1
 51 |             elif symbol_change > 0 and market_change < 0:
 52 |                 if symbol_return >= symbol_change and market_return <= market_change:
 53 |                     df_events[symbol].loc[dates[i]] = 1
 54 |             else:
 55 |                 print ("Here we are only interested in the opposite movements of symbol and market, \
 56 |                     i.e. you should ensure symbol_change and market_change are of opposite signs")
 57 |     return df_events
 58 | 
 59 | 
 60 | def output_events_as_trades(df_events_input, output_filename="df_trades.csv"):
 61 |     """
 62 |     Create df_trades based on df_events_input. When an event occurs, buy 100 shares of 
 63 |     the equity on that day; sell automatically 5 trading days later. The df_trades will be 
 64 |     fed into a market simulator to execute trades and measure performance. For the final 
 65 |     few events assume that we exit on the last day, so hold it less than 5 days
 66 | 
 67 |     Parameters:
 68 |     df_events_input: A dataframe filled with either 1's for detected events or NAN's for no events
 69 |     output_filename: Name of output file
 70 | 
 71 |     Returns:
 72 |     df_trades: A dataframe and a csv file to be used as input to market simulator
 73 |     """
 74 | 
 75 |     # Get the date range
 76 |     date_range = get_exchange_days(start_date=df_events_input.index.min(), 
 77 |         end_date=df_events_input.index.max(), dirpath="../../data/dates_lists")
 78 | 
 79 |     # Make a copy of df_events_input and drop all-NAN rows and columns
 80 |     # to save time iterating over df_events later
 81 |     df_events = df_events_input.dropna(axis=0, how="all")
 82 |     df_events = df_events.dropna(axis=1, how="all")
 83 |     df_trades = pd.DataFrame(columns=["Date", "Symbol", "Order", "Shares"])
 84 |         
 85 |     for symbol in df_events.columns:
 86 |         for date in df_events.index:
 87 |             if df_events[symbol][date] == 1:
 88 |                 df_buy = pd.DataFrame([[date, symbol, "BUY", 100]], columns=df_trades.columns)
 89 |                 # If the 5-day hold period is after the last date of the date_range, 
 90 |                 # we sell the asset on that last date
 91 |                 if date_range.index(date) + 5 >= len(date_range):
 92 |                     sell_date_index = len(date_range) - 1
 93 |                 else:
 94 |                     sell_date_index = date_range.index(date) + 5
 95 |                 df_sell = pd.DataFrame([[date_range[sell_date_index], symbol, "SELL", 100]], 
 96 |                     columns=df_trades.columns)
 97 |                 df_trades = df_trades.append(df_buy)
 98 |                 df_trades = df_trades.append(df_sell)
 99 | 
100 |     df_trades.set_index("Date", inplace=True)
101 |     df_trades.sort_index(inplace=True)
102 |     df_trades.to_csv(output_filename)
103 | 
104 |     return df_trades
105 | 
106 | 
107 | def plot_events(df_events_input, data_dict, num_backward=20, num_forward=20,
108 |                 output_filename="event_chart", market_neutral=True, error_bars=True,
109 |                 market_sym="SPY"):
110 |     """ 
111 |     Plot a chart for the events found by an event detector
112 | 
113 |     Parameters:
114 |     df_events_input: A dataframe filled with 1's for detected events or NAN's for no events
115 |     data_dict: A dictionary whose keys are types of data, e.g. Adj Close, Volume, etc. and 
116 |     values are dataframes with dates as indices and symbols as columns
117 |     num_backward: Number of periods to look back
118 |     num_forward: Number of periods to look ahead
119 |     output_filename: Name of output file
120 |     market_neutral: True/False - whether to exclude market return from stock return
121 |     error_bars: True/False - whether to show error bars, i.e. standard deviations
122 |     market_sym: Symbol of the market index
123 |     
124 |     Returns:
125 |     A pdf file plotting the means and standard deviations of the returns within the date 
126 |     range of [num_backward, num_forward], including the event day
127 |     """
128 | 
129 |     df_events = df_events_input.copy()
130 |     
131 |     # Compute daily return
132 |     df_returns = compute_daily_returns(data_dict["Adj Close"])
133 | 
134 |     if market_neutral == True:
135 |         # Substract market returns from all returns of all symbols
136 |         df_returns = df_returns.sub(df_returns[market_sym].values, axis=0)
137 |         del df_returns[market_sym]
138 |         del df_events[market_sym]
139 | 
140 |     # Since we want to look back num_backward rows and ahead num_forward dates of the event,
141 |     # we ignore the first num_backward and last num_forward rows, and replace them with NAN's
142 |     df_events.values[0:num_backward, :] = np.NaN
143 |     if num_forward > 0:
144 |         df_events.values[-num_forward:, :] = np.NaN
145 | 
146 |     # Number of events
147 |     num_events = int(df_events.count().sum())
148 |     assert num_events > 0, "Zero events in the event matrix"
149 |     all_events_returns = []
150 | 
151 |     # Look for the events and add them to a numpy ndarray which has shape of
152 |     # (num_events, num_backward days before event + 1 event day + num_forward days after event)
153 |     for symbol in df_events.columns:
154 |         for j, event_date in enumerate(df_events.index):
155 |             if df_events[symbol][event_date] == 1:
156 |                 single_event_returns = df_returns[symbol][j - num_backward:j + 1 + num_forward]
157 |                 if type(all_events_returns) == type([]):
158 |                     all_events_returns = single_event_returns
159 |                 else:
160 |                     all_events_returns = np.vstack((all_events_returns, single_event_returns))
161 | 
162 |     # If there is only 1 event, ensure that the ndarray has the above mentioned shape
163 |     if len(all_events_returns.shape) == 1:
164 |         all_events_returns = np.expand_dims(all_events_returns, axis=0)
165 | 
166 |     # Compute cumulative product returns
167 |     all_events_returns = np.cumprod(all_events_returns + 1, axis=1)
168 | 
169 |     # Normalize cumulative returns by event day
170 |     all_events_returns = (all_events_returns.T / all_events_returns[:, num_backward]).T
171 | 
172 |     # Compute the means and standard deviations
173 |     mean_returns = np.mean(all_events_returns, axis=0)
174 |     std_returns = np.std(all_events_returns, axis=0)
175 | 
176 |     # The date range to be used as the x-axis
177 |     x_axis_range = range(-num_backward, num_forward + 1)
178 | 
179 |     # Plot the chart
180 |     plt.clf()
181 |     plt.axhline(y=1.0, xmin=-num_backward, xmax=num_forward, color="k")
182 |     if error_bars == True:
183 |         plt.errorbar(x_axis_range[num_backward:], mean_returns[num_backward:],
184 |                     yerr=std_returns[num_backward:], ecolor="r")
185 |     plt.plot(x_axis_range, mean_returns, linewidth=3, label="mean", color="b")
186 |     plt.xlim(-num_backward - 1, num_forward + 1)
187 |     if market_neutral == True:
188 |         plt.title("Market-relative mean return of {} events".format(num_events))
189 |     else:
190 |         plt.title("Mean return of {} events".format(num_events))
191 |     plt.xlabel("Days")
192 |     plt.ylabel("Cumulative Returns")
193 |     plt.savefig(output_filename, format="pdf")
194 | 
195 | 
196 | if __name__ == "__main__":
197 |     start_date = dt.datetime(2008, 1, 1)
198 |     end_date = dt.datetime(2009, 12, 31)
199 |     dates = get_exchange_days(start_date, end_date, dirpath="../../data/dates_lists", 
200 |         filename="NYSE_dates.txt")
201 | 
202 |     symbols = load_txt_data(dirpath="../../data/symbols_lists", filename="sp5002012.txt").tolist()
203 |     symbols.append("SPY")
204 | 
205 |     keys = ["Open", "High", "Low", "Adj Close", "Volume", "Close"]
206 |     data_dict = get_data_as_dict(dates, symbols, keys)
207 | 
208 |     # Fill NAN values if any
209 |     for key in keys:
210 |         data_dict[key] = data_dict[key].fillna(method="ffill")
211 |         data_dict[key] = data_dict[key].fillna(method="bfill")
212 |         data_dict[key] = data_dict[key].fillna(1.0)
213 | 
214 |     # Detect events
215 |     df_events = detect_return_diff(symbols, data_dict)
216 | 
217 |     # Plot means and standard deviations of events
218 |     plot_events(df_events, data_dict, num_backward=20, num_forward=20,
219 |                 output_filename="event_chart.pdf", market_neutral=True, error_bars=True,
220 |                 market_sym="SPY")
221 |     
222 |     # Output the event as trades to be fed into marketsim
223 |     df_trades = output_events_as_trades(df_events, "df_trades.csv")
224 | 


--------------------------------------------------------------------------------
/02b_event_analyzer/event_analyzer_bollinger.py:
--------------------------------------------------------------------------------
  1 | """Bollinger value as a technical indicator"""
  2 | 
  3 | import os
  4 | import pandas as pd
  5 | import matplotlib.pyplot as plt
  6 | import copy
  7 | import sys
  8 | # Append the path of the directory one level above the current directory to import util
  9 | sys.path.append("../")
 10 | from util import *
 11 | from event_analyzer import output_events_as_trades, plot_events
 12 | 
 13 | 
 14 | def get_bollinger_bands(rolling_mean, rolling_std, num_std=2):
 15 |     """
 16 |     Calculate upper and lower Bollinger Bands
 17 | 
 18 |     Parameters:
 19 |     rolling_mean: Rolling mean of a series
 20 |     rolling_meanstd: Rolling std of a series
 21 |     num_std: Number of standard deviations for the bands
 22 | 
 23 |     Returns: Bollinger upper band and lower band
 24 |     """
 25 |     upper_band = rolling_mean + rolling_std * num_std
 26 |     lower_band = rolling_mean - rolling_std * num_std
 27 |     return upper_band, lower_band
 28 | 
 29 | 
 30 | def compute_bollinger_value(price, rolling_mean, rolling_std):
 31 |     """
 32 |     Output a value indicating how many standard deviations a price is from the mean
 33 | 
 34 |     Parameters:
 35 |     price: Price, typically adjusted close price, series of a symbol
 36 |     rolling_mean: Rolling mean of a series
 37 |     rolling_std: Rolling std of a series
 38 | 
 39 |     Returns:
 40 |     bollinger_val: the number of standard deviations a price is from the mean
 41 |     """
 42 | 
 43 |     bollinger_val = (price - rolling_mean) / rolling_std
 44 |     return bollinger_val
 45 | 
 46 | 
 47 | def plot_bollinger(symbol, start_date, end_date, window=20, num_std=1):
 48 |     """
 49 |     Plot Bollinger bands and value for a symbol
 50 | 
 51 |     Parameters:
 52 |     symbol: A symbol of interest
 53 |     start_date: First timestamp to consider (inclusive)
 54 |     end_date: Last day to consider (inclusive)rolling_mean: Rolling mean of a series
 55 |     window: Number of days to look back for rolling_mean and rolling_std
 56 |     num_std: Number of standard deviations for the bands
 57 | 
 58 |     Returns:
 59 |     Plot two subplots, one for the Adjusted Close Price and Bollinger bands, the other 
 60 |     for the Bollinger value
 61 |     """
 62 | 
 63 |     # Get NYSE trading dates
 64 |     dates = get_exchange_days(start_date, end_date, dirpath="../../data/dates_lists", 
 65 |         filename="NYSE_dates.txt")
 66 | 
 67 |     # Get stock data
 68 |     df_price = get_data([symbol], dates)
 69 | 
 70 |     # Compute rolling mean
 71 |     rolling_mean = df_price[symbol].rolling(window=window).mean()
 72 | 
 73 |     # Compute rolling standard deviation
 74 |     rolling_std = df_price[symbol].rolling(window=window).std()
 75 | 
 76 |     # Compute Bollinger bands and value
 77 |     upper_band, lower_band = get_bollinger_bands(rolling_mean, rolling_std, num_std)
 78 |     bollinger_val = compute_bollinger_value(df_price[symbol], rolling_mean, rolling_std)
 79 |     
 80 |     # Create 2 subplots
 81 |     # First subplot: symbol's adjusted close price, rolling mean and Bollinger Bands
 82 |     f, ax = plt.subplots(2, sharex=True)
 83 |     ax[0].fill_between(upper_band.index, upper_band, lower_band, color="gray", alpha=0.4, 
 84 |         linewidth=2.0, label="Region btwn Bollinger Bands")
 85 |     ax[0].plot(df_price[symbol], label=symbol + " Adjusted Close", color="b")
 86 |     ax[0].set_title("{} Adjusted Close with Bollinger Bands (num. of std = {})".format(
 87 |         symbol, num_std))
 88 |     ax[0].set_ylabel("Adjusted Close Price")
 89 |     ax[0].legend(loc="upper center")
 90 | 
 91 |     # Second subplot: the bollinger value
 92 |     ax[1].axhspan(-num_std, num_std, color="gray", alpha=0.4, linewidth=2.0,
 93 |         label="Region btwn {} & {} std".format(-num_std, num_std))
 94 |     ax[1].plot(bollinger_val, label=symbol + " Bollinger Value", color="b")
 95 |     ax[1].set_title("{} Bollinger Value)".format(symbol))
 96 |     ax[1].set_xlabel("Date")
 97 |     ax[1].set_ylabel("Bollinger Value")
 98 |     ax[1].set_xlim(bollinger_val.index.min(), bollinger_val.index.max())
 99 |     ax[1].legend(loc="upper center")
100 |     plt.show()
101 | 
102 | 
103 | def detect_bollinger(symbols, data_dict, window=20, 
104 |     symbol_bv_change=-2.0, market_bv_change=1.0):
105 |     """ 
106 |     Create the event dataframe based on changes in Bollinger values. Here we are only 
107 |     interested in the opposite movements of symbol and market, i.e. symbol_bv_change 
108 |     and market_bv_change are of opposite signs
109 | 
110 |     Parameters:
111 |     symbols: A list of symbols of interest
112 |     data_dict: A dictionary whose keys are types of data, e.g. Adj Close, Volume, etc. and 
113 |     values are dataframes with dates as indices and symbols as columns
114 |     window: Number of days to look back for rolling_mean and rolling_std
115 |     symbol_bv_change: Change in the Bollinger value of symbol
116 |     market_bv_change: Change in the Bollinger value of market
117 |     
118 |     Returns:
119 |     df_events: A dataframe filled with either 1's for detected events or NAN's for no events
120 |     """
121 | 
122 |     df_close = data_dict["Adj Close"]
123 |     market_close = df_close["SPY"]
124 | 
125 |     # Create a dataframe filled with NAN's
126 |     df_events = copy.deepcopy(df_close)
127 |     df_events = df_events * np.NAN
128 | 
129 |     # Dates for the event range
130 |     dates = df_close.index
131 | 
132 |     # Compute rolling mean for market
133 |     market_rm = market_close.rolling(window=window).mean()
134 | 
135 |     # Compute rolling standard deviation for market
136 |     market_rstd = market_close.rolling(window=window).std()
137 | 
138 |     # Compute Bollinger value for market
139 |     market_bv = compute_bollinger_value(market_close, market_rm, market_rstd)
140 | 
141 |     for symbol in symbols:
142 |         # Compute rolling mean for symbol
143 |         symbol_rm = df_close[symbol].rolling(window=window).mean()
144 | 
145 |         # Compute rolling standard deviation for symbol
146 |         symbol_rstd = df_close[symbol].rolling(window=window).std()
147 | 
148 |         # Compute Bollinger value for symbol
149 |         symbol_bv = compute_bollinger_value(df_close[symbol], symbol_rm, symbol_rstd)
150 | 
151 |         for i in range(window, len(dates)):
152 |             # Get the Bollinger values today and yesterday
153 |             symbol_bv_yesterday = symbol_bv.loc[dates[i - 1]]
154 |             symbol_bv_today = symbol_bv.loc[dates[i]]
155 |             market_bv_today = market_bv.loc[dates[i]]
156 | 
157 |             # Event is found if both the symbol and market cross their respective thresholds
158 |             if symbol_bv_change < 0 and market_bv_change > 0:
159 |                 if symbol_bv_yesterday >= symbol_bv_change and \
160 |                 symbol_bv_today <= symbol_bv_change and market_bv_today >= market_bv_change:
161 |                     df_events[symbol].loc[dates[i]] = 1
162 |             elif symbol_bv_change > 0 and market_bv_change < 0:
163 |                 if symbol_bv_yesterday <= symbol_bv_change and \
164 |                 symbol_bv_today >= symbol_bv_change and market_bv_today <= market_bv_change:
165 |                     df_events[symbol].loc[dates[i]] = 1
166 |             else:
167 |                 print ("Here we are only interested in the opposite movements of symbol and market, \
168 |                     i.e. you should ensure symbol_bv_change and market_bv_change are of opposite signs")
169 |     return df_events
170 | 
171 | 
172 | if __name__ == "__main__":
173 |     # Plot Bollinger bands and values for Google
174 |     plot_bollinger("GOOG", dt.datetime(2010, 1, 1), dt.datetime(2010, 12, 31))
175 | 
176 |     start_date = dt.datetime(2008, 1, 1)
177 |     end_date = dt.datetime(2009, 12, 31)
178 |     dates = get_exchange_days(start_date, end_date, dirpath="../../data/dates_lists", 
179 |         filename="NYSE_dates.txt")
180 | 
181 |     symbols = load_txt_data(dirpath="../../data/symbols_lists", filename="sp5002012.txt").tolist()
182 |     symbols.append("SPY")
183 | 
184 |     keys = ["Open", "High", "Low", "Adj Close", "Volume", "Close"]
185 |     data_dict = get_data_as_dict(dates, symbols, keys)
186 | 
187 |     # Fill NAN values if any
188 |     for key in keys:
189 |         data_dict[key] = data_dict[key].fillna(method="ffill")
190 |         data_dict[key] = data_dict[key].fillna(method="bfill")
191 |         data_dict[key] = data_dict[key].fillna(1.0)
192 | 
193 |     # Detect events
194 |     df_events = detect_bollinger(symbols, data_dict)
195 | 
196 |     # Plot means and standard deviations of events
197 |     plot_events(df_events, data_dict, num_backward=20, num_forward=20,
198 |                 output_filename="bollinger_event_chart.pdf", market_neutral=True, error_bars=True,
199 |                 market_sym="SPY")
200 |     
201 |     # Output the event as trades to be fed into marketsim
202 |     df_trades = output_events_as_trades(df_events, "df_trades_bollinger.csv")


--------------------------------------------------------------------------------
/02b_event_analyzer/event_chart.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ntrang086/computational_investing/ae8f6267b76c02a3fef502ff31ebafc235fc8e2c/02b_event_analyzer/event_chart.pdf


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | # Computational investing
 2 | 
 3 | There are two parts:
 4 | 
 5 | 1 Compute stock return using Capital Asset Pricing Model (CAPM)
 6 | 
 7 | 2 Projects:
 8 | 
 9 | a) Market simulator: Read in an orders file that contains trade orders (buy and sell), compute a portfolio value for all the trades and other statistics and compare the portfolio's performance with that of $SPX
10 | 
11 | b) Technical analysis: Implement a basic technical indicator and an advanced one (Bollinger value) to detect events of interest, plot them and output events as trades to be fed into a market simulator
12 | 
13 | ## Setup
14 | 
15 | You need Python 2.7+, and the following packages: pandas, numpy, scipy and matplotlib.
16 | 
17 | 
18 | ## Data
19 | 
20 | Data files can be downloaded from [this link](http://quantsoftware.gatech.edu/images/a/af/ML4T_2017Fall.zip) or from [Yahoo Finance](https://finance.yahoo.com/)
21 | 
22 | Place the data into a directory named 'data' and it should be one level above this repository.
23 | 
24 | ## Run
25 | 
26 | To run any script file, use:
27 | 
28 | ```bash
29 | python 
30 | ```
31 | 
32 | Source: [Part 2](http://quantsoftware.gatech.edu/Computational_Investing) of [Machine Learning for Trading](http://quantsoftware.gatech.edu/Machine_Learning_for_Trading_Course) by Georgia Tech
33 | 


--------------------------------------------------------------------------------
/util.py:
--------------------------------------------------------------------------------
  1 | """Utility code."""
  2 | 
  3 | import os
  4 | import pandas as pd
  5 | import numpy as np
  6 | import datetime as dt
  7 | import matplotlib.pyplot as plt
  8 | 
  9 | 
 10 | def symbol_to_path(symbol, base_dir=os.path.join("../..", "data")):
 11 |     """Return CSV file path given ticker symbol."""
 12 |     return os.path.join(base_dir, "{}.csv".format(str(symbol)))
 13 | 
 14 | 
 15 | def get_data(symbols, dates, addSPY=True):
 16 |     """Read stock data (adjusted close) for given symbols from CSV files."""
 17 |     df = pd.DataFrame(index=dates)
 18 |     if addSPY and 'SPY' not in symbols:  # add SPY for reference, if absent
 19 |         symbols = ['SPY'] + symbols
 20 | 
 21 |     for symbol in symbols:
 22 |         df_temp = pd.read_csv(symbol_to_path(symbol), index_col='Date',
 23 |                 parse_dates=True, usecols=['Date', 'Adj Close'], na_values=['nan'])
 24 |         df_temp = df_temp.rename(columns={'Adj Close': symbol})
 25 |         df = df.join(df_temp)
 26 |         if symbol == 'SPY':  # drop dates SPY did not trade
 27 |             df = df.dropna(subset=["SPY"])
 28 | 
 29 |     return df
 30 | 
 31 | 
 32 | def normalize_data(df):
 33 |     """Normalize stock prices using the first row of the dataframe"""
 34 |     return df/df.iloc[0,:]
 35 | 
 36 | 
 37 | def compute_daily_returns(df):
 38 |     """Compute and return the daily return values"""
 39 |     daily_returns = df.pct_change()
 40 |     daily_returns.iloc[0,:] = 0
 41 |     return daily_returns
 42 | 
 43 | 
 44 | def compute_sharpe_ratio(k, avg_return, risk_free_rate, std_return):
 45 |     """
 46 |     Compute and return the Sharpe ratio
 47 |     Parameters:
 48 |     k: adjustment factor, sqrt(252) for daily data, sqrt(52) for weekly data, sqrt(12) for monthly data
 49 |     avg_return: daily, weekly or monthly return
 50 |     risk_free_rate: daily, weekly or monthly risk free rate
 51 |     std_return: daily, weekly or monthly standard deviation
 52 |     Returns: 
 53 |     sharpe_ratio: k * (avg_return - risk_free_rate) / std_return
 54 |     """
 55 |     return k * (avg_return - risk_free_rate) / std_return
 56 |     
 57 | 
 58 | def plot_data(df, title="Stock prices", xlabel="Date", ylabel="Price", save_fig=False, fig_name="plot.png"):
 59 |     """Plot stock prices with a custom title and meaningful axis labels."""
 60 |     ax = df.plot(title=title, fontsize=12)
 61 |     ax.set_xlabel(xlabel)
 62 |     ax.set_ylabel(ylabel)
 63 |     if save_fig == True:
 64 |         plt.savefig(fig_name)
 65 |     else:
 66 |         plt.show()
 67 | 
 68 | 
 69 | def load_txt_data(dirpath, filename):
 70 |     """ Load the data from a txt file and store them as a numpy array
 71 | 
 72 |     Parameters:
 73 |     dirpath: The path to the directory where the file is stored
 74 |     filename: The name of the file in the dirpath
 75 |     
 76 |     Returns:
 77 |     np_data: A numpy array of the data
 78 |     """
 79 | 
 80 |     try:
 81 |         filepath= os.path.join(dirpath, filename)
 82 |     except KeyError:
 83 |         print ("The file is missing")
 84 | 
 85 |     np_data = np.loadtxt(filepath, dtype=str)
 86 | 
 87 |     return np_data
 88 | 
 89 | 
 90 | def get_exchange_days(start_date = dt.datetime(1964,7,5), end_date = dt.datetime(2020,12,31),
 91 |     dirpath = "../data/dates_lists", filename="NYSE_dates.txt"):
 92 |     """ Create a list of dates between start_date and end_date (inclusive) that correspond 
 93 |     to the dates there was trading at an exchange. Default values are given based on NYSE.
 94 | 
 95 |     Parameters:
 96 |     start_date: First timestamp to consider (inclusive)
 97 |     end_date: Last day to consider (inclusive)
 98 |     dirpath: The path to the directory where the file is stored
 99 |     filename: The name of the file in the dirpath
100 |     
101 |     Returns:
102 |     dates: A list of dates between start_date and end_date on which an exchange traded
103 |     """
104 | 
105 |     # Load a text file located in dirpath
106 |     dates_str = load_txt_data(dirpath, filename)
107 |     all_dates_frome_file = [dt.datetime.strptime(date, "%m/%d/%Y") for date in dates_str]
108 |     df_all_dates = pd.Series(index=all_dates_frome_file, data=all_dates_frome_file)
109 | 
110 |     selected_dates = [date for date in df_all_dates[start_date:end_date]]
111 | 
112 |     return selected_dates
113 | 
114 | 
115 | def get_data_as_dict(dates, symbols, keys):
116 |     """ Create a dictionary with types of data (Adj Close, Volume, etc.) as keys. Each value is 
117 |     a dataframe with symbols as columns and dates as rows
118 | 
119 |     Parameters:
120 |     dates: A list of dates of interest
121 |     symbols: A list of symbols of interest
122 |     keys: A list of types of data of interest, e.g. Adj Close, Volume, etc.
123 |     
124 |     Returns:
125 |     data_dict: A dictionary whose keys are types of data, e.g. Adj Close, Volume, etc. and 
126 |     values are dataframes with dates as indices and symbols as columns
127 |     """
128 | 
129 |     data_dict = {}
130 |     for key in keys:
131 |         df = pd.DataFrame(index=dates)
132 |         for symbol in symbols:
133 |             df_temp = pd.read_csv(symbol_to_path(symbol), index_col="Date",
134 |                     parse_dates=True, usecols=["Date", key], na_values=["nan"])
135 |             df_temp = df_temp.rename(columns={key: symbol})
136 |             df = df.join(df_temp) 
137 |         data_dict[key] = df
138 |     return data_dict
139 | 


--------------------------------------------------------------------------------