カテゴリー別アーカイブ: XBRL財務分析

XBRL財務分析

いろいろやってみよう

XBRLからEXCELを使って財務指標を計算する方法は一通り説明しました。後は、実践あるのみです。
実際に、さまざまな企業の有価証券報告書XBRLデータを使って財務分析をしてみてください。
例えば、特定の企業の過去の全XBRLデータを集めてきて、時系列分析をするのもよいでしょう。EDINETにはXBRLデータは2008年7月以降に開示された有価証券報告書(半期・四半期報告書)分がありますので、過去3年分くらい集めることができます。
ツールを使って四半期ごとの売上高や利益の推移をまとめ、EXCELのグラフ機能を使えば簡単・きれいに説得力のある資料を作成できることと思います。
ライバル企業のデータを集めて比較分析する、なんていうのも同じです。
EDINETだけでなく、TDnetで開示されている決算短信や業績予想などもXBRLで開示されています。ツールはEDINETのXBRL向けに作成しましたが、TDnetのXBRLでも問題なく動作しますので、投資家はXBRLデータから投資判断を自動で行うようなロジックをEXCELだけで作成することができるでしょう。

ダウンロードがめんどくさい

XBRLを使えば使うほど感じるのは、現在のEDINET/TDnetだとXBRLを集める作業がめんどくさい、という点につきます。XBRLがコンピュータで使われることを想定しているのにもかかわらず、EDINETもTDnetも、ダウンロードを手作業でやらせることを想定しています。これは、EDINETやTDnetに対して、XBRLのほうが後付けの機能なので仕方がないところもあります。
この点に関しては、[有報キャッチャーウェブサービス AtomAPI]で、簡単なAtomAPIでXBRLデータを取得できるサービスが展開されています。
とはいえ、やはり開示元であるEDINETやTDnetから1次ソースをいち早く確実に取得したい、というのが情報利用者の本音だと思います。
2013年に稼動が予定されている次世代EDINETでは、この点が改善されることを大いに期待しています。

総資産当期純利益率(ROA)

総資産当期純利益率(ROA)を計算します。
引き続き、サンプルデータには、三菱重工の2011年3月期の有価証券報告書のXBRLデータを使います。
有報キャッチャーからも直接ダウンロードできます[直リンク])
ここでは、ROAの計算式は、以下のとおりとします。

ROA = 純利益 ÷ 総資産 × 100

必要な数値は、「純利益」と「総資産」です。勘定科目リストから、「純利益」は『NetIncome』、「総資産」は『Assets』がよさそうです。
注意すべきはコンテキストです。NetIncomeは期間コンテキスト、Assetsは時点コンテキストですので、以下のように入力すればよさそうです。(緑のセルは計算式です)
総資産当期純利益率(ROA)1
計算結果は以下のようになります。
総資産当期純利益率(ROA)2
うん、ばっちり。
それにしても三菱重工、昨年に比べると資産を圧縮する一方で純利益が倍増したので、ROAがずいぶん改善したのがわかります。それでも低いけど。ちなみに筆者はもう5年以上もこの会社の株主です(ちょっとだけどね)。

もう少しだけ厳密に

ところで、ROAのように、財務指標の計算で期間コンテキストの値と時点コンテキストの値を両方用いるときは、時点コンテキストの値は、期間の開始時点と終了時点の平均をとる場合があります。(ちょっとググってみたら、アルパカの会社なんかが、採用していました)
ROAでいうと、計算式が以下のようになります。

ROA = 純利益 ÷ ((総資産(期首時) + 総資産(期末時)) ÷ 2) × 100

ROAの理論としては、こちらのほうがより厳密(理論に近い)と言えます。
この計算式で計算してみましょう。
総資産当期純利益率(ROA)3
さて、期首のコンテキストはどうすればよいでしょう。コンテキストの命名規約を見ても、時点コンテキストは各期間コンテキストの終了時点しか存在しません。
結論から言うと、期首時点コンテキストは、その前の期間の期末時点コンテキストになります。
例えば、『今期の期首のコンテキスト = 前期末のコンテキスト』なのです。
基本的な会計の知識があれば、当たり前ですね。前期末の現金残高が今期の期首の現金残高は同じでしょ、と。
この点、XBRLの仕様上は以下のように記述されています。

instant 要素の内容に時刻指定がない日付を指定したときには,同日のT00:00:00 のP1D後,すなわち,一日後をdateTime として指定したものとする。これは,日が終了する午後12 時を表す。

ちょっとわかりづらいのですが、「instant要素の内容に時刻指定がない日付を指定したとき」というのが、時点コンテキストになります。時点コンテキストの時間指定がない場合(普通はない)は、その日の終了する午後12時、つまりその日から次の日に変わる瞬間、を表す、ということなのです。
具体的な例で見てみましょう。
ある期間コンテキストのinstant要素が「2011-03-31」であった場合、それは、2011年3月31日から2011年4月1日に変わる瞬間を意味します。それは、2011年3月31日で終わる期間の期末時点であり、かつ、2011年4月1日から始まる期間の期首時点でもあるわけです。
では、CurrentYearConsolidatedDuration(今年度・連結・期間)の期首時点のコンテキストはなんでしょうか?それは、前年度・連結・時点コンテキスト、すなわち「Prior1YearConsolidatedInstant」です。さらに、Prior1YearConsolidatedDuration(前年度・連結・期間)の期首時点のコンテキストは、前々年度・連結・時点コンテキスト、すなわち「Prior2YearConsolidatedInstant」となるわけです。
総資産当期純利益率(ROA)4
今期のROAは計算できましたが、前期のROAがエラーになっています。このXBRLインスタンスには、Prior2YearConsolidatedInstantのAssetsのファクトが存在しないようです。
現在では、EDINETの有価証券報告書のXBRLには、財務諸表の本表部分の数値が記述されているだけです。Assetsは貸借対照表上の数値ですが、貸借対照表は今期と前期の数値しかありません。ですので、前々期のAssetsは存在しないのです。
前期(2010年3月期)のROAを計算するためには、2010年3月期の有価証券報告書のXBRLを使って同じ計算をすればよいのです。三菱重工の2010年3月期のXBRLインスタンスはURL「http://resource.ufocatch.com/xbrl/edinet/ED2010062400796/jpfr-asr-E02126-000-2010-03-31-01-2010-06-24.xbrl」で直接参照することができます。セルA1にこのURLを入力するだけで再計算されますので、興味がある方は、ぜひご自分で試してみてください。

売上高経常利益率

売上高経常利益率を計算します。
引き続き、サンプルデータには、三菱重工の2011年3月期の有価証券報告書のXBRLデータを使います。
有報キャッチャーからも直接ダウンロードできます[直リンク])
売上高経常利益率の計算式は、以下のとおりです。

売上高経常利益率 = 経常利益 ÷ 売上高 × 100

必要な数値は、「経常利益」と「売上高」です。
対応する要素を決めますので、勘定科目リストから、「経常利益」と「売上高」に該当しそうな要素を探していきます。「経常利益」は『OrdinaryIncome』、「売上高」は『NetSales』がよさそうです。
では、計算式がほとんど同じなので、流動資産のときのシートをコピーして、売上高経常利益率を計算してみます。分子のCurrentAssetsをOrdinaryIncomeに、分母のCurrentLiabilitiesをNetSalesに変更すればよさそうですね。
売上高経常利益率を
あれ?エラーになりました。
そういえば、コンテキストの確認を忘れていました。
OrdinaryIncomeやNetSalesは、勘定科目リストによると、periodTypeがdurationになっています。CurrentAssets/CurrentLiabilitiesはinstantでした。ということは、コンテキストが違うので修正する必要がありそうです。コンテキストの命名規約によると、時点コンテキストを期間コンテキストに直すには、最後の「Instant」を「Duration」に変更するだけでよさそうです。
売上高経常利益率2
これでよさそうですね。
PDFの有価証券報告書と同じ(正しい)数値が取得できていることが確認できます。

コンテキストを決める

インスタンスファイルから要素を指定してファクトを取り出すと、複数のファクトが出てきます。これらは同じ要素のコンテキスト違いのファクトです。ファクトは、要素名とコンテキストでユニークになっています。
 コンテキストとは、日本語で文脈という意味です。XBRLでは、そのファクトがどのような文脈(事業体・報告期間・シナリオ)に属するのかを示しています。
EDINETで開示されるXBRLデータでは、ガイドラインによってコンテキストの命名ルールが決められています。

EDINETのコンテキスト命名ルール

引用元:報告書インスタンス作成ガイドライン

コンテキストID:{相対期間又は時点}{期間又は時点}((_{メンバーの要素名})×n)(_{連番3 桁})

相対期間又は時点

設定値 説明
CurrentYear 当年度
Interim 中間期
Prior1Year 前年度
Prior1Interim 前中間期
Prior2Year 前々年度
Prior2Interim 前々中間期
Prior{数値}Year {数値}年度前
Prior{数値}Interim {数値}年度前中間期
CurrentYTD 当四半期累計期間
CurrentQuarter 当四半期会計期間
Prior{数値}YTD {数値}年度前同四半期累計期間
Prior{数値}Quarter {数値}年度前同四半期会計期間
FilingDate 提出日
RecordDate 議決権行使の基準日
RecentDate 最近日

期間又は時点

設定値 説明
Instant 時点
Duration 期間

ファクトが期間のコンテキストに属するか、時点のコンテキストに属するかは、要素のperiodType属性によって決まります。基本的には、BS科目(資産・負債・資本に属する勘定科目の要素)はある時点の状態を表す概念なので、時点コンテキストに属し、PL科目(収益・費用に属する勘定科目の要素)はある期間の成績を表す概念なので、期間コンテキストに属します。

例えば、流動比率の計算のときの「CurrentYearInstant」は、その有価証券報告書の『当年度・連結・時点』、「Prior1YearInstant」は、『前年度・連結・時点』を表すコンテキストです。CurrentAssetsやCurrentLiabilitiesが時点コンテキストに属すため、これらのコンテキストのファクトを抜き出しています。
コンテキスト

要素を決める

財務指標を計算するためには、各指標の計算に必要な項目に該当する要素を決めなければいけません。流動比率であれば、「流動資産」と「流動負債」に該当する要素を決めます。

タクソノミから候補の要素を見つける

要素を決めるためには、まずどのような要素が存在しているかを調べて、その中から適切なものをピックアップしていきます。

XBRLでは、要素はタクソノミに定義されています。各企業が独自に増やすこともできますが、一般的な勘定科目はあらかじめタクソノミに定義されており、XBRLインスタンスの作成者はできるだけその中から適切な科目を選んで利用することが推奨されています。ですから、一般的な財務指標の計算については、独自に拡張されるものは無視して問題ないと思われます。(どのような名前の要素になるか予想できませんので、現実的には無視するしかない)

では、EDINETのタクソノミを見てみましょう、、、といいたいところですが、EDINETのタクソノミの場合には、金融庁が「勘定科目リスト」を提供してくれてますので、手っ取り早くそちらを参照すると便利です。

勘定科目リストを開くとB列からG列がその要素に紐付けられているラベル(表示名)です。そのうちB列の日本語標準ラベルとC列の日本語冗長ラベルを見れば、どのような要素かは想像がつきます。
「流動資産」に該当しそうな科目を探していくと、いきなり「一般商工業」シートの7行目B列に『流動資産』というラベルがあります。しかし、この科目はC列が『流動資産、タイトル項目』となっていることから、実際に金額を持つ要素ではなく、タイトル行として使用される要素です。(abstractが『true』であることが金額を持たない要素であることを示しています)
さらに見ていくと、123行目B列にまたも『流動資産』というラベルがあります。C列も『流動資産』となっており、どうやらこれが探していた要素のようです。要素名は『CurrentAssets』となっています。
同様に「流動負債」を探すと、457行目の『CurrentLiabilities』が見つかります。

実データで確かめる

候補としてピックアップした要素が適切かどうか実際のデータで確かめていきます。
確かめる方法は、実データのなかからいくつかをサンプリングして、その要素のファクト(値)を抽出したものと、EDINETのHTMLやPDFで記述されている財務諸表の値とを比較していく、という地味なものです。
例えば、商船三井の2018年3月期の有価証券報告書のケースを見てみると、ツールで取得した値(下図)と、有価証券報告書(PDFファイル)の連結貸借対照表の流動資産・流動負債の金額を見比べると、きちんと一致していることがわかります。
計算結果

流動比率を計算してみる

作成したツールを利用して、代表的な安全性指標である流動比率を計算してみます。
[ツールのダウンロード]

流動比率(%) = 流動資産 ÷ 流動負債 × 100

サンプルデータには、商船三井の2018年3月期の有価証券報告書のXBRLデータを使います。XBRLデータはEDINETからダウンロードしておいてください。
有報キャッチャーからも直接ダウンロードできます[直リンク])

手順

  1. ダウンロードしたXBRLデータを解凍し、PublicDocフォルダの下にあるインスタンスファイル(jpcrp030000-asr-001_E04236-000_2018-03-31_01_2018-06-26.xbrl)をツール(EXCELファイル)と同じフォルダに置く。
  2. ツール(EXCELファイル)を開く
  3. 新規シートを追加し、そのシートに下の図のように入力します。計算式
  4. 計算結果は以下のようになります。
    計算結果
    もし、計算結果がエラー(#N/A)になった場合、A1のインスタンスファイルをフルパス(C:\~から)で指定してください。

セルの入力内容

  • A1:インスタンスファイル名
  • B2:コンテキスト「連結の今期末時点」のコンテキストID
  • C2:コンテキスト「連結の前期末時点」のコンテキストID
  • A3:要素「流動資産」の要素名
  • A4:要素「流動負債」の要素名
  • B3:C4:インスタンスファイルから「同列のコンテキスト」の「同行の要素」のファクト(値)を取得するワークシート関数
  • B5:C5:流動比率の計算式

説明

行方向に要素名、列方向にコンテキストIDを並べて、CurrentAssets(流動資産)とCurrentLiabilities(流動負債)の値(ファクト)を取得し、流動比率を計算しています。
XBRLで財務指標を計算する場合、最も難しいのが要素名の決定です。
流動比率の場合、必要なデータは「流動資産」と「流動負債」です。ある程度会計の知識がある人が貸借対照表を見れば、流動資産と流動負債の金額を見つけるのは簡単です。しかし、そこは融通の利かないコンピュータ、XBRLから抜き出すためには、きっちり要素名を指定してあげる必要があります。ここでは「流動資産」は「CurrentAssets」、「流動負債」は「CurrentLiabilities」、として計算しました。
では、どうすれば正しい要素名を見つけられるのでしょうか。

完成したツールと使い方

完成したツールは、こちらからダウンロードできます。

ツールには、これまでに説明したワークシート関数を使って、四半期ごとの最近3年間の売上高や利益などを取得するサンプルが設定されています。
このワークシート関数を別のEXCELファイルでも使いたい、という場合には、アドイン化しておくと便利です。

ファクト取得関数

続いて、ファクト取得関数を実装します。

Function XBRLFact(ByVal instanceFilePath As String, Optional ByVal ContextID As String = "", Optional ByVal namespaceURI As String = "", Optional ElementName As String = "")

    On Error GoTo ErrorHandler
    
    Dim inst As TInstance
    Dim i As Integer
    Dim result() As Variant
    Dim selectedFacts() As TFact
    
    'インスタンスを取得する
    inst = ParseInstanceFile(instanceFilePath)
    'インスタンスのファクトから、出力対象のファクトを選択する
    selectedFacts = FilterFact(inst.Facts, ContextID, namespaceURI, ElementName)
    
    '結果を格納する2次元配列の大きさでReDimする。
    '該当のデータが見つからなかったら、UBound(facts)でエラーになるため、ErrorHandlerに飛ぶ
    ReDim result(0 To UBound(selectedFacts), 0 To 6)
    For i = 0 To UBound(selectedFacts)
        result(i, 0) = selectedFacts(i).Value
        result(i, 1) = selectedFacts(i).ElementName
        result(i, 2) = selectedFacts(i).ElementNamespace
        result(i, 3) = selectedFacts(i).ContextID
        result(i, 4) = selectedFacts(i).Nil
        result(i, 5) = selectedFacts(i).Unit
        result(i, 6) = selectedFacts(i).Decimals
    Next
    
    XBRLFact = result
    Exit Function
    
ErrorHandler:
    Err.Clear
    XBRLFact = CVErr(xlErrNA)
    
End Function

'ファクトの配列から、対象のファクトを選択する
Private Function FilterFact(Facts() As TFact, ContextID As String, namespaceURI As String, ElementName As String) As TFact()
    
    Dim i As Integer
    Dim j As Integer
    Dim selectedFacts() As TFact
    
    For i = LBound(Facts) To UBound(Facts)
        If (ContextID = "" Or Facts(i).ContextID = ContextID) And _
           (namespaceURI = "" Or Facts(i).ElementNamespace = namespaceURI) And _
           (ElementName = "" Or Facts(i).ElementName = ElementName) Then
        
            ReDim Preserve selectedFacts(0 To j)
            selectedFacts(UBound(selectedFacts)) = Facts(i)
            j = j + 1
        
        End If
    Next
    
    FilterFact = selectedFacts
    
End Function

こちらもXBRLの解析が完了しているインスタンスが取得できてしまえば、きわめて簡単です。メンバー変数であるファクトの配列から、条件に合致するファクトを選別し、出力しています。ファクトの配列から条件に合致するファクトだけを選別するため、下請関数FilterFactを作成しました。
なお、この関数をワークシート関数として使う場合、配列数式として入力する必要があります。配列数式として入力するためには、ワークシートの出力範囲のセルを選択して、F2 キーを押し、Ctrl キーと Shift キーを押しながらEnter キーを押します。この数式が配列数式として入力されていない場合、単一の値(1行1列目の値)のみが計算結果として返されます。

コンテキスト取得関数

XBRLインスタンスファイルを解析する関数が完成したので、ユーザがワークシートに入力して呼び出すことができるワークシート関数を実装していきます。
最初に、コンテキスト取得関数を実装します。

Function XBRLContext(ByVal instanceFilePath As String, Optional ByVal ContextID As String = "")

    On Error GoTo ErrorHandler
    
    Dim inst As TInstance
    Dim i As Integer
    Dim j As Integer
    Dim result() As Variant
    
    'インスタンスを取得する
    inst = ParseInstanceFile(instanceFilePath)
    
    If (ContextID = "") Then
        'コンテキストIDを省略した場合、n行6列(n:インスタンスのコンテキスト配列の長さ)
        ReDim result(0 To UBound(inst.Contexts), 0 To 5)
    Else
        'コンテキストIDを指定した場合、1行6列
        ReDim result(0 To 0, 0 To 5)
    End If
    
    '配列にコンテキスト情報を出力する
    For i = 0 To UBound(inst.Contexts)
        If (ContextID = "" Or inst.Contexts(i).ContextID = ContextID) Then
            result(j, 0) = inst.Contexts(i).ContextID
            result(j, 1) = inst.Contexts(i).StartDate
            result(j, 2) = inst.Contexts(i).EndDate
            result(j, 3) = inst.Contexts(i).Instant
            result(j, 4) = inst.Contexts(i).Scenario
            result(j, 5) = inst.Contexts(i).ScenarioNonConsolidate
            j = j + 1
        End If
    Next
    
    If (j = 0) Then
        'j=0の場合は、出力対象のコンテキストが見つからなかったケース
        'その場合は、#N/A を返す
        XBRLContext = CVErr(xlErrNA)
    Else
        XBRLContext = result
    End If
    Exit Function
    
ErrorHandler:
    Err.Clear
    XBRLContext = CVErr(xlErrNA)
End Function

XBRLの解析が完了しているインスタンスが取得できてしまえば、きわめて簡単です。メンバー変数であるコンテキストの配列から、名前が一致する(引数が省略された場合は全ての)コンテキストを選別し、出力します。

出力結果を格納するresult()は、2次元配列です。結果をワークシートに出力する場合、最初の添え字が行方向に展開されて、2つめの添え字が列方向に展開されます。

なお、この関数をワークシート関数として使う場合、配列数式として入力する必要があります。配列数式として入力するためには、ワークシートの出力範囲のセルを選択して、F2 キーを押し、Ctrl キーと Shift キーを押しながらEnter キーを押します。この数式が配列数式として入力されていない場合、単一の値(1行1列目の値)のみが計算結果として返されます。

XBRLインスタンスファイル解析

ここから実際にEXCEL VBAでの実装の説明に入ります。

ユーザ定義型

まず、ワークシート関数を実装する前に、XBRLインスタンスファイルをユーザ定義型の変数に読み込む処理を実装していきます。
最初に必要なユーザ定義型を3つ定義します。

'コンテキスト
Public Type TContext
    'コンテキストID
    ContextID As String
    '開始日
    StartDate As Variant
    '終了日
    EndDate As Variant
    '基準日
    Instant As Variant
    'シナリオ
    Scenario As Variant
    'NonConsolidateシナリオかどうか
    ScenarioNonConsolidate As Boolean
End Type

'ファクト
Public Type TFact
    '要素名
    ElementName As String
    '名前空間
    ElementNamespace As String
    'コンテキストID
    ContextID As String
    '値
    Value As Variant
    '単位
    Unit As Variant
    'Nil属性
    Nil As Boolean
    '精度(decimals属性)
    Decimals As Variant
End Type

'XBRLインスタンス
Public Type TInstance
    'XBRLインスタンスのファイルパス
    FilePath As String
    'コンテキスト
    Contexts() As TContext
    'ファクト
    Facts() As TFact
End Type

TContext型の開始日・終了日・基準日を(Date型にせず)Variant型にしたのは、VBAではDate型の初期値の関係で未設定の場合の扱いが面倒になるからです。値を設定するさいに、Date型の値を設定し、未設定の状態では初期値のEmptyとなるようにします。
TContext型のScenarioNonConsolidateは、シナリオにNonConsolidate要素が含まれているかを示します。EDINETのXBRLインスタンスにおいては、コンテキストのシナリオにNonConsolidate要素が含まれているということは、非連結(単独)のコンテキストであることを示します。

TFact型の値は、要素によって、文字列だけでなく日付・真偽・金額などが設定されるため、Variant型にしています。XBRLインスタンスから読み取った文字列の形式により、適当に型変換して設定します。

TInstance型は、XBRLインスタンスの情報を保持します。インスタンスには、コンテキストとファクト以外にも、参照しているスキーマのリファレンスや、単位やフットノートなどが記述されていますが、このツールでは意図的に無視しています。コンテキストとファクトは動的配列で保持します。

XMLファイルの読み込み

XBRLインスタンスはXMLで記述されています。本ツールでは、Microsoft.XMLDOMを使ってXMLを処理することにします。

'パースしたXBRLインスタンスを保持しておく領域
Private parsedInstanceFile(1024) As TInstance

'指定されたパスのインスタンスファイルを読み込んでパースし、
'XBRLインスタンス型にして変換する
Function ParseInstanceFile(ByVal path As String) As TInstance

    'モジュールレベル変数に保持されているかチェックし、
    '保持されている場合にはそれを返す
    Dim i As Integer
    
    For i = 0 To UBound(parsedInstanceFile)
        If (parsedInstanceFile(i).FilePath = path) Then
            ParseInstanceFile = parsedInstanceFile(i)
            Exit Function
        End If
        'FilePathが""のものがあったら、その先はないのでExitする。
        If (parsedInstanceFile(i).FilePath = "") Then
            Exit For
        End If
    Next

    'XMLオブジェクト(インスタンスファイルをセットする)
    Dim xml As Object
    'パースしたインスタンスを設定する
    Dim newInstance As TInstance
        
    'XMLファイル(インスタンスファイル)をロードする
    Set xml = CreateObject("Microsoft.XMLDom")
    '同期処理にする(ロードが完了するまで処理を待つ)
    xml.async = False
    'ロードする
    xml.Load path
    'インスタンスのメンバーを設定
    newInstance.FilePath = path
    newInstance.Contexts = GetContexts(xml)
    newInstance.Facts = GetFacts(xml)
    
    'モジュールレベル変数に保管しておく
    If (i <= UBound(parsedInstanceFile)) Then
        parsedInstanceFile(i) = newInstance
    End If
    
    Set xml = Nothing
    ParseInstanceFile = newInstance
    
End Function

ParseInstanceFile関数は、引数でXBRLインスタンスのパスを受け取り、そのパスのファイルをロードして解析(パース)して、TInstance型に格納します。
XMLをロードして解析する処理は重くなるため、一度解析したらモジュールレベルの変数に保存し、同じパスのファイルは何度も解析しないようにします。なお、保存する配列の長さは1025(添字0~1024まで)となっていますが、この長さに深い意味はありません。

この関数のキモは、TInstance型の変数 newInstance のメンバーContextsとFactsに値を設定するところですが、それはそれぞれ下請け関数のGetContexts/GetFacts関数で行います。

コンテキストの取得

XBRLインスタンスをロードしたXMLオブジェクトからコンテキスト(の配列)を取得します。

'インスタンスファイルのXMLオブジェクトから、コンテキストを取得する
Function GetContexts(ByVal xml As Object) As TContext()

    Dim nodeContexts As Collection
    Dim result() As TContext
    Dim idxNode As Object
    Dim i As Integer
    
    Set nodeContexts = myGetElementsByTagNameNS(xml, NAMESPACE_XBRLI, "context")
    
    ReDim result(0 To nodeContexts.Count - 1)
    i = 0
    For Each idxNode In nodeContexts
        result(i) = ToContext(idxNode)
        i = i + 1
    Next
    GetContexts = result

End Function

'XMLノードからTContextに変換する
Function ToContext(ByVal xml As Object) As TContext
    Dim result As TContext
    Dim element As Object
    Dim dateElements As Collection
    Dim att As Object
    Dim scenarioElements As Collection
    
    'コンテキストIDの設定
    For Each att In xml.Attributes()
        If (att.baseName = "id") Then
            result.ContextID = att.Text
            Exit For
        End If
    Next
    
    '開始日の設定
    Set dateElements = myGetElementsByTagNameNS(xml, NAMESPACE_XBRLI, "startDate")
    If (dateElements.Count > 0) Then
        result.StartDate = CDate(dateElements(1).Text)
    End If
    
    '終了日の設定
    Set dateElements = myGetElementsByTagNameNS(xml, NAMESPACE_XBRLI, "endDate")
    If (dateElements.Count > 0) Then
        result.EndDate = CDate(dateElements(1).Text)
    End If
    
    '基準日の設定
    Set dateElements = myGetElementsByTagNameNS(xml, NAMESPACE_XBRLI, "instant")
    If (dateElements.Count > 0) Then
        result.Instant = CDate(dateElements(1).Text)
    End If
        
    '非連結メンバー(NonConsolidateMember)かどうかの設定
    Set scenarioElements = myGetElementsByTagNameNS(xml, NAMESPACE_XBRLI, "scenario")
    If (scenarioElements.Count > 0) Then
        result.Scenario = scenarioElements(1).Text
        For Each element In scenarioElements(1).getElementsByTagName("*")
            If (element.Text Like "*NonConsolidatedMember*") Then
                result.ScenarioNonConsolidate = True
                Exit For
            End If
        Next
    End If
    
    ToContext = result
    
End Function

GetContexts関数は、XMLオブジェクトから、名前空間「http://www.xbrl.org/2003/instance」、名称「context」のノードを取得します。この各ノードが1つのTContext型の変数に該当します。

contextノードは、典型的には以下のようなXMLノードです。

<xbrli:context id="CurrentYearDuration_NonConsolidatedMember">
    <xbrli:entity>
        <xbrli:identifier scheme="http://disclosure.edinet-fsa.go.jp">E32364-000</xbrli:identifier>
    </xbrli:entity>
    <xbrli:period>
        <xbrli:startDate>2016-07-01</xbrli:startDate>
        <xbrli:endDate>2017-06-30</xbrli:endDate>
    </xbrli:period>
    <xbrli:scenario>
        <xbrldi:explicitMember dimension="jppfs_cor:ConsolidatedOrNonConsolidatedAxis">jppfs_cor:NonConsolidatedMember</xbrldi:explicitMember>
    </xbrli:scenario>
</xbrli:context>

ToContext関数は、各contextノードからTContext型の変数に変換します。id属性の値をContextIDに、startDateタグの値をStartDateに、endDateタグの値をEndDate、Instantタグの値をInstant、scenarioタグの内容をScenarioにそれぞれ設定していきます。scenarioタグの中にNonConsolidatedMember(非連結メンバー)が含まれるかチェックし、含まれる場合にはScenarioNonConsolidateの値をTrue(非連結)にしています。

ファクトの取得

XBRLインスタンスをロードしたXMLオブジェクトからファクト(の配列)を取得します。

'インスタンスファイルのXMLオブジェクトから、ファクトを取得する
Function GetFacts(ByVal xml As Object) As TFact()

    Dim xbrlNodes As Object
    Dim result() As TFact
    Dim element As Object
    Dim nodeFacts As Collection
    Dim idx As Integer
    
    Set nodeFacts = New Collection
    'xbrlのノード(ルートノード)を取得する
    Set xbrlNodes = myGetElementsByTagNameNS(xml, NAMESPACE_XBRLI, "xbrl")
    If (xbrlNodes.Count > 0) Then
        'ルートノードの子ノードには、ファクトのほか、コンテキストやフットノートリンクやSchemaRefなどが定義される。
        'それらの名前空間は決まっているため、それ以外の名前空間に属したものをファクトとする。
        For Each element In xbrlNodes(1).ChildNodes()
            If (element.namespaceURI <> NAMESPACE_LINKBASE And _
                element.namespaceURI <> NAMESPACE_XBRLI) Then
                nodeFacts.Add element
            End If
        Next
    End If
    
    ReDim result(0 To nodeFacts.Count - 1)
    idx = 0
    For Each element In nodeFacts
        result(idx) = ToFact(element)
        idx = idx + 1
    Next
    GetFacts = result
    
End Function

'XMLノードからTFactに変換する
Function ToFact(ByVal xml As Object) As TFact
    Dim result As TFact
    Dim att As Object
    
    result.ElementName = xml.baseName
    result.ElementNamespace = xml.namespaceURI
    For Each att In xml.Attributes()
        If (att.baseName = "contextRef") Then
            result.ContextID = att.Text
        ElseIf (att.baseName = "unitRef") Then
            result.Unit = att.Text
        ElseIf (att.baseName = "decimals") Then
            result.Decimals = att.Text
        ElseIf (att.namespaceURI = NAMESPACE_XMLSI And att.baseName = "nil") Then
            result.Nil = CBool(att.Text)
        End If
    Next
    result.Value = IIf(result.Nil, CVErr(xlErrNull), ToValue(xml.Text))
    
    ToFact = result
End Function

'TFactのValueに設定する値を、文字列の形式から適切な型に変換して返す
Private Function ToValue(ByVal str As String) As Variant

    If (IsNumeric(str)) Then
        ToValue = Val(str)
    ElseIf (UCase(str) = "TRUE" Or UCase(str) = "FALSE") Then
        ToValue = CBool(str)
    ElseIf (IsDate(str)) Then
        ToValue = CDate(str)
    Else
        ToValue = str
    End If
    
End Function

GetFacts関数は、XMLオブジェクトから、xbrlノード(ルートノード)の子ノードのうちFactを表すノードを取得します。各ノードが1つのTFact型の変数に該当します。

ToFact関数は、そのノードをTFactに変換します。contextRef属性、unitRef属性、decimals属性、nil属性の値をそれぞれメンバー変数に格納し、ノードのTextをValueに設定します。Value設定するときに、ToValue関数で文字列のフォーマットからデータ型を判定し、キャストしています。