Deep Learning · Time Series

LSTM — Long Short-Term Memory Network

LSTM (Long Short-Term Memory) eliminates the Vanishing Gradient problem of standard RNNs through three specialised gates — Forget, Input, and Output. In algorithmic trading, LSTM retains long-range dependencies in OHLCV data, allowing the model to factor in significant events from several weeks back — such as a support-level breach or central bank decision — when generating a signal today.

65–75%
Out-of-Sample Acc.
+5000
Candles Required
1–4h
GPU Training Time
H1–D1
Optimal Timeframe

How Does LSTM Learn? — Three Control Gates

Input
xt
Forget
ft
Input
it
Cell State
Ct
Output
ot
Hidden State
σ(tanh)
🗑️
Forget Gate
Forget Gate · ft = σ(Wf·[ht-1, xt])
Decides which information from the previous Cell State to discard. In trading: filtering outdated price noise and obsolete trends.
📥
Input Gate
Input Gate · it = σ(Wi·[ht-1, xt])
Decides what new information from the current candle enters the Cell State. In trading: capturing key price structures like Breakouts and Gaps.
📤
Output Gate
Output Gate · ot = σ(Wo·[ht-1, xt])
Decides which part of the Cell State becomes the output Hidden State passed to the next layer. In trading: generating the final Buy/Sell/Hold signal.

Real-World LSTM Performance in Financial Markets

70%
Average Trend
Classification Accuracy (H4)
11
Input Features
(OHLCV + Indicators)
60
Input Window
Length (candles)
0.62
Decision Threshold
for Buy Signal

ExpertNevees Implementation Specification

📊
Input Data
  • OHLCV: Open, High, Low, Close, Volume — last 60 candles
  • RSI(14): Relative Strength Index
  • ATR(14): Average True Range for volatility
  • EMA(20) & EMA(50): Exponential moving averages
  • Normalization: MinMaxScaler per feature
  • Tensor Shape: [Batch, 60, 11]
🎯
Model Output
  • Type: 3-class classification with Softmax
  • P(Up): Probability of rise in next 3 candles
  • P(Down): Probability of fall in next 3 candles
  • P(Neutral): Sideways market or no signal
  • Buy Threshold: P(Up) > 0.62 → Buy signal
  • Delivery: Via ZeroMQ to EA in MT5

LSTM Gate Equations

Mathematics · Notation
# Forget gate — decides what fraction of the previous cell state to discard
f_t = sigma( W_f . [h_(t-1), x_t] + b_f )

# Input gate — decides what new information gets stored
i_t = sigma( W_i . [h_(t-1), x_t] + b_i )

# Candidate values for updating the cell state
C~_t = tanh( W_C . [h_(t-1), x_t] + b_C )

# Cell state update: combine old memory (scaled by forget gate) with new candidate
C_t = f_t * C_(t-1) + i_t * C~_t

# Output gate — decides what fraction of memory is exposed as output
o_t = sigma( W_o . [h_(t-1), x_t] + b_o )

# Final hidden state passed to the next layer or output
h_t = o_t * tanh(C_t)

LSTM Implementation with TensorFlow/Keras

Python · TensorFlow/Keras
import numpy as np
import tensorflow as tf
from tensorflow.keras import Sequential, Input
from tensorflow.keras.layers import LSTM, Dense, Dropout, BatchNormalization
from sklearn.preprocessing import MinMaxScaler

# ── Stacked Bi-directional LSTM architecture for trend classification ──
def build_lstm_model(seq_len=60, n_features=11, n_classes=3):
    model = Sequential([
        Input(shape=(seq_len, n_features)),
        # First LSTM layer — returns full sequence for the second layer
        LSTM(128, return_sequences=True),
        BatchNormalization(),
        Dropout(0.25),

        # Second LSTM layer — collapses sequence to a single context vector
        LSTM(64, return_sequences=False),
        BatchNormalization(),
        Dropout(0.20),

        # Projection head before classifier
        Dense(32, activation='relu'),
        # Output: 3-class softmax — class 0=Down, 1=Neutral, 2=Up
        Dense(n_classes, activation='softmax')
    ])
    model.compile(
        # learning_rate=1e-3 with cosine decay recommended for long runs
        optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3),
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model

# ── Feature engineering: OHLCV + 6 technical indicators → [Batch, 60, 11] ──
def make_sequences(df, seq_len=60):
    # Per-feature MinMax scaling applied on training split only (avoid leakage)
    scaler = MinMaxScaler()
    scaled = scaler.fit_transform(df[[
        'open', 'high', 'low', 'close', 'volume',  # OHLCV
        'rsi_14', 'atr_14', 'ema_20', 'ema_50',      # momentum / volatility
        'bb_upper', 'bb_lower'                        # Bollinger Band channels
    ]])
    X, y = [], []
    for i in range(seq_len, len(scaled)):
        X.append(scaled[i-seq_len:i])  # sliding window → shape [60, 11]
        y.append(get_label(df, i))     # forward-label: 0=Down, 1=Neutral, 2=Up
    return np.array(X), np.array(y), scaler

# ── Real-time inference server: receives OHLCV from MT5 EA via ZeroMQ REP ──
import zmq

def signal_server(model, scaler, threshold=0.62):
    ctx = zmq.Context()
    sock = ctx.socket(zmq.REP)
    sock.bind("tcp://*:5555")
    while True:
        data = sock.recv_json()          # receive last 60 candles from MT5 EA
        X = prepare_input(data, scaler)  # scale + reshape → [1, 60, 11]
        probs = model.predict(X, verbose=0)[0]  # [p_down, p_neutral, p_up]
        # Only emit a directional signal when confidence exceeds threshold
        signal = "BUY"  if probs[2] > threshold else \
                 "SELL" if probs[0] > threshold else "HOLD"
        sock.send_json({"signal": signal, "confidence": float(probs.max()),
                        "probs": probs.tolist()})

Training, Prediction & Evaluation — LSTM

Python · Train / Predict / Evaluate
# ── Training run + Out-of-Sample evaluation ──
from sklearn.metrics import classification_report, accuracy_score

X, y, scaler = make_sequences(df, seq_len=60)

# Time-ordered split — NEVER shuffle time series data (causes lookahead leakage)
split = int(len(X) * 0.8)
X_train, X_test = X[:split], X[split:]
y_train, y_test = y[:split], y[split:]

model = build_lstm_model(seq_len=60, n_features=11, n_classes=3)
history = model.fit(
    X_train, y_train,
    validation_split=0.15,
    epochs=50,
    batch_size=32,
    callbacks=[tf.keras.callbacks.EarlyStopping(patience=8, restore_best_weights=True)],
    verbose=1
)

# Out-of-sample prediction: probabilities per class → [Down, Neutral, Up]
probs = model.predict(X_test)
y_pred = probs.argmax(axis=1)

