The purpose of this Tutorial is to explain the code used in the Quick Start, and demonstrate an example of one of the DSWS API delivery methods and in result help you discover how to use it.
DSWS provides a SOAP based web service, built using the Windows Communication Foundation (WCF) framework, to access Datastream content. The metadata is published through Web Service Definition Language (WSDL). This document describes the SOAP objects used in the product along with some sample requests.
You can use any platform that can access SOAP based web services for integration. You can also refer to our test page to see the SOAP request and response messages for various operations.
The basic steps for retrieving data are:
Create a DSServiceClient instance.
Importing the SOAP metadata from the WSDL reference will create a DSServiceClient class in the references.cs file. This class supports the methods for requesting a secure token and subsequent data requests.
Request a secure token.
All data requests must be accompanied by a secure token identifying you as the client. You obtain a secure token by calling the GetToken method supplying your Datastream credentials. In addition, your Datastream account must be authorised for access to the API service.
The returned token is valid for 24 hours and should be used for all subsequent requests with a new token being requested shortly before expiry of the current token.
Sending requests and processing the response.
The API supports just two data methods: GetData and GetDataBundle. Both methods need a valid secure token supplied with each request.
Both methods also use a common DSDataRequest object to request one or more sets of data from the system. Both methods return one or more DSDataResponse objects containing the result of each query.
GetData is used to make a single request from the system using one DSDataRequest object. This single request can retrieve data for multiple instruments with a common set of datatypes and a common reporting period. This method returns one DSDataResponse object containing the data for all requested instruments.
GetDataBundle is used to retrieve multiple datasets in one request using an array of DSDataRequest objects. Each DSDataRequest object is independent of any other DSDataRequest within the bundle. GetDataBundle returns a collection of DSDataResponse objects. While slightly more complex to construct and decode, GetDataBundle is more efficient than GetData because the API servers will perform parallel processing of bundled requests.
The DSServiceClient class is the custom WCF client object used to connect to the Datastream ClientAPI service. It extends the .NET System.ServiceModel.ClientBase class and is generated from the WSDL file.
The class supports the GetToken, GetData and GetDataBundle methods for authentication and retrieving data. These methods are described in more detail below. In addition, the class supports a Ping method for basic connectivity checks.
The instantiated DSServiceClient object should be used for all subsequent token and data requests.
<!-- App.config entry -->
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IDSService"
maxBufferSize="60000000" maxReceivedMessageSize="60000000"
receiveTimeout="00:11:00" sendTimeout="00:11:00">
<security mode="Transport"></security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://product.datastream.com/DswsClient/V1/DSService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IDSService"
contract="DSWSReference.IDSService" name="BasicHttpBinding_IDSService" />
</client>
</system.serviceModel>
// C# code
using (var dsClient = new DSServiceClient())
{
//let's try a simple ping test
bool gotPing = false;
try
{
var pingResp = dsClient.Ping(null);
gotPing = true; // success
}
catch (Exception e)
{
// process the error
}
}
This class also provides constructors to supply the endpoint and binding details programmatically.
For any connectivity issues, the Ping method can be used to test the round trip connectivity to the ClientAPI servers. The Ping method simply replies immediately with null. The Ping method can also be easily tested using Powershell or Curl using the Rest\JSON interface.
// Powershell should return a HTTP 200 status code or will display an error message
$T = Invoke-WebRequest -URI "https://product.datastream.com/dswsclient/V1/DSService.svc/rest/Ping"
$T.StatusCode
// Curl for Windows
curl https://product.datastream.com/dswsclient/V1/DSService.svc/rest/Ping
All data requests must be accompanied by a secure token identifying you as the client. You obtain a secure token by calling the GetToken method supplying your Datastream credentials. In addition, your Datastream account must be authorised for access to the API service.
// Create a token request
var tokenRequest = new DSGetTokenRequest()
{
UserName = "xxxxx", //add your Datastream ID and password here
Password = "yyyyyy",
};
using (var dsClient = new DSServiceClient())
{
string tokenValue = null;
DateTime tokenExpiry = default(DateTime);
// Issue a call to get the token
try
{
var tokenResponse = dsClient.GetToken(tokenRequest);
// Read the token from the response
tokenValue = tokenResponse.TokenValue;
tokenExpiry = tokenResponse.TokenExpiry;
}
catch (System.ServiceModel.FaultException<DSFault> tokenFault)
{
// Process the credentials error
Console.WriteLine("Error retrieving token for user {0}. Code {1}, Message {2}.",
tokenRequest.UserName, tokenFault.Code.Name, tokenFault.Message);
}
catch (Exception e)
{
// Process the network or endpoint error
Console.WriteLine("Error retrieving token HRES 0x{1:X}, Message {2}.",
e.HResult, e.Message);
}
}
The GetToken method accepts an instance of a DSGetTokenRequest object as a parameter. This object has two fields of type string, UserName and Password, which accept your Datastream credentials. The object also contains a Properties field that accepts a collection of string-object pairs. This field is reserved for possible future enhancements and provides no current functionality.
For valid credentials, GetToken returns a DSGetTokenResponse object. This contains a TokenValue field of type string containing the secure token, and a TokenExpiry field of type DateTime specifying the timestamp (in UTC) when the token will expire. This token can be used for all subsequent data requests up until the time of expiry, when a new token will be required. In practice, the token is valid for 24 hours, and you should request the new token prior to the expiry of the old token.
DSGetTokenResponse also contains a Properties field that returns a collection of string-object pairs. This field is reserved for possible future enhancements and currently returns a null reference.
For invalid requests, GetToken returns a DSFault object specifying the error. This contains Code and Message fields of type string. For invalid input, the Code field will contain the value “InvalidArgument” and the Message field will specify the invalid input such as “UserName must have a value. (UserName)”. For invalid credentials, the Code field will contain the value “InvalidCredentials” and the Message field will contain an error message such as “Invalid credentials were supplied. ACF01012 PASSWORD NOT MATCHED” or “Invalid credentials were supplied. ACF01013 LOGONID xxxxxxx SUSPENDED BECAUSE OF PASSWORD VIOLATIONS”.
In addition to valid credentials, user accounts must be authorised for access to the API service. If your account has not been granted access, GetToken will return an “InvalidCredentials” error with the Message field containing an error message of the form “User 'xxxxxxx' not entitled to ClientApi service”. If you encounter this error, please contact your Datastream representative for authorisation.
Finally, for general network or endpoint errors, where a connection to our servers is not possible, a generic Exception error will occur. You also need to handle this type of error.
With a valid secure token, data can be retrieved using the GetData and GetDataBundle methods. Both methods require one or more DSDataRequest objects to define the set of data to be returned.
A DSDataRequest object allows you to specify one or more instruments to be requested, a list of datatypes to return for each of these instruments, date information specifying the period to retrieve data for, and a user defined tag that is returned with the response.
A simple C# example of creating a DSDataRequest:
// Create a data request
var req1 = new DSDataRequest()
{
Instrument = new DSInstrument() { Value = "<VOD.L>" },
DataTypes = new[] { new DSDataType() { Value = "PH" } },
Date = new DSDate() { Kind = DSDateKind.Snapshot, Start = "-10D" },
Tag = "SomeClientDefinedReference"
};
The DSInstrument member of a DSDataRequest object is used to specify one or more instruments. It consists of a Value field of type string used to specify the required instruments, and an optional Properties field containing an array of string-object pairs used to further specify the content of the Value field.
C# examples creating DSInstrument objects with various request settings:
// Create a data request specifying multiple instruments
var req1 = new DSDataRequest()
{
Instrument = new DSInstrument()
{
Value = "<BLND.L>,GB0031348658,<VOD.L>",
Properties = new DSStringObjectKVPair[]
{
new DSStringObjectKVPair() { Key = "ReturnName", Value = true }
}
},
Date = new DSDate() { Kind = DSDateKind.TimeSeries, Start = "-7D" },
// other members follow
};
// Create a data request containing expressions
var req2 = new DSDataRequest()
{
Instrument = new DSInstrument()
{
Value = "PCH#(<VOD.L>(PH),1Y)",
Properties = new DSStringObjectKVPair[]
{
new DSStringObjectKVPair() { Key = "IsExpression", Value = true }
}
},
DataTypes = null, // Expressions don’t use DSDataTypes
// other members follow
};
// Create a data request requesting data for a constituent list
var req3 = new DSDataRequest()
{
Instrument = new DSInstrument()
{
Value = "LFTSE100",
Properties = new DSStringObjectKVPair[]
{
new DSStringObjectKVPair() { Key = "IsList", Value = true }
}
},
DataTypes = new[]
{
new DSDataType() { Value = "NAME" },
new DSDataType() { Value = "ISIN" },
new DSDataType() { Value = "SECD" }
},
// other members follow
};
The Value field of DSInstrument accepts a comma separated list of instruments. Instruments can be defined in the following formats (where applicable) with Equities examples given for Vodafone, Microsoft, Daimler and Toyota:
Datastream Mnemonic (MNEM): This is a unique identification code assigned by Datastream. For all markets except the UK and Ireland, the first 1 or 2 characters plus a colon are a country identification prefix:
e.g. VOD, @MSFT, D:DAI, J:TYMO
Datastream Code (DSCD): This is the unique six-character identification code allocated by Datastream for every instrument. This code can be used instead of a series mnemonic:
e.g. 953133, 719643, 688700, 905289
ISIN (ISIN): The International Security Identification Number is a code that uniquely identifies a security:
e.g. GB00BH4HKS39, US5949181045, DE0007100000, JP3633400001
Sedol (SECD): This is an identification code based on the code issued by the London Stock Exchange. Note UK and Ireland stocks need to be prefixed by “UK”:
e.g. UKBH4HKS3, 2588173, 5529027, 6900643
RIC (RIC): The Reuters Instrument Code. Note: RICs must be decorated by RIC\T1C delimiters:
e.g. <VOD.L>, < MSFT.O>, < DAIGn.F>, < 7203.T>
Thomson Ticker (T1C): The Thomson One Ticker: Note: T1Cs must be decorated by RIC\T1C delimiters:
e.g. <VOD-LN>, < MSFT-US>, < DAI-FF>, < 7203-TO>
Local Code (LOC): This is an identification code based on the official local exchange code. It comprises up to 12 characters, prefixed by an alphabetic country code.
e.g. UK:VOD, U59491810, D710000, J7203
In addition to Equities, Datastream also supports other financial data, such as Economics (e.g. UKGDP...D), Investment trusts (e.g. SMT), Corporate Bonds (e.g. 8393A1), Interest rates (e.g. TRUS10T), Exchange Rates (e.g. UKDOLLR), etc. The full range of instruments can be searched using our Datastream For Office (DFO), Eikon or ThomsonOne products. Alternatively, you can search for items directly in a browser using your Datastream credentials.
Datastream also supports Constituent Lists of instruments, e.g. LFTSE100, LS&PCOMP, LDAXINDX, LSTOKYOSE, etc. List instruments are only supported in Snapshot mode, and only one list is permitted per request. The datatypes supplied with the list request are applied to all the constituents of the list, and the response for each item returned in the DSDataResponse object. Again, these lists can be searched for using DFO, EIKON, etc.
To retrieve TimeSeries data for the constituents of a list, it is necessary to make a Snapshot request for the ID of each of the constituents (e.g. MNEM, ISIN, etc as defined above), and to then make TimeSeries requests for the individual constituents using GetData or GetDataBundle.
Finally, Datastream also supports pre-defined functions that can be incorporated in formulas (known as Expressions) of the form Function#(Expression, Parameters), where:
Example expressions are:
The full range of functions can be searched using our Datastream For Office (DFO) or Eikon products. Alternatively, you can examine the functions directly in a browser. The available predefined Datastream functions can be reviewed using the Expressions Tool in DFO.
The DSInstrument member of a DSDataRequest object also contains a Properties field. This is a collection of string-object pairs. The primary purpose of this field is to provide hints to the servers as to how to process the contents of the Value field. Expressions and constituent lists require slightly different processing on the servers, so supplying a hint to identify these items, if you know they are lists or expressions, speeds up processing.
In addition, the Properties field can also be used to request the descriptive names of the requested instruments when requesting TimeSeries data.
The Properties field currently supports the following settings (see the C# examples section above for instances of their use):
ReturnCurrency and ReturnDates (Deprecated): These flags originally supported a prototype dataset that was never released into production and are now deprecated.
The DSDataTypes member of a DSDataRequest object is used to specify a collection of one or more DSDataType objects specifying the datatypes to be returned for each of the specified instruments. The DSDataType object consists of a Value field of type string used to specify the required datatype, and an optional Properties field containing an array of string-object pairs used to further specify the content of the Value field.
C# examples creating a DSDataType objects with various request settings:
// Creating a data request specifying no datatypes returns just the default
// datatype (X) for each requested item
var req1 = new DSDataRequest()
{
Instrument = new DSInstrument() { Value = "U:HOG,VOD" },
DataTypes = null, // Default datatype X assumed
// other members follow
};
// You can specify the default datatype explicitly
var req2 = new DSDataRequest()
{
Instrument = new DSInstrument() { Value = "U:HOG,VOD" },
DataTypes = new[] { new DSDataType() { Value = "X" } },
// other members follow
};
// You can request multiple datatypes including built-in Datastream
// expressions such as 897E (Historical Beta)
var req3 = new DSDataRequest()
{
Instrument = new DSInstrument() { Value = "U:HOG,VOD" },
DataTypes = new[]
{
new DSDataType() { Value = "NAME" },
new DSDataType() { Value = "ISIN" },
new DSDataType() { Value = "897E" }
},
// other members follow
};
// You can simplify the use of expressions using the DSDataType and
// the “symbol substitution” feature where each requested instrument is
// substituted for X in any expression
// e.g. LN#(X(RI)) and VOD will be substituted as LN#(VOD(RI))
var req4 = new DSDataRequest()
{
Instrument = new DSInstrument() { Value = "U:HOG,VOD" },
DataTypes = new[]
{
new DSDataType() { Value = "LN#(X(RI))" },
new DSDataType() { Value = "PCH#(X(PH), 1Y)" }
},
// other members follow
};
// You can use the Properties member of DSDataType to request
// the return of the descriptive name for each datatype
var req5 = new DSDataRequest()
{
Instrument = new DSInstrument() { Value = "U:HOG,VOD" },
DataTypes = new[]
{
new DSDataType()
{
Value = "PH",
Properties = new DSStringObjectKVPair[]
{
new DSStringObjectKVPair() { Key = "ReturnName", Value = true }
}
},
new DSDataType()
{
Value = "PL",
Properties = new DSStringObjectKVPair[]
{
new DSStringObjectKVPair() { Key = "ReturnName", Value = true }
}
}
},
// other members follow
};
Currently, the Properties field only supports one option, ReturnName. When set True for a datatype, this will return a full display name for the datatype if available. The display names are returned as a collection of key-value pairs (e.g. “PH” : ”PRICE HIGH“) in the DataTypeNames member of the DSDataResponse object (see DSDataResponse section later for an example).
The Value field of DSDataType accepts a single datatype. Examples of the range of datatypes available on Datastream are (the actual datatypes to request are given in parentheses):
The DSDataType can also be used to simplify requesting expressions. If you have a range of instruments you need to use in an expression, rather than constructing a unique expression for each required instrument such as (e.g. Exchange Rates):
PCH#(MAV#(UKDOLLR,1M),1M)
PCH#(MAV#(USEURSP,1M),1M)
PCH#(MAV#(CHIYUA$,1M),1M)
The expression can be simplified using the DSDataType field and the “Symbol Substitution” feature, where any single, isolated X character is replaced with the supplied instruments. For the previous instruments, we can construct a common expression in a datatype of the form PCH#(MAV#(X,1M),1M):
var req4 = new DSDataRequest()
{
Instrument = new DSInstrument() { Value = "UKDOLLR,USEURSP,CHIYUA$" },
DataTypes = new[] { new DSDataType() { Value = "PCH#(MAV#(X,1M),1M)" } },
// other members follow
};
Finally, if you don’t supply any datatype when making a standard request, a default datatype, X, is sent to the mainframe. This default datatype is interpolated by the mainframe as “return the default value for the given instrument”. The default value varies depending on the requested instrument type. For example, for equities and ETFs, the default is the official closing price (P). For exchange rates the default is the ER datatype; the midpoint between the bid and offered rates.
Note: for constituent lists, if the IsList property is supplied and set to True in the DSInstrument object, then the datatype NAME is sent as the default to the mainframe and returned as the default response field for each constituent of the list.
The full range of datatypes can be searched using our Datastream For Office (DFO), Eikon or ThomsonOne products. Alternatively, you can search for datatypes directly in a browser using your Datastream credentials.
The DSDate member of a DSDataRequest object is used to specify the period to retrieve data from for each of the specified instruments. The DSDate object consists of a DSDateKind field specifying the request as a Snapshot or TimeSeries request, Start and Stop dates, and a Frequency string used when requesting TimeSeries data.
C# examples creating a DSDate objects with various request settings:
// Creating a data request specifying snapshot data with relative date of -10 days
// Relative dates can be specified in days, weeks, months, quarters or years
// e.g. -5W, -12M, -3Q, -5Y
var req1 = new DSDataRequest()
{
Instrument = new DSInstrument() { Value = "VOD"},
Date = new DSDate() { Kind = DSDateKind.Snapshot, Start = "-10D" },
// other members follow
};
// Creating a data request specifying snapshot data with an absolute date
// Absolute dates can be specified in YYYY-MM-DD or YYYYMMDD formats
var req2 = new DSDataRequest()
{
Instrument = new DSInstrument() { Value = "VOD"},
Date = new DSDate() { Kind = DSDateKind.Snapshot, Start = "2018-06-01" },
// other members follow
};
// Creating a data request specifying snapshot data with latest date
// that has data available
var req3 = new DSDataRequest()
{
Instrument = new DSInstrument() { Value = "VOD"},
Date = new DSDate() { Kind = DSDateKind.Snapshot, Start = "LATESTDATE" },
// other members follow
};
// Creating a timeseries request for daily data between 01 and 20 June 2018
var req4 = new DSDataRequest()
{
Instrument = new DSInstrument() { Value = "VOD"},
Date = new DSDate()
{
Kind = DSDateKind.TimeSeries,
Start = "2018-06-01",
End = "2018-06-20",
Frequency = "D"
},
// other members follow
};
// Creating a timeseries request for monthly data from the birth date of the stock
// on Datastream until the latest date
var req4 = new DSDataRequest()
{
Instrument = new DSInstrument() { Value = "VOD"},
Date = new DSDate()
{
Kind = DSDateKind.TimeSeries,
Start = "BDATE",
End = "",
Frequency = "M"
},
// other members follow
};
// Creating a timeseries request for quarterly data (mid-quarter dates) for
// the last 12 quarters
var req4 = new DSDataRequest()
{
Instrument = new DSInstrument() { Value = "VOD"},
Date = new DSDate()
{
Kind = DSDateKind.TimeSeries,
Start = "-12QTM",
End = "-1QTM",
Frequency = "Q"
},
// other members follow
};
The DSDateKind member of DSDate is a simple enumerated type with value either Snapshot or TimeSeries.
The Start and End members of DSDate specify the start and end dates for data retrieval. These are string values and can accept the following input formats:
Relative offsets with specific starting dates specifying the start, mid and end periods of given weeks, months, quarters and years. The offsets are relative to the current date (week, month, quarter or year):
WKS, WKM & WKE: literals representing the start, mid and end dates of specific weeks, respectively.
e.g. “-5WKM” – The date representing Wednesday 5 weeks previous to the current week.
MTS, MTM & MTE: literals representing the first, mid (15th) and last trading dates (weekdays) of specific months, respectively.
e.g. “-3MTS” – The first weekday of the third month prior to the current month.
QTS, QTM & QTE: literals representing the first, mid (15th of second month in the quarter) and last trading dates (weekdays) of specific quarters, respectively.
e.g. “-8QTE” – The last weekday of the eighth quarter prior to the current quarter.
YRS, YRM & YRE: literals representing the start, mid (15th June) and end dates of specific years, respectively.
e.g. “-4YRM” – The date representing 15th June 4 years prior to the current year.
The Frequency field is only relevant for TimeSeries requests and specifies the frequency of the returned data. The Frequency field supports the following values:
The following table gives a comparison of the differences in output between the daily frequencies. The X represents dates and values that are not returned.
The Start, End and Frequency settings have the following defaults if not specified:
The End and Frequency settings are ignored if the request type is Snapshot.
The Tag member of a DSDataRequest object is simply an optional client specified string. This string can be used to uniquely identify each request, e.g. a hash key, and is returned in the Tag field of the corresponding DSDataResponse object.
A full DSDataRequest combines all the above elements:
DSDataRequest testRequest = new DSDataRequest()
{
Instrument = new DSInstrument()
{
// Vodafone as Mnemonic, MSFT as ISIN, Daimler as RIC, Toyota as Local code
Value = "VOD,US5949181045,<DAIGn.F>,J7203",
// Request full symbol names
Properties = new DSStringObjectKVPair[]
{
new DSStringObjectKVPair() { Key = "ReturnName", Value = true }
}
},
DataTypes = new[]
{
// Request full datatype names in response
new DSDataType()
{
Value = "PH",
Properties = new DSStringObjectKVPair[]
{
new DSStringObjectKVPair() { Key = "ReturnName", Value = true }
}
},
new DSDataType()
{
Value = "DD",
Properties = new DSStringObjectKVPair[]
{
new DSStringObjectKVPair() { Key = "ReturnName", Value = true }
}
},
new DSDataType()
{
Value = "PYD",
Properties = new DSStringObjectKVPair[]
{
new DSStringObjectKVPair() { Key = "ReturnName", Value = true }
}
}
},
Date = new DSDate()
{
Kind = DSDateKind.TimeSeries,
Start = "2017-01-01",
End = "2017-12-31",
Frequency = "D"
},
Tag = "myUniqueTag"
};
Whilst each DSDataRequest object supports multiple instruments and datatypes, request limits are imposed by the system.
Prior to the following new limits being imposed, clients were allowed to request up to 2000 items in any one request; where the number of items is defined as the number of instruments requested multiplied by the number of datatypes. If your Datastream credentials were authorised for accessing the API prior to 01 August 2018, the new limits will not be applied to your account in order to maintain compatibility with your existing requests. However, we would recommend that all clients try and adhere to the following new limits.
Clients granted access to the API after 01 August 2018 will be restricted to the following limits in any one DSDataRequest:
The above limits permit the following example permutations in any one DSDataRequest:
When used with GetDataBundle requests, where a collection of DSDataRequest objects can be supplied, there are additional limits imposed on the number of items that can be requested across the bundle. Please see the GetDataBundle section for details.
With a valid secure token and one or more DSDataRequest objects defined, data can be retrieved using the GetData and GetDataBundle methods.
GetData is used to make a single request from the system using one DSDataRequest object. This single request can retrieve data for multiple instruments with a common set of datatypes and a common reporting period. This method returns one DSDataResponse object containing the data for all requested instruments.
The GetData method takes one parameter, a DSGetDataRequest object, to wrap the token and DSDataRequest items, and returns a DSGetDataResponse object upon success. Upon failure, a DSFault exception object is thrown.
/* This code snippet assumes the previous creation of the DSServiceClient
* instance (dsClient), successfull retrieval of a token (tokenValue), and
* the creation of a DSDataRequest instance (testRequest).
* See the relevant previous sections for examples of how to create these instances.
* */
// The token and DSDataRequest instance must be wrapped in a DSGetDataRequest instance.
var dataReq = new DSGetDataRequest()
{
TokenValue = tokenValue,
DataRequest = testRequest,
};
try
{
// Issue the GetData request.
DSGetDataResponse myGetDataResp = dsClient.GetData(dataReq);
// Retrieve the DSDataResponse object.
DSDataResponse response = myGetDataResp.DataResponse;
// Process the response DSDataResponse.
// See the DSDataResponse section
}
catch (System.ServiceModel.FaultException<DSFault> reqFault)
{
// Process any request error
Console.WriteLine("Error requesting GetData for user {0}. Code {1}, Message {2}.",
tokenRequest.UserName, reqFault.Code.Name, reqFault.Message);
}
catch (Exception e)
{
// Process any network or endpoint error
Console.WriteLine("Error requesting GetData HRES 0x{1:X}, Message {2}.",
e.HResult, e.Message);
}
The DSGetDataRequest object, in addition to the token and DSDataRequest parameters, also contains a Properties field that accepts a collection of string-object pairs. This field is reserved for possible future enhancements and provides no current functionality.
The section describing the returned DSDataResponse object explains how to process a successful response.
For invalid requests, GetData returns a DSFault object specifying the error. This contains Code and Message fields of type string. For invalid input, the Code field will contain the value “InvalidArgument” and the Message field will specify the invalid input such as “tokenValue cannot be empty. (tokenValue)”. The parameters of the supplied DSDataRequest are also validated and any errors returned, e.g. “Instrument cannot be null (Instrument)”.
For invalid or expired tokens, the Code field will contain the value “InvalidToken” and the Message field will contain a descriptive error message such as “Invalid token string.” or “Token has expired”.
Finally, for general network or endpoint errors, where a connection to our servers is not possible, a generic Exception error will occur. You also need to handle this type of error.
GetDataBundle is used to retrieve multiple datasets in one request using an array of DSDataRequest objects.
Whilst each DSDataRequest object can be independent of any other DSDataRequest within the bundle, bundled request usually have a common request and response processing formats:
// Simulating constructing a batch of instruments read from a database into a collection
string[] reqInstruments = new string[] { "VOD", "@MSFT", "D:DAI", "J:TYMO", "BARC",
"MKS", "LGEN", "RBS", "BLND", "LLOY" };
// In a bundled request we usually want to request the same datatypes
DSDataType[] reqDataTypes = new[]
{
new DSDataType() { Value = "PH" },
new DSDataType() { Value = "PCH#(X(PH), 1Y)" }
};
// and a common date range for each instrument in the batch
DSDate reqDateRange = new DSDate()
{
Kind = DSDateKind.TimeSeries,
Start = "2018-06-01",
End = "2018-06-20",
Frequency = "D"
};
// Build an array of DSDataRequest items from the collection of instruments
// One request for each instrument in this example
DSDataRequest[] dataReqs = new DSDataRequest[reqInstruments.Count()];
int nindex = 0;
foreach (string inst in reqInstruments)
{
dataReqs[nindex++] = new DSDataRequest()
{
Instrument = new DSInstrument() { Value = inst }, // Set the instrument
DataTypes = reqDataTypes, // Assign the list of datatypes we created earlier
Date = reqDateRange, // Assign the common date range
Tag = inst // You can assign a unique identifier to each request.
};
}
The GetDataBundle method takes one parameter, a DSGetDataBundleRequest object, to wrap the token and the array of DSDataRequest items, returning a DSGetDataBundleResponse object upon success. Upon failure, a DSFault exception object is thrown.
The DSGetDataBundleRequest object, in addition to the token and DSDataRequests parameters, also contains a Properties field that accepts a collection of string-object pairs. This field is reserved for possible future enhancements and provides no current functionality.
/* This code snippet assumes the previous creation of the DSServiceClient
* instance (dsClient), successfull retrieval of a token (tokenValue), and
* the creation of a DSDataRequest array (dataReqs).
* See the relevant previous sections for examples of how to create these instances.
* */
// The token and DSDataRequest array must be wrapped in a DSGetDataBundleRequest
var dataReq = new DSGetDataBundleRequest()
{
TokenValue = tokenValue,
DataRequests = dataReqs
};
try
{
// Issue the GetDataBundle request.
DSGetDataBundleResponse myGetDataBundleResp = dsClient.GetDataBundle(dataReq);
// Retrieve the DSDataResponse array.
DSDataResponse[] myResps = myGetDataBundleResp.DataResponses;
// Process the response for each request
foreach (var response in myResps)
{
// Get the unique request identifier from the Tag field if needed.
string reqIdentifier = response.Tag;
// Process the response DSDataResponse.
// See the DSDataResponse section
}
}
catch (System.ServiceModel.FaultException<DSFault> reqFault)
{
// Process any request error
Console.WriteLine("Error in GetDataBundle for user {0}. Code {1}, Message {2}.",
tokenRequest.UserName, reqFault.Code.Name, reqFault.Message);
}
catch (Exception e)
{
// Process any network or endpoint error
Console.WriteLine("Error in GetDataBundle HRES 0x{1:X}, Message {2}.",
e.HResult, e.Message);
}
The returned DSGetDataBundleResponse object contains a DataResponses property, a simple array of DSDataResponse objects corresponding to the supplied requests. This collection can simply be iterated and each individual DSDataResponse processed. The section describing DSDataResponse objects explains how to process a response.
The DSGDataBundleResponse also supports a Properties field that returns a collection of string-object pairs. Like the Properties Field of the DSGetDataBundleRequest object, this field is reserved for possible future enhancements and provides no current functionality.
For invalid requests, GetDataBundle returns a DSFault object specifying the error. This contains Code and Message fields of type string. For invalid input, the Code field will contain the value “InvalidArgument” and the Message field will specify the invalid input such as “tokenValue cannot be empty. (tokenValue)”.
For invalid or expired tokens, the Code field will contain the value “InvalidToken” and the Message field will contain a descriptive error message such as “Invalid token string.” or “Token has expired.”. The parameters of each of the supplied DSDataRequests items are also validated and any errors returned, e.g. “Instrument cannot be null (Instrument)”.
Finally, for general network or endpoint errors, where a connection to our servers is not possible, a generic Exception error will occur. You also need to handle this type of error.
As for the new limits imposed on individual DSDataRequest objects (see the DSDataRequest Limits section previously), there are also limits imposed by the servers on bundled requests. Prior to 01 August 2018, bundled requests could request up to a maximum of 2000 items, where the number of items is defined as the number of instruments requested multiplied by the number of datatypes. This 2000 item limit could be requested in any combination, from one constituent DSDataRequest containing 2000 items, to 2000 constituent DSDataRequests each containing just one item per request.
If your Datastream credentials were authorised for accessing the API prior to 01 August 2018, the following new limits will not be applied to your account in order to maintain compatibility with your existing requests.
Clients granted access to the API after 01 August 2018 will have the following limits imposed on a bundled request in addition to the individual limits imposed on any one DSDataRequest,:
The above limits permit the following example permutations in any one GetDataBundle request:
Both the GetData and GetDataBundle requests return one or more DSDataResponse objects. For multiple response objects returned in GetDataBundle, the collection can be processed in turn using a simple array iterator.
// Retrieve the DSDataResponse array.
DSDataResponse[] myResps = myGetDataBundleResp.DataResponses;
// Process the response for each request
foreach (var response in myResps)
{
// Get the unique request identifier from the Tag field if needed.
string reqIdentifier = response.Tag;
// Process the response DSDataResponse.
// See later example code for processing a single DSDataResponse
}
Responses from the API service are primarily represented as an array of Datatype responses, each containing a result for every instrument requested.
The DSDataResponse object has two key properties; namely, the Dates property containing an array of DateTime objects representing the set of dates for which data is returned, and the DataTypeValues property, an array of DSDataTypeResponseValue objects describing the response for each requested datatype. The DSDataTypeResponseValue contains the set of responses for each requested instrument for the given datatype.
The DSDataResponse object also contains 4 optional response properties; namely, the Tag, AdditionalResponses, SymbolNames and DataTypeNames members.
The Dates property returns an array of DateTime objects. This is a common set of UTC timezone dates corresponding to the set of values returned for each returned datatype. As each DSDataTypeResponseValue is processed, every valid instrument that returns data for the given datatype will return a corresponding collection of values matching the size of the DateTime array.
For valid Snapshot requests, which return a set of single response values for each requested datatype, the Dates property returns an array containing only one DateTime element. For valid TimeSeries requests, there will one or more DateTime elements returned in the array depending on the time period and frequency requested.
For invalid requests, where no response data is available, the Dates property will contain a null value rather than an array of DateTime objects. For TimeSeries requests, a null Dates response will be returned only if all instruments are invalid, all datatypes are invalid, or the specified date range is not supported by the system. For Snapshot requests, a null Dates response is sent if any one datatype is invalid or the requested date is not supported by the system. Although the Dates property will be null for invalid requests, the DataTypeValues property will detail the error condition. The error condition will be common across all responses; only the first response datatype needs to be processed in order to retrieve the general error message.
// This snippet assumes the DSDataResponse object has previously been retrieved
// into the variable response.
// Get the collection of dates corresponding with the responses to be processed.
DateTime[] respDates = response.Dates;
if (respDates == null) // No valid responses returned for any item.
{
// get the first SymbolValue that is of type DSSymbolResponseValueType.Error
// from the first datatype and process the general error
DSDataTypeResponseValue firstType = response.DataTypeValues.FirstOrDefault();
if (firstType != null)
{
DSSymbolResponseValue respErrorSymbol = firstType.SymbolValues.FirstOrDefault(
x => x.Type == DSSymbolResponseValueType.Error);
if (respErrorSymbol != null)
{
string errMsg = respErrorSymbol.Value.ToString();
// process this general error message
}
}
}
else
{
// There is some valid data (note, not all responses may have valid data.)
// Process the response for each requested datatype.
// See next section....
}
The DataTypeValues property contains an array of DSDataTypeResponseValue objects describing the response for each requested datatype. Each DSDataTypeResponseValue object consists of a DataType property containing the returned datatype’s name, and a SymbolValues property containing a collection of DSSymbolResponseValue objects; one for each instrument requested.
foreach (var datatypeValue in resp.DataTypeValues)
{
string datatype = datatypeValue.DataType; // the current dataype being processed
// Each response datatype will have a list of responses for the symbols requested.
// Process the response for each requested symbol in turn
foreach (var symbolValue in datatypeValue.SymbolValues)
{
string symbolID = symbolValue.Symbol;
string curr = symbolValue.Currency;
// Process the DSSymbolResponseValue type and value.
}
}
The DSSymbolResponseValue object returns the data for the given symbol and datatype. It provides the 4 following properties:
For valid Snapshot responses, the Type property will specify a DSSymbolResponseValueType enumeration value of either Bool, DateTime, Double, Int or String, and the Value property will be an object of the specified type. If the requested datatype and instrument produce an error result, the type will be Error, and the Value property will contain a string specifying the error.
// Each response datatype will have a list of responses for the symbols requested.
// Process the datatype response for each requested symbol in turn.
foreach (var symbolValue in datatypeValue.SymbolValues)
{
string symbolID = symbolValue.Symbol;
string curr = symbolValue.Currency;
// did an error occur for this symbol and datatype?
if (symbolValue.Type == DSSymbolResponseValueType.Error)
{
// Process the request failure.
string strErr = (symbolValue.Value == null ? string.Empty :
(string)symbolValue.Value);
}
else
{
// Process the data for this symbol and datatype.
// You can process differently based on returned type
switch (symbolValue.Type)
{
case DSSymbolResponseValueType.Bool:
{ bool boolRes = (bool)symbolValue.Value; }
break;
case DSSymbolResponseValueType.DateTime:
{ DateTime dateRes = (DateTime)symbolValue.Value; }
break;
case DSSymbolResponseValueType.Double:
{ Double dblRes = (double)symbolValue.Value; }
break;
case DSSymbolResponseValueType.Int:
{ int intRes = (int)symbolValue.Value; }
break;
case DSSymbolResponseValueType.String:
{ string strRes = (string)symbolValue.Value; }
break;
default: // Snapshots shouldn't return any other type
break;
}
}
}
For valid TimeSeries requests, the Type property will specify a DSSymbolResponseValueType enumeration value of one of DoubleArray, DateTimeArray or ObjectArray. If the requested datatype and instrument produce an error result, the type will be Error, and the Value property will contain a string specifying the error.
// Each response datatype will have a list of responses for the symbols requested.
// Process the datatype response for each requested symbol in turn
foreach (var symbolValue in datatypeValue.SymbolValues)
{
string symbolID = symbolValue.Symbol;
string curr = symbolValue.Currency;
// Did an error occur for this symbol and datatype?
if (symbolValue.Type == DSSymbolResponseValueType.Error)
{
// Process the request failure. Here we just crop for display purposes
string strErr = (symbolValue.Value == null ? string.Empty :
(string)symbolValue.Value);
}
else
{
// Process the data for this symbol and datatype.
// You can process differently based on returned type
switch (symbolValue.Type)
{
case DSSymbolResponseValueType.DateTimeArray:
{
DateTime[] trimDates = (DateTime[])symbolValue.Value;
break;
}
case DSSymbolResponseValueType.DoubleArray:
{
double[] trimDbls = (double[])symbolValue.Value;
break;
}
case DSSymbolResponseValueType.ObjectArray:
default: //TimeSeries shouldn't return any other type other than above
{
object[] trimObjs = (object[])symbolValue.Value;
break;
}
}
}
}
Whilst the Dates and DataTypeValues are key properties of any response, the DSDataResponse object also contains 4 optional response properties:
The DataTypeNames member: As for symbol names, the expanded name of the requested datatypes can also be returned. Setting ReturnName in the Properties member of each DSDataType object will return the expanded datatype name as key-value string pairs (see the last example request in the DSDataType section). Example key-value pairs are “PH”-“PRICE HIGH”, “DD”-”DIV RATE ADJUSTED”, “PYD”-“DIV PAY DATE”, etc. For invalid datatypes, a null string will be returned.
The following is sample code for processing the above optional fields:
// Retrieve the DSDataResponse object.
DSDataResponse response = myGetDataResp.DataResponse;
// Get the unique request identifier from the Tag field if needed.
string reqIdentifier = response.Tag;
// If request was a Timeseries request, the Additionalresponses collection
// will contain a Frequency member with value "D", "W", "M", "Q" or "Y"
if (response.AdditionalResponses != null
&& response.AdditionalResponses[0].Key == "Frequency")
{
string responseFreq = response.AdditionalResponses[0].Value.ToString();
}
// Process any SymbolValues mapping requested instruments
// to full symbol names (e.g. "VOD" to "VODAFONE GROUP")
if (response.SymbolNames != null)
{
foreach (var kvSymbol in response.SymbolNames)
{
string reqCode = kvSymbol.Key;
string symbolName = kvSymbol.Value;
}
}
// Process any DataTypeNames mapping requested datatypes
// to full name (e.g. "PH" to "PRICE HIGH")
if (response.DataTypeNames != null)
{
foreach (var kvDataType in response.DataTypeNames)
{
string reqDatatype = kvDataType.Key;
string typeName = kvDataType.Value;
}
}