简体   繁体   English

触发运行 Google Sheets 脚本以批量从 Yahoo Finance 获取 URL 数据

[英]Trigger-run a Google Sheets script to fetch URL data from Yahoo Finance in batches

Prelude - this is quite a long post but mostly because of the many pictures to clarify my issue :) Prelude - 这是一个相当长的帖子,但主要是因为有很多图片来澄清我的问题:)

I have been pulling company data from Yahoo!我一直在从 Yahoo! 提取公司数据! Finance, first for only a few stocks, but currently for hundred of stocks (and soon to be thousands).金融,最初只针对几只股票,但目前针对数百只股票(很快将达到数千只)。 I am currently pulling this data live, with one urlFetch per stock ticker each time I load the spreadsheet.我目前正在实时提取这些数据,每次加载电子表格时每个股票代码都有一个 urlFetch。 This presents three problems:这提出了三个问题:

  • It takes a long time for the sheet to load as it is making hundreds of requests to Yahoo!加载工作表需要很长时间,因为它向 Yahoo! 发出了数百个请求。
  • I regularly get rate limited on Google side (max. 20k url fetches per day)我经常在 Google 方面受到速率限制(每天最多 20k 的 url 获取)
  • I might get rate-limited on Yahoo!我可能会在 Yahoo! 上获得速率限制! Finance side财务方面

I am therefore looking for a better way:因此,我正在寻找更好的方法:

  • Instead of calling Yahoo for each ticker each time the spreadsheet loads, I will run a google script that fetches data for each ticker once per day我不会在每次加载电子表格时为每个代码调用 Yahoo,而是运行一个谷歌脚本,每天一次为每个代码获取数据

Consider the following simplified example sheet (tab db ):考虑以下简化的示例表(tab db ):

在此处输入图像描述

The current script reads:当前脚本如下:

function trigger() {
  const db = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('db');
  const tickers = db.getRange('A2:A' + db.getLastRow()).getValues().flat();

  for (var row = 0; row < tickers.length; row++) {
    var data = yahoo(tickers[row]);
    db.getRange(row + 2, 2, 1, 3).setValues(data);
  }
}

function yahoo(ticker) {
  return [ticker, "SOME", "DATA", "MORE"]
}

Please note that for purposes of testing, yahoo() is simply returning an array.请注意,出于测试目的, yahoo()只是返回一个数组。 In reality, the yahoo() function pulls JSON data from Yahoo!实际上, yahoo()函数从 Yahoo! 中提取JSON数据。 After running the script, the spreadsheet looks like:运行脚本后,电子表格如下所示:

在此处输入图像描述

So far so good.到目前为止,一切都很好。 But if the list is not 3 but 5000 tickers long, running the script as-is will get me rate-limited quickly (or waiting very long for the spreadsheet to load).但是,如果列表不是 3 个而是 5000 个代码长,那么按原样运行脚本会让我快速限制速率(或者等待很长时间才能加载电子表格)。 Therefore, I thought about the following:因此,我想到了以下几点:

  • Each time the script is run, it will only pull data for 5 tickers.每次运行脚本时,它只会拉取 5 个股票代码的数据。 The script will be run once per minute.该脚本将每分钟运行一次。 (5 tickers and per minute for the purposes of this thread) (就本主题而言,每分钟 5 个代码)
  • The script will keep track of the last time it downloaded data for each ticker该脚本将跟踪上次下载每个代码的数据的时间

Assume that the list currently looks like this:假设列表当前如下所示:

在此处输入图像描述

Assume that today is May 31st, and the script is run:假设今天是 5 月 31 日,运行脚本:

在此处输入图像描述

Starting at the top of the lists, the script should want to update 5 tickers that have not yet been updated today:从列表顶部开始,脚本应该要更新今天尚未更新的 5 个代码:

  • BLK in row 2 has already been updated today, so it is ommitted第 2 行的BLK今天已经更新,所以省略
  • AAPL was last updated yesterday, so it is the first ticker that gets queried with Yahoo! AAPL最后一次更新是昨天,所以它是第一个被 Yahoo! 查询的股票代码。
  • etc.等等

Now row 2 to 9 have updated data.现在第 2 到 9 行已更新数据。 The second time the script is run, it should update the next five.第二次运行脚本时,它应该更新接下来的五个。 Again starting from the top, looking for the first 5 tickers either (1) without a "last run" date or (2) a run date before today:再次从顶部开始,查找前 5 个代码(1)没有“最后运行”日期或(2)今天之前的运行日期:

在此处输入图像描述

As you can see, lines 11 to 15 are now updated too.如您所见,第 11 到 15 行现在也更新了。 TSLA was skipped, because (for whatever reason), it already was updated today. TSLA被跳过,因为(无论出于何种原因)它今天已经更新。

Here is the same list again, just with 2 more tickers.这里又是同一个列表,只是多了 2 个代码。 If the script is run a few times on june 1st, the result will be like this:如果脚本在 6 月 1 日运行几次,结果将是这样的:

在此处输入图像描述

  • 3 batches of 5 tickers 3 批 5 行情
  • one batch of 2 tickers一批 2 行情

This works great if the Yahoo!如果 Yahoo! Finance service would always return data for each ticker.财务服务将始终返回每个代码的数据。 However, it will not.但是,它不会。 For example because:例如因为:

  • it has no data for a certain ticker它没有特定代码的数据
  • the request is timing out请求超时

I believe I need a solution to keep track of errors while downloading data.我相信我需要一个解决方案来在下载数据时跟踪错误。 Assume the script is run again a few times (triggered by a once-per-minute trigger in Google script) on june 2nd, and have the following result:假设脚本在 6 月 2 日再次运行几次(由 Google 脚本中每分钟一次的触发器触发),结果如下:

在此处输入图像描述

We see two tickers ( JPM and ORCL ) where data could not be updated.我们看到两个代码( JPMORCL )的数据无法更新。 Both are marked in the error column, filled by the script-to-be.两者都标记在错误列中,由待编写的脚本填充。

Let's assume we run the script again on june 3rd.假设我们在 6 月 3 日再次运行脚本。 On this day, JPM data is downloading perfectly, but ORCL again is generating an error.这一天, JPM数据正在完美下载,但ORCL再次出现错误。 Yahoo!雅虎! didn't return any data.没有返回任何数据。 The error column is updated to 2 . error列更新为2

在此处输入图像描述

If a ticker did not get data returned for 2 attempts in a row ( error = 2 ), it should be forever skipped.如果代码连续 2 次尝试未返回数据( error = 2 ),则应永远跳过它。 I will fill it in manually at some point, or look into whether I have typed in a non-existing ticker for instance.我会在某个时候手动填写它,或者查看我是否输入了一个不存在的代码。

Keeping that of errornous downloads prevents the script from getting stuck.保留错误下载可以防止脚本卡住。 Without it, if there are 5 tickers at the top of the list that keep throwing errors, the script will never go past beyond those 5. It will try to attempt to download data from Yahoo over and over for these tickers.如果没有它,如果列表顶部有 5 个不断抛出错误的代码,脚本将永远不会超过这 5 个。它会尝试一遍又一遍地尝试从 Yahoo 下载这些代码的数据。

In this last picture, we see the result of the script being run on june 4th.在最后一张图片中,我们看到了脚本在 6 月 4 日运行的结果。 I have colored again the batches (5 tickers) that were updated per run/ minute.我再次为每运行/分钟更新的批次(5 个代码)着色。

在此处输入图像描述

I tried my best to explain how I am thinking of building a error-proof downloading from Yahoo!我尽力解释了我是如何考虑从 Yahoo! 构建防错下载的。 Finance.金融。 In the rest of my spreadsheet, whenever I need metadata from company, I can simple take it from this db tab instead of querying Yahoo!在我的电子表格的其余部分,每当我需要来自公司的元数据时,我都可以简单地从这个db选项卡中获取它,而不是查询 Yahoo! over and over.一遍又一遍。

My issue is that my scripting skills are limited.我的问题是我的脚本技能有限。 I am not overseeing how to start building this.我不是在监督如何开始构建它。 Could someone please:有人可以请:

  • Give me a start with (pseudo) code OR给我一个开始(伪)代码或
  • Give feedback on my thoughts.反馈我的想法。 Am I missing something here that could present a problem?我在这里遗漏了一些可能会出现问题的东西吗?

