Scanner API
Introduction
Scanner API supports queries to dxFeed Scanner and is REST-based. The API has one static resource URL per method, accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and methods.
Scanner API supports the following methods:
/scanner/estimate - returns search result size estimates
/scanner/snapshot - performs search and returns JSON-encoded entities
/scanner/snapshot/csv - performs search and returns result in CSV format
Scanner Queries
A Scanner query is a JSON schema query with the following properties.
Property | Type | Description | Expected values | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
instrumentCategory* | String | The configuration includes one category | instrumentCategory: or instrumentCategory: | |||||||||
datapoints | Array | Requested datapoints. In further query schema parts, a specific index references each datapoint as defined in this property | See ScanQuery.Datapoints for details | |||||||||
selectors | Array | JSON with select and value properties | See ScanQuery.Selectors for details | |||||||||
filters | Array | Filters applied to the query | See ScanQuery.Filters for details | |||||||||
sorters | Array | Sorting criteria applied to the query. It is represented as a set of sorters, where each sorter references a datapoint by its 0-based index as defined by the datapoints' value. The default order is descending or ascending if the reversed option is set to true
| The value format is: "sorters": [ { "datapoint": 7, "reversed": false } ], | |||||||||
outputs | Array | Datapoint indices to include in the output | The value format is: "outputs": [ { "datapoint": 0 }, { "datapoint": 1 } ] | |||||||||
options | Array | Additional query options. Currently, Scanner API supports only one option - snapshotSize integer($int32), which specifies the maximum results to be returned | The value format is: "options": { "snapshotSize": 1000 } |
ScanQuery.Datapoints
Each datapoint consists of two parts, a datapoint name (name) and a datapoint expression (expr). dxFeed Scanner supports 150+ pre-built datapoints; here is the full list. You can use dxScript expressions to build custom datapoints using functions and pre-built datapoints.
Please routinely check the supported pre-built datapoints list here and the /docs/datapoints/ endpoint since new Scanner releases sometimes result in datapoint updates.
Datapoint parameters
The dxFeed Scanner technical indicators support parametrized calculation. A user can specify candleCount, candlePeriod, timePeriod and session parameters to display its calculation.
candlePeriod parameter defines candle type for calculations using:
1m - for 1-minute candle
2m - for 2-minute candle
3m - for 3-minute candle
4m - for 4-minute candles
5m - for 5-minute candles
10m - for 10-minute candle
15m - for 15-minute candle
30m - for 30-minute candle
1h - for 1-hour candle
2h - for 2-hour candle
4h - for 4-hour candle
1d - for 1-day candle
1w - for 1-week candle
1mo - for 1-month candle
1y - for 1-year candle
candleCount parameter defines the exact candle number used for calculation.
session parameter defines the session types used for calculations:
all (default if the parameter is not specified) - regular, pre-market, and post-market
regular - only regular
timePeriod parameter defines the calendar period for the candles' number restrictions:
1d - all fully finished candles in the defined number of calendar days will be taken, any number of calendar days can be defined
1w - all fully finished candles in the defined number of calendar weeks will be taken, any number of weeks can be defined
1mo - all fully finished candles in the defined number of calendar months will be taken, any number of months can be defined
1y - all fully finished candles in the defined number of calendar years will be taken, any number of years can be defined
ytd - all fully finished candles between the start of the year and the requested date will be taken
Example. Ratio between ATM IV and statistical volatility for a month
To get the ratio between ATM Implied Volatility and statistical volatility for one month, use the following datapoint:
{ "name": "RATIO_IV_TO_SV_1_MONTH", "expr": "underlying.atmIv30Day / underlying.statVol1Month(session=\"regular\")" }
Example. Total call volume ratio to the average for 10 days
To calculate the options session call volume ratio to the 10 days average, use the following datapoint:
{ "name": "VOLUME_TOTAL_CALL_RATIO", "expr": "underlying.optionsCallVolume(session=\"regular\") / underlying.optionsCallVolumeAvg10Day(session=\"regular\")" }
ScanQuery.Selectors
Note
This section is obsolete. Please refer to it for backward compatibility only. Please refer to Appendix B, Migrate selectors to filters for details on how to implement functionality using filters.
This function is deprecated, relevant features shall be now implemented with ScanQuery.Filters functionality. See also Appendix A, Special functions.
Scanner API optimized the selectors to narrow down the set of symbols on which the search is performed. You can use type (e.g. OPTION
, ETF
, STOCK
), symbol (e.g. AAPL
, FB
, .AMZN190628C1300
, etc.), and underlying for derivatives (AAPL
, FB
) to limit the set of instruments you work with.
Property | Type | Description | Expected values |
---|---|---|---|
select* | string | Selector type |
|
values* | array | Selector values to apply | Depending on selector type and the symbol universe dxFeed Scanner works with |
Example. Selecting options only
"selectors": [ { "select": "type", "values": [ "OPTION" ] } ]
ScanQuery.Filters
Filters are search criteria built on datapoints. In each filter, you can define a datapoint and criteria (alternatives property) that the datapoint shall match.
Combine several alternatives using
OR
operation for the selected datapoint to meet any alternativeFilters themselves are combined using
AND
operation for the selected symbol datapoint to meet all alternatives.
Property | Type | Description | ||||||
---|---|---|---|---|---|---|---|---|
datapoint* | integer($int32) | Datapoint index in datapoint array (0-based). If this is the only provided filter property, then the specified datapoint must have a boolean type | ||||||
not | Boolean | Whether the filter should be negated. False by default | ||||||
alternatives | Array | Alternatives for the filtered datapoint:
|
Example. Filtering a datapoint to be greater than 1.5
"filters": [ { "datapoint": 7, "alternatives": [ { "predicate": ">=", "args": [ 1.5 ] } ] } ]
Appendix A. Special functions
Relative Options
When running requests in the OPTION
category, it is possible to access any neighboring datapoint option by strike and with the same or opposite type (call vs put). There is a special function to specify how to choose such a relative option.
Parameter | Type | Default Value | Description |
---|---|---|---|
strikeIntervals | number | 0 | positive or negative integer:
|
isOpposite | boolean | false |
|
Example. Search for a tradeable vertical spread
A vertical spread is an options trading strategy that involves the simultaneous buying and selling of options of the same type (i.e., either puts or calls) and expiry, but at different strike prices. The vertical term comes from the strike prices' position. Traders will use a vertical spread when they expect a moderate move in the underlying asset's price. Vertical spreads are mainly directional plays and can be tailored to reflect the trader's view, bearish or bullish, on the underlying asset.
Consider we want to find a two strikes wide vertical spread with both legs tradable and delta at around .45, where tradable stands for leg open interest being greater than 100. We want these spreads to be sorted by spread price, that is the first leg's price minus the second one's price. For that query, we would format the request as follows:
{ "instrumentCategory": "OPTION", "datapoints": [ { "expr": "underlying.price" }, { "expr": "strikePrice" }, { "name": "secondLegStrikePrice", "expr": "strikePrice(option(strikeIntervals=2))" }, { "name": "spreadPrice", "expr": "price-price(option(strikeIntervals=2))" }, { "expr": "greeks.delta" }, { "expr": "openInterest" }, { "name": "secondLegDelta", "expr": "greeks.delta(option(strikeIntervals=2))" }, { "name": "secondLegOpenInterest", "expr": "openInterest(option(strikeIntervals=2))" } ], "filters": [ { "datapoint": 4, "alternatives": [ { "predicate": "[]", "args": [ "0.43", "0.45" ] } ] }, { "datapoint": 6, "alternatives": [ { "predicate": "[]", "args": [ "0.43", "0.45" ] } ] }, { "datapoint": 5, "alternatives": [ { "predicate": ">", "args": [ "100" ] } ] }, { "datapoint": 7, "alternatives": [ { "predicate": ">", "args": [ "100" ] } ] } ], "sorters": [ { "datapoint": 3, "reversed": true } ], "options": { "snapshotSize": 100, "allAsOutputs": true } }
And the result might look like:
symbol | underlying.price | strikePrice | secondLegStrikePrice | spreadPrice | greeks.delta | openInterest | secondLegDelta | secondLegOpenInterest |
---|---|---|---|---|---|---|---|---|
.SPY220617C476 | 464.7 | 476 | 478 | 0.65 | 0.446 | 389 | 0.431176 | 1241 |
.SPX220218C4710 | 4649.27 | 4710 | 4720 | 0.96 | 0.445457 | 235 | 0.434049 | 163 |
.QQQ220617C406 | 391.52 | 406 | 408 | 2.34 | 0.444805 | 508 | 0.432029 | 838 |
.SPXW220121C4700 | 4649.27 | 4700 | 4710 | 6.5 | 0.445454 | 786 | 0.430869 | 273 |
.SPX220121C4700 | 4649.27 | 4700 | 4710 | 8.09 | 0.444977 | 17749 | 0.430869 | 273 |
.TSLA240119C1900 | 1063.38 | 1900 | 1950 | 9.49 | 0.440436 | 1105 | 0.430127 | 827 |
.SPXW220218C4710 | 4649.27 | 4710 | 4720 | 33.36 | 0.445756 | 149 | 0.434049 | 163 |
Scanner results filtering by index constituent symbols
It's possible to narrow down the Scanner results to a list of symbols constituting a public index, like the S&P 500, Russel 2000, etc.
Use a special inList
predicate to achieve such filtering. This predicate accepts one or many index names as arguments and narrows down the result set to the list of symbols from any index. For example, the following request returns only symbols that are part of the S&P 500, Russel 2000, or both indices:
{ "instrumentCategory": "UNDERLYING", "datapoints": [ { "expr": "symbol" } ], "filters": [ { "datapoint": 0, "alternatives": [ { "predicate": "inList", "args": ["SP500", "Russell2000"] } ] } ] }
To retrieve a full list of supported indices, use the IPF request with ipf?help=lists. For a list of index constituent symbols, use IPF request with ipf?lists=LISTNAME syntax. For example, ipf?lists=SP500 returns symbols for the S&P 500 index constituents.
Note
Use demo/demo credentials to check IPF examples.
Appendix B. Migrate from selectors to filters
The selector mechanism is deprecated in the Scanner HTTP API and will be removed in the near future.
The original selectors' purpose was to allow filtering specific datapoints using a given list of values. This was not initially possible to do using regular filters. Now, though filters support a predicate anyOf
that allows the selectors' functionality replication.
Mapping Between Selectors and Expressions
Selector name | Datapoint expression |
---|---|
symbol | symbol |
type | type |
underlying | underlying.symbol |
Examples
Request with selectors
The following is a request example with selectors in the body:
{ "instrumentCategory": "UNDERLYING", "selectors": [ { "select": "type", "values": [ "STOCK", "ETF" ] } ], "datapoints": [ { "expr": "price" } ], "filters": [ { "datapoint": 0, "alternatives": [ { "predicate": ">", "args": [ "100" ] } ] } ], "outputs": [ { "datapoint": 0 } ] }
Refactored request without selectors
To archive the same result using a filter, we need to remove the selectors' field from the request and instead add a datapoint and a corresponding filter.
The following is an updated request example without selectors:
{ "instrumentCategory": "UNDERLYING", "datapoints": [ { "expr": "price" }, { "expr": "type" } ], "filters": [ { "datapoint": 0, "alternatives": [ { "predicate": ">", "args": [ "100" ] } ] }, { "datapoint": 1, "alternatives": [ { "predicate": "anyOf", "args": [ "STOCK", "ETF" ] } ] } ], "outputs": [ { "datapoint": 0 } ] }
Several things changed:
selectors' field was removed from the body
a new entry was added to the datapoints field; this datapoint uses the values of the select as an expr
a new entry was added to the filters field; the new filter refers to the new datapoint and uses the
anyOf
predicate
The exact same migration procedure can be applied for other selectors, mapping the old select to the expr specified in the table above.
If the same request has multiple selectors, you need to create a separate datapoint and filter for each one.
Note
Unless the user has explicitly specified they want to see the new datapoint and filter in the results, we only add it to the request and not into the outputs section.
Note
You should never include datapoints with symbol expression to the outputs. The actual symbols are always available in the response as JSON object keys. Requesting them additionally in the outputs adds unneeded traffic and processing.
Samples
Request
{ "instrumentCategory": "OPTION", "datapoints": [ { "name": "SYMBOL", "expr": "symbol" }, { "name": "UNDERLIER_SYMBOL", "expr": "underlying.symbol" }, { "name": "MARKET_EXCHANGE", "expr": "officialPlaceOfListing" }, { "name": "UNDERLIER_TYPE", "expr": "underlying.type" }, { "name": "IMPLIED_VOLATILITY_30_DAY", "expr": "underlying.atmIv30Day" }, { "name": "LAST_PRICE", "expr": "underlying.price(session=\"regular\")" }, { "name": "OPEN_INTEREST_OPTION", "expr": "openInterest" }, { "name": "RATIO_IV_TO_SV_1_MONTH", "expr": "underlying.atmIv30DayPosInRange / underlying.statVol1Month(session=\"regular\")" }, { "name": "STATISTICAL_VOLATILITY_1_MONTH", "expr": "underlying.statVol1Month(session=\"regular\")" }, { "name": "VOLUME", "expr": "underlying.dayVolume(session=\"regular\")" }, { "name": "VOLUME_OPTION", "expr": "dayVolume" }, { "name": "VOLUME_TOTAL_OPTION_10_DAY_AVERAGE", "expr": "underlying.optionsTotalVolumeAvg10Day" }, { "name": "VOLUME_TOTAL_CALL_RATIO", "expr": "underlying.optionsCallVolume(session=\"regular\") / underlying.optionsCallVolumeAvg10Day(session=\"regular\")" } ], "outputs": [ { "datapoint": 0 }, { "datapoint": 1 }, { "datapoint": 2 }, { "datapoint": 4 }, { "datapoint": 5 }, { "datapoint": 6 }, { "datapoint": 7 }, { "datapoint": 8 }, { "datapoint": 9 }, { "datapoint": 10 }, { "datapoint": 11 }, { "datapoint": 12 } ], "filters": [ { "datapoint": 3, "alternatives": [ { "predicate": "==", "args": [ "STOCK" ] } ] }, { "datapoint": 6, "alternatives": [ { "predicate": ">=", "args": [ 500 ] } ] }, { "datapoint": 7, "alternatives": [ { "predicate": ">=", "args": [ 1.5 ] } ] }, { "datapoint": 10, "alternatives": [ { "predicate": ">=", "args": [ 100 ] } ] }, { "datapoint": 11, "alternatives": [ { "predicate": ">=", "args": [ 1000 ] } ] } ], "sorters": [ { "datapoint": 7, "reversed": false } ], "options": { "snapshotSize": 1000 } } { "instrumentCategory": "OPTION", "datapoints": [ { "name": "SYMBOL", "expr": "symbol" }, { "name": "UNDERLIER_SYMBOL", "expr": "underlying.symbol" }, { "name": "MARKET_EXCHANGE", "expr": "officialPlaceOfListing" }, { "name": "UNDERLIER_TYPE", "expr": "underlying.type" }, { "name": "IMPLIED_VOLATILITY_30_DAY", "expr": "underlying.atmIv30Day" }, { "name": "LAST_PRICE", "expr": "underlying.price(session=\"regular\")" }, { "name": "OPEN_INTEREST_OPTION", "expr": "openInterest" }, { "name": "RATIO_IV_TO_SV_1_MONTH", "expr": "underlying.atmIv30DayPosInRange / underlying.statVol1Month(session=\"regular\")" }, { "name": "STATISTICAL_VOLATILITY_1_MONTH", "expr": "underlying.statVol1Month(session=\"regular\")" }, { "name": "VOLUME", "expr": "underlying.dayVolume(session=\"regular\")" }, { "name": "VOLUME_OPTION", "expr": "dayVolume" }, { "name": "VOLUME_TOTAL_OPTION_10_DAY_AVERAGE", "expr": "underlying.optionsTotalVolumeAvg10Day" }, { "name": "VOLUME_TOTAL_CALL_RATIO", "expr": "underlying.optionsCallVolume(session=\"regular\") / underlying.optionsCallVolumeAvg10Day(session=\"regular\")" } ], "outputs": [ { "datapoint": 0 }, { "datapoint": 1 }, { "datapoint": 2 }, { "datapoint": 4 }, { "datapoint": 5 }, { "datapoint": 6 }, { "datapoint": 7 }, { "datapoint": 8 }, { "datapoint": 9 }, { "datapoint": 10 }, { "datapoint": 11 }, { "datapoint": 12 } ], "selectors": [ { "select": "type", "values": [ "OPTION" ] } ], "filters": [ { "datapoint": 3, "alternatives": [ { "predicate": "==", "args": [ "STOCK" ] } ] }, { "datapoint": 6, "alternatives": [ { "predicate": ">=", "args": [ 500 ] } ] }, { "datapoint": 7, "alternatives": [ { "predicate": ">=", "args": [ 1.5 ] } ] }, { "datapoint": 10, "alternatives": [ { "predicate": ">=", "args": [ 100 ] } ] }, { "datapoint": 11, "alternatives": [ { "predicate": ">=", "args": [ 1000 ] } ] } ], "sorters": [ { "datapoint": 7, "reversed": false } ], "options": { "snapshotSize": 1000 } }
Response, snapshot
{ "outputNames": [ "SYMBOL", "UNDERLIER_SYMBOL", "MARKET_EXCHANGE", "IMPLIED_VOLATILITY_30_DAY", "LAST_PRICE", "OPEN_INTEREST_OPTION", "RATIO_IV_TO_SV_1_MONTH", "STATISTICAL_VOLATILITY_1_MONTH", "VOLUME", "VOLUME_OPTION", "VOLUME_TOTAL_OPTION_10_DAY_AVERAGE", "VOLUME_TOTAL_CALL_RATIO" ], "entries": [ { "symbol": ".LLY210319C200", "outputs": [ ".LLY210319C200", "LLY", "BATO", 0.4582537027697168, 208.15, 2060, 5.2591509629209, 0.1888967835442762, 2990271, 709, 19432.828571428574, 1.8236217044220673 ] }, { "symbol": ".LLY210319C202.5", "outputs": [ ".LLY210319C202.5", "LLY", "BATO", 0.4582537027697168, 208.15, 765, 5.2591509629209, 0.1888967835442762, 2990271, 524, 19432.828571428574, 1.8236217044220673 ] } ] }