The APEX:E3 API offers users access to a powerful backtesting framework.

The following information uses javascript examples, but they can be easily converted to Python or any other programming language of choice.

Visit the APEX:E3 github repo to view how to run backtests in Python and NodeJs.

The run-backtest.js contains an example of how to run a backtest.


The trading strategy buys when the EMA 50 crosses the EMA 200 from below and sells when the opposite condition arises.

The file contains the required trading strategy and indicator configurations:

let indicatorParams = {
indicator1: { type: 'ema', period: 50, priceComponent: 'close' },
indicator2: { type: 'ema', period: 200, priceComponent: 'close'}
};

let strategyParams = {
entryIndicator1: 'ema_ind1',
entryOperator: 'CROSSES_BELOW',
entryIndicator2: 'ema_ind2',
exitIndicator1: 'ema_ind1',
exitOperator: 'CROSSES_ABOVE',
exitIndicator2: 'ema_ind2',
stopLoss: 0.1
}


This indicator params specifiy the indicators and their parameters.

The strategy params specify the trade entry and exit conditions.

These configurations can be supplied as parameters to the runBacktest function:

let results = await apexe3.runBacktest(
10000,
'COINBASEPRO',
'BTC',
'USD',
'2018-01-01',
'2020-12-31',
indicatorParams,
strategyParams,
'DIGITAL',
'SPOT');

  • 10000 - represents the startup capital

  • COINBASEPRO - the exchange

  • BTC - the base

  • USD - the quote

  • 2018-01-01 - start date

  • 2020-12-31 - end date

  • indicatorParams - indicator settings

  • strategyParams - strategy settings

Backtest MACD strategies

Strategy settings (strategyParams)

  • Entry condition: Buy when the macd is greater than the macd histogram

  • Exit condition: Sell when the macd signal is less than the macd

  • Stop Loss: Sell if losses exceed 5%

Indicator settings (indicatorParams):

  • Indicator 1 has type price

  • Indicator 2 has type macd. Because it is of type macd, we have to specify the shortPeriod, longPeriod and signal period. The priceComponent is used to define which part of the open high low close to use when calculating the macd. In the example below, close is used. If the priceComponent is not specified, close is used by default.

Note: the macd indicator has the following attributes, which are referenced in the strategy:

  • macd

  • histogram

  • signal

Read about the Macd indicator here.


***single quotes will be needed for all attributes and values when using Python

let indicatorParams = {

indicator1: { type: 'price' },
indicator2: { type: 'macd', shortPeriod: 12, longPeriod: 26,
signalPeriod: 9, priceComponent: 'close' }
};

let strategyParams = { entryIndicator1: 'macd_ind2',
entryOperator: '>',
entryIndicator2: 'histogram_ind2',
exitIndicator1: 'signal_ind2',
exitOperator: '<',
exitIndicator2: 'macd_ind2',
stopLoss: 5
}

*** notice the _ind2 suffix in the strategyParams: _ind2 refers to indicator2 in the indicatorParams. macd was specified within indicator2, therefore the macd indicators attributes have a _ind2 suffix in the strategyParams.

Backtest RSI strategies

Strategy settings (strategyParams)

  • Entry condition: Buy when the rsi is less than 30

  • Exit condition: Sell when the rsi is greater than 70

  • Stop Loss: Sell if losses exceed 5%

Indicator settings (indicatorParams):

  • Indicator 1 has type price

  • Indicator 2 has type rsi. Because it is of type rsi, we have to specify the period.
    You can replace rsi with ema or sma.
    The priceComponent is used to define which part of the open high low close to use when calculating the rsi. In the example below, close is used. If the priceComponent is not specified, close is used by default.

Read more about the RSI here.

***single quotes will be needed for all attributes and values when using Python

let indicatorParams = {
indicator1: { type: 'price' },
indicator2: { type: 'rsi', period: '14'}
};

let strategyParams = {
entryIndicator1: 'rsi_ind2',
entryOperator: '<',
entryIndicator2: '30',
exitIndicator1: 'rsi_ind2',
exitOperator: '>',
exitIndicator2: '70',
stopLoss: 5
};

Backtest EMA or SMA strategies

Strategy settings (strategyParams)

  • Entry condition: Buy when the EMA 50 crosses the EMA 200 from below

  • Exit condition: Sell when the EMA 50 crosses the EMA 200 from above

  • Stop Loss: Sell if losses exceed 1%

Indicator settings (indicatorParams):

  • Indicator 1 has type ema. Because it is of type ema, we have to specify the period (50).

  • Indicator 2 has type ema. Because it is of type ema, we have to specify the period (200).
    You can replace ema with sma, if you want to test the SMA.
    The priceComponent is used to define which part of the open high low close to use when calculating the ema or sma. In the example below, close is used. If the priceComponent is not specified, close is used by default.

Read more about EMA here and SMA here

Replace EMA with SMA if the indicatorParams setup uses SMA.

***single quotes will be needed for all attributes and values when using Python

let indicatorParams = {
indicator1: { type: 'ema', period: 50, priceComponent: 'close' },
indicator2: { type: 'ema', period: 200, priceComponent: 'close'}
};

let strategyParams = {
entryIndicator1: 'ema_ind1',
entryOperator: 'CROSSES_BELOW',
entryIndicator2: 'ema_ind2',
exitIndicator1: 'ema_ind1',
exitOperator: 'CROSSES_ABOVE',
exitIndicator2: 'ema_ind2',
stopLoss: 1
}

***Notice the use of _ind1 and _ind2 suffixes in the strategyParams

Backtest Bollinger Bands strategies

This strategy uses 2 indicators - EMA and Bollinger Bands:

Strategy settings (strategyParams)

  • Entry condition: Buy when the close price crosses the bollinger lower band from below

  • Exit condition: Sell when the close price crosses the EMA from above

  • Stop Loss: Sell if losses exceed 5%

Indicator settings (indicatorParams):

  • Indicator 1 has type bollinger. Because it is of type bollinger, we have to specify the period, stdDevUpper and stdDevLower values.

  • Indicator 2 has type ema. Because it is of type ema, we have to specify the period.

    The priceComponent is used to define which part of the open high low close to use when calculating the bollinger . In the example below, close is used. If the priceComponent is not specified, close is used by default.

Note: the bollinger indicator has the following attributes, which are refrenced in the strategy:

  • bollingerUpper

  • bollingerMiddle

  • bollingerLower

Read more about EMA here and bollinger bands here.

***single quotes will be needed for all attributes and values when using Python

let indicatorParams = {
indicator1: { type: 'bollinger',
period: 20,
stdDevUpper: 2,
stdDevLower: 2
},
indicator2: { type: 'ema', period: 30}
};

let strategyParams = {
entryDirection: 'long',
entryIndicator1: 'close',
entryOperator: 'CROSSES_BELOW',
entryIndicator2: 'bollingerLower_ind1',
exitIndicator1: 'close',
exitOperator: 'CROSSES_ABOVE',
exitIndicator2: 'ema_ind2',
stopLoss: 5
}

***Notice the use of _ind1 and _ind2 suffixes in the strategyParams

Data Generated by the Backtest

Once a backtest is successfully run, 3 data sets are generated:

  • analysis - this is a summary report showing how the backtest performed

  • trades - this contains all the trades associated with the backtest

  • maketData - this contains the OHLCV prices, relevant indicator values and entry and exit prices used in the backtest

Did this answer your question?