print(f"Test Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(classification_report(y_test, y_pred, target_names=['Down', 'Neutral', 'Up']))

# Single latest-window inference (what signal_server() does per tick)
latest_probs = model.predict(X_test[-1:])[0]
print(f"Latest signal -> Down={latest_probs[0]:.2f}  Neutral={latest_probs[1]:.2f}  Up={latest_probs[2]:.2f}")

Common LSTM Implementation Mistakes

🛠️ Implementation Notes That Are Often Overlooked
  • Scaler Data Leakage: Fitting MinMaxScaler/StandardScaler on the full dataset before the train/test split leaks future statistics into training. Fit the scaler on train only, then transform test.
  • Raw Prices Instead of Returns: Feeding non-stationary absolute prices instead of log-returns or percent change causes the model to overfit to the price level and break down at new price ranges.
  • Ignoring Class Imbalance: The Neutral class usually dominates. Without class_weight or resampling, the model learns to always predict Neutral to keep accuracy high — a high but useless accuracy.
  • Shuffling the Train/Test Split: Using train_test_split with shuffle=True on time series causes lookahead bias — the model learns from future patterns during training.

For Which Use Cases is LSTM Suitable?

📈
Close Price Prediction
Best model for Close Price regression on H1–D1 timeframes. Sufficient historical data (5000+ candles) required.
🔄
Trend Classification
Up/Down/Neutral classification for the next 3 candles. Combined with ATR risk management for automated entries and exits.
🛡️
Filter for Existing EA Signals
LSTM as confirmation filter for classic EAs. Only signals confirmed by LSTM are executed, significantly reducing false signals.
💹
Swing & Positional Trading
LSTM models on H4 and D1 for Swing Trading strategies with multi-day position holding.
🌐
Volatility Forecasting
LSTM for forecasting future ATR values and automatically adjusting position size based on expected volatility.
🤖
Combine with Attention Mechanism
LSTM + Temporal Attention to identify key candles in the input sequence, achieving higher accuracy than plain LSTM in trending markets.
⚠️ Real-World Limitations — Know Before You Invest
  • Sideways Markets: Accuracy drops below 55% in ranging, directionless markets. LSTM is for trend detection, not trendless conditions.
  • Retraining Required: The model needs weekly or monthly retraining with fresh data. Markets shift, and stale models degrade.
  • Overfitting: Without Walk-Forward Validation and proper Feature Engineering, the model overfits to training data. High In-Sample accuracy alone is not sufficient.
  • Black Swan Events: LSTM cannot predict unexpected events like geopolitical crises or sudden central bank policy shifts.
  • Not Sufficient Alone: No model builds a profitable system by itself. Combining LSTM with quantitative risk management, broker selection, and validated parameters is essential.

LSTM vs Other Deep Learning Models

Feature LSTM GRU Transformer (TFT) Attention Mechanism
Training Speed Moderate Fast (+20%) Slow (GPU required) Moderate
Long-term Memory ✓ Strong ✓ Good ✓✓ Very Strong ✓✓ Very Strong
Interpretability Weak Weak Moderate Excellent (Attention Heatmap)
Data Required +5000 candles +4000 candles +10000 candles +5000 candles
Out-of-Sample Accuracy 65–75% 63–72% 68–78% 67–76%
Best For Price prediction H1–D1 Daily retraining Multi-step forecasting Key candle detection

Foundational Papers for This Model

  • Hochreiter, S. & Schmidhuber, J. (1997) Long Short-Term Memory Neural Computation, 9(8), 1735–1780
    View Paper
  • Gers, F. A., Schmidhuber, J. & Cummins, F. (2000) Learning to Forget: Continual Prediction with LSTM Neural Computation, 12(10), 2451–2471
    View Paper

Ready to Build Your LSTM Trading Bot?

ExpertNevees team trains a custom LSTM model on your target market data, performs Walk-Forward Validation, and connects it to MT5 via ZeroMQ.

Other ExpertNevees AI Models

Deep Learning · Time Series

GRU — Fast & Efficient LSTM Alternative

GRU (Gated Recurrent Unit) is a simplified variant of LSTM that uses two gates instead of three, improving training speed by 15–25% without meaningful accuracy loss — a practical choice for systems requiring daily or weekly retraining.

63–72%
Out-of-Sample Acc.
+20%
Faster than LSTM
2
Control Gates
M15–H4
Optimal Timeframe

How Does GRU Learn? — Two Gates Instead of Three

Input
xt
Reset
rt
Update
zt
Hidden State
σ(tanh)
🔄
Reset Gate
Reset Gate · rt = σ(Wr·[ht-1, xt])
Determines how much of the past memory to ignore when computing the candidate state. In trading: quickly discarding the influence of irrelevant old candles.
🔁
Update Gate
Update Gate · zt = σ(Wz·[ht-1, xt])
Blends how much of the previous state to keep versus how much of the new candidate state to use — merging LSTM's Forget and Input gates into one.

Real GRU vs LSTM Comparison on EUR/USD H1

66%
GRU Accuracy
(5000 H1 candles)
35m
GRU Training Time
(vs 45min LSTM)
2
Gates vs
3 Gates in LSTM
24h
Adaptive
Retraining Cycle

ExpertNevees Implementation Specification

📊
Input Data
  • OHLCV: Same Feature Engineering pipeline as LSTM
  • Timeframe: M15 to H4 for short-term prediction
  • Prediction Horizon: 1 to 5 candles ahead
  • Retraining: Every 24 hours with new data
🎯
Model Output
  • Type: 3-class classification with Softmax
  • Typical Accuracy: 2% lower than LSTM
  • Inference Speed: Faster due to fewer parameters
  • Delivery: Via ZeroMQ to EA in MT5

GRU Gate Equations

Mathematics · Notation
# Update gate — how much of the previous state to keep (merges roles of f_t and i_t)
z_t = sigma( W_z . [h_(t-1), x_t] )

# Reset gate — how much of the previous memory to ignore
r_t = sigma( W_r . [h_(t-1), x_t] )

# Candidate state, with the reset gate applied to previous memory
h~_t = tanh( W . [r_t * h_(t-1), x_t] )

# Final hidden state — linear interpolation between old memory and new candidate
h_t = (1 - z_t) * h_(t-1) + z_t * h~_t

GRU Implementation with TensorFlow/Keras

Python · TensorFlow/Keras
import pandas as pd
import tensorflow as tf
from tensorflow.keras.layers import GRU, Dense, Dropout, BatchNormalization
from tensorflow.keras import Sequential, Input

# ── GRU architecture — 2-gate design, 15-25% faster to train than LSTM ──
def build_gru_model(seq_len=60, n_features=11, n_classes=3):
    model = Sequential([
        Input(shape=(seq_len, n_features)),
        # Reset Gate + Update Gate fused — fewer params than LSTM's 3 gates
        GRU(128, return_sequences=True),
        BatchNormalization(),
        Dropout(0.25),

        # Final GRU layer outputs a single hidden state h_T
        GRU(64, return_sequences=False),
        BatchNormalization(),
        Dropout(0.20),

        Dense(32, activation='relu'),
        # 3-class softmax: 0=Down, 1=Neutral, 2=Up
        Dense(n_classes, activation='softmax')
    ])
    model.compile(
        optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3),
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model

# ── Adaptive daily retraining — fine-tunes existing weights on fresh data ──
def daily_retrain_job(model_path, new_data_path):
    df = pd.read_csv(new_data_path)
    # make_sequences(): same feature pipeline as the LSTM model (11 features, window=60)
    X, y, scaler = make_sequences(df, seq_len=60)
    model = tf.keras.models.load_model(model_path)
    # Short fine-tune (5 epochs) avoids catastrophic forgetting
    model.fit(X, y, epochs=5, batch_size=32, verbose=0)
    model.save(model_path)  # overwrite checkpoint with updated weights

Training, Prediction & Evaluation — GRU

Python · Train / Predict / Evaluate
# ── Training run + Out-of-Sample evaluation ──
from sklearn.metrics import classification_report, accuracy_score

X, y, scaler = make_sequences(df, seq_len=60)
split = int(len(X) * 0.8)  # time-ordered split — no shuffling for time series
X_train, X_test = X[:split], X[split:]
y_train, y_test = y[:split], y[split:]

model = build_gru_model(seq_len=60, n_features=11, n_classes=3)
model.fit(
    X_train, y_train,
    validation_split=0.15,
    epochs=50,
    batch_size=32,
    callbacks=[tf.keras.callbacks.EarlyStopping(patience=8, restore_best_weights=True)],
    verbose=1
)
model.save("gru_model.h5")  # checkpoint used by daily_retrain_job()

y_pred = model.predict(X_test).argmax(axis=1)
print(f"Test Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(classification_report(y_test, y_pred, target_names=['Down', 'Neutral', 'Up']))

# Latest-candle inference (single sample, what a live EA would call each tick)
latest_probs = model.predict(X_test[-1:])[0]
signal = ['SELL', 'HOLD', 'BUY'][latest_probs.argmax()]
print(f"Latest signal: {signal}  (confidence={latest_probs.max():.2%})")

Common GRU Implementation Mistakes

🛠️ Implementation Notes That Are Often Overlooked
  • Skipping Comparison with LSTM: Don't choose GRU purely for speed. On some datasets LSTM is more accurate; always benchmark both on the same data and split.
  • Scaler Data Leakage: Exactly like LSTM, the scaler must be fit on train data only, never the full dataset.
  • Missing Early Stopping: Without a proper patience value, the model either stops at a noisy validation-loss point or never stops and overfits.
  • Irregular Retraining: GRU's speed is not an excuse for irregular retraining; it needs the same scheduling discipline as LSTM.

For Which Use Cases is GRU Suitable?

Daily-Retrain Systems
Model needs updating every 24 hours with fresh data. GRU has lower training time and computational cost.
📉
Short-Term Trend Prediction
Predicting 1 to 5 candles ahead on M15–H4 timeframes with acceptable accuracy and high speed.
💻
Limited Compute Resources
Fewer GRU parameters mean lower GPU memory usage, suitable for resource-constrained servers.
🔬
Rapid Strategy Prototyping
Quickly testing different Feature Engineering ideas with shorter training time than LSTM.
🌍
Simultaneous Multi-Symbol Forex Monitoring
Low resource consumption lets GRU run across dozens of pairs simultaneously, scanning live opportunities on a low-cost server.
🖥️
Lightweight Deployment on Budget Forex VPS
With no need for a powerful GPU, GRU is a practical, cost-effective choice for EAs running on cheap broker VPS servers.
⚠️ Real-World Limitations — Know Before You Invest
  • Weaker Long-Term Dependency: For events with multi-week impact (e.g. older economic news), LSTM's three-gate structure has a theoretical edge.
  • 2–3% Accuracy Drop: In comparative backtests, GRU typically shows 2–3% lower accuracy than LSTM.
  • Sideways Markets: Like LSTM, GRU accuracy also degrades in ranging markets without a clear trend.

GRU vs LSTM and Other Deep Learning Models

FeatureGRULSTMTransformer (TFT)
Number of Gates23No Gate (Attention)
Training SpeedFast (+20%)ModerateSlow (GPU required)
Out-of-Sample Accuracy63–72%65–75%68–78%
Best ForDaily retrainingPrice prediction H1–D1Multi-step forecasting

Foundational Papers for This Model

  • Cho, K. et al. (2014) Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation Proceedings of EMNLP 2014
    View Paper
  • Chung, J., Gulcehre, C., Cho, K. & Bengio, Y. (2014) Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling NeurIPS 2014 Deep Learning Workshop
    View Paper

Ready to Build Your GRU Trading Bot?

ExpertNevees team builds a custom GRU model with daily Adaptive Retraining and connects it to MT5 via ZeroMQ.

Other ExpertNevees AI Models

Deep Learning · Classification

MLP — Multi-Layer Perceptron

MLP (Multi-Layer Perceptron) is the simplest and fastest deep learning model for trading signal classification. Its performance depends entirely on input Feature Engineering quality, with sub-0.5ms inference.

60–70%
Out-of-Sample Acc.
<0.5ms
Inference Time
20–40
Input Features
~10min
Training Time

MLP Layer Structure — From Features to Signal

Input
20-40 Features
Hidden 1
128 neurons + Dropout
Hidden 2
64 neurons + Dropout
Output
Softmax: Buy/Sell/Hold
📐
Input Layer
Input Layer · Feature Vector
Each feature (indicator or statistical) is one input neuron. The quality of these features determines the model's final accuracy.
🌀
Hidden Layers
Hidden Layers · ReLU + Dropout(0.3)
Learns non-linear combinations of features. Dropout 0.3 prevents the model from overfitting to market noise.
🎯
Output Layer
Output Layer · Softmax(3)
The Softmax function outputs a probability for each class (Buy/Sell/Hold) that always sums to 1.

Real-World MLP Performance in Signal Classification

65%
Average Accuracy
Buy/Sell/Hold Classification
30
Average Input
Features
0.5ms
Inference Latency
per Prediction
0.3
Recommended
Dropout Rate

ExpertNevees Implementation Specification

📊
Key Input Features
  • RSI(14), MACD Signal, ATR(14)/Price
  • Bollinger %B: Price position within band
  • Candle Body/Shadow Ratio
  • Volume Z-Score: Relative to 20-candle average
  • Session Indicator: Asian/London/NY
🎯
Model Output
  • Type: Buy/Sell/Hold classification with Softmax
  • Speed: Inference under 0.5ms
  • Deployment: ONNX or ZMQ Socket in EA/cBot

MLP Forward Pass & Loss Function

Mathematics · Notation
# First hidden layer: linear combination of inputs + ReLU non-linearity
a1 = ReLU( W1 . x + b1 )

# Output layer: projection into class space
z2 = W2 . a1 + b2

# Map output to a probability distribution over classes (Sell/Hold/Buy)
y_hat = softmax(z2) = exp(z2_i) / sum_j exp(z2_j)

# Cross-Entropy loss used to optimize weights via Backpropagation
Loss = - sum_i y_i * log(y_hat_i)

MLP Implementation with TensorFlow/Keras

Python · TensorFlow/Keras
import numpy as np
import tensorflow as tf
import onnxruntime as ort
import tf2onnx
from tensorflow.keras.layers import Dense, Dropout, BatchNormalization, Input
from tensorflow.keras import Sequential, Model

# ── MLP for tabular signal classification — no sequence dimension needed ──
def build_mlp_model(n_features=30, n_classes=3):
    model = Sequential([
        Input(shape=(n_features,)),
        # Wide first layer: captures non-linear interactions between indicators
        Dense(128, activation='relu'),
        BatchNormalization(),
        Dropout(0.3),

        # Narrower second layer: feature compression
        Dense(64, activation='relu'),
        BatchNormalization(),
        Dropout(0.3),

        # Output: Buy=2, Sell=0, Hold=1 (sparse integer labels)
        Dense(n_classes, activation='softmax')
    ])
    model.compile(
        optimizer='adam',
        loss='sparse_categorical_crossentropy',
        metrics=['accuracy']
    )
    return model

# ── Export to ONNX for sub-0.5ms inference inside EA via onnxruntime ──
def export_to_onnx(keras_model, output_path="mlp_signal.onnx"):
    spec = (tf.TensorSpec((None, keras_model.input_shape[-1]),
                          tf.float32, name="features"),)
    tf2onnx.convert.from_keras(keras_model, input_signature=spec,
                                output_path=output_path)

# ── ONNX inference: load once, reuse session for every tick ──
def predict_onnx(session, features: np.ndarray) -> str:
    # features shape: [1, n_features], dtype float32
    inp = {session.get_inputs()[0].name: features.astype('float32')}
    probs = session.run(None, inp)[0][0]   # [p_sell, p_hold, p_buy]
    return ["SELL", "HOLD", "BUY"][probs.argmax()]

Training, Prediction & Evaluation — MLP

Python · Train / Predict / Evaluate
# ── Training run + Out-of-Sample evaluation ──
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, accuracy_score

# Tabular features (no time dimension) — indicators computed per row
X = feature_df[feature_columns].values   # shape: [n_samples, 30]
y = feature_df['label'].values           # 0=Sell, 1=Hold, 2=Buy

# shuffle=False keeps the test set as the most recent time window
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, shuffle=False
)

model = build_mlp_model(n_features=X.shape[1], n_classes=3)
model.fit(
    X_train, y_train,
    validation_split=0.15,
    epochs=40,
    batch_size=64,
    callbacks=[tf.keras.callbacks.EarlyStopping(patience=6, restore_best_weights=True)],
    verbose=1
)

y_pred = model.predict(X_test).argmax(axis=1)
print(f"Test Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(classification_report(y_test, y_pred, target_names=['Sell', 'Hold', 'Buy']))

# Export + single-sample ONNX inference (production path used in the EA)
export_to_onnx(model, "mlp_signal.onnx")
session = ort.InferenceSession("mlp_signal.onnx")
print("Live signal:", predict_onnx(session, X_test[-1:]))

Common MLP Implementation Mistakes

🛠️ Implementation Notes That Are Often Overlooked
  • Dropping the Time Dimension: MLP has no inherent sense of order; without manually engineered lag features (e.g. returns from 1–10 candles back), the model loses recent-trend information.
  • Unscaled Features: Fully-connected networks are sensitive to differing feature scales (e.g. RSI 0–100 vs returns around 0.001). Without StandardScaler, large-magnitude features dominate training.
  • Too Many Layers for the Data Size: A deep, wide network trained on only a few thousand samples typically overfits badly; Dropout and L2 regularization are essential.
  • Classification Accuracy ≠ Profit: 65% signal accuracy without accounting for spread, commission, and slippage cannot represent real trading outcomes.

For Which Use Cases is MLP Suitable?

Ultra-Fast Real-time Deployment
With sub-0.5ms inference, ideal for scalping and latency-sensitive systems.
🧩
Signal Classification with Manual Features
When strong Feature Engineering is already prepared, MLP offers the best speed-to-accuracy ratio.
🛠️
Initial Strategy Prototyping
Quickly testing different indicator combinations before moving to more complex Sequential models.
📦
Lightweight EA/cBot Deployment
Small model size and high inference speed make it ideal for direct EA deployment via ONNX.
🧠
Meta-Model in a Stacking Ensemble
As the final Ensemble layer, MLP combines the outputs of Random Forest, XGBoost, and LSTM into one sharper final signal.
⚔️
Tick-Level Scalping Filter
With sub-0.5ms inference latency, MLP can decide on every incoming tick — ideal for scalping and lightweight HFT strategies.
⚠️ Real-World Limitations — Know Before You Invest
  • No Sequential Understanding: Cannot recognize sequential patterns (e.g., three consecutive bullish candles) without manual features.
  • Heavy Feature Engineering Dependency: Without quality input, accuracy quickly approaches random-chance levels.
  • Lower Accuracy Ceiling: Compared to Sequential models, MLP's accuracy ceiling is typically 5–8% lower.

MLP vs Other Classification Models

FeatureMLPRandom ForestLSTM
Inference Speed<0.5ms~1ms~3-5ms
Sequential UnderstandingNoneNoneStrong
InterpretabilityWeakFeature ImportanceWeak
Out-of-Sample Accuracy60–70%62–70%65–75%

Foundational Papers for This Model

  • Rumelhart, D. E., Hinton, G. E. & Williams, R. J. (1986) Learning Representations by Back-Propagating Errors Nature, 323, 533–536
    View Paper

Ready to Build Your MLP Trading Bot?

ExpertNevees team designs strong Feature Engineering, trains the MLP model, and connects it to your EA/cBot via ONNX or ZeroMQ.

Other ExpertNevees AI Models

Machine Learning · Ensemble

Random Forest — Trend Classification by Ensemble Voting

Random Forest is a combination of 100–500 independent decision trees that, via majority voting, deliver the most stable trend classification model. Key advantage: interpretable Feature Importance.

62–70%
Out-of-Sample Acc.
300
Decision Trees
1.2+
Sharpe Ratio
in Trend
CPU
No GPU Needed

How Does Random Forest Decide? — Majority Voting

🌳
Up
🌳
Down
🌳
Up
🌳
Up
🌳
Down
🌳
Up
🌳
Up
🌳
Down
Final Vote: 5 of 8 Trees → Buy Signal Issued
🎲
Bootstrap Sampling
Random Sampling with Replacement
Each tree trains on a different random sample of the data, ensuring diversity between trees.
✂️
Feature Randomness
Random Feature Subset per Split
At each tree split, only a random subset of features is considered, reducing correlation between trees.
🗳️
Majority Voting
Majority Vote Aggregation
The final output is the class voted by the most trees — drastically reducing model variance.

Feature Importance — Unique Advantage on XAUUSD

ATR(14)
18.3%
Volume Z-Score
15.7%
RSI(14)
12.4%
MACD Signal
9.8%

After training on XAUUSD data, you can see which indicator has the most predictive impact — invaluable for simplifying the strategy.

ExpertNevees Implementation Specification

📊
Input Data
  • Technical Features: ATR, RSI, MACD, Volume Z-Score
  • n_estimators: 300-500 trees
  • max_depth: 8-12 to prevent overfitting
  • Walk-Forward: 6-month windows (4 Train / 2 Test)
🎯
Model Output
  • Type: Up/Down/Neutral classification
  • Feature Importance: Ranking of each indicator's impact
  • Typical Accuracy: 62-70% Out-of-Sample

Gini Impurity & Ensemble Voting

Mathematics · Notation
# Gini impurity — used to pick the best split at each tree node
Gini(t) = 1 - sum_c ( p_c )^2      # p_c = fraction of class-c samples in node t

# Impurity reduction from a split — the tree picks the split that maximizes this
Delta_Gini = Gini(parent) - ( n_L/n * Gini(L) + n_R/n * Gini(R) )

# Each tree trains on a Bootstrap sample with a random feature subset (m ~ sqrt(p))
D_b = Bootstrap(D),   features_b = RandomSubset(features, m)

# Final prediction: majority vote across B independent trees
y_hat = mode( T_1(x), T_2(x), ..., T_B(x) )

Random Forest Implementation with Scikit-learn

Python · Scikit-learn
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import TimeSeriesSplit
import matplotlib.pyplot as plt

# ── Conservative hyperparameters tuned for noisy financial tabular data ──
model = RandomForestClassifier(
    n_estimators=300,        # 300 trees: accuracy plateaus beyond ~400
    max_depth=10,            # shallow depth prevents memorising tick noise
    min_samples_leaf=20,    # at least 20 samples per leaf — key overfitting guard
    max_features='sqrt',    # random feature subsetting ensures tree diversity
    class_weight='balanced', # compensates for imbalanced Up/Neutral/Down classes
    n_jobs=-1,              # parallel training across all CPU cores
    random_state=42
)

# ── Purged Walk-Forward Validation — critical: no future leakage in splits ──
tscv = TimeSeriesSplit(n_splits=5, gap=5)  # gap=5 avoids look-ahead bias
oos_scores = []
for fold, (train_idx, test_idx) in enumerate(tscv.split(X), 1):
    model.fit(X[train_idx], y[train_idx])
    score = model.score(X[test_idx], y[test_idx])
    oos_scores.append(score)
    print(f"Fold {fold} OOS Accuracy: {score:.2%}")
print(f"Mean OOS Accuracy: {np.mean(oos_scores):.2%} ± {np.std(oos_scores):.2%}")

# ── Feature Importance — reveals which indicators drive prediction ──
importances = sorted(
    zip(feature_names, model.feature_importances_),
    key=lambda x: x[1], reverse=True
)
for name, score in importances[:10]:
    print(f"  {name:<20} {score:.4f}")

Final Evaluation & Prediction — Random Forest

Python · Train / Predict / Evaluate
# ── Final model fit + holdout evaluation + single-sample prediction ──
from sklearn.metrics import classification_report, accuracy_score

# Fit on the full Walk-Forward training window, then evaluate on the final holdout
train_idx, test_idx = list(tscv.split(X))[-1]  # most recent fold = closest to live data
model.fit(X[train_idx], y[train_idx])
y_pred = model.predict(X[test_idx])

print(f"Holdout Accuracy: {accuracy_score(y[test_idx], y_pred):.4f}")
print(classification_report(y[test_idx], y_pred, target_names=['Down', 'Neutral', 'Up']))

# Prediction with class probabilities for the newest available candle
latest_probs = model.predict_proba(X[-1:])[0]
signal = ['Down', 'Neutral', 'Up'][latest_probs.argmax()]
print(f"Latest signal: {signal}  (confidence={latest_probs.max():.2%})")

Common Random Forest Implementation Mistakes

🛠️ Implementation Notes That Are Often Overlooked
  • Wrong Cross-Validation: Using plain KFold instead of TimeSeriesSplit lets future data leak into training folds — reported accuracy will be falsely inflated.
  • Misreading Feature Importance: Among correlated features (e.g. several similar indicators), importance gets split unevenly between them and can be misleading.
  • Unbounded Tree Depth: Without limiting max_depth and min_samples_leaf, individual trees can memorize noise in the training data.
  • Ignoring Transaction Costs in Labeling: If Buy/Sell labels are based purely on return sign (no threshold), the model also signals on tiny, worthless fluctuations.

For Which Use Cases is Random Forest Suitable?

🛡️
Most Stable Model for Volatile Markets
Combining hundreds of trees with different Bootstrap samples drastically reduces variance and minimizes overfitting.
🔍
Feature Selection for Strategy
Feature Importance helps identify truly effective indicators and simplifies the strategy.
📊
Trend Classification with Limited Data
Compared to deep learning models, performs acceptably even with less data.
💻
No GPU Required
Training and inference run entirely on CPU, suitable for deployment on simple servers or VPS.
📏
Baseline Model for Fair Model Comparison
Before investing in more complex, costlier models, Random Forest provides an honest baseline to measure their real added value.
Reliable Walk-Forward Validation for Client Reporting
Random Forest’s strong resistance to overfitting makes it a safe choice for transparent, defensible performance reports to clients.
⚠️ Real-World Limitations — Know Before You Invest
  • No Sequential Understanding: Treats each sample independently and cannot grasp dependencies between consecutive candles.
  • Poor Extrapolation: Performance degrades in entirely new market conditions not seen in training data.
  • Accuracy Ceiling: In noisy financial data, rarely exceeds 70% Out-of-Sample.

Random Forest vs Other Ensemble Models

FeatureRandom ForestXGBoostMLP
Training MethodParallel (Faster)SequentialBackpropagation
Overfitting RiskLowModerate (needs tuning)Moderate
InterpretabilityFeature ImportanceSHAP ValuesWeak
Out-of-Sample Accuracy62–70%68–74%60–70%

Foundational Papers for This Model

Ready to Build Your Random Forest System?

ExpertNevees team trains a Random Forest model with full Walk-Forward Validation and delivers a Feature Importance report.

Other ExpertNevees AI Models

Machine Learning · Boosting

XGBoost — Champion of Financial Classification

XGBoost is an ensemble algorithm based on decision trees where each new tree corrects the errors of the previous one. It has won the most prizes in Kaggle financial prediction competitions.

68–74%
Out-of-Sample Acc.
+1500
Samples Required
500–2k
Number of Trees
2–8ms
REST/ZMQ Latency

How Does XGBoost Learn? — Sequential Error Correction

Tree 1
Error: 35%
Tree 2
Error: 24%
Tree 3
Error: 17%
...
...
Tree 800
Error: 26%
📉
Gradient Boosting
Residual Error Fitting
Each new tree trains on the residual error of previous trees, not the original data.
⚖️
Regularization
L1/L2 + Tree Pruning
Combining L1/L2 regularization with tree pruning ensures protection against overfitting to market noise.
🎯
Learning Rate
Shrinkage Parameter (η)
A small learning rate (0.01–0.1) makes the model learn slowly and overfit less, at the cost of needing more trees.

Key Hyperparameters for Financial Data

4-6
Recommended
max_depth
0.01-0.1
Recommended
learning_rate
500-2000
Recommended
n_estimators
0.7-0.9
Recommended
subsample

ExpertNevees Implementation Specification

📊
Input Data
  • Features: 40 technical and statistical features
  • Tuning: GridSearch or Optuna
  • Min Samples: 1500 training records
🎯
Output & Integration
  • Model Storage: joblib pickle format
  • Serving: FastAPI REST endpoint
  • Latency: 2-8ms via HTTP

XGBoost Regularized Objective

Mathematics · Notation
# Overall objective: prediction loss + model-complexity penalty
Obj = sum_i l(y_i, y_hat_i) + sum_k Omega(f_k)

# Regularization term — penalizes number of leaves (T) and leaf-weight magnitude (w)
Omega(f) = gamma * T + (1/2) * lambda * ||w||^2

# Second-order Taylor approximation of the loss around the current prediction
l(y, y_hat + f) ~= l(y, y_hat) + g*f + (1/2)*h*f^2      # g,h = first and second derivatives

# Split gain — the criterion used to choose the best split at each boosting step
Gain = (1/2) * [ G_L^2/(H_L+lambda) + G_R^2/(H_R+lambda) - (G_L+G_R)^2/(H_L+H_R+lambda) ] - gamma

XGBoost Implementation with Optuna Tuning

Python · XGBoost + Optuna
import xgboost as xgb
import optuna
from sklearn.model_selection import TimeSeriesSplit
import numpy as np

# ── Optuna objective: Walk-Forward CV inside each trial prevents leakage ──
def objective(trial):
    params = {
        'max_depth':     trial.suggest_int('max_depth', 4, 6),
        'learning_rate': trial.suggest_float('learning_rate', 1e-2, 0.1, log=True),
        'n_estimators':  trial.suggest_int('n_estimators', 500, 2000),
        'subsample':     trial.suggest_float('subsample', 0.7, 0.9),
        'colsample_bytree': trial.suggest_float('colsample_bytree', 0.6, 0.9),
        'reg_lambda':    trial.suggest_float('reg_lambda', 1, 10), # L2 regularisation
        'objective': 'multi:softprob', 'num_class': 3,
        'eval_metric': 'mlogloss', 'n_jobs': -1,
    }
    # TimeSeriesSplit ensures no look-ahead bias across folds
    tscv = TimeSeriesSplit(n_splits=4, gap=5)
    oos_scores = []
    for tr, te in tscv.split(X):
        m = xgb.XGBClassifier(**params)
        m.fit(X[tr], y[tr], eval_set=[(X[te], y[te])],
              early_stopping_rounds=30, verbose=False)
        oos_scores.append(m.score(X[te], y[te]))
    return np.mean(oos_scores)

study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=100, show_progress_bar=True)

# ── Serve best model via FastAPI — EA calls POST /predict per tick ──
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()
best_model = xgb.XGBClassifier(**study.best_params)
best_model.fit(X, y)

class Features(BaseModel):
    values: list[float]

@app.post("/predict")
def predict(req: Features):
    probs = best_model.predict_proba([req.values])[0]
    return {"signal": ["SELL", "HOLD", "BUY"][probs.argmax()],
            "confidence": float(probs.max())}

Tuned Model Evaluation & Prediction — XGBoost

Python · Train / Predict / Evaluate
# ── Holdout evaluation of the Optuna-tuned model + single-sample prediction ──
from sklearn.metrics import classification_report, accuracy_score
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)

final_model = xgb.XGBClassifier(**study.best_params)
final_model.fit(X_train, y_train)

y_pred = final_model.predict(X_test)
print(f"Test Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(classification_report(y_test, y_pred, target_names=['Sell', 'Hold', 'Buy']))

# Single-sample probability prediction — same call used inside the /predict endpoint
probs = final_model.predict_proba(X_test[-1:])[0]
print(f"Latest signal: {['SELL','HOLD','BUY'][probs.argmax()]}  (confidence={probs.max():.2%})")

Common XGBoost Implementation Mistakes

🛠️ Implementation Notes That Are Often Overlooked
  • Leakage in Indicator Calculation: Computing indicators like SMA or RSI over a window that includes the current/future candle (instead of a proper shift) leaks data.
  • Overfitting to Validation via Optuna: Running hundreds of trials against the same validation set overfits the hyperparameters to it; always keep a final, untouched holdout test set.
  • No early_stopping_rounds: A fixed n_estimators without early stopping causes either overfitting or unnecessarily long training.
  • Not Benchmarked Against a Simpler Model: If a plain Random Forest gets close to XGBoost's accuracy, the added complexity and tuning time isn't economically justified.

For Which Use Cases is XGBoost Suitable?

🏆
Highest Accuracy on Small Tabular Data
With fewer than 5,000 samples, XGBoost typically outperforms deep learning models.
Low-Latency Deployment via REST
Easy integration with EA or cBot via FastAPI REST endpoint with 2-8ms latency.
🔧
Strategies with Tabular Features
Suited for combining technical, fundamental, and sentiment indicators into one matrix.
📈
Financial Competitions & Benchmarks
Industry standard for Kaggle financial prediction competitions and academic benchmarks.
🏅
Multi-Asset Opportunity Ranking & Selection
Simultaneously scoring dozens of forex and crypto symbols to automatically pick the best daily trading opportunities.
🔎
Signal Interpretation with SHAP for Transparency
Using SHAP Values to clearly explain why XGBoost issued a specific signal — ideal for professional client reporting.
⚠️ Real-World Limitations — Know Before You Invest
  • Needs Precise Tuning: Unlike Random Forest, XGBoost is more sensitive to precise hyperparameter tuning.
  • Higher Overfitting Risk: Sequential training without Early Stopping can overfit quickly.
  • No Sequential Understanding: Like Random Forest, doesn't natively understand dependencies between consecutive candles.

XGBoost vs Random Forest and LSTM

FeatureXGBoostRandom ForestLSTM
Out-of-Sample Accuracy68–74%62–70%65–75%
Min Data Required+1,500 samples+2,000 samples+5,000 candles
Tuning RequiredHighLowHigh
GPU RequiredNoNoPreferred

Foundational Papers for This Model

  • Chen, T. & Guestrin, C. (2016) XGBoost: A Scalable Tree Boosting System Proceedings of the 22nd ACM SIGKDD International Conference on Knowledge Discovery and Data Mining (KDD '16)
    View Paper
  • Liu, J. (2024) Predicting Chinese Stock Market Using XGBoost Multi-Objective Optimization with Optimal Weighting PeerJ Computer Science, 10, e1931
    View Paper

Ready to Build Your XGBoost System?

ExpertNevees team optimizes the XGBoost model with Optuna Tuning and connects it to your EA/cBot via FastAPI or ZeroMQ.

Other ExpertNevees AI Models

Machine Learning · Alpha Factor

Linear Factor — Extracting Alpha Signals from Markets

A Linear Factor Model is a method (Alpha Factor Engineering) that converts raw market data into return-predictive signals using Ridge/Lasso Regression — the most critical step in any trading ML pipeline.

IC > 0.05
Valuable Factor
5
Main Factor Categories
Alphalens
Evaluation Tool
CPU
No GPU Needed

Alpha Factor Categories in Algorithmic Trading

Momentum
Return-1M
Return-3M
RSI-Momentum
Value
P/E Ratio
EV/EBITDA
Dividend Yield
Quality
ROE
Debt/Equity
Earnings Growth
Volatility
ATR/Price
Realized Vol
Beta
Sentiment
Short Interest
Analyst Revisions

Factor Evaluation with Alphalens

📈
Information Coefficient
IC = corr(Factor, Future Return)
Correlation between factor value and future return. IC above 0.05 indicates a factor with predictive value.
📐
Information Ratio
IR = IC / Std(IC)
Shows factor stability over time. A factor with high but unstable IC is less reliable.
🔄
Turnover
Implicit Transaction Cost
Measures implicit transaction cost of the factor — high-turnover factors may lose profit to trading fees.

ExpertNevees Implementation Specification

📊
Input Data
  • Market Data: OHLCV, volume, order book
  • Alternative Data: Sentiment, satellite images, XBRL
  • Combination: Linear or ML-based weighting (XGBoost/RF)
🎯
Model Output
  • Factor Score: Final combined score per asset
  • Ranking: Assets ranked by Alpha Score
  • Integration: Input for classification models or HRP

Multi-Factor Linear Regression

Mathematics · Notation
# Fama-French style factor model: excess return as a linear combination of risk factors
r_i - r_f = alpha_i + beta_1*MKT + beta_2*SMB + beta_3*HML + epsilon_i

# Ridge coefficient estimate with an L2 penalty to control multicollinearity between factors
beta_hat = argmin_beta  ||y - X*beta||^2 + lambda * ||beta||^2

# Information Coefficient — rank correlation between predicted and actual returns
IC = corr( rank(y_hat), rank(y) )

Alpha Factor Implementation with Alphalens

Python · Alphalens + Ridge Regression
import pandas as pd
import numpy as np
from sklearn.linear_model import Ridge
from sklearn.preprocessing import RobustScaler
import alphalens as al

# ── Build composite alpha factor from three orthogonal signal families ──
def build_factor_score(df: pd.DataFrame) -> pd.DataFrame:
    # Momentum: 20-day price return — buy recent winners
    df['momentum']   = df['close'].pct_change(20)
    # Volatility: normalised ATR — penalise high-noise assets
    df['volatility']  = df['atr_14'] / df['close']
    # Value: inverse P/E → lower P/E is more attractive
    df['value']       = -df['pe_ratio']

    # Cross-sectionally scale each factor (RobustScaler handles outliers)
    scaler = RobustScaler()
    factors = scaler.fit_transform(df[['momentum', 'volatility', 'value']])

    # Ridge regression learns optimal factor weights from realised forward returns
    model = Ridge(alpha=1.0)
    model.fit(factors, df['forward_return'])
    df['factor_score'] = model.predict(factors)
    return df

# ── Evaluate with Alphalens — IC > 0.05 signals a statistically useful factor ──
factor_data = al.utils.get_clean_factor_and_forward_returns(
    factor=df['factor_score'],
    prices=price_data,
    periods=(1, 5, 10)   # 1-day, 5-day, 10-day forward return horizons
)
ic = al.performance.factor_information_coefficient(factor_data)
print(f"Mean IC: {ic.mean():.3f}   IR: {(ic.mean()/ic.std()):.2f}")

Evaluation & Asset Ranking Prediction — Factor Model

Python · Train / Predict / Evaluate
# ── Train/test split + regression evaluation + latest cross-sectional prediction ──
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score

X = factors  # scaled [momentum, volatility, value] matrix built above
y = df['forward_return'].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)

model = Ridge(alpha=1.0)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)

print(f"Test MSE: {mean_squared_error(y_test, y_pred):.6f}")
print(f"Test R^2: {r2_score(y_test, y_pred):.4f}")

# Latest cross-section: rank assets by predicted forward return (long top decile)
latest_scores = model.predict(X[-len(symbols):])
ranked = sorted(zip(symbols, latest_scores), key=lambda x: x[1], reverse=True)
print("Top ranked assets:", ranked[:5])

Common Factor Model Implementation Mistakes

🛠️ Implementation Notes That Are Often Overlooked
  • Look-Ahead Bias in Fundamental Data: Using EPS or financials keyed to the report date (not the actual public release date) lets the model use information that wasn't available at that time.
  • Multicollinearity Between Factors: Correlated factors (e.g. short- and long-term momentum) destabilize Ridge/regression coefficients; check VIF before modeling.
  • Assuming Static Factor Relationships: The relationship between a factor and forward returns shifts across time and market regimes; the model should be refit on a rolling basis.
  • Confusing IC with Classification Accuracy: A low Information Coefficient like 0.03–0.08 is normal for a factor model; don't expect classification-style accuracy from it.

For Which Use Cases is Alpha Factor Engineering Suitable?

🧱
Foundation of Any Trading ML Pipeline
Before any classification or prediction model, the quality of input features determines success.
📊
Factor Investing Strategies
Asset selection based on Momentum, Value, and Quality factor rankings.
🛰️
Leveraging Alternative Data
Social media sentiment and satellite images can provide stronger alpha than traditional indicators.
🔗
Input for More Complex Models
The Factor Score output becomes a standard input feature for XGBoost, Random Forest, or LSTM.
🏗️
Backbone for Intelligent Portfolio Allocation
The Factor Score output feeds directly into capital-allocation algorithms like HRP or Mean-Variance Optimization.
📟
Live Dashboard for Factor Risk Exposure Monitoring
Real-time tracking of portfolio exposure to Momentum, Value, and Volatility factors for proactive risk management.
⚠️ Real-World Limitations — Know Before You Invest
  • Factors Decay: As a factor becomes widely known, its predictive power (Alpha Decay) typically diminishes.
  • Limited Linear Dependency: Linear Factor models don't capture complex non-linear relationships as well as tree-based or deep learning models.
  • Data Snooping: Excessive testing of different factors on the same historical data increases the risk of spurious patterns.

Linear Factor vs More Advanced Combination Methods

FeatureLinear FactorXGBoostAutoencoder
Relationship TypeLinearNon-linearNon-linear Latent
InterpretabilityVery HighModerate (SHAP)Low
Computation SpeedVery FastFastModerate

Foundational Papers for This Model

  • Fama, E. F. & French, K. R. (1993) Common Risk Factors in the Returns on Stocks and Bonds Journal of Financial Economics, 33(1), 3–56
    View Paper

Ready to Build Your Factor-Based Strategy?

ExpertNevees team extracts alpha factors, evaluates them with Alphalens, and prepares them as input for your ML strategies.

Other ExpertNevees AI Models

Deep Learning · Attention

Transformer — Most Advanced Financial Prediction Model

Developed by Google DeepMind, the Temporal Fusion Transformer (TFT) combines Attention Mechanism, LSTM, and Variable Selection Network. It views all time points in parallel and uses Self-Attention to identify the most critical moments.

Advanced
Out-of-Sample Acc.
+10k
Candles Required
4–12h
GPU Training Time
8GB+
VRAM Required

Why Transformer Outperforms LSTM

Each column represents a time step (candle); color intensity shows Self-Attention weight — the Transformer simultaneously attends to an FOMC meeting 3 weeks ago (bright column) and today's candle.

Parallel Processing
Parallel Sequence Processing
Unlike LSTM's sequential processing, Transformer views all time points simultaneously and discovers long-range dependencies without gradient degradation.
🧬
Variable Selection Network
Dynamic Feature Weighting
Automatically determines which feature (price, volume, news) matters more at each moment and weights it accordingly.
🔮
Multi-Horizon Forecasting
5/10/20-Step Forecast
Can predict 5, 10, or 20 candles ahead simultaneously, not just the next single step like standard LSTM.

Real Cost of Running Transformer

8GB+
Minimum
GPU VRAM
10k+
Candles Needed
for Proper Training
4-12h
Training Time
on GPU
5/10/20
Multi-step
Horizon (candles)

ExpertNevees Implementation Specification

📊
Multi-Source Input Data
  • Price + Volume: Multi-timeframe OHLCV
  • Economic News: Economic calendar + Sentiment Score
  • Static Variables: Symbol, sector, asset type
🎯
Model Output
  • Multi-step Forecast: 5, 10, or 20 candles ahead
  • Regime Detection: Trending/Ranging/High Vol
  • Confidence Interval: Quantile Forecasts (P10/P50/P90)

Self-Attention & Multi-Head Formula

Mathematics · Notation
# Scaled Dot-Product Attention
Attention(Q, K, V) = softmax( Q * K^T / sqrt(d_k) ) * V

# Combine multiple parallel attention heads to learn different relationships at once
MultiHead(Q,K,V) = Concat(head_1, ..., head_h) * W_O ,   head_i = Attention(Q*W_i^Q, K*W_i^K, V*W_i^V)

# Pinball (Quantile) loss for probabilistic P10/P50/P90 forecasting
L_q(y, y_hat) = max( q*(y - y_hat), (q-1)*(y - y_hat) )

TFT Implementation with PyTorch Forecasting

Python · PyTorch Forecasting
import pytorch_lightning as pl
from pytorch_forecasting import TemporalFusionTransformer, TimeSeriesDataSet
from pytorch_forecasting.metrics import QuantileLoss

# ── Multi-source TimeSeriesDataSet ──
# known_reals: features available at prediction time (e.g. calendar, macro)
# unknown_reals: features only observed up to the encoder window
training = TimeSeriesDataSet(
    df,
    time_idx="time_idx",
    target="close",
    group_ids=["symbol"],               # one group per trading instrument
    max_encoder_length=60,              # 60-candle lookback window
    max_prediction_length=10,           # forecast 10 steps ahead simultaneously
    time_varying_known_reals=[          # available during the decoder phase
        "rsi", "atr", "sentiment_score",
        "hour_of_day", "day_of_week"    # session / seasonality signals
    ],
    time_varying_unknown_reals=[        # observed only in the encoder
        "close", "volume", "spread"
    ],
    static_categoricals=["asset_class"], # FX, Crypto, Equity — handled by VSN
    target_normalizer="softplus",       # ensures non-negative price targets
)

# ── Build TFT with Quantile Loss for probabilistic forecasting ──
# quantiles=[0.1, 0.5, 0.9] → P10/P50/P90 confidence bands per candle
tft = TemporalFusionTransformer.from_dataset(
    training,
    hidden_size=64,
    attention_head_size=4,              # Multi-Head Self-Attention heads
    dropout=0.2,
    hidden_continuous_size=16,
    loss=QuantileLoss(quantiles=[0.1, 0.5, 0.9]),
    log_interval=10,
    learning_rate=3e-3,
)

# ── Train with early stopping to prevent overfitting ──
trainer = pl.Trainer(max_epochs=30, gradient_clip_val=0.1,
                     accelerator="gpu", devices=1)
trainer.fit(tft, train_dataloaders=train_loader, val_dataloaders=val_loader)

# ── Probabilistic prediction: returns P10/P50/P90 bands for 10 candles ──
raw_predictions = tft.predict(val_loader, mode="raw", return_x=True)
p10, p50, p90 = raw_predictions.output.prediction.unbind(dim=-1)

Forecast Accuracy Evaluation — Transformer

Python · Train / Predict / Evaluate
# ── Evaluate probabilistic forecasts + single-series prediction ──
from pytorch_forecasting.metrics import QuantileLoss

# Quantile loss on the validation set — lower is better; compares P10/P50/P90 to actuals
val_loss = trainer.validate(tft, dataloaders=val_loader)[0]
print(f"Validation Quantile Loss: {val_loss['val_loss']:.4f}")

# Point forecast accuracy using the P50 (median) quantile as the prediction
actuals = torch.cat([y[0] for x, y in iter(val_loader)])
mae = (p50 - actuals).abs().mean()
print(f"MAE (P50 vs actual close): {mae:.5f}")

# Forecast the next 10 candles for a single symbol from the latest known window
new_prediction = tft.predict(new_raw_data, mode="quantiles",
                              trainer_kwargs=dict(accelerator="cpu"))
p10, p50, p90 = new_prediction[0].unbind(dim=-1)
print(f"Next-candle forecast: P10={p10[0]:.5f}  P50={p50[0]:.5f}  P90={p90[0]:.5f}")

Common Transformer Implementation Mistakes

🛠️ Implementation Notes That Are Often Overlooked
  • Insufficient Data: Due to its high parameter count, a Transformer overfits badly with fewer than several thousand sequences; use LSTM or GRU for smaller datasets.
  • Mixing Up Static vs. Time-Varying Covariates: If a static feature (like the symbol) is mistakenly defined as time-varying, the model learns the wrong pattern.
  • Interpreting Attention Weights as Causal: High attention weights don't necessarily mean a causal relationship; they only show where the model focused, not why.
  • Skipping Quantile Calibration Checks: If the P10–P90 band doesn't actually cover roughly 80% of real outcomes, the model's uncertainty estimates are not calibrated.

For Which Use Cases is Transformer Suitable?

📅
Simultaneous Multi-Step Forecasting
Forecasting 5, 10, or 20 candles ahead in one pass, suitable for planning longer-term positions.
📰
Combining Price and News Multi-Source
Simultaneously combining price, volume, economic news, and sentiment in a single model.
🌪️
Market Regime Detection
Detecting Trending vs Ranging vs High Volatility to dynamically switch strategy.
💼
Projects with Sufficient Budget and Data
Best choice for projects that can cover GPU costs and collecting +10,000 candles.
🌍
Multi-Symbol Forecasting with a Single Shared Model
By defining multiple assets as group_ids in TimeSeriesDataSet, a single TFT model can forecast several instruments at once.
🎯
Dynamic Position Sizing from Confidence Bands
Using the model’s P10/P50/P90 output bands to intelligently scale position size relative to forecast uncertainty.
⚠️ Real-World Limitations — Know Before You Invest
  • Impractical Without GPU: Training and inference on CPU is impractical for real projects.
  • Highest Overfitting Risk: The most powerful model, but without rigorous Cross-Validation, In-Sample results will be entirely misleading.
  • High Data Requirement: With fewer than 10,000 candles, the model struggles to learn meaningful patterns.