PS. PS。 I understand that I am still making 5 urlfetches per time the script is run.我知道每次运行脚本时我仍在进行 5 次 urlfetches。 It was suggested to me that I should batch these 5 together (which would prevent me from being rate limited at least on Google's side).有人建议我将这 5 个一起批处理(这将防止我至少在 Google 方面受到速率限制)。 This is a great idea, but I found it difficult to understand how it works, so I would rather first have a script that works and that I can follow.这是一个好主意,但我发现很难理解它是如何工作的,所以我宁愿首先有一个可以工作并且我可以遵循的脚本。 In a later stage, I will definitely upgrade / make it more efficient :)在以后的阶段,我一定会升级/提高效率:)

If you've read all the way until here, thank you so much.如果您一直阅读到这里,非常感谢。 Any help is greatly appreciated!任何帮助是极大的赞赏!

[EDIT1]: in reality, yahoo() looks like this: [EDIT1]:实际上, yahoo()看起来像这样:

function yahoo(ticker) {
  const url = 'https://query2.finance.yahoo.com/v10/finance/quoteSummary/' + encodeURI(ticker) + '?modules=price,assetProfile,summaryDetail';
  
  let response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
  if (response.getResponseCode() == 200) {
      var object = JSON.parse(response.getContentText());
  }

  let fwdPE  = object.quoteSummary.result[0]?.summaryDetail?.forwardPE?.fmt || '-';
  let sector = object.quoteSummary.result[0]?.assetProfile?.sector || '-';
  let mktCap = object.quoteSummary.result[0]?.price?.marketCap?.fmt || '-';

  return [[fwdPE, sector, mktCap]];
}

[EDIT2] Example spreadsheet here . [EDIT2] 此处的电子表格示例

[EDIT3] Current scripts in example spreadsheet: [EDIT3] 示例电子表格中的当前脚本:

function trigger() {
  const max = 5; // From your question, maximum execution of "yahoo" is 5.

  const today = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "yyyyMMdd");
  const db    = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('db');
  const range = db.getRange('A2:F' + db.getLastRow());
  
  const { values } = range.getValues().reduce((o, r) => {
    const [ticker, b, c, d, e, f] = r;
    if (o.c < max && (e.toString() == "" || Utilities.formatDate(e, Session.getScriptTimeZone(), "yyyyMMdd") != today)) {
      try {
        o.c++;
        o.values.push([...yahoo(ticker), today, null]);
      } catch (_) {
        o.values.push([ticker, b, c, d, today, ["", "0"].includes(f.toString()) ? 1 : f + 1]);
      }
    } else {
      o.values.push(r);
    }
    return o;
  }, { values: [], c: 0 });
  range.setValues(values);
}


function yahoo(ticker) {
  const url = 'https://query2.finance.yahoo.com/v10/finance/quoteSummary/' + encodeURI(ticker) + '?modules=price,assetProfile,summaryDetail';
  
  let response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
  if (response.getResponseCode() == 200) {
      var object = JSON.parse(response.getContentText());
  }

  let fwdPE  = object.quoteSummary.result[0]?.summaryDetail?.forwardPE?.fmt || '-';
  let sector = object.quoteSummary.result[0]?.assetProfile?.sector || '-';
  let mktCap = object.quoteSummary.result[0]?.price?.marketCap?.fmt || '-';

  return [[ticker, fwdPE, sector, mktCap]];
}

[EDIT4] Result after running script for 4 times: [EDIT4] 运行脚本 4 次后的结果:

在此处输入图像描述

[EDIT5] Existing data in columns B and C gets overwritten [EDIT5] BC列中的现有数据被覆盖

Before running the script:在运行脚本之前: 在此处输入图像描述

After running the script:运行脚本后: 在此处输入图像描述

[EDIT6]: [编辑6]:

function trigger() {
  const max = 5; // From your question, maximum execution of "yahoo" is 5.

  const todayObj = new Date();
  const today = Utilities.formatDate(todayObj, Session.getScriptTimeZone(), "yyyyMMdd");
  const db = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('db2');
  const range = db.getRange('A2:AO' + db.getLastRow());

  const { values } = range.getValues().reduce((zo, zr) => {
    const [ticker, b, c, d, e, f, g, h, i, j, k, l, m, n, r, o, p, q, r, s, t, u, v, w, x, y, z, aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am, an, ao] = zr;
    if (zo.zc < max && (g.toString() == "" || Utilities.formatDate(an, Session.getScriptTimeZone(), "yyyyMMdd") != today)) {
      try {
        zo.zc++;
        zo.values.push([ticker, b, c, ...yahoo(ticker), todayObj, null]);
      } catch (_) {
        zo.values.push([ticker, b, c, d, e, f, g, h, i, j, k, l, m, n, r, o, p, q, r, s, t, u, v, w, x, y, z, aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, al, am, todayObj, ["", "0"].includes(an.toString()) ? 1 : ao + 1]);
      }
    } else {
      zo.values.push(zr);
    }
    return zo;
  }, { values: [], zc: 0 });
  range.setValues(values);
}


