Skip to main content

User API: Building Backticks and Positions

Last updated November 4, 2026

This article is the reference for building Backticks from your own code: creating a Backtick, adding and changing its positions, applying several changes in one call, and filing, trashing and deleting it. Running a Backtick and reading its results have their own articles.

The plan you need is in Use the tradeSteward API From Your Own Code. Without Backtick Professional every Backtick request answers 403 entitlement_required. Your API client also needs the Backtick permissions you chose for it on Connected Apps; each section below names the one it needs. Choose What a Connection May Do explains the permissions, and Safeguards the account limits.

Every path below sits under /rest/v1 and answers the standard envelope (ok, data, error, apiVersion, requestId) described in Your First Request. Two headers come up on nearly every write:

  • Idempotency-Key: a unique value you choose, 1 to 128 letters, digits, dots, underscores or hyphens. The same key with the same request returns the first answer. Keys are kept for 2 days. Required on every create, edit, clone and run.
  • If-Match: the editVersion you last read. Required on every edit of a Backtick or its positions. A stale value answers 412 version_mismatch with the current version in error.data.current (a whole number, the same as editVersion in a read, for example 3; send it back as the If-Match value); a missing header answers 428 precondition_required.

Position settings use the same dictionary as bots: GET /schema/bot-settings lists every setting; it is covered in Account, Trading and Schema Endpoints. A position's settings are checked when you add or change it, and a refusal names each bad field, for example positions.0.stop-delta. POST /bots/validate checks a bot's settings without saving, as described in Bot Write Endpoints.

The life of a Backtick

A Backtick has a run status that every read and every write answer carries:

StatusMeaningWhat you can do
draftBuilt and never run, or put back after a run could not go ahead.Edit it, add or change positions, run it.
scheduledA draft or failed Backtick whose run the server will start later on its own. The answer also carries positionInLine and reason.Read it, or cancel the scheduled run. Every edit answers 409 invalid_state with the reason scheduled until you cancel.
queuedWaiting for a Backtick server.Read it or cancel it.
runningA server is working on it. progress moves from 0 to 1.Read it or cancel it.
completedThe run finished and results are ready.Read results. To change or run it again, clone it.
failedThe run failed. errorMessage says why.Edit it and run it again, as you would a draft.

Only a draft or failed Backtick is edited and run as it is. An edit of a queued, running or completed Backtick answers 409 not_editable, and a run of a completed one answers 409 invalid_state with the reason already_run: clone it with POST /backtests/{uid}/clone and work on the copy. Cancelling a queued or running run makes the Backtick a draft, even one that had failed before. Cancelling a scheduled run leaves it as it was: a draft stays a draft and a failed Backtick stays failed. Either way it can be edited and run again.

Every edit is guarded by editVersion. Read the Backtick, send that version in If-Match, and read the new editVersion from the answer before the next edit. Two programs editing the same Backtick cannot overwrite each other: the second one gets 412 version_mismatch and reads again.

Where API Backticks live

A Backtick made through the API is not saved. It is filed under the recent library section with source set to rest (or mcp when an AI assistant made it), and it appears on the API/MCP tab of your Backtick library. Its expiresAt is the UTC time it moves to the trash: 48 hours after its last change. Any later change starts a new 48 hours, and while it is scheduled, queued or running it stays until the run ends (expiresAt is null then). To keep it, file it under saved or archived with POST /backtests/{uid}/library-status; expiresAt is then null. Moving it back to recent starts the 48 hours again. A Backtick made on the website has a null source and never expires this way.

The library sections are recent (the website calls it Unsaved), saved, archived and trash. A Backtick in the trash cannot be read, edited, run, cloned or converted: those answer 404 not_found on reads and 409 invalid_state with the reason in_trash on writes. Restore it first. Only a Backtick in the trash can be permanently deleted.

What a write answers

Every request that changes a Backtick answers with the Backtick itself. With the backtick.view permission, data is the Backtick as GET /backtests/{uid} returns it, without its positions, plus the fields of that write (for example positionId after adding a position, or sharesEnded after a library move). Without backtick.view, data holds only backtestUID, status, libraryStatus, source, expiresAt and editVersion, plus the fields of the write, and never the name, notes, dates, results or settings.

Limits that apply to every write

  • Changes share one limit for the account and one for the access token. Most writes spend one unit of it; a batch of changes and a run of several Backticks spend more. X-RateLimit-Remaining counts units, not calls.
  • Runs, cancels, trashes and permanent deletes take turns for one account. One that arrives while another is in progress answers 503 lock_busy with Retry-After: 2, nothing was done, and it did not count toward your limit. Send it again. Edits take turns only per Backtick and never wait for a run.
  • While changes are switched off on our side, every write answers 503 writes_disabled; reads keep working.
  • A Backtick holds at most 250 positions on Backtick Professional. GET /backtick/capabilities reports the limit for your plan.

Reading Backticks

GET /backtick/capabilities What Backtick can test on your plan

Returns the symbols with historical data, the last day data is available, and the limits of your plan. Read it once before building Backticks so your test dates and position counts fit. Operation id: getBacktickCapabilities. Permission: backtick.view (View Backticks and results).

Returns symbols (each with symbol, from and to dates), dataThrough (the last trading day with data, as a date), and limits with maxPositions (positions in one Backtick), maxQueuedPositionYears (how much work you may have queued at once, counted as positions times years of test period) and sweeptickMaxIterations (how large one SweepTick may be: 256 combinations plus the core position on Backtick Professional; the field counts the core, so it reports 257). A limit is null when your plan does not set it.

{
  "ok": true,
  "data": {
    "symbols": [{"symbol": "$SPX.X", "from": "2014-06-01", "to": "2026-10-31"}],
    "dataThrough": "2026-10-31",
    "limits": {"maxPositions": 250, "maxQueuedPositionYears": 8000, "sweeptickMaxIterations": 257}
  },
  "error": null, "apiVersion": "rest-v1", "requestId": "req_7Kp2"
}

Refusals: 403 permission_denied, 403 entitlement_required.

GET /backtests List your Backticks

Lists the Backticks of your account, newest activity first, with headline metrics for completed ones. Operation id: listBacktests. Permission: backtick.view (View Backticks and results).

NameTypeRequiredMeaning
libraryquery, stringNorecent, saved, archived, trash or all (default). all is every section except the trash.
statusquery, stringNoOnly Backticks in this status: draft, scheduled, queued, running, completed or failed. draft and failed leave out Backticks with a scheduled run; scheduled lists only those.
limitquery, integerNoBackticks per page, 1 to 100. Default 50. A larger number is treated as 100.
cursorquery, stringNonextCursor from the previous page. Leave it out for the first page.

Returns backtests, a list of summaries, and nextCursor (null on the last page). Each summary has backtestUID, name, status, libraryStatus, source, expiresAt, testType (backtick or sweep), testPeriodStart, testPeriodEnd, startingBalance (a decimal string in dollars), createdAt and completedAt (UTC), metrics (the headline numbers of a completed run, otherwise null with metricsUnavailableReason saying why; a SweepTick never has them, because its positions are alternatives) and errorMessage for a failed run. A scheduled Backtick adds positionInLine and reason. Refusals: 400 bad_request for an unknown library or status, a limit that is not a whole number, or a cursor that is not one this list gave.

GET /backtests/{uid} Read one Backtick

Returns one Backtick with its fields, its positions and the editVersion you send in If-Match on the next edit. Operation id: getBacktest. Permission: backtick.view (View Backticks and results).

NameTypeRequiredMeaning
uidpath, stringYesThe Backtick id (backtestUID), from GET /backtests.

Returns everything a list summary has, plus editVersion, notes, settings and positions. settings holds the Backtick-level fields: commissionProfile (a commission profile id), the switches coercePrice, minPercQty1, minLevQty1, maxQty49SPX, qty1normalize and ignoreConflicts (booleans), the six overrides slippageOverride-entry, -exit, -stop (percent of the bid/ask spread, 0 to 200) and delayOverride-entry, -exit, -stop (seconds, 0 to 240), and skipDays (dates and date ranges left out of the test). Each item of positions has positionId, name, botId (the bot it was copied from, or null), symbol, slippageDelayOverride (1 when the Backtick's overrides apply to it, 0 when it ignores them) and, for a completed run, metrics with totalProfit, tradeCount, winRate and maxDrawdown. On a SweepTick the answer also holds sweep, the definition GET /backtests/{uid}/sweep returns (see SweepTick Endpoints). Refusals: 404 not_found for a Backtick that is not in your account or is in the trash.

GET /backtests/{uid}/positions/{positionId} Read one position

Returns one position with its full settings in the dictionary format, which is the shape you send back to change it, copy it into another Backtick, or use as the source of a SweepTick. Operation id: getBacktestPosition. Permission: backtick.view (View Backticks and results).

NameTypeRequiredMeaning
uidpath, stringYesThe Backtick id.
positionIdpath, integerYesThe position id, from GET /backtests/{uid}.

Returns positionId, name, botId, status (the Backtick's run status), metrics (the position's full metrics for a completed run, otherwise null with metricsUnavailableReason saying why), dictionaryVersion (the version of the settings dictionary the settings are written in), settings (every setting, the symbol and slippageDelayOverride included) and, for a Multi-Entry position, entries (each with positionId, entryStart and size, which is null when the entry takes the position's size) and multiEntryLength in its settings. An entry read on its own names its position in parentPositionId. A scheduled Backtick adds positionInLine and reason. Refusals: 404 not_found.

Creating Backticks

POST /backtests Create a draft Backtick

Creates a draft Backtick, empty or with new positions. Nothing is run. Operation id: createBacktest. Permission: backtick.create (Create Backticks). Header: Idempotency-Key (required).

NameTypeRequiredMeaning
namestringYesThe name shown in your library. At most 35 characters.
testPeriodStartdateNoFirst day of the test period (YYYY-MM-DD). Left out, your Backtick default is used.
testPeriodEnddate or nullNoLast day of the test period. Null is accepted, but a Backtick without an end date cannot be run.
startingBalancestringNoStarting balance in dollars, as a decimal string with at most 2 decimals, for example "100000". At least "1000.00". A JSON number is refused.
notesstring or nullNoFree text shown with the Backtick. At most 2,000 characters.
settingsobjectNoThe Backtick-level settings in the shape GET /backtests/{uid} returns them. Fields left out start from your defaults. commissionProfile must be one of your own profiles that is not archived; if your account has no default profile, send one, or the create answers 422 naming commissionProfile with the reason required. The switches take true or false, the overrides a whole number or null, and skipDays holds at most 5,000 entries. A field sent at the wrong level is refused with the reason belongs_in_settings or belongs_at_top_level.
positionsarrayNoNew positions, each an object with name and settings (dictionary format). A position's settings may carry slippageDelayOverride (0 or 1). A Multi-Entry position cannot be sent here: add it afterwards with POST /backtests/{uid}/positions.

Returns 201 with the new Backtick (see What a write answers) plus positionIds, the ids of the positions created, in the order sent. Keep backtestUID and editVersion.

POST /rest/v1/backtests
Authorization: Bearer <token>
Idempotency-Key: create-2026-11-03-001
Content-Type: application/json

{
  "name": "SPX 10 Delta Put Spread 9:31a",
  "testPeriodStart": "2024-01-02",
  "testPeriodEnd": "2025-12-31",
  "startingBalance": "100000",
  "positions": [
    {"name": "10d PCS 9:31", "settings": {"symbol": "$SPX.X", "...": "settings from GET /schema/bot-settings"}}
  ]
}

{
  "ok": true,
  "data": {
    "backtestUID": "a1B2c3D4e5F6",
    "name": "SPX 10 Delta Put Spread 9:31a",
    "status": "draft",
    "libraryStatus": "recent",
    "source": "rest",
    "expiresAt": "2026-11-05T14:12:09Z",
    "testType": "backtick",
    "testPeriodStart": "2024-01-02",
    "testPeriodEnd": "2025-12-31",
    "startingBalance": "100000.00",
    "createdAt": "2026-11-03T14:12:09Z",
    "completedAt": null,
    "metrics": null,
    "metricsUnavailableReason": "This Backtick has not been run.",
    "errorMessage": null,
    "editVersion": 1,
    "notes": null,
    "settings": {"commissionProfile": 12, "coercePrice": false, "slippageOverride-entry": null, "skipDays": []},
    "positionIds": [48213]
  },
  "error": null, "apiVersion": "rest-v1", "requestId": "req_9mQ1"
}

Refusals: 422 validation_failed with fieldErrors naming each bad field (a position's errors are named as positions.0.stop-delta; the batch route names them changes[0].stop-delta); 409 backtick_quota when the Backtick would hold more positions than your plan allows; 428 idempotency_key_required; 409 idempotency_conflict when the key was used with a different body.

POST /backtests/from-bots Create a Backtick from existing bots

Creates a draft Backtick whose positions are copies of bots you already have, so you can test what a bot would have done. Nothing is run and the bots are not changed. Operation id: createBacktestFromBots. Permissions: backtick.create (Create Backticks) and bots.settings.view (View bot settings); it needs both. Header: Idempotency-Key (required).

NameTypeRequiredMeaning
namestringYesThe name shown in your library.
botIdsarray of stringsYesThe bots to copy, each once, from GET /bots.
testPeriodStart, testPeriodEnd, startingBalance, notes, settingsas aboveNoThe same meaning as in POST /backtests.

Returns 201 with the new Backtick. Each position's botId names the bot it was copied from. Refusals: 422 validation_failed naming botIds, with the reason required (an empty list, a repeated id or a value that is not a bot id), bot_not_found (a bot that is not yours or that the client may not see), no_enabled_entries (a Multi-Entry bot with no entry switched on), position_not_found with error.data.reason dependency (a bot depends on or launches a bot that is not in the list: add it) or bot_settings_invalid; 422 validation_failed for the other fields, as in POST /backtests; 403 constraint_violation when a Multi-Entry bot has entries on a broker account the client may not use, or entries it did not create; 403 entitlement_required when your plan has no API access to bots (bots.settings.view needs it); 409 backtick_quota with the reason bot_limit when the request names more bots than one Backtick can hold.

POST /backtests/{uid}/clone Copy a Backtick into a new draft

Copies a Backtick, with its positions and settings, into a new draft. This is how you change or run again a Backtick that is queued, running or completed. Takes no body. Operation id: cloneBacktest. Permission: backtick.create (Create Backticks). Header: Idempotency-Key (required).

NameTypeRequiredMeaning
uidpath, stringYesThe Backtick to copy.

Returns 201 with the copy, plus clonedFrom (the source id) and signalsStrippedOnCopy (true when the source had an imported signals file and the copy was made without it, because your plan does not include signals files). The copy is a new unsaved API Backtick with its own 48 hours. Refusals: 404 not_found; 409 invalid_state with the reason in_trash.

POST /backtests/compose Build a portfolio from positions of several Backticks

Creates a draft Backtick whose positions are copies of positions of your saved or recent Backticks. Nothing is run and the sources are not changed. Operation id: composeBacktest. Permissions: backtick.create and backtick.view; it needs both. Header: Idempotency-Key (required).

NameTypeRequiredMeaning
namestringYesThe name shown in your library.
positionsarray of objectsYes1 to 250 items, each with backtestUID and positionId, and optionally name, settings (only what differs from the copied position) and references (keep or clear).
testPeriodStart, testPeriodEnd, startingBalance, notes, settingsas aboveNoThe same meaning as in POST /backtests.

A position that launches or depends on other positions is copied only when they are in the same call, and the copy then points at their new ids. Otherwise it is refused, unless its item sends references as clear. Returns 201 with the new Backtick, positionIds and composedFrom: for each item, newPositionId, remappedReferences, clearedReferences, settingsDiffer (the Backtick settings of its source that differ from the new Backtick) and, when it applies, accountDropped. Refusals: 422 validation_failed naming positions.N.backtestUID or positions.N.positionId with the reason not_found, in_trash, archived, signals, multi_entry_unsupported, has_references or trigger_entry; 409 backtick_quota with the reason position_limit.

Editing Backticks and positions

PATCH /backtests/{uid} Change a Backtick's own fields

Changes the name, dates, balance, notes or settings of a draft or failed Backtick. Send only what changes, and at least one field. Operation id: updateBacktest. Permission: backtick.edit (Edit Backticks and positions). Headers: If-Match and Idempotency-Key (both required).

NameTypeRequiredMeaning
uidpath, stringYesThe Backtick id.
namestringNoNew name, at most 35 characters.
testPeriodStartdateNoFirst day of the test period.
testPeriodEnddate or nullNoLast day. Null clears it, and a Backtick without an end date cannot be run.
startingBalancestringNoStarting balance in dollars, as a decimal string with at most 2 decimals, at least "1000.00". A JSON number is refused.
notesstring or nullNoNull or empty text clears the notes. At most 2,000 characters.
settingsobjectNoThe Backtick-level settings to change, with the same rules as on a create. Fields left out keep their values. An empty object is refused.

Returns 200 with the Backtick and its new editVersion. Refusals: 422 validation_failed (also for an empty body); 409 not_editable for a queued, running or completed Backtick; 409 invalid_state with the reason scheduled while a run is scheduled, sweep_managed when you name a SweepTick's name, dates, balance or run settings (change those with PATCH /backtests/{uid}/sweep; notes, ignoreConflicts and the position size minimums are still changed here), or in_trash; 412 version_mismatch; 428 precondition_required.

POST /backtests/{uid}/positions Add a position

Adds one position to a draft or failed Backtick. Operation id: createBacktestPosition. Permission: backtick.edit (Edit Backticks and positions). Headers: If-Match and Idempotency-Key (both required).

NameTypeRequiredMeaning
uidpath, stringYesThe Backtick id.
namestringYesThe position's name. At most 30 characters for a Multi-Entry position.
settingsobjectYes, unless copyFrom is sentPosition settings in the dictionary format. May carry slippageDelayOverride (0 or 1; a new position starts at 0). With copyFrom, only the settings that differ from the copied position.
copyFromobjectNo{"positionId": 8}: copy that position of the same Backtick instead of sending all its settings. The copy has its settings with yours laid over them, judged as an edit of the copied position with those settings is, and is a new position, not linked to a bot. A Multi-Entry position, and the entries of one, cannot be copied (422, reason multi_entry_unsupported on copyFrom.positionId), and copyFrom does not go with entries.
entriesarrayNoOnly for a Multi-Entry position: 1 to 200 items, each with entryStart as "HH:MM:SS" in US Eastern time, from 09:30:50 to 15:59:59, no two the same. The settings must then carry multiEntryLength, must not carry the six entry window settings, and may set enterByTimeOrTrigger only to time. Every entry takes the position's size.

Returns 201 with the Backtick plus positionId, and for a Multi-Entry position entryPositionIds (only with backtick.view). Refusals: 422 validation_failed; 409 not_editable; 409 invalid_state with the reason scheduled, sweep_managed (a SweepTick's positions are generated) or in_trash; 409 backtick_quota with the reason position_limit; 412 version_mismatch.

PATCH /backtests/{uid}/positions/{positionId} Change a position

Changes the name, the settings or the entries of a position of a draft or failed Backtick. Send only what changes, at least one of them. Operation id: updateBacktestPosition. Permission: backtick.edit (Edit Backticks and positions). Headers: If-Match and Idempotency-Key (both required).

NameTypeRequiredMeaning
uidpath, stringYesThe Backtick id.
positionIdpath, integerYesThe position to change.
namestringNoNew name. A Multi-Entry position's name cannot be changed.
settingsobjectNoThe settings to change, in the dictionary format.
entriesarrayNoOnly for a Multi-Entry position: the complete list of its entries after the change, 1 to 200. An item with positionId keeps that entry (send it as the read listed it); an item with only entryStart adds one; an entry the list leaves out is removed.

Returns 200 with the Backtick plus positionId and changed (false when the request asked for nothing new), and entryPositionIds for a Multi-Entry position with backtick.view. Every change of a Multi-Entry position sets every entry to the position's size, except a change of slippageDelayOverride alone. Refusals: 422 validation_failed (also for an empty body); 422 family_edit_unsupported when the request is aimed at an entry instead of its position; 409 not_editable; 409 invalid_state with the reason scheduled, sweep_managed or in_trash, with the reason position_referenced when removing an entry another position depends on (referencingPositionIds lists them, with backtick.view), or with the reason entries_share_a_time when two entries of the Multi-Entry position start at the same time (edit it on tradeSteward); 404 not_found; 412 version_mismatch.

DELETE /backtests/{uid}/positions/{positionId} Remove a position

Removes one position from a draft or failed Backtick. Takes no body. Removing a Multi-Entry position removes its entries with it. Operation id: deleteBacktestPosition. Permission: backtick.edit (Edit Backticks and positions). Headers: If-Match and Idempotency-Key (both required).

NameTypeRequiredMeaning
uidpath, stringYesThe Backtick id.
positionIdpath, integerYesThe position to remove.

Returns 200 with the Backtick plus deletedPositionIds: with backtick.view, every position removed (a Multi-Entry position and its entries); without it, only the one in the path. Refusals: 409 invalid_state with the reason position_referenced when another position of the Backtick depends on this one (change that position first; referencingPositionIds lists them, with backtick.view), or with the reason scheduled, sweep_managed or in_trash; 422 family_edit_unsupported when aimed at an entry; 409 not_editable; 404 not_found; 412 version_mismatch.

POST /backtests/{uid}/changes Apply several changes in one call

Applies an ordered list of 1 to 50 changes to one draft or failed Backtick as a single edit. Use it to set up a whole Backtick in one request instead of one request per position. Operation id: applyBacktestChanges. Permission: backtick.edit (Edit Backticks and positions). Headers: If-Match and Idempotency-Key (both required).

NameTypeRequiredMeaning
uidpath, stringYesThe Backtick id.
changesarrayYes1 to 50 change objects, applied in order. Each has an action and only the members listed for that action below.
actionMembersDoes
settingsfields (the shape of the PATCH /backtests/{uid} body), name (replaces fields.name)Changes the Backtick's own fields.
addPositionname, settings or copyFromAdds a position, as POST /backtests/{uid}/positions does. With copyFrom ({"positionId": 8}, a position of the same Backtick) the new position is a copy of that one, and settings holds only what you change; a change with copyFrom is saved on its own, so it cannot copy a position added earlier in the same call.
updatePositionpositionId, and name and/or settingsChanges a position.
deletePositionpositionIdRemoves a position.

How a batch behaves:

  • Ordered. Each change sees what the ones before it left. A change to a position that an earlier change removed answers not_found at its place. A position added in the call has no positionId yet, so a later change in the same call cannot name it: that change answers 422 validation_failed with the reason added_in_this_call.
  • All or nothing. The first change that is refused refuses the whole call: error.data.changeIndex is its place in the list (the first is 0), each field error names its field as changes[3].stop-delta, and nothing was saved.
  • One version check. If-Match is checked once for the whole call, and the answer carries the new editVersion.
  • Multi-Entry positions go in a call of their own. A change that carries entries, or that names a Multi-Entry position or one of its entries, answers 422 family_edit_unsupported with the reason batch, and nothing is saved.
  • How allowance is spent. A longer list spends more of the limit on changes than a short one, from both the account's and the token's limit. A call refused for the shape of the list itself (a member other than changes, an empty list, more than 50 items, an item that is not an object) spends none. A call refused for what is inside one change (an unknown member, a missing or unknown action, a missing positionId) has already spent its units.
  • On a SweepTick, settings changes work as PATCH /backtests/{uid} allows; a position change answers 409 invalid_state with the reason sweep_managed at its place.

Returns 200 with the Backtick plus changes: one item per change, in order, with its index, action, changed, the positionId of a position added, and deletedPositionIds for a removal. A call in which nothing needed saving answers changed false at the top.

POST /rest/v1/backtests/a1B2c3D4e5F6/changes
If-Match: 1
Idempotency-Key: setup-a1B2c3D4e5F6-1

{
  "changes": [
    {"action": "settings", "fields": {"testPeriodEnd": "2026-06-30", "settings": {"slippageOverride-entry": 50}}},
    {"action": "addPosition", "name": "10d PCS 10:00", "settings": {"...": "..."}},
    {"action": "updatePosition", "positionId": 48213, "settings": {"slippageDelayOverride": 1}}
  ]
}

"data": {
  "backtestUID": "a1B2c3D4e5F6", "status": "draft", "editVersion": 2, "...": "...",
  "changes": [
    {"index": 0, "action": "settings", "changed": true},
    {"index": 1, "action": "addPosition", "changed": true, "positionId": 48214},
    {"index": 2, "action": "updatePosition", "changed": true, "positionId": 48213}
  ]
}

Refusals: 422 validation_failed with changeIndex; 404 not_found with changeIndex; 422 family_edit_unsupported; 409 not_editable; 409 invalid_state with the reason scheduled or in_trash; 412 version_mismatch.

Filing, trashing and deleting

POST /backtests/{uid}/library-status Move a Backtick to another library section

Files a Backtick under recent, saved or archived. Filing an API Backtick under saved or archived is what keeps it past its 48 hours. Operation id: setBacktestLibraryStatus. Permission: backtick.library (Change library status). No Idempotency-Key: a repeat answers the current state.

NameTypeRequiredMeaning
uidpath, stringYesThe Backtick id.
statusstringYesrecent, saved or archived. To trash a Backtick use the trash route.

Returns 200 with the Backtick plus sharesEnded: moving a Backtick out of the saved section ends every group share of it, and this is how many ended. Moving it back does not bring them back. Refusals: 422 validation_failed for any other status; 409 invalid_state with the reason in_trash; 404 not_found.

POST /backtests/library-status Move up to 50 Backticks to one library section

Takes backtestUIDs (1 to 50 ids, each once) and status, and files each Backtick as the request above would, in the order sent. Operation id: setBacktestsLibraryStatus. Permission: backtick.library. No Idempotency-Key. Returns 200 with status and items, one per Backtick with its backtestUID and an outcome: moved (with priorLibraryStatus and sharesEnded), unchanged, refused (with the error the single request would answer, such as in_trash), unknown (read the Backtick before sending it again) or notAttempted (send it again). It spends one unit of the limit on changes per 10 Backticks, rounded up. A repeated id, an empty list, more than 50 or another section is refused whole with 422 validation_failed.

POST /backtests/{uid}/trash Move a Backtick to the trash

Moves a Backtick to the trash, where it can be restored until it is permanently deleted. A Backtick that was saved loses its group shares. A run scheduled for the Backtick is ended first. Takes no body. Operation id: trashBacktest. Permission: backtick.trash (Move to trash and restore).

NameTypeRequiredMeaning
uidpath, stringYesThe Backtick id.

Returns 200 with the Backtick, now with libraryStatus trash, plus sharesEnded (how many group shares ended; 0 unless it was saved and shared). A repeat answers the same. Refusals: 404 not_found; 503 lock_busy with Retry-After when another run, cancel, trash or delete of your account is in progress (send it again).

POST /backtests/{uid}/restore Take a Backtick out of the trash

Restores a trashed Backtick into the recent section. Takes no body. Operation id: restoreBacktest. Permission: backtick.trash (Move to trash and restore).

NameTypeRequiredMeaning
uidpath, stringYesThe Backtick id.

Returns 200 with the Backtick, now with libraryStatus recent. Refusals: 409 invalid_state with the reason not_in_trash; 404 not_found.

DELETE /backtests/{uid} Permanently delete a trashed Backtick

Permanently deletes a Backtick that is in the trash. This cannot be undone. A run scheduled for the Backtick is ended first. Takes no body. Operation id: purgeBacktest. Permission: backtick.purge (Permanently delete Backticks). This permission is off by default on a new API client; turn it on under the client's permissions on Connected Apps if your program needs it.

NameTypeRequiredMeaning
uidpath, stringYesThe Backtick id.

Returns 200 with {"backtestUID": "...", "purged": true}, the same with or without backtick.view. Refusals: 409 invalid_state with the reason not_in_trash (trash it first); 404 not_found; 503 lock_busy with Retry-After (send it again).

  To delete a Backtick your program made, trash it and then delete it, in that order. An unsaved API Backtick you leave alone moves to the trash on its own 48 hours after its last change.

GET /backtick/commission-profiles List your commission profiles

Lists the commission profiles a Backtick can use, the default first. A profile's profileId is the value of a Backtick's settings.commissionProfile. Operation id: listCommissionProfiles. Permission: backtick.view (View Backticks and results). Send archived=true to include archived profiles.

Returns items and defaultProfileId (null when no default is in use). Each item has profileId, name, isDefault, archived, version, createdAt, updatedAt and the rates: equityPerContract, equityMinPerLeg, equityMaxPerLeg, indexPerContract, indexMinPerLeg, indexMaxPerLeg, orderMinCharge, orderMaxCharge, freeOpenPremium, freeOpenDirection, freeClosePremium, freeCloseDirection, assignmentPerContract, assignmentMin, assignmentMax and perSymbolFees. Amounts are decimal strings in dollars. SPX is an index option, so SPX Backticks use the index rates. A null maximum means no maximum; "0.00" makes that charge free. Refusals: 403 permission_denied, 403 entitlement_required.

GET /backtick/commission-profiles/{profileId} Read one commission profile

Returns one profile, archived ones included, with its version and backtickCount (how many of your Backticks use it, trashed ones included). Operation id: getCommissionProfile. Permission: backtick.view. Refusals: 404 not_found.

POST /backtick/commission-profiles Create a commission profile

Creates a profile with the fields above. name, equityPerContract and indexPerContract are required; per-contract and per-leg amounts run 0.00 to 100.00, the order, free-premium and assignment amounts 0.00 to 200.00, with at most two decimals, for example "0.87". Names are unique in your account. Send isDefault: true to make it the default; the first profile of an account must be. Operation id: createCommissionProfile. Permission: backtick.create (Create Backticks). Header: Idempotency-Key (required).

Returns 201 with the profile. Refusals: 422 validation_failed naming each field at fault; 503 service_unavailable: without data.outcome nothing was saved and you can send it again; with outcome unknown or saved, list or read your profiles before trying again.

PATCH /backtick/commission-profiles/{profileId} Change a commission profile

Send only the fields to change; null clears an optional amount. If-Match is the version from your last read. A change applies to every later run of every Backtick that uses the profile, including queued and scheduled runs that have not started. Results already run keep their numbers, so to compare against them create a new profile instead. The default can move to another profile but cannot be turned off, and an archived profile cannot become the default until you restore it on tradeSteward. Operation id: updateCommissionProfile. Permission: backtick.edit (Edit Backticks and positions). Headers: If-Match and Idempotency-Key (both required).

Returns 200 with the profile. Refusals: 428 precondition_required without If-Match; 412 version_mismatch with data.current; 422 validation_failed; 404 not_found; 503 service_unavailable, read as for a create.