Section 01

Backtest API Reference

Complete reference for the SentimenTrader Backtest API. Submit strategy parameters as structured JSON, monitor execution via task polling, and retrieve backtest results — covering 4 rule types, 16 condition operators, and 3 signal categories.

💡 Auto-Detection & Defaults
Signal types are auto-detected from indicator names — you don't need to specify signal.type. All missing parameters fall back to frontend-aligned defaults automatically. See Defaults and Signal sections for details.
⚡ Quickstart — 30 seconds to your first backtest
// 1. Copy this. 2. Replace email & password. 3. Run.
curl https://st-tools.sentimentrader.com/backtest/start_backtest \
  -u "[email protected]:your-password" \
  -H "Content-Type: application/json" \
  -d '{
    "symbol": "SPY",
    "entry": {
      "rule_type": "SingleCondition",
      "condition": {
        "signal": { "name": "Short Term Combined Model" },
        "rule": { "comparison_type": "SV", "type_name": "<", "comparison_value": "-10" }
      }
    },
    "exit": {
      "rule_type": "RiskControlOnly"
    }
  }'

You'll get a task_id back immediately. Poll GET /backtest/status/{task_id} for results. Only 3 fields are required — the other 12 parameters auto-fill with sensible defaults.

Try /backtest/validate → to preview filled params without submitting. Full request example →

Section 02

API Endpoint

POST /backtest/start_backtest 202 400 401 503
curl https://st-tools.sentimentrader.com/backtest/start_backtest \
  -u "[email protected]:password" \
  -H "Content-Type: application/json" \
  -d '{
    "symbol": "SPY",
    "name": "Short Term Combined Model Strategy",
    "benchmark": "SPX",
    "direction": "Long",
    "frequency": "Daily",
    "strategy": "Single",
    "model": "cash",
    "capital": 100000,
    "capital_alloc": "1.000000",
    "commission": "0.0000",
    "slippage": "0.0000",
    "start_date": "1800-10-01",
    "trade_price": 2,
    "risk_control": "//21",
    "entry": {
      "rule_type": "SingleCondition",
      "delay_bars": 0,
      "condition": {
        "signal": {
          "type": "Sentiment",
          "name": "Short Term Combined Model",
          "params": { "extreme_optimism": "10", "extreme_pessimism": "-10" }
        },
        "rule": {
          "comparison_type": "SV",
          "type_name": "<",
          "comparison_value": "-10"
        }
      }
    },
    "exit": {
      "rule_type": "SingleCondition",
      "delay_bars": 0,
      "condition": {
        "signal": {
          "type": "Sentiment",
          "name": "Short Term Combined Model",
          "params": { "extreme_optimism": "10", "extreme_pessimism": "-10" }
        },
        "rule": {
          "comparison_type": "SV",
          "type_name": ">",
          "comparison_value": "13.5"
        }
      }
    }
  }'
// 202 Accepted
{
  "message": "Backtest task created successfully",
  "task_id": "a3f8c1d2-7b4e-4f6a-9c8d-1e2f3a4b5c6d"
}
// StrategyParams schema
type StrategyParams = {
  symbol:      string  // required — trading ticker
  name:        string  // strategy label
  direction:   string  // "Long" | "Short" (default: "Long")
  frequency:   string  // "Daily" | "Hourly" | "Weekly" | "Monthly"
  capital:     number  // initial capital (default: 100000)
  trade_price: number  // 0=Next Open, 1=Next Close, 2=Current Close (default: 2)
  risk_control: string  // "{stopLoss}/{takeProfit}/{maxDays}" (default: "//21")
  entry:       RuleBlock  // entry rule definition
  exit:        RuleBlock  // exit rule definition
}
Section 03

Authentication

HTTP Basic Auth. All endpoints except the public whitelist require authentication.

# HTTP Basic Auth
curl https://st-tools.sentimentrader.com/backtest/start_backtest \
  -u "[email protected]:password"
import requests

