J-Quants APIによる銘柄情報・財務情報の出力

# 1. はじめに
個人投資家向けのAPIデータ配信サービス「J-Quants」を使用すると、東証上場銘柄の様々な金融データが取得できます。

J-Quants運営チーム様の記事
https://qiita.com/j_quants/items/724c7dda5262a1ad835a

使いこなせれば本格的なデータ分析が可能となるはずです。
この素晴らしいデータに報いるためにも、金融ドメイン知識をモリモリつけたいものですね。

さて、J-Quantsでは無料登録でも財務データをAPI取得できます（スゴい！）。
取得できる年数に制限がありますが、スクリーニングや銘柄管理など、十分に活用できるシーンはありそうです。
そこで、まずは銘柄コードから銘柄や財務のデータを取得して、スプレッドシートに出力するGASコードを作成しました。

# 2. データを取得してスプレッドシートに出力
## 2.1. リフレッシュトークンとIDトークンの取得
以下のコードでIDトークンが取得できるか確認します。「成功」が出力できればOK。
```GAS:getTokens
function getTokens() {
  var url1 = "https://api.jquants.com/v1/token/auth_user";
  var data = {
    "mailaddress": "*******@*********",
    "password": "***********"
  };
 
  var options1 = {
    "method" : "post",
    "payload" : JSON.stringify(data),
    "contentType" : "application/json",
    "muteHttpExceptions" : true
  };
  
  var response1 = UrlFetchApp.fetch(url1, options1);
  
  if (response1.getResponseCode() === 200) {
    var jsonRes1 = JSON.parse(response1.getContentText());
    var REFRESH_TOKEN = jsonRes1["refreshToken"];
    
    var url2 = "https://api.jquants.com/v1/token/auth_refresh?refreshtoken=" + REFRESH_TOKEN;
    var options2 = {
      "method" : "post",
      "muteHttpExceptions" : true
    };
    
    var response2 = UrlFetchApp.fetch(url2, options2);
    
    if (response2.getResponseCode() === 200) {
      Logger.log("成功"); // Success
    } else {
      Logger.log("失敗"); // Failure
    }
    
  } else {
    Logger.log("失敗"); // Failure
  }
}
```

## 2.2. J-Quants APIで取得できるデータ

![jquant.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472306/b7f0e582-9404-cb94-fd6c-96f241fe03d1.jpeg)

無料プランでは以下の機能が使用可能です。
・上場企業の情報を取得する事ができます
https://jpx.gitbook.io/j-quants-ja/api-reference/listed_info
・株価情報を取得することができます
https://jpx.gitbook.io/j-quants-ja/api-reference/daily_quotes
・四半期の財務情報が取得できます
https://jpx.gitbook.io/j-quants-ja/api-reference/statements
・翌日発表予定の決算情報が取得できます
https://jpx.gitbook.io/j-quants-ja/api-reference/announcement

## 2.3. データ取得とスプレッドシートへの出力
今回は、スプレッドシートの１列目に記載した銘柄コードから、APIにより銘柄と財務情報を取得し、選別した結果を２列名以降に出力するGASコードを示します。
コード作成にはChatGPTさんの力をフルで借りました（ChatGPTさんはvar変数がお好きなようです）。

