Let's denote the prices by $x_t \geq 0$, i.e. at time $t \in \mathbb{N}$ we observe some price $x_t$.
For a trader to do well in the stock market on day $t$ he should want to predict $x_t$ via $$x_t \sim p(x_t|x_{t-1}, \ldots x_1).$$
Autoregressive Models
Markov Model
Causality
In principle, there's nothing wrong with unfolding $p(x_1, \ldots x_T)$ in reverse order. $$p(x_1, \ldots x_T) = \prod_{t=T}^1 p(x_t|x_{t+1}, \ldots x_T).$$
In many cases, however, there exists a natural direction for the data, namely going forward in time.
It is clear that future events cannot influence the past.
For instance, Hoyer et al., 2008 show that in some cases we can find $x_{t+1} = f(x_t) + \epsilon$ for some additive noise, whereas the converse is not true.
This is great news, since it is typically the forward direction that we're interested in estimating.
from mxnet import autograd, nd, gluon, init
import gluonbook as gb
# display routines
%matplotlib inline
from matplotlib import pyplot as plt
from IPython import display
display.set_matplotlib_formats('svg')
embedding = 4 # embedding dimension for autoregressive model
T = 1000 # generate a total of 1000 points
time = nd.arange(0,T)
x = nd.sin(0.01 * time) + 0.2 * nd.random.normal(shape=(T))
plt.plot(time.asnumpy(), x.asnumpy());
features = nd.zeros((T-embedding, embedding)) # (1000 - 4, 4) = (996, 4)
# features[:, 0] = x[0:996]
# features[:, 1] = x[1:997]
# features[:, 2] = x[2:998]
# features[:, 3] = x[3:999]
for i in range(embedding):
features[:, i] = x[i:T - embedding + i]
# labels = x[4:]
labels = x[embedding:]
ntrain = 600
train_data = gluon.data.ArrayDataset(features[:ntrain,:], labels[:ntrain])
test_data = gluon.data.ArrayDataset(features[ntrain:,:], labels[ntrain:])
# vanilla MLP architecture
def get_net():
net = gluon.nn.Sequential()
net.add(gluon.nn.Dense(10, activation='relu'))
net.add(gluon.nn.Dense(10, activation='relu'))
net.add(gluon.nn.Dense(1))
net.initialize(init=init.Xavier(), force_reinit=True)
return net
# least mean squares loss
loss = gluon.loss.L2Loss()
# simple optimizer using adam, random shuffle and minibatch size 16
def train_net(net, data, loss, epochs, learning_rate):
batch_size = 16
trainer = gluon.Trainer(net.collect_params(), 'adam', {'learning_rate': learning_rate})
data_iter = gluon.data.DataLoader(data, batch_size, shuffle=True)
for epoch in range(1, epochs + 1):
for X, y in data_iter:
with autograd.record():
l = loss(net(X), y)
l.backward()
trainer.step(batch_size)
l = loss(net(data[:][0]), nd.array(data[:][1]))
print('epoch %d, loss: %f' % (epoch, l.mean().asnumpy()))
return net
net = get_net()
net = train_net(
net=net,
data=train_data,
loss=loss,
epochs=10,
learning_rate=0.01
)
l = loss(net(test_data[:][0]), nd.array(test_data[:][1]))
print('test loss: %f' % l.mean().asnumpy())
epoch 1, loss: 0.026710 epoch 2, loss: 0.025081 epoch 3, loss: 0.025592 epoch 4, loss: 0.026057 epoch 5, loss: 0.027615 epoch 6, loss: 0.024617 epoch 7, loss: 0.023896 epoch 8, loss: 0.024280 epoch 9, loss: 0.024480 epoch 10, loss: 0.026319 test loss: 0.028010
estimates = net(features)
plt.plot(time.asnumpy(), x.asnumpy(), label='data');
plt.plot(time[embedding:].asnumpy(), estimates.asnumpy(), label='estimate');
plt.legend();
estimates.shape
(996, 1)
predictions = nd.zeros_like(estimates)
# ntrain - embedding = 600 - 4 = 596
predictions[:(ntrain-embedding)] = estimates[:(ntrain-embedding)]
# T - embedding = 996
for i in range(ntrain-embedding, T-embedding):
predictions[i] = net(predictions[(i-embedding):i].reshape(1,-1)).reshape(1)
plt.plot(time.asnumpy(), x.asnumpy(), label='data');
plt.plot(time[embedding:].asnumpy(), estimates.asnumpy(), label='estimate');
plt.plot(time[embedding:].asnumpy(), predictions.asnumpy(), label='multistep');
plt.legend();
k = 33 # look up to k - embedding steps ahead
# T-k = 1000-33 = 967
features = nd.zeros((T-k, k))
# features[:, 0] = x[0:967]
# features[:, 1] = x[1:968]
# features[:, 2] = x[2:969]
# features[:, 3] = x[3:970]
for i in range(embedding):
features[:,i] = x[i:T-k+i]
# features[:, 4] = net(features[:, 0:4]).reshape((-1))
# features[:, 5] = net(features[:, 1:5]).reshape((-1))
# ...
# features[:, 32] = net(features[:, 28:32]).reshape((-1))
for i in range(embedding, k):
features[:,i] = net(features[:,(i-embedding):i]).reshape((-1))
for i in (4, 8, 16, 32):
plt.plot(time[i:T-k+i].asnumpy(), features[:,i].asnumpy(), label=('step ' + str(i)))
plt.legend();