Loading…
Menu

Blog Home

July 28, 2026

Defining Your Edge (Part 1): Pairing a Discrete Fourier Transform with a Spiking Neural Network in a Trading Robot

Learn how pairing a Discrete Fourier Transform (DFT) with a Spiking Neural Network (SNN) creates a non-correlated algorithmic trading edge in MQL5.

Introduction

Modern algorithmic trading systems frequently suffer from data bloat and an over-reliance on correlated, lagging indicators. To build a genuine statistical edge, traders need non-correlated, complementary systems.

This series introduces a diverse library of specialized algorithm-neural network pairings, starting with an unconventional hybrid: a Discrete Fourier Transform (DFT) cycle decoder paired with a biological Leaky Integrate-and-Fire (LIF) Spiking Neural Network (SNN). Our goal is to isolate hidden market frequencies and confirm trade timing through temporal pressure.

Attached files Download

The Fourier–SNN Architecture

This pairing combines spectral decomposition with time-based confirmation to minimize false signals:

  • The Fourier Engine (Cycle Decoder): Traditional indicators lag because they rely on static lookback windows. The DFT dynamically transforms price action or indicator series (such as MACD and RSI) into predictive wave functions, identifying potential cyclical turning points before they visually unfold on a chart.

  • The SNN Engine (Temporal Execution Gate): The SNN controls execution timing by converting market inputs into synaptic stimulation. Rather than generating immediate, noisy signals, inputs charge an internal digital "membrane potential." This potential decays when momentum stalls, but accumulates when multiple market variables align over consecutive bars. A trade fires only when the accumulated voltage crosses a strict firing threshold, ensuring that cyclical reversals are verified by sustained momentum.

       +----------------------------+
       | Raw Price / MACD / RSI     |
       +----------------------------+
                     |
                     v
       +----------------------------+
       | Discrete Fourier Transform | (Extracts Dominant Cycle)
       +----------------------------+
                     |
                     v
       +----------------------------+
       | Spiking Neural Network     | (Accumulates Temporal Voltage)
       +----------------------------+
                     |
                     v
       +----------------------------+
       | Order Execution Spike      | (Triggers when V_m >= V_th)
       +----------------------------+

Ideal Market Regime

Systems often fail when transitioning between regimes (e.g., shifting from a trend to a range). This hybrid specifically targets regime transitions: the Fourier engine filters out local whipsaws by decoding the dominant cycle length, while the SNN prevents overreacting to short-term spikes by requiring temporal validation across multiple bars.

Algorithmic Analogy & Mathematical Foundations

Combining Moving Averages with traditional artificial neural networks (ANNs) often yields brittle models. Visualizing the market as a turbulent ocean highlights how our hybrid architecture differs:

The Analogy: The Discrete Fourier Transform acts as a submarine sonar, ignoring surface chop to map the rhythmic tide waves deep underwater. The Spiking Neural Network acts as a disciplined torpedo commander, listening to incoming sonar pings to build internal pressure, launching an attack only when the threshold is crossed.

Algorithmic Analogy & Mathematical Foundations

Combining Moving Averages with traditional artificial neural networks (ANNs) often yields brittle models. Visualizing the market as a turbulent ocean highlights how our hybrid architecture differs:

The Analogy: The Discrete Fourier Transform acts as a submarine sonar, ignoring surface chop to map the rhythmic tide waves deep underwater. The Spiking Neural Network acts as a disciplined torpedo commander, listening to incoming sonar pings to build internal pressure, launching an attack only when the threshold is crossed.

1. The Spectral Engine (Discrete Fourier Transform)

We treat rolling price history as a finite, discrete time-series vector x[n] of length N, where n \in \{0, 1, \dots, N-1\} represents the bar index. The DFT maps this spatial vector into a frequency spectrum X[k], where each complex coefficient corresponds to a harmonic frequency index k:


f1