```GAS:getData
function getData() {
  
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  
  // ヘッダーの記入
  sheet.getRange(1, 1).setValue('銘柄コード');
  sheet.getRange(1, 2).setValue('会社名');
  sheet.getRange(1, 3).setValue('33業種コード名');
  sheet.getRange(1, 4).setValue('市場区分名');
  sheet.getRange(1, 5).setValue('開示日');
  sheet.getRange(1, 6).setValue('開示時刻');
  sheet.getRange(1, 7).setValue('当会計期間の種類');
  sheet.getRange(1, 8).setValue('一株あたり当期純利益');
  sheet.getRange(1, 9).setValue('一株あたり純資産');
  sheet.getRange(1, 10).setValue('一株あたり配当実績_合計');
  sheet.getRange(1, 11).setValue('自己資本比率');
  sheet.getRange(1, 12).setValue('営業活動によるキャッシュ・フロー');
  sheet.getRange(1, 13).setValue('投資活動によるキャッシュ・フロー');
  sheet.getRange(1, 14).setValue('財務活動によるキャッシュ・フロー');
  sheet.getRange(1, 15).setValue('期末発行済株式数');

  // トークンの取得
  var url1 = "https://api.jquants.com/v1/token/auth_user";
  var data = {
    "mailaddress": "*******@*********",
    "password": "***********"
  };
 
  var options1 = {
    "method" : "post",
    "payload" : JSON.stringify(data),
    "contentType" : "application/json",
    "muteHttpExceptions" : true
  };
  
  var response1 = UrlFetchApp.fetch(url1, options1);
  
  if (response1.getResponseCode() === 200) {
    var jsonRes1 = JSON.parse(response1.getContentText());
    var REFRESH_TOKEN = jsonRes1["refreshToken"];
    
    var url2 = "https://api.jquants.com/v1/token/auth_refresh?refreshtoken=" + REFRESH_TOKEN;
    var options2 = {
      "method" : "post",
      "muteHttpExceptions" : true
    };
    
    var response2 = UrlFetchApp.fetch(url2, options2);
        
    if (response2.getResponseCode() === 200) {
      var response2Json = JSON.parse(response2.getContentText());
      var idToken = response2Json.idToken;
      Logger.log("成功"); // Success
    } else {
      Logger.log("失敗"); // Failure
    }
    
  } else {
    Logger.log("失敗"); // Failure
  }

  // シートの１列目から銘柄コードの配列を取得
  var lastRow = sheet.getLastRow();
  var codes = [];
  for (var i = 2; i <= lastRow; i++) {
    var code = sheet.getRange(i, 1).getValue() + "0"; //末尾に0を加える
    codes.push(code);
  }

  // リスト情報と財務諸表のURLを作成
  var infoUrls = codes.map(code => `https://api.jquants.com/v1/listed/info?code=${code}`);
  var statementUrls = codes.map(code => `https://api.jquants.com/v1/fins/statements?code=${code}`);
  
  // 認証ヘッダーを設定
  var headers = {
    "Authorization": "Bearer " + idToken
  };

  // リクエストオブジェクトを作成
  var infoRequests = infoUrls.map(url => ({ "url": url, "headers": headers }));
  var statementRequests = statementUrls.map(url => ({ "url": url, "headers": headers }));

  // fetchAllで一括リクエスト
  var infoResponses = UrlFetchApp.fetchAll(infoRequests);
  var statementResponses = UrlFetchApp.fetchAll(statementRequests);


  // 銘柄一覧リクエストのレスポンスを処理
  infoResponses.forEach(function(response, index) {
    var rowIndex = 2 + index;
    var content = JSON.parse(response.getContentText());
    var info = content['info'][0]; // 最初の要素を取得

    if (info) {
      sheet.getRange(rowIndex, 2).setValue(info['CompanyName']);
      sheet.getRange(rowIndex, 3).setValue(info['Sector33CodeName']);
      sheet.getRange(rowIndex, 4).setValue(info['MarketCodeName']);
    } else {
      sheet.getRange(rowIndex, 2, 1, 3).clearContent(); // 会社名、業種コード名、市場区分名をクリア
    }
  });

  // 財務情報リクエストのレスポンスを処理
  statementResponses.forEach(function(response, index) {
    var rowIndex = 2 + index;
    var content = JSON.parse(response.getContentText());
    var fyStatements = content['statements'].filter(statement => statement['TypeOfCurrentPeriod'] === 'FY'); //会計年度をFYでフィルタリング

    //最新のFY財務情報を取得
    if (fyStatements.length > 0) {
      fyStatements.sort(function(a, b) {
        return new Date(b['DisclosedDate']) - new Date(a['DisclosedDate']);
      });

      var latestFYStatement = fyStatements[0];

      // 情報をセット
      sheet.getRange(rowIndex, 5, 1, 11).setValues([[
        latestFYStatement['DisclosedDate'],
        latestFYStatement['DisclosedTime'],
        latestFYStatement['TypeOfCurrentPeriod'],
        latestFYStatement['EarningsPerShare'],
        latestFYStatement['BookValuePerShare'],
        latestFYStatement['ResultDividendPerShareAnnual'],
        latestFYStatement['EquityToAssetRatio'],
        latestFYStatement['CashFlowsFromOperatingActivities'],
        latestFYStatement['CashFlowsFromInvestingActivities'],
        latestFYStatement['CashFlowsFromFinancingActivities'],
        latestFYStatement['NumberOfIssuedAndOutstandingSharesAtTheEndOfFiscalYearIncludingTreasuryStock']
      ]]);
    } else {
      sheet.getRange(rowIndex, 2, 1, 11).clearContent(); // 財務情報をクリア
    }
  });

}

```

# 3. 結果
↓これが、、、
![231104_in.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472306/841e1dd5-f22f-319a-8407-dd1c2c0cac2f.jpeg)
↓こうなります。
![231104_out.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472306/3aa1decc-d9a1-504b-5874-44ddac30766c.jpeg)

メールアドレスやパスワードは、スクリプトプロパティに格納した方が望ましいです。
また、レスポンス重視でfetchAllで一括リクエストしてますが、銘柄数が多い場合は制限かけた方がいいかもです。

取得した情報を基にして、複数の銘柄を独自指標を用いて分析することが可能ですね。
以上、少しでも参考になれば幸いです。