text: # -*- coding: utf-8 -*-
  """
  Created on Thu Sep  7 19:15:13 2017
  
  @author: RAGHAV
  """
  
  import time
  import numpy as np
  import pandas as pd
  import pandas_datareader as pdr
  
  from keras.layers import LSTM
  from keras.models import Sequential
  from keras.layers.wrappers import TimeDistributed
  from keras.layers.core import Dense, Activation, Dropout
  
  from sklearn.preprocessing import MinMaxScaler
  
  
  
  import seaborn as sns
  import matplotlib.pyplot as plt
  
  sns.set_style('whitegrid')
  sns.set_context('talk')
  
  params = {'legend.fontsize': 'x-large',
            'figure.figsize': (15, 5),
            'axes.labelsize': 'x-large',
            'axes.titlesize':'x-large',
            'xtick.labelsize':'x-large',
            'ytick.labelsize':'x-large'}
  
  plt.rcParams.update(params)
  
  # get stock price information 
  def get_raw_data(index_name,retry_attempts = 3):   
      if index_name:
          while retry_attempts > 0 :
              try:
                  df = pdr.get_data_yahoo(index_name)
                  new_df = df.reindex(index=pd.date_range(df.index.min(), 
                                            df.index.max(), 
                                            freq='D')).fillna(method='ffill')
                  retry_attempts = 0
                  return new_df
              except:
                  print("Data pull failed. {} retry attempts remaining".\
                        format(retry_attempts))
                  retry_attempts = retry_attempts - 1
      else:
          print("Invalid usage. Parameter index_name is required")
      return None
      
  
  # prepare training and testing data sets for LSTM based regression modeling
  def get_reg_train_test(timeseries,sequence_length= 51,
                     train_size=0.9,roll_mean_window=5,
                     normalize=True,scale=False):
      # smoothen out series
      if roll_mean_window:
          timeseries = timeseries.rolling(roll_mean_window).mean().dropna()
      
      # create windows
      result = []
      for index in range(len(timeseries) - sequence_length):
          result.append(timeseries[index: index + sequence_length])
             
      
      # normalize data as a variation of 0th index
      if normalize:
          normalised_data = []
          for window in result:
              normalised_window = [((float(p) / float(window[0])) - 1) \
                                     for p in window]
              normalised_data.append(normalised_window)
          result = normalised_data
      
      # identify train-test splits
      result = np.array(result) 
      row = round(train_size * result.shape[0])
      
      # split train and test sets
      train = result[:int(row), :]
      test = result[int(row):, :]
      
      # scale data in 0-1 range
      scaler = None
      if scale:
          scaler=MinMaxScaler(feature_range=(0, 1))
          train = scaler.fit_transform(train)
          test = scaler.transform(test)
        
      # split independent and dependent variables  
      x_train = train[:, :-1]
      y_train = train[:, -1]
          
          
      x_test = test[:, :-1]
      y_test = test[:, -1]
      
      # Transforms for LSTM input
      x_train = np.reshape(x_train, (x_train.shape[0], 
                                     x_train.shape[1], 
                                     1))
      x_test = np.reshape(x_test, (x_test.shape[0], 
                                   x_test.shape[1], 
                                   1)) 
      
      return x_train,y_train,x_test,y_test,scaler   
  
  
  # prepare training and testing data sets for LSTM based sequence modeling
  def get_seq_train_test(time_series, scaling=True,train_size=0.9):
      scaler = None
      if scaling:
          scaler = MinMaxScaler(feature_range=(0, 1))
          scaled_stock_series = scaler.fit_transform(time_series)
      else:
          scaled_stock_series = time_series
          
      train_size = int(len(scaled_stock_series) * train_size)
  
      train = scaled_stock_series[0:train_size]
      test = scaled_stock_series[train_size:len(scaled_stock_series)]
      
      return train,test,scaler 
  
  
  # Get stacked LSTM model for regression modeling
  def get_reg_model(layer_units=[100,100],dropouts=[0.2,0.2],window_size=50):
      # build LSTM network
      model = Sequential()
      
      # hidden layer 1
      model.add(LSTM(layer_units[0], 
                     input_shape=(window_size,1), 
                     return_sequences=True))
      model.add(Dropout(dropouts[0]))
      
      # hidden layer 2
      model.add(LSTM(layer_units[1]))
      model.add(Dropout(dropouts[1]))
      
      # output layer
      model.add(Dense(1))
      model.add(Activation("linear"))
      
      start = time.time()
      model.compile(loss="mse", optimizer="rmsprop")
      print("> Compilation Time : ", time.time() - start)
      print(model.summary())
      return model
  
  
  # Get stacked LSTM model for sequence modeling
  def get_seq_model(hidden_units=4,input_shape=(1,1),verbose=False):
      # create and fit the LSTM network
      model = Sequential()
      # samples*timesteps*featuress
  
      model.add(LSTM(input_shape=input_shape, 
                     units = hidden_units, 
                     return_sequences=True
      ))
      
      # readout layer. TimeDistributedDense uses the same weights for all
      # time steps.
      model.add(TimeDistributed(Dense(1)))
      start = time.time()
      
      model.compile(loss="mse", optimizer="rmsprop")
      
      if verbose:
          print("> Compilation Time : ", time.time() - start)
          print(model.summary())
          
      return model
  
  
  # Window wise prediction function
  def predict_reg_multiple(model, data, window_size=6, prediction_len=3):
      prediction_list = []
      
      # loop for every sequence in the dataset
      for window in range(int(len(data)/prediction_len)):
          _seq = data[window*prediction_len]
          predicted = []
          # loop till required prediction length is achieved
          for j in range(prediction_len):
              predicted.append(model.predict(_seq[np.newaxis,:,:])[0,0])
              _seq = _seq[1:]
              _seq = np.insert(_seq, [window_size-1], predicted[-1], axis=0)
          prediction_list.append(predicted)
      return prediction_list
  
  
  # Plot window wise 
  def plot_reg_results(predicted_data, true_data, prediction_len=3):
      fig = plt.figure(facecolor='white')
      ax = fig.add_subplot(111)
      
      # plot actual data
      ax.plot(true_data, 
              label='True Data',
              c='black',alpha=0.3)
      
      # plot flattened data
      plt.plot(np.array(predicted_data).flatten(), 
               label='Prediction_full',
               c='g',linestyle='--')
      
      #plot each window in the prediction list
      for i, data in enumerate(predicted_data):
          padding = [None for p in range(i * prediction_len)]
          plt.plot(padding + data, label='Prediction',c='black')
  
      plt.title("Forecast Plot with Prediction Window={}".format(prediction_len))
      plt.show()
      