├── .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 HTMLtag? 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 | python30 | ``` 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 | --------------------------------------------------------------------------------