FRTB SA Calculator API Documentation

Getting started

The ISDA FRTB-SA API is an API which accepts a Capital CRIF input and returns FRTB-SA Capital results based on ISDA's own calculator.

Notices

ISDA does not log/save the CRIF data you submit with your requests or the corresponding Capital results.

Accessing the API & Contacts

Each bank will be issued a unique API key for authentication purposes. All users within a bank are expected to use the same API key.
Please contact us at isda-analytics-frtb-sa@isda.org if:

  • Your bank has not received an API key.
  • You see something which you think is wrong as you use the API (including any behaviour which is inconsistent with this documentation). In your email, please include the body of your request, the HTTP code, and body of the response.

Postman & FRTB SA Calculator page (GUI)

If you're interested in submitting some of the example requests outlined in this documentation without having to write code, there are many free tools which allow you to do this. This demo demonstrates how to use Postman (one such tool) to send the Getting started example request. If you choose to use this tool, you do so at your own risk and discretion. Remember to remove your API key after use.

ISDA Analytics (Perun) also offers an FRTB SA Calculator page, which provides the same functionality as this API but you interact with the page using GUI (graphical user interface). The Calculator page requires you to execute each step manually (i.e. with a mouse) and upload your CRIF in Excel (.xlsx) format rather than the JSON format required by this API. To access the FRTB SA Calculator page, log into https://perun.isda.org/ and click the "FRTB SA Calculator" button on the top right.

Commitments

Currently, there's a limit of 100 API requests per day for each bank. Each request can contain a maximum of 10,000 CRIF rows.

Tip If your CRIF has more than 10,000 rows, we recommend you submit a separate request for each portfolio since the calculations are independent. You can then put the individual results together.

If you use this API, you're committing to not submitting multiple requests in parallel (i.e., only ever make a request if your previous request has already received a response) and not submitting more than 10 requests in any given second.

Getting started example (Example 1)

Submit a POST request to https://u18epe1glc.execute-api.us-east-1.amazonaws.com/api/calculate-capital, making sure to include "x-api-key": "your-unique-api-key" in the request header with the following JSON in the body:

Example 1 request body

{
  "model_parameters": {
    "jurisdiction": "US",
    "calculation_date": "2024-01-30"
  },
  "columns": [ "ApiRowID", "Portfolio ID", "Trade ID", "Variant", "Sensitivity ID", "RiskType", "Qualifier", "Bucket", "Label1", "Label2", "Amount", "AmountCurrency", "AmountUSD", "Label3", "EndDate", "CreditQuality", "LongShortInd", "CoveredBondInd", "TrancheThickness" ],
  "data": [
    [ 1, "Portfolio_1", "Trade_1", null, null, "GIRR_DELTA", "EUR", "2", "0.50", "RefCurve1", 2451076, "USD", 2451076, null, null, null, null, null, null ],
    [ 2, "Portfolio_1", "Trade_1", null, null, "EQ_DELTA", "Index1", "5", null, "Repo", 166932, "USD", 166932, null, null, null, null, null, null ]
  ]
}

                                                
Notes about the request:
  • Only POST requests are accepted.
  • "jurisdiction" must be one of the following: "BASEL", "UK_PRA", "US", "CHINA", "CRR". Learn more about which version of each jurisdiction is implemented here.
  • "calculation_date" is required for every request. (You can set this to be the calculation cob date. While this is only used to calculate maturity scaling for DRC_NS when it's not Variant 3, you're still required to submit this with every request.)
  • The order of "columns" is prescribed as given in this example. You are expected to pass this exact list of columns (in the same order) with each request, so the request is self-explanatory.
  • "data" expects a list of lists where each inner list corresponds to a CRIF row (with e.g., first value corresponding to "ApiRowID", second value to "Portfolio ID", etc.). The CRIF format and expected data types are described in section CRIF format expected by API.

The response has HTTP Code 200 (Success) and includes the following body:

Example 1 response body

{
    "could_compute_capital": true,
    "validation_outcome": "ACCEPTED",
    "model_parameters": {
        "jurisdiction": "US",
        "calculation_date": "2024-01-30"
    },
    "validation_observations_recorded": {
        "columns": [ "Severity", "Check Name", "Row ID", "Column", "Value", "Comment" ],
        "data": []
    },
    "capital_result": {
        "columns": [ "Portfolio", "Correlation Scenario", "Risk Type", "Currency", "CRIF Capital" ],
        "data": [
            [ "Portfolio_1", "high", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_1", "low", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_1", "medium", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_1", "high", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "low", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "medium", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", null, "Portfolio_Max", "USD", 29964.72783366117 ],
            [ "Portfolio_1", null, "SbM_Max", "USD", 29964.72783366117 ],
            [ "Portfolio_1", "high", "SbM_Total", "USD", 29964.72783366117 ],
            [ "Portfolio_1", "low", "SbM_Total", "USD", 29964.72783366117 ],
            [ "Portfolio_1", "medium", "SbM_Total", "USD", 29964.72783366117 ]
        ]
    }
}
                                                
Notes about the response body:
  • Notice data under capital_result which contains the calculated capital for Risk Types you submitted in your request, but also for additional aggregated Risk Types, where applicable.
  • Explanation of other fields of the response body is given in the Validation Outcome and Observations section.

CRIF format expected by API

Risk Data Standards and additional requirements

The CRIF data you submit with your request must follow the format dictated by the Risk Data Standards, except it's passed as JSON and there are two additional requirements:

  • "ApiRowID" is a new required field (effectively corresponding to RowID in Excel). This is necessary so specific validation_observations_recorded included in the response can easily point out to you which rows have issues. Each "ApiRowID" must be unique.
  • Variant 1 for DRC_NS is not allowed since Label3 is in AmountCCY and the API does not support FX conversions.

Important All calculations are based on the "AmountUSD" you provide. Notice that in the response, the "capital_result" "Currency" will always be USD. In other words, the API Calculator only takes in USD and returns USD—this is to prevent differences due to any FX conversions.

While the "Amount" and "AmountCurrency" fields in the request are effectively ignored by the API calculation, you are still required to submit them. This is so that the API CRIF format does not further deviate from the standard CRIF format dictated by the Risk Data Standards.

Data types

Since the CRIF data is passed in JSON, it is also important to define the data types expected. The data types accepted for each CRIF row depend on the field/column they correspond to:

Data Types Definition

{
    "ApiRowID": integer,
    "Portfolio ID": string,
    "Trade ID": string | null,
    "Variant": string | null,
    "Sensitivity ID": string | null,
    "RiskType": string,
    "Qualifier": string | null,
    "Bucket": string | null,
    "Label1": string | null,
    "Label2": string | null,
    "Amount": float,
    "AmountCurrency": string,
    "AmountUSD": float,
    "Label3": string | null,
    "EndDate": string (date in "YYYY-MM-DD" format) | null,
    "CreditQuality": string | null,
    "LongShortInd": string | null,
    "CoveredBondInd": string | null,
    "TrancheThickness": string | null
}
                                            
Where:
  • integer corresponds to an integer (whole number not surrounded by quotes). It must meet: 0 ≤ x ≤ 2,147,483,647.
  • string corresponds to a string (the values must be surrounded by double quotes). Max length of any string is 256 charcters.
  • null is to be used where no value is provided (rather than providing an empty string, i.e., "").
  • | corresponds to "OR".
  • float corresponds to a decimal number (whole number or decimal number not surrounded by quotes). The max amount of digits before the decimal place is 32. The max amount of digits after the decimal place is 8.

Notice that in Getting started example request body the first row of data has "Bucket": "2". While 2 is a numerical value, it is still surrounded by double quotes as the Bucket field is not always numerical and, as such, a string is expected to be passed.

Validation Outcome and Observations

If you breach a particular set of requirements, the API response body will outline the breaches in validation_observations_recorded. The next section outlines multiple examples of validation_observations_recorded.

Accepted

Let's revisit example response body from the Getting started example (Example 1):

Example 1 response body

{
    "could_compute_capital": true,
    "validation_outcome": "ACCEPTED",
    "model_parameters": {
        "jurisdiction": "US",
        "calculation_date": "2024-01-30"
    },
    "validation_observations_recorded": {
        "columns": [ "Severity", "Check Name", "Row ID", "Column", "Value", "Comment" ],
        "data": []
    },
    "capital_result": {
        "columns": [ "Portfolio", "Correlation Scenario", "Risk Type", "Currency", "CRIF Capital" ],
        "data": [
            [ "Portfolio_1", "high", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_1", "low", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_1", "medium", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_1", "high", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "low", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "medium", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", null, "Portfolio_Max", "USD", 29964.72783366117 ],
            [ "Portfolio_1", null, "SbM_Max", "USD", 29964.72783366117 ],
            [ "Portfolio_1", "high", "SbM_Total", "USD", 29964.72783366117 ],
            [ "Portfolio_1", "low", "SbM_Total", "USD", 29964.72783366117 ],
            [ "Portfolio_1", "medium", "SbM_Total", "USD", 29964.72783366117 ]
        ]
    }
}
                                                

If there are no issues in your submitted data, such as in the Getting Started example (Example 1) above, the "validation_outcome" is "ACCEPTED" (i.e., data entirely accepted with no issues). This is also in line with the fact that "data" under "validation_observations_recorded" is empty, indicating we observed nothing wrong when validating your file.

"validation_outcome": "ACCEPTED" implies "could_compute_capital": true and you can see the capital results in "data" under "capital_result".

"validation_outcome": "ACCEPTED" always results in HTTP 200 (success).

There are other possible values for "validation_outcome" which imply a specific HTTP code and whether "could_compute_capital" is true or false.

Partially Accepted

Results in HTTP code 200 and "could_compute_capital": true.

Example 2 (Partially Accepted) request body

{
  "model_parameters": {
    "jurisdiction": "US",
    "calculation_date": "2024-01-30"
  },
  "columns": [ "ApiRowID", "Portfolio ID", "Trade ID", "Variant", "Sensitivity ID", "RiskType", "Qualifier", "Bucket", "Label1", "Label2", "Amount", "AmountCurrency", "AmountUSD", "Label3", "EndDate", "CreditQuality", "LongShortInd", "CoveredBondInd", "TrancheThickness" ],
  "data": [
    [ 1, "Portfolio_1", "Trade_1", null, null, "GIRR_DELTA", "EUR", "2", "0.50", "RefCurve1", 2451076, "USD", 2451076, null, null, null, null, null, null ],
    [ 2, "Portfolio_1", "Trade_1", null, null, "hello", "Index1", "5", null, "Repo", 166932, "USD", 166932, null, null, null, null, null, null ],
    [ 3, "Portfolio_2", "Trade_1", null, null, "hi", "EUR", "2", "0.50", "RefCurve1", 2451076, "USD", 2451076, null, null, null, null, null, null ],
    [ 4, "Portfolio_4", "Trade_1", null, null, "EQ_DELTA", "Index1", "5", null, "Repo", 166932, "USD", 166932, null, null, null, null, null, null ]
  ]
}

                                                
Example 2 (Partially Accepted) response body

{
    "could_compute_capital": true,
    "validation_outcome": "PARTIALLY_ACCEPTED",
    "model_parameters": {
        "jurisdiction": "US",
        "calculation_date": "2024-01-30"
    },
    "validation_observations_recorded": {
        "columns": [ "Severity", "Check Name", "Row ID", "Column", "Value", "Comment" ],
        "data": [
            [
                "ROWS_REMOVED_FILE_ACCEPTED",
                "invalid_risk_types",
                2,
                null,
                "HELLO",
                "Found invalid risk type HELLO in portfolio Portfolio_1 - excluding row. The acceptable Risk Types are: COMM_CURV, COMM_DELTA, COMM_VEGA, CSR_NS_CURV, CSR_NS_DELTA, CSR_NS_VEGA, CSR_SC_CURV, CSR_SC_DELTA, CSR_SC_VEGA, CSR_SNC_CURV, CSR_SNC_DELTA, CSR_SNC_VEGA, DRC_NS, DRC_SC, DRC_SNC, EQ_CURV, EQ_DELTA, EQ_VEGA, FX_CURV, FX_DELTA, FX_VEGA, GIRR_CURV, GIRR_DELTA, GIRR_VEGA, RRAO_01_PERCENT, RRAO_1_PERCENT"
            ],
            [
                "ROWS_REMOVED_FILE_ACCEPTED",
                "invalid_risk_types",
                3,
                null,
                "HI",
                "Found invalid risk type HI in portfolio Portfolio_2 - excluding row. The acceptable Risk Types are: COMM_CURV, COMM_DELTA, COMM_VEGA, CSR_NS_CURV, CSR_NS_DELTA, CSR_NS_VEGA, CSR_SC_CURV, CSR_SC_DELTA, CSR_SC_VEGA, CSR_SNC_CURV, CSR_SNC_DELTA, CSR_SNC_VEGA, DRC_NS, DRC_SC, DRC_SNC, EQ_CURV, EQ_DELTA, EQ_VEGA, FX_CURV, FX_DELTA, FX_VEGA, GIRR_CURV, GIRR_DELTA, GIRR_VEGA, RRAO_01_PERCENT, RRAO_1_PERCENT"
            ]
        ]
    },
    "capital_result": {
        "columns": [ "Portfolio", "Correlation Scenario", "Risk Type", "Currency", "CRIF Capital" ],
        "data": [
            [ "Portfolio_1", "high", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "low", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "medium", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", null, "Portfolio_Max", "USD", 29463.931833661172 ],
            [ "Portfolio_1", null, "SbM_Max", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "high", "SbM_Total", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "low", "SbM_Total", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "medium", "SbM_Total", "USD", 29463.931833661172 ],
            [ "Portfolio_4", "high", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_4", "low", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_4", "medium", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_4", null, "Portfolio_Max", "USD", 500.796 ],
            [ "Portfolio_4", null, "SbM_Max", "USD", 500.796 ],
            [ "Portfolio_4", "high", "SbM_Total", "USD", 500.796 ],
            [ "Portfolio_4", "low", "SbM_Total", "USD", 500.796 ],
            [ "Portfolio_4", "medium", "SbM_Total", "USD", 500.796 ]
        ]
    }
}
                                                
Notes about response:
  • "data" under "validation_observations_recorded" informs you that rows 2 and 3 of your (CRIF) data were removed and outlines the reasons why.
  • Since row 3 was the only row tagged to Portfolio_2, and it was removed, no capital was calculated for Portfolio_2 (as seen in "data" under "capital_result").
  • Portfolio_1 included row 1 and 2. Row 2 was removed because it didn't pass validation but row 1 did pass; therefore, the "data" under "capital_result" tagged to Portfolio_1 is based purely on row 1.
  • Since there was more than one row which passed validation, then "validation_outcome": "PARTIALLY_ACCEPTED" and "could_compute_capital": true. If no rows passed validation, the "validation_outcome" would have changed to "REJECTED".
  • Important We recommend you assess all rows in "data" under "validation_observations_recorded" which have "Severity": "ROWS_REMOVED_FILE_ACCEPTED" to make sure no data is being excluded. Please note that there might be other rows within "data" under "validation_observations_recorded" with a different severity than "ROWS_REMOVED_FILE_ACCEPTED" while "validation_outcome" remains "PARTIALLY_ACCEPTED". Any such rows would be of lower severity and would be more informational, not leading to an exclusion of an entire CRIF row.

Rejected

Results in HTTP code 422 and "could_compute_capital": false.

Example 3 (Rejected) request body

{
  "model_parameters": {
    "jurisdiction": "US",
    "calculation_date": "2024-01-30"
  },
  "columns": [ "hello", "Portfolio ID", "Trade ID", "Variant", "Sensitivity ID", "RiskType", "Qualifier", "Bucket", "Label1", "Label2", "Amount", "AmountCurrency", "AmountUSD", "Label3", "EndDate", "CreditQuality", "LongShortInd", "CoveredBondInd", "TrancheThickness" ],
  "data": [
    [ 1, "Portfolio_1", "Trade_1", null, null, "GIRR_DELTA", "EUR", "2", "0.50", "RefCurve1", 2451076, "USD", 2451076, null, null, null, null, null, null ]
  ]
}

                                                
Example 3 (Rejected) response body

{
    "could_compute_capital": false,
    "validation_outcome": "REJECTED",
    "model_parameters": {
        "jurisdiction": "US",
        "calculation_date": "2024-01-30"
    },
    "validation_observations_recorded": {
        "columns": [ "Severity", "Check Name", "Row ID", "Column", "Value", "Comment" ],
        "data": [
            [
                "ISSUE_LEADING_TO_FILE_REJECTION",
                "incorrect_columns",
                null,
                null,
                "['hello', 'Portfolio ID', 'Trade ID', 'Variant', 'Sensitivity ID', 'RiskType', 'Qualifier', 'Bucket', 'Label1', 'Label2', 'Amount', 'AmountCurrency', 'AmountUSD', 'Label3', 'EndDate', 'CreditQuality', 'LongShortInd', 'CoveredBondInd', 'TrancheThickness']",
                "Columns provided in the request do not match the expected columns in the same order: ['ApiRowID', 'Portfolio ID', 'Trade ID', 'Variant', 'Sensitivity ID', 'RiskType', 'Qualifier', 'Bucket', 'Label1', 'Label2', 'Amount', 'AmountCurrency', 'AmountUSD', 'Label3', 'EndDate', 'CreditQuality', 'LongShortInd', 'CoveredBondInd', 'TrancheThickness']"
            ]
        ]
    },
    "capital_result": {
        "columns": [ "Portfolio", "Correlation Scenario", "Risk Type", "Currency", "CRIF Capital" ],
        "data": []
    }
}
                                                
Notes about response:
  • "validation_outcome": "REJECTED" indicates none of your CRIF data could be used to compute capital. Correspondingly, there are no rows in "data" under "capital_result".
  • The single observation in "data" under "validation_observations_recorded" informs you that you've passed the wrong headers ("hello" instead of "ApiRowID").
  • Important Please fix all observations in "data" under "validation_observations_recorded" which have "Severity": "ISSUE_LEADING_TO_FILE_REJECTION" to make sure your CRIF data isn't rejected in its entirety next time.

Now let's look at another "REJECTED" example with a slightly different observation comment:

Example 4 (Rejected) request body

{
  "model_parameters": {
    "jurisdiction": "US",
    "calculation_date": "2024-01-30"
  },
  "columns": [ "ApiRowID", "Portfolio ID", "Trade ID", "Variant", "Sensitivity ID", "RiskType", "Qualifier", "Bucket", "Label1", "Label2", "Amount", "AmountCurrency", "AmountUSD", "Label3", "EndDate", "CreditQuality", "LongShortInd", "CoveredBondInd", "TrancheThickness" ],
  "data": [
    [ 1, "Portfolio_1", "Trade_1", null, null, "GIRR_DELTA", "EUR", "2", "0.50", "RefCurve1", "hello", "USD", 2451076, null, null, null, null, null, null ],
    [ 2, "Portfolio_1", "Trade_1", null, null, "EQ_DELTA", "Index1", "5", null, "Repo", 166932, "USD", 166932, null, null, null, null, null, null ],
    [ 3, "Portfolio_2", "Trade_1", null, null, "GIRR_DELTA", "EUR", "2", "0.50", "RefCurve1", 2451076, "USD", 2451076, null, null, null, null, null, null ],
    [ 4, "Portfolio_4", "Trade_1", null, null, "EQ_DELTA", "Index1", "5", null, "Repo", 166932, "USD", 166932, null, null, null, null, null, null ]
  ]
}

                                                
Example 4 (Rejected) response body

{
    "could_compute_capital": false,
    "validation_outcome": "REJECTED",
    "model_parameters": {},
    "validation_observations_recorded": {
        "columns": [ "Severity", "Check Name", "Row ID", "Column", "Value", "Comment" ],
        "data": [
            [
                "ISSUE_LEADING_TO_FILE_REJECTION",
                "invalid_request_body_format",
                null,
                null,
                "",
                "('body', 'data', 0, 10): value is not a valid decimal"
            ]
        ]
    },
    "capital_result": {
        "columns": [ "Portfolio", "Correlation Scenario", "Risk Type", "Currency", "CRIF Capital" ],
        "data": []
    }
}
                                                
Notes about response:
  • Any observations in "data" under "validation_observations_recorded" which have "Check Name": "invalid_request_body_format" are not generated by ISDA and might be a little harder to interpret, but should follow a similar structure to what's described in the following explanation: In our example, the "Comment" of "('body', 'data', 0, 10): value is not a valid decimal" indicates that the value in your request's body, under "data", the first row (the automatic responses are 0-indexed, i.e., 0 corresponds to 1st row, 1 corresponds to 2nd row, etc. - this is regardless of what ApiRowID you pass), 11th value (again 0-indexed) is not a float (a decimal number), since we passed "hello".

Accepted with Comments

Results in HTTP code 200 and "could_compute_capital": true.

Example 5 (Accepted with Comments) request body

{
  "model_parameters": {
    "jurisdiction": "US",
    "calculation_date": "2024-01-30"
  },
  "columns": [ "ApiRowID", "Portfolio ID", "Trade ID", "Variant", "Sensitivity ID", "RiskType", "Qualifier", "Bucket", "Label1", "Label2", "Amount", "AmountCurrency", "AmountUSD", "Label3", "EndDate", "CreditQuality", "LongShortInd", "CoveredBondInd", "TrancheThickness" ],
  "data": [
    [ 1, "Portfolio_1", "Trade_1", null, null, "GIRR_DELTA", "EUR", "2", "0.50", "RefCurve1", 2451076, "USD", 2451076, null, null, null, null, null, null ],
    [ 2, "Portfolio_2", "Trade_2", null, null, "FX_DELTA", "CZK", "2", null, null, 166932, "USD", 166932, null, null, null, null, null, null ]
  ]
}

                                                
Example 5 (Accepted with Comments) response body

{
    "could_compute_capital": true,
    "validation_outcome": "ACCEPTED_WITH_COMMENTS",
    "model_parameters": {
        "jurisdiction": "US",
        "calculation_date": "2024-01-30"
    },
    "validation_observations_recorded": {
        "columns": [ "Severity", "Check Name", "Row ID", "Column", "Value", "Comment" ],
        "data": [
            [
                "DATA_ACCEPTED_WITH_COMMENTS",
                "currency_bucket_inconsistency",
                2,
                null,
                "",
                "For risk type FX_DELTA in Portfolio Portfolio_2, you passed bucket 2 and Qualifier CZK. For this risk type, a value of 2 for column 'Bucket' is associated with a reduced risk weight for the following currencies ('USD', 'EUR', 'JPY', 'GBP', 'AUD', 'CAD', 'CHF', 'MXN', 'CNY', 'NZD', 'HKD', 'SGD', 'TRY', 'KRW', 'SEK', 'ZAR', 'INR', 'NOK', 'BRL'). The currency you passed in the Qualifier column does not belong to this list, please consider whether this is correct."
            ]
        ]
    },
    "capital_result": {
        "columns": [ "Portfolio", "Correlation Scenario", "Risk Type", "Currency", "CRIF Capital" ],
        "data": [
            [ "Portfolio_1", "high", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "low", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "medium", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", null, "Portfolio_Max", "USD", 29463.931833661172 ],
            [ "Portfolio_1", null, "SbM_Max", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "high", "SbM_Total", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "low", "SbM_Total", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "medium", "SbM_Total", "USD", 29463.931833661172 ],
            [ "Portfolio_2", "high", "FX_DELTA", "USD", 17705.812379557898 ],
            [ "Portfolio_2", "low", "FX_DELTA", "USD", 17705.812379557898 ],
            [ "Portfolio_2", "medium", "FX_DELTA", "USD", 17705.812379557898 ],
            [ "Portfolio_2", null, "Portfolio_Max", "USD", 17705.812379557898 ],
            [ "Portfolio_2", null, "SbM_Max", "USD", 17705.812379557898 ],
            [ "Portfolio_2", "high", "SbM_Total", "USD", 17705.812379557898 ],
            [ "Portfolio_2", "low", "SbM_Total", "USD", 17705.812379557898 ],
            [ "Portfolio_2", "medium", "SbM_Total", "USD", 17705.812379557898 ]
        ]
    }
}
            
Notes about response:
  • Note the only observation raised in "data" within "validation_observations_recorded" does not have severity "ISSUE_LEADING_TO_FILE_REJECTION" or "ROWS_REMOVED_FILE_ACCEPTED". This indicates that this is an informational observation (i.e., the CRIF row was not removed but it was either amended or left in its original form with a comment raised, which is the case here).
  • You can see that the second CRIF row was the only row tagged to Portfolio_2 and capital results for that portfolio are included in "data" under "capital_result".

Additional responses

There are a number of other circumstances which result in responses with unique HTTP code and body combinations:

Response HTTP Code Response Body Issue ISDA Comment
403 {"message": "Forbidden"} Your submitted header does not contain the "x-api-key" key or the value passed is not your exact API key. The header needs to include "x-api-key": "replace_with_your_API_key".
403 {"message": "Missing Authentication Token"} You submitted your request with a method other than POST. We are aware "missing authentication token" is not an accurate message but unfortunately this message is not generated by ISDA. This message should, however inaccurate, uniquely identify the wrong method issue.
422 {"message": "Unable to decode JSON from request body."} You submitted no body in your request.
429 {"message": "Limit Exceeded"} You submitted more requests than allowed (the current quota is 100 requests per day). The system keeps track of how many requests were submitted with your API key per day.
429 {"message": "Too Many Requests"} One of the following:
  • You submitted multiple requests in parallel (i.e., submitted a request before a response for a previous request came back), which is not allowed as per the Commitments section.
  • You submitted more than 10 requests within a single second, which is not allowed as per the Commitments section.
  • Multiple other banks have sent API requests before you, and the server is busy responding to their requests.
We recommend you wait 30 seconds before submitting another request.
500 {"message": "Internal Server Error", "log_id": XXX} This is returned when the server encounters an unexpected error. Please feel free to submit the exact same request one more time after 30 seconds. If you still get a 500, please email us at isda-analytics-frtb@isda.org, specifying the "log_id" provided in the response body and including the full body you submitted with your request. Please do not attempt submitting the exact same request to the server again; you would most definitely get another 500.
502 This response will return HTML in the body restating "502 Bad Gateway" instead of returning JSON. The API server is down for maintenance. If this issue persists in 3 hours, please contact isda-analytics-frtb@isda.org.

If you receive an odd response which isn't readable and isn't covered in the above table, please double-check you're submitting a valid JSON in the body of your request. Free JSON validators are available online which can help you confirm this.

Important If you receive a non-200/non-422 response, for which the code and body combination is not included in the above table, and you're sure you're submitting a valid JSON in the body, please contact us at isda-analytics-frtb@isda.org. Please don't forget to specify 1) your submitted request header and body and 2) HTTP code and body of the received response.

Settings in model_parameters

Default Settings

So far, all request examples given in this documentation passed US as jurisdiction in the request.

Let's take the Getting started example, but pass CRR as jurisdiction instead:

Example 6 request body

{
  "model_parameters": {
    "jurisdiction": "CRR",
    "calculation_date": "2024-01-30"
  },
  "columns": [ "ApiRowID", "Portfolio ID", "Trade ID", "Variant", "Sensitivity ID", "RiskType", "Qualifier", "Bucket", "Label1", "Label2", "Amount", "AmountCurrency", "AmountUSD", "Label3", "EndDate", "CreditQuality", "LongShortInd", "CoveredBondInd", "TrancheThickness" ],
  "data": [
    [ 1, "Portfolio_1", "Trade_1", null, null, "GIRR_DELTA", "EUR", "2", "0.50", "RefCurve1", 2451076, "USD", 2451076, null, null, null, null, null, null ],
    [ 2, "Portfolio_1", "Trade_1", null, null, "EQ_DELTA", "Index1", "5", null, "Repo", 166932, "USD", 166932, null, null, null, null, null, null ]
  ]
}

                                                        
Example 6 response body

{
    "could_compute_capital": true,
    "validation_outcome": "ACCEPTED",
    "model_parameters": {
        "jurisdiction": "CRR",
        "calculation_date": "2024-01-30",
        "CRR_RW_INFL_XCCY": "Alt1",
        "VEGA_CORR_INFL_XCCY": "Alt1",
        "CSR_NS_INDX_BUCKET_NAME_CORRELATION": "Alt1",
        "DRC_NS_COVERED_SENIORITY": "Alt1",
        "CRR_CSR_NS_INDX_RATING_CORR": "Alt1"
    },
    "validation_observations_recorded": {
        "columns": [ "Severity", "Check Name", "Row ID", "Column", "Value", "Comment" ],
        "data": []
    },
    "capital_result": {
        "columns": [ "Portfolio", "Correlation Scenario", "Risk Type", "Currency", "CRIF Capital" ],
        "data": [
            [ "Portfolio_1", "high", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_1", "low", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_1", "medium", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_1", "high", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "low", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "medium", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", null, "Portfolio_Max", "USD", 29964.72783366117 ],
            [ "Portfolio_1", null, "SbM_Max", "USD", 29964.72783366117 ],
            [ "Portfolio_1", "high", "SbM_Total", "USD", 29964.72783366117 ],
            [ "Portfolio_1", "low", "SbM_Total", "USD", 29964.72783366117 ],
            [ "Portfolio_1", "medium", "SbM_Total", "USD", 29964.72783366117 ]
        ]
    }
}
                                                        

Notice there is now more info in the response model_parameters (on top of jurisdiction and calculation_date) - this extra info is called Settings.

CRR, UK_PRA, and BASEL are jurisdictions where ambiguity in certain rules can result in multiple implementations of those rules. Each implementation impacts the end result of the capital calculation.

In the API, every ambiguous rule corresponds to a different Setting and each Setting has alternative implementations (e.g. "Alt1" or "Alt2") to be selected from. The individual Settings and their alternative implementations are outlined here.

If the jurisdiction specified in your request is CRR, UK_PRA, or BASEL, the API calculator automatically applies the following default Settings(?) based on the selected jurisdiction:

Jurisdiction CRR_RW_INFL_XCCY VEGA_CORR_INFL_XCCY CSR_NS_INDX_BUCKET_NAME_CORRELATION DRC_NS_COVERED_SENIORITY CRR_CSR_NS_INDX_RATING_CORR
CRR Alt1 Alt1 Alt1 Alt1 Alt1
UK_PRA Not applicable Alt1 Not applicable Alt1 Not applicable
BASEL Not applicable Alt1 Not applicable Alt1 Not applicable

As you can see, the Settings returned in the Example 6 response body are the same as the default Settings for CRR in the above table.

Similarly, the following would happen if e.g. UK_PRA was passed in the request:

Example 7 request body

{
  "model_parameters": {
    "jurisdiction": "UK_PRA",
    "calculation_date": "2024-01-30"
  },
  "columns": [ "ApiRowID", "Portfolio ID", "Trade ID", "Variant", "Sensitivity ID", "RiskType", "Qualifier", "Bucket", "Label1", "Label2", "Amount", "AmountCurrency", "AmountUSD", "Label3", "EndDate", "CreditQuality", "LongShortInd", "CoveredBondInd", "TrancheThickness" ],
  "data": [
    [ 1, "Portfolio_1", "Trade_1", null, null, "GIRR_DELTA", "EUR", "2", "0.50", "RefCurve1", 2451076, "USD", 2451076, null, null, null, null, null, null ],
    [ 2, "Portfolio_1", "Trade_1", null, null, "EQ_DELTA", "Index1", "5", null, "Repo", 166932, "USD", 166932, null, null, null, null, null, null ]
  ]
}

                                                        
Example 7 response body

{
    "could_compute_capital": true,
    "validation_outcome": "ACCEPTED",
    "model_parameters": {
        "jurisdiction": "UK_PRA",
        "calculation_date": "2024-01-30",
        "VEGA_CORR_INFL_XCCY": "Alt1",
        "DRC_NS_COVERED_SENIORITY": "Alt1"
    },
    "validation_observations_recorded": {
        "columns": [ "Severity", "Check Name", "Row ID", "Column", "Value", "Comment" ],
        "data": []
    },
    "capital_result": {
        "columns": [ "Portfolio", "Correlation Scenario", "Risk Type", "Currency", "CRIF Capital" ],
        "data": [
            [ "Portfolio_1", "high", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_1", "low", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_1", "medium", "EQ_DELTA", "USD", 500.796 ],
            [ "Portfolio_1", "high", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "low", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", "medium", "GIRR_DELTA", "USD", 29463.931833661172 ],
            [ "Portfolio_1", null, "Portfolio_Max", "USD", 29964.72783366117 ],
            [ "Portfolio_1", null, "SbM_Max", "USD", 29964.72783366117 ],
            [ "Portfolio_1", "high", "SbM_Total", "USD", 29964.72783366117 ],
            [ "Portfolio_1", "low", "SbM_Total", "USD", 29964.72783366117 ],
            [ "Portfolio_1", "medium", "SbM_Total", "USD", 29964.72783366117 ]
        ]
    }
}
                                                        

Notice that only the two Settings applicable to UK_PRA, namely VEGA_CORR_INFL_XCCY and DRC_NS_COVERED_SENIORITY, are returned in the response body.

Changing default Settings

If you wish to specify which Settings(?) should be used during the capital calculation, you may:

  1. Specify all of the Settings (regardless of whether you're changing them):

    Example 8 request body
    
    {
      "model_parameters": {
        "jurisdiction": "CRR",
        "calculation_date": "2024-01-30",
        "CRR_RW_INFL_XCCY": "Alt1",
        "VEGA_CORR_INFL_XCCY": "Alt2",
        "CSR_NS_INDX_BUCKET_NAME_CORRELATION": "Alt2",
        "DRC_NS_COVERED_SENIORITY": "Alt1",
        "CRR_CSR_NS_INDX_RATING_CORR": "Alt1"
      },
      "columns": [ "ApiRowID", "Portfolio ID", "Trade ID", "Variant", "Sensitivity ID", "RiskType", "Qualifier", "Bucket", "Label1", "Label2", "Amount", "AmountCurrency", "AmountUSD", "Label3", "EndDate", "CreditQuality", "LongShortInd", "CoveredBondInd", "TrancheThickness" ],
      "data": [
        [ 1, "Portfolio_1", "Trade_1", null, null, "GIRR_DELTA", "EUR", "2", "0.50", "RefCurve1", 2451076, "USD", 2451076, null, null, null, null, null, null ],
        [ 2, "Portfolio_1", "Trade_1", null, null, "EQ_DELTA", "Index1", "5", null, "Repo", 166932, "USD", 166932, null, null, null, null, null, null ]
      ]
    }
    
                                                            

    Notice that VEGA_CORR_INFL_XCCY and CSR_NS_INDX_BUCKET_NAME_CORRELATION were passed as Alt2 (different from default behaviour), while the rest were passed as Alt1 (same as default behaviour).

    Which will be fully reflected in the response:

    Example 8 response body
    
    {
        "could_compute_capital": true,
        "validation_outcome": "ACCEPTED",
        "model_parameters": {
            "jurisdiction": "CRR",
            "calculation_date": "2024-01-30",
            "CRR_RW_INFL_XCCY": "Alt1",
            "VEGA_CORR_INFL_XCCY": "Alt2",
            "CSR_NS_INDX_BUCKET_NAME_CORRELATION": "Alt2",
            "DRC_NS_COVERED_SENIORITY": "Alt1",
            "CRR_CSR_NS_INDX_RATING_CORR": "Alt1"
        },
        "validation_observations_recorded": {
            "columns": [ "Severity", "Check Name", "Row ID", "Column", "Value", "Comment" ],
            "data": []
        },
        "capital_result": {
            "columns": [ "Portfolio", "Correlation Scenario", "Risk Type", "Currency", "CRIF Capital" ],
            "data": [
                [ "Portfolio_1", "high", "EQ_DELTA", "USD", 500.796 ],
                [ "Portfolio_1", "low", "EQ_DELTA", "USD", 500.796 ],
                [ "Portfolio_1", "medium", "EQ_DELTA", "USD", 500.796 ],
                [ "Portfolio_1", "high", "GIRR_DELTA", "USD", 29463.931833661172 ],
                [ "Portfolio_1", "low", "GIRR_DELTA", "USD", 29463.931833661172 ],
                [ "Portfolio_1", "medium", "GIRR_DELTA", "USD", 29463.931833661172 ],
                [ "Portfolio_1", null, "Portfolio_Max", "USD", 29964.72783366117 ],
                [ "Portfolio_1", null, "SbM_Max", "USD", 29964.72783366117 ],
                [ "Portfolio_1", "high", "SbM_Total", "USD", 29964.72783366117 ],
                [ "Portfolio_1", "low", "SbM_Total", "USD", 29964.72783366117 ],
                [ "Portfolio_1", "medium", "SbM_Total", "USD", 29964.72783366117 ]
            ]
        }
    }
                                                            

    Tip Note that there would be nothing wrong with always passing all the applicable Settings(?) for a jurisdiction, even if all the Settings(?) passed are the same as the default behaviour. You may choose to do this for your request to be more self-explanatory. It is, however, completely optional.

  2. Specify a subset of Settings for which you want the behavior to be different than the default behavior for the jurisdiction:

    Example 9 request body
    
    {
      "model_parameters": {
        "jurisdiction": "CRR",
        "calculation_date": "2024-01-30",
        "CRR_RW_INFL_XCCY": "Alt2",
        "VEGA_CORR_INFL_XCCY": "Alt2"
      },
      "columns": [ "ApiRowID", "Portfolio ID", "Trade ID", "Variant", "Sensitivity ID", "RiskType", "Qualifier", "Bucket", "Label1", "Label2", "Amount", "AmountCurrency", "AmountUSD", "Label3", "EndDate", "CreditQuality", "LongShortInd", "CoveredBondInd", "TrancheThickness" ],
      "data": [
        [ 1, "Portfolio_1", "Trade_1", null, null, "GIRR_DELTA", "EUR", "2", "0.50", "RefCurve1", 2451076, "USD", 2451076, null, null, null, null, null, null ],
        [ 2, "Portfolio_1", "Trade_1", null, null, "EQ_DELTA", "Index1", "5", null, "Repo", 166932, "USD", 166932, null, null, null, null, null, null ]
      ]
    }
    
                                                            

    The response will now have the default Settings for that jurisdiction, with the exception of CRR_RW_INFL_XCCY and VEGA_CORR_INFL_XCCY which were both passed as Alt2, instead of the Alt1 default:

    Example 9 response body
    
    {
        "could_compute_capital": true,
        "validation_outcome": "ACCEPTED",
        "model_parameters": {
            "jurisdiction": "CRR",
            "calculation_date": "2024-01-30",
            "CRR_RW_INFL_XCCY": "Alt2",
            "VEGA_CORR_INFL_XCCY": "Alt2",
            "CSR_NS_INDX_BUCKET_NAME_CORRELATION": "Alt1",
            "DRC_NS_COVERED_SENIORITY": "Alt1",
            "CRR_CSR_NS_INDX_RATING_CORR": "Alt1"
        },
        "validation_observations_recorded": {
            "columns": [ "Severity", "Check Name", "Row ID", "Column", "Value", "Comment" ],
            "data": []
        },
        "capital_result": {
            "columns": [ "Portfolio", "Correlation Scenario", "Risk Type", "Currency", "CRIF Capital" ],
            "data": [
                [ "Portfolio_1", "high", "EQ_DELTA", "USD", 500.796 ],
                [ "Portfolio_1", "low", "EQ_DELTA", "USD", 500.796 ],
                [ "Portfolio_1", "medium", "EQ_DELTA", "USD", 500.796 ],
                [ "Portfolio_1", "high", "GIRR_DELTA", "USD", 29463.931833661172 ],
                [ "Portfolio_1", "low", "GIRR_DELTA", "USD", 29463.931833661172 ],
                [ "Portfolio_1", "medium", "GIRR_DELTA", "USD", 29463.931833661172 ],
                [ "Portfolio_1", null, "Portfolio_Max", "USD", 29964.72783366117 ],
                [ "Portfolio_1", null, "SbM_Max", "USD", 29964.72783366117 ],
                [ "Portfolio_1", "high", "SbM_Total", "USD", 29964.72783366117 ],
                [ "Portfolio_1", "low", "SbM_Total", "USD", 29964.72783366117 ],
                [ "Portfolio_1", "medium", "SbM_Total", "USD", 29964.72783366117 ]
            ]
        }
    }
                                                            
Important:

If you're a user who aims to benchmark your own capital calculator against the ISDA calculator by using this API, it is important you pass the same Settings(?) in the request as are implemented in your own capital calculator.

Alternatively, if you're a user who just wishes to use the API out of the box, you may submit your request without passing any of the Settings(?), resulting in the default Settings being used.

Settings which are not applicable

If you attempt to pass any of the Settings tagged as "Not applicable" in the default Settings table, such as including CRR_RW_INFL_XCCY while also passing UK_PRA:

Example 10 request body

{
  "model_parameters": {
    "jurisdiction": "UK_PRA",
    "calculation_date": "2024-01-30",
    "CRR_RW_INFL_XCCY": "Alt1",
    "VEGA_CORR_INFL_XCCY": "Alt2"
  },
  "columns": [ "ApiRowID", "Portfolio ID", "Trade ID", "Variant", "Sensitivity ID", "RiskType", "Qualifier", "Bucket", "Label1", "Label2", "Amount", "AmountCurrency", "AmountUSD", "Label3", "EndDate", "CreditQuality", "LongShortInd", "CoveredBondInd", "TrancheThickness" ],
  "data": [
    [ 1, "Portfolio_1", "Trade_1", null, null, "GIRR_DELTA", "EUR", "2", "0.50", "RefCurve1", 2451076, "USD", 2451076, null, null, null, null, null, null ],
    [ 2, "Portfolio_1", "Trade_1", null, null, "EQ_DELTA", "Index1", "5", null, "Repo", 166932, "USD", 166932, null, null, null, null, null, null ]
  ]
}

                                                

You will get a validation_outcome REJECTED:

Example 10 response body

{
    "could_compute_capital": false,
    "validation_outcome": "REJECTED",
    "model_parameters": {
        "jurisdiction": "UK_PRA",
        "calculation_date": "2024-01-30",
        "VEGA_CORR_INFL_XCCY": "Alt2",
        "DRC_NS_COVERED_SENIORITY": "Alt1"
    },
    "validation_observations_recorded": {
        "columns": [ "Severity", "Check Name", "Row ID", "Column", "Value", "Comment" ],
        "data": [
            [
                "ISSUE_LEADING_TO_FILE_REJECTION",
                "check_allowed_settings",
                null,
                null,
                "CRR_RW_INFL_XCCY",
                "Setting CRR_RW_INFL_XCCY is not allowed for Jurisdiction UK_PRA. Please review allowed Settings in section 'Settings in model_parameters' of the API documentation."
            ]
        ]
    },
    "capital_result": {
        "columns": [ "Portfolio", "Correlation Scenario", "Risk Type", "Currency", "CRIF Capital" ],
        "data": []
    }
}
                                                

If you attempt to pass any Settings, while also passing jurisdiction as either US or CHINA:

Example 11 request body

{
  "model_parameters": {
    "jurisdiction": "US",
    "calculation_date": "2024-01-30",
    "VEGA_CORR_INFL_XCCY": "Alt1"
  },
  "columns": [ "ApiRowID", "Portfolio ID", "Trade ID", "Variant", "Sensitivity ID", "RiskType", "Qualifier", "Bucket", "Label1", "Label2", "Amount", "AmountCurrency", "AmountUSD", "Label3", "EndDate", "CreditQuality", "LongShortInd", "CoveredBondInd", "TrancheThickness" ],
  "data": [
    [ 1, "Portfolio_1", "Trade_1", null, null, "GIRR_DELTA", "EUR", "2", "0.50", "RefCurve1", 2451076, "USD", 2451076, null, null, null, null, null, null ],
    [ 2, "Portfolio_1", "Trade_1", null, null, "EQ_DELTA", "Index1", "5", null, "Repo", 166932, "USD", 166932, null, null, null, null, null, null ]
  ]
}

                                                

You will also get a validation_outcome REJECTED:

Example 11 response body

{
    "could_compute_capital": false,
    "validation_outcome": "REJECTED",
    "model_parameters": {
        "jurisdiction": "US",
        "calculation_date": "2024-01-30"
    },
    "validation_observations_recorded": {
        "columns": [ "Severity", "Check Name", "Row ID", "Column", "Value", "Comment" ],
        "data": [
            [
                "ISSUE_LEADING_TO_FILE_REJECTION",
                "check_allowed_settings",
                null,
                null,
                "VEGA_CORR_INFL_XCCY",
                "Setting VEGA_CORR_INFL_XCCY is not allowed for Jurisdiction US. Please review allowed Settings in section 'Settings in model_parameters' of the API documentation."
            ]
        ]
    },
    "capital_result": {
        "columns": [ "Portfolio", "Correlation Scenario", "Risk Type", "Currency", "CRIF Capital" ],
        "data": []
    }
}
                                                

Useful information for ISDA Analytics (Perun) Capital Benchmarking users

  • ISDA Analytics (Perun) has historically offered an FRTB SA Calculator as part of any benchmarking exercise. This calculator would compute the capital based on your submitted CRIF-equivalent file and call it "CRIF Capital", which would then be, for example, compared to your Submitted Capital in the "CRIF Reconciliation" report. You have also been able to navigate to "Calculate" Page in the "Calculator" section to see what the capital impact would be if you changed any of your CRIF data.

    However, since these were benchmarking exercises, you could only upload/submit data for specific hypothetical portfolios and trades.

    Important This API allows you to submit any CRIF data, not restricted to the hypothetical portfolios dictated by a benchmarking exercise, and responds with the calculated capital.

    Therefore, unlike in benchmarking exercises, the Portfolio ID and Trade ID fields in the CRIF data you submit are freeform strings (i.e. not restricted).

  • As previously mentioned here, the API calculator consumes only the AmountUSD you pass in your CRIF and returns all calculated capital in USD to avoid FX conversions. This is different to ISDA Analytics (Perun) Capital Benchmarking exercises, where ISDA works with the Amount submitted in your CRIF, converts it to the Portfolio Currency, and displays all results in Portfolio Currency.

  • As previously mentioned here, the API returns calculated capital for Risk Types you submitted in your request and for additional aggregated Risk Types, where applicable. However, unlike the Calculator Page within a Capital Benchmarking exercise (i.e. Phase), the API does not provide bucket-level breakdowns (Kb and Sb) or outline inter-bucket correlations.

  • As previously mentioned here, the API only allows you to submit 10,000 CRIF rows per request, which is not the case in Capital Benchmarking exercises where each CRIF file does not have a row cap.