PA Engine API

Overview

Through the PA Engine API, request analytics for multi-asset class performance, attribution, and risk, including the flexibility of choosing your portfolio, benchmark, return period, and more.

API Definition

swagger_file_format

API Documentation

SDK Library

Code Snippet

Building and running a calculation
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;

using FactSet.AnalyticsAPI.Engines.v2.Api;
using FactSet.AnalyticsAPI.Engines.v2.Client;
using FactSet.AnalyticsAPI.Engines.v2.Model;

using FactSet.Protobuf.Stach;
using Google.Protobuf;

namespace FactSet.AnalyticsAPI.Engines.v2.Example.Examples
{
    public class PAEngineExample
    {
        private static Configuration _engineApiConfiguration;
        private const string BASE_PATH = "https://api.factset.com";
        private const string USER_NAME = "<username-serial>";
        private const string PASSWORD = "<apiKey>";
        private const string PA_DEFAULT_DOCUMENT = "PA_DOCUMENTS:DEFAULT";
        private const string PA_BENCHMARK_SP_50 = "BENCH:SP50";
        private const string PA_BENCHMARK_R_1000 = "BENCH:R.1000";

        private static Configuration GetEngineApiConfiguration()
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            if (_engineApiConfiguration != null)
            {
                return _engineApiConfiguration;
            }

            _engineApiConfiguration = new Configuration
            {
                BasePath = BASE_PATH,
                Username = USER_NAME,
                Password = PASSWORD
            };

            return _engineApiConfiguration;
        }

        public static void Main(string[] args)
        {
            try
            {
                var calculationParameters = new Calculation
                {
                    Pa = new Dictionary<string, PACalculationParameters> { { "1", GetPaCalculationParameters() } }
                };

                var calculationApi = new CalculationsApi(GetEngineApiConfiguration());

                var runCalculationResponse = calculationApi.RunCalculationWithHttpInfo(calculationParameters);

                var calculationId = runCalculationResponse.Headers["Location"][0].Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Last();
                ApiResponse<CalculationStatus> getStatus = null;

                while (getStatus == null || getStatus.Data.Status == CalculationStatus.StatusEnum.Queued || getStatus.Data.Status == CalculationStatus.StatusEnum.Executing)
                {
                    if (getStatus != null)
                    {
                        if (getStatus.Headers.ContainsKey("Cache-Control"))
                        {
                            var maxAge = getStatus.Headers["Cache-Control"][0];
                            if (string.IsNullOrWhiteSpace(maxAge))
                            {
                                Console.WriteLine("Sleeping for 2 seconds");
                                // Sleep for at least 2 seconds.
                                Thread.Sleep(2000);
                            }
                            else
                            {
                                var age = int.Parse(maxAge.Replace("max-age=", ""));
                                Console.WriteLine($"Sleeping for {age} seconds");
                                Thread.Sleep(age * 1000);
                            }
                        }
                    }

                    getStatus = calculationApi.GetCalculationStatusByIdWithHttpInfo(calculationId);
                }
                Console.WriteLine("Calculation Completed");

                // Check for failed calculations
                foreach (var paCalculationParameters in getStatus.Data.Pa)
                {
                    if (paCalculationParameters.Value.Status == CalculationUnitStatus.StatusEnum.Failed)
                    {
                        Console.WriteLine($"CalculationId : {paCalculationParameters.Key} Failed!!!");
                        Console.WriteLine($"Error message : {paCalculationParameters.Value.Error}");
                    }
                }

                // Get result of successful calculations
                foreach (var paCalculationParameters in getStatus.Data.Pa)
                {
                    PrintResult(paCalculationParameters);
                }

                Console.ReadKey();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }

        private static PACalculationParameters GetPaCalculationParameters()
        {
            // Build PA Engine calculation parameters
            var componentsApi = new ComponentsApi(GetEngineApiConfiguration());

            var componentsResponse = componentsApi.GetPAComponentsWithHttpInfo(PA_DEFAULT_DOCUMENT);

            if (componentsResponse.StatusCode != HttpStatusCode.OK)
            {
                LogError(componentsResponse);
                return null;
            }

            var paComponentId = componentsResponse.Data.First().Key;
            Console.WriteLine($"PA Component Id : {paComponentId}");

            var paAccountIdentifier = new PAIdentifier(PA_BENCHMARK_SP_50);
            var paAccounts = new List<PAIdentifier> { paAccountIdentifier };
            var paBenchmarkIdentifier = new PAIdentifier(PA_BENCHMARK_R_1000);
            var paBenchmarks = new List<PAIdentifier> { paBenchmarkIdentifier };

            var paCalculation = new PACalculationParameters(paComponentId, paAccounts, paBenchmarks);

            return paCalculation;
        }

        private static void PrintResult(KeyValuePair<string, CalculationUnitStatus> calculation)
        {
            if (calculation.Value.Status == CalculationUnitStatus.StatusEnum.Success)
            {
                ApiResponse<string> resultResponse = null;

                var utilityApi = new UtilityApi(GetEngineApiConfiguration());
                resultResponse = utilityApi.GetByUrlWithHttpInfo(calculation.Value.Result);

                if (resultResponse.StatusCode != HttpStatusCode.OK)
                {
                    LogError(resultResponse);
                    return;
                }

                Console.WriteLine($"CalculationId : {calculation.Key} Succeeded!!!");
                Console.WriteLine($"CalculationId : {calculation.Key} Result");
                Console.WriteLine("/****************************************************************/");

                // converting the data to Package object
                var jpSettings = JsonParser.Settings.Default;
                var jp = new JsonParser(jpSettings.WithIgnoreUnknownFields(true));
                var package = jp.Parse<Package>(resultResponse.Data);

                // To convert package to 2D tables.
                var tables = package.ConvertToTableFormat();
                Console.WriteLine(tables[0]);

                // Uncomment the following line to generate an Excel file
                // package.GenerateCSV();
                Console.WriteLine("/****************************************************************/");
            }
        }

        private static void LogError<T>(ApiResponse<T> response)
        {
            Console.WriteLine("Error!!!");
            Console.WriteLine($"Status Code: {response.StatusCode}");
            Console.WriteLine($"Request Key: {response.Headers["X-DataDirect-Request-Key"]}");
            Console.WriteLine($"Reason: {response.Data}");
        }
    }
}

 

package examples;

import java.util.List;
import java.util.Map;

import factset.analyticsapi.engines.v2.*;
import factset.analyticsapi.engines.v2.api.*;
import factset.analyticsapi.engines.v2.models.*;
import factset.analyticsapi.engines.v2.models.CalculationStatus.StatusEnum;

import com.google.protobuf.util.JsonFormat;
import com.google.protobuf.InvalidProtocolBufferException;
import com.factset.protobuf.stach.PackageProto.Package.Builder;
import com.factset.protobuf.stach.PackageProto.Package;

public class PAEngineExample {

  private static ApiClient apiClient = null;
  private static final String BASE_PATH = "https://api.factset.com";
  private static final String USERNAME = "<username-serial>";
  private static final String PASSWORD = "<apiKey>";
  public static final String PA_DEFAULT_DOCUMENT = "PA_DOCUMENTS:DEFAULT";
  private static final String COMPONENT_NAME = "Weights";
  private static final String COMPONENT_CATEGORY = "Weights / Exposures";

  public static void main(String[] args) throws InterruptedException {
    try {
      // Build PA Calculation Parameters List

      // Get all component from PA_DEFAULT_DOCUMENT with Name COMPONENT_NAME & category COMPONENT_CATEGORY
      ComponentsApi componentsApi = new ComponentsApi(getApiClient());
      Map<String, ComponentSummary> components = componentsApi.getPAComponents(PA_DEFAULT_DOCUMENT);
      String componentId = components.entrySet().stream()
          .filter(c -> c.getValue().getName().equals(COMPONENT_NAME) && c.getValue().getCategory().equals(COMPONENT_CATEGORY))
          .iterator().next().getKey();
      System.out.println(
          "ID of component with Name '" + COMPONENT_NAME + "' and category '" + COMPONENT_CATEGORY + "' : " + componentId);

      Calculation parameters = new Calculation();

      PACalculationParameters paItem = new PACalculationParameters();

      paItem.setComponentid(componentId);

      PAIdentifier accountPaIdentifier1 = new PAIdentifier();
      accountPaIdentifier1.setId("BENCH:SP50");
      paItem.addAccountsItem(accountPaIdentifier1);

      PAIdentifier accountPaIdentifier2 = new PAIdentifier();
      accountPaIdentifier2.setId("BENCH:R.2000");
      paItem.addAccountsItem(accountPaIdentifier2);

      PAIdentifier benchmarkPaIdentifier = new PAIdentifier();
      benchmarkPaIdentifier.setId("BENCH:R.2000");
      paItem.addBenchmarksItem(benchmarkPaIdentifier);

      PADateParameters dateParameters = new PADateParameters();
      dateParameters.setStartdate("20180101");
      dateParameters.setEnddate("20181231");
      dateParameters.setFrequency("Monthly");
      paItem.setDates(dateParameters);

      parameters.putPaItem("1", paItem);

      // Run Calculation Request
      CalculationsApi apiInstance = new CalculationsApi(getApiClient());
      ApiResponse<Void> createResponse = null;

      createResponse = apiInstance.runCalculationWithHttpInfo(parameters);

      String[] locationList = createResponse.getHeaders().get("Location").get(0).split("/");
      String requestId = locationList[locationList.length - 1];

      // Get Calculation Request Status
      ApiResponse<CalculationStatus> getStatus = null;

      while (getStatus == null || getStatus.getData().getStatus() == StatusEnum.QUEUED
          || getStatus.getData().getStatus() == StatusEnum.EXECUTING) {
        if (getStatus != null) {
          List<String> cacheControl = getStatus.getHeaders().get("Cache-Control");
          if (cacheControl != null) {
            int maxAge = Integer.parseInt(cacheControl.get(0).replaceAll("max-age=", ""));
            System.out.println("Sleeping for: " + maxAge + " seconds");
            Thread.sleep(maxAge * 1000);
          } else {
            System.out.println("Sleeping for: 2 seconds");
            Thread.sleep(2 * 1000);
          }
        }
        getStatus = apiInstance.getCalculationStatusByIdWithHttpInfo(requestId);
      }

      System.out.println("Calculation Completed!!!");

      // Check for Failed Calculations
      for (Map.Entry<String, CalculationUnitStatus> calculationParameters : getStatus.getData().getPa().entrySet()) {
        if (calculationParameters.getValue().getStatus() == CalculationUnitStatus.StatusEnum.FAILED) {
          System.out.println("CalculationId : " + calculationParameters.getKey() + " Failed!!!");
          System.out.println("Error message : " + calculationParameters.getValue().getError());
          return;
        }
      }

      // Get Result of Successful Caculations
      for (Map.Entry<String, CalculationUnitStatus> calculationParameters : getStatus.getData().getPa().entrySet()) {
        if (calculationParameters.getValue().getStatus() == CalculationUnitStatus.StatusEnum.SUCCESS) {
          UtilityApi utilityApiInstance = new UtilityApi(apiClient);
          ApiResponse<String> resultResponse = utilityApiInstance
              .getByUrlWithHttpInfo(calculationParameters.getValue().getResult());
          System.out.println("CalculationId : " + calculationParameters.getKey() + " Succeeded!!!");
          System.out.println("CalculationId : " + calculationParameters.getKey() + " Result");

          Builder builder = Package.newBuilder();
          try {
            JsonFormat.parser().ignoringUnknownFields().merge(resultResponse.getData(), builder);
          } catch (InvalidProtocolBufferException e) {
            System.out.println("Error while deserializing the response");
            e.printStackTrace();
          }

          Package result = (Package) builder.build();
          // To convert result to 2D tables.
          List<TableData> tables = StachExtensions.convertToTableFormat(result);
          System.out.println(tables.get(0)); // Prints the result in 2D table format.
          // Uncomment the following line to generate an Excel file
          // StachExtensions.generateExcel(result);
        }
      }
    } catch (ApiException e) {
      handleException("PAEngineExample#Main", e);
      return;
    }
  }

  private static ApiClient getApiClient() throws ApiException {
    if (apiClient != null) {
      return apiClient;
    }

    apiClient = new ApiClient();
    apiClient.setConnectTimeout(30000);
    apiClient.setReadTimeout(30000);
    apiClient.setBasePath(BASE_PATH);
    apiClient.setUsername(USERNAME);
    apiClient.setPassword(PASSWORD);

    return apiClient;
  }

