# 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('../../')

from utils import analysis

"""
Explanation: This is a notebook to aid in the development of the market simulator. One initial version was created as part of the Machine Learning for Trading course. It has to be adapted for use in the Capstone project.
End of explanation
"""


from utils import marketsim as msim

orders_path = '../../data/orders/orders-my-leverage.csv'
orders_df = pd.read_csv(orders_path, index_col='Date', parse_dates=True, na_values=['nan'])

orders_df

data_df = pd.read_pickle('../../data/data_df.pkl')
port_vals_df, values = msim.simulate_orders(orders_df, data_df)

port_vals_df.plot()

values

analysis.value_eval(port_vals_df, graph=True, verbose=True, data_df=data_df)

"""
Explanation: To use the market simulator with the q-learning agent it must be possible to call it with custom data, stored in RAM. Let's try that.
End of explanation
"""


'AAPL' in data_df.columns.tolist()

data_df.index.get_level_values(0)[0]

symbols = data_df.columns.get_level_values(0).tolist()

symbols.append('CASH')
positions_df = pd.DataFrame(index=symbols, columns=['shares', 'value'])
positions_df

close_df = data_df.xs('Close', level='feature')
close_df.head()

current_date = close_df.index[-1]
current_date

positions_df['shares'] = np.zeros(positions_df.shape[0])
positions_df.loc['CASH','shares'] = 1000
positions_df

SHARES = 'shares'
VALUE = 'value'
CASH = 'CASH'

prices = close_df.loc[current_date]
prices[CASH] = 1.0
positions_df[VALUE] = positions_df[SHARES] * prices
positions_df

ORDER_SYMBOL = 'symbol'
ORDER_ORDER = 'order'
ORDER_SHARES = 'shares'
BUY = 'BUY'
SELL = 'SELL'
NOTHING = 'NOTHING'
order = pd.Series(['AAPL', BUY, 200], index=[ORDER_SYMBOL, ORDER_ORDER, ORDER_SHARES])
order

if order[ORDER_ORDER] == 'BUY':
    positions_df.loc[order[ORDER_SYMBOL], SHARES] += order[ORDER_SHARES]
    positions_df.loc[CASH, SHARES] -= order[ORDER_SHARES] * close_df.loc[current_date, order[ORDER_SYMBOL]]
if order[ORDER_ORDER] == 'SELL':
    positions_df.loc[order[ORDER_SYMBOL], SHARES] -= order[ORDER_SHARES]
    positions_df.loc[CASH, SHARES] += order[ORDER_SHARES] * close_df.loc[current_date, order[ORDER_SYMBOL]]

positions_df[VALUE] = positions_df[SHARES] * prices

positions_df.loc['AAPL']

positions_df.loc[CASH]

close_df.loc[current_date, 'AAPL']

116*200

positions_df[VALUE].iloc[:-1]

values = positions_df[VALUE]
leverage = np.sum(np.abs(values.iloc[:-1])) / (np.sum(values))

leverage

"""
Explanation: That function has many of the desired characteristics, but doesn't follow the dynamics necessary for the interaction with the agent. The solution will be to implement a new class, called Portfolio, that will accept orders, keep track of the positions and return their values when asked for.
End of explanation
"""


from recommender.portfolio import Portfolio

p = Portfolio(data_df)

from recommender.order import Order

o1 = Order(['AAPL', BUY, 150])
print(o1)

p.positions_df

p.positions_df.loc['AAPL']

p.execute_order(o1)

p.positions_df.loc[['AAPL','CASH']]

p.add_market_days(1)

p.current_date

p.positions_df.loc[['AAPL', CASH]]

p.add_market_days(1)

p.current_date

p.positions_df.loc[['AAPL', CASH]]

p.positions_df[VALUE].sum()

p.execute_order(Order(['AAPL',SELL,100]))
p.positions_df[p.positions_df[SHARES] != 0]

"""
Explanation: Let's test the Portfolio class
End of explanation
"""


p.execute_order(Order(['MSFT',BUY,120]))
p.get_positions()

p.leverage_limit = 2

"""
Explanation: Let's add a leverage limit of 2
End of explanation
"""


p.execute_order(Order(['AAPL',BUY, 10]))
p.get_positions()

"""
Explanation: Let's buy a less than the limit
End of explanation
"""


p.execute_order(Order(['AAPL',BUY, 5000]))
p.get_positions()

"""
Explanation: Now, let's buy more than the limit
End of explanation
"""


p.execute_order(Order(['AAPL',SELL, 300]))
p.get_positions()

"""
Explanation: The last order wasn't executed because the leverage limit was reached. That's good.
Let's now go short on AAPL, but less than the limit
End of explanation
"""


p.execute_order(Order(['AAPL',SELL, 3000]))
p.get_positions()

"""
Explanation: Now, the same, but this time let's pass the limit.
End of explanation
"""


pos = p.get_positions()
pos[VALUE].sum()

p.add_market_days(1000)
p.get_positions()

p.add_market_days(6000)
p.get_positions()

p.get_positions()[VALUE].sum()

p.add_market_days(-7000)  # Back in time...
p.get_positions()

p.current_date

"""
Explanation: Nothing happened because the leverage limit was reached. That's ok.
End of explanation
"""


p.close_df.loc[p.current_date, 'GOOG']

p.execute_order(Order(['GOOG', BUY, 100]))
p.get_positions()

"""
Explanation: Let's try to buy GOOG before it entered the market...
End of explanation
"""


# I need to add some cash, because I lost a lot of money shorting AAPL in the last 20 years, and I need to meet the leverage limits.
p.positions_df.loc[CASH, SHARES] = 100000
p.update_values()

p.add_market_days(7200)
p.execute_order(Order(['GOOG', BUY, 100]))
p.get_positions()

"""
Explanation: Ok, nothing happened. That's correct.
Now, let's add some years and try to buy GOOG again...
End of explanation
"""


p.leverage_limit

p.my_leverage_reached()

p.get_leverage()

"""
Explanation: Good. This time GOOG was bought!
What about the leverage?
End of explanation
"""


