Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 14 Next »

This document describes how to use the AMANA XBRL Engine to utilize XBRL instances and taxonomies.

Used Technologies

.NET Framework

AMANA.XBRL.Engine is a class library project based on .NET 4.7.2 which can be easily embedded into any .NET 4.7.2+ project.

Usually, referenced assemblies are available automatically. However, the following components are used by the AMANA.XBRL.Engine:

Generally, all the recent versions of Windows are supported. For more information, refer to the .NET 4.7.2 System Requirements at:

System Requirements for version 4.7.2

Getting started

License File

The AMANA XBRL Engine requires a license file. The Engine will look for the license file at the following locations:

  • At the processor’s default XBRL settings folder “%APPDATA%\Roaming\AMANAconsulting”.
  • At the processor’s XBRL cache folder, which can be defined in the XbrlSetting’s XbrlFileCachePath property. The default value for this property is “%APPDATA%\AMANAconsulting\XBRLCache”.
  • At the location, where the engine’s .dll files are stored.

XBRL Settings File

Certain functionalities of the AMANA.XBRL.Engine library can be controlled from the Settings file (named SmartXBRLSettings.xml) which resides in the AMANAConsulting directory under following path:

%APPDATA%\AMANAConsulting

A typical SmartXBRLSettings.xml file might look like this:

<?xml version="1.0"?>
<XBRLSettings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ThrowXBRLExceptionForUnitTesting>false</ThrowXBRLExceptionForUnitTesting>
  <MaxValidationExceptionLimit>1000</MaxValidationExceptionLimit>
  <UseProxy>NoProxy</UseProxy>
  <ProxyPort>0</ProxyPort>
  <ProxyUseAuthentication>false</ProxyUseAuthentication>
  <XBRLFileCachePath>C:\Users\username\AppData\Roaming\AMANAconsulting\XBRLCache</XBRLFileCachePath>
  <ForceOfflineMode>true</ForceOfflineMode>
  <FillOffllineCache>true</FillOffllineCache>
  <ReDownloadAndOverrideCachedFiles>false</ReDownloadAndOverrideCachedFiles>
  <TaxonomyPackageCacheType>None</TaxonomyPackageCacheType>
  <DisableAsyncTaxonomyLoading>false</DisableAsyncTaxonomyLoading>
  <DisableBinaryCache>false</DisableBinaryCache>
</XBRLSettings>

There are few settings which might alter the behavior of the Engine:

  • ThrowXBRLExceptionForUnitTesting: Once set to true, forces the Engine to throw a XbrlException if one occurs.
  • MaxValidationExceptionLimit: Specified in numbers defining the Validation exception limit. Once limit is reached, the Engine throws a so-called XbrlExceptionLimitReachedException exception.
  • UseProxy: Once set, forces the Engine to use a Proxy Server in order to access the remote location where the taxonomy is situated. If enabled, the following Properties must be specified:
    • ProxyAddress: Must be a valid string.
    • ProxyPort: Must be a valid integer.
    • ProxyUser: Must be a valid string.
    • ProxyPasswordEncrypted: Must be a valid string.
    • ProxyUseAuthentication:  Must be a valid bool.
  • XBRLFileCachePath: Specifies a path to local folder where the taxonomies are stored.
  • ForceOfflineMode: Once enabled, forces the Engine to use the local cache where the taxonomies are located. If the taxonomies are not found, the engine will download them from the internet. The local cache path is specified by the XBRLFileCachePath setting.
  • FillOffllineCache: Once set to true, enables the Engine to access remote location where a taxonomy is hosted and download it. The folder path where the taxonomy will be stored, is specified by the XBRLFileCachePath setting.
  • ReDownloadAndOverrideCachedFiles: When enabled, the cached files will always be replaced with new ones loaded from the internet.
  • TaxonomyPackageCacheType: Select in which form the taxonomy packages will be stored. You can choose between None, Extracted and Zip.
  • DisableAsyncTaxonomyLoading: Once enabled, the files of a taxonomy are read asynchronously instead of one after another.
  • DisableBinaryCache: When set to false, the faster binary cache is used. If a file is not found, the Engine will look for it in the cache folder.

The settings can be stored as an XML file using it’s static Save method:

var settings = new XbrlSettings();
XbrlSettings.Save(settings, @"C:\path\settings.xml");

The XbrlSettings class also provides a method for loading such an XML file:

var settings = XbrlSettings.Load(@"C:\path\settings.xml");

XBRL Offline Cache

The XBRL Offline Cache is a built-in functionality of the AMANA.XBRL.Engine allowing the Taxonomy Discovery Process to process local versions of taxonomies. This feature speeds up the entire discovery process in comparison with the online mode. Prior to running taxonomy discovery against local data (either processing taxonomy or instance), the taxonomy has to be locally cached (in other words, saved on the local file system). The path to the local cache folder is specified by the XBRLFileCachePath property. In order to activate the feature, the following steps should be undertaken:

  • Set FillOffllineCache property to true. This enables the Engine to save taxonomies on the local file system as soon as they are loaded from the internet. By activating this property, all files downloaded from a remote source will be stored in the cache folder.
  • Set the ForceOfflineMode. This option forces the AMANA.XBRL.Engine to access taxonomies from local file system.

