RでEDINETから財務データを入手・分析する~⑥前処理編3/3~

記事一覧

はじめに

本記事は前処理編2/3の続きであり、6番目の記事にあたる。本記事で行う作業は、前2記事の内容を統合した関数を定義し、実際にzipファイルを分析可能なデータへと整頓することである。

また、本ブログの執筆に際してこちらの記事を参考にさせていただいた。

考慮事項

詳細な流れはRによる実装の手順に回し、ここでは分析にあたって考慮すべき事項、説明すべき事項を挙げる。

labelStringについて

前記事で、xbrlDoAll()の処理は1分程度かかるとした。筆者の試行錯誤の結果、処理時間のボトルネックxbrlProcessLabels()だと特定した。この関数は前処理編1/3で実行済みのため、処理速度の改善が行えた。

会計基準について

導入編で挙げた通り、本ブログでは日本基準を採用する上場企業のみを抽出することを考える。IFRSとUSGAAPに従う企業のデータを抽出はしないが、どの企業がそれらに従っているかをデータとして保持しておくことは有用と考えられるから、その点をカバーする。

連結財務諸表について

連結財務諸表を作成する上場企業は、有価証券報告書の中で個別と連結2つの財務諸表を報告している。よってxbrlファイル内にも、その両方の情報が混在している。一方で、上場企業には子会社を持たず、連結財務諸表を作成していない企業も存在する。それらの企業にはxbrlファイル内の個別財務諸表の情報のみしかない。

そこで、連結財務諸表を作成する企業は連結財務諸表を、そうではない企業は個別財務諸表を抽出する必要があり、その判断をしなければならない。具体的には、「個別財務諸表にあたらない行」を含む(nrow() > 0)なら連結、そうでないなら(nrow() == 0)個別を取得するようにする。

Rによる実装

手順

詳細な流れは以下の通りである。

  1. for文で使いまわす情報である財務諸表名、提出日の年、labelStringを含むdata.frame(JGAAP_label_YYYY.csv)を用意する。

  2. zipファイルを解凍し、xbrlファイルへのpathを取得する。

  3. fact、context、採用する会計基準を取得する。

  4. 会計基準で場合分けする。メインの処理は日本基準の採用企業である。

  5. fact、context、labelStringをjoinし、連結財務諸表作成企業であるか判断する。その後、判断に応じて適切なdefinitionとjoined_fclを統合したjoined_dataを作成する。

  6. joined_dataに最後の調整を加える。

  7. joined_tidy_dataのlabelStringに欠損があるか調べる*1。欠損がない場合は"docID.csv"としてtidyupフォルダに保存する。ある場合はエラーを出す。

  8. 日本基準を採用していない場合は、そのdocIDと採用する会計基準のみをもつ"**_docID.txt"として保存する。

  9. 解凍後のXBRLファイル群の削除、for文内の変数のクリーンアップを行い、進捗状況をconsoleに表示する。

  10. 2~9の内容を繰り返す。

コード

上記の手順に倣った関数を以下に与える。

