- SECTIONS
The Quant Engine API is designed to fetch quant-style datasets from FactSet's content databases. These datasets are created by generating a timeline of dates and a corresponding universe for each date, followed by fetching a cube of content using screening expressions, FQL expressions, or universal screen parameters for each universe on each date.
Defining Inputs There are multiple methods to define each input. The universe can be specified using a list of identifiers, a universe-limiting screening code, or a universal screen document. Formulas can be screening codes, FQL codes, or universal screen parameters, and these can be mixed and matched. For instance, you can define the universe with a universal screen document and use that universe to retrieve parameters not only from the universal screen but also from screening and FQL codes.
Understanding the Data Cube It's important to note that while we refer to this construct as a data cube for simplicity, it isn't a true cube in the mathematical sense. It has three dimensions—date, formula, and identifier—but the size of the universe dimension isn't constant. If the universe is defined by a screening formula or a universal screen, its size and composition may vary from date to date. Additionally, the type of data stored in the cube varies with each formula; it could be scalar floats, integers, strings, or one-dimensional arrays of scalars. Depending on the universe's size, the number of dates, and formulas, generating the dataset might take several seconds, minutes, or even hours. As a result, the Quant Engine API employs an asynchronous request/response pattern.
Endpoints and Retrieval The API includes endpoints for creating the data "cube," checking its status, and retrieving the data. One retrieval endpoint fetches the data containing the universe and formula details, while the other retrieves the info part that includes error reports, performance metrics, and additional result information. An example workflow is given below:
Step 1: Trigger a Quant API calculation using the POST /calculations
endpoint.
Step 2: The results will be given back if calculated within the X-FactSet-Api-Long-Running-Deadline header value provided by user (in seconds) or you get back a calculation ID in POST response. This Calculation ID can then be passed to /calculations/{id}/status
endpoint to get the status of the calculation and then use the /calculation/{id}/units/{unitID}/result
endpoint to consume the results in feather
format associated with calculation units that have status updated as completed.
Step 3: The Calculation Id generated in #1 can be then used in the /calculation/{id}/units/{unitID}/info
endpoint to consume the metadata information of the associated calculation in feather
format.
Please use the Report Issue or Try For Free buttons to find out more.
import time
import os
from pathlib import Path
import pandas as pd
from fds.sdk.QuantEngine.apis import QuantCalculationsApi
from fds.sdk.QuantEngine import ApiClient, Configuration, ApiException
from fds.sdk.QuantEngine.models import *
from fds.sdk.utils.authentication import ConfidentialClient
from urllib3 import Retry
host = os.environ['FACTSET_HOST']
fds_username = os.environ['FACTSET_USERNAME']
fds_api_key = os.environ['FACTSET_API_KEY']
def main():
# Basic authentication: FactSetApiKey
# See https://github.com/FactSet/enterprise-sdk#api-key
# for information how to create an API key
config = Configuration()
config.host = host
config.username = fds_username
config.password = fds_api_key
# add proxy and/or disable ssl verification according to your development environment
# config.proxy = "<proxyUrl>"
config.verify_ssl = False
# (Preferred) OAuth 2.0: FactSetOAuth2
# See https://github.com/FactSet/enterprise-sdk#oauth-20
# for information on how to create the app-config.json file
# See https://github.com/FactSet/enterprise-sdk-utils-python#authentication
# for more information on using the ConfidentialClient class
# config = Configuration(
# fds_oauth_client=ConfidentialClient('/path/to/app-config.json')
# )
# Setting configuration to retry api calls on http status codes of 429 and 503.
config.retries = Retry(total=3, status=3, status_forcelist=frozenset([429, 503]), backoff_factor=2,
raise_on_status=False)
api_client = ApiClient(config)
try:
screeningExpressionUniverse = QuantScreeningExpressionUniverse(source="ScreeningExpressionUniverse",
universe_expr="(ISON_DOW AND P_PRICE > 150)=1",
universe_type="Equity",
security_expr="TICKER")
fdsDate = QuantFdsDate(source="FdsDate",
start_date="0", end_date="-2M", frequency="M", calendar="FIVEDAY",
override_universal_screen_calendar=False)
screeningExpression1 = QuantScreeningExpression(source="ScreeningExpression",
expr="FG_GICS_SECTOR", name="Sector (scr)")
screeningExpression2 = QuantScreeningExpression(source="ScreeningExpression",
expr="P_PRICE", name="Price (scr)")
fqlExpression1 = QuantFqlExpression(source="FqlExpression",
expr="P_PRICE(#DATE,#DATE,#FREQ)", name="Price (fql)", is_array_return_type=True)
fqlExpression2 = QuantFqlExpression(source="FqlExpression",
expr="OS_TOP_HLDR_POS(3,#DATE,#DATE,M,,S,SEC)", name="Top 3 Pos (fql)", is_array_return_type=True)
# uncomment the below code line to setup cache control; max-stale=0 will be a fresh adhoc run and the max-stale value is in seconds.
# Results are by default cached for 12 hours; Setting max-stale=300 will fetch a cached result which is 5 minutes older.
# cache_control = "max-stale=0"
quant_calculation_parameters = {"1": QuantCalculationParameters(
universe=screeningExpressionUniverse,
dates=fdsDate,
formulas=OneOfQuantFormulasArray([screeningExpression1, screeningExpression2, fqlExpression1, fqlExpression2]))
}
quant_calculations_meta = QuantCalculationMeta(format='Feather')
quant_calculation_parameter_root = QuantCalculationParametersRoot(
data=quant_calculation_parameters, meta=quant_calculations_meta)
quant_calculations_api = QuantCalculationsApi(api_client)
post_and_calculate_response = quant_calculations_api.post_and_calculate_with_http_info(
quant_calculation_parameters_root=quant_calculation_parameter_root)
# comment the above line and uncomment the below line to run the request with the cache_control header defined earlier
# post_and_calculate_response = quant_calculations_api.post_and_calculate_with_http_info(
# quant_calculation_parameters_root=quant_calculation_parameter_root, cache_control=cache_control)
if post_and_calculate_response[1] == 201:
output_calculation_result('data', post_and_calculate_response[0])
else:
calculation_id = post_and_calculate_response[0].response.data.calculationid
print("Calculation Id: " + calculation_id)
status_response = quant_calculations_api.get_calculation_status_by_id_with_http_info(id=calculation_id)
while status_response[1] == 202 and (status_response[0].data.status in ("Queued", "Executing")):
max_age = '5'
age_value = status_response[2].get("cache-control")
if age_value is not None:
max_age = age_value.replace("max-age=", "")
print('Sleeping: ' + max_age)
time.sleep(int(max_age))
status_response = quant_calculations_api.get_calculation_status_by_id_with_http_info(id=calculation_id)
for (calculation_unit_id, calculation_unit) in status_response[0].data.units.items():
if calculation_unit.status == "Success":
print("Calculation Unit Id: " +
calculation_unit_id + " Succeeded!!!")
result_response = quant_calculations_api.get_calculation_unit_result_by_id_with_http_info(id=calculation_id,
unit_id=calculation_unit_id)
print("Calculation Data")
output_calculation_result(
'data', result_response[0].read())
result_response = quant_calculations_api.get_calculation_unit_info_by_id_with_http_info(id=calculation_id,
unit_id=calculation_unit_id)
print("Calculation Info")
output_calculation_result(
'info', result_response[0].read())
else:
print("Calculation Unit Id:" +
calculation_unit_id + " Failed!!!")
print("Error message : " + str(calculation_unit.errors))
except ApiException as e:
print("Api exception Encountered")
print(e)
exit()
def output_calculation_result(output_prefix, result):
filename = Path(f'{output_prefix}-Output.ftr')
print(f'Writing output to {filename}')
filename.write_bytes(result)
df = pd.read_feather(filename)
print(df)
if __name__ == '__main__':
main()
v3
Summary
- Version 3.11.1 – Released on 08/21/2023
- Version 3.11.0 – Released on 03/14/2023
- Version 3.10.0 – Released on 01/23/2023
- Version 3.9.0 – Released on 06/06/2022
- Version 3.8.0 – Released on 03/21/2022
- Version 3.7.2 – Released on 02/14/2022
- Version 3.7.1 – Released on 10/21/2021
- Version 3.7.0 – Released on 09/20/2021
- Version 3.6.0 – Released on 09/07/2021
- Version 3.6.0 – Released on 08/23/2021
- Version 3.5.0 – Released on 08/06/2021
- Version 3.4.0 – Released on 07/28/2021
- Version 3.3.0 – Released on 07/19/2021
- Version 3.2.0 – Released on 07/12/2021
- Version 3.1.0 – Released on 06/28/2021
- Version 3.0.0 – Released on 06/07/2021
Functionality Additions
- Added new "warnings" field in Quant API response body to expose warnings from Quant Engine. [3.11.0]
- Updated the Quant API request body size limit to 195kb (195000 bytes). Breaching this limit will generate a 413 error response code. [3.10.0]
- Added security headers to the response headers. [3.10.0]
- Added two new optional fields "dateOffset" and "jobDescription" to the Quant API post request. [3.9.0]
- Added new optional field 'OverrideUniversalScreenCalendar' to Quants Post request. [3.8.0]
- Depricated support for screeningExpressionUniverse, universalScreenUniverse, identifierUniverse, fdsDate, dateList, screeningExpression, fqlExpression, universalScreenParameter & allUniversalScreenParameters. [3.7.0]
- Added support for new parameters - universe, dates & formulas. [3.7.0]
- Added new cache-control headers Age and Last-Modified on result endpoints so clients can see when the request was last calculate. [v3.6.0]
- Adding delete endpoint to cancel Quant calculations. [3.3.0]
- Deprecated ContentOrganization and added StachContentOrganization. [v3.1.0 ]
- Deprecated ContentType and added CalculationFormat as replacement. [v3.1.0 ]
Changes
- POST/PUT changes in meta section [3.1.0]
- Quant API released [3.0.0]
Bug Fixes
- Minor bug fix to removed "Required Attribute" for GetAllCalculations endpoint. [v3.11.1]
- IsArrayReturnType set to nullable. [3.11.0]
- Fixed a minor bug in Quant API error message. [3.11.0]
- Fixed bug in content-type header. [3.7.2]
- Fixed an issue where progress percentage for Quant calculations was not present as a member of the response body when retrieving calculation status via GET. [3.7.1]
- In engines v3 if we try to get results for a completed unit before other units have been completed or the batch is marked as completed it returns an error that it couldn't get batch status. [3.5.0]
- Fix bug that prevented running a calculation which was previously canceled and had the same parameters. [3.3.0]
- Fix to support row and column STACH content organizations. [3.2.0]