Exchanges, symbols, sources and timeframes

An indicator is not just a formula. When you define a block, you also choose which market it reads, which timeframe it uses to build its series, and which source it takes as input. These three dimensions are defined by symbol, timeframe, and source.

The [backtest] block defines the main symbol and the main timeframe of the strategy. If an indicator block does not define its own symbol or timeframe, it falls back to these defaults.

[backtest]
symbol    = "BINANCE:BTCUSDT"
timeframe = "240"

With this setup, any block that does not define symbol or timeframe runs by default on BINANCE:BTCUSDT on 240 (4h).

Exchanges, symbols, and timeframes

Whale‑E downloads market data through public endpoints. Available markets depend on the exchange. Market types that require authentication are not supported.

ExchangePrefixSupported markets
BinanceBINANCESpot, USD‑M futures, COIN‑M futures
KuCoinKUCOINSpot, USDT‑M perpetuals, COIN‑M perpetuals
BitgetBITGETSpot, USDT‑M/USDC‑M perpetuals, COIN‑M perpetuals
BybitBYBITSpot, Linear perpetuals (USDT‑M), Inverse perpetuals (COIN‑M)
OKXOKXSpot, Perpetual swaps (Linear/Inverse)

The symbol key accepts either a single string or an array of strings, both in [backtest] and in blocks that support it. Each value must use the EXCHANGE:SYMBOL format.

symbol = "BINANCE:BTCUSDT"
symbol = ["KUCOIN:ETHUSDT", "BINANCE:BTCUSDT"]

The timeframe key also accepts a single string or an array of strings. You can use a number of minutes from 1 to 1440, a number of days followed by D such as 1D or 7D, or a number of weeks followed by W such as 1W or 4W. D and W are accepted as aliases for 1D and 1W. Monthly timeframes such as 1M are not supported.

timeframe = "60"
timeframe = ["15", "D", "W"]

This syntax describes the timeframes Whale‑E accepts in a strategy. It does not mean that every exchange exposes every timeframe directly. When the requested timeframe is not provided natively by the exchange, Whale‑E rebuilds it from a lower timeframe supported by that market.

The source key

The source key defines the input series a block reads before it performs its own calculation. When a block supports it, source can be a single string or an array of strings, and each value takes one of two forms:

  • a standard candle source: open, close, high, low, hl2, hlc3, ohlc4, hlcc4, volume
  • the id of another indicator
source = "close"
source = ["close", "hlc3"]
source = "rsi_fast"

These two forms do not read their data from the same place, and that is what decides the role of symbol:

  • for a standard source, the block reads market data, and symbol selects which market to read it from;
  • for an indicator id, the block reuses the series already calculated upstream, and symbol is ignored.

Some blocks do not expose a source key because they always use fixed inputs such as high, low, and close. In these blocks, symbol defines the market and timeframe defines the time axis those fixed inputs are read from.

The timeframe is set by the block that consumes the series

A block always calculates on its own timeframe. If it does not define one, it falls back to the main timeframe from [backtest]. When its source is another indicator, it reuses the series already calculated but never inherits the producer timeframe: it projects the series onto its own axis. A series is always interpreted using the timeframe of the block or expression that consumes it, not the timeframe of the block that produced it.

In the example below, the strategy runs on 60, while rsi_4h is calculated on 240:

[backtest]
symbol    = "BINANCE:BTCUSDT"
timeframe = "60"

[[rsi]]
id        = "rsi_4h"
source    = "close"
timeframe = "240"
length    = 14

[[ma]]
id     = "ma_rsi"
source = "rsi_4h"
type   = "sma"
length = 10

ma_rsi does not define timeframe, so it uses the main 60 timeframe. When it reads rsi_4h, the RSI series calculated on 4-hour candles is projected onto the 60 axis: until a new 4-hour candle closes, the 1-hour candles reuse the last closed 4-hour RSI value.

The same rule applies to history. Here, series[1] means the value visible one 60 candle earlier on the axis of the block reading the series, not the previous 240 candle of the block that produced it. The same logic applies in conditional blocks, in indicators that read another indicator, and in [[custom_series]] formulas.

Reading a price from another market

Because symbol selects the market of a standard source, a block can read a price from a market other than the main symbol. Here, rsi_eth_daily is calculated on the daily close of BINANCE:ETHUSDT, while the strategy runs on BINANCE:BTCUSDT:

[backtest]
symbol    = "BINANCE:BTCUSDT"
timeframe = "240"

[[rsi]]
id        = "rsi_eth_daily"
source    = "close"
symbol    = "BINANCE:ETHUSDT"
timeframe = "D"
length    = 14

If you then use that series in the strategy running on 240, Whale‑E makes it visible on the 4-hour axis. Between two daily closes, the engine reuses the latest known daily value.

Mixing prices and indicators in a single block

A single block can combine standard sources such as close and indicator ids. symbol then applies only to standard sources; for an id, the block reuses the series already produced. If source contains only ids, the block-level symbol key is ignored entirely.

[backtest]
symbol    = "BINANCE:BTCUSDT"
timeframe = "240"

[[price]]
id        = "eth_daily"
symbol    = "BINANCE:ETHUSDT"
timeframe = "D"

[[ma]]
id     = "ma_mix"
source = ["close", "eth_daily"]
symbol = ["BINANCE:BTCUSDT", "BINANCE:SOLUSDT"]
type   = "sma"
length = 10

Here, ma_mix produces three grids:

  • one moving average built from close on BINANCE:BTCUSDT
  • one moving average built from close on BINANCE:SOLUSDT
  • one moving average built from the eth_daily series

The two symbols apply only to close: eth_daily keeps the one defined in its own block.

Combining sources, symbols, and timeframes

Qualitative values such as source, symbol, and timeframe create distinct grids. As soon as a block accepts an array for one of these keys, one combination is generated for each available choice.

[backtest]
symbol    = ["BINANCE:BTCUSDT", "BINANCE:ETHUSDT"]
timeframe = "240"

[[ma]]
id        = "ma_fast"
source    = ["close", "hlc3"]
timeframe = ["240", "D"]
type      = "sma"
length    = 20

In this example, the moving-average block offers:

  • 2 main symbols from [backtest]
  • 2 sources (close and hlc3)
  • 2 block timeframes (240 and D)

This creates 2 × 2 × 2 = 8 distinct grids. The numeric parameter length does not create new grids; it is explored inside each grid.