tidyup_data <- function(path_zipdata, path_label, path_tidyup, start, end){
  
  print(str_c("start!", now(), sep = ", "))
  
  i <- as.integer(start - 1L)
  
  fs_name <- c("BalanceSheet", "StatementOfIncome", "StatementOfCashFlows",
               "StatementOfComprehensiveIncome", "StatementOfChangesInEquity")

  path_zip <- dirname(path_zipdata) %>% unique()

  filing_year <- 
    path_zip %>%
    str_split("/", simplify = TRUE) %>% 
    str_subset("filing$") %>% 
    str_sub(1L, 4L)
  
  ## label
  label <- 
    dir(path_label, filing_year, full.names = TRUE) %>% 
    str_subset("\\.csv$") %>% 
    read_csv()
  
  for(zip in path_zipdata[start:end]) {
    
    unzip(zipfile = zip,
          exdir = path_zip)
    
    i <- i + 1L
    
    docID <- get_docID(zip, path_zip)
    
    path_xbrl <- 
      dir(str_c(path_zip, "/XBRL/Publicdoc"), pattern = "\\.xbrl", full.names = T) %>% 
      str_subset("ifrs", negate = TRUE)
    
    ## fact
    joined_fact <- get_fact(path_xbrl)
    
    ## context
    joined_context <- get_context(path_xbrl)
    
    ## accounting_standard
    ac_standard <- get_ac_standard(joined_fact)
    
    
    if(ac_standard == "Japan GAAP"){
      
      joined_fcl <- join_fcl(joined_fact, joined_context, label)
      
      ## consolidated or NOT
      if(is_consolidated(joined_fcl)){
        
        ## definition
        joined_definition <- 
          get_definition(path_xbrl, fs_name) %>% 
          slice(str_which(.$roleId, "Consolidated"))
        
        
        joined_data <- 
          joined_fcl %>% 
          left_join(joined_definition, by = "elementId") %>% 
          slice(str_which(.$elementId, "jppfs")) %>% 
          slice(str_which(.$contextId, "NonConsolidated", negate = TRUE)) 
        
      } else {
        
        ## definition
        joined_definition <- 
          get_definition(path_xbrl, fs_name) %>% 
          slice(str_which(.$roleId, "Consolidated", negate = TRUE))  # remove consolidated
        
        
        joined_data <- 
          joined_fcl %>% 
          left_join(joined_definition , by = "elementId") %>%   
          slice(str_which(.$elementId, "jppfs"))
        
      }
      
      joined_tidy_data <- tidy_joined_data(joined_data)
      
      NA_flag <-
        joined_tidy_data %>%
        filter(is.na(labelString)) %>% 
        nrow()
      
      if(NA_flag == 0) {
        
        # save data
        joined_tidy_data %>% 
          write_excel_csv(path = str_c(path_tidyup, "/", docID, ".csv"))
        
      } else {
        
        stop(str_c("No.", i, " there are NA rows in labelString!"))
        
      }
      # JGAAP finished
      
    } else if(ac_standard == "IFRS"){
      
      # save as "IFRS_**.txt"
      tibble(
        docID = docID,
        ac_standard = "IFRS"
      ) %>% 
        write_tsv(path = str_c(path_tidyup, "/", "IFRS_", docID, ".txt"))
      
    } else if(ac_standard == "US GAAP"){
      
      # save as "USGAAP_**.txt"
      tibble(
        docID = docID,
        ac_standard = "US GAAP"
      ) %>% 
        write_tsv(path = str_c(path_tidyup, "/", "USGAAP_", docID, ".txt"))
      
    }
    
    ## clean up
    unlink(str_c(path_zip, "/XBRL"), recursive = TRUE)
    unlink("xbrl.Cache", recursive = TRUE)
    rm(list = ls(pattern = "path_xbrl|^joined|^docID$|^ac_standard$|NA_flag"))
    invisible(gc()) ; invisible(gc())
    
    print(str_c(i, now(), sep = ", "))
    
    # repeat
  }
  
}

tidyup_data(path_zipdata, path_label, path_tidyup, start = 1L, end = length(path_zipdata))

tidyupフォルダ内に"「企業のdocID」.csv"が保存されていれば成功である。以下にその例示を与える。

labelString fact startDate endDate elementId contextId roleId
現金及び預金 1093117000 NA 2018-03-31 CashAndDeposits Prior1YearInstant ConsolidatedBalanceSheet
現金及び預金 2184165000 NA 2019-03-31 CashAndDeposits CurrentYearInstant ConsolidatedBalanceSheet
受取手形及び売掛金 4267755000 NA 2018-03-31 NotesAndAccountsReceivableTrade Prior1YearInstant ConsolidatedBalanceSheet
受取手形及び売掛金 5012113000 NA 2019-03-31 NotesAndAccountsReceivableTrade CurrentYearInstant ConsolidatedBalanceSheet
電子記録債権 746345000 NA 2018-03-31 ElectronicallyRecordedMonetaryClaimsOperatingCA Prior1YearInstant ConsolidatedBalanceSheet
電子記録債権 992825000 NA 2019-03-31 ElectronicallyRecordedMonetaryClaimsOperatingCA CurrentYearInstant ConsolidatedBalanceSheet

なお、日本基準以外を採用する場合の例示は以下である。

docID ac_standard
S100GBVE IFRS

おわりに

次回の記事では、本記事までで取得したデータに企業名や業種の情報を加えて分析する。

*1:laberStringはelementIdをkeyとしてjoinしている。ここで、企業が予め用意されていないelementIdを使っていると、joinできずに欠損となる。2020.6.29に提出されたデータに試してみたところ、繰延税金資産のelementIdについて、4番目の記事で入手したlabelのデータに含まれないelementIdを用いている例を発見した。その場合、"there are NA rows in labelString!"というエラーを吐くようにしている。

RでEDINETから財務データを入手・分析する~⑤前処理編2/3~

記事一覧

はじめに

本記事は前処理編1/3の続きであり、5番目の記事にあたる。本記事で行う作業は、xbrlファイルからのデータ抽出のために必要な機能を補助関数として定義することである。

また、本ブログの執筆に際してこちらの記事を参考にさせていただいた。

本記事の概説

実際の整頓の流れの解説は次の記事に回す。本記事では8つの補助関数を定義しているが、それぞれをどうやって使うかを理解する必要はまだない。それぞれの関数がどんな機能を持って、何をアウトプットするのかが分かれば十分である。

参考記事との比較

