├── .github └── workflows │ ├── main.yml │ └── pythontesting.py ├── README.md ├── StockAnalysis.ipynb ├── cipython.py └── stock.py /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the master branch 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v2 27 | 28 | # Runs a single command using the runners shell 29 | - name: Run a one-line script 30 | run: pip install pandas 31 | - name : run code 32 | run: python cipython.py 33 | - name : run more code 34 | run : python .github/workflows/pythontesting.py 35 | 36 | # Runs a set of commands using the runners shell 37 | - name: Run a multi-line script 38 | run: | 39 | echo Add other actions to build, 40 | echo test, and deploy your project. 41 | -------------------------------------------------------------------------------- /.github/workflows/pythontesting.py: -------------------------------------------------------------------------------- 1 | print('HELLLOOOO WORLD THIS MEANS THE CI ACCESSED THE PYTHON FILE') 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StockForecast 2 | Using machine learning to predict/forecast the future trend of stock prices. 3 | 4 | 5 | Check out the Medium article that goes into detail about this repo! 6 | https://medium.com/@lucas.rea1998/predicting-future-stock-market-trends-with-python-machine-learning-2bf3f1633b3c 7 | -------------------------------------------------------------------------------- /StockAnalysis.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import yfinance as yf\n", 10 | "import datetime\n", 11 | "import pandas as pd\n", 12 | "import numpy as np\n", 13 | "from finta import TA\n", 14 | "import matplotlib.pyplot as plt\n", 15 | "\n", 16 | "from sklearn import svm\n", 17 | "from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier, VotingClassifier \n", 18 | "from sklearn.neighbors import KNeighborsClassifier\n", 19 | "from sklearn.model_selection import train_test_split, GridSearchCV\n", 20 | "from sklearn.metrics import confusion_matrix, classification_report, mean_squared_error, accuracy_score" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 2, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "\"\"\"\n", 30 | "Defining some constants for data mining\n", 31 | "\"\"\"\n", 32 | "\n", 33 | "NUM_DAYS = 10000 # The number of days of historical data to retrieve\n", 34 | "INTERVAL = '1d' # Sample rate of historical data\n", 35 | "symbol = 'SPY' # Symbol of the desired stock\n", 36 | "\n", 37 | "# List of symbols for technical indicators\n", 38 | "INDICATORS = ['RSI', 'MACD', 'STOCH','ADL', 'ATR', 'MOM', 'MFI', 'ROC', 'OBV', 'CCI', 'EMV', 'VORTEX']" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 3, 44 | "metadata": {}, 45 | "outputs": [ 46 | { 47 | "name": "stdout", 48 | "output_type": "stream", 49 | "text": [ 50 | "[*********************100%***********************] 1 of 1 completed\n", 51 | "6895\n" 52 | ] 53 | }, 54 | { 55 | "data": { 56 | "text/plain": [ 57 | "" 58 | ] 59 | }, 60 | "execution_count": 3, 61 | "metadata": {}, 62 | "output_type": "execute_result" 63 | }, 64 | { 65 | "data": { 66 | "image/png": "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\n", 67 | "text/plain": [ 68 | "
" 69 | ] 70 | }, 71 | "metadata": { 72 | "needs_background": "light" 73 | }, 74 | "output_type": "display_data" 75 | } 76 | ], 77 | "source": [ 78 | "\"\"\"\n", 79 | "Next we pull the historical data using yfinance\n", 80 | "Rename the column names because finta uses the lowercase names\n", 81 | "\"\"\"\n", 82 | "\n", 83 | "start = (datetime.date.today() - datetime.timedelta( NUM_DAYS ) )\n", 84 | "end = datetime.datetime.today()\n", 85 | "\n", 86 | "data = yf.download(symbol, start=start, end=end, interval=INTERVAL)\n", 87 | "data.rename(columns={\"Close\": 'close', \"High\": 'high', \"Low\": 'low', 'Volume': 'volume', 'Open': 'open'}, inplace=True)\n", 88 | "print(len(data))\n", 89 | "\n", 90 | "tmp = data.iloc[-60:]\n", 91 | "tmp['close'].plot()" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 4, 97 | "metadata": {}, 98 | "outputs": [ 99 | { 100 | "data": { 101 | "text/plain": [ 102 | "" 103 | ] 104 | }, 105 | "execution_count": 4, 106 | "metadata": {}, 107 | "output_type": "execute_result" 108 | }, 109 | { 110 | "data": { 111 | "image/png": "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\n", 112 | "text/plain": [ 113 | "
" 114 | ] 115 | }, 116 | "metadata": { 117 | "needs_background": "light" 118 | }, 119 | "output_type": "display_data" 120 | } 121 | ], 122 | "source": [ 123 | "\"\"\"\n", 124 | "Next we clean our data and perform feature engineering to create new technical indicator features that our\n", 125 | "model can learn from\n", 126 | "\"\"\"\n", 127 | "\n", 128 | "def _exponential_smooth(data, alpha):\n", 129 | " \"\"\"\n", 130 | " Function that exponentially smooths dataset so values are less 'rigid'\n", 131 | " :param alpha: weight factor to weight recent values more\n", 132 | " \"\"\"\n", 133 | " \n", 134 | " return data.ewm(alpha=alpha).mean()\n", 135 | "\n", 136 | "data = _exponential_smooth(data, 0.65)\n", 137 | "\n", 138 | "tmp1 = data.iloc[-60:]\n", 139 | "tmp1['close'].plot()" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": 5, 145 | "metadata": {}, 146 | "outputs": [ 147 | { 148 | "name": "stdout", 149 | "output_type": "stream", 150 | "text": [ 151 | "Index(['close', '14 period RSI', 'MACD', 'SIGNAL', '14 period STOCH %K', 'MFV',\n", 152 | " '14 period ATR', 'MOM', '14 period MFI', 'ROC', 'OBV', '20 period CCI',\n", 153 | " '14 period EMV', 'VIm', 'VIp', 'ema50', 'ema21', 'ema15', 'ema5',\n", 154 | " 'normVol'],\n", 155 | " dtype='object')\n" 156 | ] 157 | } 158 | ], 159 | "source": [ 160 | "def _get_indicator_data(data):\n", 161 | " \"\"\"\n", 162 | " Function that uses the finta API to calculate technical indicators used as the features\n", 163 | " :return:\n", 164 | " \"\"\"\n", 165 | "\n", 166 | " for indicator in INDICATORS:\n", 167 | " ind_data = eval('TA.' + indicator + '(data)')\n", 168 | " if not isinstance(ind_data, pd.DataFrame):\n", 169 | " ind_data = ind_data.to_frame()\n", 170 | " data = data.merge(ind_data, left_index=True, right_index=True)\n", 171 | " data.rename(columns={\"14 period EMV.\": '14 period EMV'}, inplace=True)\n", 172 | "\n", 173 | " # Also calculate moving averages for features\n", 174 | " data['ema50'] = data['close'] / data['close'].ewm(50).mean()\n", 175 | " data['ema21'] = data['close'] / data['close'].ewm(21).mean()\n", 176 | " data['ema15'] = data['close'] / data['close'].ewm(14).mean()\n", 177 | " data['ema5'] = data['close'] / data['close'].ewm(5).mean()\n", 178 | "\n", 179 | " # Instead of using the actual volume value (which changes over time), we normalize it with a moving volume average\n", 180 | " data['normVol'] = data['volume'] / data['volume'].ewm(5).mean()\n", 181 | "\n", 182 | " # Remove columns that won't be used as features\n", 183 | " del (data['open'])\n", 184 | " del (data['high'])\n", 185 | " del (data['low'])\n", 186 | " del (data['volume'])\n", 187 | " del (data['Adj Close'])\n", 188 | " \n", 189 | " return data\n", 190 | "\n", 191 | "data = _get_indicator_data(data)\n", 192 | "print(data.columns)" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": 6, 198 | "metadata": {}, 199 | "outputs": [], 200 | "source": [ 201 | "live_pred_data = data.iloc[-16:-11]" 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "execution_count": 7, 207 | "metadata": {}, 208 | "outputs": [ 209 | { 210 | "name": "stdout", 211 | "output_type": "stream", 212 | "text": [ 213 | "6866\n" 214 | ] 215 | } 216 | ], 217 | "source": [ 218 | "def _produce_prediction(data, window):\n", 219 | " \"\"\"\n", 220 | " Function that produces the 'truth' values\n", 221 | " At a given row, it looks 'window' rows ahead to see if the price increased (1) or decreased (0)\n", 222 | " :param window: number of days, or rows to look ahead to see what the price did\n", 223 | " \"\"\"\n", 224 | " \n", 225 | " prediction = (data.shift(-window)['close'] >= data['close'])\n", 226 | " prediction = prediction.iloc[:-window]\n", 227 | " data['pred'] = prediction.astype(int)\n", 228 | " \n", 229 | " return data\n", 230 | "\n", 231 | "data = _produce_prediction(data, window=15)\n", 232 | "del (data['close'])\n", 233 | "data = data.dropna() # Some indicators produce NaN values for the first few rows, we just remove them here\n", 234 | "data.tail()\n", 235 | "print(len(data))" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": 16, 241 | "metadata": {}, 242 | "outputs": [ 243 | { 244 | "name": "stderr", 245 | "output_type": "stream", 246 | "text": [ 247 | "/Users/lucasrea/.pyenv/versions/3.7.3/lib/python3.7/site-packages/sklearn/ensemble/forest.py:245: FutureWarning: The default value of n_estimators will change from 10 in version 0.20 to 100 in 0.22.\n", 248 | " \"10 in version 0.20 to 100 in 0.22.\", FutureWarning)\n" 249 | ] 250 | }, 251 | { 252 | "name": "stdout", 253 | "output_type": "stream", 254 | "text": [ 255 | "RF Accuracy = 0.6554416788677402\n", 256 | "KNN Accuracy = 0.6809419228892142\n", 257 | "ENSEMBLE Accuracy = 0.6817959980478276\n" 258 | ] 259 | } 260 | ], 261 | "source": [ 262 | "def cross_Validation(data):\n", 263 | "\n", 264 | " # Split data into equal partitions of size len_train\n", 265 | " \n", 266 | " num_train = 10 # Increment of how many starting points (len(data) / num_train = number of train-test sets)\n", 267 | " len_train = 40 # Length of each train-test set\n", 268 | " \n", 269 | " # Lists to store the results from each model\n", 270 | " rf_RESULTS = []\n", 271 | " knn_RESULTS = []\n", 272 | " gbt_RESULTS = []\n", 273 | " ensemble_RESULTS = []\n", 274 | " \n", 275 | " i = 0\n", 276 | " \n", 277 | " # Models which will be used\n", 278 | " rf = RandomForestClassifier()\n", 279 | " knn = KNeighborsClassifier()\n", 280 | " \n", 281 | " # Create a tuple list of our models\n", 282 | " estimators=[('knn', knn), ('rf', rf)]\n", 283 | " ensemble = VotingClassifier(estimators, voting='soft')\n", 284 | " \n", 285 | " while True:\n", 286 | " \n", 287 | " # Partition the data into chunks of size len_train every num_train days\n", 288 | " df = data.iloc[i * num_train : (i * num_train) + len_train]\n", 289 | " i += 1\n", 290 | " #print(i * num_train, (i * num_train) + len_train)\n", 291 | " \n", 292 | " if len(df) < 40:\n", 293 | " break\n", 294 | " \n", 295 | " y = df['pred']\n", 296 | " features = [x for x in df.columns if x not in ['pred']]\n", 297 | " X = df[features]\n", 298 | "\n", 299 | " X_train, X_test, y_train, y_test = train_test_split(X, y, train_size= 7 * len(X) // 10,shuffle=False)\n", 300 | " \n", 301 | " # fit models\n", 302 | " rf.fit(X_train, y_train)\n", 303 | " knn.fit(X_train, y_train)\n", 304 | " ensemble.fit(X_train, y_train)\n", 305 | " \n", 306 | " # get predictions\n", 307 | " rf_prediction = rf.predict(X_test)\n", 308 | " knn_prediction = knn.predict(X_test)\n", 309 | " ensemble_prediction = ensemble.predict(X_test)\n", 310 | " \n", 311 | "# print('rf prediction is ', rf_prediction)\n", 312 | "# print('knn prediction is ', knn_prediction)\n", 313 | "# print('ensemble prediction is ', ensemble_prediction)\n", 314 | "# print('truth values are ', y_test.values)\n", 315 | " \n", 316 | " # determine accuracy and append to results\n", 317 | " rf_accuracy = accuracy_score(y_test.values, rf_prediction)\n", 318 | " knn_accuracy = accuracy_score(y_test.values, knn_prediction)\n", 319 | " ensemble_accuracy = accuracy_score(y_test.values, ensemble_prediction)\n", 320 | " \n", 321 | "# print(rf_accuracy)\n", 322 | "# print(knn_accuracy)\n", 323 | "# print(ensemble_accuracy)\n", 324 | " rf_RESULTS.append(rf_accuracy)\n", 325 | " knn_RESULTS.append(knn_accuracy)\n", 326 | " ensemble_RESULTS.append(ensemble_accuracy)\n", 327 | " \n", 328 | " print('RF Accuracy = ' + str( sum(rf_RESULTS) / len(rf_RESULTS)))\n", 329 | " print('KNN Accuracy = ' + str( sum(knn_RESULTS) / len(knn_RESULTS)))\n", 330 | " print('ENSEMBLE Accuracy = ' + str( sum(ensemble_RESULTS) / len(ensemble_RESULTS)))\n", 331 | " \n", 332 | "cross_Validation(data)" 333 | ] 334 | }, 335 | { 336 | "cell_type": "code", 337 | "execution_count": null, 338 | "metadata": {}, 339 | "outputs": [], 340 | "source": [] 341 | } 342 | ], 343 | "metadata": { 344 | "kernelspec": { 345 | "display_name": "Python 3", 346 | "language": "python", 347 | "name": "python3" 348 | }, 349 | "language_info": { 350 | "codemirror_mode": { 351 | "name": "ipython", 352 | "version": 3 353 | }, 354 | "file_extension": ".py", 355 | "mimetype": "text/x-python", 356 | "name": "python", 357 | "nbconvert_exporter": "python", 358 | "pygments_lexer": "ipython3", 359 | "version": "3.7.3" 360 | } 361 | }, 362 | "nbformat": 4, 363 | "nbformat_minor": 4 364 | } 365 | -------------------------------------------------------------------------------- /cipython.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | print('this code ran') 4 | 5 | -------------------------------------------------------------------------------- /stock.py: -------------------------------------------------------------------------------- 1 | import yfinance as yf 2 | import datetime 3 | import os 4 | import pandas as pd 5 | import numpy as np 6 | from finta import TA 7 | import time 8 | 9 | from sklearn.ensemble import RandomForestClassifier 10 | from sklearn.model_selection import train_test_split 11 | from sklearn.metrics import confusion_matrix, classification_report 12 | 13 | from pyspark.sql import SparkSession 14 | from pyspark.ml.feature import VectorAssembler 15 | from pyspark.ml.classification import RandomForestClassifier 16 | from pyspark.ml.classification import GBTClassifier 17 | from pyspark.ml import Pipeline 18 | from pyspark.ml.evaluation import MulticlassClassificationEvaluator 19 | 20 | 21 | 22 | spark = SparkSession.builder.appName('stockanalysis').getOrCreate() 23 | 24 | 25 | class Ticker(): 26 | 27 | NUM_DAYS = 1000 # The number of days of historical data to retrieve 28 | INTERVAL = '1d' # Sample rate of historical data 29 | 30 | # List of symbols for technical indicators 31 | INDICATORS = ['RSI', 'MACD', 'STOCH', 'ADL', 'ATR', 'MOM', 'MFI', 'ROC', 'OBV', 'CCI', 'EMV', 'VORTEX'] 32 | 33 | 34 | def __init__(self, symbol): 35 | 36 | """ 37 | Constructor for class 38 | Will obtain historical data for NUM_DAYS number of days 39 | :param symbol: ticker of stock 40 | """ 41 | 42 | self.symbol = symbol 43 | self._get_historical_data() 44 | 45 | def _get_historical_data(self): 46 | 47 | """ 48 | Function that uses the yfinance API to get stock data 49 | :return: 50 | """ 51 | 52 | start = (datetime.date.today() - datetime.timedelta( self.NUM_DAYS) ) 53 | end = datetime.datetime.today() 54 | 55 | self.data = yf.download(self.symbol, start=start, end=end, interval=self.INTERVAL) 56 | self.data.rename(columns={"Close": 'close', "High": 'high', "Low": 'low', 'Volume': 'volume', 'Open': 'open'}, inplace=True) 57 | 58 | def _exponential_smooth(self, alpha): 59 | 60 | """ 61 | Function that exponentially smooths dataset so values are less 'rigid' 62 | :param alpha: weight factor to weight recent values more 63 | """ 64 | 65 | self.data = self.data.ewm(alpha=alpha).mean() 66 | 67 | def _get_indicator_data(self): 68 | 69 | """ 70 | Function that uses the finta API to calculate technical indicators used as the features 71 | :return: 72 | """ 73 | 74 | for indicator in self.INDICATORS: 75 | ind_data = eval('TA.' + indicator + '(self.data)') 76 | if not isinstance(ind_data, pd.DataFrame): 77 | ind_data = ind_data.to_frame() 78 | self.data = self.data.merge(ind_data, left_index=True, right_index=True) 79 | self.data.rename(columns={"14 period EMV.": '14 period EMV'}, inplace=True) 80 | 81 | # Also calculate moving averages for features 82 | self.data['ema50'] = self.data['close'] / self.data['close'].ewm(50).mean() 83 | self.data['ema21'] = self.data['close'] / self.data['close'].ewm(21).mean() 84 | self.data['ema14'] = self.data['close'] / self.data['close'].ewm(14).mean() 85 | self.data['ema5'] = self.data['close'] / self.data['close'].ewm(5).mean() 86 | 87 | # Remove columns that won't be used as features 88 | del (self.data['open']) 89 | del (self.data['high']) 90 | del (self.data['low']) 91 | del (self.data['volume']) 92 | del (self.data['Adj Close']) 93 | 94 | def _produce_prediction(self, window=10): 95 | 96 | """ 97 | Function that produces the 'truth' values 98 | At a given row, it looks 'window' rows ahead to see if the price increased (1) or decreased (0) 99 | :param window: number of days, or rows to look ahead to see what the price did 100 | """ 101 | 102 | prediction = (self.data.shift(-window)['close'] >= self.data['close']) 103 | prediction = prediction.iloc[:-window] 104 | self.data['pred'] = prediction.astype(int) 105 | 106 | def _produce_data(self, window): 107 | 108 | """ 109 | Main data function that calls the others to smooth, get features, and create the predictions 110 | :param window: value used to determine the prediction 111 | :return: 112 | """ 113 | 114 | self._exponential_smooth(0.9) 115 | self._get_indicator_data() 116 | self._produce_prediction(window=window) 117 | 118 | del (self.data['close']) 119 | self.data = self.data.dropna() 120 | 121 | def _split_data(self): 122 | 123 | """ 124 | Function to partition the data into the train and test set 125 | :return: 126 | """ 127 | 128 | self.y = self.data['pred'] 129 | features = [x for x in self.data.columns if x not in ['pred']] 130 | self.X = self.data[features] 131 | 132 | self.X_train, self.X_test, self.y_train, self.y_test = train_test_split(self.X, self.y, train_size= 2 * len(self.X) // 3) 133 | 134 | def _train_random_forest(self): 135 | 136 | """ 137 | Function that uses random forest classifier to train the model 138 | :return: 139 | """ 140 | 141 | rf = RandomForestClassifier(n_jobs=-1, n_estimators=85, random_state=65) 142 | rf.fit(self.X_train, self.y_train.values.ravel()) 143 | prediction = rf.predict(self.X_test) 144 | 145 | print(classification_report(self.y_test, prediction)) 146 | print(confusion_matrix(self.y_test, prediction)) 147 | print(rf.feature_importances_) 148 | 149 | 150 | def _data_clean(self, x=15): 151 | 152 | t1 = time.time() 153 | self._produce_data(window=x) 154 | self._split_data() 155 | print(str(time.time() - t1) + ' seconds to clean data') 156 | 157 | 158 | def _model(self): 159 | 160 | t1 = time.time() 161 | self._train_random_forest() 162 | print(time.time() - t1) 163 | 164 | def _spark_rf(self): 165 | self.df = spark.createDataFrame(self.data) 166 | 167 | features = [] 168 | for col in self.df.columns: 169 | if col == 'pred': 170 | continue 171 | else: 172 | features.append(col) 173 | 174 | (trainingData, testData) = self.df.randomSplit([0.7, 0.3], seed=24234232) 175 | 176 | assembler = VectorAssembler(inputCols=features, outputCol="features") 177 | #rf = RandomForestClassifier(labelCol="pred", featuresCol="features", numTrees=500) 178 | gbt = gbt = GBTClassifier(labelCol="pred", featuresCol="features", maxIter=200) 179 | pipeline = Pipeline(stages=[assembler, gbt]) 180 | 181 | model = pipeline.fit(trainingData) 182 | predictions = model.transform(testData) 183 | 184 | # Select (prediction, true label) and compute test error 185 | evaluator = MulticlassClassificationEvaluator( 186 | labelCol="pred", predictionCol="prediction", metricName="accuracy") 187 | accuracy = evaluator.evaluate(predictions) 188 | print("Test Error = %g" % (1.0 - accuracy)) 189 | 190 | 191 | t = Ticker('SPY') 192 | t._data_clean() 193 | t._spark_rf() 194 | 195 | 196 | #t._model() 197 | 198 | 199 | 200 | 201 | --------------------------------------------------------------------------------