100% FreeNo Signup Required
Markets
DJIA38,892.45+156.78(+0.40%)
S&P 5005,021.84+23.45(+0.47%)
NASDAQ15,927.90-45.23(-0.28%)
SPY502.18+2.34(+0.47%)
QQQ437.52-1.23(-0.28%)
AAPL189.45+1.89(+1.01%)
MSFT412.91+3.45(+0.84%)
NVDA878.35+12.56(+1.45%)
GOOGL141.28+0.78(+0.56%)
TSLA185.67-4.34(-2.28%)
META485.12+8.92(+1.87%)
ES=F5,025.50+18.25(+0.36%)
NQ=F17,845.75-32.50(-0.18%)
VIX14.23-0.45(-3.06%)
DJIA38,892.45+156.78(+0.40%)
S&P 5005,021.84+23.45(+0.47%)
NASDAQ15,927.90-45.23(-0.28%)
SPY502.18+2.34(+0.47%)
QQQ437.52-1.23(-0.28%)
AAPL189.45+1.89(+1.01%)
MSFT412.91+3.45(+0.84%)
NVDA878.35+12.56(+1.45%)
GOOGL141.28+0.78(+0.56%)
TSLA185.67-4.34(-2.28%)
META485.12+8.92(+1.87%)
ES=F5,025.50+18.25(+0.36%)
NQ=F17,845.75-32.50(-0.18%)
VIX14.23-0.45(-3.06%)
LIVE

TensorFlow for Trading: Neural Network Price Prediction

Build neural network trading models with TensorFlow and Keras. LSTMs, CNNs, and transformer architectures for financial time series prediction.

DJ

Dr. James Chen

March 14, 2026

|7 min read

TensorFlow for Trading: Neural Network Price Prediction

Deep learning models offer the ability to learn complex temporal patterns from raw financial data. LSTMs capture long-range dependencies in price sequences, convolutional networks detect local patterns similar to chart formations, and attention mechanisms weigh the relevance of different time steps dynamically.

However, deep learning for trading is significantly harder than for computer vision or NLP. Financial time series have low signal-to-noise ratios, non-stationarity, and regime changes that violate the i.i.d. assumptions underlying most neural network training. This guide covers architectures that address these challenges with production-tested patterns.

Key Takeaways

  • LSTMs remain the workhorse for financial time series due to their ability to model sequential dependencies with forget gates.
  • Attention mechanisms improve LSTM performance by focusing on relevant historical time steps.
  • Data preprocessing (stationarity, normalization, sequence construction) matters more than architecture choice.
  • Regularization is essential: dropout, early stopping, and small model capacity prevent overfitting to noise.

Data Preparation for Neural Networks

Neural networks require careful preprocessing that differs from traditional ML pipelines.

python
import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
from sklearn.preprocessing import StandardScaler

def prepare_sequences(
features: np.ndarray,
target: np.ndarray,
sequence_length: int = 60,
forecast_horizon: int = 5,
) -> tuple[np.ndarray, np.ndarray]:
"""
Create sliding window sequences for time series models.

Args:
features: (T, F) array of feature values
target: (T,) array of target values
sequence_length: number of past time steps per sample
forecast_horizon: gap between end of input and target

Returns:
X: (N, sequence_length, F) input sequences
y: (N,) targets
"""
X, y = [], []
for i in range(sequence_length, len(features) - forecast_horizon + 1):
X.append(features[i - sequence_length:i])
y.append(target[i + forecast_horizon - 1])
return np.array(X), np.array(y)

def create_dataset(
df: pd.DataFrame,
target_col: str = "target",
feature_cols: list[str] | None = None,
sequence_length: int = 60,
forecast_horizon: int = 5,
test_ratio: float = 0.2,
val_ratio: float = 0.1,
) -> dict:
"""
Full preprocessing pipeline: scale, sequence, and split temporally.
"""
if feature_cols is None:
feature_cols = [c for c in df.columns if c != target_col]

features = df[feature_cols].values
target = df[target_col].values

# Temporal split points
n = len(features)
train_end = int(n * (1 - test_ratio - val_ratio))
val_end = int(n * (1 - test_ratio))

# Scale on training data only
scaler = StandardScaler()
features[:train_end] = scaler.fit_transform(features[:train_end])
features[train_end:val_end] = scaler.transform(features[train_end:val_end])
features[val_end:] = scaler.transform(features[val_end:])

# Create sequences
X, y = prepare_sequences(features, target, sequence_length, forecast_horizon)

# Adjust split indices for sequence creation offset
offset = sequence_length + forecast_horizon - 1
train_samples = train_end - offset
val_samples = val_end - offset

return {
"X_train": X[:train_samples],
"y_train": y[:train_samples],
"X_val": X[train_samples:val_samples],
"y_val": y[train_samples:val_samples],
"X_test": X[val_samples:],
"y_test": y[val_samples:],
"scaler": scaler,
"feature_names": feature_cols,
}