参考にさせていただいたこちらの記事との大きな差異は、必要な機能を補助関数として分けたことと、xbrlDoAll()を使っていないことである。前者はコードの可読性とメンテナンスの容易さに資する。xbrlDoAll()は、xbrlファイルを指定すればその中にある情報(factやcontextIdなど)をdata.frameのlistとしてまとめて出してくれる便利な関数であるが、参考先にもある通り1企業あたり1分以上の時間がかかる。

本ブログではlow-level functionを用いて、処理のボトルネックである部分を独立させて1回のみ実行することによって、1企業あたりの整理にかかる時間を1~4秒に短縮した。ただし、筆者はxbrlデータの仕組みについて十分熟知しておらず、また本記事の処理は試行錯誤の結果から導いたものであるため、加工されたデータの網羅性、正確性については一切保証できない。

XBRLpackageのリファレンスマニュアルはこちらである。

Rによる実装

概説で述べた通り、以下の関数群をどうやって使うかはまだ理解不要である。それぞれ何をしたいのかが分かれば次回の記事の理解がスムーズとなる。

get_docID()

解凍したzipファイルのdocIDを取得する関数である。抽出後のデータを"docID.csv"としてtidyupフォルダに保存するために必要となる。インプットはzipファイルのpathと、zipフォルダのpathである。アウトプットにはdocIDを返す。

get_docID <- function(zip, path_zip){
  
  zip %>% 
    str_remove(pattern = path_zip) %>% 
    str_remove(pattern = "/") %>% 
    str_remove(pattern = ".zip") %>% 
    return()
}

get_fact()

xbrlファイルからfactを含むdata.frameを抽出する関数である。流れは

  1. xbrlParse()xbrlファイルを"parse"し*1
  2. xbrlProcess**()で**情報をdata.frameとして抽出したあと
  3. xbrlFree()でメモリを開放する

となっている。インプットはxbrlファイルへのpathであり、アウトプットはfact情報を含むdata.frameである。

get_fact <- function(path_xbrl){
  
  parsed <- xbrlParse(path_xbrl)
  
  fact <- xbrlProcessFacts(parsed)
  
  xbrlFree(parsed)
  
  ##
  
  fact %>% 
    as_tibble() %>% 
    mutate_at("fact", str_conv, encoding = "UTF-8") %>% 
    mutate_if(is.factor, as.character) %>% 
    return()
}

get_context()

get_fact()と同様。contextIdの入手のために必要である。

get_context <- function(path_xbrl){
  
  parsed <- xbrlParse(path_xbrl)
  
  context <- xbrlProcessContexts(parsed)
  
  xbrlFree(parsed)
  
  context %>% 
    as_tibble() %>% 
    mutate_if(is.factor, as.character) %>% 
    return()
}

get_definition()

基本的にget_fact()と同様。roleIdの入手のために必要である。definition情報はxbrlファイルではなくxmlファイルに入っているため、その辺りを修正している。ただし統一化のために、インプットはget_fact()と同様、xbrlファイルへのpathとしている。アウトプットはroleIdを含むdata.frameである。

get_definition <- function(path_xbrl, fs_name){
  
  path_pubdoc <- dirname(path_xbrl)
  
  path_def <- 
    dir(path_pubdoc, full.names = TRUE) %>% 
    str_subset(pattern = "def.xml$") %>% 
    str_subset(pattern = "ifrs", negate = TRUE)
  
  parsed_def <- xbrlParse(path_def)
  
  definition <- xbrlProcessArcs(parsed_def, arcType = "definition")
  
  xbrlFree(parsed_def)
  
  definition %>% 
    as_tibble() %>% 
    mutate_all(as.character) %>% 
    rename("elementId" = toElementId) %>% 
    slice(str_which(.$roleId, str_c(fs_name, collapse = "|"))) %>% 
    slice(str_which(.$roleId, "jppfs")) %>% 
    select(elementId, roleId) %>% 
    return()
}

get_ac_standard()

採用している会計基準を返す関数である。インプットにはget_fact()で取得したdata.frameを用いる。アウトプットは

のいずれかを想定している*2

get_ac_standard <- function(joined_fact){
  
  joined_fact %>% 
    slice(str_which(.$elementId, "AccountingStandardsDEI")) %>% 
    purrr::pluck("fact") %>% 
    return()
}

join_fcl()

fact、context, labelStringをそれぞれ含むdata.frameをjoinする関数である。他の関数より定義する必要性は薄いが、次回の記事における統合関数のコードをすっきりさせるために定義した。

join_fcl <- function(joined_fact, joined_context, label){
  
  joined_fact %>% 
    left_join(joined_context, by = "contextId") %>% 
    left_join(label, by = "elementId") %>% 
    return()
}

is_consolidated()

「連結財務諸表の情報をもつ行数」を返す関数である。連結財務諸表作成企業であれば1以上(TRUE)の値をとり、そうでなければ0(FALSE)を返す*3。インプットはjoin_fcl()でjoinしたdata.frameである。

