<reponame>African-Quant/WQU_MScFE_Capstone_Grp9<filename>.ipynb_checkpoints/deploy-checkpoint.py
# -*- coding: utf-8 -*-
"""models

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1B2oxbnW3_438Fxp0mBpdVrGCk5UFK5pT
"""

import os
import re
import tpqoa

import random
import numpy as np
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
from sklearn.model_selection import RandomizedSearchCV, GridSearchCV
import pandas as pd

from datetime import date, timedelta
import warnings
warnings.filterwarnings('ignore')


from sklearn.ensemble import RandomForestClassifier
from lightgbm import LGBMClassifier
from xgboost import XGBClassifier


pairs = ['EUR_USD', 'USD_JPY', 'GBP_USD', 'USD_CHF', 
        'AUD_USD', 'USD_CAD', 'NZD_USD', 'EUR_GBP', 'EUR_JPY',
        'GBP_JPY', 'CHF_JPY', 'GBP_CHF', 'EUR_AUD', 'EUR_CAD', 
        'AUD_CAD', 'AUD_JPY', 'CAD_JPY', 'NZD_JPY', 'GBP_CAD', 
        'GBP_NZD', 'GBP_AUD', 'AUD_NZD',  'AUD_CHF', 'EUR_NZD',
        'NZD_CHF', 'CAD_CHF', 'NZD_CAD',  'EUR_CHF']

def get_data(instr, gran = 'D', td=1000):
    start = f"{date.today() - timedelta(td) }"      
    end = f"{date.today() - timedelta(1)}"        
    granularity = gran          
    price = 'M'
    data = api.get_history(instr, start, end, granularity, price)
    data.drop(['complete'], axis=1, inplace=True)
    data.reset_index(inplace=True)
    data.rename(columns = {'time':'Date','o':'Open','c': 'Close', 'h':'High', 'l': 'Low'}, inplace = True)
    data.set_index('Date', inplace=True)
    return data


# ATR
def eATR(df1,n=14):
    """This calculates the exponential Average True Range of of a dataframe of the open,
    high, low, and close data of an instrument"""

    df = df1[['Open',	'High',	'Low',	'Close']].copy()
    # True Range
    df['TR'] = 0
    for i in range(len(df)):
        try:
            df.iloc[i, 4] = max(df.iat[i,1] - df.iat[i,2],
                         abs(df.iat[i,1] - df.iat[i-1,3]),
                         abs(df.iat[i,2] - df.iat[i-1,3]))
        except ValueError:
            pass

    # eATR
    df['eATR'] = df['TR'].ewm(span=n, adjust=False).mean()
           
    return df['eATR']

def ssl(df1):
    """This function adds the ssl indicator as features to a dataframe
    """
    df = df1.copy()
    df['smaHigh'] = df['High'].rolling(window=10).mean()
    df['smaLow'] = df['Low'].rolling(window=10).mean()
    df['hlv'] = 0
    df['hlv'] = np.where(df['Close'] > df['smaHigh'],1,np.where(df['Close'] < df['smaLow'],-1,df['hlv'].shift(1)))
    df['sslDown'] = np.where(df['hlv'] < 0, df['smaHigh'], df['smaLow'])
    df['sslUp'] = np.where(df['hlv'] < 0, df['smaLow'], df['smaHigh'])
    df['sslPosition'] = np.where(df['Close'] > df['sslUp'], 1,
                               np.where(df['Close'] < df['sslDown'], -1, 0))
    return df[['sslDown', 'sslUp', 'sslPosition']]

# Waddah Attar
def WAE(df1):
      """This function creates adds the indicator Waddah Attar features to a dataframe
        """
    df = df1.copy()

  # EMA
    long_ema = df.loc[:,'Close'].ewm(span=40, adjust=False).mean()
    short_ema = df.loc[:,'Close'].ewm(span=20, adjust=False).mean()

      # MACD
    MACD = short_ema - long_ema
  
  # bBands
    sma20 = df.loc[:,'Close'].rolling(window=20).mean()  # 20 SMA
    
    stddev = df.loc[:,'Close'].rolling(window=20).std() # 20 STDdev
    lower_band = sma20 - (2 * stddev)
    upper_band = sma20 + (2 * stddev)

    #<NAME>
    t1 = (MACD - MACD.shift(1))* 150
    #t2 = MACD.shift(2) - MACD.shift(3)
    df['e1'] = upper_band - lower_band
    df['e2'] = -1 *df['e1']
        #e2 = upper_band.shift(1) - lower_band.shift(1)

    df['trendUp'] = np.where(t1 > 0, t1, 0)
    df['trendDown'] =  np.where(t1 < 0, t1, 0)

    df['waePosition'] = np.where(df['trendUp'] > 0, 1,
                               np.where(df['trendDown'] < 0, -1, 0))
  
  
    return df[['e1','e2','trendUp', 'trendDown', 'waePosition']]