resp = requests.post(
    "https://st-tools.sentimentrader.com/backtest/start_backtest",
    auth=("[email protected]", "password"),
    json=params
)
// Authentication headers
Header          Value
Authorization   Basic base64(email:password)
Content-Type    application/json
â„šī¸ Public Endpoints (No Auth Required)
/health, /auth-info, /docs, /openapi.json, /redoc, /backtest/tutorial
Section 04

Core Strategy Parameters

Fundamental parameters that define the trading strategy identity and scope.

🧩 Strategy = Symbol + Entry Rule + Exit Rule
strategy
├── symbol "SPY" — ticker to trade
├── direction "Long" — trade direction
├── capital 100000 — starting capital
├── frequency "Daily" — candle period
├── entry ─── rule_type ─── condition
│ │ ├── signal — which indicator?
│ │ ├── rule — what comparison?
│ │ └── qualifier — extra filter? (optional)
└── exit ─── (same structure as entry)

For this symbol, enter when <condition>, exit when <condition>. Entry and exit share identical structure — only the trigger timing differs. Details: Rule Definition →

ParameterTypeRequiredDescriptionExample
Required
symbolⓘ
The trading instrument ticker. This is the only required parameter — all others have defaults. Used both for price data retrieval and as the backtest universe.
stringrequiredTrading ticker symbol"SPY", "AAPL", "XBTUSD"
Auto-Defaulted
strategystringautoStrategy type. Default: "Single""Single"
directionstringautoTrade direction. Default: "Long""Long" / "Short"
frequencystringautoCandle period. Default: "Daily""Daily", "Hourly", "Weekly"
benchmarkstringautoPerformance benchmark. Default: "SPX""SPX", "HSI"
Optional
namestringoptionalUser-defined strategy name"Short Term Combined Model Strategy"
descriptionobjectoptionalStrategy description container{"Introduction": "...", "Rules": "..."}
Section 05

Backtest & Capital Configuration

ParameterTypeDefaultDescriptionExample
Date Range
start_datestring"1800-10-01"Backtest start (YYYY-MM-DD)"1800-10-01"
end_datestringcurrent dateBacktest end date"2026-06-01"
Capital
capitalnumber100000Initial capital100000
capital_allocstring"1.000000"Per-trade allocation (string)"1.000000"
commissionstring"0.0000"Per-trade commission (string)"0.0000"
slippagestring"0.0000"Per-trade slippage (string)"0.0000"
Section 06

Advanced Parameters

Section 07

Frontend-Aligned Defaults

The API auto-fills all missing parameters via _apply_default_params(). Only override when you explicitly need different values.

💡 Minimal Request = symbol + entry + exit
Only 3 fields are required — all others auto-fill. Use POST /backtest/validate to preview the merged result without submitting a backtest. The response shows user_supplied (what you passed) vs defaults_applied (what the API filled in) side-by-side.
🔍 Validate & Preview
// POST /backtest/validate — no Redis/RabbitMQ overhead
// Request: same body as /backtest/start_backtest
curl /backtest/validate \
  -H "Content-Type: application/json" \
  -d '{ "symbol": "SPY", "entry": {...}, "exit": {...} }'

