Skip to content

Commit 5ecbecb

Browse files
committed
added
1 parent 685ddae commit 5ecbecb

File tree

2 files changed

+313
-1
lines changed

2 files changed

+313
-1
lines changed
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
---
2+
title: "MSFT Stock Prediction using LSTM or GRU"
3+
date: 2024-06-16T00:00:00+01:00
4+
description: "Short Stock price analysis on MSFT, then a prediction is tested using GRU"
5+
menu:
6+
sidebar:
7+
name: GRU
8+
identifier: GRU
9+
parent: stock_prediction
10+
weight: 9
11+
hero: images/stock-market-prediction-using-data-mining-techniques.jpg
12+
tags: ["Finance", "Deep Learning", "Forecasting"]
13+
categories: ["Finance"]
14+
---
15+
## Introduction
16+
17+
In this article, we will explore time series data extracted from the **stock market**, focusing on prominent technology companies such as Apple, Amazon, Google, and Microsoft. Our objective is to equip data analysts and scientists with the essential skills to effectively manipulate and interpret stock market data.
18+
19+
To achieve this, we will utilize the *yfinance* library to fetch stock information and leverage visualization tools such as Seaborn and Matplotlib to illustrate various facets of the data. Specifically, we will explore methods to analyze stock risk based on historical performance, and implement predictive modeling using **GRU/ LSTM** models.
20+
21+
Throughout this tutorial, we aim to address the following key questions:
22+
23+
1. How has the stock price evolved over time?
24+
2. What is the average **daily return** of the stock?
25+
3. How does the **moving average** of the stocks vary?
26+
4. What is the **correlation** between different stocks?
27+
5. How can we forecast future stock behavior, exemplified by predicting the closing price of Apple Inc. using LSTM or GRU?"
28+
29+
---
30+
31+
## Getting Data
32+
33+
The initial step involves **acquiring and loading** the data into memory. Our source of stock data is the **Yahoo Finance** website, renowned for its wealth of financial market data and investment tools. To access this data, we'll employ the **yfinance** library, known for its efficient and Pythonic approach to downloading market data from Yahoo. For further insights into yfinance, refer to the article titled [Reliably download historical market data from with Python](https://aroussi.com/post/python-yahoo-finance).
34+
35+
### Install Dependencies
36+
37+
```bash
38+
pip install -qU yfinance seaborn
39+
```
40+
41+
### Configuration Code
42+
43+
```python
44+
import pandas as pd
45+
import numpy as np
46+
47+
import matplotlib.pyplot as plt
48+
import seaborn as sns
49+
sns.set_style('whitegrid')
50+
plt.style.use("fivethirtyeight")
51+
%matplotlib inline #comment if you are not using a jupyter notebook
52+
53+
# For reading stock data from yahoo
54+
from pandas_datareader.data import DataReader
55+
import yfinance as yf
56+
from pandas_datareader import data as pdr
57+
58+
yf.pdr_override()
59+
60+
# For time stamps
61+
from datetime import datetime
62+
63+
# Get Microsoft data
64+
data = yf.download("MSFT", start, end)
65+
```
66+
67+
## Statistical Analysis on the price
68+
69+
### Summary
70+
71+
```python
72+
# Summary Stats
73+
data.describe()
74+
```
75+
76+
### Closing Price
77+
78+
The closing price is the last price at which the stock is traded during the regular trading day. A stock’s closing price is the standard benchmark used by investors to track its performance over time.
79+
80+
```python
81+
plt.figure(figsize=(14, 5))
82+
plt.plot(data['Adj Close'], label='Close Price')
83+
plt.xlabel('Date')
84+
plt.ylabel('Close Price [$]')
85+
plt.title('Stock Price History')
86+
plt.legend()
87+
plt.show()
88+
```
89+
90+
### Volume of Sales
91+
92+
Volume is the amount of an asset or security that _changes hands over some period of time_, often over the course of a day. For instance, the stock trading volume would refer to the number of shares of security traded between its daily open and close. Trading volume, and changes to volume over the course of time, are important inputs for technical traders.
93+
94+
```python
95+
plt.figure(figsize=(14, 5))
96+
plt.plot(data['Volume'], label='Volume')
97+
plt.xlabel('Date')
98+
plt.ylabel('Volume')
99+
plt.title('Stock Price History')
100+
plt.show()
101+
```
102+
103+
### Moving Average
104+
105+
The moving average (MA) is a simple **technical analysis** tool that smooths out price data by creating a constantly updated average price. The average is taken over a specific period of time, like 10 days, 20 minutes, 30 weeks, or any time period the trader chooses.
106+
107+
```python
108+
ma_day = [10, 20, 50]
109+
110+
# compute moving average (can be also done in a vectorized way)
111+
for ma in ma_day:
112+
column_name = f"{ma} days MA"
113+
data[column_name] = data['Adj Close'].rolling(ma).mean()
114+
115+
plt.figure(figsize=(14, 5))
116+
data[['Adj Close', '10 days MA', '20 days MA', '50 days MA']].plot()
117+
plt.xlabel('Date')
118+
plt.ylabel('Volume')
119+
plt.title('Stock Price History')
120+
plt.show()
121+
```
122+
123+
## Statistical Analysis on the returns
124+
125+
Now that we've done some baseline analysis, let's go ahead and dive a little deeper. We're now going to analyze the risk of the stock. In order to do so we'll need to take a closer look at the daily changes of the stock, and not just its absolute value. Let's go ahead and use pandas to retrieve teh daily returns for the **Microsoft** stock.
126+
127+
```python
128+
# Compute daily return in percentage
129+
data['Daily Return'] = data['Adj Close'].pct_change()
130+
131+
# simple plot
132+
plt.figure(figsize=(14, 5))
133+
data['Daily Return'].hist(bins=50)
134+
plt.title('MSFT Daily Return Distribution')
135+
plt.xlabel('Daily Return')
136+
plt.show()
137+
138+
# histogram
139+
plt.figure(figsize=(8, 5))
140+
data['Daily Return'].plot()
141+
plt.title('MSFT Daily Return')
142+
plt.show()
143+
144+
```
145+
146+
## Data Preparation
147+
148+
```python
149+
# Create a new dataframe with only the 'Close column
150+
X = data.filter(['Adj Close'])
151+
# Convert the dataframe to a numpy array
152+
X = X.values
153+
# Get the number of rows to train the model on
154+
training_data_len = int(np.ceil(len(X)*.95))
155+
156+
# Scale data
157+
from sklearn.preprocessing import MinMaxScaler
158+
scaler = MinMaxScaler(feature_range=(0,1))
159+
scaled_data = scaler.fit_transform(X)
160+
161+
scaled_data
162+
```
163+
164+
Split training data into small chunks to ingest into LSTM and GRU
165+
166+
```python
167+
# Create the training data set
168+
# Create the scaled training data set
169+
train_data = scaled_data[0:int(training_data_len), :]
170+
# Split the data into x_train and y_train data sets
171+
x_train = []
172+
y_train = []
173+
seq_length = 60
174+
for i in range(seq_length, len(train_data)):
175+
x_train.append(train_data[i-60:i, 0])
176+
y_train.append(train_data[i, 0])
177+
if i<= seq_length+1:
178+
print(x_train)
179+
print(y_train, end="\n\n")
180+
181+
# Convert the x_train and y_train to numpy arrays
182+
x_train, y_train = np.array(x_train), np.array(y_train)
183+
184+
# Reshape the data
185+
x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], 1))
186+
```
187+
188+
## GRU
189+
190+
Gated-Recurrent Unit (GRU) is adopted in this part
191+
192+
```python
193+
from tensorflow.keras.models import Sequential
194+
from tensorflow.keras.layers import GRU, Dense, Dropout
195+
196+
lstm_model = Sequential()
197+
lstm_model.add(GRU(units=128, return_sequences=True, input_shape=(seq_length, 1)))
198+
lstm_model.add(Dropout(0.2))
199+
lstm_model.add(GRU(units=64, return_sequences=False))
200+
lstm_model.add(Dropout(0.2))
201+
lstm_model.add(Dense(units=1))
202+
203+
lstm_model.compile(optimizer='adam', loss='mean_squared_error')
204+
lstm_model.fit(x_train, y_train, epochs=10, batch_size=8)
205+
```
206+
207+
## LSTM
208+
209+
Long Short-Term Memory (LSTM) is adopted in this part
210+
211+
```python
212+
from tensorflow.keras.layers import LSTM
213+
214+
lstm_model = Sequential()
215+
lstm_model.add(LSTM(units=128, return_sequences=True, input_shape=(seq_length, 1)))
216+
lstm_model.add(Dropout(0.2))
217+
lstm_model.add(LSTM(units=64, return_sequences=False))
218+
lstm_model.add(Dropout(0.2))
219+
lstm_model.add(Dense(units=1))
220+
221+
lstm_model.compile(optimizer='adam', loss='mean_squared_error')
222+
lstm_model.fit(x_train, y_train, epochs=10, batch_size=8)
223+
```
224+
225+
## Testing Metrics
226+
227+
* root mean squared error (RMSE)
228+
229+
```python
230+
231+
# Create the testing data set
232+
# Create a new array containing scaled values from index 1543 to 2002
233+
test_data = scaled_data[training_data_len - 60: , :]
234+
# Create the data sets x_test and y_test
235+
x_test = []
236+
y_test = dataset[training_data_len:, :]
237+
for i in range(60, len(test_data)):
238+
x_test.append(test_data[i-60:i, 0])
239+
240+
# Convert the data to a numpy array
241+
x_test = np.array(x_test)
242+
243+
# Reshape the data
244+
x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1], 1 ))
245+
246+
# Get the models predicted price values
247+
predictions_gru = gru_model.predict(x_test)
248+
predictions_gru = scaler.inverse_transform(predictions_gru)
249+
predictions_lstm = lstm_model.predict(x_test)
250+
predictions_lstm = scaler.inverse_transform(predictions_lstm)
251+
252+
# Get the root mean squared error (RMSE)
253+
rmse_lstm = np.sqrt(np.mean(((predictions_lstm - y_test) ** 2)))
254+
rmse_gru = np.sqrt(np.mean(((predictions_gru - y_test) ** 2)))
255+
print(f"LSTM RMSE: {rmse_lstm:.4f}, GRU RMSE: {rmse_gru:.4f}")
256+
```
257+
258+
> "LSTM RMSE: **4.2341**, GRU RMSE: 3.3575"
259+
260+
### Test Plot
261+
262+
{{< img src="/posts/finance/stock_prediction/GRU/images/test_results.png" align="center" title="Results">}}
263+
GRU-based model shows a bit better results both graphically and on MSE. However, this does not tell us anything about the actual profitability of these models.
264+
265+
## Possible trading performance
266+
267+
The strategy implementation is:
268+
269+
* BUY: if prediction > actual_price
270+
* SELL: if prediction < actual_price
271+
272+
To close a position the next candle _close_ is waited. However, LSTM and GRU has some offset that does not allow a proper utilization of this strategy.
273+
274+
Hence, the **returns** of the predictions are adopted.
275+
276+
```python
277+
# Assume a trading capital of $10,000
278+
trading_capital = 10000
279+
pred_gru_df = pd.DataFrame(predictions_gru, columns=['Price'])
280+
pred_test_df = pd.DataFrame(y_test, columns=['Price'])
281+
pred_gru_df['returns'] = pred_gru_df.pct_change(-1)
282+
pred_test_df['returns'] = pred_test_df.pct_change(-1)
283+
284+
# Compute Wins
285+
wins = ((pred_gru_df.dropna().returns<0) & (pred_test_df.dropna().returns<0)) | ((pred_gru_df.dropna().returns>0) & (pred_test_df.dropna().returns>0))
286+
print(wins.value_counts())
287+
288+
returns_df = pd.concat([pred_gru_df.returns, pred_test_df.returns], axis=1).dropna()
289+
total_pos_return = pred_test_df.dropna().returns[wins].abs().sum()
290+
total_neg_return = pred_test_df.dropna().returns[np.logical_not(wins)].abs().sum()
291+
292+
# compute final capital and compare with BUY&HOLD strategy
293+
final_capital = trading_capital*(1+total_pos_return-total_neg_return)
294+
benchmark_return = (valid.Close.iloc[-1] - valid.Close.iloc[0])/valid.Close.iloc[0]
295+
bench_capital = trading_capital*(1+benchmark_return)
296+
print(final_capital, bench_capital)
297+
```
298+
299+
> returns
300+
>
301+
>
302+
> True 81
303+
> False 72
304+
>
305+
> Name: count, dtype: int64
306+
307+
> 10535.325 9617.617
308+
309+
## Conclusion
310+
311+
As showed in the previous section, these two simple Deep Learning models exhibits interesting positive results both regarding regression and trading metrics.
312+
The latter is particularly important, indeed a return of **5%** is obtained while the stock price decreased of approximately 4%. This also lead to a very high sharpe and colmar ratio.

data/en/sections/experiences.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ experiences:
2727
# give some points about what was your responsibilities at the company.
2828
responsibilities:
2929
- Designing of NLP models as DistilBERT or GPT-2. Then ChatGPT API and Anthropic.
30-
- Evaluation of the future _junior_ candidates in my team.
30+
- Evaluation of future _junior_ candidates in my team.
3131
- Managing (on the technical side) an external company / service provider of our team.
3232
- Emotion Detection from voice/ audio -> state-of-the-art model (TIM-Net)
3333

0 commit comments

Comments
 (0)