# Basic imports
import os
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import datetime as dt
import scipy.optimize as spo
import sys
from time import time
from sklearn.metrics import r2_score, median_absolute_error

%matplotlib inline

%pylab inline
pylab.rcParams['figure.figsize'] = (20.0, 10.0)

%load_ext autoreload
%autoreload 2

sys.path.append('../../')

"""
Explanation: This notebook is to aid in the development of a complete market simulator.
End of explanation
"""


levels = [-13.5, -10.0, -1.0, 2.0, 3.0]

real_value = -6.7
temp_list = levels + [real_value]
temp_list

temp_list.sort()
temp_list

sorted_index = temp_list.index(real_value)
if sorted_index == 0:
    q_value = levels[0]
elif sorted_index == len(temp_list)-1:
    q_value = levels[-1]
else:
    q_value = (temp_list[sorted_index-1] + temp_list[sorted_index+1])/2
q_value

def quantize(real_value, levels):
    temp_list = levels + [real_value]
    temp_list.sort()
    sorted_index = temp_list.index(real_value)
    if sorted_index == 0:
        q_value = levels[0]
    elif sorted_index == len(temp_list)-1:
        q_value = levels[-1]
    else:
        q_value = (temp_list[sorted_index-1] + temp_list[sorted_index+1])/2
    return q_value

levels

x = arange(-20,20,0.2)
x_df = pd.DataFrame(x, columns=['real_value'])
x_df

len(x_df.values.tolist())

from functools import partial

# x_df.apply(lambda x:print('{} \n {}'.format(x,'-'*20)), axis=1)
x_df['q_value'] = x_df.apply(lambda x: partial(quantize, levels=levels)(x[0]), axis=1)
x_df.head()

plt.plot(x_df['real_value'], x_df['q_value'])

"""
Explanation: Let's first create a quantization function
End of explanation
"""


data_df = pd.read_pickle('../../data/data_df.pkl')

first_date = data_df.index.get_level_values(0)[0]
first_date

one_input_df = data_df.loc[first_date,:]
one_input_df

"""
Explanation: Let's create an Indicator and extract some values
End of explanation
"""


num_days = 50
end_date = data_df.index.get_level_values(0).unique()[num_days-1]

sym_data = data_df['MSFT'].unstack()
sym_data.head()

batch_data = sym_data[first_date:end_date]
batch_data.shape

from recommender.indicator import Indicator

arange(0,1e4,1)

batch_data.head()

ind1 = Indicator(lambda x: x['Close'].mean(), arange(0,10000,100).tolist(), batch_data)

ind1.extracted_data

ind1.extract(batch_data)

ind1.q_levels

"""
Explanation: Normally, the data to pass to the extractor will be all the data, for one symbol, during a period of some days.
End of explanation
"""


ind2 = Indicator(lambda x: (x['Volume']/x['Close']).max(), arange(0,1e8,1e6).tolist(), batch_data)

ind2.extract(batch_data)

(batch_data['Volume']/batch_data['Close']).max()

ind3 = Indicator(lambda x: x['High'].min(), arange(0,100,1).tolist(), batch_data)

ind3.extract(batch_data)

"""
Explanation: Another Indicator
End of explanation
"""


indicators = [ind1, ind2, ind3]

vect_state = tuple(map(lambda x: x.extract(batch_data), indicators))
vect_state

"""
Explanation: Let's create a function to enumerate states from a vectorial state.
End of explanation
"""


len(ind1.q_levels)

q_values = [ind1.q_levels[0]] + ((np.array(ind1.q_levels[1:]) + np.array(ind1.q_levels[:-1])) / 2).tolist() + [ind1.q_levels[-1]]
q_values[:10]

len(q_values)

"""
Explanation: Let's generate the q_values for the q_levels
End of explanation
"""


len(ind1.q_levels)

import itertools as it

states_list = list(it.product(np.arange(len(ind1.q_levels)), np.arange(len(ind2.q_levels)), np.arange(len(ind3.q_levels))))

len(states_list)

states_list

states_list.index((5,1,13))

indicators = {'ind1': ind1,
              'ind2': ind2,
              'ind3': ind3}

states = list(it.product(*map(lambda x: arange(len(x.q_levels)), indicators.values())))

states

len(states)

states.index((5,1,13))

"""
Explanation: To make it easier to work with states, the "quantize" function was changed. Now it returns the number of interval in which the real value lies, instead of the "approximated, quantized value".
End of explanation
"""


states_list = states.copy()

state_vectors = dict(enumerate(states_list))
state_vectors

states =  dict(zip(state_vectors.values(), state_vectors.keys()))
states

import random

index = random.randint(0, len(states))
index

states[state_vectors[index]] == index

state_vectors[states[state_vectors[index]]] == state_vectors[index]

rand_vec = tuple(np.random.randint(0,100,3))
rand_vec

state_vectors[states[rand_vec]] == rand_vec

"""
Explanation: Let's now test the bidirectional dictionary to store the states and state vectors.
End of explanation
"""


from recommender.environment import Environment
from recommender.order import Order

env = Environment(data_df, indicators)

env.state_vectors

len(env.indicators['ind2'].q_levels)

env.states[(1,100,1)]

old_pos_df = env.portfolio.get_positions()
reward, new_state = env.get_consequences([Order(['AAPL',Order.BUY, 100]),
                                          Order(['AAPL',Order.SELL, 45]),
                                          Order(['AAPL', Order.BUY, 10])])
new_pos_df = env.portfolio.get_positions()

old_pos_df

new_pos_df

import recommender.portfolio as port

def reward_value_change(old_pos_df, new_pos_df):
    return new_pos_df[port.VALUE].sum() - old_pos_df[port.VALUE].sum()

def reward_cash_change(old_pos_df, new_pos_df):
    return new_pos_df.loc[port.CASH, port.VALUE] - old_pos_df.loc[port.CASH, port.VALUE]

reward_value_change(old_pos_df, new_pos_df)

reward_cash_change(old_pos_df, new_pos_df)

"""
Explanation: Let's create a reward function
End of explanation
"""


data_df.shape

from recommender.agent import Agent

NUM_ACTIONS = 3  # BUY, SELL, NOTHING
SYMBOL = 'AAPL'

indicators = {'ind1':}

env = Environment(data_df, indicators=indicators,  symbol=SYMBOL)

agent = Agent(num_states,
              num_actions,
              alpha=0.2,
              gamma=0.9,
              random_actions_rate=0.9,
              random_actions_decrease=0.999,
              dyna_iterations=0,
              verbose=False)

(self,
                 data_df,
                 indicators=None,
                 initial_cap=1000,
                 leverage_limit=3.0,
                 reward_fun=None,
                 symbol='AAPL')


"""
Explanation: Let's run a complete Environment-Agent pair.
End of explanation
"""