// Response — two columns: what you passed vs what was auto-filled
{
  "status": "valid",
  "user_supplied": {
    "symbol": "SPY",
    "name": "Short Term Combined Model Strategy",
    "entry": {
      "rule_type": "SingleCondition",
      "condition": {
        "signal": { "type": "Sentiment", "name": "Short Term Combined Model", "params": { "extreme_optimism": "10", "extreme_pessimism": "-10" } },
        "rule": { "comparison_type": "SV", "type_name": "<", "comparison_value": "-10" }
      }
    },
    "exit": {
      "rule_type": "SingleCondition",
      "condition": {
        "signal": { "type": "Sentiment", "name": "Short Term Combined Model", "params": { "extreme_optimism": "10", "extreme_pessimism": "-10" } },
        "rule": { "comparison_type": "SV", "type_name": ">", "comparison_value": "13.5" }
      }
    }
  },
  "defaults_applied": {
    "benchmark": "SPX",
    "capital": 100000,
    "capital_alloc": "1.000000",
    "commission": "0.0000",
    "slippage": "0.0000",
    "direction": "Long",
    "frequency": "Daily",
    "model": "cash",
    "strategy": "Single",
    "start_date": "1800-10-01",
    "trade_price": 2,
    "risk_control": "//21",
    "exclude_oo": 0,
    "market_environment": 0,
    "index_moving_average_slope": 0,
    "opted": 0,
    "win_rate": "0.0000"
  },
  "entry": {
    "rule_type": "SingleCondition",
    "signals": [
      { "code": "SI:Short Term Combined Model=extreme_optimism=10--extreme_pessimism=-10;SC:SV/2/-10", "signal_name": "Short Term Combined Model" }
    ]
  },
  "exit": {
    "rule_type": "SingleCondition",
    "signals": [
      { "code": "SI:Short Term Combined Model=extreme_optimism=10--extreme_pessimism=-10;SC:SV/0/13.5", "signal_name": "Short Term Combined Model" }
    ]
  }
}
ParameterFrontend DefaultNotes
benchmark"SPX"Performance benchmark
capital100000Initial capital
capital_alloc"1.000000"100% per trade
commission"0.0000"Zero commission (string)
slippage"0.0000"Zero slippage (string)
direction"Long"Trade direction
frequency"Daily"Data frequency
model"cash"Account model
strategy"Single"Strategy type
start_date"1800-10-01"Max historical range
trade_price20=Next Open, 1=Next Close, 2=Current Close
risk_control"//21"No stop/take-profit, 21-day forced exit
exclude_oo0No overnight exclusion
market_environment0No market filter
index_moving_average_slope0No index filter
opted0No optimization
win_rate"0.0000"No win-rate filter (string)
Section 08

Rule Definition (entry / exit)

Both entry and exit share the same structure.

// Rule block structure
{
  "rule_type": "SingleCondition | Scoring | TimeOrder | RiskControlOnly",
  "delay_bars": 0,
  "condition": {
    "signal":    { ... },
    "rule":      { ... },
    "qualifier": { ... }   // optional
  }
}
// SingleCondition example
{
  "rule_type": "SingleCondition",
  "delay_bars": 0,
  "condition": {
    "signal": {
      "type": "Sentiment",
      "name": "Short Term Combined Model",
      "params": {"extreme_optimism":"10","extreme_pessimism":"-10"}
    },
    "rule": {
      "comparison_type": "SV",
      "type_name": "<",
      "comparison_value": "-10"
    }
  }
}
// RuleBlock schema
type RuleBlock = {
  rule_type:  string  // "SingleCondition" | "Scoring" | "TimeOrder" | "RiskControlOnly"
  delay_bars: number  // wait N bars after condition (default: 0)
  condition:  {
    signal:    Signal      // { type, name, params? }
    rule:      Rule        // { comparison_type, type_name, comparison_value }
    qualifier: Qualifier?  // optional secondary filter
  }
}
FieldTypeDescriptionExample
delay_barsⓘ
Number of bars to wait after condition is met before executing the trade. 0 = execute on the same bar the signal fires. 1 = wait one bar then execute.
intWait N bars after condition met. 0=same bar, 1=next bar0
Section 09

Rule Types

SingleCondition relation: 1
One signal must trigger. Simplest rule type — a single condition drives entry or exit.
Scoring relation: 2
Multiple signals vote. condition is an array of {signal, rule} objects. Trigger when score_threshold of them are satisfied.
TimeOrder relation: 3
Signals must fire in sequence (steps). signals_in_market controls max interval. Default: 100000 (no limit).
RiskControlOnly relation: 0
Exit-only. No signal conditions — exit controlled solely by top-level risk_control.
âš ī¸ Rule Type Format Differences
SingleCondition: entry.condition = single {signal, rule} object
Scoring: entry.condition = array of [{signal, rule}, ...] + score_threshold
TimeOrder: entry.steps = array of [{signal, rule}, ...] + signals_in_market (NOT under condition!)
âš ī¸ TimeOrder Specifics
signals_in_market: Default 100000 = no interval limit. Set e.g. 21 to require all steps within 21 bars.
cond[0]: Copies first step's qualifier (NOT default {3,21,S}).
â„šī¸ RiskControlOnly Internals
Dummy signal: SI:Smart Money Confidence;IDS:EO0.7/EP0.3;SC:SV/0/999999
EO=0.7, EP=0.3, value=999999, condition=> (code 0).
Section 10