LSTM Model for Price Direction

The LSTM architecture with dropout regularization is the most reliable starting point for financial time series.

python
def build_lstm_model(
    sequence_length: int,
    n_features: int,
    lstm_units: list[int] = [64, 32],
    dropout_rate: float = 0.3,
    learning_rate: float = 0.001,
    task: str = "classification",
) -> keras.Model:
    """
    LSTM model with stacked layers, dropout, and batch normalization.

Args:
sequence_length: length of input sequences
n_features: number of input features
lstm_units: units per LSTM layer
dropout_rate: dropout probability
task: 'classification' for direction, 'regression' for returns
"""
inputs = keras.Input(shape=(sequence_length, n_features))
x = inputs

# Stacked LSTM layers
for i, units in enumerate(lstm_units):
return_sequences = i < len(lstm_units) - 1
x = layers.LSTM(
units,
return_sequences=return_sequences,
kernel_regularizer=keras.regularizers.l2(1e-4),
)(x)
x = layers.BatchNormalization()(x)
x = layers.Dropout(dropout_rate)(x)

# Dense head
x = layers.Dense(16, activation="relu")(x)
x = layers.Dropout(dropout_rate / 2)(x)

if task == "classification":
outputs = layers.Dense(1, activation="sigmoid")(x)
loss = "binary_crossentropy"
metrics = ["accuracy"]
else:
outputs = layers.Dense(1)(x)
loss = "huber"
metrics = ["mae"]

model = keras.Model(inputs, outputs)
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=learning_rate),
loss=loss,
metrics=metrics,
)
return model

Build and inspect

model = build_lstm_model( sequence_length=60, n_features=25, lstm_units=[64, 32], ) model.summary()

CNN-LSTM Hybrid Architecture

Combine 1D convolutions (for local pattern detection) with LSTMs (for sequential modeling).

python
def build_cnn_lstm_model(
    sequence_length: int,
    n_features: int,
    conv_filters: list[int] = [64, 32],
    kernel_size: int = 3,
    lstm_units: int = 32,
    dropout_rate: float = 0.3,
) -> keras.Model:
    """
    CNN extracts local patterns, LSTM models temporal dependencies.
    Particularly effective for detecting chart-pattern-like structures.
    """
    inputs = keras.Input(shape=(sequence_length, n_features))
    x = inputs

# Convolutional feature extraction
for filters in conv_filters:
x = layers.Conv1D(
filters, kernel_size, padding="causal", activation="relu"
)(x)
x = layers.BatchNormalization()(x)
x = layers.MaxPooling1D(pool_size=2)(x)
x = layers.Dropout(dropout_rate)(x)

# LSTM temporal modeling
x = layers.LSTM(lstm_units)(x)
x = layers.Dropout(dropout_rate)(x)

# Output
x = layers.Dense(16, activation="relu")(x)
outputs = layers.Dense(1, activation="sigmoid")(x)

model = keras.Model(inputs, outputs)
model.compile(
optimizer=keras.optimizers.Adam(0.001),
loss="binary_crossentropy",
metrics=["accuracy"],
)
return model

Attention-Enhanced LSTM

Adding an attention mechanism lets the model focus on the most relevant time steps rather than relying solely on the final hidden state.

python
class TemporalAttention(layers.Layer):
    """Attention mechanism for time series sequences."""

def __init__(self, units: int = 32, **kwargs):
super().__init__(**kwargs)
self.units = units
self.W = layers.Dense(units, activation="tanh")
self.V = layers.Dense(1, use_bias=False)

def call(self, hidden_states):
# hidden_states: (batch, timesteps, features)
score = self.V(self.W(hidden_states)) # (batch, timesteps, 1)
attention_weights = tf.nn.softmax(score, axis=1)
context = tf.reduce_sum(attention_weights * hidden_states, axis=1)
return context, attention_weights

def build_attention_lstm(
sequence_length: int,
n_features: int,
lstm_units: int = 64,
attention_units: int = 32,
dropout_rate: float = 0.3,
) -> keras.Model:
"""LSTM with temporal attention for interpretable predictions."""
inputs = keras.Input(shape=(sequence_length, n_features))

# LSTM returns all hidden states
lstm_out = layers.LSTM(
lstm_units, return_sequences=True,
kernel_regularizer=keras.regularizers.l2(1e-4),
)(inputs)
lstm_out = layers.Dropout(dropout_rate)(lstm_out)

# Attention layer
context, attention_weights = TemporalAttention(attention_units)(lstm_out)

# Dense head
x = layers.Dense(32, activation="relu")(context)
x = layers.Dropout(dropout_rate)(x)
outputs = layers.Dense(1, activation="sigmoid")(x)

model = keras.Model(inputs, outputs)
model.compile(
optimizer=keras.optimizers.Adam(0.001),
loss="binary_crossentropy",
metrics=["accuracy"],
)
return model

Training with Proper Callbacks

Use callbacks to prevent overfitting and save the best model during training.