is_consolidated <- function(joined_fcl){
  joined_fcl %>% 
    slice(str_which(.$elementId, "jppfs")) %>%
    slice(str_which(.$contextId, "NonConsolidated", negate = T)) %>%
    nrow() %>% 
    return()
}

tidy_joined_data()

最終的に結合されたdata.frameの調整をする関数である。調整内容は

  1. 不要な列を落とす
  2. ある列に共通して含まれる情報のない文字を削る
  3. 販売費及び一般管理費(SGA)の内訳項目をPL項目扱いとする*4

である。

tidy_joined_data <- function(joined_data){
  
  joined_data %>% 
    select(labelString, fact, startDate, endDate, elementId, contextId, roleId) %>% 
    mutate_at("elementId", str_remove, pattern = "jppfs_cor_") %>% 
    separate(col = roleId, into = c("garbage", "roleId"), sep = "rol_") %>% 
    select(-garbage) %>% 
    mutate(roleId = if_else(is.na(.$roleId) & str_detect(.$elementId, "SGA"), 
                            true = "StatementOfIncome", false = .$roleId)) %>% 
    return()
}

おわりに

次回の記事では、前記事及び本記事の内容を統合した関数を定義し、その実行によって実際にzipファイルを分析可能な形に整頓していく。

*1:C++を呼んでいるため速いらしい。

*2:修正国際会計基準なんて知らない。

*3:子会社を持たず個別財務諸表のみを作成する上場企業も存在する。

*4:これにより人件費や広告宣伝費を分析できる。

RでEDINETから財務データを入手・分析する~④前処理編1/3~

記事一覧

はじめに

本記事は書類取得編の続きであり、4番目の記事にあたる。本記事で行う作業は、前記事で取得したzipファイルを利用して、labelStringを含むデータを取得することである。

また、本ブログの執筆に際してこちらの記事を参考にさせていただいた。

前処理の前作業

データの整理にあたっては、予めどんな情報が必要であるかというゴールを設定する必要がある。不要な変数を多く含んでいると分析の際に見通しが悪くなり、一つの処理にかかる時間が増え望ましくない。そこで、まず本ブログで抽出する情報を列挙し、その後に前処理のデザインを説明する。

取得情報の整理

以下、変数を列挙する。それぞれの具体的なイメージは導入編で例示した表と見比べられたい。

  • labelString

    • 勘定科目名を表す。本記事で取得する情報である。
  • fact

    • 実際の値を表す。
  • startDate&endDate

    • 期首日及び期末日を表す。
  • elementId

    • 勘定科目に結びついたkeyのようなもの。XBRLに含まれる多種の情報を、このelementIdをkeyとしてjoinしていく。
  • contextId

    • 時制、期間か時点か、メンバーの要素名などを含む。連結財務諸表か否かの判断のために用いる。
  • roleId

    • どの財務諸表に該当するかを表す。例えば、税金等調整前当期純利益はBSとCF計算書にあり、このroleIdを除いた要素が同じである。

前処理のデザイン

全3回に分けて解説する。

  • 1回目たる当記事ではlabelStringを取得する。分析データの年度ごとに必要な作業となる。

  • 2回目では、xbrlファイルからの情報の抽出に必要となる機能を補助関数として定義する。

  • 3回目では、当記事と次の記事の内容を統合した1つの大きな関数を定義する。提出日単位で実行することで、その日のzipファイルを全て分析可能なcsvファイルへと変換できる。

関数の実行について

本記事の関数は、年度ごとに1回行うだけでよい*1

Rによる実装

手順

流れは以下の通りである。

  1. zipファイルを一つ解凍する。

  2. 解凍したデータから、label情報をもつxmlファイルへの(外部)リンクを取り出す。

  3. 日本語のlabel情報をもつxmlファイルを"jppfs_label_YYYY.xml"として保存する。

  4. そのxmlファイルをdata.frameに読込み、文字コードの修正と冗長ラベルの削除を挟んでから、labelString及びelementIdを含む"JGAAP_label_YYYY.csv"をlabelフォルダに保存する。

  5. 解凍したファイルを削除する。

手順4に1分程度の時間がかかる。

コード

上記手順に倣った関数を以下に与える。

year <- 2019

path_zipdata <- dir(path_zip, pattern = ".zip", full.names = TRUE)