Signal Definition

Define what data source drives the condition. Signal types are auto-detected.

💡 Auto-Detection Rule
1. Name in all_indicators.csv (6284 indicators) → Sentiment (SI)
2. Name in ids_technical_name_map.json → Technical (TI)
3. Otherwise → Symbol (SY)
You don't need to specify signal.type — the API detects it automatically.
Sentiment (SI)
Emotion/Sentiment Indicator
Named indicators from all_indicators.csv. EO/EP must be set per indicator — defaults are rarely correct.
EO/EP: Per indicator
Example: "Short Term Combined Model"
Symbol (SY)
Price Data Signal
OHLCV data. Name format: "Symbol {Column}". Never include ticker in name.
EO/EP: 0/0
Example: "Symbol Close"
Technical (TI)
Technical Indicator
Classic indicators from ids_technical_name_map.json. URL/IDS auto-filled from map.
EO/EP: 20/80
Example: "Relative Strength Index"
âš ī¸ Common Pitfall — Symbol Name
✅ "Symbol Close" — correct
❌ "SPY Close" — wrong (ticker goes in top-level symbol param)
❌ "XBTUSD Close" — wrong

Column ∈ {Open, High, Low, Close, Volume}
âš ī¸ "Ratio Rank" Is Part of the Name
"Small Cap / S&P 500 Relative Ratio Rank" — "Ratio Rank" is part of the indicator name, NOT a qualifier. Do NOT add a Range Rank qualifier for this indicator.
Section 11

Rule Conditions

FieldTypeDescriptionExample
comparison_typeⓘ
The baseline for comparison. SV = raw signal value. SVMA/SVEMA = compare against a moving average of the signal. MA = only for Technical indicators with Moving Average type.
stringComparison baseline: "SV" (value), "SVMA" (SMA), "SVEMA" (EMA), "MA" (Technical MA only)"SV"
type_namestringComparison operator. See Condition Codes"<", "CrossAbove"
comparison_valuestring|numberThreshold value"-10", 95
periodstringMA period (required for SVMA/SVEMA)"21"
â„šī¸ SVMA / SVEMA Auto-Qualifier
Using "SVMA" or "SVEMA" as comparison_type automatically generates a qualifier with the specified period and MA type. No need to add a manual qualifier in this case.
Section 12

Qualifier — Advanced Calculations

Optional secondary filter on the same indicator. Fires only when both main condition AND qualifier are true.

// Qualifier object
{
  "comparison_operator": "Range Rank",
  "calculation_period": "21",
  "ma_type": "S"         // "S" = SMA, "E" = EMA
}
âš ī¸ Technical Indicators Don't Support Qualifiers
Combining "Technical" signal type with a qualifier raises a ValueError. Use qualifiers only with Sentiment and Symbol signals.
Section 13

Condition Codes

16 operators mapped from type_name string to numeric code.

0> (Greater than)main
1= (Equal to)main
2< (Less than)main
3CrossAbovemain
4CrossBelowmain
5Rate Of Changequal
6% of Previous Rangequal
7MA Ratioqual
8Range Rankqual
9DrawDown %qual
10DrawUp %qual
11Net Changequal
12All-time Highspecial
13All-time Lowspecial
14Consecutive Days Abovespecial
15Consecutive Days Belowspecial
âš ī¸ CrossBelow ≠ <
CrossBelow (code 4) is a crossing event — signal crosses below the threshold.
< (code 2) is a static comparison — signal is below the threshold.
These produce very different trade counts. Never substitute.

Qualifier Abbreviations

AbbrFull NameCodeTypical Periods
OPR% of Previous Range610, 20
RRRange Rank821, 84, 252
DDDrawDown %921, 252
DUDrawUp %1021, 252
ROCRate Of Change55, 10, 21
MARMA Ratio721
NCNet Change111, 5
Section 14

Risk Control Format

Format: "{stop_loss}/{take_profit}/{max_holding_days}"

Format: "{stop_loss}/{take_profit}/{max_holding_days}"
Separator: / (empty = not set)
ValueStop-LossTake-ProfitMax HoldingDescription
"//21"——21 daysNo stop/take-profit, 21-day forced exit (default)
"-5/10/21"-5%+10%21 daysStop-loss -5%, take-profit 10%
"//"———No risk control at all
Section 15

Signal Encoding (Internal)

API auto-encodes user-friendly JSON into internal format. You never need to build this manually.

{SI|SY|TI}:{name};IDS:EO{opt}/EP{pess};SC:{comparison}/{condition_code}/{value}
SegmentMeaningExample
SI / SY / TISignal type prefixSI / SY / TI
IDS:EO/EPExtreme optimism/pessimismIDS:EO50/EP30
SC:comp/code/valSignal conditionSC:SV/2/15 (SV < 15)

Encoding Examples

// Sentiment: Short Term Combined Model < -10
SI:Short Term Combined Model;IDS:EO10/EP-10;SC:SV/2/-10

// Symbol: Symbol Close CrossAbove MA
SY:Symbol Close;IDS:EO0/EP0;SC:SVMA/3/21

// Technical: RSI < 30
TI:Relative Strength Index;IDS:EO20/EP80;SC:SV/2/30

// RiskControlOnly exit dummy
SI:Smart Money Confidence;IDS:EO0.7/EP0.3;SC:SV/0/999999
Section 16

API Response

Three endpoints handle the full lifecycle: submit a backtest, poll for results, and list past tasks.

Submit Backtest

POST /backtest/start_backtest 202 400 401 503
json
{
  "message": "Backtest task created successfully",
  "task_id": "uuid"
}
Submit errors
400 means required fields or strategy structure are invalid. 503 means Redis is unavailable and the task was not queued. 500 can occur when RabbitMQ delivery or CSV generation fails.

Check Status

GET /backtest/status/{task_id} 200 401
{
  "status": "PENDING",
  "params": { "symbol": "SPY", "...": "..." },
  "username": "[email protected]",
  "created_at": "2026-06-08T12:00:00"
}
// SUCCESS — backtest finished, result contains metrics and trade dates
{
  "status": "SUCCESS",
  "result": {
    "Symbol": "SPY",
    "Frequency": "Daily",
    "Total Trades": 44,
    "Total Positive": 40,
    "Total Negative": 4,
    "Win Rate": 90.91,
    "Average Win %": 3.72,
    "Average Loss %": -8.99,
    "Each Return": [11.72, 3.56, /* ...per trade */],
    "Total Return (%)": 184.85,
    "BuyHoldGain": 1578.54,
    "CAGR": 3.19,
    "Sharpe Ratio": 0.25,
    "Max Drawdown": 55.09,
    "DrawDown Period": 5123,
    "Time in Market": 32.15,
    "EntryDate": ["1997-07-18", /* ... */],
    "ExitDate": ["1998-02-13", /* ... */],
    "Last Entry Date": "2026-03-06",
    "Last Exit Date": "2026-05-15",
    "BT End Date": "2026-06-05"
  },
  "params": { "symbol": "SPY", "...": "..." },
  "username": "[email protected]",
  "created_at": "2026-06-08T12:00:00"
}

List Tasks

GET /backtest/tasks?limit=20&offset=0 200
json
{
  "tasks": [
    {
      "task_id": "a3f8c1d2-7b4e-4f6a-9c8d-1e2f3a4b5c6d",
      "status": "SUCCESS",
      "strategy_name": "",
      "symbol": "SPY",
      "frequency": "Daily",
      "created_at": "2026-06-05T12:00:00"
    }
  ],
  "total": 5,
  "limit": 20,
  "offset": 0
}
Section 17

Indicators List

Returns all available backtest indicators/signals from the SentimenTrader indicator database (22k+ entries). Can be filtered by name, type, or market symbol.

Endpoint

GET /backtest/indicators/list

Response Fields