The following example shows how a remotely referenced file will then be stored in the local cache folder.

Remote reference:

http://www.eba.europa.eu/eu/fr/xbrl/crr/fws/corep/its-2014-05/2015-02-16/mod/corep_ind.xsd

Local folder path:

C:\Users\user.name\AppData\Roaming\AMANAconsulting\XBRLCache\www.eba.europa.eu\eu\fr\xbrl\crr\fws\corep\its-2013-02\2014-07-31\mod\corep_ind.xsd

Loading XBRL Documents

Processor

The Processor class is responsible for loading XBRL documents, like XBRL taxonomies or XBRL instance files. The default constructor will try to read the XBRL settings file from the default settings location “%APPDATA%/Roaming/AMANAconsulting/SmartXBRLSettings.xml”:

var processor = new Processor();

The path to the XBRL settings file can also be passed as a string:

var processor = new Processor(@"C:\path\XBRLSettings.xml");

You can also create an XbrlSettings object, which keeps the settings and passes it to the constructor. The settings from this settings object will be used instead of the deafult settings. You can access the processor’s settings using its Settings property.

var xbrlSettings = new XbrlSettings();
var processor = new Processor(xbrlSettings);

How to open/read a XBRL Taxonomy

AMANA.XBRL.Engine allows loading taxonomies from both Url (remote location) and local file system. A taxonomy can be loaded using the processor’s ReadXbrlTaxonomy method. You can pass the path to a locally stored taxonomy, which will be read by the processor:

var taxonomy = processor.ReadXbrlTaxonomy(
    @"C:\EIOPA\eiopa.europa.eu\eu\xbrl\s2md\fws\solvency\solvency2\2015-10-21\mod\ars.xsd");

In this case, the entry point of the taxonomy will be the file path! This can lead to problems, e.g. when generating an instance document. It is recommended to use the entry point of the desired taxonomy and e.g. pass it as an URL. This can be done by passing the entry point to the processor, which will then download the taxonomy:

var taxonomy = processor.ReadXbrlTaxonomy(
    "http://eiopa.europa.eu/eu/xbrl/s2md/fws/solvency/solvency2/2015-10-21/mod/ars.xsd");

The URL can also be passed as an URI object. 

var uri = new Uri(
    "http://eiopa.europa.eu/eu/xbrl/s2md/fws/solvency/solvency2/2015-10-21/mod/ars.xsd");
var taxonomy = processor.ReadXbrlTaxonomy(uri);

When loading a XBRL taxonomy via an URL, the processor can be configured to first look for the XBRL schema files in the local cache. If the schema files are not found, it will try to load the files via the internet. By default, all downloaded files are stored in the XBRL cache folder. The cache folder, proxy settings and other settings can be configured in the Processor’s settings file.

Taxonomy Validation

A loaded taxonomy can also be validated to check if it fulfills the XBRL specification by calling it’s Validate method, which conducts explicit XML schema validation:

var processor = new Processor();
var taxonomy = processor.ReadXbrlTaxonomy(
    "http://eiopa.europa.eu/eu/xbrl/s2md/fws/solvency/solvency2/2015-10-21/mod/ars.xsd");
taxonomy.Validate();
var validationResults = taxonomy.TaxonomyResultSet.GetItems().ToList();

The Validate(GenericLinkbaseContainer container = null) function has an optional parameter of type GenericLinkbaseContainer, which contains all generic links. If it exists, all the contained links are validated as well. After calling the Validate method, the TaxonomyResultSet property of the XbrlTaxonomy will be filled. Each item within this result set is a single validation error. To get all items, call the TaxonomyResultSet.GetItems() method, which will return an IEnumerable of IValidationResult objects. The most important properties of IValidationResult are:

  • IsValid: Specifies whether the taxonomy is valid or not.
  • Message: A validation error message.
  • Message: Specifies validation message.
  • Severity: Specifies message severity. Following Enum values can only be assigned with the property:
    • Error

    • Warning

    • Information

  • Type: Specifies the validation type which is restricted to one of the following (Enum):
    • TaxonomySpec21
    • TaxonomyXml
    • TaxonomySec
    • InstanceXml
    • InstanceSpec21
    • InstanceSec
    • InstanceEBilanz
    • TaxonomyTableLinkbaseSpec
    • InstanceTableLinkbaseSpec
    • TaxonomyDimensionSpec
    • InstanceDimensionSpec
    • InstanceFormulaSpec
    • TaxonomyFormulaSpec
    • TaxonomyExtensibleEnumerations
    • InstanceExtensibleEnumerations
    • FilingRules
    • InlineXbrl
    • InlineXbrlSchema
    • InlineXhtmlSchema
    • ReportGeneration
    • ReportMapping
    • Other
  • RuleId: The ID of the rule specified by the taxonomy.
  • Rule: The rule definition itself.

Taxonomy Caching Processor

The TaxonomyCachingProcessor provides faster access to taxonomies that are referenced by a remote Uri by keeping them cached in the memory after they are opened for the first time. It is highly recommended to use this feature for servers or in constantly running applications. By default, cached taxonomies removed from the memory after 2 days or when the memory usage of the system reaches 95%. The TaxonomyCachingProcessor provides a ReadXbrltaxonomy just like the XbrlProcessor. The following example shows how to open a taxonomy with the TaxonomyCachingProcessor . When the same taxonomy is read at a later point, it will be loaded directly from the cache in the memory.