function yahoo(ticker) {
  const url = 'https://query2.finance.yahoo.com/v10/finance/quoteSummary/' + encodeURI(ticker) + '?modules=summaryDetail,financialData,defaultKeyStatistics';

  let response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
  if (response.getResponseCode() == 200) {
    var object = JSON.parse(response.getContentText());
  }

  // misc
  let marketCap               = object.quoteSummary.result[0]?.summaryDetail?.marketCap?.raw                       || '-';
  let dividendRate            = object.quoteSummary.result[0]?.summaryDetail?.dividendRate?.raw                    || '-';
  let dividendYield           = object.quoteSummary.result[0]?.summaryDetail?.dividendYield?.raw                   || '-';
  let payoutRatio             = object.quoteSummary.result[0]?.summaryDetail?.payoutRatio?.raw                     || '-';
  let fiveYAvgDivYield        = object.quoteSummary.result[0]?.summaryDetail?.fiveYearAvgDividendYield?.raw        || '-';
  let insidersPercentHeld     = object.quoteSummary.result[0]?.majorHoldersBreakdown?.insidersPercentHeld?.raw     || '-';
  let institutionsPercentHeld = object.quoteSummary.result[0]?.majorHoldersBreakdown?.institutionsPercentHeld?.raw || '-';
  
  // dates
  let earningsDate            = object.quoteSummary.result[0]?.calendarEvents?.earnings?.earningsDate[0]?.raw      || '-';
  let exDividendDate          = object.quoteSummary.result[0]?.calendarEvents?.exDividendDate?.raw                 || '-';
  let dividendDate            = object.quoteSummary.result[0]?.calendarEvents?.dividendDate?.raw                   || '-';

  // earnings
  let totalRevenue            = object.quoteSummary.result[0]?.financialData?.totalRevenue?.raw                    || '-';
  let revenueGrowth           = object.quoteSummary.result[0]?.financialData?.revenueGrowth?.raw                   || '-';
  let revenuePerShare         = object.quoteSummary.result[0]?.financialData?.revenuePerShare?.raw                 || '-';
  let ebitda                  = object.quoteSummary.result[0]?.financialData?.ebitda?.raw                          || '-';
  let grossProfits            = object.quoteSummary.result[0]?.financialData?.grossProfits?.raw                    || '-';
  let earningsGrowth          = object.quoteSummary.result[0]?.financialData?.earningsGrowth?.raw                  || '-';
  let grossMargins            = object.quoteSummary.result[0]?.financialData?.grossMargins?.raw                    || '-';
  let ebitdaMargins           = object.quoteSummary.result[0]?.financialData?.ebitdaMargins?.raw                   || '-';
  let operatingMargins        = object.quoteSummary.result[0]?.financialData?.operatingMargins?.raw                || '-';
  let profitMargins           = object.quoteSummary.result[0]?.financialData?.profitMargins?.raw                   || '-';

  // cash
  let totalCash               = object.quoteSummary.result[0]?.financialData?.totalCash?.raw                       || '-';
  let freeCashflow            = object.quoteSummary.result[0]?.financialData?.freeCashflow?.raw                    || '-';
  let opCashflow              = object.quoteSummary.result[0]?.financialData?.operatingCashflow?.raw               || '-';
  let cashPerShare            = object.quoteSummary.result[0]?.financialData?.totalCashPerShare?.raw               || '-';

  // debt
  let totalDebt               = object.quoteSummary.result[0]?.financialData?.totalDebt?.raw                       || '-';
  let debtToEquity            = object.quoteSummary.result[0]?.financialData?.debtToEquity?.raw                    || '-';

  // ratios
  let quickRatio              = object.quoteSummary.result[0]?.financialData?.quickRatio?.raw                      || '-';
  let currentRatio            = object.quoteSummary.result[0]?.financialData?.currentRatio?.raw                    || '-';
  let trailingEps             = object.quoteSummary.result[0]?.defaultKeyStatistics?.trailingEps?.raw              || '-';
  let forwardEps              = object.quoteSummary.result[0]?.defaultKeyStatistics?.forwardEps?.raw               || '-';
  let pegRatio                = object.quoteSummary.result[0]?.defaultKeyStatistics?.pegRatio?.raw                 || '-';
  let priceToBook             = object.quoteSummary.result[0]?.defaultKeyStatistics?.priceToBook?.raw              || '-';
  let returnOnAssets          = object.quoteSummary.result[0]?.financialData?.returnOnAssets?.raw                  || '-';
  let returnOnEquity          = object.quoteSummary.result[0]?.financialData?.returnOnEquity?.raw                  || '-';

  let enterpriseValue         = object.quoteSummary.result[0]?.defaultKeyStatistics?.enterpriseValue?.raw          || '-';
  let bookValue               = object.quoteSummary.result[0]?.defaultKeyStatistics?.bookValue?.raw                || '-';

  return [
    marketCap, dividendRate, dividendYield, payoutRatio, fiveYAvgDivYield, insidersPercentHeld, institutionsPercentHeld,
    earningsDate, exDividendDate, dividendDate,
    totalRevenue, revenueGrowth, revenuePerShare, ebitda, grossProfits, earningsGrowth, grossMargins, ebitdaMargins, operatingMargins, profitMargins,
    totalCash, freeCashflow, opCashflow, cashPerShare,
    totalDebt, debtToEquity,
    quickRatio, currentRatio, trailingEps, forwardEps, pegRatio, priceToBook, returnOnAssets, returnOnEquity,
    enterpriseValue, bookValue
  ];
}