python
def train_model(
    model: keras.Model,
    data: dict,
    epochs: int = 100,
    batch_size: int = 64,
    patience: int = 15,
) -> keras.callbacks.History:
    """
    Train with early stopping, learning rate reduction,
    and model checkpointing.
    """
    callbacks = [
        keras.callbacks.EarlyStopping(
            monitor="val_loss",
            patience=patience,
            restore_best_weights=True,
        ),
        keras.callbacks.ReduceLROnPlateau(
            monitor="val_loss",
            factor=0.5,
            patience=5,
            min_lr=1e-6,
        ),
        keras.callbacks.ModelCheckpoint(
            "best_model.keras",
            monitor="val_loss",
            save_best_only=True,
        ),
    ]

history = model.fit(
data["X_train"], data["y_train"],
validation_data=(data["X_val"], data["y_val"]),
epochs=epochs,
batch_size=batch_size,
callbacks=callbacks,
verbose=1,
)

return history

Model Evaluation and Backtesting

Evaluate neural network predictions with both ML metrics and financial performance.

python
def evaluate_nn_predictions(
    model: keras.Model,
    X_test: np.ndarray,
    y_test: np.ndarray,
    actual_returns: np.ndarray,
    threshold: float = 0.5,
) -> dict:
    """
    Comprehensive evaluation combining ML and financial metrics.
    """
    # Raw predictions
    y_prob = model.predict(X_test, verbose=0).flatten()
    y_pred = (y_prob >= threshold).astype(int)

# ML metrics
from sklearn.metrics import (
accuracy_score, precision_score, recall_score, roc_auc_score
)
ml_metrics = {
"accuracy": accuracy_score(y_test, y_pred),
"precision": precision_score(y_test, y_pred, zero_division=0),
"recall": recall_score(y_test, y_pred, zero_division=0),
"auc_roc": roc_auc_score(y_test, y_prob),
}

# Financial metrics
strategy_returns = np.where(y_pred == 1, actual_returns, 0)
cumulative = np.cumprod(1 + strategy_returns)
peak = np.maximum.accumulate(cumulative)
drawdown = (cumulative - peak) / peak

financial_metrics = {
"total_return": cumulative[-1] - 1,
"sharpe_ratio": (
np.mean(strategy_returns) / np.std(strategy_returns)

  • np.sqrt(252) if np.std(strategy_returns) > 0 else 0
),
"max_drawdown": drawdown.min(),
"win_rate": (
np.sum((y_pred == 1) & (actual_returns > 0))
/ np.sum(y_pred == 1) if np.sum(y_pred == 1) > 0 else 0
),
"trade_count": np.sum(y_pred == 1),
}

return {ml_metrics, financial_metrics}

Production Deployment Considerations

Deploying neural network models for live trading requires additional infrastructure.

python
# Model serialization for deployment
model.save("models/lstm_trading_v1.keras")
loaded_model = keras.models.load_model(
    "models/lstm_trading_v1.keras",
    custom_objects={"TemporalAttention": TemporalAttention},
)

TensorFlow Lite for low-latency inference

converter = tf.lite.TFLiteConverter.from_keras_model(model) tflite_model = converter.convert() with open("models/lstm_trading_v1.tflite", "wb") as f: f.write(tflite_model)

Inference benchmarking

import time test_input = np.random.randn(1, 60, 25).astype(np.float32) times = [] for _ in range(100): start = time.perf_counter() _ = model.predict(test_input, verbose=0) times.append(time.perf_counter() - start) print(f"Avg inference: {np.mean(times)*1000:.1f}ms")

FAQ

Are LSTMs still relevant compared to transformers for trading?

LSTMs remain highly relevant for trading because financial time series are typically short (60-250 time steps), making transformer self-attention less advantageous than in NLP (where sequences span thousands of tokens). LSTMs also have fewer parameters, which reduces overfitting risk on the limited, noisy data typical of financial applications. Use transformers when you have large amounts of diverse input data (e.g., combining price, text, and alternative data).

How do I prevent my neural network from overfitting to noise?

Use aggressive regularization: 30-50% dropout, L2 weight decay (1e-4 to 1e-3), early stopping with 10-15 epoch patience, and small model capacity (32-64 LSTM units). Additionally, augment training data with noise injection, and use walk-forward validation rather than a single train/test split.

What sequence length should I use for financial LSTMs?

For daily data, sequence lengths of 30-60 trading days typically work best. Shorter sequences (10-20 days) miss longer-term patterns, while very long sequences (200+ days) introduce vanishing gradient issues and dilute recent information. For intraday data, use 60-120 bars. Always tune sequence length as a hyperparameter via walk-forward validation.

Should I predict price levels or returns?

Always predict returns or direction, never raw price levels. Price levels are non-stationary, which violates neural network training assumptions and leads to models that simply memorize the last known price. Log returns are approximately stationary and centered near zero, making them suitable targets for both classification (direction) and regression (magnitude).

Related Articles