def lag_feat(data1):
      """This function adds lag returns as features to a dataframe
    """
    data = data1.copy()
    lags = 8
    cols = []
    for lag in range(1, lags + 1):
        col = f'lag_{lag}'
        data[col] = data['ret'].shift(lag)
        cols.append(col)
    return data[cols]

def datepart_feat(df0, colname = 'Date'):
    """This function adds some common pandas date parts like 'year',
        'month' etc as features to a dataframe
    """
    df = df0.copy()
    df.reset_index(inplace=True)
    df1 = df.loc[:,colname]
    nu_feats = ['Day', 'Dayofweek', 'Dayofyear']
    
    targ_pre = re.sub('[Dd]ate$', '', colname)
    for n in nu_feats:
        df[targ_pre+n] = getattr(df1.dt,n.lower())

    df[targ_pre+'week'] = df1.dt.isocalendar().week
    df['week'] = np.int64(df['week'])
    df[targ_pre+'Elapsed'] = df1.astype(np.int64) // 10**9
    nu_feats.extend(['week', 'Elapsed'])
    df.set_index(colname, inplace=True)
    return df[nu_feats]

def gen_feat(pair):
    df0 = get_data(pair)
    df0['ret'] = df0['Close'].pct_change()
    df0['dir'] = np.sign(df0['ret'])
    eATR_ = eATR(df0).shift(1)
    wae = WAE(df0).shift(1)
    ssl1 = ssl(df0).shift(1)
    datepart = datepart_feat(df0)
    lags = lag_feat(df0)
    return pd.concat([df0,  eATR_, wae, ssl1, datepart, lags], axis=1).dropna()

# random forest
def rfc(xs, y, n_estimators=40, max_samples=1000,
       max_features=0.5, min_samples_leaf=5, **kwargs):
    return RandomForestClassifier(n_jobs=-1, n_estimators=n_estimators,
        max_samples=max_samples, max_features=max_features,
        min_samples_leaf=min_samples_leaf, oob_score=True).fit(xs, y)

def rfc_results():
  """This function trains a Random Forest classifier and outputs the 
  out-of-sample performance from the validation and test sets
  """
  df1 = pd.DataFrame() 
  df2 = pd.DataFrame()
  for pair in pairs:
    # retrieving the data and preparing the features
    dataset = gen_feat(pair)
    dataset.drop(['Open', 'High', 'Low', 'Close'], axis=1, inplace=True)

    # selecting the features to train on
    cols = list(dataset.columns)
    feats = cols[2:]

    #splitting into training, validation and test sets
    df_train = dataset.iloc[:-100,:]
    train = df_train.copy()
    df_test = dataset.iloc[-100:,:]
    test = df_test.copy()
    train_f = train.iloc[:-100,:]
    valid = train.iloc[-100:,:]

    #training the algorithm
    m = rfc(train_f[feats], train_f['dir'])

    # Pickle
    (f'{pair}_rfc.pkl').save(m)

    #preparing results for both validation and test sets
    valid_pred = m.predict(valid[feats])
    # valid_acc = accuracy_score(valid['dir'], valid_pred)
    test_pred = m.predict(test[feats])
    # test_acc = accuracy_score(test['dir'], test_pred)

    # Results for validation set
    valid_results = perf_stats(valid_pred*valid['ret']).to_frame(name=pair)
    valid_results = valid_results.rename_axis('pairs')

    # Results for test set
    test_results = perf_stats(test_pred*test['ret']).to_frame(name=pair)
    test_results = test_results.rename_axis('pairs')

    # Merge
    df1 = pd.concat([df1, valid_results], axis=1)
    df2 = pd.concat([df2, test_results], axis=1)

  #output
  return df1.T, df2.T

# Light GBM
def lgb(xs, y, learning_rate=0.15, boosting_type='gbdt',
        objective='binary', n_estimators=50,
        metric=['auc', 'binary_logloss'],
        num_leaves=100, max_depth= 1,
        **kwargs):
    return LGBMClassifier().fit(xs, y)

