The first thing you can do to help a neural network learn more rapidly is to normalize all inputs between 0 and 1. The library sklearn has a preprocess.scale() function that does just that -- make sure to do it separately for training and testing data (or training, validation and testing data if you use three separate sets). This alone can make a huge difference. I saw a deep reinforcement learning tutorial where it massively accelerated the learning. Just to be on the safe side, tensorflow.keras.layers has a BatchNormalization() function that you can add in between each LSTM laters as
model.add( BatchNormalization() )
Another very important detail here is that you never introduced dropouts between your layers. It's essentially designed to randomnly get rid of some information and it is typically used in all types of recurrent networks. As a default, you could add it between each layer with something like a 20% dropout rate:
model.add( Dropout(0.2) )
This one can also be found in the layers module of tensorflow.keras. A last thing is that people routinely stack at least one dense layer on top of LSTM layers before the output layer is introduced. Cook and Hall (2015) at the Federal Reserve found it was working well on macroeconomic data and it seems to be the standard everywhere. So, these are the first simple things I'd personally try first.
At the level of finance, you do have to think carefully about how you label the data. For example, if you can go short on an asset, you have to make sure that you're not labeling all down moves as opportunities to take a short position -- the fall must be large enough to overcome fees, slippage and price impact. Likewise with price increases. This will generally mean that you have a lot of "do nothing" labels in your data and that means there is a local optimum where saying do nothing 100% of the time is actually hard to beat. I see that you're using weights, but if you have a lot of data, a simple way to deal with it is to just randomnly select a balanced (or closer to being balanced) dataset. If that fails, weighing observations like you do would be the last simple option before considering oversampling.
I've seen some systems that use very few features do relatively well on cyptocurrencies, for example. But then again it's a tutorial and they didn't bother doing more than identifying the sign of the growth rate over 3 minutes interval... Depending on how smart you are about labeling or your goal, accuracy might be hard to get. Ask Jim Simons. It took him 40 years and a lot of very smart PhDs to get big in quantitative finance.
EDIT
You can, for example, use
model = Sequential()
for i in range(N_LSTM_CELLS-1):
model.add( LSTM(LSTM(HIDDEN_CELL_NUMBER,
input_shape=(100, 3),
return_sequences=True)) )
model.add( Dropout(0.2) )
model.add( BatchNormalization() )
model.add( LSTM(LSTM(HIDDEN_CELL_NUMBER,
input_shape=(100, 3)) )
model.add( Dropout(0.2) )
model.add( BatchNormalization() )
model.add( Dense(N_UNITS, activation="relu") )
model.add( Dense(3, activation="softmax") )
And you can visit pythonprogramming.net, the Deep Learning Course.