File size: 6,591 Bytes
b4ef86a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import yfinance as yf
import os

import tensorflow as tf

import streamlit as st

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

# from utils import StackLayer,BlockLayer


plt.style.use("dark_background")
mpl.rcParams['figure.facecolor'] = '#121212' 
mpl.rcParams['axes.facecolor'] = '#121212'
mpl.rcParams['grid.color'] = 'gray' 
mpl.rcParams['grid.linestyle'] = ':'

mpl.rcParams['text.color'] = 'white'  
mpl.rcParams['axes.labelcolor'] = 'white'
mpl.rcParams['axes.edgecolor'] = 'white'
mpl.rcParams['xtick.color'] = 'white'
mpl.rcParams['ytick.color'] = 'white'

class BlockLayer(tf.keras.layers.Layer):
 def __init__(self,lookback_period,horizon,n_layers,n_units,**kwargs):
    super().__init__(**kwargs)
    self.lookback_period=lookback_period
    self.horizon=horizon
    self.n_layers=n_layers
    self.n_units=n_units

    self.fully_connected=tf.keras.Sequential([tf.keras.layers.Dense(n_units,activation='relu') for _ in range(n_layers)],name="Fully_Connected_Layer")
    self.theta_layer=tf.keras.layers.Dense(lookback_period+horizon,activation='linear',name="Theta_Layer")

 def call(self,input):

    x=self.fully_connected(input)
    backcast_forecast=self.theta_layer(x)

    backcast=backcast_forecast[:,:-self.horizon]
    forecast=backcast_forecast[:,-self.horizon:]

    return backcast,forecast

class StackLayer(tf.keras.layers.Layer):
  def __init__(self,lookback_period,horizon,n_layers,n_units,num_blocks=4,**kwargs):
    super().__init__(**kwargs)
    self.num_blocks=num_blocks
    self.horizon=horizon
    self.first_block=BlockLayer(lookback_period=lookback_period,horizon=horizon,n_layers=n_layers,n_units=n_units,name="Initial_Block")
    self.block_list=[BlockLayer(lookback_period=lookback_period,horizon=horizon,n_layers=n_layers,n_units=n_units,name=f"Block_{i}") for i in range(1,num_blocks)]

  def call(self,input):

    block_backcast,block_forecast=self.first_block(input)
    stack_forecast_residual=tf.zeros(shape=(self.horizon),dtype=tf.float32)
    stack_forecast_residual=tf.expand_dims(stack_forecast_residual,axis=0)
    stack_forecast_residual=tf.keras.layers.Add()([stack_forecast_residual,block_forecast])
    stack_backcast_residual=tf.keras.layers.Subtract()([input,block_backcast])

    for block in self.block_list:
      block_backcast,block_forecast=block(stack_backcast_residual)
      stack_forecast_residual=tf.keras.layers.Add()([block_forecast,stack_forecast_residual])
      stack_backcast_residual=tf.keras.layers.Subtract()([stack_backcast_residual,block_backcast])

    return stack_backcast_residual,stack_forecast_residual

def initialize_session():
    if "yesterday" not in st.session_state:
        st.session_state.yesterday="Available"

def load_data():
  BTC_ticker=yf.Ticker("BTC-USD")
  BT_data=BTC_ticker.history(period="1wk")
  df=pd.DataFrame({"Close":BT_data["Close"]})
  if len(df)<=7:
    st.session_state.yesterday="Not Available"
    st.error("Yesterday's Price has not yet been updated. Please come back later to predict the price of Bitcoin tomorrow.")
  else:
    st.session_state.yesterday="Available"
    return df[-7:]

@st.cache_data
def load_results():
    app_directory = os.path.dirname(os.path.abspath(__file__))
    results_path = os.path.join(app_directory, 'Results.csv')
    results=pd.read_csv(results_path)
    results.rename(columns={"Unnamed: 0":"Model"},inplace=True)
    return results

@st.cache_resource(show_spinner=False)
def load_model():
    app_directory = os.path.dirname(os.path.abspath(__file__))
    model_path = os.path.join(app_directory, 'final_n_beats_v1')
    model = tf.keras.models.load_model(model_path,custom_objects={"BlockLayer":BlockLayer,
                                                                  "StackLayer":StackLayer})
    return model


df=load_data()
info=st.container()

with info:
    st.header("CoinSage")
    st.subheader("How It Works ?🤔")
    st.markdown("CoinSage uses the [NBeats](https://arxiv.org/abs/1905.10437) , a pure Deep Learning Model created by ElementAI that won the M4 Competition. The model uses a Stack of Blocks with residual connections (to prevent overfitting), where each Block consists of Fully Connected Layers. I have trained the model on closing Bitcoin Prices of the Last Five Years, with a window of 7 (it predicts the price of the BitCoin tomorrow using the prices of BitCoin in the previous week) starting from July 2nd,2018 till July 1st 2023. The model acheived a stunning MAE of 400 USD of the Test Set. However, since forecasting the price of BitCoin is linked with Aleatory Uncertainty, the model is not expected to be 100% accurate and we recommed to you not heavily rely on the model's predictions but rather use it as a reference. I am constantly working on finding better models to deal with Aleatory Uncertainty and Im currently working on understanding Bayesian Neural Networks. I will update the app once I build and test the model.")
    st.subheader("Other Models I Trained")
    st.markdown("In the process of creating this app, I have trained other Deep Learning Models suchs as DNNs, CNNs, LSTMs and also an Ensemble of DNNs trained on different loss functions. However (although not by much), I have found that NBeats outperforms all of them. The below plot shows the MAE of the different models on the Test Set.")
    results=load_results()
    fig,ax=plt.subplots(figsize=(15,12))
    ax=plt.bar(results["Model"],results["MAE"],width=0.3,label="MAE",color=(0.1,0.6,0.6,0.9))
    for i in range(len(results["Model"])):
        plt.text(i,results["MAE"][i]+2,round(results["MAE"][i],2),ha="center",fontsize=13)
    plt.xticks(rotation=90,fontsize=15)
    plt.xlabel("Model",fontsize=15)
    plt.ylabel("MAE (in USD)",fontsize=15)
    plt.title("MAE of Different Models",fontsize=20)
    st.pyplot(fig)

col1,col2,col3=st.columns(3)
if st.session_state.yesterday=="Available":
    if col2.button("Predict Tomorrow's Bitcoin"):
        fig, ax = plt.subplots(figsize=(10,7))
        ax=plt.plot(df["Close"])
        ax=plt.title("BTC-USD Closing Prices For Last Week")
        ax=plt.xlabel("Date")
        ax=plt.ylabel("Price(USD)")
        st.pyplot(fig)
        with st.spinner("Loading Model..."):
            model=load_model()
        st.success("Model Loaded Successfully!!")
        with st.spinner("Predicting..."):
            prediction=model.predict(df["Close"].values.reshape(1,7))
        st.success("Prediction Done!!")
        st.subheader(f"Tomorrow's Bitcoin Price is expected to be {prediction[0][0]:.2f} USD")