def lgb_results():
  """This function trains a Light Gradient Boosting Method and outputs the 
  out-of-sample performance from the validation and test sets
  """
  df1 = pd.DataFrame() 
  df2 = pd.DataFrame()
  for pair in pairs:
    # retrieving the data and preparing the features
    dataset = gen_feat(pair)
    dataset.drop(['Open', 'High', 'Low', 'Close'], axis=1, inplace=True)

    # selecting the features to train on
    cols = list(dataset.columns)
    feats = cols[2:]

    #splitting into training, validation and test sets
    df_train = dataset.iloc[:-1000,:]
    train = df_train.copy()
    df_test = dataset.iloc[-1000:,:]
    test = df_test.copy()
    train_f = train.iloc[:-1000,:]
    valid = train.iloc[-1000:,:]

    #training the algorithm
    m = lgb(train_f[feats], train_f['dir']);

    # Pickle
    (f'{pair}_lgb.pkl').save(m)

    #preparing results for both validation and test sets
    valid_pred = m.predict(valid[feats])
    # valid_acc = accuracy_score(valid['dir'], valid_pred)
    test_pred = m.predict(test[feats])
    # test_acc = accuracy_score(test['dir'], test_pred)

    # Results for validation set
    valid_results = perf_stats(valid_pred*valid['ret']).to_frame(name=pair)
    valid_results = valid_results.rename_axis('pairs')

    # Results for test set
    test_results = perf_stats(test_pred*test['ret']).to_frame(name=pair)
    test_results = test_results.rename_axis('pairs')

    # Merge
    df1 = pd.concat([df1, valid_results], axis=1)
    df2 = pd.concat([df2, test_results], axis=1)

  #output
  return df1.T, df2.T

  # eXtreme Gradient Boosting
def xgb(xs, y):
  return XGBClassifier().fit(xs, y)

def xgb_results():
  """This function trains a eXtreme Gradient Boosting Method and outputs the 
  out-of-sample performance from the validation and test sets
  """
  df1 = pd.DataFrame() 
  df2 = pd.DataFrame()
  for pair in pairs:
    # retrieving the data and preparing the features
    dataset = gen_feat(pair)
    dataset.drop(['Open', 'High', 'Low', 'Close'], axis=1, inplace=True)

    # selecting the features to train on
    cols = list(dataset.columns)
    feats = cols[2:]

    #splitting into training, validation and test sets
    df_train = dataset.iloc[:-1000,:]
    train = df_train.copy()
    df_test = dataset.iloc[-1000:,:]
    test = df_test.copy()
    train_f = train.iloc[:-1000,:]
    valid = train.iloc[-1000:,:]

    #training the algorithm
    m = xgb(train_f[feats], train_f['dir']);

    # Pickle
    (f'{pair}_xgb.pkl').save(m)

    #preparing results for both validation and test sets
    valid_pred = m.predict(valid[feats])
    # valid_acc = accuracy_score(valid['dir'], valid_pred)
    test_pred = m.predict(test[feats])
    # test_acc = accuracy_score(test['dir'], test_pred)

    # Results for validation set
    valid_results = perf_stats(valid_pred*valid['ret']).to_frame(name=pair)
    valid_results = valid_results.rename_axis('pairs')

    # Results for test set
    test_results = perf_stats(test_pred*test['ret']).to_frame(name=pair)
    test_results = test_results.rename_axis('pairs')

    # Merge
    df1 = pd.concat([df1, valid_results], axis=1)
    df2 = pd.concat([df2, test_results], axis=1)

  #output
  return df1.T, df2.T


# -*- coding: utf-8 -*-
"""models

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1B2oxbnW3_438Fxp0mBpdVrGCk5UFK5pT
"""

import os
import re
import tpqoa

import random
import numpy as np
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
from sklearn.model_selection import RandomizedSearchCV, GridSearchCV
import pandas as pd

from datetime import date, timedelta
import warnings
warnings.filterwarnings('ignore')


from sklearn.ensemble import RandomForestClassifier
from lightgbm import LGBMClassifier
from xgboost import XGBClassifier


pairs = ['EUR_USD', 'USD_JPY', 'GBP_USD', 'USD_CHF', 
        'AUD_USD', 'USD_CAD', 'NZD_USD', 'EUR_GBP', 'EUR_JPY',
        'GBP_JPY', 'CHF_JPY', 'GBP_CHF', 'EUR_AUD', 'EUR_CAD', 
        'AUD_CAD', 'AUD_JPY', 'CAD_JPY', 'NZD_JPY', 'GBP_CAD', 
        'GBP_NZD', 'GBP_AUD', 'AUD_NZD',  'AUD_CHF', 'EUR_NZD',
        'NZD_CHF', 'CAD_CHF', 'NZD_CAD',  'EUR_CHF']

