-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathModel.py
70 lines (51 loc) · 2.62 KB
/
Model.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import numpy as np
# setting the seed allows for reproducible results
np.random.seed(123)
import tensorflow as tf
from tensorflow.keras.layers import LSTM, Flatten, Dense
from tensorflow.keras.models import Sequential
import tensorflow.keras.backend as K
import pandas as pd
class Model:
def __init__(self):
self.data = None
self.model = None
def __build_model(self, input_shape, outputs):
'''
Builds and returns the Deep Neural Network that will compute the allocation ratios
that optimize the Sharpe Ratio of the portfolio
inputs: input_shape - tuple of the input shape, outputs - the number of assets
returns: a Deep Neural Network model
'''
model = Sequential([
LSTM(64, input_shape=input_shape),
Flatten(),
Dense(outputs, activation='softmax')
])
def sharpe_loss(_, y_pred):
# make all time-series start at 1
data = tf.divide(self.data, self.data[0])
# value of the portfolio after allocations applied
portfolio_values = tf.reduce_sum(tf.multiply(data, y_pred), axis=1)
portfolio_returns = (portfolio_values[1:] - portfolio_values[:-1]) / portfolio_values[:-1] # % change formula
sharpe = K.mean(portfolio_returns) / K.std(portfolio_returns)
# since we want to maximize Sharpe, while gradient descent minimizes the loss,
# we can negate Sharpe (the min of a negated function is its max)
return -sharpe
model.compile(loss=sharpe_loss, optimizer='adam')
return model
def get_allocations(self, data: pd.DataFrame):
'''
Computes and returns the allocation ratios that optimize the Sharpe over the given data
input: data - DataFrame of historical closing prices of various assets
return: the allocations ratios for each of the given assets
'''
# data with returns
data_w_ret = np.concatenate([ data.values[1:], data.pct_change().values[1:] ], axis=1)
data = data.iloc[1:]
self.data = tf.cast(tf.constant(data), float)
if self.model is None:
self.model = self.__build_model(data_w_ret.shape, len(data.columns))
fit_predict_data = data_w_ret[np.newaxis,:]
self.model.fit(fit_predict_data, np.zeros((1, len(data.columns))), epochs=20, shuffle=False)
return self.model.predict(fit_predict_data)[0]