  private static void handleException(String method, ApiException e) {
    System.err.println("Exception when calling " + method);
    if (e.getResponseHeaders() != null && e.getResponseHeaders().containsKey("x-datadirect-request-key")) {
      System.out.println("x-datadirect-request-key: " + e.getResponseHeaders().get("x-datadirect-request-key").get(0));
    }
    System.out.println("Status code: " + e.getCode());
    System.out.println("Reason: " + e.getResponseBody());
    e.printStackTrace();
  }
}

 

import time
import json

from google.protobuf import json_format
from google.protobuf.json_format import MessageToJson
from google.protobuf.json_format import MessageToDict
from fds.protobuf.stach.Package_pb2 import Package

from fds.analyticsapi.engines.v2.configuration import Configuration;
from fds.analyticsapi.engines.v2.api_client import ApiClient;
from fds.analyticsapi.engines.v2.api.components_api import ComponentsApi
from fds.analyticsapi.engines.v2.api.configurations_api import ConfigurationsApi
from fds.analyticsapi.engines.v2.api.calculations_api import CalculationsApi
from fds.analyticsapi.engines.v2.api.utility_api import UtilityApi
from fds.analyticsapi.engines.v2.models.calculation import Calculation
from fds.analyticsapi.engines.v2.models.pa_calculation_parameters import PACalculationParameters
from fds.analyticsapi.engines.v2.models.pa_identifier import PAIdentifier
from fds.analyticsapi.engines.v2.models.pa_date_parameters import PADateParameters

# Copy 'Converting API output to Table Format' snippet to a file with name 'stach_extension.py' to use below import statement
from stach_extensions import StachExtensions

host = "https://api.factset.com"
username = "<username-serial>"
password = "<apiKey>"

pa_document_name = "PA_DOCUMENTS:DEFAULT";
pa_benchmark_sp_50 = "BENCH:SP50";
pa_benchmark_r_1000 = "BENCH:R.1000";
startdate = "20180101"
enddate = "20181231"
frequency = "Monthly"

config = Configuration()
config.host = host
config.username = username
config.password = password
# add proxy and/or disable ssl verification according to your development environment
# config.proxy = "<proxyUrl>"
config.verify_ssl = False

api_client = ApiClient(config)

components_api = ComponentsApi(api_client);

components = components_api.get_pa_components(pa_document_name)
component_id = list(components.keys())[0]

pa_account_identifier = PAIdentifier(pa_benchmark_sp_50)
pa_accounts = [pa_account_identifier]
pa_benchmark_identifier = PAIdentifier(pa_benchmark_r_1000);
pa_benchmarks = [pa_benchmark_identifier]
pa_dates = PADateParameters(startdate, enddate, frequency)

pa_calculation_parameters = {"1": PACalculationParameters(component_id, pa_accounts, pa_benchmarks, pa_dates)}

calculation = Calculation(pa=pa_calculation_parameters)
print(calculation)

calculations_api = CalculationsApi(api_client)
run_calculation_response = calculations_api.run_calculation_with_http_info(calculation=calculation)

if(run_calculation_response[1] != 202):
    print("Calculation Failed!!!");
    print("Status Code: " + run_calculation_response[1])
    print("Request Key: " + run_calculation_response[2].get("x-datadirect-request-key"))
    print(run_calculation_response[0])
    quit()

calculation_id = run_calculation_response[2].get("location").split("/")[-1]
print("Calculation Id: " + calculation_id)

status_response = calculations_api.get_calculation_status_by_id_with_http_info(calculation_id)
while (status_response[1] == 200 and (status_response[0].status == "Queued" or status_response[0].status == "Executing")):
    max_age = '5'
    age_value = status_response[2].get("cache-control")
    if(age_value != None):
        max_age = age_value.replace("max-age=", "")
    print('Sleeping: ' + max_age)
    time.sleep(int(max_age))
    status_response = calculations_api.get_calculation_status_by_id_with_http_info(calculation_id)

if(status_response[1] != 200):
    print("Calculation Failed!!!");
    print("Status Code: " + status_response[1])
    print("Request Key: " + status_response[2].get("x-datadirect-request-key"))
    print(status_response[0])
    quit()

for calculation_unit in status_response[0].pa.values():
    print(calculation_unit)
    if calculation_unit.status == "Failed":
        print("Calculation Failed!!!");
    elif calculation_unit.status == "Success":
        utility_api = UtilityApi(api_client)
        result_response = utility_api.get_by_url_with_http_info(calculation_unit.result)

        if(result_response[1] != 200):
            print("Calculation Failed!!!");
            print("Status Code: " + result_response[1])
            print("Request Key: " + result_response[2].get("x-datadirect-request-key"))
            print(result_response[0])
            quit()

        # converting the data to Package object
        result =  json_format.Parse(json.dumps(result_response[0]), Package())
        # print(MessageToJson(result)) # To print the result object as a JSON
        # print(MessageToDict(result)) # To print the result object as a Dictionary
        tables = StachExtensions.convert_to_table_format(result) # To convert result to 2D tables.
        print(tables[0]) # Prints the result in 2D table format.
        # StachExtensions.generate_excel(result) # To get the result in table format exported to excel file.

 

Converting API output to Table format
using System;
using System.Collections.Generic;
using System.Linq;
using FactSet.Protobuf.Stach;
using FactSet.Protobuf.Stach.Table;

namespace FactSet.AnalyticsAPI.Engines.v2.Example.Examples
{
    public static class StachExtensions
    {
        public static void GenerateCSV(this Package package)
        {
            foreach (var table in package.ConvertToTableFormat())
            {
                System.IO.File.WriteAllText($"{Guid.NewGuid():N}.csv", table.ToString());
            }
        }

        public static List<Table> ConvertToTableFormat(this Package package)
        {
            var tables = new List<Table>();
            foreach (var primaryTableId in package.PrimaryTableIds)
            {
                tables.Add(GenerateTable(package, primaryTableId));
            }

            return tables;
        }

        private static Table GenerateTable(Package package, string primaryTableId)
        {
            var primaryTable = package.Tables[primaryTableId];
            var headerId = primaryTable.Definition.HeaderTableId;
            var headerTable = package.Tables[headerId];
            var columnIds = primaryTable.Definition.Columns.Select(c => c.Id).ToList();
            var headerColumnIds = headerTable.Definition.Columns.Select(c => c.Id).ToList();
            var dimensionColumnsCount = primaryTable.Definition.Columns.Count(c => c.IsDimension);
            var rowCount = primaryTable.Data.Rows.Count;
            var headerRowCount = headerTable.Data.Rows.Count;

            var table = new Table
            {
                Rows = new List<Row>()
            };
            // Constructs the column headers by considering dimension columns and header rows
            foreach (var columnId in headerColumnIds)
            {
                var headerRow = new Row { Cells = new List<string>() };
                for (int j = 0; j < dimensionColumnsCount; j++)
                {
                    headerRow.Cells.Add("");
                }

                for (int i = 0; i < headerRowCount; i++)
                {
                    headerRow.Cells.Add(Convert.ToString(headerTable.Data.Columns[columnId]
                        .GetValueHelper(headerTable.Definition.Columns.First(c => c.Id == columnId).Type, i)));
                }
                table.Rows.Add(headerRow);
            }
            // Constructs the column data
            for (int i = 0; i < rowCount; i++)
            {
                var dataRow = new Row { Cells = new List<string>() };
                foreach (var columnId in columnIds)
                {
                    dataRow.Cells.Add(Convert.ToString(primaryTable.Data.Columns[columnId]
                        .GetValueHelper(primaryTable.Definition.Columns.First(c => c.Id == columnId).Type, i)));
                }
                table.Rows.Add(dataRow);
            }

            return table;
        }
    }

    public static class SeriesDataHelper
    {
        public static object GetValueHelper(this SeriesData seriesData, DataType dataType, int index)
        {
            switch (dataType)
            {
                case DataType.Bool:
                    {
                        return seriesData.BoolArray?.Values?[index];
                    }
                case DataType.Double:
                    {
                        return seriesData.DoubleArray?.Values?[index];
                    }
                case DataType.Duration:
                    {
                        var v = seriesData.DurationArray?.Values?[index];
                        return v?.ToTimeSpan();
                    }
                case DataType.Float:
                    {
                        return seriesData.FloatArray?.Values?[index];
                    }
                case DataType.Int32:
                    {
                        return seriesData.Int32Array?.Values?[index];
                    }
                case DataType.Int64:
                    {
                        return seriesData.Int64Array?.Values?[index];
                    }
                case DataType.String:
                    {
                        return seriesData.StringArray?.Values?[index];
                    }
                case DataType.Timestamp:
                    {
                        var v = seriesData.TimestampArray?.Values?[index];
                        return v?.ToDateTime();
                    }
                default:
                    throw new NotImplementedException($"{dataType} is not implemented");
            }
        }
    }

    public class Table
    {
        public List<Row> Rows { get; set; }

        public override string ToString()
        {
            return string.Join(Environment.NewLine, Rows);
        }
    }

    public class Row
    {
        public List<string> Cells { get; set; }

        public override string ToString()
        {
            return string.Join(",", Cells.Select(c => c.Replace(",", "")));
        }
    }
}

 

package examples;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.poi.ss.formula.eval.NotImplementedException;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import com.factset.protobuf.stach.NullValues;
import com.factset.protobuf.stach.PackageProto.Package;
import com.factset.protobuf.stach.table.DataTypeProto.DataType;
import com.factset.protobuf.stach.table.SeriesDataProto.SeriesData;
import com.factset.protobuf.stach.table.SeriesDefinitionProto.SeriesDefinition;
import com.factset.protobuf.stach.table.TableProto.Table;

import com.google.protobuf.Duration;
import com.google.protobuf.Timestamp;

public class StachExtensions {
  // The purpose of this class is to provide the helper methods for converting
  // stach to Tabular format.

  public static void generateExcel(Package packageObj) {
    for (TableData table : convertToTableFormat(packageObj)) {
      writeDataToExcel(table, UUID.randomUUID().toString() + ".xlsx");
    }
  }

  public static List<TableData> convertToTableFormat(Package packageObj) {
    List<TableData> tables = new ArrayList<TableData>();
    for (String primaryTableId : packageObj.getPrimaryTableIdsList()) {
      tables.add(generateTable(packageObj, primaryTableId));
    }
    return tables;
  }

  private static TableData generateTable(Package packageObj, String primaryTableId) {
    Map<String, Table> tablesMap = packageObj.getTablesMap();
    Table primaryTable = tablesMap.get(primaryTableId);
    String headerId = primaryTable.getDefinition().getHeaderTableId();
    Table headerTable = tablesMap.get(headerId);
    int headerRowCount = headerTable.getData().getRowsCount();
    int rowsCount = primaryTable.getData().getRowsCount();

    TableData table = new TableData();

    // Construct the column headers by considering dimension columns and header
    // rows.
    List<SeriesDefinition> headerTableSeriesDefinitions = headerTable.getDefinition().getColumnsList();
    List<SeriesDefinition> primaryTableSeriesDefinitions = primaryTable.getDefinition().getColumnsList();

    Map<String, SeriesData> headerTableColumns = headerTable.getData().getColumnsMap();
    Map<String, SeriesData> primaryTableColumns = primaryTable.getData().getColumnsMap();

    for (SeriesDefinition headerTableseriesDefinition : headerTableSeriesDefinitions) {
      Row headerRow = new Row();
      for (SeriesDefinition primaryTableSeriesDefinition : primaryTableSeriesDefinitions) {
        if (primaryTableSeriesDefinition.getIsDimension()) {
          headerRow.Cells.add(primaryTableSeriesDefinition.getDescription());
        }
      }

      String headerColumnId = headerTableseriesDefinition.getId();
      String nullFormat = headerTableseriesDefinition.getFormat().getNullFormat();
      for (int i = 0; i < headerRowCount; i++) {
        headerRow.Cells.add(SeriesDataHelper.getValueHelper(headerTableColumns.get(headerColumnId),
            headerTableseriesDefinition.getType(), i, nullFormat).toString());
      }
      table.Rows.add(headerRow);
    }

    // Construct the column data
    for (int i = 0; i < rowsCount; i++) {
      Row dataRow = new Row();
      for (SeriesDefinition primaryTableSeriesDefinition : primaryTableSeriesDefinitions) {
        String nullFormat = primaryTableSeriesDefinition.getFormat().getNullFormat();
        String primaryTableColumnId = primaryTableSeriesDefinition.getId();
        dataRow.Cells.add(SeriesDataHelper.getValueHelper(primaryTableColumns.get(primaryTableColumnId),
            primaryTableSeriesDefinition.getType(), i, nullFormat).toString());
      }
      table.Rows.add(dataRow);
    }
    return table;
  }

