0
$\begingroup$

I am building a model for predicting stock portfolio positions, by minimizing a Sharpe loss function (corresponding to maximize the Sharpe ratio of the portfolio).

The architecture is puting standardized return data into an inception module to extract features, then predicting the positions using an LSTM.

However, the validation loss does not go down when I train the networks on different learning rates. And all of them saturated at a similar number.
Are there any possible direction I can explore? Any problems in the networks?

val loss under different LR

The model code and summary is as below:

class SharpeLoss(tf.keras.losses.Loss):
    def __init__(self, output_size: int = 1):
        self.output_size = output_size  # in case we have multiple targets => output dim[-1] = output_size * n_quantiles
        super().__init__()

    def call(self, y_true, weights):
        captured_returns = weights * y_true
        mean_returns = tf.reduce_mean(captured_returns)
        # print(f'sharpe: {-sharpe_loss*np.std(train_df[column])- np.mean(train["column"]))/))
        return -(
            mean_returns
            / tf.sqrt(
                tf.reduce_mean(tf.square(captured_returns))
                - tf.square(mean_returns)
                + 1e-9
            )
            * tf.sqrt(252.0)
        )

def create_LSTM(T, NF, hidden_layer_size, dropout_rate, max_gradient_norm, learning_rate, output_size):
    input = Input(shape=(T, NF, 1)) # row, column, depth (for going through the convolution layer)

    # build the inception module
    convsecond_1 = Conv2D(64, (1, 1), padding='same')(input)
    convsecond_1 = keras.layers.LeakyReLU(alpha=0.01)(convsecond_1)
    convsecond_1 = Conv2D(64, (3, 1), padding='same')(convsecond_1)
    convsecond_1 = keras.layers.LeakyReLU(alpha=0.01)(convsecond_1)

    convsecond_2 = Conv2D(64, (1, 1), padding='same')(input)
    convsecond_2 = keras.layers.LeakyReLU(alpha=0.01)(convsecond_2)
    convsecond_2 = Conv2D(64, (5, 1), padding='same')(convsecond_2)
    convsecond_2 = keras.layers.LeakyReLU(alpha=0.01)(convsecond_2)

    convsecond_3 = MaxPooling2D((3, 1), strides=(1, 1), padding='same')(input)
    convsecond_3 = Conv2D(64, (1, 1), padding='same')(convsecond_3)
    convsecond_3 = keras.layers.LeakyReLU(alpha=0.01)(convsecond_3)

    convsecond_output = keras.layers.concatenate([convsecond_1, convsecond_2, convsecond_3], axis=3)
    convsecond_output = Conv2D(4, (1, 1), padding='same')(convsecond_output)
    convsecond_output = Conv2D(1, (1, 1), padding='same')(convsecond_output)
    conv_reshape = Reshape((int(convsecond_output.shape[1]), int(convsecond_output.shape[2])))(convsecond_output)

    # build the last LSTM layer
    lstm = tf.keras.layers.LSTM(hidden_layer_size, return_sequences=True,dropout=dropout_rate,stateful=False,activation="tanh",recurrent_activation="sigmoid",recurrent_dropout=0,unroll=False,use_bias=True)(conv_reshape)
    dropout = keras.layers.Dropout(dropout_rate)(lstm)

    # build the output layer

    output = tf.keras.layers.TimeDistributed(tf.keras.layers.Dense(output_size,activation=tf.nn.tanh))(dropout[..., :, :])

    model = keras.Model(inputs=input, outputs=output)

    adam = keras.optimizers.Adam(lr=learning_rate,clipnorm=max_gradient_norm)

    sharpe_loss = SharpeLoss(output_size).call

    model.compile(loss=sharpe_loss,optimizer=adam,sample_weight_mode="temporal")
    return model

LSTM = create_LSTM(T=252, NF=1, hidden_layer_size=160, dropout_rate=0.4, max_gradient_norm=100, learning_rate=0.1, output_size=1)

LSTM.summary()
 Layer (type)                Output Shape                 Param #   Connected to                  
==================================================================================================
 input_4 (InputLayer)        [(None, 252, 1, 1)]          0         []                            
                                                                                                  
 conv2d_15 (Conv2D)          (None, 252, 1, 64)           128       ['input_4[0][0]']             
                                                                                                  
 conv2d_17 (Conv2D)          (None, 252, 1, 64)           128       ['input_4[0][0]']             
                                                                                                  
 leaky_re_lu_15 (LeakyReLU)  (None, 252, 1, 64)           0         ['conv2d_15[0][0]']           
                                                                                                  
 leaky_re_lu_17 (LeakyReLU)  (None, 252, 1, 64)           0         ['conv2d_17[0][0]']           
                                                                                                  
 max_pooling2d_3 (MaxPoolin  (None, 252, 1, 1)            0         ['input_4[0][0]']             
 g2D)                                                                                             
                                                                                                  
 conv2d_16 (Conv2D)          (None, 252, 1, 64)           12352     ['leaky_re_lu_15[0][0]']      
                                                                                                  
 conv2d_18 (Conv2D)          (None, 252, 1, 64)           20544     ['leaky_re_lu_17[0][0]']      
                                                                                                  
 conv2d_19 (Conv2D)          (None, 252, 1, 64)           128       ['max_pooling2d_3[0][0]']     
                                                                                                  
 leaky_re_lu_16 (LeakyReLU)  (None, 252, 1, 64)           0         ['conv2d_16[0][0]']           
                                                                                                  
 leaky_re_lu_18 (LeakyReLU)  (None, 252, 1, 64)           0         ['conv2d_18[0][0]']           
                                                                                                  
 leaky_re_lu_19 (LeakyReLU)  (None, 252, 1, 64)           0         ['conv2d_19[0][0]']           
                                                                                                  
 concatenate_3 (Concatenate  (None, 252, 1, 192)          0         ['leaky_re_lu_16[0][0]',      
 )                                                                   'leaky_re_lu_18[0][0]',      
                                                                     'leaky_re_lu_19[0][0]']      
                                                                                                  
 reshape_3 (Reshape)         (None, 252, 192)             0         ['concatenate_3[0][0]']       
                                                                                                  
 lstm_2 (LSTM)               (None, 252, 160)             225920    ['reshape_3[0][0]']           
                                                                                                  
 dropout_2 (Dropout)         (None, 252, 160)             0         ['lstm_2[0][0]']              
                                                                                                  
 tf.__operators__.getitem_2  (None, 252, 160)             0         ['dropout_2[0][0]']           
  (SlicingOpLambda)                                                                               
                                                                                                  
 time_distributed_2 (TimeDi  (None, 252, 1)               161       ['tf.__operators__.getitem_2[0
 stributed)                                                         ][0]']                        
                                                                                                  
==================================================================================================
Total params: 259361 (1013.13 KB)
Trainable params: 259361 (1013.13 KB)
Non-trainable params: 0 (0.00 Byte)
$\endgroup$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.