Where:

  • X[k] is the k-th complex frequency coefficient representing phase and amplitude.

  • k is the discrete frequency index (k = 0, 1, \dots, N-1).

  • N is the historical window size.

  • x[n] is the input signal at bar n (raw price, MACD line, or RSI).

  • j = \sqrt{-1} is the imaginary unit.

Using Euler's Formula (e^{-j\theta} = \cos\theta - j\sin\theta), we decompose this complex expression into real and imaginary Cartesian components:

f2
f3

For each frequency component  k, we derive the structural wave amplitude A[k] and phase shift \phi[k] using the Pythagorean theorem and a two-argument arctangent function:

f4

f5

After looping through frequencies up to the Nyquist limit (N/2), we isolate the dominant frequency component  k_{\text{dom}} possessing the maximum amplitude A[k]. The projected state of the dominant wave y_{\text{pred}} is calculated as:
f6

This normalizes the continuous cycle output strictly within the range $[-1.0, 1.0]$, representing cyclic troughs and peaks, respectively.

2. The Neural Engine (Leaky Integrate-and-Fire)

Instead of passing inputs through static activation functions, the Leaky Integrate-and-Fire (LIF) neuron treats inputs as continuous electrical currents that modify an internal variable: the membrane potential (V_m).

In continuous time, the membrane potential dynamics obey a first-order linear differential equation:

f7

Where:

  • \tau_m is the membrane time constant (\tau_m = R \cdot C_m), controlling the rate of passive voltage leakage back to baseline.

  • V_m(t) is the instantaneous membrane potential.

  • R is the membrane resistance.

  • I(t) is the incoming pre-synaptic current.

In our discrete-time bar processing system, we model this leakage using a discrete decay factor $\gamma \in [0, 1]$. The membrane potential $V_m[t]$ updates according to incoming pre-synaptic signals:

f8

Where:

  • V_m[t-1] is the prior bar's membrane potential (memory state).

  • \gamma is the discrete decay rate (InpSnnDecay).

  • w_i is the static synaptic weight assigned to signal channel i.

  • S_i[t] is the normalized pre-synaptic input from channel i at bar t.

The neuron evaluates V_m[t] against a predefined firing threshold V_{\text{th}} (InpSnnThreshold). The output spike function H[t] is defined as:

f9

When a spike occurs (H[t] = 1), an instantaneous hyperpolarization reset purges the inner state back to baseline (V_m[t] \to 0) to prevent runaway feedback loops.

MQL5 Implementation

Below is a pure, modular MQL5 implementation of the hybrid trading robot. To maintain absolute control over tick processing and neural state evaluation, this design avoids the overhead of MQL5's CExpert framework, leveraging CTrade and CSymbolInfo strictly for execution safety.

1. Environment Setup & Modes


//--- Operational Modes for Diagnostic Testing

enum EMode

  {

   MODE_FOURIER_PRICE = 0,     // Fourier Transform on Raw Price Cycles

   MODE_FOURIER_MACD  = 1,     // Fourier Transform on MACD Turning Points

   MODE_FOURIER_RSI   = 2,     // Fourier Transform on RSI Turning Points

   MODE_FOURIER_COMBINED = 4,  // Combined Fourier (Price + MACD + RSI)

   MODE_PRICE_ACTION_SNN = 5,  // Pure Price Action via SNN

   MODE_INDICATOR_PATTERN = 6, // Indicator Momentum Divergences via SNN

   MODE_HYBRID_FULL   = 7      // Full Hybrid (Fourier Cycle + SNN Gate)

  };

2. The DFT Cycle Engine

//+------------------------------------------------------------------+

//| Mathematical Engine: Discrete Fourier Transform (DFT)            |

//+------------------------------------------------------------------+

