Topic: In search of the fastest Strategy Generator

Hello Traders,

I spent a lot of time analysing the performance of EA Studio and Express Generator's indicators over the last two weeks.

I designed a simulation program to load a particular data file and calculate the Moving Average and RSI indicators.

I made it first in JavaScript using the code from Express Generator.

After testing, refactoring and optimisation, I made a similar program in C.

The C version was 2 times faster for calculating the Moving Average and 15% faster for RSI.

Then, I made it in Fortran. It took me a week to read several Fortran books and to "master" the language.
Expectedly, the Frotran version was about 30% faster than the C version.

I continued researching and learned that Fortran is faster than C, not because of the features it has but because of the features it does not have.

I almost accepted those results. However, I wondered "why" it happens and the particular reason.
Several days later, I learned a lot about modern 64-bit Assembly, disassembled the programs and studied the code.

I was very happy when I found the reason the Fortran code was faster. (It was rather geeky, but if you are interested, I may explain).

On that latter, I decided to use this knowledge to improve my current code.

The first result is compiling C to a similar assembly as Fortran. It practically made the program very similar.
Then, I did the same in JavaScript.

And ... surprise, surprise. All three programs, Fortran, C, and JavaScript, performed similarly.

Calculating 2000 indicators with periods from 1 to 200 on 200k bars.

=======================================
Indicator  Language       Time      
---------------------------------------
MA         Fortran        1.70 sec 
MA         C              1.72 sec 
MA         JavaScript     1.72 sec 
---------------------------------------
RSI        Fortran        6.81 sec 
RSI        C              6.80 sec 
RSI        JavaScript     6.84 sec 
=======================================

The tests are made under Linux with the latest GFortran, GCC, and NodeJS.
The optimisation level for Fortran and C was "O2".

I also made a test with the optimisation level "-Ofast." Both C and Fortran showed double improvement in performance.
"Ofast" option is not suitable for code distribution.

I'm going to use this knowledge in the PineGen generator.

Re: In search of the fastest Strategy Generator

I just uploaded the code at GitHub: https://github.com/PopovMP/ma_benchmark

You may take a look or even download and test it. (It will work on Linux and probably MakOS).

Here is how the Simple MA looks in JS:

function simpleMA(maRef, price, period, shift) {
    maRef.fill(0.0, 0, period + shift - 1);

    let sum = 0.0;
    for (let bar = 0; bar < period; ++bar) {
        sum += price[bar];
    }

    let prev = sum / period;
    maRef[period + shift - 1] = prev;

    for (let bar = period, len = price.length - shift; bar < len; ++bar) {
        let temp;
        temp  = price[bar];
        temp -= price[bar - period];
        temp /= period;
        prev += temp;
        maRef[bar + shift] = prev;
    }
}

in C

void simpleMA(double* restrict ma_ref, const double* const price,
              const int bars, const int period, const int shift) {
    memset(ma_ref, 0.0, (period + shift - 1) * sizeof(double));

    double sum = 0.0;
    for (int bar = 0; bar < period; ++bar) {
        sum += price[bar];
    }

    double prev = sum / period;
    ma_ref[period + shift - 1] = prev;

    for (int bar = period, len = bars - shift; bar < len; ++bar) {
        register double temp;
        temp  = price[bar];
        temp -= price[bar - period];
        temp /= period;
        prev += temp;
        ma_ref[bar + shift] = prev;
    }
}

and Fortran:

subroutine simple_ma(ma_ref, price, bars, period, shift)
    real(real64), dimension(:), intent(inout) :: ma_ref
    real(real64), dimension(:), intent(in)    :: price
    integer,                    intent(in)    :: bars
    integer,                    intent(in)    :: period
    integer,                    intent(in)    :: shift

    real(real64) :: temp, prev, sum
    integer      :: bar

    ma_ref(1:period + shift) = 0.0_real64

    sum = 0.0_real64
    do bar = 1, period
        sum = sum + price(bar)
    end do

    prev = sum / period
    ma_ref(period + shift) = prev

    do bar = period + 1, bars - shift
        temp = price(bar)
        temp = temp - price(bar - period)
        temp = temp / period
        prev = prev + temp
        ma_ref(bar + shift) = prev
    end do
end subroutine simple_ma

Trade Safe!