FieldTypeDescription
chart_namestringDisplay name of the indicator
urlstringInternal indicator code used as indicator param in signal definitions
market_symbolstringAssociated market symbol (e.g. SPX, SPY, COMP)
typestringIndicator type: optix, trend, reltrend, other
updatedstringUpdate frequency: Daily, Weekly, Monthly

Query Parameters

ParamTypeRequiredDescription
qstringNoSearch indicator name (case-insensitive, partial match)
typestringNoFilter by indicator type (optix / trend / reltrend / other)
symbolstringNoFilter by market symbol (e.g. SPX, SPY)

Response Example

json
[
  {
    "chart_name": "Macro Index Model",
    "url": "macro_index_model",
    "market_symbol": "SPX",
    "type": "other",
    "updated": "Monthly"
  },
  {
    "chart_name": "SPY Optix",
    "url": "etf_spy",
    "market_symbol": "SPY",
    "type": "optix",
    "updated": "Daily"
  }
]
💡 Filtered Search
Unfiltered responses return all 22k+ indicators (~3 MB). Use ?q=, ?type=, or ?symbol= to narrow results. Combine params for precision: ?q=Macro&type=other.

cURL Examples

bash
# Search by indicator name
curl -u user:pass "https://st-tools.sentimentrader.com/backtest/indicators/list?q=Macro+Index+Model"

# Filter by type
curl -u user:pass "https://st-tools.sentimentrader.com/backtest/indicators/list?type=optix"

# Filter by market symbol
curl -u user:pass "https://st-tools.sentimentrader.com/backtest/indicators/list?symbol=SPX"
Section 18

Indicator Metadata

Returns detailed metadata for a specific backtest indicator, including description, sentiment level, extreme thresholds, and chart configuration.

Endpoint

GET /backtest/indicators/metadata

Query Parameters

ParamTypeRequiredDescription
indicatorstringYesIndicator code from url field in /indicators/list (e.g. model_smart_dumb_spread)

Response Fields

FieldTypeDescription
sentimentstringSentiment level (0–4)
descriptionstringDetailed HTML description of the indicator
opt_extremefloatOptimism extreme threshold
pess_extremefloatPessimism extreme threshold
contrarystringContrary indicator flag ("1.0" or "0")
premiumstringPremium indicator flag ("0" or "1")
chart_typestringChart render type (e.g. "line")
updatedstringUpdate frequency
⏱ Frequency Must Match
The backtest frequency parameter must match the indicator's updated field. If they differ, validation passes but the backtest silently produces empty results. Example: Macro Index Model has "Monthly" → use "frequency": "Monthly", not "Daily".

Response Example

