Strategy System

How strategies are defined — the DSL structure, all available functions, operators, and a complete example.

Overview

A strategy is a JSON object with five top-level keys:configuration, inputs,conditions, score, and decision. Each serves a distinct role in the evaluation pipeline.

The strategy is passed to the API as a serialized JSON string in thestrategySnapshotJson field. Internally, the engine parses and evaluates it against historical OHLCV data candle by candle.
configurationinputsconditionsscoredecision

#Configuration

Defines the execution timeframe. The engine uses this to determine which candle interval to evaluate the strategy against.

configuration
"configuration": {
  "timeframe": "1H"
}

Supported timeframes

ValueDescription
15M15-minute candles
30M30-minute candles
1H1-hour candles (most common)
2H2-hour candles
4H4-hour candles
1DDaily candles

#Inputs

Inputs are named, reusable computed values. They are evaluated once per candle before conditions are checked. An input can reference market data, built-in functions, or other previously defined inputs.

inputs
"inputs": {
  "emaFast":   "ema(close, 9)",
  "emaSlow":   "ema(close, 21)",
  "rsiValue":  "rsi(close, 14)",
  "atrValue":  "atr(14)",
  "volSpike":  "volumeSpike(1.5)"
}

Available market data

closeopenhighlowvolume

Rules

  • Input names must be unique.
  • Inputs can reference other inputs defined above them (order matters).
  • Circular references are not allowed.
  • Input expressions must resolve to a numeric value.

#Conditions

Conditions are named boolean expressions. They are evaluated per candle using inputs, market data, and comparison operators. A condition must resolve to true orfalse.

conditions
"conditions": {
  "trendUp":    "emaFast > emaSlow",
  "rsiHealthy": "rsiValue > 40 AND rsiValue < 65",
  "breakout":   "crossUp(close, emaSlow)",
  "volConfirm": "volSpike > 1"
}

Supported operators

OperatorTypeExample
>Relational"rsiValue > 40"
<Relational"rsiValue < 70"
>=Relational"score >= 50"
<=Relational"atrValue <= 200"
==Equality"timeframe == 1H"
!=Equality"close != open"
ANDLogical"condA AND condB"
ORLogical"condA OR condB"

Rules

  • All variables referenced in a condition must be defined in inputs.
  • Conditions must resolve to a boolean. A numeric value of 0 is false; non-zero is true.
  • No future data may be referenced. All expressions use only current or past candle data.
  • Randomness is not permitted — strategies must be fully deterministic.

#Score

The score block assigns a numeric weight to each condition. On every candle, the engine sums the scores of all conditions that evaluate to true. This total is the candle's score.

score
"score": {
  "trendUp":    20,
  "rsiHealthy": 30,
  "breakout":   40,
  "volConfirm": 10
}
💡
The score system is what makes EmidLabs strategies compositional. Rather than requiring all conditions to be true simultaneously, you define a threshold the combined score must reach. This mirrors how real conviction is built — multiple aligned signals, not a rigid checklist.

The score values are arbitrary integers. What matters is their relative weight. A condition with score 40 contributes twice as much as one with score 20.

#Decision

The decision block defines the entry rule. The engine evaluates this expression per candle after computing the total score. When it evaluates to true, a new trade is opened at the close of that candle.

decision
"decision": {
  "entry": "score >= 50"
}

The decision expression can reference score (the total for the candle) as well as any defined condition by name.

Combined decision example
"decision": {
  "entry": "score >= 50 AND breakout"
}

#Built-in Functions

Indicators

FunctionDescription
ema(series, period)Exponential moving average of series over period candles.
sma(series, period)Simple moving average of series over period candles.
rsi(series, period)Relative Strength Index. Returns 0–100.
atr(period)Average True Range over period candles.

Signals

FunctionDescription
crossUp(a, b)Returns true on the candle where a crosses above b.
crossDown(a, b)Returns true on the candle where a crosses below b.

Series operators

FunctionDescription
highest(series, n)Highest value of series over the last n candles.
lowest(series, n)Lowest value of series over the last n candles.
change(series)Difference between the current and previous candle value.

Volume

FunctionDescription
volumeSma(period)Simple moving average of volume over period candles.
volumeSpike(multiplier)Returns current volume / volumeSma. >1 means volume is above average.

Price action

FunctionDescription
body()Absolute difference between open and close.
range()Absolute difference between high and low.
upperWick()Size of the upper wick: high - max(open, close).
lowerWick()Size of the lower wick: min(open, close) - low.

Math

FunctionDescription
abs(x)Absolute value.
min(a, b)Minimum of two values.
max(a, b)Maximum of two values.

#Full Strategy Example

A trend-following strategy with EMA crossover, RSI confirmation, and volume spike entry.

strategy.json — Trend + RSI + Volume
{
  "configuration": {
    "timeframe": "1H"
  },
  "inputs": {
    "emaFast":  "ema(close, 9)",
    "emaSlow":  "ema(close, 21)",
    "rsiValue": "rsi(close, 14)",
    "volRatio": "volumeSpike(1.3)"
  },
  "conditions": {
    "trendUp":    "emaFast > emaSlow",
    "rsiHealthy": "rsiValue > 40 AND rsiValue < 65",
    "volConfirm": "volRatio > 1",
    "crossover":  "crossUp(emaFast, emaSlow)"
  },
  "score": {
    "trendUp":    20,
    "rsiHealthy": 25,
    "volConfirm": 15,
    "crossover":  40
  },
  "decision": {
    "entry": "score >= 60"
  }
}
💡
In this example, entry requires either a crossover (40pts) + any one other signal, or all three non-crossover conditions simultaneously (60pts). The crossover alone is not enough — which prevents false triggers in noisy markets.

Invalid Patterns

What to avoid

  • Referencing undefined inputs in conditions or the decision block.
  • Using conditions that do not resolve to a boolean (e.g., a numeric expression).
  • Referencing future data (lookahead bias).
  • Circular input references.
  • Using randomness or non-deterministic logic of any kind.
Invalid — undefined variable
"conditions": {
  "breakout": "close > resistance"  // ERROR: 'resistance' not defined in inputs
}