def get_data(instr, gran = 'D', td=1000):
    start = f"{date.today() - timedelta(td) }"      
    end = f"{date.today() - timedelta(1)}"        
    granularity = gran          
    price = 'M'
    data = api.get_history(instr, start, end, granularity, price)
    data.drop(['complete'], axis=1, inplace=True)
    data.reset_index(inplace=True)
    data.rename(columns = {'time':'Date','o':'Open','c': 'Close', 'h':'High', 'l': 'Low'}, inplace = True)
    data.set_index('Date', inplace=True)
    return data


# ATR
def eATR(df1,n=14):
    """This calculates the exponential Average True Range of of a dataframe of the open,
    high, low, and close data of an instrument"""

    df = df1[['Open',	'High',	'Low',	'Close']].copy()
    # True Range
    df['TR'] = 0
    for i in range(len(df)):
        try:
            df.iloc[i, 4] = max(df.iat[i,1] - df.iat[i,2],
                         abs(df.iat[i,1] - df.iat[i-1,3]),
                         abs(df.iat[i,2] - df.iat[i-1,3]))
        except ValueError:
            pass

    # eATR
    df['eATR'] = df['TR'].ewm(span=n, adjust=False).mean()
           
    return df['eATR']

def ssl(df1):
    """This function adds the ssl indicator as features to a dataframe
    """
    df = df1.copy()
    df['smaHigh'] = df['High'].rolling(window=10).mean()
    df['smaLow'] = df['Low'].rolling(window=10).mean()
    df['hlv'] = 0
    df['hlv'] = np.where(df['Close'] > df['smaHigh'],1,np.where(df['Close'] < df['smaLow'],-1,df['hlv'].shift(1)))
    df['sslDown'] = np.where(df['hlv'] < 0, df['smaHigh'], df['smaLow'])
    df['sslUp'] = np.where(df['hlv'] < 0, df['smaLow'], df['smaHigh'])
    df['sslPosition'] = np.where(df['Close'] > df['sslUp'], 1,
                               np.where(df['Close'] < df['sslDown'], -1, 0))
    return df[['sslDown', 'sslUp', 'sslPosition']]

# Waddah Attar
def WAE(df1):
      """This function creates adds the indicator Waddah Attar features to a dataframe
        """
    df = df1.copy()

  # EMA
    long_ema = df.loc[:,'Close'].ewm(span=40, adjust=False).mean()
    short_ema = df.loc[:,'Close'].ewm(span=20, adjust=False).mean()

      # MACD
    MACD = short_ema - long_ema
  
  # bBands
    sma20 = df.loc[:,'Close'].rolling(window=20).mean()  # 20 SMA
    
    stddev = df.loc[:,'Close'].rolling(window=20).std() # 20 STDdev
    lower_band = sma20 - (2 * stddev)
    upper_band = sma20 + (2 * stddev)

    #<NAME>
    t1 = (MACD - MACD.shift(1))* 150
    #t2 = MACD.shift(2) - MACD.shift(3)
    df['e1'] = upper_band - lower_band
    df['e2'] = -1 *df['e1']
        #e2 = upper_band.shift(1) - lower_band.shift(1)

    df['trendUp'] = np.where(t1 > 0, t1, 0)
    df['trendDown'] =  np.where(t1 < 0, t1, 0)

    df['waePosition'] = np.where(df['trendUp'] > 0, 1,
                               np.where(df['trendDown'] < 0, -1, 0))
  
  
    return df[['e1','e2','trendUp', 'trendDown', 'waePosition']]

def lag_feat(data1):
      """This function adds lag returns as features to a dataframe
    """
    data = data1.copy()
    lags = 8
    cols = []
    for lag in range(1, lags + 1):
        col = f'lag_{lag}'
        data[col] = data['ret'].shift(lag)
        cols.append(col)
    return data[cols]

def datepart_feat(df0, colname = 'Date'):
    """This function adds some common pandas date parts like 'year',
        'month' etc as features to a dataframe
    """
    df = df0.copy()
    df.reset_index(inplace=True)
    df1 = df.loc[:,colname]
    nu_feats = ['Day', 'Dayofweek', 'Dayofyear']
    
    targ_pre = re.sub('[Dd]ate$', '', colname)
    for n in nu_feats:
        df[targ_pre+n] = getattr(df1.dt,n.lower())

    df[targ_pre+'week'] = df1.dt.isocalendar().week
    df['week'] = np.int64(df['week'])
    df[targ_pre+'Elapsed'] = df1.astype(np.int64) // 10**9
    nu_feats.extend(['week', 'Elapsed'])
    df.set_index(colname, inplace=True)
    return df[nu_feats]

