Skip to content

Commit

Permalink
add yfinance LSTM example
Browse files Browse the repository at this point in the history
  • Loading branch information
briangu committed Dec 24, 2023
1 parent 57b9b22 commit 102d4d8
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 3 deletions.
2 changes: 1 addition & 1 deletion examples/ml/lstm/lstm.kg
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ trainx::((#windows)-1)#windows;trainy::((#labels)-1)#labels
testx::windows@-1;testy::labels@-1

:" print the first window and label "
.d("series ");.d(1#trainw);.d(" predicts ");.p(1#trainl)
.d("series ");.d(1#trainx);.d(" predicts ");.p(1#trainy)

:" create the stateful model trainer "
t::trainer(m)
Expand Down
3 changes: 1 addition & 2 deletions examples/stocks/yfinance/fetch.kg
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
.py("klongpy.db")
.pyf("numpy";"std")
.pyf("numpy";"mean")
.pyf("numpy";["std" "mean"])

;" pip3 install yfinance "
.pyf("yfinance";"download")
Expand Down
51 changes: 51 additions & 0 deletions examples/stocks/yfinance/lstm.kg
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.py("klongpy.db")
.py("lstm.py")
.py("mkwindows.py")

;" pip3 install yfinance "
.pyf("yfinance";"download")
fetch::{[args];args:::{};args,"tickers",,x;args,"start",,y;args,"end",,z;.pyc(download;[];args)}

:" yfinance returns a pandas dataframe so we need to convert it to a KlongPy table "
data::fetch("MSFT";"2020-01-01"; "2023-01-01"])
T::.table(data)

:" get close prices "
close::T?"Close"

k::10
windows::mkwindows(close;k)

:" create the model wrapper "
m::model(1;100;1)

:" create windowed data "
windows::mkwindows(close;k)
labels::{x@0}'1_windows
windows::(-1)_windows

.d("series ");.d(1#windows);.d(" predicts ");.p(1#labels)

:" normalize windows and labels "
wmax::{(x@>x)@0}'windows
windows::wmax{y%x}'windows
labels::wmax{y%x}'labels

trainx::(-1)_windows;trainy::(-1)_labels
testx::windows@-1;testy::labels@-1

:" print the first window and label "
.d("series ");.d(1#trainx);.d(" predicts ");.p(1#trainy)

:" create the stateful model trainer "
t::trainer(m)

:" train the model using the Each-2 operator which calls t(x;y) for each window and label "
loss::trainx t'trainy

:" show that the training loss decreases "
.d("training loss: ");.d(loss@0);.d(" --> ");.p(loss@-1)

:" predict using the model "
p::predictor(m)
.d("prediction: ");.d((wmax@-1)*p(testx));.d(" should be close to ");.p((wmax@-1)*testy)
62 changes: 62 additions & 0 deletions examples/stocks/yfinance/lstm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import torch
import torch.nn as nn
import numpy as np

class LSTMModel(nn.Module):
def __init__(self, input_size, hidden_layer_size, output_size):
super(LSTMModel, self).__init__()
self.lstm = nn.LSTM(input_size, hidden_layer_size)
self.linear = nn.Linear(hidden_layer_size, output_size)

def forward(self, input_seq):
# Reshape input_seq to (seq_len, batch, input_size)
input_seq = input_seq.view(len(input_seq), 1, -1)
lstm_out, _ = self.lstm(input_seq)
predictions = self.linear(lstm_out[-1].view(1, -1))
return predictions[:, 0]


class LSTMWrapper:
"""
Use a wrapper class to prevent KlongPy from converting the callable model to a Klong function.
"""
def __init__(self, input_size, hidden_layer_size, output_size):
self.model = LSTMModel(input_size, hidden_layer_size, output_size)


class Trainer:

def __init__(self, wrapper):
self.model = wrapper.model
self.loss_function = nn.MSELoss()
self.optimizer = torch.optim.Adam(self.model.parameters(), lr=0.001)

def __call__(self, x, y):
x = torch.tensor(x).float()
y = torch.tensor([y]).float()
self.optimizer.zero_grad()
y_pred = self.model(x)
single_loss = self.loss_function(y_pred, y)
single_loss.backward()
self.optimizer.step()
return single_loss.item()

class Predictor:
def __init__(self, wrapper):
self.model = wrapper.model

def __call__(self, x):
x = torch.tensor(x).float()
with torch.no_grad():
return self.model(x).item()

def load(filename):
model =torch.load(filename)
model.eval()
return model

def save(filename, model):
torch.save(model, filename)


klongpy_exports = {"load": load, "model": LSTMWrapper, "trainer": Trainer, "predictor": Predictor}

0 comments on commit 102d4d8

Please sign in to comment.