💹

J-Quants API を GAS で使う (3) 銘柄毎に RSI を書き込む

2023/12/24に公開

この記事は JPX 総研さんが提供している J-Quants API を GAS (Google Apps Script) で利用する方法を説明するシリーズの第三回目です。

これまでの記事では、下準備として API 呼び出しに必要となる ID トークンを取得し、

https://zenn.dev/seratch/articles/aa2adaab185758

GAS スクリプトを用意して Google スプレッドシートのデータ更新をできるようにしました。

https://zenn.dev/seratch/articles/3dca8c0c04d0ae

今回は GAS のスクリプトを実行してシート内にある銘柄コード毎に 14 日間 RSI (Relative Strength Index) を C 列のセルに書き込むということをやってみましょう。

シートに列を追加

C 列を追加して、ここに RSI を書き込んでいくことにしましょう。

スプレッドシートの名前は何でもよいですが、ここで使用するシートの名前は「銘柄管理」としています。以下に示すコード例はこの命名を前提としているので、別のものにする場合はコード内の該当箇所も同じように書き換えてください。

スクリプトを書き換える

コードエディタを開いて、前回の記事でつくった myFunction() を更新しましょう。

RSI 計算のために終値の情報が 15 日分必要ですが、この API は取引日数を指定するのではなく、開始日にちを指定しないといけないので、土日祝日を考慮して +10 ほど多めに指定します。

データを取得したら、前日終値と当日終値の差分を 14 日分取得して、上がり幅の合計が上がり幅 + 下がり幅の合計の何パーセントにあたるかを計算します。

function myFunction() {
  const idToken = issueIdToken();
  const headers = { 'Authorization': `Bearer ${idToken}` };
  const sheetName = '銘柄管理'; // シートの名前と同一であること
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
  // まとめてセルの値情報を持ってくる
  const codeCells = sheet.getRange(2, 1, sheet.getDataRange().getLastRow() - 1); // ヘッダー行をスキップしています
  const codes = codeCells.getValues();
  for (let i = 0; i < codes.length; i++) {
    const code = codes[i][0].toString();
    if (!code) { // 銘柄コードが設定されていない行があったらそれ以降は処理スキップ
      break;
    }
    const [closePrice, rsi] = _fetchLatestClosePriceAndRSI(code, headers);
    // 銘柄コードが書かれているセルの右隣のセルに取得した値を書き込む
    sheet.getRange(i + 2, 2).setValue(closePrice); // 最新の終値
    sheet.getRange(i + 2, 3).setValue(rsi); // RSI
  }
}

// 銘柄コードとリクエストヘッダーを受け取って、最新の終値と 14 日間 RSI を返すメソッド
function _fetchLatestClosePriceAndRSI(code, headers) {
  // 株価四本値 API - https://jpx.gitbook.io/j-quants-ja/api-reference/daily_quotes
  const endpoint = 'https://api.jquants.com/v1/prices/daily_quotes';
  const to = _formatDate(new Date());
  const fromDate = new Date();
  fromDate.setDate(fromDate.getDate() - 25); // 最低 15 日分必要、土日祝日を考慮して少し多めに取ってくる
  const from = _formatDate(fromDate);

  // パラメーターつきの API URL
  const url = `${endpoint}?code=${code}&to=${to}&from=${from}`;
  // ID トークンをリクエストヘッダーに含めてリクエストを実行
  const apiResponse = UrlFetchApp.fetch(url, { headers });
  const body = JSON.parse(apiResponse.getContentText());
  // console.log(JSON.stringify(body, null ,2));
  let quotes = body.daily_quotes;

  const gainsAndLosses = []; // 分母となる上げ幅・下げ幅全て含むもの
  const gains = []; // 分子となる上げ幅だけを含むもの
  let latestClosePrice = undefined; // 最新の終値
  if (quotes) {
    quotes = quotes.slice(-15);
    const latestQuote = quotes[quotes.length - 1];
    latestClosePrice = latestQuote.AdjustmentClose || latestQuote.Close;
    let previousDayClose = undefined;
    for (const q of quotes) {
      if (q && (q.AdjustmentClose || q.Close)) {
        const close = Number.parseFloat(q.AdjustmentClose || q.Close);
        if (previousDayClose) {
          const diff = close - previousDayClose;
          gainsAndLosses.push(Math.abs(diff));
          if (diff > 0) { gains.push(diff); } // 上げ幅
        }
        previousDayClose = close; // 次の比較のために代入
      }
    }
  }
  const numerator = gains.reduce((s, a) => s + a, 0);
  const denominator = gainsAndLosses.reduce((s, a) => s + a, 0);
  const rsi = Math.round((numerator / denominator) * 1000) / 10;
  return [latestClosePrice, rsi];
}

// 株価四本値 API に渡す日付の形式に変換するユーティリティ
function _formatDate(dt) {
  const y = dt.getFullYear().toString();
  const m = ('00' + (dt.getMonth() + 1)).slice(-2);
  const d = ('00' + dt.getDate()).slice(-2);
  return `${y}${m}${d}`;
}

そのまま「Run」ボタンから myFunction を実行してみてください。以下のように 0-100 の値が表示されていれば、うまく計算できているはずです。

終わりに

いかがだったでしょうか?今回の変更は、前回すでに使っていた株価四本値 API の取得範囲を広げて RSI を計算するロジックを追加しただけなので、割と簡単な変更ですね。実行速度も十分に速いと思います。

ここで終わるのもちょっと中途半端かなぁという気がしてきたので、次回は財務情報 API を使った例も紹介してみたいと思います。

Discussion