TaxonomyCachingProcessor taxCacheProcessor = new TaxonomyCachingProcessor();
Uri uri = new Uri("http://www.eba.europa.eu/eu/fr/xbrl/crr/fws/ae/cir-680-2014/2018-03-31/mod/ae_con.xsd");
XbrlTaxonomy tax = taxCacheProcessor.ReadXbrlTaxonomy(uri);

How to open a XBRL Instance Document

The XbrlDocument class provides the XbrlInstances property, which contains the instances data. The most important properties are:

  • List<FactBase> Facts: Facts, i.e. reported data, of the instance document.

  • Dictionary<XmlQualifiedName, List<FactBase>>BaseFactsByElement: Facts by the XML qualified name of the corresponding concept/metric.

  • List<Context> Contexts: All contexts defined in the instance document.

  • List<Units> Units: All units defined in the instance document.

  • List<XbrlTaxonomy> Schemas: The XBRL taxonomies/schemas, which are used by the instance document.

To load an XBRL instance file, use the Processor’s LoadXbrlDocument method. For example this will load a locally stored file:

var xbrlDocument = processor.LoadXbrlDocument(@"C:\path\xbrlInstance.xml");

It is also possible to pass a Stream or an XmlReader to this method:

using (var stream = new FileStream(@"C:\path\xbrlInstance.xml", FileMode.Open))
{
    var xbrlDocument = processor.LoadXbrlDocument(stream);
}

Validation

XBRL Instance Validation

When loading an instance document, the XBRL taxonomies which are referred by the instance, will also be loaded. The instance file can be validated against the XBRL schema and validation rules defined in the referenced taxonomy files by calling the method XbrlDocument.Validate(bool validateXbrlTaxonomies, bool attachResultsToFacts, GenericLinkbaseContainer container = null):

The Validate() method accepts the following parameters:

  • bool validateXbrlTaxonomies: Boolean parameter specifing whether the taxonomy, which the provided instance conforms to, should be validated as well.
  • bool attachResultsToFacts: If set to true, all fact related validation results will be included in the Fact.ValidationResults List.
  • GenericLinkbaseContainer container: The container which stores all generic links of the XBRL taxonomy. Can usually be ignored.

There are two more overloads:

  • Validate(): Skips the validation of the XbrlTaxonomy itself (equal to Validate(true, false) ).
  • Validate(bool validateXbrlTaxonomies, GenericLinkbaseContainer container = null): The fact related validation results will not be included in the Fact.ValidationResults list.

The method can be called as follows:

XbrlDocument xbrlDoc = new XbrlDocument();
xbrlDoc.Load(@"c:\testInstanz.xml");
xbrlDoc.Validate(); //Skips the validation of the XbrlTaxonomy itself

The Validate() method conducts explicit XML schema validation. The results of the validation can be easily retrieved using GetItems() method of XbrlDocument.DocumentResultSet property:

IEnumerable<XbrlMessage> messages = xbrlDoc.DocumentResultSet.GetItems();

Formula Validation

The AMANA.XBRL.Engine supports formula validation per XBRL Formula Linkbase specification. The Formula validation can be simply initiated by the extension method ValidateIncludingFormula() of the XbrlDocument class:

using AMANA.XBRL.Engine.Plugin.FormulaLinkbase;

// starting formula validation
instanceDocument.ValidateIncludingFormula(
      validateTaxonomies: false, 
      verbose: false, 
      attachResultsToFacts: false, 
      new List<string>(), 
      new List<string>()); 

var resultList = instanceDocument.DocumentResultSet.GetItems();

This method first performs XBRL instance validation with optional taxonomy validation. This is because before formula invocation we should have a valid instance document. If XBRL instance validation fails, the formula validation will not run. The result of the formula validation is included in the DocumentResultSet property of the XbrlDocument class.

The behaviour of formula validation can be controlled using the following function parameters:

  • validateTaxonomies: The parameter specifies whether the taxonomy, which the provided instance conforms to, should be validated as well. The taxonomy validation is not necessary for performing formula validation.
  • verbose: Once enabled, forces the validator to display/log all messages generated during the formula validation. Can be used for detailed analysis.
  • attachResultsToFacts: if enabled, forces formula processor attach errors to related facts for future references. It allows tracking errors by each fact for user UI for example.
  • InclusionIds: The list of string specifying Validation Formula IDs which should be performed in formula validation.
  • ExclusionIds: On the contrary, the ExclusionIds specifies the list of Validation Formula IDs which should be skipped during the formula validation.
  • EnableTPL: Enables using .NET Task Parallel Library to speed up Formula processing performance. This option is enabled by default.

Filing Rule Validation

Many authorities define filing rules, a set of additional rules which are not defined in the XBRL taxonomy itself. Hence those rules are manually programmed in the Engine DLL files. If the filing rules are updated, an update of the engine is also needed. To run file rule validaitons for an XBRL instance document, an FilingRulesValidator object must be created. In the following codeblock, a method ValidateInstance is written which demonstrate how to use the FilingRulesValidator

