"""
# **LBGM**
* LightGBM is a gradient boosting framework that uses tree based learning algorithms. It is designed to be distributed and efficient with the following advantages:
* Faster training speed and higher efficiency (6 times faster than XGBoost)
* Lower memory usage.
* Better accuracy.
* Support of parallel, distributed, and GPU learning.
* Capable of handling large-scale data.
"""
"""

### **<span>Dataset Structure</span>**

> **train.csv** - The training set
> 
> 1.  timestamp - A timestamp for the minute covered by the row.
> 2.  Asset_ID - An ID code for the cryptoasset.
> 3.  Count - The number of trades that took place this minute.
> 4.  Open - The USD price at the beginning of the minute.
> 5.  High - The highest USD price during the minute.
> 6.  Low - The lowest USD price during the minute.
> 7.  Close - The USD price at the end of the minute.
> 8.  Volume - The number of cryptoasset u units traded during the minute.
> 9.  VWAP - The volume-weighted average price for the minute.
> 10. Target - 15 minute residualized returns. See the 'Prediction and Evaluation section of this notebook for details of how the target is calculated.
> 11. Weight - Weight, defined by the competition hosts [here](https://www.kaggle.com/cstein06/tutorial-to-the-g-research-crypto-competition)
> 12. Asset_Name - Human readable Asset name.
> 
>
> **example_test.csv** - An example of the data that will be delivered by the time series API.
> 
> **example_sample_submission.csv** - An example of the data that will be delivered by the time series API. The data is just copied from train.csv.
> 
> **asset_details.csv** - Provides the real name and of the cryptoasset for each Asset_ID and the weight each cryptoasset receives in the metric.
> 
> **supplemental_train.csv** - After the submission period is over this file's data will be replaced with cryptoasset prices from the submission period. In the Evaluation phase, the train, train supplement, and test set will be contiguous in time, apart from any missing data. The current copy, which is just filled approximately the right amount of data from train.csv is provided as a placeholder.
>
> - 📌 There are 14 coins in the dataset
>
> - 📌 There are 4 years  in the [full] dataset
"""
"""
# **Importing Libraries**
"""
import pandas as pd
import numpy as np
from datetime import datetime
from lightgbm import LGBMRegressor
import gresearch_crypto
import traceback
import time
from datetime import datetime
import matplotlib.pyplot as plt
import plotly.graph_objects as go
from sklearn.model_selection import GridSearchCV
import seaborn as sns
from sklearn.model_selection import train_test_split
"""
### **Importing Data**
"""
import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))
"""
## **Loading Data**
"""
path = "/kaggle/input/g-research-crypto-forecasting/"
df_train = pd.read_csv(path + "train.csv")
df_test = pd.read_csv(path + "example_test.csv")
df_asset_details = pd.read_csv(path + "asset_details.csv")
df_supp_train = pd.read_csv(path + "supplemental_train.csv")
# Checking Train data 
df_train.head()
df_train.shape
# Checking Test data set
df_test.head()
df_test.shape
"""
* By visualising Train and Test data set (train.csv) except contains less rows.
"""
"""
### **EDA**
* Let's continue describing and analyzing the data 
"""
df_train.describe()
# Checking Test data sets
df_test.describe()
df_train.isnull().sum()
df_test.isnull().sum()
"""
* seems like the VWAP column has NaN values we will check it later
"""
df_train.info()
df_test.info()
"""
* By checking train data and test data there is some missing values in train data set
"""
"""
## Timestamp
* We define a helper function that will turn a date format into a timestamp to use for indexing.
"""
df_asset_details
"""
* There are total 14 unque coins
"""
df_supp_train
df_supp_train.info()
df_supp_train.isnull().sum()
# auxiliary function, from datetime to timestamp
totimestamp = lambda s: np.int32(time.mktime(datetime.strptime(s, "%d/%m/%Y").timetuple()))
"""
### ****Checking Time range****
"""
df_train.Asset_ID.unique()
df_train.Asset_ID.value_counts()
"""
**Bitcoin**
"""
df_train[df_train['Asset_ID'] == 1].set_index("timestamp")
btc = df_train[df_train['Asset_ID'] == 1].set_index("timestamp")
datetime.fromtimestamp(btc.index[0])
# Starting Date Time
# %A= day,%B = Month, %d = month number, %Y  = Year,%I =hours , %M =minutes starting date, %S = seconds
datetime.fromtimestamp(btc.index[0]).strftime("%A, %B %d, %Y  %I: %M: %S")
# Above function store in a starting date formate
beg_btc = datetime.fromtimestamp(btc.index[0]).strftime("%A, %B %d, %Y  %I: %M: %S")
# Ending Date time
datetime.fromtimestamp(btc.index[-1]).strftime("%A, %B %d, %Y %I : %M : %S")
end_btc = datetime.fromtimestamp(btc.index[-1]).strftime("%A, %B %d, %Y %I : %M : %S")
print('Bitcoin data goes from ', beg_btc, ' to ', end_btc)
"""
# Selecting 4-Coins
* Bitcoin  = btc
* Etherium = eth
* Binance  = bnb
* Cardano  = ada
"""
## Checking Time Range
btc = df_train[df_train["Asset_ID"]==1].set_index("timestamp") # Asset_ID = 1 for Bitcoin
eth = df_train[df_train["Asset_ID"]==6].set_index("timestamp") # Asset_ID = 6 for Ethereum
bnb = df_train[df_train["Asset_ID"]==0].set_index("timestamp") # Asset_ID = 0 for Binance Coin
ada = df_train[df_train["Asset_ID"]==3].set_index("timestamp") # Asset_ID = 3 for Cardano