def gen_feat(pair):
    df0 = get_data(pair)
    df0['ret'] = df0['Close'].pct_change()
    df0['dir'] = np.sign(df0['ret'])
    eATR_ = eATR(df0).shift(1)
    wae = WAE(df0).shift(1)
    ssl1 = ssl(df0).shift(1)
    datepart = datepart_feat(df0)
    lags = lag_feat(df0)
    return pd.concat([df0,  eATR_, wae, ssl1, datepart, lags], axis=1).dropna()

# random forest
def rfc(xs, y, n_estimators=40, max_samples=1000,
       max_features=0.5, min_samples_leaf=5, **kwargs):
    return RandomForestClassifier(n_jobs=-1, n_estimators=n_estimators,
        max_samples=max_samples, max_features=max_features,
        min_samples_leaf=min_samples_leaf, oob_score=True).fit(xs, y)

def rfc_results():
  """This function trains a Random Forest classifier and outputs the 
  out-of-sample performance from the validation and test sets
  """
  df1 = pd.DataFrame() 
  df2 = pd.DataFrame()
  for pair in pairs:
    # retrieving the data and preparing the features
    dataset = gen_feat(pair)
    dataset.drop(['Open', 'High', 'Low', 'Close'], axis=1, inplace=True)

    # selecting the features to train on
    cols = list(dataset.columns)
    feats = cols[2:]

    #splitting into training, validation and test sets
    df_train = dataset.iloc[:-100,:]
    train = df_train.copy()
    df_test = dataset.iloc[-100:,:]
    test = df_test.copy()
    train_f = train.iloc[:-100,:]
    valid = train.iloc[-100:,:]

    #training the algorithm
    m = rfc(train_f[feats], train_f['dir'])

    # Pickle
    (f'{pair}_rfc.pkl').save(m)

    #preparing results for both validation and test sets
    valid_pred = m.predict(valid[feats])
    # valid_acc = accuracy_score(valid['dir'], valid_pred)
    test_pred = m.predict(test[feats])
    # test_acc = accuracy_score(test['dir'], test_pred)

    # Results for validation set
    valid_results = perf_stats(valid_pred*valid['ret']).to_frame(name=pair)
    valid_results = valid_results.rename_axis('pairs')

    # Results for test set
    test_results = perf_stats(test_pred*test['ret']).to_frame(name=pair)
    test_results = test_results.rename_axis('pairs')

    # Merge
    df1 = pd.concat([df1, valid_results], axis=1)
    df2 = pd.concat([df2, test_results], axis=1)

  #output
  return df1.T, df2.T

# Light GBM
def lgb(xs, y, learning_rate=0.15, boosting_type='gbdt',
        objective='binary', n_estimators=50,
        metric=['auc', 'binary_logloss'],
        num_leaves=100, max_depth= 1,
        **kwargs):
    return LGBMClassifier().fit(xs, y)

def lgb_results():
  """This function trains a Light Gradient Boosting Method and outputs the 
  out-of-sample performance from the validation and test sets
  """
  df1 = pd.DataFrame() 
  df2 = pd.DataFrame()
  for pair in pairs:
    # retrieving the data and preparing the features
    dataset = gen_feat(pair)
    dataset.drop(['Open', 'High', 'Low', 'Close'], axis=1, inplace=True)

    # selecting the features to train on
    cols = list(dataset.columns)
    feats = cols[2:]

    #splitting into training, validation and test sets
    df_train = dataset.iloc[:-1000,:]
    train = df_train.copy()
    df_test = dataset.iloc[-1000:,:]
    test = df_test.copy()
    train_f = train.iloc[:-1000,:]
    valid = train.iloc[-1000:,:]

    #training the algorithm
    m = lgb(train_f[feats], train_f['dir']);

    # Pickle
    (f'{pair}_lgb.pkl').save(m)

    #preparing results for both validation and test sets
    valid_pred = m.predict(valid[feats])
    # valid_acc = accuracy_score(valid['dir'], valid_pred)
    test_pred = m.predict(test[feats])
    # test_acc = accuracy_score(test['dir'], test_pred)

    # Results for validation set
    valid_results = perf_stats(valid_pred*valid['ret']).to_frame(name=pair)
    valid_results = valid_results.rename_axis('pairs')

    # Results for test set
    test_results = perf_stats(test_pred*test['ret']).to_frame(name=pair)
    test_results = test_results.rename_axis('pairs')

    # Merge
    df1 = pd.concat([df1, valid_results], axis=1)
    df2 = pd.concat([df2, test_results], axis=1)

  #output
  return df1.T, df2.T

  # eXtreme Gradient Boosting