json
{
  "model_smart_dumb_spread": {
    "sentiment": "4",
    "description": "

The Smart Money Confidence and Dumb Money Confidence...", "opt_extreme": -0.25, "pess_extreme": 0.25, "contrary": "1.0", "premium": "0", "chart_type": "line", "updated": "Daily" } }

cURL Example

bash
curl -u user:pass "https://st-tools.sentimentrader.com/backtest/indicators/metadata?indicator=model_smart_dumb_spread"
Section 19

CSV Mode

Use CSV mode when your frontend already has prepared OHLCV and signal rows. The API skips internal CSV generation, validates the rows, stores the CSV payload, and queues the task.

json
{
  "symbol": "SPY",
  "csv_mode": true,
  "csv_data": [
    { "Date": "2024-01-02", "Open": 470, "High": 472.5, "Low": 469, "Close": 471.5, "Volume": 1000 }
  ],
  "entry": {
    "rule_type": "SingleCondition",
    "condition": {
      "signal": { "type": "Symbol", "name": "Symbol Close" },
      "rule": { "comparison_type": "SV", "type_name": ">", "comparison_value": "0" }
    }
  },
  "exit": { "rule_type": "RiskControlOnly" }
}
FieldRequirement
csv_dataRequired when csv_mode is true; must be an array of row objects
Required columnsDate, Open, High, Low, Close, Volume
Signal columnsEntry1-Entry5 and Exit1-Exit5 are optional; missing columns are filled with 0
Extra columnsDropped before queueing; the worker receives the standard 16-column shape
Section 20

Complete Workflow

The three-step cycle for every backtest: validate encoding, submit job, poll for results.

Step 1: Validate

Always validate before submitting. Returns encoded signals so you can verify the API interpreted your params correctly. No Redis/RabbitMQ overhead.

POST /backtest/validate

Request body is identical to submit. Response shows encoded signals:

json
{
  "status": "valid",
  "entry": {
    "signals": [{"code": "SI:Macro Index Model;IDS:EO0.7/EP0.7;SC:SV/3/0.7"}],
    "cond": [{"newCondition": 3, "conditionPeriod": "21", "ma_type": "S"}]
  },
  "exit": {
    "signals": [{"code": "SI:Macro Index Model;IDS:EO0.7/EP0.7;SC:SV/4/0.7"}],
    "cond": [{"newCondition": 4, "conditionPeriod": "21", "ma_type": "S"}]
  },
  "warnings": []
}

Step 2: Submit

After confirming encoding, submit to start the backtest. Returns a task_id for polling.

POST /backtest/start_backtest
json
{
  "message": "Backtest task created successfully",
  "task_id": "a3f8c1d2-7b4e-4f6a-9c8d-1e2f3a4b5c6d"
}

Step 3: Poll

Poll every 10–15 seconds until status is SUCCESS or FAILED. Typical backtests complete in 10–60 seconds. TimeOrder SPX Daily (~25K bars) may take up to 90s.

GET /backtest/status/{task_id}
StatusMeaningAction
PENDINGQueuedPoll in 10–15s
RUNNINGProcessingPoll in 10–15s
SUCCESSCompleteParse result
FAILEDErrorCheck result
Section 21

Troubleshooting

Common issues and how to diagnose them.

SymptomLikely CauseFix
0 trades Frequency mismatch — indicator is Monthly but frequency: "Daily" Match frequency to indicator's updated field
0 trades (pre-1990) Volume column was 0, worker treated as suspended Fixed (Volume floor = 1)
Validate or submit returns 400 Scoring condition is a single object instead of an array, or required fields are missing Use "condition": [{...}, {...}] (array)
TimeOrder returns 400 Conditions wrapped in "condition": {"steps": [...]} Use "steps": [...] directly at entry/exit level
Fewer trades than expected risk_control: "//21" forces exit too early Use "//" when exit has explicit signals
Duplicate frequency key in JSON Last value wins — e.g. "Monthly" then "Daily" → "Daily" used Check JSON for accidental duplicates
Section 22

Full Examples

// Minimal — only 3 fields, 12 params auto-filled
// This is all you need. Everything else gets sensible defaults.
{
  "symbol": "SPY",
  "entry": {
    "rule_type": "SingleCondition",
    "condition": {
      "signal": { "name": "Short Term Combined Model" },
      "rule": {
        "comparison_type": "SV",
        "type_name": "<",
        "comparison_value": "-10"
      }
    }
  },
  "exit": {
    "rule_type": "RiskControlOnly"
  }
}
// → 202 Accepted  { "message": "Backtest task created successfully", "task_id": "a3f8..." }
// Auto-filled: benchmark="SPX", capital=100000, direction="Long",
//   frequency="Daily", trade_price=2, risk_control="//21" â€Ļ +7 more
// SingleCondition — Sentiment < -10 entry, > 13.5 exit
// Minimal: only symbol + entry + exit required; all other params auto-filled
{
  "symbol": "SPY",
  "name": "Short Term Combined Model Strategy",
  "benchmark": "SPX",
  "direction": "Long",
  "frequency": "Daily",
  "capital": 100000,
  "capital_alloc": "1.000000",
  "trade_price": 2,
  "risk_control": "//21",
  "entry": {
    "rule_type": "SingleCondition",
    "delay_bars": 0,
    "condition": {
      "signal": {
        "type": "Sentiment",
        "name": "Short Term Combined Model",
        "params": {"extreme_optimism":"10","extreme_pessimism":"-10"}
      },
      "rule": {
        "comparison_type": "SV",
        "type_name": "<",
        "comparison_value": "-10"
      }
    }
  },
  "exit": {
    "rule_type": "SingleCondition",
    "delay_bars": 0,
    "condition": {
      "signal": {
        "type": "Sentiment",
        "name": "Short Term Combined Model",
        "params": {"extreme_optimism":"10","extreme_pessimism":"-10"}
      },
      "rule": {
        "comparison_type": "SV",
        "type_name": ">",
        "comparison_value": "13.5"
      }
    }
  }
}
// SingleCondition with Range Rank qualifier — delay 1 bar
{
  "symbol": "SPY",
  "name": "Smart Money Confidence Spread Strategy",
  "benchmark": "SPX",
  "direction": "Long",
  "frequency": "Daily",
  "capital": 100000,
  "capital_alloc": "1.000000",
  "trade_price": 2,
  "risk_control": "//21",
  "entry": {
    "rule_type": "SingleCondition",
    "delay_bars": 1,
    "condition": {
      "signal": {
        "type": "Sentiment",
        "name": "Smart Money / Dumb Money Confidence Spread"
      },
      "rule": {
        "comparison_type": "SV",
        "type_name": "=",
        "comparison_value": "100",
        "qualifier": {
          "comparison_operator": "Range Rank",
          "calculation_period": "21",
          "ma_type": "S"
        }
      }
    }
  },
  "exit": {
    "rule_type": "SingleCondition",
    "delay_bars": 0,
    "condition": {
      "signal": {
        "type": "Sentiment",
        "name": "Smart Money / Dumb Money Confidence Spread"
      },
      "rule": {
        "comparison_type": "SV",
        "type_name": ">=",
        "comparison_value": "-100"
      }
    }
  }
}
// TimeOrder — Multi-step entry with 21-bar forced exit
{
  "symbol": "SPY",
  "name": "Small Cap Relative Ratio Strategy",
  "benchmark": "SPX",
  "direction": "Long",
  "frequency": "Daily",
  "capital": 100000,
  "capital_alloc": "1.000000",
  "trade_price": 2,
  "risk_control": "//21",
  "entry": {
    "rule_type": "TimeOrder",
    "signals_in_market": 100000,
    "steps": [
      {
        "signal": { "name": "Small Cap / S&P 500 Relative Ratio Rank" },
        "rule": { "comparison_type":"SV", "type_name":"CrossAbove", "comparison_value":"95" }
      },
      {
        "signal": { "name": "Small Cap / S&P 500 Relative Ratio Rank" },
        "rule": { "comparison_type":"SV", "type_name":"CrossBelow", "comparison_value":"15" }
      }
    ]
  },
  "exit": {
    "rule_type": "RiskControlOnly"
  }
}
// Scoring — 2 of 3 voters must be true to trigger entry
{
  "symbol": "SPY",
  "name": "Multi-Signal Voting Strategy",
  "benchmark": "SPX",
  "direction": "Long",
  "frequency": "Daily",
  "capital": 100000,
  "capital_alloc": "1.000000",
  "trade_price": 2,
  "risk_control": "//21",
  "entry": {
    "rule_type": "Scoring",
    "score_threshold": 2,
    "voters": [
      {
        "signal": { "type": "Sentiment", "name": "Short Term Combined Model", "params": {"extreme_optimism":"10","extreme_pessimism":"-10"} },
        "rule": { "comparison_type": "SV", "type_name": "<", "comparison_value": "-10" }
      },
      {
        "signal": { "type": "Sentiment", "name": "Smart Money / Dumb Money Confidence Spread" },
        "rule": { "comparison_type": "SV", "type_name": "<", "comparison_value": "-30" }
      },
      {
        "signal": { "type": "Sentiment", "name": "Small Cap / S&P 500 Relative Ratio Rank" },
        "rule": { "comparison_type": "SV", "type_name": ">", "comparison_value": "80" }
      }
    ]
  },
  "exit": {
    "rule_type": "RiskControlOnly"
  }
}
// score_threshold=2: at least 2 of the 3 voters must be true
// Each voter is a complete condition (signal + rule)