beg_btc = datetime.fromtimestamp(btc.index[0]).strftime("%A, %B %d, %Y %I:%M:%S") 
end_btc = datetime.fromtimestamp(btc.index[-1]).strftime("%A, %B %d, %Y %I:%M:%S") 
beg_eth = datetime.fromtimestamp(eth.index[0]).strftime("%A, %B %d, %Y %I:%M:%S") 
end_eth = datetime.fromtimestamp(eth.index[-1]).strftime("%A, %B %d, %Y %I:%M:%S")
beg_bnb = datetime.fromtimestamp(eth.index[0]).strftime("%A, %B %d, %Y %I:%M:%S") 
end_bnb = datetime.fromtimestamp(eth.index[-1]).strftime("%A, %B %d, %Y %I:%M:%S")
beg_ada = datetime.fromtimestamp(eth.index[0]).strftime("%A, %B %d, %Y %I:%M:%S") 
end_ada = datetime.fromtimestamp(eth.index[-1]).strftime("%A, %B %d, %Y %I:%M:%S")

print('Bitcoin data goes from ', beg_btc, ' to ', end_btc) 
print('Ethereum data goes from ', beg_eth, ' to ', end_eth)
print('Binance coin data goes from ', beg_bnb, ' to ', end_bnb) 
print('Cardano data goes from ', beg_ada, ' to ', end_ada)
"""
* Here we have 4-Years of data range 2018-2021
"""
"""
# Checking corelation of each individual coin
* By using heatmap
"""
"""
#### **Btc**
"""
plt.figure(figsize=(8,6))
sns.heatmap(btc[['Count','Open','High','Low','Close','Volume','VWAP','Target']].corr(), 
            vmin=-1.0, vmax=1.0, annot=True, cmap='coolwarm', linewidths=0.1)
plt.show()
"""
# Checking candle chart b/w 2-coins Btc,Eth
"""
btc.iloc[-1440:]
"""
# Checking candle stick for Etherium for last 24 hrs
"""
eth.iloc[-1440:]
# Checking 1 day candle stick chart 24hrs = 1440 mins
btc_mini = btc.iloc[-1440:]
eth_mini = eth.iloc[-1440:]

# index = timestamp,
fig = go.Figure(data=[go.Candlestick(x=btc_mini.index, open=btc_mini['Open'], high=btc_mini['High'], low=btc_mini['Low'], close=btc_mini['Close'])])
fig.update_xaxes(title_text="$")
fig.update_yaxes(title_text="Index")
fig.update_layout(title="Bitcoin Price, Last 24 hours")
fig.show()

fig = go.Figure(data=[go.Candlestick(x=eth_mini.index, open=eth_mini['Open'], high=eth_mini['High'], low=eth_mini['Low'], close=eth_mini['Close'])])
fig.update_xaxes(title_text="$")
fig.update_yaxes(title_text="Index")
fig.update_layout(title="Ethereum Price,Last 24 hours")
fig.show()
"""
* We can Observe the corelation b/w two coins
"""
"""
# Ploting the Btc & Etherium
"""
f = plt.figure(figsize=(15,4))

# fill NAs for BTC and ETH
btc = btc.reindex(range(btc.index[0],btc.index[-1]+60,60),method='pad')
eth = eth.reindex(range(eth.index[0],eth.index[-1]+60,60),method='pad')

ax = f.add_subplot(121)
plt.plot(btc['Close'], color='yellow', label='BTC')
plt.legend()
plt.xlabel('Time (timestamp)')
plt.ylabel('Bitcoin')

