A complete LSTM — forget, input and output gates, cell state, forward and backpropagation through time — implemented in NumPy with no deep-learning framework, wired to an Adam optimiser, gradient clipping, a learning-rate schedule and early stopping, and pointed at Indonesian gold prices. The point was to understand the recurrence, and then to measure the result instead of claiming it.
Measured result
The run sits below the truth at every point it was tested on. Mean absolute error and forecast bias come back identical in magnitude — 985.2841 against −985.2841 — which is only possible when every residual has the same sign.
visualizations/summary_report.txt
The four gates of an LSTM are easy to draw and easy to mis-implement. This writes them out: the concatenated input, each gate’s weight matrix, the cell state update, and the gradients that flow back through all of it across a sequence.
Around the cell sits the rest of what training needs and a tutorial usually skips — Adam with its two moment estimates, gradient clipping by norm, a step learning-rate schedule, early stopping, and a preprocessing stage that builds moving averages, RSI, MACD, Bollinger bands, lags and rolling volatility into a feature matrix.
The recorded ten-epoch run reports an R² of −0.35 — worse than predicting the mean — with 55.9% directional accuracy, a residual autocorrelation of 0.95, and a simulated strategy return of 2.5% against 27.98% for simply holding the asset. The model lost to doing nothing by roughly 25 percentage points.
That is the result, and it is the useful part. A residual autocorrelation that close to one says the model is mostly reproducing yesterday’s price, which is what a naive predictor does and what any honest time-series baseline is supposed to catch. Directional accuracy a few points above a coin flip, on one short run, is not a signal.
One pair of numbers says more than the rest. Mean absolute error is 985.2841 and forecast bias is −985.2841 — identical in magnitude, opposite in sign. The mean of the residuals can only equal the mean of their absolute values when every residual points the same way, so the model sits below the truth at every point it was tested on. An explained variance of 0.71 beside that negative R² fits exactly: the shape is roughly tracked and the level is systematically wrong.
A project like this is finished when it can tell you that clearly. The implementation is correct; the claim that a small LSTM on price history predicts commodity prices is not, and the evaluation harness exists precisely so the second statement does not get to hide behind the first.