API reference

The backtest of a screener filter set

GET/track-recordKey required

The backtest of a screener filter set

Returns the record of the past trades of one family (or all five) whose facts at entry matched every filter: the TradingPal screener's Track record panel for the same filters, computed from the same stored past-trade ledger. filters takes the same predicate list as /scan. Filters that describe today rather than the trade date cannot cut past trades and come back under filters.left_out. edge=on counts only the trades the Edge Filter kept (tiers S to C). Under minimum_trades matched trades, track_record is null and note says why. unfiltered is the same family's record without the filters, for comparison. Needs a key on an account with Premium+.

Authentication

Needs a key: send Authorization: Bearer tp_live_… (see Authentication). Counted against the key's limits of 60 requests a minute and 10,000 a day.

Parameters

NameInTypeRequiredDescription
familyquerystring (enum)noKeep one family: rising_wedge, falling_wedge, bullish_pennant, bearish_pennant, triangle. Omit for all five. Anything else answers 400 bad_request. One of rising_wedge, falling_wedge, bullish_pennant, bearish_pennant, triangle.
filtersquerystringnoThe screener's own filters, URL-encoded: a JSON array of predicates, ANDed together; a kind may repeat (for example two moving-average lengths). Numeric kinds take op (<, <=, >, >=, =) and value, or op between with value and upper (inclusive), choice and text kinds take value, boolean kinds take value true or false, and the moving-average, performance and high/low kinds also take period. For example filters=[{"kind":"price","op":">","value":10},{"kind":"rsi","op":"<","value":40},{"kind":"asset_type","value":"stock"}]. Every predicate must match; a setup with no evidence for a kind never matches. The kinds, with what each takes and means, are the x-filters list on this operation (the reference page shows it as a table). A malformed list answers 400 bad_request with the reason.
edgequerystring (enum)noon: only the past trades the Edge Filter kept (tiers S to C); off: every trade. One of off, on.
X-TradingPal-ClientheaderstringnoOptional name of the agent or script making the call. It is recorded in the account's usage log so the user can see which client made which calls; the User-Agent is recorded when it is absent.

Request

curl "https://api.tradingpal.io/api/v1/track-record?family=value&filters=value&edge=value" \
  -H "Authorization: Bearer $TRADINGPAL_API_KEY"

Response

JSON. The fields this route adds are below; every response also carries the envelope fields (api_version, as_of_session, data_version, source, disclaimer).

FieldTypeDescription
familystring · nullableThe family asked for (rising_wedge, falling_wedge, bullish_pennant, bearish_pennant, triangle); null for all five.
labelstringThe family's display name, or All patterns.
edgeobjectThe Edge Filter side the record was cut from.
edge.requestedstring (enum)off or on, as asked. One of off, on.
edge.appliedbooleanFalse when on was asked of a ledger that seals no Edge Filter tiers; the record is then every trade's.
filtersobjectHow the filters were used.
filters.appliedarray of objectThe predicates that cut the past trades, canonical.
filters.left_outarray of objectPredicates the record ignores, each with its reason: they describe today (market cap, security type, exchange, sector, country, phase, distance, the model's estimates) or the ledger holds no values for them.
matched_tradesintegerPast trades that matched every applied filter.
too_fewbooleanTrue under minimum_trades matched trades; track_record is then null.
minimum_tradesintegerThe fewest matched trades a record is given for.
track_recordTrack recordThe record of the matched trades (scope filtered). Null when too_few.
unfilteredTrack recordThe same family's record without the filters, on the same Edge Filter side, for comparison (scope unfiltered).
notestring · nullableWhy no win rate is given, when too_few.

Example response

Response
{
  "api_version": "v1",
  "data_version": "patterns-D-2026-11-12",
  "family": "falling_wedge",
  "label": "Falling wedge",
  "edge": {
    "requested": "off",
    "applied": true
  },
  "filters": {
    "applied": [
      {
        "kind": "min_rr",
        "op": ">=",
        "value": 2
      }
    ],
    "left_out": [
      {
        "kind": "market_cap",
        "op": ">",
        "value": 1000000000,
        "reason": "describes today, not the trade date, so it cannot cut past trades"
      }
    ]
  },
  "matched_trades": 1287,
  "too_few": false,
  "minimum_trades": 30,
  "track_record": {
    "scope": "filtered",
    "family": "falling_wedge",
    "symbol": null,
    "win_rate_pct": 54,
    "sample_size": 1287,
    "wins": 695,
    "losses": 592,
    "avg_win_pct": 8.1,
    "avg_loss_pct": -4.2,
    "median_win_pct": 6.3,
    "avg_trade_pct": 2.4,
    "avg_r": 0.41,
    "expected_r": 0.41,
    "profit_factor": 1.61,
    "avg_hold_bars": 11.2,
    "backtest_range": [
      "2006-09-25",
      "2026-09-25"
    ],
    "measured": "Every detector-confirmed breakout … open trades are excluded.",
    "summary": "Falling wedge: 1,287 resolved trades from 2006-09-25 to 2026-09-25; 54% reached the target, average win +8.1%, average loss -4.2%, median win +6.3%."
  },
  "unfiltered": {
    "scope": "unfiltered",
    "family": "falling_wedge",
    "symbol": null,
    "win_rate_pct": 54,
    "sample_size": 1287,
    "wins": 695,
    "losses": 592,
    "avg_win_pct": 8.1,
    "avg_loss_pct": -4.2,
    "median_win_pct": 6.3,
    "avg_trade_pct": 2.4,
    "avg_r": 0.41,
    "expected_r": 0.41,
    "profit_factor": 1.61,
    "avg_hold_bars": 11.2,
    "backtest_range": [
      "2006-09-25",
      "2026-09-25"
    ],
    "measured": "Every detector-confirmed breakout … open trades are excluded.",
    "summary": "Falling wedge: 1,287 resolved trades from 2006-09-25 to 2026-09-25; 54% reached the target, average win +8.1%, average loss -4.2%, median win +6.3%."
  },
  "note": null
}

Errors

Errors are JSON with the Error shape; the fix field says what to do.

StatusCodeMeaningFix
401missing_api_keyThe request had no Authorization: Bearer tp_live_… header and the route needs one.Send the header on every keyed call. Only the demo, families and universe routes answer without it.
401invalid_api_keyThe key is not one we know, or it has been revoked.Create a new key on the API Keys page and replace it wherever the old one was stored.
403forbiddenThe account behind the key is not on Premium+. API and MCP access requires Premium+ subscription at $30/month; Premium alone does not unlock the API.Get Premium+ ($30/month, all of the benefits in Premium plus API and MCP access) from the Quickstart, where a first subscription is $0 for 1 month, or switch an existing Premium subscription to it there, then retry with the same key.
429rate_limitedThe key passed 60 requests in a minute or 10,000 in a day, or a keyless caller passed 30 a minute from one IP.Wait details.retry_after_seconds, then retry. Cache the nightly answers; they do not change until the next session.
400bad_requestA query parameter is malformed: limit or offset is not an integer, outcome is not all, resolved or open, a scan filter (family, direction, status, max_distance_pct) is not one of its allowed values, or filters is not a valid predicate list (the message names the predicate).The message names the parameter. Correct it and retry.