double CalculateFourierTurningPoint(const double &data[])

  {

   int N = ArraySize(data);

   if(N == 0) return 0.0;


   double max_amplitude = 0.0;

   double dominant_phase = 0.0;

   int dominant_k = 1;


   // Loop over non-DC frequencies up to the Nyquist limit (N / 2)

   for(int k = 1; k < N / 2; k++)

     {

      double real = 0.0;

      double imag = 0.0;


      for(int n = 0; n < N; n++)

        {

         double angle = 2.0 * M_PI * k * n / N;

         real += data[n] * MathCos(angle);

         imag -= data[n] * MathSin(angle);

        }


      double amplitude = MathSqrt(real * real + imag * imag);

      if(amplitude > max_amplitude)

        {

         max_amplitude = amplitude;

         dominant_phase = MathArctan2(imag, real);

         dominant_k = k;

        }

     }


   // Reconstruct dominant harmonic projected to current bar (N - 1)

   return MathSin(dominant_phase + (2.0 * M_PI * dominant_k * (N - 1) / N));

  }

3. The Spiking Neural Network Engine

//+------------------------------------------------------------------+

//| AI Engine: Leaky Integrate-and-Fire (LIF) Spiking Neuron         |

//+------------------------------------------------------------------+

int EvaluateSpikingNeuralNetwork(double inputSignal1, double inputSignal2, double inputSignal3)

  {

   // Synaptic Weights: Fourier (w1), RSI Momentum (w2), MACD Momentum (w3)

   static const double w1 = 0.6, w2 = 0.4, w3 = 0.5;

   

   // Persistent Membrane State Variables

   static double membranePotentialBuy  = 0.0;

   static double membranePotentialSell = 0.0;


   // 1. Passive Leakage Phase

   membranePotentialBuy  *= (1.0 - InpSnnDecay);

   membranePotentialSell *= (1.0 - InpSnnDecay);


   // 2. Charge Integration Phase

   if(inputSignal1 > 0)

      membranePotentialBuy += inputSignal1 * w1;

   else

      membranePotentialSell += MathAbs(inputSignal1) * w1;


   if(inputSignal2 > 0)

      membranePotentialBuy += inputSignal2 * w2;

   else

      membranePotentialSell += MathAbs(inputSignal2) * w2;


   if(inputSignal3 > 0)

      membranePotentialBuy += inputSignal3 * w3;

   else

      membranePotentialSell += MathAbs(inputSignal3) * w3;


   // 3. Threshold & Action Potential Phase

   bool buySpike  = (membranePotentialBuy  >= InpSnnThreshold);

   bool sellSpike = (membranePotentialSell >= InpSnnThreshold);


   if(buySpike && !sellSpike)

     {

      membranePotentialBuy = 0.0; // Hyperpolarization Reset

      return 1;

     }

   if(sellSpike && !buySpike)

     {

      membranePotentialSell = 0.0; // Hyperpolarization Reset

      return -1;

     }


   return 0; // No Action Potential

  }

4. Signal Integration Pipeline

//+------------------------------------------------------------------+

//| Generates Hybrid Trading Signals                                |

//+------------------------------------------------------------------+

int ExecuteHybridFull()

  {

   double closePrices[];

   ArrayResize(closePrices, InpFourierWindow);

   if(CopyClose(_Symbol, _Period, 0, InpFourierWindow, closePrices) < InpFourierWindow)

      return 0;


   // Extract primary harmonic wave from close prices

   double fourierPriceSignal = CalculateFourierTurningPoint(closePrices);


   // Pull indicator historical buffers

   double rsiValues[], macdValues[];

   ArrayResize(rsiValues, 5);

   ArrayResize(macdValues, 5);


   if(CopyBuffer(hRSI, 0, 0, 5, rsiValues) < 5) return 0;

   if(CopyBuffer(hMACD, MAIN_LINE, 0, 5, macdValues) < 5) return 0;


   // Derive spatial velocity vectors (5-bar directional momentum)

   double rsiMomentum  = rsiValues[0] - rsiValues[4];

   double macdMomentum = macdValues[0] - macdValues[4];


   if(InpUseSNN)

     {

      return EvaluateSpikingNeuralNetwork(fourierPriceSignal, rsiMomentum, macdMomentum);

     }


   return 0;

  }

5. Position Management & Execution Safety

//+------------------------------------------------------------------+

//| Position Management & Execution Logic                            |

//+------------------------------------------------------------------+

void ManagePositions(int signalType)

  {

   ExtSymbolInfo.RefreshRates();

   bool hasOpenPosition = false;

   int totalPositions = PositionsTotal();


   // Iterate backwards to safely inspect open positions

   for(int i = totalPositions - 1; i >= 0; i--)

     {

      ulong ticket = PositionGetTicket(i);

      if(ticket != 0 && 

         PositionGetString(POSITION_SYMBOL) == _Symbol && 

         PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)

        {

         long type = PositionGetInteger(POSITION_TYPE);


         // Close position on opposite SNN spike

         if((type == POSITION_TYPE_BUY && signalType == -1) ||

            (type == POSITION_TYPE_SELL && signalType == 1))

           {

            ExtTrade.PositionClose(ticket, InpSlippage);

           }

         else

           {

            hasOpenPosition = true; // Signal matches current position

           }

        }

     }


   // Execute new entries on clear state

   if(!hasOpenPosition)

     {

      if(signalType == 1)

        {

         double price = NormalizeDouble(ExtSymbolInfo.Ask(), ExtSymbolInfo.Digits());

         if(!ExtTrade.Buy(InpLotSize, _Symbol, price, 0.0, 0.0))

            PrintFormat("Buy Failed. Ask=%G Error=%d", price, GetLastError());

        }

      else if(signalType == -1)

        {

         double price = NormalizeDouble(ExtSymbolInfo.Bid(), ExtSymbolInfo.Digits());

         if(!ExtTrade.Sell(InpLotSize, _Symbol, price, 0.0, 0.0))

            PrintFormat("Sell Failed. Bid=%G Error=%d", price, GetLastError());

        }

     }

  }

Strategy Tester Results & Performance Analysis

Testing Sandbox

  • Asset: GBPJPY

  • Timeframe: 2-Hour (H2)

  • In-Sample Optimization Period: 2025.01.01 to 2026.01.01

  • Out-of-Sample Forward Period: 2026.01.01 to 2026.05.01

  • Key Optimized Inputs:

    • InpFourierWindow: 90 bars

    • InpSnnDecay: 0.82 (High 82% leakage rate per bar to eliminate stale noise)

    • InpSnnThreshold: 1.40

MetricResult
Initial Capital$10,000.00
Net Profit$41,305.70
Win Rate62.5%
Total Trades56
Profit Factor1.64
Max Equity Drawdown7.57%

The aggressive decay rate (82%) ensures that memory clears quickly, forcing the network to fire action spikes strictly when price harmonics, momentum, and velocity align within a tight 1-to-2 bar window.


Conclusion & Future Roadmap

This test setup supports the viability of combining a spectral engine with a time-based neural gate. By routing Fourier wave data through a Leaky Integrate-and-Fire neuron, the hybrid engine effectively isolates key market turning points while reducing noise.

Next Steps for Further Exploration

  1. Dynamic Synaptic Plasticity: Implementing Spike-Timing-Dependent Plasticity (STDP) to dynamically adjust input weights ($w_1, w_2, w_3$) based on real-time trade performance.

  2. Radix-2 FFT Migration: Replacing the $O(N^2)$ direct Discrete Fourier Transform loop with a Fast Fourier Transform (FFT) to optimize computation speed for larger lookback windows.

  3. Volatility-Scaled Decay Rates: Dynamically scaling the membrane decay parameter (InpSnnDecay) based on ATR (Average True Range) to account for shifting market regimes automatically.


V 0.0.2