# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

import pandas as pd
import numpy as np
import seaborn as sns
import tensorflow as tf
from keras.models import Sequential
from keras.layers import Dense, SimpleRNN
import warnings
warnings.filterwarnings("ignore")

returns = pd.read_pickle("../Data/returns.pkl")
returns

returns = returns.iloc[1:]

# +
# dropping columns with all NaN
drop_columns = []

for col in returns.columns:
    if returns[col].isnull().all() == True:
        drop_columns.append(col)
        
returns.drop(columns=drop_columns, inplace=True)


# -

def get_investable(t, n_rows):
    "Find stocks in investable universe at time t\
    (stocks in the S&P500 that have prices recorded for the last n_rows days)"
    
    df_investable = returns.copy(deep = True).sort_index(ascending = False)
    
    most_recent = t
    least_recent = t + pd.DateOffset(-n_rows)
    
    #take n_rows worth of data upto time specified
    df_investable = df_investable[most_recent+pd.DateOffset(-1):least_recent]
    
    #find all stocks that exist in the S&P at this time period
    investable_universe = []
    for col in df_investable.columns:
        if ~df_investable[col].iloc[:n_rows].isna().any():
            investable_universe.append(col)
        
    df_investable = df_investable[investable_universe]
    
    return df_investable


# +
t = pd.to_datetime("2020-02-25")

get_investable(t, 80)


# +
# Converts investable dataframe into 3D tensor for input into RNN fitting
# Eg. if investable_df has shape (52, 635), it is converted to a numpy array of shape (52, 1, 635)

def formatX(investable_df):
    train_values = []
    # Iterates through each day in investable df and appends feature values to train_values
    for i in range(len(investable_df.index)):
        train_values.append(investable_df.iloc[i].values)
    train_values = np.array(train_values) # converts to numpy array
    train_values = np.reshape(train_values, (train_values.shape[0], 1, train_values.shape[1])) # reshapes to 3-dimensional
    return train_values


# -

formatX(get_investable(t, 80))


# +
# Formats output for a stock as a np array
# Takes in a stock, a date, and a number of rows to look back on and formats the output values as a numpy array
# Eg. formatY(stockX, Feb.25, 80) outputs the returns for stockX starting from 80 days before Feb.25 up to Feb.25

def formatY(stock, t, n_rows):
    most_recent = t
    least_recent = t + pd.DateOffset(-n_rows)
    return returns[stock].loc[least_recent:most_recent].shift(-1).iloc[:-1].values[::-1]


# +
# Creating a Simple RNN for an inputted stock

def get_model(stock, t, n_rows, investable_df):
    # Converts features and output values to a 3D tensor and numpy array respectively
    convertedX = convertToMatrix(investable_df)
    convertedY = formatY(stock, t, n_rows)
    
    # Builds model with three layers (SimpleRNN, Dense, Dense)
    model = Sequential()
    model.add(SimpleRNN(units=32, input_shape=(convertedX.shape[1], convertedX.shape[2]), activation="relu"))
    model.add(Dense(8, activation="relu")) 
    model.add(Dense(1))
    model.compile(loss='mean_squared_error', optimizer='rmsprop')
    
    # Fits model on formatted inputs and outputs
    model.fit(convertedX, convertedY, epochs=100, batch_size=16, verbose=0)
    
    return model


# +
# Predicts stock price at a given date
import datetime

# Number of days to look back at when training model
look_back = 80

def pred_date(stock, date):
    investable = get_investable(date, look_back)
    
    # Checking for invalid inputs
    if stock not in investable.columns:
        print(stock + " is not in the investable universe")
        return
    if investable.shape[0] == 0:
        print("Investable universe is empty")
        return
    
    ''' investable.iloc[0:1] is the first row of the investable df 
    (i.e. the row we're using to predict the stock's return at time date) '''
    convertedX = formatX(investable.iloc[0:1])
    
    ''' We train the model on the day before date (the parameter) so that the date we're predicting
    for is not included in the training set'''
    model = get_model(stock, date - datetime.timedelta(days=1), look_back, investable)
    
    return model.predict(convertedX)[0, 0]


# -

pred_date("916328", t)


# Predicts returns for all stocks in the investable universe at a given date
def pred_date_all(date):
    investable = get_investable(date, look_back)
    df = pd.DataFrame()
    stocks = []
    predictions = []
    count = 0 # used to keep track of progress when running function
    for stock in investable.columns:
        stocks.append(stock)
        predictions.append(pred_date(stock, date))
        count += 1
        percent = count / len(investable.columns)
        print(str(percent * 100) + "% ")
    df["stock"] = stocks
    df["pred_" + date.strftime("%Y-%m-%d")] = predictions
    return df


pred_t = pred_date_all(t)

pred_t


# Takes in output of pred_date_all and returns a portfolio and expected returns
def get_portfolio(predictions_df, num_stocks):
    pred_col = predictions_df.columns[-1]
    sorted_df = predictions_df.sort_values(by=pred_col, ascending=False)
    top = sorted_df.head(num_stocks)
    bot = sorted_df.tail(num_stocks)
    returns = top[pred_col].mean() - bot[pred_col].mean()
    return top, bot, returns


get_portfolio(pred_t, 5)



StarcoderdataJupyterScriptsDedupFiltered