Transformer vs Other Deep Learning Models

FeatureTransformerLSTMAttention
Time ProcessingParallelSequentialDepends on combo
Hardware RequirementGPU 8GB+ requiredGPU optionalDepends on base model
Multi-step ForecastNativeNeeds configNeeds config
Data Required+10,000 candles+5,000 candles+5,000 candles

Foundational Papers for This Model

  • Vaswani, A. et al. (2017) Attention Is All You Need Advances in Neural Information Processing Systems 30 (NeurIPS 2017)
    View Paper
  • Lim, B., Arık, S. Ö., Loeff, N. & Pfister, T. (2021) Temporal Fusion Transformers for Interpretable Multi-horizon Time Series Forecasting International Journal of Forecasting, 37(4), 1748–1764
    View Paper

Ready to Build Your Transformer Trading Bot?

ExpertNevees team implements the TFT model with rigorous Cross-Validation and multi-source data, connecting it to MT5 via ZeroMQ.

Other ExpertNevees AI Models

Deep Learning · Attention

Attention — Focus-Driven Trading Models

Attention Mechanism enables the model to focus precisely on the most significant candles rather than weighting all time steps equally — much like a trader paying special attention to key candles. In one published study (Zhang et al., 2019) across the Russell 2000, DJIA and Nasdaq indices, adding attention to LSTM reduced prediction error (MAPE) by roughly 22–34% versus plain LSTM. The Attention Weight Heatmap reveals exactly why and where the model focused.

3
Main Attention Types
O(n²)
Memory Complexity
High
Interpretability
+LSTM
Best Combo

Types of Attention in Algorithmic Trading

🔁
Self-Attention
Each time step is compared with all other steps. Discovers long-range dependencies between distant candles without gradient degradation.
🔗
Cross-Attention
Relationship between two different data sources, e.g. price and economic news. The model learns which news affected which price range.
🧩
Multi-Head Attention
Multiple parallel Attention Heads, each focused on a different aspect: trend, volatility, news reaction. Outputs combined.
⏱️
Temporal Attention on LSTM
Attention layer added on top of LSTM output, telling the model which hidden state matters most — result: better interpretability and accuracy.

Attention Weight Visualization — CPI Example

When the CPI rate is announced (bright column), Cross-Attention assigns high weight to that time step. The model learns gold's reaction to CPI differs from NFP. This Heat Map is valuable for verifying meaningful learning.

ExpertNevees Implementation Specification

📊
Input Data
  • LSTM/GRU Output: Hidden states per time step
  • Second Source (Cross): Sentiment or economic calendar
  • Number of Heads: 4-8 parallel heads
🎯
Model Output
  • Context Vector: Weighted combination of hidden states
  • Attention Weights: Visualizable as Heat Map
  • Final Signal: From Context Vector to Softmax

Attention Formula over LSTM Outputs

Mathematics · Notation
# Raw attention score for each time step relative to the context vector
e_t = v^T * tanh( W_h * h_t + b )

# Normalize scores into a probability distribution over all time steps (60 candles)
alpha_t = exp(e_t) / sum_j exp(e_j)

# Final context vector: weighted average of all hidden states based on importance
context = sum_t alpha_t * h_t

Temporal Attention Implementation on LSTM

Python · TensorFlow/Keras
import tensorflow as tf
from tensorflow.keras.layers import LSTM, Dense, Dropout, Layer, Input
from tensorflow.keras import Model

# ── Custom Temporal Attention layer — additive (Bahdanau-style) ──
# Computes a scalar alignment score for each time step, then softmax-normalises
class TemporalAttention(Layer):
    def __init__(self, units=64, **kwargs):
        super().__init__(**kwargs)
        self.W = Dense(units)   # projects LSTM hidden states
        self.V = Dense(1)      # collapses to a single scalar score

    def call(self, lstm_outputs, return_weights=False):
        # lstm_outputs shape: [batch, T, units]
        scores = self.V(tf.nn.tanh(self.W(lstm_outputs)))  # [batch, T, 1]
        weights = tf.nn.softmax(scores, axis=1)            # attention distribution over time
        # Weighted sum: high-weight time steps contribute more to prediction
        context = tf.reduce_sum(weights * lstm_outputs, axis=1)  # [batch, units]
        if return_weights:
            return context, weights  # weights → Attention Heatmap visualisation
        return context

# ── Build LSTM + Temporal Attention model using Functional API ──
inp = Input(shape=(60, 11))             # [Batch, 60 candles, 11 features]
x = LSTM(128, return_sequences=True)(inp)  # full sequence → all hidden states h_1 … h_60
x = Dropout(0.25)(x)
context = TemporalAttention(64)(x)         # squeeze sequence → context vector
x = Dropout(0.2)(context)
x = Dense(32, activation='relu')(x)
out = Dense(3, activation='softmax')(x)  # Up / Neutral / Down

model = Model(inp, out)
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

Training, Evaluation & Attention-Weight Analysis

Python · Train / Predict / Evaluate
# ── Training run + evaluation + attention-weight inspection ──
from sklearn.metrics import classification_report, accuracy_score

X, y, scaler = make_sequences(df, seq_len=60)
split = int(len(X) * 0.8)
X_train, X_test = X[:split], X[split:]
y_train, y_test = y[:split], y[split:]

model.fit(
    X_train, y_train,
    validation_split=0.15,
    epochs=50,
    batch_size=32,
    callbacks=[tf.keras.callbacks.EarlyStopping(patience=8, restore_best_weights=True)],
    verbose=1
)