def xgb(xs, y):
  return XGBClassifier().fit(xs, y)

def xgb_results():
  """This function trains a eXtreme Gradient Boosting Method and outputs the 
  out-of-sample performance from the validation and test sets
  """
  df1 = pd.DataFrame() 
  df2 = pd.DataFrame()
  for pair in pairs:
    # retrieving the data and preparing the features
    dataset = gen_feat(pair)
    dataset.drop(['Open', 'High', 'Low', 'Close'], axis=1, inplace=True)

    # selecting the features to train on
    cols = list(dataset.columns)
    feats = cols[2:]

    #splitting into training, validation and test sets
    df_train = dataset.iloc[:-1000,:]
    train = df_train.copy()
    df_test = dataset.iloc[-1000:,:]
    test = df_test.copy()
    train_f = train.iloc[:-1000,:]
    valid = train.iloc[-1000:,:]

    #training the algorithm
    m = xgb(train_f[feats], train_f['dir']);

    # Pickle
    (f'{pair}_xgb.pkl').save(m)

    #preparing results for both validation and test sets
    valid_pred = m.predict(valid[feats])
    # valid_acc = accuracy_score(valid['dir'], valid_pred)
    test_pred = m.predict(test[feats])
    # test_acc = accuracy_score(test['dir'], test_pred)

    # Results for validation set
    valid_results = perf_stats(valid_pred*valid['ret']).to_frame(name=pair)
    valid_results = valid_results.rename_axis('pairs')

    # Results for test set
    test_results = perf_stats(test_pred*test['ret']).to_frame(name=pair)
    test_results = test_results.rename_axis('pairs')

    # Merge
    df1 = pd.concat([df1, valid_results], axis=1)
    df2 = pd.concat([df2, test_results], axis=1)

  #output
  return df1.T, df2.T
# -*- coding: utf-8 -*- models  Automatically generated by Colaboratory.  Original file is located at     https://colab.research.google.com/drive/1B2oxbnW3_438Fxp0mBpdVrGCk5UFK5pT # ATR This calculates the exponential Average True Range of of a dataframe of the open,     high, low, and close data of an instrument # True Range # eATR This function adds the ssl indicator as features to a dataframe # Waddah Attar This function creates adds the indicator Waddah Attar features to a dataframe # EMA # MACD # bBands # 20 SMA # 20 STDdev #<NAME> #t2 = MACD.shift(2) - MACD.shift(3) #e2 = upper_band.shift(1) - lower_band.shift(1) This function adds lag returns as features to a dataframe This function adds some common pandas date parts like 'year',         'month' etc as features to a dataframe # random forest This function trains a Random Forest classifier and outputs the    out-of-sample performance from the validation and test sets # retrieving the data and preparing the features # selecting the features to train on #splitting into training, validation and test sets #training the algorithm # Pickle #preparing results for both validation and test sets # valid_acc = accuracy_score(valid['dir'], valid_pred) # test_acc = accuracy_score(test['dir'], test_pred) # Results for validation set # Results for test set # Merge #output # Light GBM This function trains a Light Gradient Boosting Method and outputs the    out-of-sample performance from the validation and test sets # retrieving the data and preparing the features # selecting the features to train on #splitting into training, validation and test sets #training the algorithm # Pickle #preparing results for both validation and test sets # valid_acc = accuracy_score(valid['dir'], valid_pred) # test_acc = accuracy_score(test['dir'], test_pred) # Results for validation set # Results for test set # Merge #output # eXtreme Gradient Boosting This function trains a eXtreme Gradient Boosting Method and outputs the    out-of-sample performance from the validation and test sets # retrieving the data and preparing the features # selecting the features to train on #splitting into training, validation and test sets #training the algorithm # Pickle #preparing results for both validation and test sets # valid_acc = accuracy_score(valid['dir'], valid_pred) # test_acc = accuracy_score(test['dir'], test_pred) # Results for validation set # Results for test set # Merge #output