private void ValidateInstance(XbrlInstance instance)
{
	var validator = new FilingRulesValidator();
	validator.LoadRules(Regulator.EBA4_3, false);
	List<FilingRuleValidationMessage> validationResults = validator.Validate(instance);
	foreach (var result in validationResults)
		Console.WriteLine($"{result.RuleId}: {result.Message}");
}

After creating the FilingRulesValidator, the rules which should be validated are loaded via its LoadRules method. The rules are validated using the method Validate. Validation results are returned as a List of FilingRuleValidationMessageObjects. The following list shows the methods of the class FilingRulesValidator:

MethodDescription
LoadRules(Regulator, bool)Loads the rules which should be validated. The first parameter defines the regulator and version of a specific filing rule set. Regulator is a enum defined in the Engine. The second parameter defines if the rule for validating the file name should also be loaded.
LoadRules(Regulator, List<string>, bool, bool)Loads the rules which should be validated. The first parameter defines the regulator and version of a specific filing rule set. As second parameter, a list of validation rule names can be passed. If the third parameter is set to true, only those rule, which name contains any string in the list of validation rules will be validated. If set to false, rules whose name contains any of the passed strings will be excluded. The last boolean parameter defines if the rule for validating the file name should also be loaded.
GetRuleNames(Regulator, bool)Gets the name of all rules for a specific rule set. The first paramter defines the regulator and version of a specific rule set. The second parameter defines if the rule for validating the file name should also be loaded.
Validate(XbrlInstance)Validates the instance documents using the rules which were previously loaded using the LoadRules method.

The FilingRuleValidationMessage class has the following attributes:

NameDescription
NameThe name of the corresponding filing rule.
IsValidIndicates if the instance fulfills the filing rule.
IsExceptionIndicates if an Exception occured during the validation of the filing rule.
MessageThe message which indicates the reason why the instance does not fulfill the filing rule.
RuleIdThe ID of the corresponding filing rule.
RuleThe name of the corresponding filing rule.
TypeReturns always the enumaration value ValidationTypes.XbrlValidaitonType.FilingRules.
Severity

Returns the severity of the error. The severity is a value of the enum ValidationTypes.Severity, which can have one of the following values: Error, Warning, Information.

Generating XBRL Instance

AMANA.XBRL.Engine facilitates seamless XBRL Instance generation process. The instance generation process can easily be organized into 4 logical steps:

  1. Context synthesis
  2. Filing Indicators generation
  3. Unit generation
  4. Facts generation

First, an object of type XbrlInstance must be derived from the XbrlDocument object:

_processor = new Processor();
string taxonomyFile = @"C:\taxonomy.xsd";
XbrlTaxonomy taxonomy = _processor.ReadXbrlTaxonomy(taxonomyFile );
var taxonomies = new List<XbrlTaxonomy>();
taxonomies.Add(taxonomy);
XbrlDocument doc = XbrlDocument.Create(_processor, taxonomies);
XbrlInstance instance = doc.Instance;

Contexts

XBRL Context is represented by the Context class. A context element contains information about the Entity, the period and dimensional information of reported facts. Each context within an instance has a unique ID.

Entity Information

There are two relevant entity information, which are reported in every context node:

  • Identifier: An ID to identify an entity, e.g. an LEI code or SEC's Central Index Key (CIK).
  • Identifier scheme: An URI which represents the type of ID used, e.g. http://standards.iso.org/iso/17442 for LEI codes or http://www.sec.gov/CIK for SEC's CIK.

Period Information

There are three different types of periods which can be expressed:

  • Instant: A point in time. Can be used to express a value for a metric in a specific point in time.
  • Duration: A period with a specified beginning and ending. Can be used to express the change of a value during a specified period.
  • Forever: Can be used to express that a value is always valid.

The concepts for which a value is reported have a 

Dimensional Information

The context also contains dimensional information from explicit and typed dimensions. Those are either grouped within a scenario or a segment. If an element in the taxonomy is the primary item of a hypercube, the context must contain dimension information according to the hypercube. There are two types of dimensions:

  • Explicit dimension: An explicit dimension defines a set of valid values which can be used for this dimension, e.g. a dimension "Country" may list all countries which can be used. Those values are also called "dimension members".
  • Typed dimension: For typed dimensions any value can be used, e.g. a table in which a row for each instrument should be reported can include a dimension "Instrument codes".

A single context can contain both, explicit and typed dimensions.

Example 1 - Multiple Explicit Dimensions

Explicit dimensions will be explained using the example below, which displays a role from the definition linkbase of the full IFRS entry point from 2019.

The role has the hypercube "Statement of changes in equity" (1). This cube is added to the role using an arc with the role "http://xbrl.org/int/dim/arcrole/all". This arcrole states that if a value for any primery item (4) is reported, you have to report also values for each dimension which is part of the hypercube in the value's context. The hypercube has two explicit dimensions (2): "Components of equity" and "Retrospective application and retrospective restatement". So for both dimensions one of it's dimension members (3) must be reported. E.g. for the dimension "Components of equity" the member "Non-controlling interests" and for the dimension "Retrospective application and retrospective restatement" the member "Previously stated" could be reported.

It is possible to report the line item "Equity" multiple times for the same period, but with different dimension member combinations.

Example 2 - Typed Dimensions

Typed dimensions will be explained using the example below, which displays a role from the Solvency II ARS taxonomy from version 2.4:

Again a list of primary items is linked with a hypercube. This Hypercube has only one typed dimension "Fund/Matching portfolio number". So if a value for "Metric: Material" should be added, its context must include the dimension "Fund/Matching portfolio Number" and a value for this dimension. Because this dimension is a typed dimensions, there are no predefined values (i.e. dimension members).

Adding Contexts

For adding Contexts, it is recommended to use the method AddInstantContext or AddDurationContet defined in the XbrlInstance class. Those methods create a context with either a instant period or a duration period and add it directly to the instance. The following code creates an instance document for EIOPA's ARS taxonomy and adds a context and a fact for reporting the company name:

// Load the taxonomy
Processor processor = new Processor();
XbrlTaxonomy taxonomy = processor.ReadXbrlTaxonomy("http://eiopa.europa.eu/eu/xbrl/s2md/fws/solvency/solvency2/2019-07-15/mod/ars.xsd");

// Create a new document (report) for the taxonomy
XbrlDocument xbrlDocument = processor.CreateXbrlDocument(taxonomy);

// Get the element with the name si1376, which is the element used for reporting the name of the company
Element undertakingNameElement = taxonomy.GetElementByLocalName("si1376");

Context context;

// Check the element's period type to determine if a context with an instant period or a context with a duration period must be created
if (undertakingNameElement.PeriodType == Element.ElementPeriodType.Instant)
{
    // Create a new context with a instant context 
    context = xbrlDocument.Instance.AddInstantContext(
        // ID of the new context, which must be unique for the current document
        "instantContext",
        // instant date of the new context
        new DateTime(2020, 12, 31),
        // identification code of the reporting entity
        "LEI/SAMPLELEICODE",
        // type of the identification code
        "http://standards.iso.org/iso/17442");
}
else
{
    // Create a new context with a duration context
    context = xbrlDocument.Instance.AddDurationContext(
        // ID of the new context, which must be unique for the current document
        "instantContext",
        // start date of the new context
        new DateTime(2020, 1, 1),
        // end date of the new context
        new DateTime(2020, 12, 31),
        // identification code of the reporting entity
        "LEI/SAMPLELEICODE",
        // type of the identification code
        "http://standards.iso.org/iso/17442");
}

// Add a fact for the element using the previously generated context
xbrlDocument.Instance.AddTextualFact(context, undertakingNameElement, "AMANA consulting GmbH");

The example below demonstrates how to create a context with explicit dimensions based on the example "Example 1 - Multiple Explicit Dimensions" in the chapter "Dimensional Information" above:

// Load the taxonomy
var processor = new Processor();
var taxonomy = processor.ReadXbrlTaxonomy("http://xbrl.ifrs.org/taxonomy/2019-03-27/full_ifrs_entry_point_2019-03-27.xsd");

// Create a new document (report) for the taxonomy
var document = XbrlDocument.Create(processor, new List<XbrlTaxonomy> { taxonomy });

// Get the dimension element & the member element for the axes
var firstAxis = taxonomy.GetElementByLocalName("ComponentsOfEquityAxis");
var memberForFirstAxis = taxonomy.GetElementByLocalName("NoncontrollingInterestsMember");

var secondAxis = taxonomy.GetElementByLocalName("RetrospectiveApplicationAndRetrospectiveRestatementAxis");
var memberForSecondAxis = taxonomy.GetElementByLocalName("PreviouslyStatedMember");

// Create a dictionary for the dimensions and members
// Each entry in the dictionary is a combination of a dimension and it's member
// The first element is the name of the dimension
// The second element is the name of the member
// The order of the elements in this dictionary is not relevant
var explicitDimensions = new Dictionary<XmlQualifiedName, XmlQualifiedName>();

explicitDimensions.Add(firstAxis.QualifiedName, memberForFirstAxis.QualifiedName);
explicitDimensions.Add(secondAxis.QualifiedName, memberForSecondAxis.QualifiedName);

Context context = document.Instance.AddInstantContext(
        // ID of the new context, which must be unique for the current document
        "instantContextId",
        // instant date of the new context
        new DateTime(2019, 12, 31),
        // identification code of the reporting entity
        "AMANA consulting GmbH",
        // type of the identification code
        "http://www.sec.gov/CIK",
        // explicit dimensions
        explicitDimensions,
        // typed dimensions
        null,
        // dimension container, usually always scenario must be used
        DimensionContainer.Scenario
        );

The example below demonstrates how to create a context with explicit dimensions based on the example "Example 2 - Typed Dimensions" in the chapter "Dimensional Information" above:

// Load the taxonomy
var processor = new Processor();
XbrlTaxonomy taxonomy = processor.ReadXbrlTaxonomy("http://eiopa.europa.eu/eu/xbrl/s2md/fws/solvency/solvency2/2019-07-15/mod/ars.xsd");

// Create an XBRL instance document based on the loaded taxonomy
XbrlDocument document = processor.CreateXbrlDocument(taxonomy);

// Get the element with the name si1376, which is the element used for reporting the name of the fund/portfolio
Element fundPortfolioNameElement = taxonomy.GetElementByLocalName("si1376");

// Get the dimension element
var typedAxis = taxonomy.GetElementByLocalName("FN");

// Create a dictionary for the dimensions and values
// Each entry in the dictionary is a combination of a dimension and it's value
// The first element is the name of the dimension
// The second element is the value for this dimension
// The order of the elements in this dictionary is not relevant
var typedDimensions = new Dictionary<XmlQualifiedName, string>();

typedDimensions.Add(typedAxis.QualifiedName, "typedValue");

Context context = document.Instance.AddInstantContext(
    // ID of the new context, which must be unique for the current document
    "instantContextId",
    // instant date of the new context
    new DateTime(2019, 12, 31),
    // identification code of the reporting entity
    "LEI/SAMPLELEICODE",
    // type of the identification code
    "http://standards.iso.org/iso/17442",
    // explicit dimensions
    null,
    // typed dimensions
    typedDimensions,
    // dimension container, usually always scenario must be used
    DimensionContainer.Scenario
);

// Add a fact for the element using the previously generated context
document.Instance.AddTextualFact(context, fundPortfolioNameElement, "fact value");

The following methods defined in the class XbrlInstance can be used for creating and adding a new context to an instance:

MethodDescription
AddInstantContext(string, DateTime, string, string)

Adds an context with an instant period to the XBRL instance. The parameters are:

  • The context's ID.
  • The context's point in time. Only the date information of the passed DateTime is relevant. If an instant context with not only with a date, but also a specific time is needed, the overload with the boolean parameter "instantDateIsDateOnly" must be used.
  • The entity identifier.
  • The entity identifier scheme.
AddInstantContext(string, DateTime, string, string, Dictionary<XmlQualifiedName, XmlQualifiedName>, Dictionary<XmlQualifiedName, string>, DimensionContainer)

Adds an context with an instant period to the XBRL instance. The parameters are:

  • The context's ID.
  • The context's point in time. Only the date information of the passed DateTime is relevant. If an instant context with not only with a date, but also a specific time is needed, the overload with the boolean parameter "instantDateIsDateOnly" must be used.
  • The entity identifier.
  • The entity identifier scheme.
  • The explicit dimensions which should be added to the context. Each entry in this dictionary is a dimension name and a value for this dimension, i.e. the name of a member of this dimension.
  • The typed dimensions which should be added to the context. Each entry in this dictionary is a dimension name and a value for this dimension.
  • Indicates, if the dimension information should ve added to the segment or the scenario.
AddInstantContext(string, DateTime, string, string, Dictionary<XmlQualifiedName, XmlQualifiedName>, Dictionary<XmlQualifiedName, string>, DimensionContainer, bool)

Adds an context with an instant period to the XBRL instance. The parameters are:

  • The context's ID.
  • The context's point in time. Only the date information of the passed DateTime is relevant. If an instant context with not only with a date, but also a specific time is needed, the overload with the boolean parameter "instantDateIsDateOnly" must be used.
  • The entity identifier.
  • The entity identifier scheme.
  • The explicit dimensions which should be added to the context. Each entry in this dictionary is a dimension name and a value for this dimension, i.e. the name of a member of this dimension.
  • The typed dimensions which should be added to the context. Each entry in this dictionary is a dimension name and a value for this dimension.
  • Indicates, if the dimension information should ve added to the segment or the scenario.
  • Indicates, wether only the date of the passed DateTime should be added to the context or also the time.
AddDurationContext(string, DateTime, DateTime, string, string)

Adds an context with an instant period to the XBRL instance. The parameters are:

  • The context's ID.
  • The context's point in time in which the time span starts. Only the date information of the passed DateTime is relevant.
  • The context's point in time in which the time span ends. Only the date information of the passed DateTime is relevant. If an end time with not only with a date, but also a specific time is needed, the overload with the boolean parameter "endDateIsDateOnly" must be used.
  • The entity identifier.
  • The entity identifier scheme.
AddDurationContext(string, Datetime, DateTime, sting, string, Dictionary<XmlQualifiedName, XmlQualifiedName>, Dictionary<XmlQualifiedName, string>, DimensionContainer)

Adds an context with an instant period to the XBRL instance. The parameters are:

  • The context's ID.
  • The context's point in time in which the time span starts. Only the date information of the passed DateTime is relevant.
  • The context's point in time in which the time span ends. Only the date information of the passed DateTime is relevant. If an end time with not only with a date, but also a specific time is needed, the overload with the boolean parameter "endDateIsDateOnly" must be used.
  • The entity identifier.
  • The entity identifier scheme.
  • The explicit dimensions which should be added to the context. Each entry in this dictionary is a dimension name and a value for this dimension, i.e. the name of a member of this dimension.
  • The typed dimensions which should be added to the context. Each entry in this dictionary is a dimension name and a value for this dimension.
  • Indicates, if the dimension information should ve added to the segment or the scenario.
AddDurationContext(string, DateTime, DateTime, string, string, Dictionary<XmlQualifiedName, XmlQualifiedName>, Dictionary<XmlQualifiedName, string>, DimensionContainer, bool)

Adds an context with an instant period to the XBRL instance. The parameters are:

  • The context's ID.
  • The context's point in time in which the time span starts. Only the date information of the passed DateTime is relevant.
  • The context's point in time in which the time span ends. Only the date information of the passed DateTime is relevant. If an end time with not only with a date, but also a specific time is needed, the overload with the boolean parameter "endDateIsDateOnly" must be used.
  • The entity identifier.
  • The entity identifier scheme.
  • The explicit dimensions which should be added to the context. Each entry in this dictionary is a dimension name and a value for this dimension, i.e. the name of a member of this dimension.
  • The typed dimensions which should be added to the context. Each entry in this dictionary is a dimension name and a value for this dimension.
  • Indicates, if the dimension information should ve added to the segment or the scenario.
  • Indicates, wether only the date of the passed end time should be added to the context or also the time.

Filing Indicator Generation

Filing indicators are used to indicate what tables (data from the tables) are reported in the instance file. Filing indicators can be retrieved from a supplied taxonomy. A common practise is to employ the following statements:

Element fIndicatorsElement = taxonomy.GlobalElements["find:fIndicators"];
Element filingIndicatorElement = taxonomy.GlobalElements["find:filingIndicator"];
Tuple fIndicators = instance.AddTupleToInstance(fIndicatorsElement);

In order to make the instance file be conform with the filing rules, filing indicators must refer to a separate context element. Such can easily be done using the following code:

Context cf = instance.AddInstantContext("c-filingIndicators", DateTime.Now, "AMANA", "http://companyName.de");

The AddInstantContext(string id, DateTime instantDate, string identifier, string identifierScheme) method accepts the following parameters: string id: The unique id of the context, DateTime instantDate: The instant date of the duration period, string identifier: The identifier of the context, string identifierScheme: The identifier scheme of the context.

A Filing indicator is a special kind of fact, thus, it is appended to the instance the same way as an ordinary XBRL fact:

Fact filingInd = new Fact(InstanceDocument.XbrlInstances, filingIndicatorElement, cf);
filingInd.Value = tableResolvedTableHierachy.ParentTable.FilingIndicatorLabel.InnerText;
instance.AddFactToTuple(fIndicators, filingInd);

tableResolvedTableHierachy is the instance of TableResolvedTableHierachy class representing the resolved XBRL table hierarchy. Usually tableResolvedTableHierachy is pulled from role.ResolvedTableHierachies() method where role is a member of Taxonomy.GlobalResolvedRoleTypes collection. Below is the sample of how filing indicators can look like in the instance file:

<find:fIndicators>
	<find:filingIndicator contextRef="c0">C_26.00</find:filingIndicator>
	<find:filingIndicator contextRef="c0">C_27.00</find:filingIndicator>
	<find:filingIndicator contextRef="c0">C_28.00</find:filingIndicator>
	<find:filingIndicator contextRef="c0">C_29.00</find:filingIndicator>
	<find:filingIndicator contextRef="c0">C_30.00</find:filingIndicator>
	<find:filingIndicator contextRef="c0">C_31.00</find:filingIndicator>
</find:fIndicators>

Unit Generation

XBRL units define XBRL fact measurements. E.g. a specific currency or pure, which is used for percentages. Units consist of numerators and optionally denominators. In most cases, only a numerator is needed to express a specific unit. E.g. If the unit of a fact is Euros, the unit consists of the numerator Euro. No denominator is needed in this case.

Denominators are only needed to express divisions. E.g. to express the earnings per share in euro, a unit is nedded with Euro as numerator and shares as denominator.

A numerator and a denominator is a XmlQualifiedName. The constant XbrlBaseConstants.Iso4217, defined in AMANA.XBRL.Engine as "http://www.xbrl.org/2003/iso4217", can be used as namespace for adding monetary units. To create a new unit and add it to an existing instance, use the method AddUnit defined in the class XbrlInstance. The following code adds the unit "EURO" to an existing instance:

XmlQualifiedName unitCode = new XmlQualifiedName("EUR", XbrlBaseConstants.Iso4217);
var numerators = new List<XmlQualifiedName>();
var denominators = new List<XmlQualifiedName>();
numerators.Add(unitCode);
Unit unit = instance.AddUnit("EURO", numerators, denominators);

This code will add the following XML node to the instance document:

<xbrli:unit id="EURO">
	<xbrli:measure>iso4217:EUR</xbrli:measure>
</xbrli:unit>

The following overloads for this method exists:

MethodDescription
AddUnit(string, XmlQualifiedName)

Adds an unit to the instance. The parameters are:

  • The ID of the new unit. The ID must be unique in the current instance.
  • The XmlQualifiedName of the unit, which should be added as numerator of the new unit.
AddUnit(string, List<XmlQualifiedName>)

Adds an unit to the instance. The parameters are:

  • The ID of the new unit. The ID must be unique in the current instance.
  • A List of XmlQualifiedNames of units, which should be added as numerators of the new unit.
AddUnit(string, List<XmlQualifiedName>, List<XmlQualifiedName>)

Adds an unit to the instance. The parameters are:

  • The ID of the new unit. The ID must be unique in the current instance.
  • A List of XmlQualifiedNames of units, which should be added as numerators of the new unit.
  • A List of XmlQualifiedNames of units, which should be added as denominators of the new unit.

Facts Generation

A XBRL fact is represented by the Fact class. The XBRL Engine distinguishes between two different fact types: monetary and textual. Monetary (as per ValueCell.Constraints.ConceptConstraint.ResolvedElement.TypeNameShort definition) comprises the followings facts:

  • monetary
  • percent
  • pure
  • decimal
  • integer

Textual are these ones:

  • QName
  • enumeration
  • date
  • boolean
  • string

The Engine features two methods to add mentioned facts: AddMonetaryFact and AddTextualFactAddMonetaryFact accepts following parameters:

  • factContext: of type Context, containing the context of the fact.
  • factUnit: of  type Unit, type exposing fact's unit.
  • factElement: of type Element, specifying underlying fact's element, usually TableValueCell.Concept.
  • value: of type decimal, holding fact's value.
  • precisions: of type int, depicting the arithmetic precision of the value.
  • decimals: of type int, depicting decimal values.
  • infinateDecimals: of type boolean, indicating whether decimals should be infinated or not.
  • elementId: of type string, specifying unique fact's id.

AddTextualFact method has the following parameters:

  • factContext: of type Context,  the context of the fact.
  • factElement: of type Element, specifying underlying fact's element.
  • value: of type string, specifying the value of the fact.
  • elementId: of type string, the id of the fact.

Typically, a monetary fact can be added following way:

// value is for example the value of a cell
var value = 1.6m;
Fact fact = null;
Context context = GetContext(cell);
Unit unit = GetUnit(cell);
string factValue = string.Format("{0}", value);

decimal decimalValue = 0;
if (value is decimal)
    decimalValue = (decimal) value;
else if (value != null)
    decimal.TryParse(factValue, NumberStyles.Any, CultureInfo.InvariantCulture, out decimalValue);

string factId = string.Format("{0}_{1}_{2}_{3}", cell.Model.TableId, cell.Row.CoordinateString, cell.Column.CoordinateString, cell.Model.ZAxesKey);
factId = XmlConvert.EncodeName(factId);
if(cell.Constraints.ConceptConstraint.ResolvedElement.TypeNameShort == "decimal")
    f = instance.AddMonetaryFact(context, unit, cell.Concept, decimalValue, 2, false, factId);
TableValueCellFact cellFact = null;
if (fact != null)
    cellFact = new TableValueCellFact(fact);

GetContext and GetUnit are the helper methods for assembling a fact's unit and context respectively. As the result, cellFact could be propagated to a delegated method associated with TableModel's GetCellData delegate. Instance facts could have following look:

<eba:mi89 contextRef="c42" unitRef="EUR" decimals="-3">777777.77</eba:mi89> 
<eba:mi121 contextRef="c43" unitRef="EUR" decimals="-3">777777.77</eba:mi121> 
<eba:pi282 contextRef="c44" unitRef="Pure" decimals="4">0.1234</eba:pi282> 
<eba:pi321 contextRef="c44" unitRef="Pure" decimals="4">0.1234</eba:pi321>

Handling Duplicate Facts

XBRL defines the facts X and Y as duplicates if all of the following conditions apply:

  1. X is not identical to Y, and
  2. The element local name of X is S-Equal (Structure-equal: XML nodes that are either equal in the XML value space, or whose XBRL-relevant sub-elements and attributes are s-equal.) to the element local name of Y, and
  3. X and Y are defined in the same namespace, and
  4. X is P-Equal (Parent-equal: instance items or tuples having the same parent.) to Y, and
  5. X is C-Equal (Context-equal: Items or sets or sequences of items having the same item type in s-equal contexts.) to Y, and
  6. X is U-Equal (Unit-equal: u-equal numeric items having the same units of measurement.) to Y, and
  7. X and Y are dimensionally equivalent (d-equal in all dimensions of each of X and Y; Two facts are d-equal for one dimension if they have the same dimension value for that dimension.), and
  8. (If X and Y are strings) X and Y have S-Equal xml:lang attributes_

Duplicate facts are XML-XBRL syntax valid. However, e.g. EBA Filing Rules don't allow duplicate facts in the XBRL Instance. Duplicate facts can be detected using following snippet:

Dictionary<string, Dictionary<XmlQualifiedName, List<Fact>>> factsByContextKeyAndConceptName = new Dictionary<string, Dictionary<XmlQualifiedName, List<Fact>>>();
Dictionary<string, string> duplicates = new Dictionary<string, string>();
if (InstanceDocument == null)
    return;

foreach (Fact f in InstanceDocument.XbrlInstances.FactsWithoutTuples)
{
    string key = Context.GetContextKey(f.ContextRef);
    if (!factsByContextKeyAndConceptName.ContainsKey(key))
        factsByContextKeyAndConceptName.Add(key, new Dictionary<XmlQualifiedName, List<Fact>>());
    if (!factsByContextKeyAndConceptName[key].ContainsKey(f.ConceptQName))
        factsByContextKeyAndConceptName[key].Add(f.ConceptQName, new List<Fact>());
    factsByContextKeyAndConceptName[key][f.ConceptQName].Add(f);
}

StringBuilder sb = new StringBuilder();
int count = 0;
foreach (var context in factsByContextKeyAndConceptName)
    foreach (var concept in context.Value)
        if (concept.Value.Count > 1)
        {

            count++;
            foreach (var doubles in concept.Value)
            {
                //tracking down what duplicates are detected
                sb.Append(doubles + Environment.NewLine);
                //deleting duplicate facts from Instance object
                InstanceDocument.XbrlInstances.RemoveFact(doubles);
            }
        }
  • No labels