y_pred = model.predict(X_test).argmax(axis=1)
print(f"Test Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(classification_report(y_test, y_pred, target_names=['Down', 'Neutral', 'Up']))

# Extract per-candle attention weights for the latest window — powers the Heatmap UI
attention_layer = TemporalAttention(64)
context, weights = attention_layer(model.layers[1](X_test[-1:]), return_weights=True)
top_candles = weights[0, :, 0].numpy().argsort()[-5:][::-1]
print("Most influential candles (index from window start):", top_candles)

Common Attention Interpretation Mistakes

🛠️ Implementation Notes That Are Often Overlooked
  • Treating Attention Weights as Causal Proof: Candles that receive high weight are not necessarily the cause of the price move; the heatmap is an interpretation tool, not proof.
  • Not Benchmarked Against Plain LSTM: Adding attention doesn't always improve accuracy. Without a direct A/B test against a plain LSTM, the extra complexity may be wasted.
  • Drawing Conclusions from a Single Sample: The attention pattern on one prediction can be noise; valid conclusions require averaging over many samples.
  • Prediction Lag and Weakness in Extreme Conditions: Even the AT-LSTM authors (Zhang et al., 2019) reported that despite high accuracy, their model shows a time lag in prediction and is not sufficiently sensitive during extreme/highly volatile market conditions — a good reminder that even peer-reviewed papers honestly report their own limitations.

For Which Use Cases is Attention Suitable?

🕯️
Identifying Key Candles
With Self-Attention on the input sequence, the model can identify which candles had the greatest impact on the prediction.
📰
Dynamic Weighting of Fundamental News
Cross-Attention weights the impact of different news types (CPI, NFP, interest rate) separately per asset.
📡
Simultaneous Multi-Timeframe Analysis
Multi-Head Attention can dedicate each head to a different timeframe (M15, H1, H4).
🔍
Improving Interpretability for Trust
Heat Map visualization lets the trader understand why the model issued a specific signal.
📄
Explainable AI Reports for Client Trust
Showing the Attention Heatmap in periodic reports lets clients or investors visually see the model’s decision logic.
🎯
Smart Stop-Loss & Take-Profit Placement
Placing SL/TP levels precisely at the candles or price zones the model assigned the highest attention weight to.
📉
Multi-Step Forecasting Without a Full Transformer
An LSTM Encoder-Attention-Decoder architecture can forecast several future steps at once. Per Wen & Li (2023), on stock price data this reduced MAPE from 30.07% (plain LSTM) to 10.52%, and on gold price from 54.17% to 19.89%; the benefit of attention grows with longer forecast horizons.
⚠️ Real-World Limitations — Know Before You Invest
  • O(n²) Memory Complexity: Every step accesses every other step directly, so memory consumption grows quickly with longer sequences.
  • Not Sufficient Alone: Attention is typically used as an add-on to LSTM/GRU or within a Transformer, not standalone.
  • Interpretation Can Be Misleading: High Attention weight doesn't always mean true causal relationship; requires careful review.

Attention vs Plain LSTM

FeatureLSTM + AttentionPlain LSTMTransformer
Long-term DependencyVery StrongModerate (Gate)Very Strong
InterpretabilityExcellent (Heatmap)WeakModerate
Out-of-Sample Accuracy67–76%65–75%68–78%

Foundational Papers for This Model

  • Bahdanau, D., Cho, K. & Bengio, Y. (2015) Neural Machine Translation by Jointly Learning to Align and Translate International Conference on Learning Representations (ICLR 2015)
    View Paper
  • Zhang, X., Liang, X., Zhiyuli, A., Zhang, S., Xu, R. & Wu, B. (2019) AT-LSTM: An Attention-based LSTM Model for Financial Time Series Prediction IOP Conference Series: Materials Science and Engineering, 569, 052037
    View Paper
  • Wen, X. & Li, W. (2023) Time Series Prediction Based on LSTM-Attention-LSTM Model IEEE Access, 11, 48322–48331
    View Paper

Ready to Build Your Attention-Enhanced Model?

ExpertNevees team implements an Attention layer on your LSTM/GRU and delivers a visual Heatmap report for interpretability.

Other ExpertNevees AI Models

Deep Learning · Vision

CNN — Visual Pattern Recognition in Charts

A CNN (Convolutional Neural Network) is a deep learning model that, by converting OHLCV data into a visual representation, learns complex patterns that can't be manually defined — from Head&Shoulders to 50+ candlestick patterns. Per a study published in PeerJ Computer Science (Mersal et al., 2025), a CNN trained on 61 Japanese candlestick patterns using real Forex data achieved 99.3% accuracy in binary trend-direction classification.

+50
Recognizable Patterns
+3000
Samples Required
20×5
Pixels per Block
ResNet
Base Architecture

How is OHLCV Converted to an Image?

Each column is one candle; Body, upper shadow, and lower shadow display in different colors. 20-candle blocks become 20×5 pixel images (with Volume and OI).

🧱
Conv Block 1
32 filters · 3×3 kernel
Extracts low-level visual features like trend lines and candle edges.
🏗️
Conv Block 2
64 filters · 3×3 kernel
Combines low-level features into more complex patterns like Head&Shoulders or Double Top.
🎯
Global Avg Pooling
→ Dense(128) → Softmax(3)
Summarizes feature maps into a fixed vector for final pattern classification.

50+ Candlestick and Chart Patterns

📐
Head & Shoulders
Double Top/Bottom
🔺
Triangle, Flag
Pennant
🕯️
Engulfing, Hammer
Doji
+50
Total Recognizable
Patterns

ExpertNevees Implementation Specification

📊
Input Data
  • Candlestick Image: 20-candle block with OHLC + Volume
  • Labeling: Via classic pattern detection algorithm or manual
  • Augmentation: Rotation and slight noise for better generalization
🎯
Model Output
  • Type: Multi-class image classification
  • Output: Pattern name + confidence probability
  • Application: Confirming classic price-action EA signals

Convolution & Pooling Operation

Mathematics · Notation
# 2D convolution operation over the candlestick image with filter K
S(i,j) = sum_m sum_n  I(i+m, j+n) * K(m,n)

# Non-linearity applied after convolution
A(i,j) = ReLU( S(i,j) + b )

# Max pooling to reduce dimensionality and add robustness to small shifts
P(i,j) = max( A(2i:2i+1, 2j:2j+1) )

CNN Implementation with TensorFlow

Python · TensorFlow/Keras
import numpy as np
from tensorflow.keras.layers import (Conv2D, MaxPooling2D, GlobalAveragePooling2D,
                                            Dense, Dropout, BatchNormalization, Input)
from tensorflow.keras import Sequential
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# ── Convert a 20-candle OHLCV window into a 2D candlestick image ──
# Output shape: [20, 64, 3] → time steps × price-channels × RGB
def ohlcv_to_image(df_slice, img_h=64):
    n = len(df_slice)
    img = np.zeros((n, img_h, 3), dtype=np.float32)
    lo, hi = df_slice['low'].min(), df_slice['high'].max()
    def norm(v): return int((v - lo) / (hi - lo + 1e-9) * (img_h - 1))
    for i, (_, row) in enumerate(df_slice.iterrows()):
        # Candle body: green for bullish, red for bearish
        color = [0, 1, 0] if row.close >= row.open else [1, 0, 0]
        y0, y1 = norm(min(row.open, row.close)), norm(max(row.open, row.close))
        img[i, y0:y1+1, :] = color
        # Wicks: grey channel
        img[i, norm(row.low):norm(row.high)+1, :] = [0.5, 0.5, 0.5]
    return img

# ── CNN architecture for chart pattern classification (50+ pattern classes) ──
model = Sequential([
    Input(shape=(20, 64, 3)),
    # Block 1: low-level edge and line detection
    Conv2D(32, (3, 3), activation='relu', padding='same'),
    BatchNormalization(),
    MaxPooling2D((2, 2)),
    # Block 2: composite pattern detection (Head&Shoulders, Double Top, etc.)
    Conv2D(64, (3, 3), activation='relu', padding='same'),
    BatchNormalization(),
    MaxPooling2D((2, 2)),
    Conv2D(128, (3, 3), activation='relu', padding='same'),
    BatchNormalization(),
    # Global pooling collapses spatial dims → fixed-length descriptor
    GlobalAveragePooling2D(),
    Dense(128, activation='relu'),
    Dropout(0.4),
    # 50+ pattern classes — use categorical_crossentropy with one-hot labels
    Dense(50, activation='softmax')
])
model.compile(optimizer='adam', loss='categorical_crossentropy',
              metrics=['accuracy'])

Training & Pattern Classification Evaluation — CNN

Python · Train / Predict / Evaluate
# ── Image dataset generation + training + evaluation ──
from sklearn.metrics import classification_report, accuracy_score
import numpy as np

# Build image dataset from labelled 20-candle windows
X_img = np.array([ohlcv_to_image(df.iloc[i:i+20]) for i in start_indices])
y_img = np.array(pattern_labels)  # integer-encoded, one of 50 pattern classes

split = int(len(X_img) * 0.8)
X_train, X_test = X_img[:split], X_img[split:]
y_train, y_test = y_img[:split], y_img[split:]

datagen = ImageDataGenerator(horizontal_flip=False, brightness_range=[0.9, 1.1])
model.fit(
    datagen.flow(X_train, tf.keras.utils.to_categorical(y_train, 50), batch_size=32),
    validation_data=(X_test, tf.keras.utils.to_categorical(y_test, 50)),
    epochs=40,
    callbacks=[tf.keras.callbacks.EarlyStopping(patience=6, restore_best_weights=True)]
)

y_pred = model.predict(X_test).argmax(axis=1)
print(f"Test Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(classification_report(y_test, y_pred))

# Classify the most recent 20-candle chart image
latest_img = ohlcv_to_image(df.tail(20))[np.newaxis, ...]
pred_class = model.predict(latest_img).argmax()
print(f"Detected pattern class: {pattern_names[pred_class]}")

Common CNN Implementation Mistakes

🛠️ Implementation Notes That Are Often Overlooked
  • Overlapping Train/Test Windows: If adjacent 20-candle windows are split between train and test, heavy overlap leaks data and inflates reported accuracy.
  • Pattern Class Imbalance: Among 50+ pattern classes, some are far rarer than others. Without class weighting or oversampling, the model ignores rare patterns.
  • Sensitivity to the Image-Encoding Method: Changing the candle count or the OHLCV-to-image coloring scheme significantly shifts label distribution and accuracy; this choice must be documented and kept consistent.
  • Pattern Detection ≠ Profitable Signal: Correctly classifying a classic pattern (e.g. Head & Shoulders) doesn't guarantee the subsequent price move will follow that pattern's classical theory.

For Which Use Cases is CNN Suitable?

🔍
Detecting Classic Price-Action Patterns
Automatic detection of Head&Shoulders, Double Top/Bottom and candle patterns without fragile manual rules.
🛡️
Confirmation Filter for Price-Action EA
Before executing a classic EA signal, CNN confirms the visual chart pattern to reduce false signals.
🧠
Discovering Unknown Patterns
CNN can identify patterns not defined in classic technical analysis literature.
📚
Building Labeled Training Datasets
By auto-labeling historical patterns, a large dataset can also be built for training other models.
🖼️
Automated Multi-Symbol Chart Scanning
Visually scanning hundreds of price charts across timeframes at once to quickly flag symbols with an active setup.
📐
Visual Detection of Support & Resistance Zones
Learning key support/resistance zones directly from chart images, without hard-coded indicator-based rules.
⚠️ Real-World Limitations — Know Before You Invest
  • Needs Precise Labeling: The quality of training labels (correct patterns) determines final accuracy.
  • Pattern ≠ Profitability: Correctly identifying a visual pattern doesn't mean a profitable trading signal; needs separate risk management.
  • Sensitive to Scaling: Price-to-image normalization must be done carefully so patterns are comparable.

CNN vs Classic Pattern Detection

FeatureCNNClassic Manual Rules
Generalization to New PatternsHighNone
Training Data Needed+3,000 samplesNone
Rule TransparencyOpaque (Black Box)Fully Transparent

Foundational Papers for This Model

  • LeCun, Y., Bottou, L., Bengio, Y. & Haffner, P. (1998) Gradient-Based Learning Applied to Document Recognition Proceedings of the IEEE, 86(11), 2278–2324
    View Paper
  • Mersal, E. R., Karaoğlan, K. M. & Kutucu, H. (2025) Enhancing Market Trend Prediction Using Convolutional Neural Networks on Japanese Candlestick Patterns PeerJ Computer Science, 11, e2719
    View Paper

Ready to Build Your CNN Model?

ExpertNevees team builds the OHLCV-to-image pipeline, trains the CNN, and connects it as a confirmation filter to your EA.

Other ExpertNevees AI Models

Reinforcement Learning · Agent

DDQN — Reinforcement Learning Trading Agent

Double Deep Q-Network is an approach where a trading agent learns the optimal strategy through direct interaction with the market environment — without manual data labeling.

+50k
Environment Steps Needed
Gym
OpenAI Environment
Sharpe
Reward Metric
Very High
Complexity

Trading Agent Learning Loop — State, Action, Reward

📊
State
Price, indicators, P&L
🤖
Agent (DDQN)
Choose optimal Action
Action
Buy / Sell / Hold
🌐
Environment
Simulated market
🏆
Reward
Sharpe Ratio Δ

Reward feeds back to the Agent so it can better evaluate the next State in the following cycle.

🌀
Online Network
Q-Value Estimation
The network used to select an action at each step, continuously updated.
🎯
Target Network
Stabilized Target Q-Value
A delayed copy of the Online Network updated every N steps, ensuring training stability.
💾
Experience Replay
Replay Buffer
Stores and randomly samples past experiences, breaking correlation between consecutive data.

The Overestimation Problem in Standard DQN

2
Separate Networks
Online + Target
Reduces
Overestimation Bias
+50k
Environment Steps
for Stable Training
GPU
Recommended
for Faster Training

DDQN is Just One Branch — Which RL Family Fits Which Trading Scenario?

Reinforcement Learning is not a single algorithm but a family of approaches — the ExpertNevees team selects and implements the right model based on the action space type, available data volume, and trading objective.

Family Algorithms Core Idea Best Suited For
Value-Based DQN, DDQN, Dueling DQN Estimate Q-Value per action and pick the max Discrete actions: Buy / Sell / Hold
Policy Gradient REINFORCE, TRPO, PPO Directly optimize the policy π(a|s) without needing max_a Q Continuous or discrete actions needing stable training
Actor-Critic A2C, A3C An Actor selects the policy while a Critic evaluates it Parallel training across multiple symbols or timeframes
Continuous Control SAC, TD3 Maximum Entropy RL for more stable exploration in continuous spaces Precise position-sizing output (e.g. 0 to 1 lot)

ExpertNevees' default choice for discrete Buy/Sell/Hold signals is DDQN — due to its training stability and easier debugging compared to policy-gradient methods. For continuous position sizing or multi-asset coordination, PPO or SAC is implemented and trained instead.

Bellman Equation & Double Q-Update

Mathematics · Notation
# Standard DQN target — suffers from optimistic overestimation bias in value estimates
y_DQN = r + gamma * max_a  Q(s', a; theta_target)

# Double DQN target — action selected by the online network, evaluated by the target network (reduces bias)
y_DDQN = r + gamma * Q( s', argmax_a Q(s', a; theta_online); theta_target )

# Loss function — mean squared Temporal Difference (TD) error
L(theta) = E[ ( y_DDQN - Q(s, a; theta_online) )^2 ]

# Policy Gradient objective (REINFORCE) — used instead of Q-learning in policy-based families
grad J(theta) = E[ grad log(pi(a|s; theta)) * A(s,a) ]

# PPO clipped surrogate objective — the industry-standard stabilizer for policy-gradient updates
L_CLIP(theta) = E[ min( r_t(theta)*A_t,  clip(r_t(theta), 1-eps, 1+eps)*A_t ) ]
# where r_t(theta) = pi_theta(a_t|s_t) / pi_theta_old(a_t|s_t)  — probability ratio

DDQN Implementation with OpenAI Gym

Python · TensorFlow + OpenAI Gym
import gym
import numpy as np
import tensorflow as tf
from collections import deque
import random

# ── Custom Gym environment — simulates order execution on historical OHLCV ──
class TradingEnv(gym.Env):
    def __init__(self, df, window=60):
        super().__init__()
        self.df = df
        self.window = window
        self.action_space = gym.spaces.Discrete(3)   # 0=Sell, 1=Hold, 2=Buy
        # State: last `window` candles × n_features
        self.observation_space = gym.spaces.Box(
            low=-np.inf, high=np.inf,
            shape=(window, df.shape[1]), dtype=np.float32)

    def step(self, action):
        prev_sharpe = self._calc_sharpe()
        self._execute_order(action)
        self.current_step += 1
        # Reward: incremental Sharpe Ratio — penalises both loss and excess risk
        reward = self._calc_sharpe() - prev_sharpe
        done = self.current_step >= len(self.df) - 1
        return self._get_obs(), reward, done, {}

# ── Experience Replay Buffer (capacity: 50 k transitions) ──
memory = deque(maxlen=50_000)

# ── DDQN training step: decouples action selection from value estimation ──
def train_step(online_net, target_net, gamma=0.99, batch_size=64):
    if len(memory) < batch_size:
        return
    batch = random.sample(memory, batch_size)
    states, actions, rewards, next_states, dones = map(np.array, zip(*batch))

    # Double DQN: online net picks the best next action, target net evaluates it
    best_actions = online_net(next_states).numpy().argmax(axis=1)
    next_q = target_net(next_states).numpy()
    targets = rewards + gamma * next_q[np.arange(batch_size), best_actions] * (~dones)

    # Bellman update — only update Q-values for taken actions
    with tf.GradientTape() as tape:
        q_pred = online_net(states)
        q_taken = tf.reduce_sum(q_pred * tf.one_hot(actions, 3), axis=1)
        loss = tf.reduce_mean(tf.square(targets - q_taken))   # MSE (Huber also works)
    online_net.optimizer.minimize(loss, online_net.trainable_weights, tape=tape)

# ── Sync target network every 500 steps to stabilise training ──
if step % 500 == 0:
    target_net.set_weights(online_net.get_weights())

Full Agent Training & Learned-Policy Evaluation

Python · Train / Predict / Evaluate
# ── Full training loop over episodes + trained-policy inference ──
online_net = build_q_network(input_shape=(60, n_features), n_actions=3)
target_net = build_q_network(input_shape=(60, n_features), n_actions=3)
target_net.set_weights(online_net.get_weights())

env = TradingEnv(train_df, window=60)
epsilon, epsilon_min, epsilon_decay = 1.0, 0.05, 0.995

for episode in range(500):
    state, done, ep_reward = env.reset(), False, 0
    while not done:
        # Epsilon-greedy exploration — decays toward pure exploitation over training
        if np.random.rand() < epsilon:
            action = env.action_space.sample()
        else:
            action = int(online_net(state[np.newaxis]).numpy().argmax())

        next_state, reward, done, _ = env.step(action)
        memory.append((state, action, reward, next_state, done))
        train_step(online_net, target_net)
        state, ep_reward = next_state, ep_reward + reward

    epsilon = max(epsilon_min, epsilon * epsilon_decay)
    if episode % 500 == 0:
        target_net.set_weights(online_net.get_weights())
    if episode % 50 == 0:
        print(f"Episode {episode}  reward={ep_reward:.3f}  epsilon={epsilon:.3f}")

# ── Evaluate the trained policy on unseen data (epsilon=0 → fully greedy) ──
test_env = TradingEnv(test_df, window=60)
state, done, total_reward = test_env.reset(), False, 0
while not done:
    action = int(online_net(state[np.newaxis]).numpy().argmax())
    state, reward, done, _ = test_env.step(action)
    total_reward += reward
print(f"Out-of-sample cumulative reward (Sharpe-based): {total_reward:.3f}")

Common DDQN Implementation Mistakes

🛠️ Implementation Notes That Are Often Overlooked
  • Poorly Designed Reward Function: The single biggest cause of DDQN failure in practice is a reward-shaping bug, not the algorithm choice; the reward must genuinely reflect trading success (e.g. risk-adjusted PnL). One real solution to this problem is learning the reward function itself from human demonstrations (instead of hand-designing it) — an approach implemented by Zhou et al. (2024) in their R-DDQN model.
  • Simulation Not Matching the Real Broker: If the simulated spread, commission, and slippage don't match real broker conditions, the trained policy behaves differently on a live account.
  • Overfitting to a Single Market Regime: Training only on one historical window (e.g. only the 2020–2021 bull market) makes the agent perform poorly in other regimes.
  • No Periodic Retraining: Markets are non-stationary; a policy that worked well last year may no longer be optimal without periodic retraining.

Beyond Double DQN — Extensions ExpertNevees Adds

🏗️
Dueling Architecture
Value + Advantage Streams
The network splits into two streams: V(s) estimating overall state value and A(s,a) estimating each action's relative advantage; Q(s,a)=V(s)+A(s,a). This split speeds up learning in states where the action choice barely matters (e.g. range-bound markets).
Prioritized Experience Replay
PER
Instead of uniform sampling from the replay buffer, transitions with larger TD-error are sampled with higher probability so the agent learns faster on challenging cases like sudden breakouts.
📶
Multi-Step Returns
N-Step Bootstrapping
Instead of a single-step update, the cumulative n-step future return is used in the target calculation; this propagates reward signal backward faster, especially useful for delayed-reward trades (e.g. closing a position after several candles).

Combining Double Q-Learning + Dueling Architecture + Prioritized Experience Replay + Multi-Step Returns forms the core of the well-known 'Rainbow DQN' architecture — the ExpertNevees team selectively adds these components onto the base DDQN depending on project needs.

For Which Use Cases is DDQN Suitable?

🎓
Learning Strategy Without Manual Labels
Unlike supervised models, no prior Buy/Sell/Hold labeling is needed.
⚖️
Direct Optimization of Risk-Adjusted Metrics
Reward Function can directly optimize Sharpe or Sortino Ratio instead of raw profit.
🔁
Markets with Relatively Stable Regimes
RL performs better in markets with relatively stable behavior patterns than in frequently regime-changing markets.
🧮
Continuous Position Management
Continuous decision-making throughout a trade's lifecycle (hold, scale-in, scale-out) instead of a one-off signal.
⚖️
Dynamic Position Sizing Agent
The agent learns optimal position size alongside trade direction based on current market conditions, not just a Buy/Sell signal.
🔄
Automated Multi-Asset Portfolio Rebalancing
Training a single agent to dynamically allocate and rebalance capital across multiple symbols without manual rules.
⚠️ Real-World Limitations — Know Before You Invest
  • Very High Data Requirement: RL needs at least 50,000 environment steps, meaning years of historical financial data.
  • Sensitive to Regime Changes: For frequently regime-changing markets, combining RL with Market Regime Detection is essential.
  • Reward Engineering Difficulty: Poor Reward Function design can push the Agent toward unexpected and risky behaviors.

DDQN vs Supervised Models

FeatureDDQN (RL)LSTM (Supervised)
Labeling RequiredNoYes (Up/Down/Neutral)
Optimization TargetDirect Sharpe RatioClassification Accuracy
Data Requirement+50,000 steps+5,000 candles
Training ComplexityVery HighModerate

Recommended Starting Values for Training

ParameterSuggested ValueRole
Learning Rate1e-4 – 5e-4Update speed of the Online Network weights
Discount Factor (γ)0.95 – 0.99Weight of future rewards relative to immediate reward
Replay Buffer Size50,000 – 200,000Capacity of stored experience memory
Batch Size32 – 128Number of samples per update step
Target Network UpdateEvery 500–1,000 stepsInterval for syncing Target Network with Online Network
Epsilon Decay0.995 – 0.999Rate of decaying random exploration in favor of exploitation

Foundational Papers for This Model

  • Mnih, V. et al. (2015) Human-Level Control through Deep Reinforcement Learning Nature, 518, 529–533
    View Paper
  • van Hasselt, H., Guez, A. & Silver, D. (2016) Deep Reinforcement Learning with Double Q-Learning Proceedings of the 30th AAAI Conference on Artificial Intelligence
    View Paper
  • Zhou, C., Huang, Y., Cui, K. & Lu, X. (2024) R-DDQN: Optimizing Algorithmic Trading Strategies Using a Reward Network in a Double DQN Mathematics, 12(11), 1621
    View Paper
  • Wang, Z., Schaul, T., Hessel, M., et al. (2016) Dueling Network Architectures for Deep Reinforcement Learning Proceedings of the 33rd International Conference on Machine Learning (ICML)
    View Paper
  • Schaul, T., Quan, J., Antonoglou, I. & Silver, D. (2016) Prioritized Experience Replay International Conference on Learning Representations (ICLR)
    View Paper
  • Schulman, J., Wolski, F., Dhariwal, P., Radford, A. & Klimov, O. (2017) Proximal Policy Optimization Algorithms arXiv preprint
    View Paper
  • Haarnoja, T., Zhou, A., Abbeel, P. & Levine, S. (2018) Soft Actor-Critic: Off-Policy Maximum Entropy Deep RL with a Stochastic Actor Proceedings of the 35th International Conference on Machine Learning (ICML)
    View Paper

Ready to Build Your Trading Agent?

ExpertNevees team designs and trains a custom Gym environment, Sharpe-Ratio-based Reward Function, and the right Agent — whether DDQN for discrete Buy/Sell/Hold signals, or PPO/SAC for continuous position sizing.

Other ExpertNevees AI Models

Deep Learning · Generative

TimeGAN — Synthetic Financial Time-Series Data

TimeGAN is a Generative Adversarial Network that generates realistic financial time-series data — for cases with insufficient historical data, crisis scenario simulation, and Data Augmentation for rare events.

4
Architecture Components
PCA/t-SNE
Diversity Evaluation
TSTR
Usefulness Metric
8GB+
VRAM Required

Four Core Components of TimeGAN Architecture

🗜️
Autoencoder
Encodes to Latent Space
🎨
Generator
Generates synthetic series
🕵️
Discriminator
Detects real/synthetic
🧭
Supervisor
Preserves temporal structure
⚔️
Adversarial Training
Generator vs Discriminator
Generator tries to fool the Discriminator while it tries to distinguish real from synthetic data — this competition improves quality.
🕒
Temporal Dynamics Preservation
Supervisor Loss
The Supervisor ensures the generated time series preserves real market patterns (e.g. autocorrelation).

Synthetic Data Quality Evaluation

📊
Diversity
PCA / t-SNE
🎯
Fidelity
Real/Synthetic Classifier Acc.
TSTR
Usefulness
Train-Synthetic Test-Real
⚠️
Risk
Mode Collapse

ExpertNevees Implementation Specification

📊
Input Data
  • Real Time Series: Limited historical OHLCV
  • Sequence Length: Fixed windows (e.g. 60 candles)
🎯
Model Output
  • Synthetic Time Series: With similar statistical properties to real data
  • Application: Augmenting training dataset for LSTM/CNN/XGBoost

TimeGAN's Three Loss Functions

Mathematics · Notation
# Reconstruction loss — the autoencoder must be able to reconstruct the original data from latent space
L_R = E[ || X - Decoder(Encoder(X)) ||^2 ]

# Supervised loss — the next-step prediction in latent space must match the real data dynamics
L_S = E[ || h_t - g(h_(t-1), z_t) ||^2 ]

# Adversarial loss — the discriminator must tell real from synthetic, the generator must fool it
L_U = E[ log D(H) ] + E[ log(1 - D(H_hat)) ]

TimeGAN Implementation with TensorFlow

Python · TensorFlow 2
import numpy as np
from ydata_synthetic.synthesizers.timeseries import TimeGAN

# ── TimeGAN hyperparameters — tune noise_dim and hidden_dim for your dataset ──
gan_args = {
    'batch_size': 128,
    'lr': 5e-4,          # lower LR stabilises adversarial training
    'noise_dim': 32,     # latent space dimension for the generator
    'seq_len': 60,       # must match encoder window of downstream model
    'n_features': 5,    # OHLCV — extend to 11 to include indicators
}

# gamma: weight of supervised (reconstruction) loss vs adversarial loss
synth = TimeGAN(model_parameters=gan_args, hidden_dim=24,
                seq_len=60, n_seq=5, gamma=1)

# Increase train_steps to 20 k–50 k if mode collapse appears
synth.train(real_data, train_steps=10_000)

# ── Sample synthetic sequences for downstream data augmentation ──
synthetic_data = synth.sample(n_samples=1_000)  # shape: [1000, 60, 5]

# ── TSTR evaluation: Train on Synthetic, Test on Real ──
# A high TSTR accuracy proves the synthetic data captures real market dynamics
model_synth = train_lstm(synthetic_data)
tstr_acc, _ = model_synth.evaluate(real_test_X, real_test_y, verbose=0)
print(f"TSTR Accuracy (synthetic→real): {tstr_acc:.2%}")

Synthetic Data Quality Evaluation — TimeGAN

Python · Train / Predict / Evaluate
# ── Discriminative Score — the standard quality metric for synthetic time series ──
# Train a classifier to distinguish real vs synthetic sequences.
# Score near 0.5 = indistinguishable (ideal); near 1.0 = poor-quality synthetic data.
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

real_flat = real_data.reshape(len(real_data), -1)
synth_flat = synthetic_data.reshape(len(synthetic_data), -1)

X_disc = np.vstack([real_flat, synth_flat])
y_disc = np.concatenate([np.ones(len(real_flat)), np.zeros(len(synth_flat))])
X_train, X_test, y_train, y_test = train_test_split(X_disc, y_disc, test_size=0.3, random_state=42)

clf = LogisticRegression(max_iter=1000)
clf.fit(X_train, y_train)
disc_score = accuracy_score(y_test, clf.predict(X_test))
print(f"Discriminative Score: {disc_score:.3f}  (0.5 = ideal, indistinguishable from real)")

# ── TSTR classification example — train an LSTM purely on synthetic sequences ──
X_synth, y_synth = label_sequences(synthetic_data)   # forward-return based labels
model_synth = build_lstm_model(seq_len=60, n_features=5, n_classes=3)
model_synth.fit(X_synth, y_synth, epochs=30, batch_size=32, verbose=0)
tstr_acc = model_synth.evaluate(real_test_X, real_test_y, verbose=0)[1]
print(f"TSTR Accuracy (trained on synthetic, tested on real): {tstr_acc:.2%}")

Common TimeGAN Implementation Mistakes

🛠️ Implementation Notes That Are Often Overlooked
  • Unnoticed Mode Collapse: If the generator produces only a few limited patterns (low diversity), a low discriminative score alone won't reveal this; sample diversity must also be checked.
  • Too Little Real Data: Training a GAN on fewer than roughly 2,000 real sequences is typically unstable and degrades synthetic data quality.
  • Not a Substitute for Fixing the Model: Adding synthetic data to a strategy that is fundamentally overfit doesn't fix the underlying problem; it just provides more data for the same mistake.
  • TSTR Alone Is Not Enough: Besides TSTR (Train on Synthetic, Test on Real), TRTS (Train on Real, Test on Synthetic) should also be checked to evaluate synthetic data quality both ways.

For Which Use Cases is TimeGAN Suitable?

📦
Augmenting Training Dataset
When historical data is insufficient for training LSTM or Transformer, TimeGAN augments the dataset.
💥
Simulating Crisis Scenarios
Generating Flash Crash or High Volatility scenarios to test strategy Robustness in rare conditions.
🧪
Backtesting on Synthetic Data
Testing strategy across thousands of synthetic market paths to evaluate stability beyond a single historical path.
🔬
Data Augmentation for Rare Events
Replicates low-frequency events (e.g., emergency central bank decisions) with similar synthetic samples.
🧪
Monte Carlo Stress-Testing for Expert Advisors
Backtesting an EA across thousands of synthetic market paths from TimeGAN to truly measure resilience to unseen scenarios.
🔒
Secure Data Sharing Without Exposing Real Broker Data
Demoing or selling a strategy using statistically realistic synthetic data, without exposing sensitive real trading data.
⚠️ Real-World Limitations — Know Before You Invest
  • High Hyperparameter Sensitivity: Unstable training and Mode Collapse are key challenges requiring careful tuning.
  • Synthetic Data Isn't Real: Should never fully replace real data for final strategy evaluation.
  • High Hardware Requirement: Stable GAN training typically requires a GPU with at least 8GB VRAM.

TimeGAN vs Other Generative/Unsupervised Models

FeatureTimeGANAutoencoder (VAE)
Main PurposeGenerating new dataDim. reduction / latent factors
Training StabilityLower (Adversarial)Higher
Primary UseData AugmentationRegime Detection

Foundational Papers for This Model

  • Yoon, J., Jarrett, D. & van der Schaar, M. (2019) Time-series Generative Adversarial Networks Advances in Neural Information Processing Systems 32 (NeurIPS 2019)
    View Paper

Ready to Leverage Synthetic Data?

ExpertNevees team trains a TimeGAN model on your historical data and evaluates synthetic data quality using Fidelity and TSTR metrics.

Other ExpertNevees AI Models

Deep Learning · Unsupervised

Autoencoder — Latent Risk Factor Extraction

An Autoencoder is an Encoder-Decoder neural network that builds a compressed representation of input data — used in algorithmic trading for dimensionality reduction, market regime identification, and latent risk factor extraction.

4
Autoencoder Types
2D
Regime Space
K-Means
Clustering
Advanced
Complexity Level

How Does Autoencoder Compress Data?

Input
40 Features
Encoder
Compression
Latent Space
Latent Factor
Decoder
Reconstruction
Reconstructed Output
40 Features
📉
Linear / Deep Autoencoder
PCA-equivalent / Non-linear Reduction
The Linear type is PCA-equivalent; the Deep type with multiple layers performs more complex non-linear reduction.
🎲
Variational (VAE)
Probabilistic Market Representation
Instead of a single point, generates a probability distribution in Latent Space modeling market uncertainty.
🧹
Denoising Autoencoder
Noise Injection + Clean Reconstruction
Noise is added to input and the model learns to reconstruct clean data — highly effective for filtering Alpha Factor noise.

Market Regime Detection with Clustering

📈
Trending
Regime
📊
Ranging
Regime
High Volatility
Regime
2D
Encoding Space
for Clustering

Autoencoder Reconstruction Loss

Mathematics · Notation
# Compress the input into a lower-dimensional latent space (bottleneck)
z = Encoder(x) ,   dim(z) << dim(x)

# Reconstruct the input from the compressed representation
x_hat = Decoder(z)

# Reconstruction error — the main criterion used for anomaly scoring
L = || x - x_hat ||^2 = || x - Decoder(Encoder(x)) ||^2

Autoencoder Implementation for Regime Detection

Python · TensorFlow/Keras + K-Means
import tensorflow as tf
from tensorflow.keras.layers import Dense, Input, Dropout
from tensorflow.keras.models import Model
from sklearn.cluster import KMeans

# ── Bottleneck Autoencoder for non-linear factor extraction ──
# Encoder compresses 40 market features into a 2D latent embedding
inp   = Input(shape=(40,))
x     = Dense(32, activation='relu')(inp)
x     = Dropout(0.1)(x)
x     = Dense(16, activation='relu')(x)
# 2D latent space → directly visualisable as a scatter plot
latent = Dense(2, activation='linear', name='latent')(x)

# Decoder reconstructs the original feature vector from the latent code
x     = Dense(16, activation='relu')(latent)
x     = Dense(32, activation='relu')(x)
output = Dense(40, activation='linear')(x)  # MSE loss → minimise reconstruction error

autoencoder = Model(inp, output)
encoder     = Model(inp, latent)            # inference-time encoder only

autoencoder.compile(optimizer='adam', loss='mse')
autoencoder.fit(X_train, X_train, epochs=100, batch_size=64, verbose=0)

# ── Cluster latent codes into market regimes via K-Means ──
latent_features = encoder.predict(X_train, verbose=0)
kmeans  = KMeans(n_clusters=3, random_state=42, n_init=10)
regimes = kmeans.fit_predict(latent_features)
# cluster labels: 0=Trending, 1=Ranging, 2=High Volatility

Reconstruction Error & Regime/Anomaly Detection

Python · Train / Predict / Evaluate
# ── Reconstruction-error evaluation + anomaly / new-regime prediction ──
import numpy as np

recon = autoencoder.predict(X_test, verbose=0)
recon_error = np.mean(np.square(X_test - recon), axis=1)
print(f"Mean reconstruction error (test set): {recon_error.mean():.5f}")

# Anomaly threshold: 95th percentile of training reconstruction error
train_recon = autoencoder.predict(X_train, verbose=0)
train_error = np.mean(np.square(X_train - train_recon), axis=1)
threshold = np.percentile(train_error, 95)

# Predict on the newest market sample
latest = X_test[-1:]
latest_error = np.mean(np.square(latest - autoencoder.predict(latest, verbose=0)))
is_anomaly = latest_error > threshold
latest_regime = kmeans.predict(encoder.predict(latest, verbose=0))[0]

print(f"Latest sample: regime={['Trending','Ranging','High Volatility'][latest_regime]}, "
      f"anomaly={is_anomaly} (error={latest_error:.5f}, threshold={threshold:.5f})")

Common Autoencoder Implementation Mistakes

🛠️ Implementation Notes That Are Often Overlooked
  • Bottleneck Dimension Too Large: If the compression layer isn't small enough, the network simply memorizes the input instead of learning a meaningful representation.
  • Static Anomaly Threshold: Choosing a fixed percentile (e.g. 95%) for the anomaly threshold is not stable across time and different volatility regimes; it should be recalibrated periodically.
  • Unscaled Inputs: Without standardizing features, large-magnitude values dominate the reconstruction loss and small-magnitude features never get meaningfully compressed.
  • Reconstruction Error Alone Gives No Direction: A high reconstruction error only indicates 'unusualness,' not price direction; it must be paired with a separate trading rule.

For Which Use Cases is Autoencoder Suitable?

🌐
Market Regime Detection
Encoding price and indicators to 2D space, then Clustering to detect Trending/Ranging/Volatile.
💰
Asset Pricing with Conditional AE
Extracting asset-specific latent risk factors for building an ML-based asset pricing model.
🧹
Denoising Alpha Signals
Denoising Autoencoder for filtering random noise from alpha factors before use in the final model.
📉
Dimensionality Reduction Before Modeling
Reducing input feature count before feeding into XGBoost or LSTM, improving training speed.
🚨
Anomaly Detection & Early Market Warning
A high reconstruction error on new data signals abnormal market behavior — useful for early Flash-Crash-like warnings.
Preprocessing for Faster Downstream Model Training
Compressing dozens of input features into a small latent space before feeding LSTM or XGBoost noticeably cuts training time.
⚠️ Real-World Limitations — Know Before You Invest
  • Low Interpretability: Factors extracted in Latent Space lack clear human-readable meaning.
  • Latent Dimension Selection: The number of latent dimensions must be carefully chosen; too few or too many yields poor results.
  • Not a Trading Signal Alone: Autoencoder output is a feature, not a Buy/Sell signal; requires a subsequent classification model.

Autoencoder vs Traditional PCA

FeatureDeep AutoencoderTraditional PCA
Reduction TypeNon-linearLinear
Computation SpeedSlower (needs training)Very Fast
Reconstruction QualityHigher for complex dataLimited to linear relations

Foundational Papers for This Model

  • Hinton, G. E. & Salakhutdinov, R. R. (2006) Reducing the Dimensionality of Data with Neural Networks Science, 313(5786), 504–507
    View Paper
  • Kingma, D. P. & Welling, M. (2013) Auto-Encoding Variational Bayes International Conference on Learning Representations (ICLR 2014)
    View Paper

Ready to Extract Your Market's Hidden Factors?

ExpertNevees team designs the appropriate Autoencoder and extracts market regime or latent risk factors for your strategy.

Other ExpertNevees AI Models

Portfolio Optimization · Risk Parity

HRP Portfolio — Hierarchical Risk Parity

Introduced by Marcos López de Prado, Hierarchical Risk Parity builds a portfolio via hierarchical asset clustering that is more stable than traditional Markowitz optimization without needing covariance matrix inversion.

No
No Covariance Inversion
3
Algorithm Steps
Reduced Drawdown
Multi-Asset
Multi-Asset

Three Steps of the HRP Algorithm

XAUUSD
XAGUSD
EURUSD
GBPUSD

Sample dendrogram: XAUUSD and XAGUSD (precious metals) cluster earlier than EURUSD and GBPUSD (forex) due to higher correlation.

🌲
Tree Clustering
Hierarchical Correlation Clustering
Assets are hierarchically clustered based on correlation, forming a tree (Dendrogram).
🔄
Quasi-Diagonalization
Matrix Reordering
The covariance matrix is reordered based on the cluster tree so similar assets are placed near each other.
⚖️
Recursive Bisection
Top-Down Weight Allocation
Weights are recursively allocated top-down through the tree, without needing to invert the full covariance matrix.

Why is HRP More Stable than Markowitz?

No Need
Covariance Inversion
Lower Sensitivity
to Estimation Error
Reduces
Maximum Drawdown
N+
Scalable
for N Assets

ExpertNevees Implementation Specification

📊
Input Data
  • Historical Returns: Daily/weekly return series per asset
  • Correlation Matrix: Computed from historical returns
  • Assets: Forex, gold, crypto, stocks (Multi-Asset)
🎯
Model Output
  • Allocation Weights: Capital percentage per asset
  • Rebalancing: Periodic reallocation (weekly/monthly)
  • Risk Report: Portfolio Drawdown and Volatility

Correlation Distance & HRP Weight Allocation

Mathematics · Notation
# Convert the correlation matrix into a valid metric distance for hierarchical clustering
d(i,j) = sqrt( 0.5 * (1 - rho(i,j)) )

# Cluster variance using inverse-variance weights within the cluster
V_c = w_c^T * Sigma_c * w_c ,   w_c = diag(Sigma_c)^-1 / sum(diag(Sigma_c)^-1)

# Recursive weight allocation between two sub-clusters at each binary split (Recursive Bisection)
alpha = 1 - V_1 / (V_1 + V_2) ,   w_1 *= alpha ,   w_2 *= (1 - alpha)

HRP Implementation with PyPortfolioOpt

Python · PyPortfolioOpt
import pandas as pd
import numpy as np
from pypfopt import HRPOpt, expected_returns, risk_models

# ── Build daily return matrix for multi-asset portfolio ──
# Supports any mix: Forex, Gold, Crypto, Equity indices
returns = pd.DataFrame({
    'XAUUSD': gold_prices.pct_change(),
    'XAGUSD': silver_prices.pct_change(),
    'EURUSD': eurusd_prices.pct_change(),
    'GBPUSD': gbpusd_prices.pct_change(),
    'BTCUSD': btc_prices.pct_change(),
}).dropna()

# ── Run HRP: no covariance matrix inversion needed ──
# Step 1: hierarchical clustering on correlation distance matrix
# Step 2: recursive bisection to assign inverse-variance weights per cluster
hrp = HRPOpt(returns)
weights = hrp.optimize(linkage_method='ward')   # ward linkage reduces within-cluster variance

hrp.clean_weights()
print("Portfolio Allocation:", weights)
# e.g. {'XAUUSD': 0.31, 'XAGUSD': 0.19, 'EURUSD': 0.28, 'GBPUSD': 0.13, 'BTCUSD': 0.09}

# ── Evaluate expected risk-adjusted performance ──
perf = hrp.portfolio_performance(verbose=True)
# returns: (expected_annual_return, annual_volatility, Sharpe_ratio)

# ── Monthly rebalancing check — reallocate when weights drift > 5% ──
current_weights = get_current_portfolio_weights()
drift = {k: abs(current_weights[k] - weights[k]) for k in weights}
if max(drift.values()) > 0.05:
    rebalance_to_target(weights)  # sends orders to MT5/cTrader API

Out-of-Sample Backtest: HRP vs Equal-Weight

Python · Train / Predict / Evaluate
# ── Out-of-sample backtest: HRP allocation vs naive Equal-Weight benchmark ──
from pypfopt import HRPOpt

split = int(len(returns) * 0.7)
train_ret, test_ret = returns.iloc[:split], returns.iloc[split:]

hrp = HRPOpt(train_ret)
hrp_weights = hrp.optimize(linkage_method='ward')

equal_weights = {k: 1 / len(hrp_weights) for k in hrp_weights}

def portfolio_return(weights, ret_df):
    w = np.array([weights[c] for c in ret_df.columns])
    return (ret_df.values @ w)

hrp_pnl = portfolio_return(hrp_weights, test_ret)
eq_pnl = portfolio_return(equal_weights, test_ret)

hrp_sharpe = hrp_pnl.mean() / hrp_pnl.std() * np.sqrt(252)
eq_sharpe = eq_pnl.mean() / eq_pnl.std() * np.sqrt(252)

print(f"HRP Out-of-Sample Sharpe:          {hrp_sharpe:.2f}")
print(f"Equal-Weight Out-of-Sample Sharpe: {eq_sharpe:.2f}")
print(f"HRP Max Drawdown: {(1 - (1 + hrp_pnl).cumprod() / (1 + hrp_pnl).cumprod().cummax()).max():.2%}")

Common HRP Implementation Mistakes

🛠️ Implementation Notes That Are Often Overlooked
  • Assuming Correlation Stability: HRP clustering is based on historical correlation; during financial crises, cross-asset correlation typically spikes toward 1, breaking down the clustering structure.
  • Sensitivity to the Distance Metric: The (1-correlation)/2 distance formula is sensitive to outliers; return series with sharp jumps should be winsorized or filtered first.
  • Ignores Expected Return: HRP is purely risk-based and doesn't consider expected returns; it's a 'risk-optimal' model, not a 'return-optimal' one.
  • No Periodic Rebalancing: As asset volatilities shift over time, static HRP weights stop being optimal; periodic (e.g. monthly) rebalancing is required.

For Which Use Cases is HRP Suitable?

💼
Multi-Asset Portfolio Management
Allocating capital across forex, gold, crypto, and stocks in a balanced way with controlled risk.
🛡️
Reducing Concentration Risk
Preventing over-allocation to correlated assets that crash simultaneously in a crisis.
📊
Stable Alternative to Markowitz
In portfolios with many assets, HRP is more robust to covariance estimation error.
🔗
Combining with Other Model Outputs
Using Factor Score or Autoencoder factors as input for allocation optimization.
🧩
Capital Allocation Across Multiple Strategies or EAs
Instead of allocating across assets, HRP can distribute capital across multiple independent strategies or EAs by return correlation.
🌐
Hybrid Forex, Gold & Crypto Portfolio
HRP’s robustness to covariance-matrix noise makes it well-suited for mixing unstably-correlated assets like crypto and forex.
⚠️ Real-World Limitations — Know Before You Invest
  • Depends on Correlation Stability: If asset correlations change rapidly, the old clustering becomes invalid.
  • No Return Forecasting: HRP only manages risk; it doesn't forecast future asset returns.
  • Needs Regular Rebalancing: Without periodic reallocation, weights drift from optimal over time.

HRP vs Markowitz and Equal-Weight

FeatureHRPMarkowitz (MVO)Equal-Weight
Covariance InversionNoYesN/A
Sensitivity to Estimation ErrorLowVery HighNone
Risk ConsiderationYes (Hierarchical)YesNo

Foundational Papers for This Model

  • López de Prado, M. (2016) Building Diversified Portfolios that Outperform Out of Sample The Journal of Portfolio Management, 42(4), 59–69
    View Paper
  • Markowitz, H. (1952) Portfolio Selection The Journal of Finance, 7(1), 77–91
    View Paper

Ready to Optimize Your Multi-Asset Portfolio?

ExpertNevees team runs the HRP algorithm on your assets and builds an automatic Rebalancing system connected to MT5/cTrader.

Other ExpertNevees AI Models

Model Combination · Ensembling

Ensemble — Combining AI Models

Ensemble is a method that combines the output of several independent AI models (such as LSTM, XGBoost, and Random Forest) so that each model's weaknesses are covered by the others' strengths. Per the classic theory of Bates & Granger (1969), combining multiple forecasts typically yields a lower mean squared error (MSE) than the single best model alone — provided the models' errors are not highly correlated with each other.

5
Core Combination Methods
1969
Theoretical Origin (Bates & Granger)
2-5
Base Models, Typical in Practice
Reduced Error Variance

Five Core Methods for Combining AI Models

🗳️
Voting / Weighted Averaging
Voting & Averaging Ensemble
The outputs of several independent models are combined via majority vote (classification) or weighted average (regression). The simplest and most robust method; each model's weight can be set by its inverse error (Bates & Granger).
🧠
Stacking (Meta-Learning)
Stacked Generalization
A simple meta-learner is trained on the outputs of several base models to learn the best non-linear combination of them — a method introduced by Wolpert (1992) that is now an industry standard.
🔀
Regime-Based Switching
Mixture of Experts
A regime-detection model (e.g. an Autoencoder) identifies the market state — trending, ranging, or high-volatility — and routes more weight to the model best suited to that regime (e.g. LSTM in trends, XGBoost in ranges).
🧩
Feature-Level Fusion
Feature-Level Ensemble
The output of one model (e.g. an Autoencoder's compressed vector or HRP weights) is fed as an additional feature into another model's input (e.g. XGBoost) — the same pattern used in the AI-filter-on-simple-strategy portfolio examples.
🏗️
Architectural Hybridization
End-to-End Hybrid Model
Instead of combining the outputs of several independent models, multiple components (e.g. Encoder-LSTM + Attention + Decoder-LSTM) are fused into one architecture and trained end-to-end. Per Wen & Li (2023), this model achieved the lowest RMSE/MAPE across 5 real datasets (including stock and gold) compared to CNN, CNN-LSTM, LSTM, Stacked-LSTM, BiLSTM, and Encoder-Decoder-LSTM.

Why Does Combining Models Usually Outperform a Single Model?

Reduces Variance
of Prediction Error
No Single
Point of Failure
🔄
Each Model's Weakness
Covered by Others
📊
More Robust to
Market Regime Shifts

ExpertNevees Implementation Specification

📊
Input Data
  • Base Model Predictions: Output/probability from each independent model (e.g. LSTM, XGBoost, RF)
  • Out-of-Fold Errors: Used to estimate weights or train the meta-learner without data leakage
  • Regime Feature (optional): Autoencoder output or market volatility for switching
🎯
Model Output
  • Final Combined Prediction: The final combined signal or predicted price
  • Per-Model Weight: Each base model's contribution to the final decision, for interpretability
  • Model Agreement Level: How aligned the base models are, an extra confidence signal

Combination Formulas: Averaging, Weighting, and Stacking

Mathematics · Notation
# Simple average across N independent base models
y_hat = (1/N) * sum_i( y_hat_i )

# Bates & Granger (1969) inverse-variance weighting — lower-error models get higher weight
w_i = sigma_i^-2 / sum_j( sigma_j^-2 ) ,   y_hat = sum_i( w_i * y_hat_i )

# Stacking (Wolpert, 1992) — a meta-learner g() learns the optimal non-linear combination
y_hat = g( y_hat_1, y_hat_2, ..., y_hat_N ; theta )

# Regime-gated mixture of experts — weight each model by the probability of its matching regime
y_hat = sum_r( P(regime=r | x) * y_hat_r(x) )

Correct Stacking Using Out-of-Fold Predictions

Python · scikit-learn
import numpy as np
from sklearn.model_selection import TimeSeriesSplit, cross_val_predict
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from xgboost import XGBClassifier
from sklearn.metrics import accuracy_score, classification_report

# ── Base models: intentionally diverse algorithms (low error correlation) ──
base_models = {
    'rf': RandomForestClassifier(n_estimators=300, max_depth=8, random_state=42),
    'xgb': XGBClassifier(n_estimators=300, max_depth=4, learning_rate=0.05),
    'lstm_probs': None,  # pre-computed LSTM probabilities, see make_sequences() on the LSTM page
}

tscv = TimeSeriesSplit(n_splits=5)  # time-ordered folds — never shuffle financial time series

# ── Critical: generate OUT-OF-FOLD predictions for the meta-learner ──
# Using in-sample predictions here would leak information and make the
# meta-learner look far more accurate than it will be live (see Common Mistakes)
oof_rf  = cross_val_predict(base_models['rf'],  X, y, cv=tscv, method='predict_proba')[:, 1]
oof_xgb = cross_val_predict(base_models['xgb'], X, y, cv=tscv, method='predict_proba')[:, 1]
oof_lstm = lstm_oof_probs  # from a separate walk-forward LSTM training loop

# ── Meta-learner: a simple Logistic Regression stacks the three OOF prediction columns ──
meta_X = np.column_stack([oof_rf, oof_xgb, oof_lstm])
meta_model = LogisticRegression()
meta_model.fit(meta_X, y)

print("Learned ensemble weights:", dict(zip(['rf', 'xgb', 'lstm'], meta_model.coef_[0])))

# ── Fit final base models on ALL training data, then combine via the meta-learner ──
base_models['rf'].fit(X_train, y_train)
base_models['xgb'].fit(X_train, y_train)

test_meta_X = np.column_stack([
    base_models['rf'].predict_proba(X_test)[:, 1],
    base_models['xgb'].predict_proba(X_test)[:, 1],
    lstm_test_probs,
])
y_pred = meta_model.predict(test_meta_X)

print(f"Ensemble Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(classification_report(y_test, y_pred, target_names=['Down', 'Up']))

Comparing the Ensemble Against the Best Single Model

Python · Train / Predict / Evaluate
# ── Bates & Granger (1969) inverse-variance weighted average, computed from validation error ──
from sklearn.metrics import mean_squared_error

val_errors = {
    'rf':  mean_squared_error(y_val, rf_val_pred),
    'xgb': mean_squared_error(y_val, xgb_val_pred),
    'lstm': mean_squared_error(y_val, lstm_val_pred),
}
inv_var = {k: 1 / v for k, v in val_errors.items()}
total = sum(inv_var.values())
weights = {k: v / total for k, v in inv_var.items()}
print("Bates-Granger weights:", weights)

ensemble_test_pred = (
    weights['rf']   * rf_test_pred +
    weights['xgb']  * xgb_test_pred +
    weights['lstm'] * lstm_test_pred
)

# ── Compare ensemble MSE against each individual model AND the naive equal-weight average ──
naive_avg_pred = (rf_test_pred + xgb_test_pred + lstm_test_pred) / 3

results = {
    'RF alone':            mean_squared_error(y_test, rf_test_pred),
    'XGBoost alone':       mean_squared_error(y_test, xgb_test_pred),
    'LSTM alone':          mean_squared_error(y_test, lstm_test_pred),
    'Equal-weight ensemble': mean_squared_error(y_test, naive_avg_pred),
    'Bates-Granger ensemble': mean_squared_error(y_test, ensemble_test_pred),
}
for name, mse in sorted(results.items(), key=lambda x: x[1]):
    print(f"{name:24s} MSE = {mse:.6f}")

Common Mistakes When Combining AI Models

🛠️ Implementation Notes That Are Often Overlooked
  • Combining Correlated Models: If two models' errors are highly correlated (e.g. two similar tree-based models), combining them yields almost no improvement. The Bates & Granger formula ignores this correlation and assumes independent errors — genuine algorithmic diversity (e.g. tree-based + neural + linear) is a necessary condition.
  • Data Leakage in Stacking: Training the meta-learner on in-sample (rather than out-of-fold) base-model predictions falsely inflates accuracy; this accuracy will never be reproduced in live trading.
  • Ignoring Latency Cost: Running N models instead of one means N times the inference time; for scalping strategies needing sub-1ms latency, a heavy ensemble can be counterproductive.
  • Averaging Uncalibrated Probabilities: Different models often have different confidence scales (e.g. a neural network's Softmax output isn't directly comparable to XGBoost's probability); calibration (e.g. Platt Scaling) should be applied before averaging.
  • Meta-Learner Overfitting: Using a complex model (e.g. a deep neural network) as the meta-learner with few base models/validation samples overfits the combination layer; a simple linear model is usually sufficient and more stable.

For Which Use Cases is Model Combination Suitable?

🗳️
Final Signal from Multi-Model Voting
Combining Random Forest, XGBoost, and LSTM outputs via majority vote to reduce false signals from any single model.
🔀
Model Switching by Market Regime
Using an Autoencoder to detect market regime, then weighting LSTM higher in trends and XGBoost higher in ranges.
🧠
Stacking for Price Forecasting
Training a simple meta-learner on several regression models' predictions to improve price-forecast accuracy over any single model.
🧩
Fusing Latent Factors with a Tree Model
Adding an Autoencoder's compressed vector or HRP weights as an extra input feature to XGBoost.
💼
Capital Allocation Across Multiple Strategies/Models
Using HRP to allocate risk-based weights across the outputs of several independent models or EAs, not just multiple assets.
🛡️
Reducing False Signals in Volatile Markets
Only executing a trade when several independent models agree on the same direction simultaneously — the exact pattern used in the AI-filter-on-simple-strategy examples.
🏗️
Hybrid Architecture for Multi-Step Forecasting
Fusing Encoder-LSTM, Attention, and Decoder-LSTM into one architecture. Per Wen & Li (2023), this combination reduced MAPE on stock data from 30.07% (plain LSTM) to 10.52%, and on gold from 54.17% to 19.89%; the architecture's advantage grows with longer forecast horizons.
⚠️ Real-World Limitations — Know Before You Invest
  • Higher Complexity and Harder Debugging: When the final signal comes from combining N models, understanding why a specific trade went wrong is harder than with a single model.
  • No Guaranteed Improvement (No Free Lunch): If the base models are weak or highly correlated, the ensemble may show no improvement over the single best model.
  • Higher Compute Cost and Development Time: Building, training, and maintaining N models instead of one requires more development time and server resources.

Voting vs. Stacking vs. Regime Switching

FeatureVoting/AveragingStackingRegime Switching
Implementation ComplexityLowMediumHigh
Data Leakage RiskNoneHigh (without OOF)Medium
InterpretabilityHighMediumHigh
Adapts to Regime ChangeLowMediumHigh

Foundational Papers for This Method

  • Bates, J. M. & Granger, C. W. J. (1969) The Combination of Forecasts Journal of the Operational Research Society, 20(4), 451–468
    View Paper
  • Wolpert, D. H. (1992) Stacked Generalization Neural Networks, 5(2), 241–259
    View Paper
  • Shi, Z., Hu, Y., Mo, G. & Wu, J. (2022) Attention-based CNN-LSTM and XGBoost Hybrid Model for Stock Prediction arXiv:2204.02623 [q-fin.ST]
    View Paper
  • Wen, X. & Li, W. (2023) Time Series Prediction Based on LSTM-Attention-LSTM Model IEEE Access, 11, 48322–48331
    View Paper

Ready to Combine Several AI Models?

ExpertNevees team can combine and implement LSTM, XGBoost, Random Forest, and other models for you via Stacking or regime switching.

Base Models Commonly Used in Ensembles

MetaTrader Native Language

MQL4 / MQL5 — MetaTrader Trading Bot Development

MQL (MetaQuotes Language) is MetaTrader's native language built on C++ foundations. MQL5 is fully Object-Oriented, supporting classes, inheritance and polymorphism. As of 2026, MetaTrader 5 leads the retail CFD market with a 62% share.

C++
Language Base
OOP
MQL5 Paradigm
62%
Retail CFD Market Share (2026)
ONNX
AI Model Support

Expert Advisor Architecture in MQL5

🔧
OnInit()
Initialization
Initialization, input parameter validation, and building indicator handles via iCustom/iMA. If initialization fails, INIT_FAILED is returned and the EA does not load.
OnTick() / OnBar()
Trading Logic
The core trading logic executed on every price tick or new candle. Includes entry/exit condition checks, volume calculation, and order submission via the CTrade class.
🧮
OpenBLAS + ONNX
AI / Matrix Support
Since 2025, MQL5 has significantly expanded its OpenBLAS library for matrix/vector operations, and ONNX support lets deep learning models trained in Python (LSTM, XGBoost, etc.) run directly inside an EA with GPU/CUDA acceleration.
🧹
OnDeinit()
Cleanup
Releasing indicator handles, closing files, and freeing memory when the EA is removed from a chart or the timeframe/symbol changes.

What's New in MetaTrader 5 and MQL5?

GPU
Faster ONNX Model
Execution via CUDA GPU
Blend2D
New Chart Rendering Engine
(Replaced GDI, since 2025)
Git
MQL5 Algo Forge:
Git-Based Version Control
OOP
Stricter Inheritance and
Override Rules since 2025

Complete Expert Advisor Structure in MQL5

MQL5 · Expert Advisor
// Volatility-adjusted position sizing based on ATR
#include 
CTrade trade;

input double RiskPercent = 1.0;      // risk per trade, % of balance
input int    AtrPeriod   = 14;
int atrHandle;

int OnInit() {
   atrHandle = iATR(_Symbol, _Period, AtrPeriod);
   if(atrHandle == INVALID_HANDLE) return(INIT_FAILED);
   return(INIT_SUCCEEDED);
}

double CalculateLotSize(double stopLossPoints) {
   double atr[];
   CopyBuffer(atrHandle, 0, 0, 1, atr);
   double riskAmount  = AccountInfoDouble(ACCOUNT_BALANCE) * RiskPercent / 100.0;
   double tickValue   = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double lot = riskAmount / (stopLossPoints * tickValue);
   return NormalizeDouble(lot, 2);
}

void OnTick() {
   static datetime lastBar = 0;
   datetime currentBar = iTime(_Symbol, _Period, 0);
   if(currentBar == lastBar) return;   // process once per new candle, not every tick
   lastBar = currentBar;

   double atr[];
   CopyBuffer(atrHandle, 0, 0, 1, atr);
   double slPoints = atr[0] / _Point * 1.5;
   double lot = CalculateLotSize(slPoints);

   // CTrade handles requote retry, slippage and price normalization internally
   trade.Buy(lot, _Symbol, 0, SymbolInfoDouble(_Symbol, SYMBOL_BID) - slPoints * _Point);
}

Common Mistakes in MQL5 Development

🛠️ Implementation Notes That Are Often Overlooked
  • Running Logic on Every Tick Instead of Every Candle: Without checking the current candle time (like the lastBar pattern above), entry logic can fire multiple times within one candle, causing unwanted duplicate trades.
  • Not Checking for INVALID_HANDLE: If an indicator handle isn't validated in OnInit(), CopyBuffer silently fails in OnTick(), and the EA decides on invalid data without warning.
  • Ignoring Requotes and Slippage: Sending orders without handling broker server errors (Requote, Off Quotes) in volatile markets causes missed trades or execution at very different prices; the CTrade class handles these internally.
  • Backtesting with Poor Data Quality: Running Strategy Tester with regular Every Tick mode (not Every Tick Based on Real Ticks) produces unrealistic results, especially for scalping strategies.

For Which Use Cases is MQL5 Suitable?

🤖
Fully Automated Expert Advisors
A trading bot that performs analysis, entry/exit, and risk management without human intervention.
🧠
Running AI Models via ONNX
Training an LSTM or XGBoost model in Python, exporting to ONNX format, and running it directly inside an EA with GPU acceleration.
🛡️
Multi-Account Risk Management Panel
A dashboard monitoring Drawdown, optimal volume, and automatic alerts across multiple MetaTrader accounts simultaneously.
📡
Automated Signal System
Continuous market analysis and signal delivery to Telegram or WhatsApp without automatic trade execution.
🔄
Internal Copy Trading Across Accounts
Automatically replicating a Master account's trades across multiple Follower accounts with independent volume ratios.
📊
Custom Indicators and Analysis Tools
Detecting candlestick patterns, key levels, or custom statistical computations not available in default indicators.
⚠️ Real-World Limitations — Know Before You Invest
  • Limited to the MetaTrader Ecosystem: MQL5 code doesn't run directly on any other platform (cTrader, TradingView, crypto exchanges); it must be rewritten for each.
  • Limited Machine Learning Libraries: Unlike Python, MQL5 lacks a rich ML library ecosystem; models must be trained in Python and then imported into MQL5 via ONNX.
  • MQL4 is Being Phased Out: MetaQuotes' new development is mostly focused on MQL5; new projects should generally start directly with MQL5.

Official MQL5 Documentation and Resources

  • MetaQuotes Ltd. MQL5 Reference — Official Language Documentation MQL5.com
    View Docs
  • MetaQuotes Ltd. What's New in MetaTrader 5 — Release Notes MetaTrader5.com
    View Docs

Ready to Build Your MQL5 Trading Bot?

ExpertNevees, with 80+ successful MQL4/MQL5 projects, implements everything from simple Expert Advisors to ONNX-powered machine learning systems for you.

Other Trading Programming Languages

cTrader Native Language

cAlgo / cBot — cTrader Trading Bot Development

cAlgo is a C#-based language developed for the cTrader platform. Unlike MQL with its own syntax, cAlgo uses the standard .NET compiler and provides full access to the C# library ecosystem. cTrader works via Direct Market Access on ECN/STP brokers.

C#
Language Base (.NET)
ECN
Direct Market Access
Open API
Connect from Any Language (JSON/HTTP)
25+
Successful ExpertNevees cBot Projects

cBot Architecture in cAlgo

🚀
OnStart()
Initialization
cBot initial setup, reading Parameters defined via Attributes, and initializing indicators.
OnBar() / OnTick()
Trading Logic
Core trading logic. The Positions and Orders objects give full control of open positions and dynamic SL/TP management.
🔗
cTrader Open API
External Integration
A platform-independent protocol (JSON/Protobuf over HTTP) enabling connections from any language — including Python — for building external analytics or copy-trading apps.
🧹
OnStop()
Cleanup
Resource cleanup, unsubscribing from events, and final logging when the cBot stops.

Complete cBot Structure in C#

C# · cAlgo cBot
using cAlgo.API;
using cAlgo.API.Indicators;

[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class AtrRiskBot : Robot
{
    [Parameter("Risk %", DefaultValue = 1.0)]
    public double RiskPercent { get; set; }

    private AverageTrueRange atr;

    protected override void OnStart()
    {
        atr = Indicators.AverageTrueRange(14, MovingAverageType.Simple);
    }

    protected override void OnBar()
    {
        // Volatility-adjusted position size, mirrors the MQL5 ATR pattern
        double slPips = atr.Result.LastValue / Symbol.PipSize * 1.5;
        double riskAmount = Account.Balance * RiskPercent / 100;
        double volume = Symbol.NormalizeVolumeInUnits(riskAmount / (slPips * Symbol.PipValue));

        if (Positions.Count == 0)
        {
            // ExecuteMarketOrder returns a TradeResult with slippage/fill details
            var result = ExecuteMarketOrder(TradeType.Buy, SymbolName, volume,
                                              "AtrRiskBot", slPips, null);
            if (!result.IsSuccessful)
                Print("Order failed: {0}", result.Error);
        }
    }
}

Common Mistakes in cAlgo Development

🛠️ Implementation Notes That Are Often Overlooked
  • Not Checking TradeResult.IsSuccessful: Ignoring ExecuteMarketOrder's result means order failures (e.g. due to invalid volume) go unnoticed, and the bot assumes a trade was opened.
  • Using Raw Volume Instead of NormalizeVolumeInUnits: Each symbol has a different minimum/step volume; sending unnormalized volume causes broker order rejection.
  • Relying on OnTick for Heavy Logic: Running complex calculations in OnTick (which fires on every price change, not just new candles) can cause high processing load and latency; OnBar suits most strategies better.

For Which Use Cases is cAlgo Suitable?

🤖
Automated cBot on ECN/STP Brokers
Strategies needing real market spread and fast execution, including scalping.
🔄
Native cTrader Copy Trading
Registering a cBot as a Signal Provider so its trades are copied to Followers without an external server.
📊
Custom Indicators with the Full C# Library
Using any standard or NuGet .NET library for complex calculations inside an indicator.
🔗
External App with Open API
Building an external dashboard or Telegram bot that connects directly to a cTrader account via the JSON/Protobuf protocol.
⚠️ Real-World Limitations — Know Before You Invest
  • Smaller User Base than MetaTrader: Far fewer brokers support cTrader compared to MetaTrader; broker choice is more limited.
  • Requires C#/.NET Knowledge: Unlike MQL which is designed to be simpler for non-specialists, learning cAlgo requires genuine familiarity with OOP concepts in C#.

Official cAlgo Documentation and Resources

  • Spotware Ltd. cAlgo API Reference — Official Documentation help.ctrader.com
    View Docs
  • Spotware Ltd. cTrader Open API — Developer Documentation openapi.ctrader.com
    View Docs

Ready to Build Your cBot on cTrader?

ExpertNevees, with 25+ successful cBot projects, implements everything from simple strategies to Open-API-connected systems for you.

Other Trading Programming Languages

TradingView Native Language · Version 6

PineScript v6 — Indicators and Strategies for TradingView

PineScript is TradingView's dedicated scripting language. Version 6 launched in November 2024, bringing a complete overhaul to the type system, dynamic data requests, and new drawing types; v5 is no longer updated and new features are released exclusively in v6.

v6
Current Version (since Nov 2024)
Bar-by-Bar
Execution Model
2
Script Types: Indicator / Strategy
Webhook
Alert-to-External-Service Connection

What Changed in PineScript v6?

bool
Boolean Values Are Now Strictly
true/false, Never na
log.*()
New log.info/warning/error
Functions for Professional Debugging
request.*()
Dynamic Calls with String Arguments
on Any Historical Bar
"""
Multiline Strings with
Automatic Indentation

Indicator vs. Strategy

📉
indicator()
Visualization Only
Only calculates and plots on the chart; executes no real or simulated trades.
💰
strategy()
Backtestable
With strategy.entry() and strategy.exit(), simulates trades in backtesting and produces Profit Factor, Win Rate, and Max Drawdown reports.
🔁
Bar-by-Bar Execution
Execution Model
Code runs for all historical bars left to right. Series variables provide access to past values via the [] operator: close[1] means the previous candle's close.
🔔
alertcondition() + Webhook
External Connectivity
Alerts are defined with dynamic placeholders like {{close}} and can be sent via Webhook to a Telegram bot or external trade-execution services.

Complete Strategy in PineScript v6

PineScript v6 · Strategy
//@version=6
strategy("ATR Risk Strategy", overlay = true, initial_capital = 10000)

riskPercent = input.float(1.0, "Risk %", minval = 0.1)
atrLen      = input.int(14, "ATR Length")

atrVal = ta.atr(atrLen)
emaFast = ta.ema(close, 20)
emaSlow = ta.ema(close, 50)

// v6: boolean values are always strictly true/false, never na
longCondition = ta.crossover(emaFast, emaSlow)

if longCondition
    stopDistance = atrVal * 1.5
    riskAmount   = strategy.equity * riskPercent / 100
    qty = riskAmount / stopDistance
    strategy.entry("Long", strategy.long, qty = qty)
    strategy.exit("Exit", "Long", stop = close - stopDistance)

// v6: new log.* functions for real debugging instead of plotting hacks
if barstate.islast
    log.info("ATR: {0}, Fast EMA: {1}, Slow EMA: {2}", atrVal, emaFast, emaSlow)

plot(emaFast, color = color.orange)
plot(emaSlow, color = color.blue)
alertcondition(longCondition, title = "Long Signal", message = "Buy {{ticker}} at {{close}}")

Common Mistakes in PineScript

🛠️ Implementation Notes That Are Often Overlooked
  • Repainting in request.security(): Improper use of higher-timeframe data without lookahead=barmerge.lookahead_off causes backtests to use future information, making results non-reproducible.
  • Ignoring Realistic Commission and Slippage: Backtesting without setting commission_value and slippage in strategy() shows results far more optimistic than real performance.
  • Not Migrating from Implicit Boolean Casts: Un-migrated v5 code relying on if myInt (instead of if myInt != 0) no longer compiles in v6 and must be explicitly rewritten.

For Which Use Cases is PineScript Suitable?

📊
Visual Analysis Indicators and Tools
Plotting levels, patterns, and signals directly on the TradingView chart for manual trading.
🧪
Rapid Strategy Idea Backtesting
Quickly testing a trading hypothesis with a full statistical report, before final implementation in MQL5 or Python.
🔔
Connecting Alerts to an External Bot
Sending a PineScript alert via Webhook to a service that converts it into a real order on MetaTrader or an exchange.
📈
Screener and Multi-Symbol Analysis
Using v6's dynamic request.security() to simultaneously check multiple symbols or timeframes in one script.
⚠️ Real-World Limitations — Know Before You Invest
  • No Native Automated Trade Execution: PineScript alone cannot send real orders to a broker; full automation requires connecting alerts via Webhook to an external execution service.
  • Computational Resource Limits: Heavy scripts (nested loops, large arrays) may hit TradingView server Timeout errors.

Official PineScript Documentation and Resources

  • TradingView Inc. Pine Script v6 Language Reference Manual tradingview.com
    View Docs
  • TradingView Inc. Migration Guide — To Pine Script Version 6 tradingview.com
    View Docs

Ready to Build a PineScript v6 Indicator or Strategy?

ExpertNevees designs PineScript v6 indicators and strategies, and connects alerts via Webhook to automated execution in MetaTrader when needed.

Other Trading Programming Languages

The Primary AI and Automation Language

Python — AI, Broker and Exchange Connectivity

Python is the dominant language for AI development in algorithmic trading — from machine learning with TensorFlow/PyTorch/Scikit-learn to direct broker and exchange connectivity. Unlike MQL5 or cAlgo which are tied to one platform, Python connects to any platform via the official MetaTrader5 library, ZeroMQ, or crypto exchange APIs.

#1
World's #1 Machine Learning Language
ZMQ
Bridge to MetaTrader
CCXT
Connects to 100+ Crypto Exchanges
ONNX
Model Export to MQL5/cAlgo

Architecture of a Python-Based Trading System

📡
Connectivity Layer
Broker/Exchange Bridge
The official MetaTrader5 library for direct connection with no middleman, or ZeroMQ for real-time bidirectional connection to an EA; for crypto, CCXT connects to 100+ exchanges with a unified interface.
🧠
Model Layer
Machine Learning
TensorFlow/Keras and PyTorch for deep learning (LSTM, Transformer, CNN), Scikit-learn and XGBoost for classic models; Pandas/NumPy for time-series data preprocessing.
⚙️
Execution Layer
Order Management
Order management, risk-based volume calculation, and full logging of every decision for later tracking and system performance review.
🔄
ONNX Export
Deployment Bridge
A model trained in Python is converted to ONNX format via tf2onnx or torch.onnx.export, then runs directly inside MQL5 or cAlgo with GPU acceleration.

Connecting to MetaTrader and Sending an Order with Python

Python · MetaTrader5
import MetaTrader5 as mt5
import pandas as pd

# Official library talks directly to the terminal — no ZMQ bridge required
if not mt5.initialize():
    print("MT5 initialize failed:", mt5.last_error())
    quit()

symbol = "EURUSD"
account_info = mt5.account_info()
balance = account_info.balance

# ATR-based position sizing, same risk model used in the MQL5/cAlgo pages
rates = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_H1, 0, 15)
df = pd.DataFrame(rates)
atr = (df['high'] - df['low']).rolling(14).mean().iloc[-1]

risk_percent = 1.0
point = mt5.symbol_info(symbol).point
sl_points = atr / point * 1.5
tick_value = mt5.symbol_info(symbol).trade_tick_value
lot = round((balance * risk_percent / 100) / (sl_points * tick_value), 2)

price = mt5.symbol_info_tick(symbol).ask
request = {
    "action":    mt5.TRADE_ACTION_DEAL,
    "symbol":    symbol,
    "volume":    lot,
    "type":      mt5.ORDER_TYPE_BUY,
    "price":     price,
    "sl":        price - sl_points * point,
    "deviation": 10,
    "magic":     123456,
    "type_filling": mt5.ORDER_FILLING_IOC,
}
result = mt5.order_send(request)
if result.retcode != mt5.TRADE_RETCODE_DONE:
    print(f"Order failed, retcode={result.retcode}")

mt5.shutdown()

Common Mistakes in Python Trading Development

🛠️ Implementation Notes That Are Often Overlooked
  • Not Checking Order retcode: Calling order_send without checking result.retcode means order failures (invalid volume, market closed) go unnoticed.
  • Running a Heavy Model in the Live Trading Loop: Calling model.predict() on a large neural network every tick without batching or caching optimization introduces significant latency to real-time decisions.
  • Incorrect Timezone in Historical Data: Combining MT5 data (usually UTC or broker time) with other sources without explicit timezone alignment causes off-by-one errors in candle labeling.
  • No Connection-Drop Handling: Simple Python scripts without automatic reconnect logic halt completely on a brief internet outage or terminal restart, leaving open trades unmonitored.

For Which Use Cases is Python Suitable?

🧠
AI Model Training and Backtesting
The primary environment for training, Walk-Forward Validation, and evaluating all 13 models featured in the site's AI section.
🔗
Cross-Platform Bridge
Receiving a PineScript alert via Webhook and automatically executing an order on MetaTrader or a crypto exchange.
Crypto Trading Bots
Unified connectivity to 100+ exchanges via CCXT with a single programming interface.
📊
Analytics Dashboard and Reporting
Building a dashboard to monitor multiple accounts or EAs simultaneously with Streamlit or Plotly Dash.
🔄
ONNX Model Export for MQL5/cAlgo
Training a model in Python and exporting to ONNX for standalone, fast execution inside an EA or cBot without needing a live Python server.
📰
Sentiment Analysis and Alternative Data
Processing news, social media, or alternative data with NLP to generate additional input features for price-based models.
⚠️ Real-World Limitations — Know Before You Invest
  • Slower Execution Than C++/C#: Being interpreted, Python is unsuitable for sub-millisecond HFT strategies; exporting the model to ONNX and running it in MQL5/cAlgo is the standard solution for those cases.
  • MetaTrader5 Library is Windows-Only: The official MetaTrader5 Python library only works fully on Windows; Linux/Mac require Wine or a ZeroMQ bridge.

Official Documentation and Resources

  • MetaQuotes Ltd. MetaTrader5 Python Package — Official Documentation PyPI / mql5.com
    View Docs
  • CCXT Contributors CCXT — CryptoCurrency eXchange Trading Library GitHub / docs.ccxt.com
    View Docs

Ready to Build a Python-Based Trading System?

ExpertNevees implements AI models, broker/exchange connectivity, and ONNX model export for you in Python.

Other Trading Programming Languages

Model Selection Guide

Comparing 13 AI Trading Models

No single model wins in every situation. This page places the key metrics of all 13 models — from classic Random Forest to advanced Transformers — side by side, so you can pick the right one based on available data, compute resources, and strategy type.

13
Models Compared
3
Main Categories
6
Task Types
60–74%
Classification Accuracy Range

13 Models, 7 Key Criteria

Model Category Primary Task Min. Data GPU Need Training Time Accuracy/Quality
Random Forest Classic Trend Classification 1,000+ candles No Minutes 62–70%
XGBoost Classic Trend Classification 2,000+ candles No Minutes–1h (with Optuna) 68–74%
MLP Classic Signal Classification 2,000+ samples Optional Minutes 60–70%
Linear Factor Classic Regression / Ranking 500+ cross-sectional rows No Seconds–minutes IC ≈ 0.03–0.08
LSTM Deep Learning Time-Series Classification/Prediction 5,000+ candles Recommended 1–4h (GPU) 65–75%
GRU Deep Learning Time-Series Classification/Prediction 5,000+ candles Recommended 35–90min (GPU) 63–72%
Attention Deep Learning Classification + Interpretability 5,000+ candles Recommended 1–4h (GPU) 67–76%
Transformer (TFT) Deep Learning Multi-Horizon Forecasting 10,000+ candles Required Several hours Lowest P50 MAE among all models
CNN Deep Learning Visual Pattern Classification 5,000+ labelled patterns Recommended 1–3h (GPU) 50+ pattern classes
DDQN Advanced Reinforcement Learning (direct decision) Simulated environment + sufficient data Recommended Hours–days (episodic) Measured via cumulative Sharpe
TimeGAN Advanced Synthetic Data Generation 2,000+ real candles Recommended Several hours Discriminative Score ≈ 0.5 ideal
Autoencoder Advanced Dimensionality Reduction / Anomaly Detection 2,000+ samples Optional Minutes Lower reconstruction error is better
HRP Portfolio Advanced Portfolio Capital Allocation Daily multi-asset returns No Seconds Sharpe vs. Equal-Weight

Where to Start Based on Your Situation

🌱
Little Data, No GPU
Start with Random Forest or XGBoost. Both run on CPU, need only a few thousand candles, and give an honest baseline before investing in more complex models.
📈
Long-Term Trend Prediction with Enough History
With 5,000+ candles and GPU access, LSTM or GRU are the industry-standard choice. If interpretability also matters, choose Attention over plain LSTM.
Ultra-Fast Real-Time Execution
For scalping with sub-1ms latency, MLP deployed via ONNX gives the best speed-to-accuracy ratio; neither LSTM nor Transformer suit this use case.
🔭
Simultaneous Multi-Candle Forecasting
If you need to forecast 5–20 candles at once with sufficient budget/data, Transformer (TFT) is the only model on this list that does this natively with confidence intervals.
🎯
Position Management, Not Just Signals
If you want the model to learn entry timing and exit sizing/timing together (not just direction classification), DDQN is the only model here that outputs trading decisions directly.
🧩
Capital Allocation Across Assets/Strategies
This is a different problem from price prediction. HRP Portfolio works on multi-asset or multi-EA returns, not on single-symbol candles.
📌 Important Notes Before Choosing Any Model
  • High test accuracy is not enough: Always evaluate with Walk-Forward Validation or TimeSeriesSplit, never a simple random split. Every model on this page follows this principle in its sample code.
  • Classic models are not always inferior: Random Forest or XGBoost often reach accuracy close to LSTM with less data and no GPU. Only add complexity once it proves real, measured benefit.
  • Ensembling multiple models usually beats a single model: Combining Random Forest, XGBoost, and LSTM outputs via an MLP layer or weighted voting is typically more stable than relying on one model alone.
  • Markets change, so must the model: Every deep and classic model on this page needs periodic retraining (daily to monthly, depending on the model). An unretrained model becomes stale within weeks.
  • No model replaces risk management: Even the highest classification accuracy will not become a profitable strategy without correct position sizing, stop-loss, and drawdown control.

Start With One of These Models

Trading Automation · Decision Guide

Why Convert Your Trading Strategy Into a Bot?

No matter how carefully a trading strategy is designed, executing it manually leaves it exposed to human delay, fatigue, and emotional decision-making. Automating a strategy into a trading bot (Expert Advisor or cBot) shifts execution from a trader's in-the-moment judgment to predefined, backtestable rules — so the outcome no longer depends on the trader's psychological state at that exact moment.

<1s
Order Execution Time
24/7
Non-Stop Monitoring
0
Deviation From Strategy Rules
+5
Years of Backtest Data

Why Does Manual Execution Undermine Even a Good Strategy?

Behavioral finance research shows that the largest gap between a strategy's theoretical and actual results usually comes not from the strategy itself, but from the trader deviating from it during execution. Fear of losing gains leads to closing winners too early, greed leads to holding losing positions too long, and after a loss, the urge to recover quickly (revenge trading) pulls the trader away from their own rules. Even experienced traders unconsciously break their own written rules during periods of fatigue or high stress.

Six Technical Reasons to Convert a Strategy Into a Trading Bot

Instant, Delay-Free Execution
A trading bot converts a signal into an order the instant a condition is met, without human decision-making delay — a difference that can be decisive in entry-sensitive strategies.
🧠
Eliminating Emotional Error
A bot executes exactly according to its coded rules and is unaffected by fear, greed, or fatigue — resulting in 100% adherence to the strategy's core logic across every trade.
📊
Rigorous Backtesting Before Real Risk
Before going live, a coded strategy can be tested across years of historical data — something practically impossible to do with manual execution.
🌐
Simultaneous Multi-Market Coverage
A single bot can simultaneously monitor dozens of currency pairs, stocks, or cryptocurrencies — something a human trader's focus and mental bandwidth simply cannot match.
🕐
24/7 Monitoring Without Fatigue
In round-the-clock markets like forex and crypto, a bot running on a VPS stays active without interruption, never missing an opportunity or exit condition at any hour.
🛡️
Enforced Risk Management
Stop-loss, position size, and daily maximum-drawdown limits are defined in code and enforced without exception on every trade — even when a human trader would be tempted to ignore them.

When Is Automation NOT the Right Choice?

⚠️ Points to Consider Before Deciding to Automate
  • Strategy Without Precise Rules: If your entries and exits are still based on market 'feel' rather than clearly codeable rules, the strategy needs to be written down and made precise first — a bot cannot build logic that doesn't exist.
  • Heavy Reliance on Qualitative News Analysis: Strategies that depend on human interpretation of unpredictable events (political decisions, breaking news) are hard to fully automate and usually need a semi-automated model.
  • Insufficient Historical Data for Backtesting: Without enough data to test against, confidence in the bot's future performance has no scientific basis and increases real risk.
  • Expecting Guaranteed Profitability: Automation improves execution, it does not remove inherent market risk. A trading bot eliminates human deviation, but it does not guarantee profit.

How ExpertNevees Converts Your Strategy Into a Bot

1️⃣
Precise Rule Documentation
Your entry, exit, position sizing, and risk management rules are documented in unambiguous, codeable form.
2️⃣
Implementation as EA or cBot
Coding on your chosen platform (MQL4/MQL5, cAlgo, PineScript, or Python) with clean, maintainable structure.
3️⃣
Backtesting on Real Historical Data
Testing across years of real tick data with a full report of Win Rate, Profit Factor, and Max Drawdown before going live.
4️⃣
Trial Run on Demo Account
Before connecting to real capital, the bot's behavior is confirmed under live market conditions on a demo account.

Common Questions About Strategy Automation

Converting a strategy into a trading bot shifts execution from a human's in-the-moment decision to predefined rules. This eliminates emotional error, brings execution speed down to milliseconds, enables rigorous backtesting across years of historical data, and allows fatigue-free 24/7 execution.
Automation executes an existing strategy rather than building one; the trader must first have a strategy with defined entry, exit, and risk management rules. ExpertNevees can also help precisely define these rules in codeable form.
Not entirely. A trading bot still needs human oversight for unexpected situations (internet outages, abnormal market events, broker updates). ExpertNevees designs bots with automatic Telegram alerts, an emergency kill switch, and daily maximum-drawdown limits.
Yes. Documented manual strategies and existing PineScript scripts on TradingView can be converted into an Expert Advisor for MetaTrader or a cBot for cTrader, fully preserving the original entry, exit, and risk-management logic.

Next Steps