get_label <- function(path_zipdata, year){
  
  zipfile <- path_zipdata[1]
  
  unzip(zipfile = zipfile,
        exdir = path_zip)
  
  path_xbrl <- dir(str_c(path_zip, "/XBRL/Publicdoc"), 
                   pattern = "\\.xbrl", 
                   full.names = T) %>% 
    str_subset("ifrs", negate = TRUE)
  
  parsed <- xbrlParse(path_xbrl)
  
  schema <- xbrlGetSchemaName(parsed)
  
  xbrlFree(parsed)
  
  ##
  
  path_pubdoc <- dirname(path_xbrl)
  
  parsed_Sch <- xbrlParse(str_c(path_pubdoc, "/", schema))
  
  linkbase <- xbrlGetLinkbaseNames(parsed_Sch)
  
  xbrlFree(parsed_Sch)
  
  linkbase %>%
    str_subset(pattern = "jppfs") %>%
    str_subset(pattern = "lab.xml") %>%
    read_xml() %>%
    write_xml(file = str_c("Data/label/jppfs_label_", year, ".xml"))
  
  path_label <- dir("Data/label", as.character(year), full.names = TRUE)
  
  parsed_label <- xbrlParse(path_label)
  
  labels <- xbrlProcessLabels(parsed_label)
  
  xbrlFree(parsed_label)
  
  ##
  
  labels %>%
    as_tibble() %>%
    mutate_at("labelString", str_conv, encoding = "UTF-8") %>%
    mutate_if(is.factor, as.character)  %>%
    slice(str_which(.$labelRole, "verbose", negate = T)) %>%
    slice(str_which(.$labelRole, "2003/role/label$")) %>% 
    select(elementId, labelString) %>%
    write_excel_csv(path = str_c("Data/label/JGAAP_label_", year, ".csv"))
  
  unlink(str_c(path_zip, "/XBRL"), recursive = TRUE)
  
}

get_label(path_zipdata, year)

labelフォルダ内に、"jppfs_label_2019.xml"と"JGAAP_label_2019.csv"が保存されていれば成功である。後者の例示が以下である。

elementId labelString
jppfs_cor_AssetsAbstract 資産の部
jppfs_cor_CurrentAssetsAbstract 流動資産
jppfs_cor_CashAndDeposits 現金及び預金
jppfs_cor_NotesAndAccountsReceivableTrade 受取手形及び売掛金
jppfs_cor_AllowanceForDoubtfulAccountsNotesAndAccountsReceivableTrade 貸倒引当金
jppfs_cor_NotesAndAccountsReceivableTradeNet 受取手形及び売掛金(純額)

おわりに

次回の記事では、xbrlファイルからの情報の抽出に必要な機能を補助関数として定義する。

*1:ここでの年度とは、3月決算企業の期末日を想定している。すなわち、2019年3月決算の企業群に対して1回行うだけでよい。ただし、筆者は3月決算企業(提出日が6月の企業)以外に試したことはない。

RでEDINETから財務データを入手・分析する~③書類取得編~

記事一覧

はじめに

本記事はdocID取得編の続きであり、3番目の記事にあたる。本記事で行う作業は、前記事で取得したdocIDに紐づく書類のXBRLデータを入手し、zipファイルとして保存することである。EDINETで公開されている『EDINET API仕様書』を読んでいることを前提とする。

また、本ブログの執筆に際してこちらの記事を参考にさせていただいた。

書類取得API

手順は前記事とおおむね同じである。docIDとリクエストパラメータを加えたリクエストにより、指定した書類のバイナリデータを取得する。

取得情報

設定可能なリクエストパラメータとして、以下の4つがある。

  • type=1

    • 提出本文書及び監査報告書を取得する。本ブログでは有価証券報告書を利用するため、このパラメータを指定する。
  • type=2

    • PDFを取得する。企業の有報サンプルが多数ほしい場合は有用であるが、使ったことはない。
  • type=3

    • 代替書面と添付文書を取得する。使ったことはない。
  • type=4

    • 英文ファイルを取得する。使ったことはない。

注意事項

本記事においては"2019-06-28"に提出された361の有報を取得する。for文により取得するが、その際は過剰アクセスを避けるために、1つにつき10秒のクールタイムを設ける。また、参考にしたこちらの記事に倣い、Rのコードはコピペでそのまま動かないようになっている。具体的には、書類取得APIのURLとリクエストパラメータについて省いている。それらは『EDINET API仕様書』に記載されているため、本記事の手順はそちらを読まなければ再現不可能である。

Rによる実装

手順

詳細な流れは以下の通りである。

  1. URLとリクエストパラメータ、docIDを指定し、クールタイムを挟んでからリクエストを行う。

  2. レスポンスのバイナリーデータを変数に格納後、zipファイルとして保存する。ファイル名は"docID.zip"とする。

  3. 書類を取得できた企業の名前、docID等を別途(ファイル構造としての)zipファイル内に"downloaded_XBRL.csv"として保存する。これは書類の取得に伴い更新されていく。

  4. インプットであるdocIDのベクトルの、何番目までを取得できたかについてConsoleに表示する。

  5. 変数をクリーンアップする。

  6. 1~5の手順を、インプットであるdocIDのベクトルの長さだけ繰り返す。

