text: import os 
  from datetime import datetime
  import time
  from sklearn.preprocessing import StandardScaler
  import plotly.express as px
  import plotly.graph_objs as go
  import matplotlib.pyplot as plt
  import seaborn as sns
  import math
  import statsmodels.api as sm 
  
  from statsmodels.tsa.ar_model import AutoReg
  from statsmodels.tsa.arima_model import ARMA
  from statsmodels.tsa.stattools import adfuller
  from statsmodels.graphics.tsaplots import plot_acf
  from statsmodels.graphics.tsaplots import plot_pacf
  from statsmodels.tsa.arima_model import ARIMA
  from statsmodels.tsa.statespace.sarimax import SARIMAX
  from statsmodels.tsa.seasonal import seasonal_decompose
  from scipy import stats
  from itertools import product
  
  import warnings
  warnings.filterwarnings("ignore")
  import numpy as np 
  import pandas as pd 
  import os 
  
  """
  Read the info and train data
  """
  info = pd.read_csv("asset_details.csv")
  ctrain = pd.read_csv("train.csv")
  
  #print(info.head(10))
  
  # Impute missing time value 
  def c_time_sub(asset_id,data=ctrain):
      df=data[ctrain["Asset_ID"]==asset_id].set_index("timestamp")
      df=df.reindex(range(df.index[0],df.index[-1]+60,60), method="pad")
      return df
  
  # subgroup BTC(bitcoin)
  btc = c_time_sub(asset_id=1)
  
  # subgroup ETH(Ethereum)
  eth = c_time_sub(asset_id=6)
  
  # subgroup cardano(bitcoin)
  ada = c_time_sub(asset_id=3)
  
  #print("btc",btc.head(10),"eth",eth.head(10),"ADA",ada.head(10))
  
  # time frame selection: from datetime to timestamp
  totimestamp= lambda s: np.int32(time.mktime(datetime.strptime(s,"%d/%m/%Y").timetuple()))
  
  # Log Return 
  def log_return(series, periods=1):
      return np.log(series).diff(periods=periods)
  
  # Data Selection
  def crypto_sub(asset_id ,data= ctrain ):
      df = data[data["Asset_ID"]==asset_id].reset_index(drop = True)
      df['timestamp'] = pd.to_datetime(df['timestamp'], unit='s')
      df = df.set_index('timestamp')
      return df
  
  #Subgroup Bitcoin
  btc1=crypto_sub(asset_id=1)
  
  #Subgroup Eth
  eth1=crypto_sub(asset_id=6)
  
  #Subgroup Ada
  ada1=crypto_sub(asset_id=3) 
  
  """
  Data visualization
  """
  info_s=info.sort_values("Weight")
  fig_index=px.bar(info_s,x="Asset_Name" , y="Weight", color="Weight", title="Popular Cryptocurrency Weight Distribution")
  #fig_index.show()
  
  """
  Cryptocurrency Log return correlation plot for 2021
  """
  # time frame selection: from datetime to timestamp
  totimestamp= lambda s: np.int32(time.mktime(datetime.strptime(s,"%d/%m/%Y").timetuple()))
  
  # Log Return 
  def log_return(series, periods=1):
      return np.log(series).diff(periods=periods)
  
  all2021=pd.DataFrame([])
  for asset_id, asset_name in zip(info.Asset_ID, info.Asset_Name):
      asset=ctrain[ctrain["Asset_ID"]==asset_id].set_index("timestamp")
      asset=asset.loc[totimestamp("01/01/2021"):totimestamp("21/09/2021")]
      asset=asset.reindex(range(asset.index[0],asset.index[-1]+60,60), method="pad")
      lret=log_return(asset.Close.fillna(0))[1:]
      all2021=all2021.join(lret,rsuffix=asset_name,how="outer")
      
  plt.imshow(all2021.corr());
  plt.yticks(info.Asset_ID, info.Asset_Name.values)
  plt.xticks(info.Asset_ID, info.Asset_Name.values,rotation="vertical");
  plt.colorbar()
  
  """
  Closing price Trend in 2021
  """
  # Impute Missing Time Value
  def c_time_sub(asset_id, data=ctrain):
      df=data[ctrain["Asset_ID"]==asset_id].set_index("timestamp")
      df=df.reindex(range(df.index[0],df.index[-1]+60,60), method="pad")
      return df
  
  #1. Subgroup Bitcoin
  btc=c_time_sub(asset_id=1)
  (btc.index[1:] -btc.index[:-1]).value_counts().head()
  btc.head()
  
  #2. Subgroup Ethereum
  eth=c_time_sub(asset_id=6)
  (btc.index[1:] -btc.index[:-1]).value_counts().head()
  eth.head()
  
  #3. Subgroup Cardano
  ada=c_time_sub(asset_id=3)
  (ada.index[1:] -ada.index[:-1]).value_counts().head()
  ada.head()
  
  # Create time interval for 2021
  def dur(start,end,data):
      df=data.loc[totimestamp(start): totimestamp(end)]
      return df
  
  btc2021= dur(start="01/01/2021", end="21/09/2021", data=btc)
  eth2021= dur(start="01/01/2021", end="21/09/2021", data=eth)
  ada2021= dur(start="01/01/2021", end="21/09/2021", data=ada)
  
  # Plot the Closing Price for BTC, ETH, ADA
  f= plt.figure(figsize=(10,12))  
  
  def gplot(no , data, price, label, ylabel, color):
      ax=f.add_subplot(no)
      plt.plot(data[price], label=label, color=color)
      plt.legend()
      plt.xlabel("Time")
      plt.ylabel(ylabel)
      return plt
  
  gplot(no=311, data=btc, price="Close" , label="BTC 2021 Overall Performance", ylabel="BTC Closing Price", color="Lightskyblue")
  gplot(no=312, data=eth, price="Close" ,label="ETH 2021 Overall Performance", ylabel="ETH Closing Price", color="Coral")
  gplot(no=313, data=ada, price="Close" ,label="Cardano 2021 Overall Performance", ylabel="ADA Closing Price", color="khaki")
  
  plt.tight_layout()
  plt.show()
  
  # Return rate for BTC,ETH and ADA
  f= plt.figure(figsize=(10,12))  
  gplot(no=311, data=btc, price="Target" , label="BTC 2021 15min Return Residue", ylabel="BTC residual return", color="Aqua")
  gplot(no=312, data=eth, price="Target" ,label="ETH 2021 15min Return Residue", ylabel="ETH residual return", color="Pink")
  gplot(no=313, data=ada, price="Target" ,label="ADA 2021 15min Return Residue", ylabel="ADA residual return", color="gold")
  
  
  plt.tight_layout()
  plt.show()
  
  
  def c_chart(data,label):
      candlestick = go.Figure(data = [go.Candlestick(x =data.index, 
                                                 open = data[('Open')], 
                                                 high = data[('High')], 
                                                 low = data[('Low')], 
                                                 close = data[('Close')])])
      candlestick.update_xaxes(title_text = 'Time',
                               rangeslider_visible = True)
  
      candlestick.update_layout(
      title = {
          'text': '{:} Candelstick Chart'.format(label),
          "y":0.8,
          "x":0.5,
          'xanchor': 'center',
          'yanchor': 'top'})
  
      candlestick.update_yaxes(title_text = 'Price in USD', ticksuffix = '$')
      return candlestick
  
  btc1.head()
  btc_candle=c_chart(btc1[-90:], label="BTC Price")
  btc_candle.show()
  
  eth_candle=c_chart(eth1[100888:100988], label="ETH Price ")
  eth_candle.show()
  
  ada_candle=c_chart(eth1[-500:-400], label="ETH Price ")
  ada_candle.show()
  
  
  # ARIMA Model
  def mini_data(data):
      df=data[["Close","Low","High","Open"]]
      return df
  
  btc1_mini=mini_data(btc1)
  eth1_mini=mini_data(eth1)
  ada1_mini=mini_data(ada1)
  btc1.head()
  
  # Function to Plot
  plt.rcParams["figure.figsize"]=(15,7)
  
  def season_df(data, label):
      df=data.resample("M").mean()
      seasonal_decompose(df.Close).plot()
      print(label)
      return plt.show()
  
  season_df(data=btc1_mini, label="BTC Seasonal Decomposition")
  
  season_df(data=eth1_mini, label="ETH Seasonal Decomposition")
  
  
  #ADA seasonal decompostion
  season_df(data=ada1_mini, label="ADA Seasonal Decomposition")
  
  #Box-Cox Transformation
  btc_month=btc1_mini.resample("M").mean()
  btc_month["close_box"], lmbda=stats.boxcox(btc_month.Close)
  
  #btc_month['close_box'], lmbda = stats.boxcox(btc_month.Close)
  
  qs = range(0, 3)
  ps = range(0, 3)
  d=1
  parameters = product(ps, qs)
  parameters_list = list(parameters)
  len(parameters_list)
  
  # Model Selection
  results = []
  best_aic = float("inf")
  warnings.filterwarnings('ignore')
  for param in parameters_list:
      try:
          model = SARIMAX(btc_month.close_box, order=(param[0], d, param[1])).fit(disp=-1)
      except ValueError:
          print('bad parameter combination:', param)
          continue
      aic = model.aic
      if aic < best_aic:
          best_model = model
          best_aic = aic
          best_param = param
      results.append([param, model.aic])
  
  result_table = pd.DataFrame(results)
  result_table.columns = ['parameters', 'aic']
  print(result_table.sort_values(by = 'aic', ascending=True).head())
  print(best_model.summary())
  
  best_model.plot_diagnostics(figsize=(15, 12))
  plt.show()
  
  def invboxcox(y,lmbda):
      if lmbda == 0:
          return(np.exp(y))
      else:
          return(np.exp(np.log(lmbda*y+1)/lmbda))
  
  
  # Prediction
  btc_month_pred = btc_month[['Close']]
  date_list = [datetime(2021, 6, 20), datetime(2021, 7, 20), datetime(2021, 8, 21), datetime(2021, 9, 21)]
  future = pd.DataFrame(index=date_list, columns= btc_month.columns)
  btc_month_pred = pd.concat([btc_month_pred, future])
  btc_month_pred['forecast'] = invboxcox(best_model.predict(start=datetime(2018, 1, 31), end=datetime(2021, 5, 31)), lmbda)
  plt.figure(figsize=(18,10))
  btc_month_pred.Close.plot()
  btc_month_pred.forecast.plot(color='r', ls='--', label='Predicted Close')
  plt.legend()
  plt.title('Bitcoin monthly forecast')
  plt.ylabel('USD')
  plt.show()