Intraday Backtester API
Run custom intraday option backtests with one-minute precision. Define a strategy with entry and exit times, delta or strike-width leg targets, stops, and profit targets, then backtest it against intraday options data going back to October 2020. The API powers the Intraday Custom Backtester in the ORATS dashboard.
This is an asynchronous job API: submit a backtest, poll its status until it completes (a two week single-symbol run typically finishes in under a minute), then fetch the results. Authentication uses the token query parameter on every request.
Available strategies: PUT_CREDIT_SPREAD, CALL_DEBIT_SPREAD, LONG_CALL, SHORT_PUT, CALL_CREDIT_SPREAD, PUT_DEBIT_SPREAD, SHORT_CALL, LONG_PUT, IRON_CONDOR, IRON_BUTTERFLY, LONG_STRADDLE, SHORT_STRADDLE, SHORT_STRANGLE, LONG_CALL_CALENDAR, LONG_PUT_CALENDAR, and DOUBLE_CALENDAR.
Product overview and pricing for this and the other Tools APIs live on the Tools APIs page.
https://api.orats.io/backtests/intradaySubmit a Backtest
Submits a backtest job. A successful submission returns a backtestId to use with the other endpoints.
Validation happens asynchronously: a response containing a backtestId means the job was accepted, not that the inputs are valid. A rejected submission returns a message field instead of a backtestId. Jobs with invalid inputs can remain queued rather than reaching an error status, so give up on jobs that stay queued well past their expected runtime.
Required attributes
- Name
email- Type
- string
- Description
Your email address, used to list your backtests on the status endpoint.
- Name
inputs- Type
- object
- Description
The backtest definition. Its fields are documented below.
- Name
inputs.symbol- Type
- string
- Description
The underlying to backtest. Ex: SPY
- Name
inputs.startDate- Type
- string
- Description
The first trade date, formatted by YYYY-MM-DD. Must be 2020-10-01 or later. Ex: 2026-06-01
- Name
inputs.entryTime- Type
- string
- Description
Entry time of day, formatted by HHMM (in EST) within market hours 0930 to 1600. Ex: 0934
- Name
inputs.strategy- Type
- string
- Description
The strategy, from the list above. Ex: PUT_CREDIT_SPREAD
- Name
inputs.legs- Type
- array
- Description
One object per leg. The first leg sets its own strike and expiration: optionType (put or call), ratio (negative for short legs), a delta band (deltaTarget, deltaMin, deltaMax, always positive), and a DTE band (dteTarget, dteMin, dteMax) where DTE 1 is same-day (0DTE), 2 is the next trading day, and the maximum target is 30.
Later legs may instead be defined relative to an earlier leg with legRelation: strikeWidth (refLeg, target in strike points, optional min and max; positive widths are further out of the money) and dteDiff (refLeg, target in days, 0 for the same expiration).
Optional attributes
- Name
inputs.endDate- Type
- string
- Description
The last trade date, formatted by YYYY-MM-DD. Omit for the most recent data.
- Name
inputs.exitTime- Type
- string
- Description
Exit time of day, formatted by HHMM (in EST). Null holds to expiration.
- Name
inputs.expiryTod- Type
- string
- Description
Filters AM or PM settled expirations: both, am, or pm. Defaults to both.
- Name
inputs.stopLossPct- Type
- number
- Description
Stop loss as a percent of entry cost. Send null explicitly to disable; when the field is omitted the engine applies a 200 percent stop.
- Name
inputs.profitTargetPct- Type
- number
- Description
Profit target as a percent of entry cost. Null for no target.
- Name
inputs.tradePct- Type
- number
- Description
Slippage as a fraction of the bid-ask spread paid on each side, 0 to 1. Ex: 0.66
- Name
inputs.commissionPerContract- Type
- number
- Description
Commission in dollars per contract per leg, applied on entry and exit. Ex: 0.65
- Name
inputs.maxHoldDays- Type
- number
- Description
Maximum trading days to hold before a forced exit. Null for none.
- Name
inputs.badPriceFilter- Type
- object
- Description
Skips entries with wide markets: maxWidthPctOfStrike and maxWidthPctOfMid as fractions. Defaults to 0.05 and 0.15.
- Name
inputs.signals- Type
- array
- Description
Signal-driven entries and exits: one object per trade with entry and exit timestamps formatted by YYYYMMDDHHMM. Null to trade on entryTime every day.
{
"email": "you@example.com",
"inputs": {
"symbol": "SPY",
"startDate": "2026-06-01",
"endDate": "2026-06-12",
"entryTime": "0934",
"exitTime": null,
"strategy": "PUT_CREDIT_SPREAD",
"legs": [
{
"optionType": "put",
"ratio": -1,
"deltaTarget": 0.3,
"deltaMin": 0.2,
"deltaMax": 0.5,
"dteTarget": 1,
"dteMin": 1,
"dteMax": 1
},
{
"optionType": "put",
"ratio": 1,
"legRelation": {
"strikeWidth": {
"refLeg": 1,
"target": 3
},
"dteDiff": {
"refLeg": 1,
"target": 0
}
}
}
],
"expiryTod": "both",
"stopLossPct": null,
"profitTargetPct": null,
"tradePct": 0.66,
"commissionPerContract": 0.65,
"maxHoldDays": null,
"badPriceFilter": {
"maxWidthPctOfStrike": 0.05,
"maxWidthPctOfMid": 0.15
},
"signals": null
}
}
Response
{
"backtestId": "aapg5gmoqs1r"
}
Backtest Status
Retrieves the status of a backtest by id, or every backtest for an email address.
By id the response is a single object; by email it is an array of the same objects. Status values are queued, running, completed, and error. While queued the object holds the submission fields; on completion it adds progress, annReturn, annSharpe, maxDrawDown, and a condensed legs summary. Poll every 10 seconds or so until status is completed.
An unknown id returns a message of Status not found with a 200 status.
Optional attributes
- Name
id- Type
- string
- Description
The backtestId to look up. Returns a single object. Ex: aapg5gmoqs1r
- Name
email- Type
- string
- Description
Returns every backtest submitted with this email as an array.
curl -L "https://api.orats.io/backtests/intraday/status?token=my-token&id=aapg5gmoqs1r"
Response
{
"email": "you@example.com",
"execution_time": 5.86,
"id": "aapg5gmoqs1r",
"progress": 100,
"status": "completed",
"strategy": "PUT_CREDIT_SPREAD",
"submitted_at": "2026-07-16T08:04Z",
"symbol": "SPY",
"updated_at": "2026-07-16T08:05:34.794Z",
"annReturn": -31.23,
"annSharpe": -8.23,
"maxDrawDown": 1.42,
"legs": [
{
"dteTarget": 1,
"deltaTarget": 0.3
},
{
"legRelation": {
"strikeWidth": {
"refLeg": 1,
"target": 3
},
"dteDiff": {
"refLeg": 1,
"target": 0
}
}
}
]
}
Backtest Results
Retrieves the results of a completed backtest: summary statistics, monthly returns by year, daily returns, and the per-leg trade log.
The trades array has one row per leg; each row carries that leg's profit (sum a trade's legs for the whole-trade profit), and margin is written on leg 1 only. The exitQuoteTime field is populated for non-expiration exits. Symbol and strategy are not repeated here; read them from the status or inputs endpoints.
Required attributes
- Name
id- Type
- string
- Description
The backtestId to retrieve. Ex: aapg5gmoqs1r
curl -L "https://api.orats.io/backtests/intraday?token=my-token&id=aapg5gmoqs1r"
Response
{
"summary": {
"AnnReturn": -31.23,
"AnnMarginReturn": -9935.18,
"AnnReturn1Yr": -25.87,
"AnnReturn5Yrs": -25.87,
"AnnSharpe": -8.23,
"AnnSortino": -5.43,
"AnnVolatility": 3.14,
"MaxDrawDown": 1.42,
"DrawDownDays": 8,
"BestMonthRtn": -1.03,
"WorstMonthRtn": -1.03,
"BestYearRtn": -1.03,
"WorstYearRtn": -1.03,
"BestTradePL": 90.06,
"WorstTradePL": -240.62,
"BestTradePct": 98.58,
"WorstTradePct": -447.16,
"PLPerTradeAvg": -76.68,
"PLPerDayAvg": -63.9,
"PLPerTradeAvgPct": -0.10267,
"PLPerDayAvgPct": -0.08556,
"StratWinRate": 50.0,
"DaysInTradeAvg": 1.0,
"TotStratTrades": 10,
"TotStratPL": -766.84,
"TotStratPLPct": -109.52,
"CreditReceivedPerTradeAvg": 70.02,
"marginPerTradeAvg": 229.98,
"mostMarginUsed": 249.32,
"leastMarginUsed": 208.64,
"bestTradePctMargin": 43.17,
"worstTradePctMargin": -100.59,
"marginToStockPct": 0.31,
"riskRewardAvg": 0.3098,
"PctOfTimeInMarket": 100.0,
"BestReturnOnRisk": 0.43165,
"ProfitFactor": 0.31
},
"monthly": [
{
"year": 2026,
"Jan": null,
"Feb": null,
"Mar": null,
"Apr": null,
"May": null,
"Jun": -1.03,
"Jul": null,
"Aug": null,
"Sep": null,
"Oct": null,
"Nov": null,
"Dec": null,
"annReturn": -1.03,
"annSharpe": -8.23,
"annSortino": -5.43,
"annVolatility": 3.14
}
],
"trades": [
{
"date": "2026-06-01",
"entryTime": "0934",
"ticker": "SPY",
"leg": 1,
"ratio": -1,
"optionType": "put",
"strike": 755.0,
"expiry": "2026-06-01",
"expiryTod": "pm",
"dte": 1,
"tradeOptPx": 1.33,
"delta": -0.3497,
"entryStockPx": 755.67,
"exitDate": "2026-06-01",
"exitTime": "1600",
"exitQuoteTime": null,
"exitStockPx": 758.54,
"exitOptionPx": 0.0,
"profit": 132.69,
"exitReason": "Expiration",
"margin": 221.32
},
{
"date": "2026-06-01",
"entryTime": "0934",
"ticker": "SPY",
"leg": 2,
"ratio": 1,
"optionType": "put",
"strike": 752.0,
"expiry": "2026-06-01",
"expiryTod": "pm",
"dte": 1,
"tradeOptPx": 0.55,
"delta": -0.0391,
"entryStockPx": 755.67,
"exitDate": "2026-06-01",
"exitTime": "1600",
"exitQuoteTime": null,
"exitStockPx": 758.54,
"exitOptionPx": 0.0,
"profit": -55.31,
"exitReason": "Expiration"
}
],
"dailyReturns": [
{
"date": "2026-06-01",
"return": 0.001024,
"marginReturn": 0.349629
},
{
"date": "2026-06-02",
"return": 0.000652,
"marginReturn": 0.198059
}
]
}
Backtest Inputs
Retrieves the original inputs a backtest was submitted with, exactly as they were sent. Useful for re-running or editing a backtest.
Required attributes
- Name
id- Type
- string
- Description
The backtestId to retrieve. Ex: aapg5gmoqs1r
curl -L "https://api.orats.io/backtests/intraday/input?token=my-token&id=aapg5gmoqs1r"
Response
{
"symbol": "SPY",
"startDate": "2026-06-01",
"endDate": "2026-06-12",
"entryTime": "0934",
"exitTime": null,
"strategy": "PUT_CREDIT_SPREAD",
"legs": [
{
"optionType": "put",
"ratio": -1,
"deltaTarget": 0.3,
"deltaMin": 0.2,
"deltaMax": 0.5,
"dteTarget": 1,
"dteMin": 1,
"dteMax": 1
},
{
"optionType": "put",
"ratio": 1,
"legRelation": {
"strikeWidth": {
"refLeg": 1,
"target": 3
},
"dteDiff": {
"refLeg": 1,
"target": 0
}
}
}
],
"expiryTod": "both",
"stopLossPct": null,
"profitTargetPct": null,
"tradePct": 0.66,
"commissionPerContract": 0.65,
"maxHoldDays": null,
"badPriceFilter": {
"maxWidthPctOfStrike": 0.05,
"maxWidthPctOfMid": 0.15
},
"signals": null
}
Save a Note
Saves a note on a backtest. The note is returned on the status endpoint as the notes field and survives re-runs. Sending an empty string clears the note.
Required attributes
- Name
id- Type
- string
- Description
The backtestId to annotate. Ex: aapg5gmoqs1r
- Name
notes- Type
- string
- Description
The note text, up to 10,000 characters.
{
"id": "aapg5gmoqs1r",
"notes": "SPY 0DTE put credit spread test"
}
Response
{
"message": "Notes updated successfully",
"id": "aapg5gmoqs1r"
}
Delete a Backtest
Deletes a backtest and its results. Deletion is idempotent: deleting an already-deleted id still returns success. After deletion the status endpoint returns a message of Status not found for the id.
Required attributes
- Name
id- Type
- string
- Description
The backtestId to delete. Ex: aapg5gmoqs1r
curl -L -X DELETE "https://api.orats.io/backtests/intraday?token=my-token&id=aapg5gmoqs1r"
Response
{
"message": "Backtest deleted successfully",
"id": "aapg5gmoqs1r"
}