なお、途中でエラーが発生し書類の取得が中断された場合に備え、インプットであるdocIDのベクトルの、任意の場所から開始できるようにしている。

コード

上記手順に倣った関数を以下に与える。

data_tidy <- 
  read_csv(dir(path_filing, ".csv", full.names = TRUE))  # 前記事のdocID.csvを読み込む

docID <-
  data_tidy %>% 
  purrr::pluck("docID")


get_zip_via_EDINET <- function(data_tidy, docID, path_zip,
                               start, end) {
  
  url_api <- "URL"
  
  url_type <- "リクエストパラメータ"

  i <- as.integer(start - 1L)
  
  for(id in docID[start:end]) {
    
    ## 過剰アクセス注意 ##
    
    Sys.sleep(10)
    
    ##                ##
    
    i <- i + 1L
    
    get_binary <- getBinaryURL(str_c(url_api, id, url_type))
    
    writeBin(get_binary, str_c(path_zip, "/", id, ".zip"))    
    
    if(file.exists(str_c(path_zip, "/downloaded_XBRL.csv"))) {
      
      data_tidy %>% 
        filter(docID == id) %>% 
        write_excel_csv(path = str_c(path_zip, "/downloaded_XBRL.csv"),
                        append = TRUE)
      
    } else {
      
      data_tidy %>% 
        filter(docID == id) %>% 
        write_excel_csv(path = str_c(path_zip, "/downloaded_XBRL.csv"),
                        append = FALSE)
      
    }
    
    print(str_c(i, now(), sep = ", "))
    
    rm(get_binary)
    
  }
  
  rm(list = ls(pattern = "^url|^id$"))
  
}

get_zip_via_EDINET(data_tidy, docID, path_zip,
                   start = 1L, end = length(docID))

zipフォルダ内に、書類のzipファイルと"downloaded_XBRL.csv"が保存されていれば成功である。

おわりに

次回の記事では、本記事で取得した書類のzipファイルを分析可能な形に整頓していく前処理を行う。

RでEDINETから財務データを入手・分析する~②docID取得編~

記事一覧

はじめに

本記事は導入編の続きであり、2番目の記事にあたる。本記事で行う作業は、書類の提出日を指定し、各書類に紐づけられたdocIDを入手することである。EDINETで公開されている『EDINET API仕様書』を読んでいることを前提とする。

EDINET APIの概要

EDINET APIとは

EDINET APIの概要について、以下、『EDINET API仕様書 Version 1』の文章を引用する。

EDINET APIは、利用者がEDINETの画面からではなく、プログラムを介してEDINETのデータベースから効率的にデータを取得できるAPI(アプリケーション・プログラミング・インターフェース)です。EDINET APIにより、EDINET利用者は効率的に開示情報を取得することが可能となります。

提供されているAPI

2つのAPIが提供されている。

  1. 提出された書類を把握するためのAPI

    • 本記事で利用するAPIである。指定した日付に提出された書類の件数及びその一覧などを取得できる。有価証券報告書書類管理番号(以下、docID)を保持するのが本記事の目標である。
  2. 提供された書類を取得するためのAPI

    • 次回以降に利用するAPIである。今回の記事で入手したdocIDをもとに、有価証券報告書XBRLデータのzipファイルを取得する。

取得情報

設定可能なリクエストパラメータとして、以下の2つがある。

  • type=1

    • メタデータのみを取得する。指定日に何件の書類が提出されたのかのみを知りたいのであればこちらを使えばよい。デフォルトはこちらであり、パラメータの省略が可能である。
  • type=2

    • 提出書類一覧及びメタデータを取得する。本記事の目的はdocIDの取得であるからこちらを指定する。

その他の特記事項

  • docIDは開示書類ごとに一意に定められている。同一企業、同一年度であっても書類が異なればdocIDは異なる。

  • 金融商品取引法に定める縦覧期間(5年)を過ぎると、書類は非開示となる。

Rによる実装

手順

詳細な流れは以下の通りである。

  1. 日付を指定し、type=2のリクエストを行う。

  2. レスポンスはjsonデータなのでlistに直しつつ、指定日に書類が提出されているか判断する。なければエラーを返す。

  3. tibbleに変形後、書類の中で「有価証券報告書」のみを抽出する*1。「訂正有価証券報告書」は様式を異にするため除外する。

  4. 上場企業のみを抽出する。secCode(銘柄コード)を持っているかで判断する。

  5. 以下の変数のみを保持する。なお、保存領域に余裕があるならばこの操作は不要である。

    • docID
    • edinetCode:提出者EDINETコード
    • filerName:提出者名
    • secCode
    • periodEnd:期末日
  6. filerNameについて様式を統一するため、"株式会社"という文言とスペースを除去する。

  7. filingフォルダ内に保存する。