  private static void writeDataToExcel(TableData table, String fileLocation) {
    XSSFWorkbook workbook = new XSSFWorkbook();
    XSSFSheet sheet = workbook.createSheet("PA Report");

    int rowsSize = table.Rows.size();
    for (int rowIndex = 0; rowIndex < rowsSize; rowIndex++) {
      XSSFRow xsswRow = sheet.createRow(rowIndex);
      List<String> cells = table.Rows.get(rowIndex).Cells;
      for (int cellIndex = 0; cellIndex < cells.size(); cellIndex++) {
        XSSFCell xssfCell = xsswRow.createCell(cellIndex);
        xssfCell.setCellValue(cells.get(cellIndex));
      }
    }

    try {
      FileOutputStream out = new FileOutputStream(new File(fileLocation));
      workbook.write(out);
      out.close();
      workbook.close();
    } catch (Exception e) {
      System.err.println("Failed to write data to excel");
      e.printStackTrace();
    }
  }
}

class SeriesDataHelper {
  public static Object getValueHelper(SeriesData seriesData, DataType dataType, int index, String nullFormat) {
    if (dataType == DataType.STRING) {
      String value = seriesData.getStringArray().getValues(index);
      return NullValues.STRING.equals(value) ? nullFormat : value;
    } else if (dataType == DataType.DOUBLE) {
      double value = seriesData.getDoubleArray().getValues(index);
      return Double.isNaN(value) ? nullFormat : value;
    } else if (dataType == DataType.BOOL) {
      return seriesData.getBoolArray().getValues(index);
    } else if (dataType == DataType.DURATION) {
      Duration value = seriesData.getDurationArray().getValues(index);
      return NullValues.DURATION.equals(value) ? nullFormat : value;
    } else if (dataType == DataType.FLOAT) {
      float value = seriesData.getFloatArray().getValues(index);
      return Float.isNaN(value) ? nullFormat : value;
    } else if (dataType == DataType.INT32) {
      int value = seriesData.getInt32Array().getValues(index);
      return NullValues.INT32 == value ? nullFormat : value;
    } else if (dataType == DataType.INT64) {
      long value = seriesData.getInt64Array().getValues(index);
      return NullValues.INT64 == value ? nullFormat : value;
    } else if (dataType == DataType.TIMESTAMP) {
      Timestamp value = seriesData.getTimestampArray().getValues(index);
      return NullValues.TIMESTAMP.equals(value) ? nullFormat : value;
    } else {
      throw new NotImplementedException(dataType + " is not implemented");
    }
  }
}

class TableData {
  List<Row> Rows = new ArrayList<Row>();

  public String toString() {
    return Rows.toString();
  }
}

class Row {
  List<String> Cells = new ArrayList<String>();

  public String toString() {
    return Cells.toString();
  }
}

 

import uuid
import math

import pandas as pd

from fds.protobuf.stach.Package_pb2 import Package
from fds.protobuf.stach.NullValues import NullValues
from fds.protobuf.stach.table.DataType_pb2 import DataType

class StachExtensions:
    """The purpose of this class is to provide the helper methods for converting Stach to Tabular format"""

    @staticmethod
    def generate_excel(package):
        for table in StachExtensions.convert_to_table_format(package):
            writer = pd.ExcelWriter(str(uuid.uuid1()) + ".xlsx")
            table.to_excel(excel_writer=writer)
            writer.save()
            writer.close()

    @staticmethod
    def convert_to_table_format(package):
        tables = list()
        for primary_table_id in package.primary_table_ids:
            tables.append(StachExtensions.generate_table(package, primary_table_id))
        return tables

    @staticmethod
    def generate_table(package_response, primary_table_id):

        if isinstance(package_response, Package):
            primary_table = package_response.tables[primary_table_id]
            header_id = primary_table.definition.header_table_id
            header_table = package_response.tables[header_id]
            dimension_columns = list(filter(lambda column_obj: column_obj.is_dimension, primary_table.definition.columns))
            dimension_columns_count = len(dimension_columns)
            row_count = len(primary_table.data.rows)
            header_row_count = len(header_table.data.rows)

            headers = list(list())
            # Constructs the column headers by considering dimension columns and header rows
            for series_definition_column in header_table.definition.columns:
                header_row = list()
                for i in range(0, dimension_columns_count, 1):
                    header_row.append(dimension_columns[i].description)

                for i in range(0, header_row_count, 1):
                    header_row.append(str(SeriesDataHelper.get_value_helper(header_table.data.columns[series_definition_column.id], series_definition_column.type, i, series_definition_column.format.null_format)))
                headers.append(header_row)

            data = list(list())
            # Constructs the column data
            for i in range(0, row_count, 1):
                data_row = list()
                for series_definition_column in primary_table.definition.columns:
                    data_row.append(str(SeriesDataHelper.get_value_helper(primary_table.data.columns[series_definition_column.id], series_definition_column.type, i, series_definition_column.format.null_format)))
                data.append(data_row)

            data_frame = pd.DataFrame(data=data)
            data_frame.columns = pd.MultiIndex.from_arrays(headers)
            return data_frame

        else:
            ValueError("Response data passed should be of package type.")


class SeriesDataHelper:

    @staticmethod
    def get_value_helper(series_data, datatype, index, null_format):
        if DataType.Name(datatype) == "STRING":
            return SeriesDataHelper.null_value_handler(datatype, series_data.string_array.values[index], null_format)
        elif DataType.Name(datatype) == "DOUBLE":
            return SeriesDataHelper.null_value_handler(datatype, series_data.double_array.values[index], null_format)
        elif DataType.Name(datatype) == "FLOAT":
            return SeriesDataHelper.null_value_handler(datatype, series_data.float_array.values[index], null_format)
        elif DataType.Name(datatype) == "INT32":
            return SeriesDataHelper.null_value_handler(datatype, series_data.int32_array.values[index], null_format)
        elif DataType.Name(datatype) == "INT64":
            return SeriesDataHelper.null_value_handler(datatype, series_data.int64_array.values[index], null_format)
        elif DataType.Name(datatype) == "BOOL":
            return SeriesDataHelper.null_value_handler(datatype, series_data.bool_array.values[index], null_format)
        elif DataType.Name(datatype) == "DURATION":
            return SeriesDataHelper.null_value_handler(datatype, series_data.duration_array.values[index], null_format)
        elif DataType.Name(datatype) == "TIMESTAMP":
            return SeriesDataHelper.null_value_handler(datatype, series_data.timestamp_array.values[index], null_format)
        else:
            ValueError("The datatype is not implemented")

    @staticmethod
    def null_value_handler(datatype, value, null_format):
        if DataType.Name(datatype) == "STRING":
            if NullValues.STRING == value:
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "DOUBLE":
            if math.isnan(value):
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "FLOAT":
            if math.isnan(value):
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "INT32":
            if NullValues.INT32 == value:
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "INT64":
            if NullValues.INT64 == value:
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "DURATION":
            if NullValues.DURATION.equals(value):
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "TIMESTAMP":
            if NullValues.TIMESTAMP.equals(value):
                return null_format
            else:
                return value
        else:
            return value

 

Building and running a calculation
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;

using FactSet.AnalyticsAPI.Engines.v2.Api;
using FactSet.AnalyticsAPI.Engines.v2.Client;
using FactSet.AnalyticsAPI.Engines.v2.Model;

using FactSet.Protobuf.Stach;
using Google.Protobuf;

namespace FactSet.AnalyticsAPI.Engines.v2.Example.Examples
{
    public class PAEngineExample
    {
        private static Configuration _engineApiConfiguration;
        private const string BASE_PATH = "https://api.factset.com";
        private const string USER_NAME = "<username-serial>";
        private const string PASSWORD = "<apiKey>";
        private const string PA_DEFAULT_DOCUMENT = "PA_DOCUMENTS:DEFAULT";
        private const string PA_BENCHMARK_SP_50 = "BENCH:SP50";
        private const string PA_BENCHMARK_R_1000 = "BENCH:R.1000";

        private static Configuration GetEngineApiConfiguration()
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            if (_engineApiConfiguration != null)
            {
                return _engineApiConfiguration;
            }

            _engineApiConfiguration = new Configuration
            {
                BasePath = BASE_PATH,
                Username = USER_NAME,
                Password = PASSWORD
            };

            return _engineApiConfiguration;
        }

        public static void Main(string[] args)
        {
            try
            {
                var calculationParameters = new Calculation
                {
                    Pa = new Dictionary<string, PACalculationParameters> { { "1", GetPaCalculationParameters() } }
                };

                var calculationApi = new CalculationsApi(GetEngineApiConfiguration());

                var runCalculationResponse = calculationApi.RunCalculationWithHttpInfo(calculationParameters);

                var calculationId = runCalculationResponse.Headers["Location"][0].Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Last();
                ApiResponse<CalculationStatus> getStatus = null;

                while (getStatus == null || getStatus.Data.Status == CalculationStatus.StatusEnum.Queued || getStatus.Data.Status == CalculationStatus.StatusEnum.Executing)
                {
                    if (getStatus != null)
                    {
                        if (getStatus.Headers.ContainsKey("Cache-Control"))
                        {
                            var maxAge = getStatus.Headers["Cache-Control"][0];
                            if (string.IsNullOrWhiteSpace(maxAge))
                            {
                                Console.WriteLine("Sleeping for 2 seconds");
                                // Sleep for at least 2 seconds.
                                Thread.Sleep(2000);
                            }
                            else
                            {
                                var age = int.Parse(maxAge.Replace("max-age=", ""));
                                Console.WriteLine($"Sleeping for {age} seconds");
                                Thread.Sleep(age * 1000);
                            }
                        }
                    }

                    getStatus = calculationApi.GetCalculationStatusByIdWithHttpInfo(calculationId);
                }
                Console.WriteLine("Calculation Completed");

                // Check for failed calculations
                foreach (var paCalculationParameters in getStatus.Data.Pa)
                {
                    if (paCalculationParameters.Value.Status == CalculationUnitStatus.StatusEnum.Failed)
                    {
                        Console.WriteLine($"CalculationId : {paCalculationParameters.Key} Failed!!!");
                        Console.WriteLine($"Error message : {paCalculationParameters.Value.Error}");
                    }
                }

                // Get result of successful calculations
                foreach (var paCalculationParameters in getStatus.Data.Pa)
                {
                    PrintResult(paCalculationParameters);
                }

                Console.ReadKey();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }

        private static PACalculationParameters GetPaCalculationParameters()
        {
            // Build PA Engine calculation parameters
            var componentsApi = new ComponentsApi(GetEngineApiConfiguration());

            var componentsResponse = componentsApi.GetPAComponentsWithHttpInfo(PA_DEFAULT_DOCUMENT);

            if (componentsResponse.StatusCode != HttpStatusCode.OK)
            {
                LogError(componentsResponse);
                return null;
            }

            var paComponentId = componentsResponse.Data.First().Key;
            Console.WriteLine($"PA Component Id : {paComponentId}");

            var paAccountIdentifier = new PAIdentifier(PA_BENCHMARK_SP_50);
            var paAccounts = new List<PAIdentifier> { paAccountIdentifier };
            var paBenchmarkIdentifier = new PAIdentifier(PA_BENCHMARK_R_1000);
            var paBenchmarks = new List<PAIdentifier> { paBenchmarkIdentifier };

            var paCalculation = new PACalculationParameters(paComponentId, paAccounts, paBenchmarks);

            return paCalculation;
        }

        private static void PrintResult(KeyValuePair<string, CalculationUnitStatus> calculation)
        {
            if (calculation.Value.Status == CalculationUnitStatus.StatusEnum.Success)
            {
                ApiResponse<string> resultResponse = null;

                var utilityApi = new UtilityApi(GetEngineApiConfiguration());
                resultResponse = utilityApi.GetByUrlWithHttpInfo(calculation.Value.Result);

                if (resultResponse.StatusCode != HttpStatusCode.OK)
                {
                    LogError(resultResponse);
                    return;
                }

                Console.WriteLine($"CalculationId : {calculation.Key} Succeeded!!!");
                Console.WriteLine($"CalculationId : {calculation.Key} Result");
                Console.WriteLine("/****************************************************************/");

                // converting the data to Package object
                var jpSettings = JsonParser.Settings.Default;
                var jp = new JsonParser(jpSettings.WithIgnoreUnknownFields(true));
                var package = jp.Parse<Package>(resultResponse.Data);

                // To convert package to 2D tables.
                var tables = package.ConvertToTableFormat();
                Console.WriteLine(tables[0]);

                // Uncomment the following line to generate an Excel file
                // package.GenerateCSV();
                Console.WriteLine("/****************************************************************/");
            }
        }

        private static void LogError<T>(ApiResponse<T> response)
        {
            Console.WriteLine("Error!!!");
            Console.WriteLine($"Status Code: {response.StatusCode}");
            Console.WriteLine($"Request Key: {response.Headers["X-DataDirect-Request-Key"]}");
            Console.WriteLine($"Reason: {response.Data}");
        }
    }
}

 

package examples;

import java.util.List;
import java.util.Map;

import factset.analyticsapi.engines.v2.*;
import factset.analyticsapi.engines.v2.api.*;
import factset.analyticsapi.engines.v2.models.*;
import factset.analyticsapi.engines.v2.models.CalculationStatus.StatusEnum;

import com.google.protobuf.util.JsonFormat;
import com.google.protobuf.InvalidProtocolBufferException;
import com.factset.protobuf.stach.PackageProto.Package.Builder;
import com.factset.protobuf.stach.PackageProto.Package;

public class PAEngineExample {

  private static ApiClient apiClient = null;
  private static final String BASE_PATH = "https://api.factset.com";
  private static final String USERNAME = "<username-serial>";
  private static final String PASSWORD = "<apiKey>";
  public static final String PA_DEFAULT_DOCUMENT = "PA_DOCUMENTS:DEFAULT";
  private static final String COMPONENT_NAME = "Weights";
  private static final String COMPONENT_CATEGORY = "Weights / Exposures";

  public static void main(String[] args) throws InterruptedException {
    try {
      // Build PA Calculation Parameters List

      // Get all component from PA_DEFAULT_DOCUMENT with Name COMPONENT_NAME & category COMPONENT_CATEGORY
      ComponentsApi componentsApi = new ComponentsApi(getApiClient());
      Map<String, ComponentSummary> components = componentsApi.getPAComponents(PA_DEFAULT_DOCUMENT);
      String componentId = components.entrySet().stream()
          .filter(c -> c.getValue().getName().equals(COMPONENT_NAME) && c.getValue().getCategory().equals(COMPONENT_CATEGORY))
          .iterator().next().getKey();
      System.out.println(
          "ID of component with Name '" + COMPONENT_NAME + "' and category '" + COMPONENT_CATEGORY + "' : " + componentId);

      Calculation parameters = new Calculation();

      PACalculationParameters paItem = new PACalculationParameters();

      paItem.setComponentid(componentId);

      PAIdentifier accountPaIdentifier1 = new PAIdentifier();
      accountPaIdentifier1.setId("BENCH:SP50");
      paItem.addAccountsItem(accountPaIdentifier1);

      PAIdentifier accountPaIdentifier2 = new PAIdentifier();
      accountPaIdentifier2.setId("BENCH:R.2000");
      paItem.addAccountsItem(accountPaIdentifier2);

      PAIdentifier benchmarkPaIdentifier = new PAIdentifier();
      benchmarkPaIdentifier.setId("BENCH:R.2000");
      paItem.addBenchmarksItem(benchmarkPaIdentifier);

      PADateParameters dateParameters = new PADateParameters();
      dateParameters.setStartdate("20180101");
      dateParameters.setEnddate("20181231");
      dateParameters.setFrequency("Monthly");
      paItem.setDates(dateParameters);

      parameters.putPaItem("1", paItem);

      // Run Calculation Request
      CalculationsApi apiInstance = new CalculationsApi(getApiClient());
      ApiResponse<Void> createResponse = null;

      createResponse = apiInstance.runCalculationWithHttpInfo(parameters);

      String[] locationList = createResponse.getHeaders().get("Location").get(0).split("/");
      String requestId = locationList[locationList.length - 1];

      // Get Calculation Request Status
      ApiResponse<CalculationStatus> getStatus = null;

      while (getStatus == null || getStatus.getData().getStatus() == StatusEnum.QUEUED
          || getStatus.getData().getStatus() == StatusEnum.EXECUTING) {
        if (getStatus != null) {
          List<String> cacheControl = getStatus.getHeaders().get("Cache-Control");
          if (cacheControl != null) {
            int maxAge = Integer.parseInt(cacheControl.get(0).replaceAll("max-age=", ""));
            System.out.println("Sleeping for: " + maxAge + " seconds");
            Thread.sleep(maxAge * 1000);
          } else {
            System.out.println("Sleeping for: 2 seconds");
            Thread.sleep(2 * 1000);
          }
        }
        getStatus = apiInstance.getCalculationStatusByIdWithHttpInfo(requestId);
      }

      System.out.println("Calculation Completed!!!");

      // Check for Failed Calculations
      for (Map.Entry<String, CalculationUnitStatus> calculationParameters : getStatus.getData().getPa().entrySet()) {
        if (calculationParameters.getValue().getStatus() == CalculationUnitStatus.StatusEnum.FAILED) {
          System.out.println("CalculationId : " + calculationParameters.getKey() + " Failed!!!");
          System.out.println("Error message : " + calculationParameters.getValue().getError());
          return;
        }
      }

      // Get Result of Successful Caculations
      for (Map.Entry<String, CalculationUnitStatus> calculationParameters : getStatus.getData().getPa().entrySet()) {
        if (calculationParameters.getValue().getStatus() == CalculationUnitStatus.StatusEnum.SUCCESS) {
          UtilityApi utilityApiInstance = new UtilityApi(apiClient);
          ApiResponse<String> resultResponse = utilityApiInstance
              .getByUrlWithHttpInfo(calculationParameters.getValue().getResult());
          System.out.println("CalculationId : " + calculationParameters.getKey() + " Succeeded!!!");
          System.out.println("CalculationId : " + calculationParameters.getKey() + " Result");

          Builder builder = Package.newBuilder();
          try {
            JsonFormat.parser().ignoringUnknownFields().merge(resultResponse.getData(), builder);
          } catch (InvalidProtocolBufferException e) {
            System.out.println("Error while deserializing the response");
            e.printStackTrace();
          }

          Package result = (Package) builder.build();
          // To convert result to 2D tables.
          List<TableData> tables = StachExtensions.convertToTableFormat(result);
          System.out.println(tables.get(0)); // Prints the result in 2D table format.
          // Uncomment the following line to generate an Excel file
          // StachExtensions.generateExcel(result);
        }
      }
    } catch (ApiException e) {
      handleException("PAEngineExample#Main", e);
      return;
    }
  }

  private static ApiClient getApiClient() throws ApiException {
    if (apiClient != null) {
      return apiClient;
    }

    apiClient = new ApiClient();
    apiClient.setConnectTimeout(30000);
    apiClient.setReadTimeout(30000);
    apiClient.setBasePath(BASE_PATH);
    apiClient.setUsername(USERNAME);
    apiClient.setPassword(PASSWORD);

    return apiClient;
  }

  private static void handleException(String method, ApiException e) {
    System.err.println("Exception when calling " + method);
    if (e.getResponseHeaders() != null && e.getResponseHeaders().containsKey("x-datadirect-request-key")) {
      System.out.println("x-datadirect-request-key: " + e.getResponseHeaders().get("x-datadirect-request-key").get(0));
    }
    System.out.println("Status code: " + e.getCode());
    System.out.println("Reason: " + e.getResponseBody());
    e.printStackTrace();
  }
}

 

import time
import json

from google.protobuf import json_format
from google.protobuf.json_format import MessageToJson
from google.protobuf.json_format import MessageToDict
from fds.protobuf.stach.Package_pb2 import Package

from fds.analyticsapi.engines.v2.configuration import Configuration;
from fds.analyticsapi.engines.v2.api_client import ApiClient;
from fds.analyticsapi.engines.v2.api.components_api import ComponentsApi
from fds.analyticsapi.engines.v2.api.configurations_api import ConfigurationsApi
from fds.analyticsapi.engines.v2.api.calculations_api import CalculationsApi
from fds.analyticsapi.engines.v2.api.utility_api import UtilityApi
from fds.analyticsapi.engines.v2.models.calculation import Calculation
from fds.analyticsapi.engines.v2.models.pa_calculation_parameters import PACalculationParameters
from fds.analyticsapi.engines.v2.models.pa_identifier import PAIdentifier
from fds.analyticsapi.engines.v2.models.pa_date_parameters import PADateParameters

# Copy 'Converting API output to Table Format' snippet to a file with name 'stach_extension.py' to use below import statement
from stach_extensions import StachExtensions

host = "https://api.factset.com"
username = "<username-serial>"
password = "<apiKey>"

pa_document_name = "PA_DOCUMENTS:DEFAULT";
pa_benchmark_sp_50 = "BENCH:SP50";
pa_benchmark_r_1000 = "BENCH:R.1000";
startdate = "20180101"
enddate = "20181231"
frequency = "Monthly"

config = Configuration()
config.host = host
config.username = username
config.password = password
# add proxy and/or disable ssl verification according to your development environment
# config.proxy = "<proxyUrl>"
config.verify_ssl = False

api_client = ApiClient(config)

components_api = ComponentsApi(api_client);

components = components_api.get_pa_components(pa_document_name)
component_id = list(components.keys())[0]

pa_account_identifier = PAIdentifier(pa_benchmark_sp_50)
pa_accounts = [pa_account_identifier]
pa_benchmark_identifier = PAIdentifier(pa_benchmark_r_1000);
pa_benchmarks = [pa_benchmark_identifier]
pa_dates = PADateParameters(startdate, enddate, frequency)

pa_calculation_parameters = {"1": PACalculationParameters(component_id, pa_accounts, pa_benchmarks, pa_dates)}

calculation = Calculation(pa=pa_calculation_parameters)
print(calculation)

calculations_api = CalculationsApi(api_client)
run_calculation_response = calculations_api.run_calculation_with_http_info(calculation=calculation)

if(run_calculation_response[1] != 202):
    print("Calculation Failed!!!");
    print("Status Code: " + run_calculation_response[1])
    print("Request Key: " + run_calculation_response[2].get("x-datadirect-request-key"))
    print(run_calculation_response[0])
    quit()

calculation_id = run_calculation_response[2].get("location").split("/")[-1]
print("Calculation Id: " + calculation_id)

status_response = calculations_api.get_calculation_status_by_id_with_http_info(calculation_id)
while (status_response[1] == 200 and (status_response[0].status == "Queued" or status_response[0].status == "Executing")):
    max_age = '5'
    age_value = status_response[2].get("cache-control")
    if(age_value != None):
        max_age = age_value.replace("max-age=", "")
    print('Sleeping: ' + max_age)
    time.sleep(int(max_age))
    status_response = calculations_api.get_calculation_status_by_id_with_http_info(calculation_id)

if(status_response[1] != 200):
    print("Calculation Failed!!!");
    print("Status Code: " + status_response[1])
    print("Request Key: " + status_response[2].get("x-datadirect-request-key"))
    print(status_response[0])
    quit()

for calculation_unit in status_response[0].pa.values():
    print(calculation_unit)
    if calculation_unit.status == "Failed":
        print("Calculation Failed!!!");
    elif calculation_unit.status == "Success":
        utility_api = UtilityApi(api_client)
        result_response = utility_api.get_by_url_with_http_info(calculation_unit.result)

        if(result_response[1] != 200):
            print("Calculation Failed!!!");
            print("Status Code: " + result_response[1])
            print("Request Key: " + result_response[2].get("x-datadirect-request-key"))
            print(result_response[0])
            quit()

        # converting the data to Package object
        result =  json_format.Parse(json.dumps(result_response[0]), Package())
        # print(MessageToJson(result)) # To print the result object as a JSON
        # print(MessageToDict(result)) # To print the result object as a Dictionary
        tables = StachExtensions.convert_to_table_format(result) # To convert result to 2D tables.
        print(tables[0]) # Prints the result in 2D table format.
        # StachExtensions.generate_excel(result) # To get the result in table format exported to excel file.

 

Converting API output to Table format
using System;
using System.Collections.Generic;
using System.Linq;
using FactSet.Protobuf.Stach;
using FactSet.Protobuf.Stach.Table;

namespace FactSet.AnalyticsAPI.Engines.v2.Example.Examples
{
    public static class StachExtensions
    {
        public static void GenerateCSV(this Package package)
        {
            foreach (var table in package.ConvertToTableFormat())
            {
                System.IO.File.WriteAllText($"{Guid.NewGuid():N}.csv", table.ToString());
            }
        }

        public static List<Table> ConvertToTableFormat(this Package package)
        {
            var tables = new List<Table>();
            foreach (var primaryTableId in package.PrimaryTableIds)
            {
                tables.Add(GenerateTable(package, primaryTableId));
            }

            return tables;
        }

        private static Table GenerateTable(Package package, string primaryTableId)
        {
            var primaryTable = package.Tables[primaryTableId];
            var headerId = primaryTable.Definition.HeaderTableId;
            var headerTable = package.Tables[headerId];
            var columnIds = primaryTable.Definition.Columns.Select(c => c.Id).ToList();
            var headerColumnIds = headerTable.Definition.Columns.Select(c => c.Id).ToList();
            var dimensionColumnsCount = primaryTable.Definition.Columns.Count(c => c.IsDimension);
            var rowCount = primaryTable.Data.Rows.Count;
            var headerRowCount = headerTable.Data.Rows.Count;

            var table = new Table
            {
                Rows = new List<Row>()
            };
            // Constructs the column headers by considering dimension columns and header rows
            foreach (var columnId in headerColumnIds)
            {
                var headerRow = new Row { Cells = new List<string>() };
                for (int j = 0; j < dimensionColumnsCount; j++)
                {
                    headerRow.Cells.Add("");
                }

                for (int i = 0; i < headerRowCount; i++)
                {
                    headerRow.Cells.Add(Convert.ToString(headerTable.Data.Columns[columnId]
                        .GetValueHelper(headerTable.Definition.Columns.First(c => c.Id == columnId).Type, i)));
                }
                table.Rows.Add(headerRow);
            }
            // Constructs the column data
            for (int i = 0; i < rowCount; i++)
            {
                var dataRow = new Row { Cells = new List<string>() };
                foreach (var columnId in columnIds)
                {
                    dataRow.Cells.Add(Convert.ToString(primaryTable.Data.Columns[columnId]
                        .GetValueHelper(primaryTable.Definition.Columns.First(c => c.Id == columnId).Type, i)));
                }
                table.Rows.Add(dataRow);
            }

            return table;
        }
    }

    public static class SeriesDataHelper
    {
        public static object GetValueHelper(this SeriesData seriesData, DataType dataType, int index)
        {
            switch (dataType)
            {
                case DataType.Bool:
                    {
                        return seriesData.BoolArray?.Values?[index];
                    }
                case DataType.Double:
                    {
                        return seriesData.DoubleArray?.Values?[index];
                    }
                case DataType.Duration:
                    {
                        var v = seriesData.DurationArray?.Values?[index];
                        return v?.ToTimeSpan();
                    }
                case DataType.Float:
                    {
                        return seriesData.FloatArray?.Values?[index];
                    }
                case DataType.Int32:
                    {
                        return seriesData.Int32Array?.Values?[index];
                    }
                case DataType.Int64:
                    {
                        return seriesData.Int64Array?.Values?[index];
                    }
                case DataType.String:
                    {
                        return seriesData.StringArray?.Values?[index];
                    }
                case DataType.Timestamp:
                    {
                        var v = seriesData.TimestampArray?.Values?[index];
                        return v?.ToDateTime();
                    }
                default:
                    throw new NotImplementedException($"{dataType} is not implemented");
            }
        }
    }

    public class Table
    {
        public List<Row> Rows { get; set; }

        public override string ToString()
        {
            return string.Join(Environment.NewLine, Rows);
        }
    }

    public class Row
    {
        public List<string> Cells { get; set; }

        public override string ToString()
        {
            return string.Join(",", Cells.Select(c => c.Replace(",", "")));
        }
    }
}

 

package examples;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.poi.ss.formula.eval.NotImplementedException;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import com.factset.protobuf.stach.NullValues;
import com.factset.protobuf.stach.PackageProto.Package;
import com.factset.protobuf.stach.table.DataTypeProto.DataType;
import com.factset.protobuf.stach.table.SeriesDataProto.SeriesData;
import com.factset.protobuf.stach.table.SeriesDefinitionProto.SeriesDefinition;
import com.factset.protobuf.stach.table.TableProto.Table;

import com.google.protobuf.Duration;
import com.google.protobuf.Timestamp;

public class StachExtensions {
  // The purpose of this class is to provide the helper methods for converting
  // stach to Tabular format.

  public static void generateExcel(Package packageObj) {
    for (TableData table : convertToTableFormat(packageObj)) {
      writeDataToExcel(table, UUID.randomUUID().toString() + ".xlsx");
    }
  }

  public static List<TableData> convertToTableFormat(Package packageObj) {
    List<TableData> tables = new ArrayList<TableData>();
    for (String primaryTableId : packageObj.getPrimaryTableIdsList()) {
      tables.add(generateTable(packageObj, primaryTableId));
    }
    return tables;
  }

  private static TableData generateTable(Package packageObj, String primaryTableId) {
    Map<String, Table> tablesMap = packageObj.getTablesMap();
    Table primaryTable = tablesMap.get(primaryTableId);
    String headerId = primaryTable.getDefinition().getHeaderTableId();
    Table headerTable = tablesMap.get(headerId);
    int headerRowCount = headerTable.getData().getRowsCount();
    int rowsCount = primaryTable.getData().getRowsCount();

    TableData table = new TableData();

    // Construct the column headers by considering dimension columns and header
    // rows.
    List<SeriesDefinition> headerTableSeriesDefinitions = headerTable.getDefinition().getColumnsList();
    List<SeriesDefinition> primaryTableSeriesDefinitions = primaryTable.getDefinition().getColumnsList();

    Map<String, SeriesData> headerTableColumns = headerTable.getData().getColumnsMap();
    Map<String, SeriesData> primaryTableColumns = primaryTable.getData().getColumnsMap();

    for (SeriesDefinition headerTableseriesDefinition : headerTableSeriesDefinitions) {
      Row headerRow = new Row();
      for (SeriesDefinition primaryTableSeriesDefinition : primaryTableSeriesDefinitions) {
        if (primaryTableSeriesDefinition.getIsDimension()) {
          headerRow.Cells.add(primaryTableSeriesDefinition.getDescription());
        }
      }

      String headerColumnId = headerTableseriesDefinition.getId();
      String nullFormat = headerTableseriesDefinition.getFormat().getNullFormat();
      for (int i = 0; i < headerRowCount; i++) {
        headerRow.Cells.add(SeriesDataHelper.getValueHelper(headerTableColumns.get(headerColumnId),
            headerTableseriesDefinition.getType(), i, nullFormat).toString());
      }
      table.Rows.add(headerRow);
    }

    // Construct the column data
    for (int i = 0; i < rowsCount; i++) {
      Row dataRow = new Row();
      for (SeriesDefinition primaryTableSeriesDefinition : primaryTableSeriesDefinitions) {
        String nullFormat = primaryTableSeriesDefinition.getFormat().getNullFormat();
        String primaryTableColumnId = primaryTableSeriesDefinition.getId();
        dataRow.Cells.add(SeriesDataHelper.getValueHelper(primaryTableColumns.get(primaryTableColumnId),
            primaryTableSeriesDefinition.getType(), i, nullFormat).toString());
      }
      table.Rows.add(dataRow);
    }
    return table;
  }

  private static void writeDataToExcel(TableData table, String fileLocation) {
    XSSFWorkbook workbook = new XSSFWorkbook();
    XSSFSheet sheet = workbook.createSheet("PA Report");

    int rowsSize = table.Rows.size();
    for (int rowIndex = 0; rowIndex < rowsSize; rowIndex++) {
      XSSFRow xsswRow = sheet.createRow(rowIndex);
      List<String> cells = table.Rows.get(rowIndex).Cells;
      for (int cellIndex = 0; cellIndex < cells.size(); cellIndex++) {
        XSSFCell xssfCell = xsswRow.createCell(cellIndex);
        xssfCell.setCellValue(cells.get(cellIndex));
      }
    }

    try {
      FileOutputStream out = new FileOutputStream(new File(fileLocation));
      workbook.write(out);
      out.close();
      workbook.close();
    } catch (Exception e) {
      System.err.println("Failed to write data to excel");
      e.printStackTrace();
    }
  }
}

class SeriesDataHelper {
  public static Object getValueHelper(SeriesData seriesData, DataType dataType, int index, String nullFormat) {
    if (dataType == DataType.STRING) {
      String value = seriesData.getStringArray().getValues(index);
      return NullValues.STRING.equals(value) ? nullFormat : value;
    } else if (dataType == DataType.DOUBLE) {
      double value = seriesData.getDoubleArray().getValues(index);
      return Double.isNaN(value) ? nullFormat : value;
    } else if (dataType == DataType.BOOL) {
      return seriesData.getBoolArray().getValues(index);
    } else if (dataType == DataType.DURATION) {
      Duration value = seriesData.getDurationArray().getValues(index);
      return NullValues.DURATION.equals(value) ? nullFormat : value;
    } else if (dataType == DataType.FLOAT) {
      float value = seriesData.getFloatArray().getValues(index);
      return Float.isNaN(value) ? nullFormat : value;
    } else if (dataType == DataType.INT32) {
      int value = seriesData.getInt32Array().getValues(index);
      return NullValues.INT32 == value ? nullFormat : value;
    } else if (dataType == DataType.INT64) {
      long value = seriesData.getInt64Array().getValues(index);
      return NullValues.INT64 == value ? nullFormat : value;
    } else if (dataType == DataType.TIMESTAMP) {
      Timestamp value = seriesData.getTimestampArray().getValues(index);
      return NullValues.TIMESTAMP.equals(value) ? nullFormat : value;
    } else {
      throw new NotImplementedException(dataType + " is not implemented");
    }
  }
}

class TableData {
  List<Row> Rows = new ArrayList<Row>();

  public String toString() {
    return Rows.toString();
  }
}

class Row {
  List<String> Cells = new ArrayList<String>();

  public String toString() {
    return Cells.toString();
  }
}

 

import uuid
import math

import pandas as pd

from fds.protobuf.stach.Package_pb2 import Package
from fds.protobuf.stach.NullValues import NullValues
from fds.protobuf.stach.table.DataType_pb2 import DataType

class StachExtensions:
    """The purpose of this class is to provide the helper methods for converting Stach to Tabular format"""

    @staticmethod
    def generate_excel(package):
        for table in StachExtensions.convert_to_table_format(package):
            writer = pd.ExcelWriter(str(uuid.uuid1()) + ".xlsx")
            table.to_excel(excel_writer=writer)
            writer.save()
            writer.close()

    @staticmethod
    def convert_to_table_format(package):
        tables = list()
        for primary_table_id in package.primary_table_ids:
            tables.append(StachExtensions.generate_table(package, primary_table_id))
        return tables

    @staticmethod
    def generate_table(package_response, primary_table_id):

        if isinstance(package_response, Package):
            primary_table = package_response.tables[primary_table_id]
            header_id = primary_table.definition.header_table_id
            header_table = package_response.tables[header_id]
            dimension_columns = list(filter(lambda column_obj: column_obj.is_dimension, primary_table.definition.columns))
            dimension_columns_count = len(dimension_columns)
            row_count = len(primary_table.data.rows)
            header_row_count = len(header_table.data.rows)

            headers = list(list())
            # Constructs the column headers by considering dimension columns and header rows
            for series_definition_column in header_table.definition.columns:
                header_row = list()
                for i in range(0, dimension_columns_count, 1):
                    header_row.append(dimension_columns[i].description)

                for i in range(0, header_row_count, 1):
                    header_row.append(str(SeriesDataHelper.get_value_helper(header_table.data.columns[series_definition_column.id], series_definition_column.type, i, series_definition_column.format.null_format)))
                headers.append(header_row)

            data = list(list())
            # Constructs the column data
            for i in range(0, row_count, 1):
                data_row = list()
                for series_definition_column in primary_table.definition.columns:
                    data_row.append(str(SeriesDataHelper.get_value_helper(primary_table.data.columns[series_definition_column.id], series_definition_column.type, i, series_definition_column.format.null_format)))
                data.append(data_row)

            data_frame = pd.DataFrame(data=data)
            data_frame.columns = pd.MultiIndex.from_arrays(headers)
            return data_frame

        else:
            ValueError("Response data passed should be of package type.")


class SeriesDataHelper:

    @staticmethod
    def get_value_helper(series_data, datatype, index, null_format):
        if DataType.Name(datatype) == "STRING":
            return SeriesDataHelper.null_value_handler(datatype, series_data.string_array.values[index], null_format)
        elif DataType.Name(datatype) == "DOUBLE":
            return SeriesDataHelper.null_value_handler(datatype, series_data.double_array.values[index], null_format)
        elif DataType.Name(datatype) == "FLOAT":
            return SeriesDataHelper.null_value_handler(datatype, series_data.float_array.values[index], null_format)
        elif DataType.Name(datatype) == "INT32":
            return SeriesDataHelper.null_value_handler(datatype, series_data.int32_array.values[index], null_format)
        elif DataType.Name(datatype) == "INT64":
            return SeriesDataHelper.null_value_handler(datatype, series_data.int64_array.values[index], null_format)
        elif DataType.Name(datatype) == "BOOL":
            return SeriesDataHelper.null_value_handler(datatype, series_data.bool_array.values[index], null_format)
        elif DataType.Name(datatype) == "DURATION":
            return SeriesDataHelper.null_value_handler(datatype, series_data.duration_array.values[index], null_format)
        elif DataType.Name(datatype) == "TIMESTAMP":
            return SeriesDataHelper.null_value_handler(datatype, series_data.timestamp_array.values[index], null_format)
        else:
            ValueError("The datatype is not implemented")

    @staticmethod
    def null_value_handler(datatype, value, null_format):
        if DataType.Name(datatype) == "STRING":
            if NullValues.STRING == value:
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "DOUBLE":
            if math.isnan(value):
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "FLOAT":
            if math.isnan(value):
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "INT32":
            if NullValues.INT32 == value:
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "INT64":
            if NullValues.INT64 == value:
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "DURATION":
            if NullValues.DURATION.equals(value):
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "TIMESTAMP":
            if NullValues.TIMESTAMP.equals(value):
                return null_format
            else:
                return value
        else:
            return value

 

Building and running a calculation
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;

using FactSet.AnalyticsAPI.Engines.v2.Api;
using FactSet.AnalyticsAPI.Engines.v2.Client;
using FactSet.AnalyticsAPI.Engines.v2.Model;

using FactSet.Protobuf.Stach;
using Google.Protobuf;

namespace FactSet.AnalyticsAPI.Engines.v2.Example.Examples
{
    public class PAEngineExample
    {
        private static Configuration _engineApiConfiguration;
        private const string BASE_PATH = "https://api.factset.com";
        private const string USER_NAME = "<username-serial>";
        private const string PASSWORD = "<apiKey>";
        private const string PA_DEFAULT_DOCUMENT = "PA_DOCUMENTS:DEFAULT";
        private const string PA_BENCHMARK_SP_50 = "BENCH:SP50";
        private const string PA_BENCHMARK_R_1000 = "BENCH:R.1000";

        private static Configuration GetEngineApiConfiguration()
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            if (_engineApiConfiguration != null)
            {
                return _engineApiConfiguration;
            }

            _engineApiConfiguration = new Configuration
            {
                BasePath = BASE_PATH,
                Username = USER_NAME,
                Password = PASSWORD
            };

            return _engineApiConfiguration;
        }

        public static void Main(string[] args)
        {
            try
            {
                var calculationParameters = new Calculation
                {
                    Pa = new Dictionary<string, PACalculationParameters> { { "1", GetPaCalculationParameters() } }
                };

                var calculationApi = new CalculationsApi(GetEngineApiConfiguration());

                var runCalculationResponse = calculationApi.RunCalculationWithHttpInfo(calculationParameters);

                var calculationId = runCalculationResponse.Headers["Location"][0].Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Last();
                ApiResponse<CalculationStatus> getStatus = null;

                while (getStatus == null || getStatus.Data.Status == CalculationStatus.StatusEnum.Queued || getStatus.Data.Status == CalculationStatus.StatusEnum.Executing)
                {
                    if (getStatus != null)
                    {
                        if (getStatus.Headers.ContainsKey("Cache-Control"))
                        {
                            var maxAge = getStatus.Headers["Cache-Control"][0];
                            if (string.IsNullOrWhiteSpace(maxAge))
                            {
                                Console.WriteLine("Sleeping for 2 seconds");
                                // Sleep for at least 2 seconds.
                                Thread.Sleep(2000);
                            }
                            else
                            {
                                var age = int.Parse(maxAge.Replace("max-age=", ""));
                                Console.WriteLine($"Sleeping for {age} seconds");
                                Thread.Sleep(age * 1000);
                            }
                        }
                    }

                    getStatus = calculationApi.GetCalculationStatusByIdWithHttpInfo(calculationId);
                }
                Console.WriteLine("Calculation Completed");

                // Check for failed calculations
                foreach (var paCalculationParameters in getStatus.Data.Pa)
                {
                    if (paCalculationParameters.Value.Status == CalculationUnitStatus.StatusEnum.Failed)
                    {
                        Console.WriteLine($"CalculationId : {paCalculationParameters.Key} Failed!!!");
                        Console.WriteLine($"Error message : {paCalculationParameters.Value.Error}");
                    }
                }

                // Get result of successful calculations
                foreach (var paCalculationParameters in getStatus.Data.Pa)
                {
                    PrintResult(paCalculationParameters);
                }

                Console.ReadKey();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }

        private static PACalculationParameters GetPaCalculationParameters()
        {
            // Build PA Engine calculation parameters
            var componentsApi = new ComponentsApi(GetEngineApiConfiguration());

            var componentsResponse = componentsApi.GetPAComponentsWithHttpInfo(PA_DEFAULT_DOCUMENT);

            if (componentsResponse.StatusCode != HttpStatusCode.OK)
            {
                LogError(componentsResponse);
                return null;
            }

            var paComponentId = componentsResponse.Data.First().Key;
            Console.WriteLine($"PA Component Id : {paComponentId}");

            var paAccountIdentifier = new PAIdentifier(PA_BENCHMARK_SP_50);
            var paAccounts = new List<PAIdentifier> { paAccountIdentifier };
            var paBenchmarkIdentifier = new PAIdentifier(PA_BENCHMARK_R_1000);
            var paBenchmarks = new List<PAIdentifier> { paBenchmarkIdentifier };

            var paCalculation = new PACalculationParameters(paComponentId, paAccounts, paBenchmarks);

            return paCalculation;
        }

        private static void PrintResult(KeyValuePair<string, CalculationUnitStatus> calculation)
        {
            if (calculation.Value.Status == CalculationUnitStatus.StatusEnum.Success)
            {
                ApiResponse<string> resultResponse = null;

                var utilityApi = new UtilityApi(GetEngineApiConfiguration());
                resultResponse = utilityApi.GetByUrlWithHttpInfo(calculation.Value.Result);

                if (resultResponse.StatusCode != HttpStatusCode.OK)
                {
                    LogError(resultResponse);
                    return;
                }

                Console.WriteLine($"CalculationId : {calculation.Key} Succeeded!!!");
                Console.WriteLine($"CalculationId : {calculation.Key} Result");
                Console.WriteLine("/****************************************************************/");

                // converting the data to Package object
                var jpSettings = JsonParser.Settings.Default;
                var jp = new JsonParser(jpSettings.WithIgnoreUnknownFields(true));
                var package = jp.Parse<Package>(resultResponse.Data);

                // To convert package to 2D tables.
                var tables = package.ConvertToTableFormat();
                Console.WriteLine(tables[0]);

                // Uncomment the following line to generate an Excel file
                // package.GenerateCSV();
                Console.WriteLine("/****************************************************************/");
            }
        }

        private static void LogError<T>(ApiResponse<T> response)
        {
            Console.WriteLine("Error!!!");
            Console.WriteLine($"Status Code: {response.StatusCode}");
            Console.WriteLine($"Request Key: {response.Headers["X-DataDirect-Request-Key"]}");
            Console.WriteLine($"Reason: {response.Data}");
        }
    }
}

 

package examples;

import java.util.List;
import java.util.Map;

import factset.analyticsapi.engines.v2.*;
import factset.analyticsapi.engines.v2.api.*;
import factset.analyticsapi.engines.v2.models.*;
import factset.analyticsapi.engines.v2.models.CalculationStatus.StatusEnum;

import com.google.protobuf.util.JsonFormat;
import com.google.protobuf.InvalidProtocolBufferException;
import com.factset.protobuf.stach.PackageProto.Package.Builder;
import com.factset.protobuf.stach.PackageProto.Package;

public class PAEngineExample {

  private static ApiClient apiClient = null;
  private static final String BASE_PATH = "https://api.factset.com";
  private static final String USERNAME = "<username-serial>";
  private static final String PASSWORD = "<apiKey>";
  public static final String PA_DEFAULT_DOCUMENT = "PA_DOCUMENTS:DEFAULT";
  private static final String COMPONENT_NAME = "Weights";
  private static final String COMPONENT_CATEGORY = "Weights / Exposures";

  public static void main(String[] args) throws InterruptedException {
    try {
      // Build PA Calculation Parameters List

      // Get all component from PA_DEFAULT_DOCUMENT with Name COMPONENT_NAME & category COMPONENT_CATEGORY
      ComponentsApi componentsApi = new ComponentsApi(getApiClient());
      Map<String, ComponentSummary> components = componentsApi.getPAComponents(PA_DEFAULT_DOCUMENT);
      String componentId = components.entrySet().stream()
          .filter(c -> c.getValue().getName().equals(COMPONENT_NAME) && c.getValue().getCategory().equals(COMPONENT_CATEGORY))
          .iterator().next().getKey();
      System.out.println(
          "ID of component with Name '" + COMPONENT_NAME + "' and category '" + COMPONENT_CATEGORY + "' : " + componentId);

      Calculation parameters = new Calculation();

      PACalculationParameters paItem = new PACalculationParameters();

      paItem.setComponentid(componentId);

      PAIdentifier accountPaIdentifier1 = new PAIdentifier();
      accountPaIdentifier1.setId("BENCH:SP50");
      paItem.addAccountsItem(accountPaIdentifier1);

      PAIdentifier accountPaIdentifier2 = new PAIdentifier();
      accountPaIdentifier2.setId("BENCH:R.2000");
      paItem.addAccountsItem(accountPaIdentifier2);

      PAIdentifier benchmarkPaIdentifier = new PAIdentifier();
      benchmarkPaIdentifier.setId("BENCH:R.2000");
      paItem.addBenchmarksItem(benchmarkPaIdentifier);

      PADateParameters dateParameters = new PADateParameters();
      dateParameters.setStartdate("20180101");
      dateParameters.setEnddate("20181231");
      dateParameters.setFrequency("Monthly");
      paItem.setDates(dateParameters);

      parameters.putPaItem("1", paItem);

      // Run Calculation Request
      CalculationsApi apiInstance = new CalculationsApi(getApiClient());
      ApiResponse<Void> createResponse = null;

      createResponse = apiInstance.runCalculationWithHttpInfo(parameters);

      String[] locationList = createResponse.getHeaders().get("Location").get(0).split("/");
      String requestId = locationList[locationList.length - 1];

      // Get Calculation Request Status
      ApiResponse<CalculationStatus> getStatus = null;

      while (getStatus == null || getStatus.getData().getStatus() == StatusEnum.QUEUED
          || getStatus.getData().getStatus() == StatusEnum.EXECUTING) {
        if (getStatus != null) {
          List<String> cacheControl = getStatus.getHeaders().get("Cache-Control");
          if (cacheControl != null) {
            int maxAge = Integer.parseInt(cacheControl.get(0).replaceAll("max-age=", ""));
            System.out.println("Sleeping for: " + maxAge + " seconds");
            Thread.sleep(maxAge * 1000);
          } else {
            System.out.println("Sleeping for: 2 seconds");
            Thread.sleep(2 * 1000);
          }
        }
        getStatus = apiInstance.getCalculationStatusByIdWithHttpInfo(requestId);
      }

      System.out.println("Calculation Completed!!!");

      // Check for Failed Calculations
      for (Map.Entry<String, CalculationUnitStatus> calculationParameters : getStatus.getData().getPa().entrySet()) {
        if (calculationParameters.getValue().getStatus() == CalculationUnitStatus.StatusEnum.FAILED) {
          System.out.println("CalculationId : " + calculationParameters.getKey() + " Failed!!!");
          System.out.println("Error message : " + calculationParameters.getValue().getError());
          return;
        }
      }

      // Get Result of Successful Caculations
      for (Map.Entry<String, CalculationUnitStatus> calculationParameters : getStatus.getData().getPa().entrySet()) {
        if (calculationParameters.getValue().getStatus() == CalculationUnitStatus.StatusEnum.SUCCESS) {
          UtilityApi utilityApiInstance = new UtilityApi(apiClient);
          ApiResponse<String> resultResponse = utilityApiInstance
              .getByUrlWithHttpInfo(calculationParameters.getValue().getResult());
          System.out.println("CalculationId : " + calculationParameters.getKey() + " Succeeded!!!");
          System.out.println("CalculationId : " + calculationParameters.getKey() + " Result");

          Builder builder = Package.newBuilder();
          try {
            JsonFormat.parser().ignoringUnknownFields().merge(resultResponse.getData(), builder);
          } catch (InvalidProtocolBufferException e) {
            System.out.println("Error while deserializing the response");
            e.printStackTrace();
          }

          Package result = (Package) builder.build();
          // To convert result to 2D tables.
          List<TableData> tables = StachExtensions.convertToTableFormat(result);
          System.out.println(tables.get(0)); // Prints the result in 2D table format.
          // Uncomment the following line to generate an Excel file
          // StachExtensions.generateExcel(result);
        }
      }
    } catch (ApiException e) {
      handleException("PAEngineExample#Main", e);
      return;
    }
  }

  private static ApiClient getApiClient() throws ApiException {
    if (apiClient != null) {
      return apiClient;
    }

    apiClient = new ApiClient();
    apiClient.setConnectTimeout(30000);
    apiClient.setReadTimeout(30000);
    apiClient.setBasePath(BASE_PATH);
    apiClient.setUsername(USERNAME);
    apiClient.setPassword(PASSWORD);

    return apiClient;
  }

  private static void handleException(String method, ApiException e) {
    System.err.println("Exception when calling " + method);
    if (e.getResponseHeaders() != null && e.getResponseHeaders().containsKey("x-datadirect-request-key")) {
      System.out.println("x-datadirect-request-key: " + e.getResponseHeaders().get("x-datadirect-request-key").get(0));
    }
    System.out.println("Status code: " + e.getCode());
    System.out.println("Reason: " + e.getResponseBody());
    e.printStackTrace();
  }
}

 

import time
import json

from google.protobuf import json_format
from google.protobuf.json_format import MessageToJson
from google.protobuf.json_format import MessageToDict
from fds.protobuf.stach.Package_pb2 import Package

from fds.analyticsapi.engines.v2.configuration import Configuration;
from fds.analyticsapi.engines.v2.api_client import ApiClient;
from fds.analyticsapi.engines.v2.api.components_api import ComponentsApi
from fds.analyticsapi.engines.v2.api.configurations_api import ConfigurationsApi
from fds.analyticsapi.engines.v2.api.calculations_api import CalculationsApi
from fds.analyticsapi.engines.v2.api.utility_api import UtilityApi
from fds.analyticsapi.engines.v2.models.calculation import Calculation
from fds.analyticsapi.engines.v2.models.pa_calculation_parameters import PACalculationParameters
from fds.analyticsapi.engines.v2.models.pa_identifier import PAIdentifier
from fds.analyticsapi.engines.v2.models.pa_date_parameters import PADateParameters

# Copy 'Converting API output to Table Format' snippet to a file with name 'stach_extension.py' to use below import statement
from stach_extensions import StachExtensions

host = "https://api.factset.com"
username = "<username-serial>"
password = "<apiKey>"

pa_document_name = "PA_DOCUMENTS:DEFAULT";
pa_benchmark_sp_50 = "BENCH:SP50";
pa_benchmark_r_1000 = "BENCH:R.1000";
startdate = "20180101"
enddate = "20181231"
frequency = "Monthly"

config = Configuration()
config.host = host
config.username = username
config.password = password
# add proxy and/or disable ssl verification according to your development environment
# config.proxy = "<proxyUrl>"
config.verify_ssl = False

api_client = ApiClient(config)

components_api = ComponentsApi(api_client);

components = components_api.get_pa_components(pa_document_name)
component_id = list(components.keys())[0]

pa_account_identifier = PAIdentifier(pa_benchmark_sp_50)
pa_accounts = [pa_account_identifier]
pa_benchmark_identifier = PAIdentifier(pa_benchmark_r_1000);
pa_benchmarks = [pa_benchmark_identifier]
pa_dates = PADateParameters(startdate, enddate, frequency)

pa_calculation_parameters = {"1": PACalculationParameters(component_id, pa_accounts, pa_benchmarks, pa_dates)}

calculation = Calculation(pa=pa_calculation_parameters)
print(calculation)

calculations_api = CalculationsApi(api_client)
run_calculation_response = calculations_api.run_calculation_with_http_info(calculation=calculation)

if(run_calculation_response[1] != 202):
    print("Calculation Failed!!!");
    print("Status Code: " + run_calculation_response[1])
    print("Request Key: " + run_calculation_response[2].get("x-datadirect-request-key"))
    print(run_calculation_response[0])
    quit()

calculation_id = run_calculation_response[2].get("location").split("/")[-1]
print("Calculation Id: " + calculation_id)

status_response = calculations_api.get_calculation_status_by_id_with_http_info(calculation_id)
while (status_response[1] == 200 and (status_response[0].status == "Queued" or status_response[0].status == "Executing")):
    max_age = '5'
    age_value = status_response[2].get("cache-control")
    if(age_value != None):
        max_age = age_value.replace("max-age=", "")
    print('Sleeping: ' + max_age)
    time.sleep(int(max_age))
    status_response = calculations_api.get_calculation_status_by_id_with_http_info(calculation_id)

if(status_response[1] != 200):
    print("Calculation Failed!!!");
    print("Status Code: " + status_response[1])
    print("Request Key: " + status_response[2].get("x-datadirect-request-key"))
    print(status_response[0])
    quit()

for calculation_unit in status_response[0].pa.values():
    print(calculation_unit)
    if calculation_unit.status == "Failed":
        print("Calculation Failed!!!");
    elif calculation_unit.status == "Success":
        utility_api = UtilityApi(api_client)
        result_response = utility_api.get_by_url_with_http_info(calculation_unit.result)

        if(result_response[1] != 200):
            print("Calculation Failed!!!");
            print("Status Code: " + result_response[1])
            print("Request Key: " + result_response[2].get("x-datadirect-request-key"))
            print(result_response[0])
            quit()

        # converting the data to Package object
        result =  json_format.Parse(json.dumps(result_response[0]), Package())
        # print(MessageToJson(result)) # To print the result object as a JSON
        # print(MessageToDict(result)) # To print the result object as a Dictionary
        tables = StachExtensions.convert_to_table_format(result) # To convert result to 2D tables.
        print(tables[0]) # Prints the result in 2D table format.
        # StachExtensions.generate_excel(result) # To get the result in table format exported to excel file.

 

Converting API output to Table format
using System;
using System.Collections.Generic;
using System.Linq;
using FactSet.Protobuf.Stach;
using FactSet.Protobuf.Stach.Table;

namespace FactSet.AnalyticsAPI.Engines.v2.Example.Examples
{
    public static class StachExtensions
    {
        public static void GenerateCSV(this Package package)
        {
            foreach (var table in package.ConvertToTableFormat())
            {
                System.IO.File.WriteAllText($"{Guid.NewGuid():N}.csv", table.ToString());
            }
        }

        public static List<Table> ConvertToTableFormat(this Package package)
        {
            var tables = new List<Table>();
            foreach (var primaryTableId in package.PrimaryTableIds)
            {
                tables.Add(GenerateTable(package, primaryTableId));
            }

            return tables;
        }

        private static Table GenerateTable(Package package, string primaryTableId)
        {
            var primaryTable = package.Tables[primaryTableId];
            var headerId = primaryTable.Definition.HeaderTableId;
            var headerTable = package.Tables[headerId];
            var columnIds = primaryTable.Definition.Columns.Select(c => c.Id).ToList();
            var headerColumnIds = headerTable.Definition.Columns.Select(c => c.Id).ToList();
            var dimensionColumnsCount = primaryTable.Definition.Columns.Count(c => c.IsDimension);
            var rowCount = primaryTable.Data.Rows.Count;
            var headerRowCount = headerTable.Data.Rows.Count;

            var table = new Table
            {
                Rows = new List<Row>()
            };
            // Constructs the column headers by considering dimension columns and header rows
            foreach (var columnId in headerColumnIds)
            {
                var headerRow = new Row { Cells = new List<string>() };
                for (int j = 0; j < dimensionColumnsCount; j++)
                {
                    headerRow.Cells.Add("");
                }

                for (int i = 0; i < headerRowCount; i++)
                {
                    headerRow.Cells.Add(Convert.ToString(headerTable.Data.Columns[columnId]
                        .GetValueHelper(headerTable.Definition.Columns.First(c => c.Id == columnId).Type, i)));
                }
                table.Rows.Add(headerRow);
            }
            // Constructs the column data
            for (int i = 0; i < rowCount; i++)
            {
                var dataRow = new Row { Cells = new List<string>() };
                foreach (var columnId in columnIds)
                {
                    dataRow.Cells.Add(Convert.ToString(primaryTable.Data.Columns[columnId]
                        .GetValueHelper(primaryTable.Definition.Columns.First(c => c.Id == columnId).Type, i)));
                }
                table.Rows.Add(dataRow);
            }

            return table;
        }
    }

    public static class SeriesDataHelper
    {
        public static object GetValueHelper(this SeriesData seriesData, DataType dataType, int index)
        {
            switch (dataType)
            {
                case DataType.Bool:
                    {
                        return seriesData.BoolArray?.Values?[index];
                    }
                case DataType.Double:
                    {
                        return seriesData.DoubleArray?.Values?[index];
                    }
                case DataType.Duration:
                    {
                        var v = seriesData.DurationArray?.Values?[index];
                        return v?.ToTimeSpan();
                    }
                case DataType.Float:
                    {
                        return seriesData.FloatArray?.Values?[index];
                    }
                case DataType.Int32:
                    {
                        return seriesData.Int32Array?.Values?[index];
                    }
                case DataType.Int64:
                    {
                        return seriesData.Int64Array?.Values?[index];
                    }
                case DataType.String:
                    {
                        return seriesData.StringArray?.Values?[index];
                    }
                case DataType.Timestamp:
                    {
                        var v = seriesData.TimestampArray?.Values?[index];
                        return v?.ToDateTime();
                    }
                default:
                    throw new NotImplementedException($"{dataType} is not implemented");
            }
        }
    }

    public class Table
    {
        public List<Row> Rows { get; set; }

        public override string ToString()
        {
            return string.Join(Environment.NewLine, Rows);
        }
    }

    public class Row
    {
        public List<string> Cells { get; set; }

        public override string ToString()
        {
            return string.Join(",", Cells.Select(c => c.Replace(",", "")));
        }
    }
}

 

package examples;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.poi.ss.formula.eval.NotImplementedException;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import com.factset.protobuf.stach.NullValues;
import com.factset.protobuf.stach.PackageProto.Package;
import com.factset.protobuf.stach.table.DataTypeProto.DataType;
import com.factset.protobuf.stach.table.SeriesDataProto.SeriesData;
import com.factset.protobuf.stach.table.SeriesDefinitionProto.SeriesDefinition;
import com.factset.protobuf.stach.table.TableProto.Table;

import com.google.protobuf.Duration;
import com.google.protobuf.Timestamp;

public class StachExtensions {
  // The purpose of this class is to provide the helper methods for converting
  // stach to Tabular format.

  public static void generateExcel(Package packageObj) {
    for (TableData table : convertToTableFormat(packageObj)) {
      writeDataToExcel(table, UUID.randomUUID().toString() + ".xlsx");
    }
  }

  public static List<TableData> convertToTableFormat(Package packageObj) {
    List<TableData> tables = new ArrayList<TableData>();
    for (String primaryTableId : packageObj.getPrimaryTableIdsList()) {
      tables.add(generateTable(packageObj, primaryTableId));
    }
    return tables;
  }

  private static TableData generateTable(Package packageObj, String primaryTableId) {
    Map<String, Table> tablesMap = packageObj.getTablesMap();
    Table primaryTable = tablesMap.get(primaryTableId);
    String headerId = primaryTable.getDefinition().getHeaderTableId();
    Table headerTable = tablesMap.get(headerId);
    int headerRowCount = headerTable.getData().getRowsCount();
    int rowsCount = primaryTable.getData().getRowsCount();

    TableData table = new TableData();

    // Construct the column headers by considering dimension columns and header
    // rows.
    List<SeriesDefinition> headerTableSeriesDefinitions = headerTable.getDefinition().getColumnsList();
    List<SeriesDefinition> primaryTableSeriesDefinitions = primaryTable.getDefinition().getColumnsList();

    Map<String, SeriesData> headerTableColumns = headerTable.getData().getColumnsMap();
    Map<String, SeriesData> primaryTableColumns = primaryTable.getData().getColumnsMap();

    for (SeriesDefinition headerTableseriesDefinition : headerTableSeriesDefinitions) {
      Row headerRow = new Row();
      for (SeriesDefinition primaryTableSeriesDefinition : primaryTableSeriesDefinitions) {
        if (primaryTableSeriesDefinition.getIsDimension()) {
          headerRow.Cells.add(primaryTableSeriesDefinition.getDescription());
        }
      }

      String headerColumnId = headerTableseriesDefinition.getId();
      String nullFormat = headerTableseriesDefinition.getFormat().getNullFormat();
      for (int i = 0; i < headerRowCount; i++) {
        headerRow.Cells.add(SeriesDataHelper.getValueHelper(headerTableColumns.get(headerColumnId),
            headerTableseriesDefinition.getType(), i, nullFormat).toString());
      }
      table.Rows.add(headerRow);
    }

    // Construct the column data
    for (int i = 0; i < rowsCount; i++) {
      Row dataRow = new Row();
      for (SeriesDefinition primaryTableSeriesDefinition : primaryTableSeriesDefinitions) {
        String nullFormat = primaryTableSeriesDefinition.getFormat().getNullFormat();
        String primaryTableColumnId = primaryTableSeriesDefinition.getId();
        dataRow.Cells.add(SeriesDataHelper.getValueHelper(primaryTableColumns.get(primaryTableColumnId),
            primaryTableSeriesDefinition.getType(), i, nullFormat).toString());
      }
      table.Rows.add(dataRow);
    }
    return table;
  }

  private static void writeDataToExcel(TableData table, String fileLocation) {
    XSSFWorkbook workbook = new XSSFWorkbook();
    XSSFSheet sheet = workbook.createSheet("PA Report");

    int rowsSize = table.Rows.size();
    for (int rowIndex = 0; rowIndex < rowsSize; rowIndex++) {
      XSSFRow xsswRow = sheet.createRow(rowIndex);
      List<String> cells = table.Rows.get(rowIndex).Cells;
      for (int cellIndex = 0; cellIndex < cells.size(); cellIndex++) {
        XSSFCell xssfCell = xsswRow.createCell(cellIndex);
        xssfCell.setCellValue(cells.get(cellIndex));
      }
    }

    try {
      FileOutputStream out = new FileOutputStream(new File(fileLocation));
      workbook.write(out);
      out.close();
      workbook.close();
    } catch (Exception e) {
      System.err.println("Failed to write data to excel");
      e.printStackTrace();
    }
  }
}

class SeriesDataHelper {
  public static Object getValueHelper(SeriesData seriesData, DataType dataType, int index, String nullFormat) {
    if (dataType == DataType.STRING) {
      String value = seriesData.getStringArray().getValues(index);
      return NullValues.STRING.equals(value) ? nullFormat : value;
    } else if (dataType == DataType.DOUBLE) {
      double value = seriesData.getDoubleArray().getValues(index);
      return Double.isNaN(value) ? nullFormat : value;
    } else if (dataType == DataType.BOOL) {
      return seriesData.getBoolArray().getValues(index);
    } else if (dataType == DataType.DURATION) {
      Duration value = seriesData.getDurationArray().getValues(index);
      return NullValues.DURATION.equals(value) ? nullFormat : value;
    } else if (dataType == DataType.FLOAT) {
      float value = seriesData.getFloatArray().getValues(index);
      return Float.isNaN(value) ? nullFormat : value;
    } else if (dataType == DataType.INT32) {
      int value = seriesData.getInt32Array().getValues(index);
      return NullValues.INT32 == value ? nullFormat : value;
    } else if (dataType == DataType.INT64) {
      long value = seriesData.getInt64Array().getValues(index);
      return NullValues.INT64 == value ? nullFormat : value;
    } else if (dataType == DataType.TIMESTAMP) {
      Timestamp value = seriesData.getTimestampArray().getValues(index);
      return NullValues.TIMESTAMP.equals(value) ? nullFormat : value;
    } else {
      throw new NotImplementedException(dataType + " is not implemented");
    }
  }
}

class TableData {
  List<Row> Rows = new ArrayList<Row>();

  public String toString() {
    return Rows.toString();
  }
}

class Row {
  List<String> Cells = new ArrayList<String>();

  public String toString() {
    return Cells.toString();
  }
}

 

import uuid
import math

import pandas as pd

from fds.protobuf.stach.Package_pb2 import Package
from fds.protobuf.stach.NullValues import NullValues
from fds.protobuf.stach.table.DataType_pb2 import DataType

class StachExtensions:
    """The purpose of this class is to provide the helper methods for converting Stach to Tabular format"""

    @staticmethod
    def generate_excel(package):
        for table in StachExtensions.convert_to_table_format(package):
            writer = pd.ExcelWriter(str(uuid.uuid1()) + ".xlsx")
            table.to_excel(excel_writer=writer)
            writer.save()
            writer.close()

    @staticmethod
    def convert_to_table_format(package):
        tables = list()
        for primary_table_id in package.primary_table_ids:
            tables.append(StachExtensions.generate_table(package, primary_table_id))
        return tables

    @staticmethod
    def generate_table(package_response, primary_table_id):

        if isinstance(package_response, Package):
            primary_table = package_response.tables[primary_table_id]
            header_id = primary_table.definition.header_table_id
            header_table = package_response.tables[header_id]
            dimension_columns = list(filter(lambda column_obj: column_obj.is_dimension, primary_table.definition.columns))
            dimension_columns_count = len(dimension_columns)
            row_count = len(primary_table.data.rows)
            header_row_count = len(header_table.data.rows)

            headers = list(list())
            # Constructs the column headers by considering dimension columns and header rows
            for series_definition_column in header_table.definition.columns:
                header_row = list()
                for i in range(0, dimension_columns_count, 1):
                    header_row.append(dimension_columns[i].description)

                for i in range(0, header_row_count, 1):
                    header_row.append(str(SeriesDataHelper.get_value_helper(header_table.data.columns[series_definition_column.id], series_definition_column.type, i, series_definition_column.format.null_format)))
                headers.append(header_row)

            data = list(list())
            # Constructs the column data
            for i in range(0, row_count, 1):
                data_row = list()
                for series_definition_column in primary_table.definition.columns:
                    data_row.append(str(SeriesDataHelper.get_value_helper(primary_table.data.columns[series_definition_column.id], series_definition_column.type, i, series_definition_column.format.null_format)))
                data.append(data_row)

            data_frame = pd.DataFrame(data=data)
            data_frame.columns = pd.MultiIndex.from_arrays(headers)
            return data_frame

        else:
            ValueError("Response data passed should be of package type.")


class SeriesDataHelper:

    @staticmethod
    def get_value_helper(series_data, datatype, index, null_format):
        if DataType.Name(datatype) == "STRING":
            return SeriesDataHelper.null_value_handler(datatype, series_data.string_array.values[index], null_format)
        elif DataType.Name(datatype) == "DOUBLE":
            return SeriesDataHelper.null_value_handler(datatype, series_data.double_array.values[index], null_format)
        elif DataType.Name(datatype) == "FLOAT":
            return SeriesDataHelper.null_value_handler(datatype, series_data.float_array.values[index], null_format)
        elif DataType.Name(datatype) == "INT32":
            return SeriesDataHelper.null_value_handler(datatype, series_data.int32_array.values[index], null_format)
        elif DataType.Name(datatype) == "INT64":
            return SeriesDataHelper.null_value_handler(datatype, series_data.int64_array.values[index], null_format)
        elif DataType.Name(datatype) == "BOOL":
            return SeriesDataHelper.null_value_handler(datatype, series_data.bool_array.values[index], null_format)
        elif DataType.Name(datatype) == "DURATION":
            return SeriesDataHelper.null_value_handler(datatype, series_data.duration_array.values[index], null_format)
        elif DataType.Name(datatype) == "TIMESTAMP":
            return SeriesDataHelper.null_value_handler(datatype, series_data.timestamp_array.values[index], null_format)
        else:
            ValueError("The datatype is not implemented")

    @staticmethod
    def null_value_handler(datatype, value, null_format):
        if DataType.Name(datatype) == "STRING":
            if NullValues.STRING == value:
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "DOUBLE":
            if math.isnan(value):
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "FLOAT":
            if math.isnan(value):
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "INT32":
            if NullValues.INT32 == value:
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "INT64":
            if NullValues.INT64 == value:
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "DURATION":
            if NullValues.DURATION.equals(value):
                return null_format
            else:
                return value
        elif DataType.Name(datatype) == "TIMESTAMP":
            if NullValues.TIMESTAMP.equals(value):
                return null_format
            else:
                return value
        else:
            return value

 

Change Logs

v2
Summary
  • v2.1.0 - Released on 09/26/19
  • v2.0.0 - Released on 08/15/19 (Combined PA Engine API v1 and PA Batch API v1 into PA Engine API v2)
Functionality Additions
  • Added option to specify holdings mode for accounts when using ‘Run Multiple Calculations’ endpoint [v2.1.0]
  • Added ‘holdingsmode’ and ‘snapshot’ (boolean) properties in ‘Component Settings Lookup’ endpoint [v2.1.0]
  • Added calculation metadata to the result pickup endpoint. The properties include settings used in ‘Run Multiple Calculations’ endpoint and account/benchmark defaults [v2.1.0]
  • Initial release [v2.0.0]
Changes
  • Renamed ‘pointscount’ property to ‘points’ in ‘Get Calculation Status By ID’ and ‘Get All Active Calculations’ endpoints. Points count related response headers were also updated accordingly in ‘Run Multiple Calculations’ endpoint [v2.1.0]
  • Renamed ‘defaultAccounts’, ‘defaultBenchmarks’ and ‘currency’ properties to ‘accounts’, ‘benchmarks’ and ‘currencyisocode’ properties respectively in ‘Component Settings Lookup’ endpoint [v2.1.0]
  • Replaced response data model in ‘Currency Lookup’ endpoint from an array of objects to dictionary of key/value pairs to be consistent with other lookup endpoints [v2.1.0]
Bug Fixes
  • No changes
v1 (Retiring on 10/31/19)
Summary
  • v1.7.8 - Released on 09/27/19
  • v1.7.7 - Released on 09/27/19
  • v1.7.6 - Released on 09/27/19
  • v1.7.5 - Released on 09/06/19
  • v1.7.4 - Released on 09/06/19
  • v1.7.3 - Released on 08/15/19
  • v1.7.2 - Released on 07/16/19
  • v1.7.1 - Released on 06/13/19
  • v1.7.0 - Released on 04/22/19
  • v1.6.1 - Released on 04/10/19
  • v1.6.0 - Released on 03/27/19
  • v1.5.0 - Released on 02/28/19
  • v1.4.0 - Released on 01/29/19
  • v1.3.0 - Released on 11/29/18
  • v1.2.0 - Released on 10/19/18
  • v1.1.0 - Released on 10/12/18
  • v1.0.0 - Released on 08/30/18
Functionality Additions
  • Retired Vault endpoints in favor of Vault API [v1.7.3]
  • Added incoming rate limit rules for user-serials on trial [v1.7.1]
  • Added account listing endpoint to list account files, composite files and sub-directories in a given directory [v1.7.0]
  • Added document listing endpoint to list PA3 documents in a given directory [v1.7.0]
  • Increased the maximum column count within a PA component to be computed per calculation from 25 to 40 [v1.5.1]
  • Added the option to override columns in a PA component through Calculations API’s Run Calculation endpoint [v1.5.0]
  • Added an endpoint to list all available FactSet-defined and client-defined columns [v1.5.0]
  • Added an endpoint to list all available column statistics that can be applied to a column when overriding in a PA Component through Calculations API’s Run Calculation endpoint [v1.5.0]
  • Returning non-single date frequency components in the components lookup endpoints [v1.4.0]
  • Added additional validations for account and benchmark in the create calculation endpoint [v1.4.0]
  • Added Get All Outstanding Calculations endpoint to fetch progress of all previously requested calculations [v1.3.0]
  • Added Rate Limit response headers to Run Calculation and Run Vault Calculation endpoints [v1.3.0]
  • Added support for Accept request header to all HTTP GET endpoints [v1.3.0]
  • Added API Keys as an additional authentication methodology. One-Time Password (OTP) authentication will be phased out eventually [v1.2.0]
  • [Alpha Release] Added Calculation API for Vault data [v1.1.0]
  • [Alpha Release] Added Helper APIs to lookup Vault configuration(s) by user-serial and by Vault identifier [v1.1.0]
  • Added the option to override groups in a PA component through Calculations API’s Run Calculation endpoint [v1.0.0]
  • Added Helper API for groups lookup [v1.0.0]
  • Added error reasons in response body for responses greater than or equal to HTTP status code 400 [v1.0.0]
  • Added API endpoint to fetch a component’s calculation settings [v1.0.0]
  • Added Api-Supported-Versions response header to describe all API versions supported by a given endpoint [v1.0.0]
Changes
  • Hostname for all endpoints has been changed from “ondemand.factset.com” to “api.factset.com” [v1.0.0]
  • Get Calculation Result Endpoint has been broken down into Get Calculation Status and Get Calculation Result endpoints [v1.0.0]
  • Run Calculation endpoint’s “documentname” property has been retired. Component identifiers will need to be fetched from ‘Fetch Component Settings’ endpoint [v1.0.0]
Bug Fixes
  • Fixed rate limit timeout from 30 milliseconds to 30 seconds [v1.7.2]
  • Fixed error reason when providing incorrect version [v1.3.0]
  • Non-successful responses (HTTP status codes >= 300) will no longer fill up a user-serial’s quota [v1.2.0]