ax2 = f.add_subplot(122)
ax2.plot(eth['Close'], color='purple', label='ETH')
plt.legend()
plt.xlabel('Time (timestamp)')
plt.ylabel('Ethereum')

plt.tight_layout()
plt.show()
"""
* We can also confirm through another visual observation that, within the 4 recent years, BTC and ETH prices are correlated.
"""
"""
# Coin corelation for 1-weak
"""
data =df_train[-10080:]
check = pd.DataFrame()
for i in data.Asset_ID.unique():
    check[i] = data[data.Asset_ID==i]['Target'].reset_index(drop=True) 
    
plt.figure(figsize=(10,8))
sns.heatmap(check.dropna().corr(), vmin=-1.0, vmax=1.0, annot=True, cmap='coolwarm', linewidths=0.1)
plt.show()
"""
* Interestingly, in the last 1-weak we have several coins that are highly correlated with one another.
"""
"""
# Feature Extraction
* we add some features for our future predection
"""
def hlco_ratio(df): 
    return (df['High'] - df['Low'])/(df['Close']-df['Open'])
def upper_shadow(df):
    return df['High'] - np.maximum(df['Close'], df['Open'])
def lower_shadow(df):
    return np.minimum(df['Close'], df['Open']) - df['Low']

def get_features(df):
    df_feat = df[['Count', 'Open', 'High', 'Low', 'Close', 'Volume', 'VWAP']].copy()
    df_feat['Upper_Shadow'] = upper_shadow(df_feat)
    df_feat['hlco_ratio'] = hlco_ratio(df_feat)
    df_feat['Lower_Shadow'] = lower_shadow(df_feat)
    return df_feat
"""
# Splitting
"""
# Training data set
train_data = df_train
train_data
def get_Xy_and_model_for_asset(df_train, asset_id):
    df = df_train[df_train["Asset_ID"] == asset_id]
    
    df = df.sample(frac=0.2)
    df_proc = get_features(df)
    df_proc['y'] = df['Target']
    df_proc.replace([np.inf, -np.inf], np.nan, inplace=True)
    df_proc = df_proc.dropna(how="any")
    
    
    X = df_proc.drop("y", axis=1)
    y = df_proc["y"]   
    model = LGBMRegressor()
    model.fit(X, y)
    return X, y, model


Xs = {}
ys = {}
models = {}

for asset_id, asset_name in zip(df_asset_details['Asset_ID'], df_asset_details['Asset_Name']):
    print(f"Training model for {asset_name:<16} (ID={asset_id:<2})")
    X, y, model = get_Xy_and_model_for_asset(train_data, asset_id)       
    try:
        Xs[asset_id], ys[asset_id], models[asset_id] = X, y, model
    except: 
        Xs[asset_id], ys[asset_id], models[asset_id] = None, None, None 
"""
# Hyperparam Tuning
* We will perform GridSearch for each LGBM model of 14 coins.
"""
parameters = {
    # 'max_depth': range (2, 10, 1),
    'num_leaves': range(21, 161, 10),
    'learning_rate': [0.1, 0.01, 0.05]
}

new_models = {}
for asset_id, asset_name in zip(df_asset_details['Asset_ID'], df_asset_details['Asset_Name']):
    print("GridSearchCV for: " + asset_name)
    grid_search = GridSearchCV(
        estimator=get_Xy_and_model_for_asset(df_train, asset_id)[2], # bitcoin
        param_grid=parameters,
        n_jobs = -1,
        cv = 5,
        verbose=True
    )
    grid_search.fit(Xs[asset_id], ys[asset_id])
    new_models[asset_id] = grid_search.best_estimator_
    grid_search.best_estimator_
# Checking the Model interface
for asset_id, asset_name in zip(df_asset_details['Asset_ID'], df_asset_details['Asset_Name']):
    print(f"Tuned model for {asset_name:<1} (ID={asset_id:})")
    print(new_models[asset_id])
"""
# Submission
"""
env = gresearch_crypto.make_env()
iter_test = env.iter_test()

for i, (df_test, df_pred) in enumerate(iter_test):
    for j , row in df_test.iterrows():        
        if new_models[row['Asset_ID']] is not None:
            try:
                model = new_models[row['Asset_ID']]
                x_test = get_features(row)
                y_pred = model.predict(pd.DataFrame([x_test]))[0]
                df_pred.loc[df_pred['row_id'] == row['row_id'], 'Target'] = y_pred
            except:
                df_pred.loc[df_pred['row_id'] == row['row_id'], 'Target'] = 0
                traceback.print_exc()
        else: 
            df_pred.loc[df_pred['row_id'] == row['row_id'], 'Target'] = 0  
    
    env.predict(df_pred)