コード

上記手順に倣った関数を以下に与える。

get_json <- function(date_filing) {
  
  url_api <- "https://disclosure.edinet-fsa.go.jp/api/v1/documents.json?date="
  
  url_type <- "&type=2"
  
  get_res <- httpGET(str_c(url_api, date_filing, url_type))
  
  data_raw <- fromJSON(get_res)
  
  data_flag <- 
    data_raw %>% 
    purrr::pluck("metadata") %>% 
    purrr::pluck("resultset") %>% 
    purrr::pluck("count")
  
  if(data_flag == 0){
    
    stop(str_c("Maybe ", date_filing, " is holiday!!"))
    
  }
    
  data_tidy <-         
    data_raw %>% 
    purrr::pluck(2) %>% 
    as_tibble() %>% 
    slice(str_which(.$docDescription, "有価証券報告書")) %>% 
    slice(str_which(.$docDescription, "訂正", negate = TRUE)) %>% 
    filter(!is.na(secCode)) %>% 
    select(docID, edinetCode, filerName, secCode, periodEnd) %>% 
    mutate_at("filerName", str_remove, pattern = "株式会社") %>% 
    mutate_at("filerName", str_squish) %>% 
    distinct()
    
  data_tidy %>% 
    write_excel_csv(str_c(path_filing, "/docID.csv"))
  
  ## API key word : docID
  
  rm(list = ls(pattern = "^url|get_res|^data"))
}

get_json(date_filing)

filingフォルダ内に"docID.csv"が保存されていれば成功である。以下にその例示を与える。

docID edinetCode filerName secCode periodEnd
S100G6IW E03717 第一商品 87460 2019-03-31
S100G9AI E03784 極東証券 87060 2019-03-31
S100GCOO E05297 ソフトフロントホールディングス 23210 2019-03-31
S100G8Y7 E03003 ほくやく・竹山ホールディングス 30550 2019-03-31
S100G9EX E21955 川田テクノロジーズ 34430 2019-03-31

注意事項

date_filingを任意の日時に変更すれば以下同様にdocIDを取得することができる。その場合は、前回の記事にて定義したcreate_file_structure()から実行する必要がある。

ただし、for文などで繰返し取得する場合は、Sys.sleep()を挟みクールタイムを設けて、相手側に負担をかけないよう配慮する。

おわりに

次回の記事では、本記事で取得したdocIDをもとに、有報のXBRLデータのzipファイルを取得する。

*1:有価証券届出書、臨時報告書、四半期報告書などもあるが、本ブログで扱わない。

RでEDINETから財務データを入手・分析する~導入編~

記事一覧

はじめに

このブログは、未来の自分のために、Rを使ってEDINETから有報を入手、加工と分析を行った記録を残すものである。想定する読者は「Rを使って上場企業の財務諸表のデータを分析してみたい者」である。このブログに従うことで、以下のようなデータが得られる*1

docID labelString fact startDate endDate elementId contextId roleId filerName secCode jpx_33
S100G3QX 現金及び預金 5174000000 NA 2018-03-31 CashAndDeposits Prior1YearInstant ConsolidatedBalanceSheet 電響社 81440 卸売業
S100G3QX 現金及び預金 5639000000 NA 2019-03-31 CashAndDeposits CurrentYearInstant ConsolidatedBalanceSheet 電響社 81440 卸売業
S100G3QX 受取手形及び売掛金 7121000000 NA 2018-03-31 NotesAndAccountsReceivableTrade Prior1YearInstant ConsolidatedBalanceSheet 電響社 81440 卸売業
S100G3QX 受取手形及び売掛金 7144000000 NA 2019-03-31 NotesAndAccountsReceivableTrade CurrentYearInstant ConsolidatedBalanceSheet 電響社 81440 卸売業
S100G3QX 電子記録債権 1067000000 NA 2018-03-31 ElectronicallyRecordedMonetaryClaimsOperatingCA Prior1YearInstant ConsolidatedBalanceSheet 電響社 81440 卸売業
S100G3QX 電子記録債権 494000000 NA 2019-03-31 ElectronicallyRecordedMonetaryClaimsOperatingCA CurrentYearInstant ConsolidatedBalanceSheet 電響社 81440 卸売業
S100G3QX 商品及び製品 3191000000 NA 2018-03-31 MerchandiseAndFinishedGoods Prior1YearInstant ConsolidatedBalanceSheet 電響社 81440 卸売業
S100G3QX 商品及び製品 3454000000 NA 2019-03-31 MerchandiseAndFinishedGoods CurrentYearInstant ConsolidatedBalanceSheet 電響社 81440 卸売業
S100G3QX 仕掛品 22000000 NA 2018-03-31 WorkInProcess Prior1YearInstant ConsolidatedBalanceSheet 電響社 81440 卸売業
S100G3QX 仕掛品 41000000 NA 2019-03-31 WorkInProcess CurrentYearInstant ConsolidatedBalanceSheet 電響社 81440 卸売業

