Skip to content

Repository files navigation

Stock Buy/Sell Signal Classifier

LSTM + Multi-Head Self-Attention Neural Network


What's New (v2)

Area Change
Leakage fix Each ticker is split chronologically before concatenation — eliminates cross-ticker temporal leakage
Execution delay Backtest now executes at next-day open, not same-day close
New features SPY regime (above/below 200MA), VIX z-score, relative strength vs. SPY, earnings proximity
Walk-forward CV --walkforward flag trains across rolling yearly windows for honest multi-regime evaluation
Confidence gate --confidence 0.55 skips low-conviction signals in both backtesting and live inference
Ensemble inference predict.py automatically averages softmax probs across all walk-forward fold checkpoints
Broader tickers Default ticker list now includes defensive/old-economy names to reduce survivorship bias

Architecture

Input (34 features, seq_len=30)
  └─► Linear Projection + LayerNorm + GELU
        └─► Bidirectional LSTM (2 layers, hidden=128)
              └─► Positional Encoding
                    └─► Multi-Head Self-Attention (4 heads)
                          └─► Residual + LayerNorm
                                └─► Mean Pool ⊕ Last Token Pool
                                      └─► MLP Head (128 → 64 → 3)
                                            └─► Sell / Hold / Buy

34 engineered features:

  • Returns: 1d, log, 5d, overnight gap
  • Momentum: RSI(14), RSI(7), ROC(10), ROC(20)
  • Trend: EMA(9/21) cross, EMA(21/50) cross, MACD line/signal/histogram
  • Volatility: ATR(14), Bollinger %B + width, HV(20)
  • Volume: OBV delta, volume z-score, VWAP ratio
  • Microstructure: close/open ratio, HL range, upper/lower wick
  • Calendar: day-of-week (sin/cos encoded)
  • [NEW] Regime: SPY above/below 200-day MA, VIX z-score
  • [NEW] Relative strength vs. SPY (1-day excess return)
  • [NEW] Earnings proximity (days to nearest earnings, normalised)

Setup

python -m venv venv
source venv/bin/activate        # Windows: venv\Scripts\activate
pip install torch --index-url https://download.pytorch.org/whl/cu121
pip install yfinance pandas numpy scikit-learn

Training

# Standard single-split training
python train.py

# Walk-forward cross-validation (recommended for realistic evaluation)
python train.py --walkforward

# Custom tickers
python train.py --tickers AAPL MSFT NVDA AMZN TSLA

# Custom output dir
python train.py --output_dir runs/experiment_1

# With confidence gating in backtest (only trade signals > 55% confidence)
python train.py --config my_config.json
# (set "confidence_threshold": 0.55 in config)

Outputs in outputs/:

File Description
best_model.pt Best checkpoint by macro F1
training_log.csv Per-epoch train/val metrics
test_preds.npy Test set predictions
test_labels.npy Test set ground truth
test_probs.npy Softmax probabilities
backtest_equity.npy Equity curve
scaler.pkl Fitted RobustScaler
feature_names.json Feature list
config_used.json Run configuration
walkforward/fold_XX/ Per-fold outputs (walk-forward mode)

Inference

# Single ticker (auto-ensembles walk-forward folds if available)
python predict.py --ticker AAPL

# With confidence threshold (UNCERTAIN shown if below threshold)
python predict.py --ticker AAPL --confidence 0.55

# Watchlist scan
python predict.py --watchlist AAPL MSFT NVDA GOOGL TSLA

# Force single model (no ensemble)
python predict.py --ticker AAPL --no_ensemble

Example output:

================================================
  Ticker     : AAPL
  Date       : 2025-01-15
  Signal     : BUY  🟢  (ensemble of 4 folds)
  Confidence :
    Sell     :  12.3%
    Hold     :  31.4%
    Buy      :  56.3%
================================================

Configuration

{
  "tickers": ["AAPL", "MSFT", "NVDA", "XOM", "JNJ"],
  "start": "2015-01-01",
  "end":   "2024-12-31",
  "seq_len": 30,
  "forward_days": 5,
  "buy_threshold": 0.02,
  "sell_threshold": -0.02,
  "include_earnings": true,
  "hidden_dim": 128,
  "lstm_layers": 2,
  "num_heads": 4,
  "epochs": 100,
  "batch_size": 256,
  "lr": 3e-4,
  "patience": 15,
  "confidence_threshold": 0.55,
  "wf_train_years": 4,
  "wf_test_years": 1
}

Key Design Decisions

No Data Leakage (v2 Fix)

Each ticker is split into train/val/test before concatenation with other tickers. In v1, concatenating first then splitting meant that ticker B's 2023 data could appear in the training set while ticker A's 2022 data was in the test set — a subtle but real form of look-ahead leakage.

Realistic Backtest Execution

Signals are generated from day N's close and executed at day N+1's open. v1 executed at the same close that generated the signal, which is impossible in practice and overstates returns.

Walk-Forward Validation

A single train/test split gives one data point on generalisation and can happen to land on an easy or hard market period. Walk-forward trains on rolling 4-year windows and tests on 1-year windows, giving multiple out-of-sample readings across different regimes (low-vol bull, COVID crash, 2022 rate-hike bear, etc.).

Confidence Gating

The model outputs a probability distribution. A Buy signal at 85% confidence is fundamentally different from one at 42%. Setting confidence_threshold: 0.55 instructs both the backtest and live inference to treat low-conviction signals as Hold, which often improves risk-adjusted returns.

Ensemble Inference

After walk-forward training, predict.py automatically detects fold checkpoints and averages their softmax probabilities. This reduces variance on live signals by 20-30% compared to a single model.


⚠️ Disclaimer

For educational and research purposes only. Past model performance does not guarantee future results. Paper-trade for at least 3–6 months before considering real capital. Financial markets are inherently unpredictable and no model eliminates that risk.

About

A Neural Network Model made to predict stocks using real time data and sentiments

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages