Back to Blog
technical-referenceldtmlstmimplementationrunbookazureml

Codex LD-TM LSTM Implementation, Architecture, Model Design, and Runbook

Step-by-step implementation runbook for the LDTM LSTM model: data prep, training configuration, Azure ML integration, and deployment checklist.

December 22, 2025·12 min read

Codex LD-TM LSTM Implementation, Architecture, Model Design, and Runbook

Date: 2026-04-22

Implementation name: LD-TM LSTM, Long-term Deep Temporal Model

Status: implemented locally. Azure deployment is intentionally deferred.

Executive Summary

LD-TM is a daily, multi-horizon prediction pipeline for the DGX trading system. It trains one LSTM model per ticker, writes inference outputs into PostgreSQL, fills actual results later, exposes prediction history through a Streamlit dashboard, and supplies live database context to a local LLM query layer.

The current implementation covers 103 tickers, including NDX-style equities plus TQQQ and SQQQ. The latest observed snapshot set contains 103 ticker rows for 2026-04-22. The database currently holds 104 snapshot rows across 103 tickers, with first observed snapshot date 2026-04-21 and latest snapshot date 2026-04-22.

Observed local performance from ldtm_run_log:

Mode Successful runs Average seconds Min seconds Max seconds
infer 105 0.60 0.33 0.88
train 105 44.78 2.71 128.94

Training aggregate:

Metric Value
Median train time 35.71 sec
P90 train time 84.08 sec
Average epochs before early stop 21.3
Average best validation loss 0.711670

Inference aggregate:

Metric Value
Average inference time 0.605 sec
Median inference time 0.611 sec
P90 inference time 0.812 sec

Current observed container memory snapshot:

Container Memory usage
trading-dashboard-test 40.32 MiB
trtllm-server 2.91 GiB
trading-postgres 93.88 MiB
prometheus 45.64 MiB
grafana 113.8 MiB

GPU query result:

GPU Name Reported total memory Reported used memory Utilization
0 NVIDIA GB10 N/A N/A 0%

The GB10 runtime reports GPU memory as N/A through nvidia-smi, so the orchestrator uses a fallback concurrency model for MIG-like or virtual GPU reporting.

Implementation Inventory

Area Path Purpose
Model config model/ldtm/config.py Hyperparameters and shared model configuration
Dataset model/ldtm/dataset.py OHLCV load, feature engineering, sliding windows, labels
Model model/ldtm/model.py Two-layer LSTM with three prediction heads
Training model/ldtm/trainer.py AdamW training loop, AMP, scheduler, early stopping
Inference model/ldtm/predict.py Load checkpoint, build latest window, output predictions
Run log schema model/ldtm/schema.sql ldtm_run_log table
Snapshot schema model/ldtm/snapshots_schema.sql ldtm_daily_snapshots table and ldtm_accuracy_30d view
Snapshot writer model/ldtm/snapshot_writer.py Converts inference log rows into daily snapshots
Fill-back model/ldtm/snapshot_fillback.py Writes actual closes and error metrics after targets mature
Orchestrator model/ldtm/orchestrate.py GPU-aware parallel train/infer dispatch
CLI runner model/ldtm/run_ldtm.sh User-facing train/infer command
Dashboard dashboard/app.py Streamlit dashboard
Blob export dashboard/export_to_blob.py Optional static JSON export for Azure Blob
LLM query llm/llm_query.py OpenAI-compatible local LLM query over live DB context
Cron install schedule/install_cron.sh Idempotent cron job installer
Daily inference cron schedule/run_ldtm_infer.sh Inference, snapshots, fill-back
Canary retrain cron schedule/run_ldtm_canary_retrain.sh Weekly NVDA/TQQQ/AAPL retrain
Monthly retrain cron schedule/run_ldtm_monthly_retrain.sh First-Sunday full retrain
Blob export cron schedule/run_blob_export.sh Optional nightly Blob export

Architecture

market_data_daily
       |
       v
OHLCV feature builder
       |
       v
30-trading-day normalized windows
       |
       v
LD-TM LSTM model per ticker
       |
       +--> trainer.py -> checkpoints -> ldtm_run_log
       |
       +--> predict.py -> ldtm_run_log
                         |
                         v
                 snapshot_writer.py
                         |
                         v
              ldtm_daily_snapshots
                         |
                         +--> snapshot_fillback.py -> actuals, errors, accuracy
                         |
                         +--> ldtm_accuracy_30d view
                         |
                         +--> dashboard/app.py
                         |
                         +--> llm/llm_query.py
                         |
                         +--> optional export_to_blob.py

Runtime services:

Service Role
trading-postgres Durable relational store for market data, predictions, run logs, summaries
model-ldtm image Training and inference runtime
trading-dashboard image Streamlit dashboard
llm-query image CLI and dashboard query bridge into local OpenAI-compatible LLM
trtllm-server Local LLM serving endpoint at http://localhost:8000/v1
Monitoring stack Prometheus, Grafana, node exporter, cAdvisor, DCGM exporter

AI Model Design

LD-TM uses one trained model per ticker. The model consumes the most recent 30 valid trading days and predicts three future closing prices:

Horizon Definition
next_day Next available trading day's close
next_monday First future Monday trading close after the window
one_month Close approximately 21 trading days after the window

Input tensor:

X shape = (batch_size, 30, 11)

The 11 features are:

Index Feature Meaning
0 open Daily open
1 high Daily high
2 low Daily low
3 close Daily close
4 volume Daily volume
5 ret1 1-day log return
6 ret5 5-day log return
7 rsi14 Wilder 14-period RSI
8 ma5 5-day simple moving average
9 ma10 10-day simple moving average
10 ma20 20-day simple moving average

Network architecture:

Input: (B, 30, 11)
  |
  v
LSTM, hidden_size=128, num_layers=2, dropout=0.2, batch_first=True
  |
  v
Last hidden state: (B, 128)
  |
  +--> Head next_day:    Linear(128, 64) -> ReLU -> Linear(64, 1)
  +--> Head next_monday: Linear(128, 64) -> ReLU -> Linear(64, 1)
  +--> Head one_month:   Linear(128, 64) -> ReLU -> Linear(64, 1)

Training settings:

Setting Value
Window size 30 trading days
Input size 11
Hidden size 128
LSTM layers 2
Dropout 0.2
Batch size 32
Epochs 100
Early stopping patience 10
Optimizer AdamW
Learning rate 1e-3
Weight decay 1e-4
Scheduler CosineAnnealingLR
Loss Sum of MSE over three horizons
AMP Enabled when CUDA is available
Split 70% train, 15% validation, 15% test by chronological window order

Mathematical Concepts and Formulas

Daily log return:

ret1_t = ln(close_t / close_(t-1))

Five-day log return:

ret5_t = ln(close_t / close_(t-5))

Moving average for window length k:

MA_k(t) = (1 / k) * sum(close_(t-i), i = 0 to k-1)

RSI:

RS_t = avg_gain_t / (avg_loss_t + epsilon)
RSI_t = 100 - 100 / (1 + RS_t)

Per-window min-max normalization:

x_scaled[t, f] = (x[t, f] - min_f) / (max_f - min_f + epsilon)

Future-close label scaling:

y_scaled = (future_close - close_min) / (close_max - close_min + epsilon)

Prediction inverse transform:

pred_close = y_pred_scaled * (close_max - close_min) + close_min

Multi-horizon training loss:

loss = MSE(y_hat_next_day, y_next_day)
     + MSE(y_hat_next_monday, y_next_monday)
     + MSE(y_hat_one_month, y_one_month)

Percent error:

pct_error = ((predicted_close - actual_close) / actual_close) * 100

Next-day direction:

next_day_direction_pred =
    "UP"   if next_day_close_pred > run_date_close
    "DOWN" otherwise

Direction accuracy:

direction_accuracy_pct =
    100 * count(next_day_direction_correct = true)
        / count(next_day_direction_correct is not null)

Implied one-month return:

implied_1m_return_pct =
    ((one_month_close_pred / next_day_close_pred) - 1) * 100

PostgreSQL implementation:

ROUND((((one_month_close_pred / NULLIF(next_day_close_pred, 0)) - 1) * 100)::numeric, 2)

The explicit ::numeric cast fixed the PostgreSQL error:

function round(double precision, integer) does not exist

Database Tables and Views

market_data_daily

Expected upstream table:

Column Type Purpose
ticker TEXT Ticker symbol
date DATE Trading date
open FLOAT Open price
high FLOAT High price
low FLOAT Low price
close FLOAT Close price
volume BIGINT Volume

Primary key:

(ticker, date)

ldtm_run_log

Run ledger for training and inference:

Column Purpose
id Surrogate primary key
run_at Timestamp of run
ticker Ticker symbol
mode train or infer
status success or failed
duration_sec Runtime in seconds
epochs_run Training epochs completed, null for inference
best_val_loss Best validation loss, null for inference
next_day_close Inference output, null for training
next_monday_close Inference output, null for training
one_month_close Inference output, null for training
error_msg Failure details

ldtm_daily_snapshots

Daily prediction and evaluation table, one row per (run_date, ticker).

Column Purpose
id Surrogate primary key
run_date Date prediction was generated
ticker Ticker
generated_at Insert/update timestamp
next_day_close_pred Predicted next trading close
next_monday_close_pred Predicted next Monday close
one_month_close_pred Predicted close about 21 trading days out
run_date_close Close on prediction date, if available
next_day_actual Actual next trading close after fill-back
next_day_actual_date Actual date used
next_day_pct_error Percent error for next-day prediction
next_day_direction_pred UP/DOWN predicted from run-date close
next_day_direction_actual UP/DOWN actual from run-date close
next_day_direction_correct Boolean correctness
next_monday_actual Actual next Monday close after fill-back
next_monday_actual_date Actual next Monday date
next_monday_pct_error Percent error for next-Monday prediction
one_month_actual Actual close around 21 trading days later
one_month_actual_date Actual date used
one_month_pct_error Percent error for one-month prediction
source_run_log_id FK to the inference run

Observed latest sample outputs for 2026-04-22:

Ticker Next day pred Next Monday pred One month pred
AAPL 268.55 268.74 275.05
ABNB 144.57 143.57 144.68
ADBE 245.30 247.47 265.87
ADI 380.51 382.01 376.79
ADP 204.62 205.47 215.16

ldtm_accuracy_30d

Dashboard view for recent accuracy:

Column Meaning
ticker Ticker
evaluated_days Number of filled next-day predictions
avg_abs_pct_error Average absolute next-day percent error
avg_abs_pct_error_weekly Average absolute next-Monday percent error
direction_accuracy_pct Percent of filled next-day directions correct
latest_run_date Latest prediction date
latest_next_day_pred Latest next-day prediction

At the time of this document, the view exists but evaluation fields are mostly empty because actuals have not yet matured for the newest predictions.

Outputs Returned

Training final output:

[LDTM] RESULT: ticker=AAPL epochs=... val_loss=... duration=...s

Inference JSON:

{
  "ticker": "AAPL",
  "next_day_close": 268.55,
  "next_monday_close": 268.74,
  "one_month_close": 275.05
}

Snapshot writer output:

[snapshot_writer] Wrote N snapshots for YYYY-MM-DD

Fill-back output:

[snapshot_fillback] Updated N rows

Dashboard URL:

http://localhost:8501

Dashboard tabs:

Tab Output
Ticker View Prediction history, actual close, next-day actual, one-month prediction, error chart
Today's Predictions All latest ticker rows sorted by implied one-month return
Accuracy Leaderboard 30-day direction accuracy and average absolute error
TQQQ Signal Latest TQQQ strategy JSON summary
LLM Query Natural language answer using predictions, accuracy, training metadata, and headlines

Current Prediction Distribution

For latest run date 2026-04-22:

Bucket Count
Bullish, implied one-month return greater than 1% 68
Neutral, between -1% and 1% 26
Bearish, below -1% 9

Top implied one-month returns:

Ticker Implied 1m return Next day pred One month pred
AMD 11.62% 282.22 315.00
APP 8.52% 457.32 496.28
INTU 8.44% 408.80 443.29
ADBE 8.39% 245.30 265.87
CSGP 7.57% 39.88 42.90

Lowest implied one-month returns:

Ticker Implied 1m return Next day pred One month pred
ODFL -21.02% 220.74 174.35
SQQQ -6.19% 56.66 53.15
DDOG -3.59% 128.96 124.33
DASH -1.91% 186.34 182.78
MAR -1.87% 377.10 370.03

These are model outputs, not trading advice.

Scheduler Design

Time Job
6:00 PM Mon-Fri Market data ingestion
6:15 PM Mon-Fri LD-TM inference, snapshot write, fill-back
6:30 PM Mon-Fri News ingestion
7:00 PM Mon-Fri Optional Azure Blob export
2:00 AM Saturday Canary retrain for NVDA, TQQQ, AAPL
1:00 AM Sunday Monthly retrain script, guarded to first Sunday only

run_ldtm_monthly_retrain.sh is scheduled every Sunday at 1 AM, but exits unless the day of month is 1 through 7.

Runbook

1. Start from project root

cd /home/aimikamirai/projects/dgx-trading-system

2. Confirm .env

Required variables:

DB_HOST
DB_PORT
DB_NAME
DB_USER
DB_PASSWORD

Optional variables:

AZURE_BLOB_CONN_STR
AZURE_BLOB_CONTAINER
AZURE_BLOB_URL
LLM_BASE_URL
LLM_API_KEY
LLM_MODEL

Azure deployment is not required. Blob export remains optional and should be treated as inactive until AZURE_BLOB_CONN_STR exists.

3. Build model image

docker build -t model-ldtm ./model/ldtm

4. Initialize LD-TM run log

bash model/ldtm/run_ldtm.sh --init-db

5. Initialize snapshot schema

docker exec -i trading-postgres psql -U postgres -d trading < model/ldtm/snapshots_schema.sql

6. Train one ticker

bash model/ldtm/run_ldtm.sh --ticker AAPL --mode train --epochs 100 --env_file .env

7. Infer one ticker

bash model/ldtm/run_ldtm.sh --ticker AAPL --mode infer --env_file .env

8. Train and infer one ticker

bash model/ldtm/run_ldtm.sh --ticker AAPL --mode both --env_file .env

9. Train or infer a group

bash model/ldtm/run_ldtm.sh --group mega_cap --mode train --parallel 4 --env_file .env

Available groups:

mega_cap, semis, software, internet, consumer, healthcare, industrial,
telecom, financials, hardware, intl, etfs

10. Infer all tickers

bash model/ldtm/run_ldtm.sh --all --mode infer --parallel 16 --env_file .env

11. Run the daily inference pipeline manually

bash schedule/run_ldtm_infer.sh

This performs:

all ticker inference -> snapshot writer -> snapshot fill-back

12. Build and run dashboard

docker build -t trading-dashboard ./dashboard
docker run -d --name trading-dashboard-test \
  --network host --env-file .env \
  -e DATA_SOURCE=db \
  trading-dashboard

Open:

http://localhost:8501

Health check:

curl -s http://localhost:8501/healthz

13. Run LLM query

The LLM query layer expects an OpenAI-compatible endpoint:

http://localhost:8000/v1

Example:

docker compose --profile llm run --rm llm-query \
  python llm_query.py --group mega_cap \
  --question "Which tickers have the strongest 1-month signal and why?"

14. Install cron jobs

Ensure scripts are executable:

chmod +x schedule/run_ldtm_infer.sh \
  schedule/run_ldtm_canary_retrain.sh \
  schedule/run_ldtm_monthly_retrain.sh \
  schedule/run_blob_export.sh

Install:

bash schedule/install_cron.sh

Verify:

crontab -l

15. Optional Blob export

Do not deploy to Azure right now. When Blob export is desired later:

  1. Add AZURE_BLOB_CONN_STR to .env.
  2. Optionally set AZURE_BLOB_CONTAINER.
  3. Run:
docker compose --profile blob run --rm blob-export

Operations and Troubleshooting

PostgreSQL ROUND error

Symptom:

function round(double precision, integer) does not exist

Fix:

ROUND((some_double_expression)::numeric, 2)

No snapshot rows

Check latest inference logs:

SELECT ticker, mode, status, next_day_close, run_at
FROM ldtm_run_log
WHERE mode = 'infer'
ORDER BY run_at DESC
LIMIT 20;

Accuracy is empty

This is expected immediately after first snapshots. ldtm_accuracy_30d needs future market data to fill next_day_actual, next_monday_actual, and one_month_actual.

Dashboard shows no data

Check:

SELECT COUNT(*), MAX(run_date) FROM ldtm_daily_snapshots;

Then confirm DATA_SOURCE=db and DB env vars are available inside the dashboard container.

LLM query fails

Check:

docker ps

Confirm the local LLM endpoint is up:

trtllm-server on port 8000

Performance Notes and Efficiency Plan

The current LD-TM model is small enough that per-ticker inference is fast, averaging about 0.605 seconds. Training is dominated by per-ticker dataset loading, DataLoader overhead, Docker startup overhead, and epoch count before early stopping. Average training time is 44.78 seconds per ticker, with median 35.71 seconds and P90 84.08 seconds.

The dashboard memory footprint is small, around 40 MiB at the observed moment. The local LLM server is the major resident memory consumer at about 2.91 GiB CPU/container memory, not counting GPU allocation that may not be reported by the GB10 runtime.

The orchestrator estimates GPU slots as:

slots = max(1, min(16, vram_mib / 600))

When GPU memory is N/A, it uses:

fallback_slots_per_gpu = 4

Recommended improvements:

Change Expected benefit
Keep a warm inference worker instead of launching one Docker container per ticker Removes most Docker startup overhead
Batch tickers inside one process Reduces Python and model initialization overhead
Load all checkpoints once per run Avoid repeated disk IO
Use one container with internal multiprocessing Better than many short-lived containers
Increase DataLoader workers for larger datasets Helps if CPU preprocessing becomes bottleneck
Persist engineered features Avoid recomputing RSI/MA/log returns for every training run
Use pinned memory when CUDA path is active Faster CPU to GPU transfers
Use FP16 AMP consistently Already enabled on CUDA for training; can extend to inference
Use torch.compile where stable May reduce model overhead for repeated inference
Export ONNX/TensorRT engines for inference Best latency path for fixed architecture
Group small models into batched inference service Higher GPU utilization

FP16 AMP is already used in training when CUDA is available. Flash Attention is not the primary lever for LD-TM because the model is an LSTM, not a Transformer attention stack. Flash Attention is more relevant to the local Mistral/TensorRT-LLM service. For LD-TM, better acceleration paths are FP16 inference, warm model execution, ONNX export, TensorRT engines where supported, and a persistent batched service.

Design Notes and Risks

  1. Per-window normalization makes the model robust to price scale, but predictions can exceed the local min/max regime.
  2. Accuracy metrics are intentionally sparse until actual market closes mature.
  3. Top and bottom implied returns are raw model signals and need accuracy weighting before becoming trading candidates.
  4. One model per ticker is operationally simple and isolates ticker behavior, but it prevents cross-sectional learning.
  5. The local LLM is an explanation and query layer, not the source of prediction truth.
  6. Blob export is ready as an optional static publishing path, but Azure deployment is paused by request.
  1. Let fill-back run for several trading days before judging direction accuracy.
  2. Add a dashboard confidence score blending implied return, direction accuracy, evaluated days, and average absolute percent error.
  3. Build a persistent inference service to reduce all-ticker inference time.
  4. Add a feature cache table or parquet cache for engineered features.
  5. Add benchmark logging for GPU memory, CPU memory, wall-clock per stage, and queue time.
  6. Add a model card row per ticker with last train date, epochs, validation loss, inference latency, and latest signal.
  7. Keep Azure deployment deferred until local accuracy, cost, and security posture are satisfactory.