なお、ブログの執筆にあたって以下の点に注意を払った。

  1. tidyverse系統の関数を多用しているが、稀なものでない限り「それらの関数の機能は何か」を説明はしない。
  2. 会計の用語に関する説明も行わない。
  3. 長文になることが予想されるため、文章量を最小にしつつ情報量を最大にすることを心掛ける。冗長な言い換え、理解に資さない例え、不要な強調は行わない。
  4. 現在わかっていないことは、ごまかさずにわからないと明記する。

手順

大まかな手順は以下の通りである。

  1. 書類の提出日を指定し、各書類に紐づけられたdocIDを入手する。
  2. APIを通じて有報のXBRLデータのzipファイルを入手する。
  3. zipファイルを解凍し、中身のうち財務諸表の数値データを取り出して分析可能な形に整える。
  4. 整頓したデータを分析する。

なお、一つの記事に一つの手順が収まらない可能性がある。

注意点

読者の時間を無駄にしないために、予め注意しておくべき点を列挙する。

  1. もし使いたい財務データがIRBANKにて入手できるようなものならば、本ブログに従うことは無駄骨となる。BS項目であれば総資産、株主資本などの合計項目がそれにあたる。そうではなく、棚卸資産売掛金といった細かい勘定科目を扱いたいときには意義がありうる*2

  2. 日経Financial QUESTなど企業が有償で提供するデータを使えるならば、その方が良い。本記事で得られるデータは、前処理にひどく時間をとられるからである。

  3. ブログ執筆時点において、5年以上の分析が可能な企業は日本基準を採用している企業に限られる。IFRSは数年前に対応されたためデータの蓄積が少なく、米国基準にいたっては対応がなされていない*3からである。

事前準備

以下、事前に準備を推奨する点を列挙する。

1. packageのインストール

手順1~3において、少なくとも以下のpackageを使用する。予めインストールすることを勧める。

library(RCurl)
library(tidyverse)
library(jsonlite)
library(XML)
library(rvest)
library(lubridate)
library(XBRL)

2. フォルダ構造

多くのデータを入手し、かつ、加工したものを保存する必要がある。効率的に分析等を行うために、フォルダ構造を以下に定める。

  • Data
    • label
    • YYYY-MM-DD_dl(データの入手日)
      • YYYY-MM-DD_filing(書類の提出日)
        • zip
        • tidyup

流れとして、たとえばまず"2019-06-28"などを指定し、その日提出された書類に関するデータを入手してから、そのデータにあるdocIDを用いて実際の有報のzipデータを入手することとなるので、このような構造にした。1番上の"Data"フォルダがあることを前提としたうえで、上記のフォルダ構造を作る関数が以下である。

date_filing <- "2019-06-28"

create_file_structure <- function(date_filing, dl_date = today()) {
  
  path <<- str_c("Data/", dl_date, "_dl")  # use <<-
  
  if(!file.exists(path)) {  # create file if NOT exists
    dir.create(path)
  }
  ##
  
  path_label <<- str_c("Data/label")
  
  if(!file.exists(path_label)) {
    dir.create(path_label)
  }
  ##
  
  path_filing <<- str_c(path, "/", date_filing, "_filing")
  
  if(!file.exists(path_filing)) {
    dir.create(path_filing)
  }
  ##
  
  path_zip <<- str_c(path_filing, "/zip")
  
  if(!file.exists(path_zip)) {
    dir.create(path_zip)
  }
  ##
  
  path_tidyup <<- str_c(path_filing, "/tidyup")
  
  if(!file.exists(path_tidyup)) {
    dir.create(path_tidyup)
  }
  
  ## return all path
}

create_file_structure(date_filing)

関数の実行によって得られたpathに関する変数は後に使うのでとっておく。

3. EDINETの操作ガイド

データの入手先であるEDINETにて、APIの仕様書やXBRLの形式に関する説明書が公開されている。その利用にあたっては、負担をかけないためにも最低限『EDINET API仕様書』を読んでおくべきである。

おわりに

次の記事では、手順の1にあたる「書類の提出日を指定し、各書類に紐づけられたdocIDを入手」を行う。執筆の励みになるため、starや記事の拡散をしていただけると嬉しい。

*1:示したのはストック情報であるためstartDate(期首日)がNAになっているが、フロー情報なら埋まっている。

*2:本ブログの手順で入手できるのは有価証券報告書内の【経理の状況】に記載される財務諸表の数値データである。本ブログで例示した電響社ならば、会社のHPにおける有報を見比べるとよい。

*3:詳細タグ付けがされておらず、1つのセルにhtmlのテーブルが直書きされている