I believe your goal is as follows.我相信你的目标如下。

  • By retrieving the values from column "A", you want to run the function yahoo , and want to update the columns "B" to "F".通过从“A”列中检索值,您想要运行函数yahoo ,并且想要将“B”列更新为“F”。
    • By checking the date of the column "E", you want to execute the function of yahoo .通过检查“E”列的日期,您想要执行yahoo的功能。
      • From your question and showing images, your values of column "E" is the date object.从您的问题和显示图像来看,“E”列的值是日期对象。 And, you want to check the year, month and day.并且,您要检查年、月和日。
    • In this case, you want to execute the function of yahoo only 5 times every running.在这种情况下,您希望每次运行只执行 5 次yahoo的功能。
    • When an error occurs from yahoo , you want to count up the column "F".yahoo发生错误时,您希望计算列“F”。 When no error occurs, you want to set null to the column "F".当没有发生错误时,您希望将“F”列设置为null
  • You want to execute the script by the time-driven trigger.您想通过时间驱动的触发器来执行脚本。
    • You can install the time-driven trigger by yourself.您可以自己安装时间驱动的触发器。

When I saw your script, the script for achieving the above goal is not included.当我看到你的脚本时,没有包含实现上述目标的脚本。 And, setValues is used in a loop.并且, setValues在循环中使用。 In this case, the process cost will become high.在这种情况下,处理成本会变高。

So, in your situation, in order to achieve your goal, how about the following sample script?那么,在您的情况下,为了实现您的目标,下面的示例脚本怎么样?

Sample script:示例脚本:

This script can be directly run with the script editor.这个脚本可以直接用脚本编辑器运行。 So, before you run this script using the time-driven trigger, I would like to recommend testing this script.因此,在您使用时间驱动触发器运行此脚本之前,我建议您测试此脚本。

After you tested this script and could confirm the output situation, please install the time-driven trigger to the function.测试此脚本并确认输出情况后,请在函数中安装时间驱动触发器。 By this, when you install the time-driven trigger to this function, the script is run by the trigger.这样,当您将时间驱动触发器安装到此函数时,脚本将由触发器运行。

function trigger() {
  const max = 5; // From your question, maximum execution of "yahoo" is 5.

  const todayObj = new Date();
  const today = Utilities.formatDate(todayObj, Session.getScriptTimeZone(), "yyyyMMdd");
  const db = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
  const range = db.getRange('A2:F' + db.getLastRow());

  const { values } = range.getValues().reduce((o, r) => {
    const [ticker, b, c, d, e, f] = r;
    if (o.c < max && (e.toString() == "" || Utilities.formatDate(e, Session.getScriptTimeZone(), "yyyyMMdd") != today)) {
      try {
        o.c++;
        o.values.push([...yahoo(ticker), todayObj, null]);
      } catch (_) {
        o.values.push([ticker, b, c, d, todayObj, ["", "0"].includes(f.toString()) ? 1 : f + 1]);
      }
    } else {
      o.values.push(r);
    }
    return o;
  }, { values: [], c: 0 });
  range.setValues(values);
}


function yahoo(ticker) {
  const url = 'https://query2.finance.yahoo.com/v10/finance/quoteSummary/' + encodeURI(ticker) + '?modules=price,assetProfile,summaryDetail';

  let response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
  if (response.getResponseCode() == 200) {
    var object = JSON.parse(response.getContentText());
  }

  let fwdPE = object.quoteSummary.result[0]?.summaryDetail?.forwardPE?.fmt || '-';
  let sector = object.quoteSummary.result[0]?.assetProfile?.sector || '-';
  let mktCap = object.quoteSummary.result[0]?.price?.marketCap?.fmt || '-';

  return [ticker, fwdPE, sector, mktCap];
}
  • When this script is run, I thought that your above goal might be able to be achieved.运行此脚本时,我认为您的上述目标可能能够实现。 So,所以,

  • From your actual yahoo , the returned value is different from your 1st script.从您的实际yahoo中,返回的值与您的第一个脚本不同。 So, I also modified it.所以,我也修改了它。

Note:笔记:

  • Unfortunately, I cannot imagine the actual script of yahoo(ticker) .不幸的是,我无法想象yahoo(ticker)的实际脚本。 So, in order to check the error, I used try-catch.因此,为了检查错误,我使用了 try-catch。 In this case, it supposes that when the values are not retrieved, an error occurs in yahoo(ticker) .在这种情况下,它假设当未检索到值时, yahoo(ticker)中发生错误。 Please be careful about this.请注意这一点。

  • I cannot understand your actual script of yahoo(ticker) .我无法理解您的yahoo(ticker)实际脚本。 So, please be careful about this.所以,请注意这一点。

  • From your question and showing images, I understood that you wanted to check the year, month and day.从您的问题和显示图像中,我了解到您想检查年、月和日。 Please be careful about this.请注意这一点。

Reference:参考:

Added:添加:

From your following additional question,根据您的以下附加问题,

also, I have added to the example sheet a second tab (db2) if I could ask you to have a brief look.另外,如果我可以请您简要看一下,我已经在示例表中添加了第二个选项卡 (db2)。 Here, I have added 2 columns in between ticker and the rest of the data that yahoo() is returning.在这里,我在代码和 yahoo() 返回的其余数据之间添加了 2 列。 Assume that I want to fill in other data here.假设我要在这里填写其他数据。 Would it be possible to adjust your script so that it leaves these columns alone, so only works on columns A and D to H?是否可以调整您的脚本以使其不理会这些列,因此仅适用于 A 和 D 到 H 列?

I understood that you want to add the empty 2 columns "B" and "C" to the result array.我知道您想将空的 2 列“B”和“C”添加到结果数组中。 In this case, please test the following sample script.在这种情况下,请测试以下示例脚本。

Sample script:示例脚本:

function trigger() {
  const max = 5; // From your question, maximum execution of "yahoo" is 5.

  const todayObj = new Date();
  const today = Utilities.formatDate(todayObj, Session.getScriptTimeZone(), "yyyyMMdd");
  const db = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('db2');
  const range = db.getRange('A2:H' + db.getLastRow());

  const { values } = range.getValues().reduce((o, r) => {
    const [ticker, b, c, d, e, f, g, h] = r;
    if (o.c < max && (g.toString() == "" || Utilities.formatDate(g, Session.getScriptTimeZone(), "yyyyMMdd") != today)) {
      try {
        o.c++;
        o.values.push([ticker, b, c, ...yahoo(ticker), todayObj, null]);
      } catch (_) {
        o.values.push([ticker, b, c, d, e, f, todayObj, ["", "0"].includes(f.toString()) ? 1 : h + 1]);
      }
    } else {
      o.values.push(r);
    }
    return o;
  }, { values: [], c: 0 });
  range.setValues(values);
}


function yahoo(ticker) {
  const url = 'https://query2.finance.yahoo.com/v10/finance/quoteSummary/' + encodeURI(ticker) + '?modules=price,assetProfile,summaryDetail';

  let response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
  if (response.getResponseCode() == 200) {
    var object = JSON.parse(response.getContentText());
  }

  let fwdPE = object.quoteSummary.result[0]?.summaryDetail?.forwardPE?.fmt || '-';
  let sector = object.quoteSummary.result[0]?.assetProfile?.sector || '-';
  let mktCap = object.quoteSummary.result[0]?.price?.marketCap?.fmt || '-';

  return [fwdPE, sector, mktCap];
}
  • Both trigger and yahoo functions were modified. triggeryahoo功能都进行了修改。 And, in order to use your 2nd tab of your provided Spreadsheet, the sheet name was also changed to db2 .而且,为了使用您提供的电子表格的第二个选项卡,工作表名称也更改为db2 Please be careful about this.请注意这一点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM