具有 Google 表的每日股票价格警报的 GAS 脚本

GAS script for Daily Stock Price Alert with Google Sheets

我在 Google 表格中找到了一个很棒的 article 股票警报系统 GAS 脚本。它激励我构建更广泛的东西,包括每日 % 移动的警报,但这超出了重点。

作者建议应用基于时间的触发器,以便当股票达到预定义的目标价格时,接收者会收到实时电子邮件提醒。该脚本运行良好,但有一个问题:只要条件成立,它就会在每个周期内发送一封电子邮件。换句话说,如果触发器设置为每分钟,您将被电子邮件轰炸。

这是基础脚本。

    function myFunction() {
      
      var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
      
      var values = spreadSheet.getDataRange().getValues();
      values.shift();
      
      var emails = ['abc@gmail.com'];
      
      for (stock in values) {
        
        // Grabs the array we want to access
        var stockArray = values[stock];   
        
        // The first item in the array is the ticker
        var stockTicker = stockArray[0];  
        
        // The CURRENT price of the stock
        var stockCurrent = stockArray[1]; 
        
        // Mathematical symbol to compare the prices (>,<)
        var stockSymbol = stockArray[2];  
        
        // The TARGET price of the stock 
        var stockTarget = stockArray[3];  
        
        // If we want to use the less than symbol
        if ( stockSymbol === '<' ) {
          
          // If current price is less than target
          if ( stockCurrent < stockTarget) {
            // Do something
          }
        }
        
        // If we want to use the greater than symbol
        if ( stockSymbol === '>' ) {
          
          // If current price is greater than target
          if ( stockCurrent > stockTarget) {
            // Do something
          }
        }   
      } 
    }

我只想(每天)收到一次警报。 author 通过在 'else' 条件后添加以下函数解决了类似的问题:

function createTriggerCheckAgain() {
      ScriptApp.newTrigger('myFunction')
      .timeBased()
      .after(60 * 60 * 24 * 1000) // one day cycle time
      .create();}
      }

我尝试将此功能添加到原始脚本中,但不幸的是,每次脚本运行时我仍然收到一封警告邮件。

为了检查脚本是否实时运行,我添加了电子邮件功能。把它们放在一起给出以下内容。这是 spreadsheet 的 link。

function myFunction() {
  
  var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
  
  var values = spreadSheet.getDataRange().getValues();
  values.shift();
  
  var emails = ['webkowuite@gmail.com'];
  
  for (stock in values) {
    
    // Grabs the array we want to access
    var stockArray = values[stock];   
    
    // The first item in the array is the ticker
    var stockTicker = stockArray[0];  
    
    // The CURRENT price of the stock
    var stockCurrent = stockArray[1]; 
    
    // Mathematical symbol to compare the prices (>,<)
    var stockSymbol = stockArray[2];  
    
    // The TARGET price of the stock 
    var stockTarget = stockArray[3];  
        
    // Emails
    var emailSubject = 'PRICE ALERT';
    var emailBody = 'your stock '+stockTicker+' has moved to its target price';
    
    // If we want to use the less than symbol
    if ( stockSymbol === '<' ) {
      
      // If current price is less than target
      if ( stockCurrent < stockTarget) {
        
        for (email in emails) {
          MailApp.sendEmail(emails[email], emailSubject, '', {
            htmlBody: emailBody
          });
        }
        
      }
      else {
        function createTriggerCheckAgain() {
          ScriptApp.newTrigger('myFunction')
          .timeBased()
          .after(60 * 60 * 24 * 1000)
          .create();
    }
  }
    }
    
    // If we want to use the greater than symbol
    if ( stockSymbol === '>' ) {
      
      // If current price is greater than target
      if ( stockCurrent > stockTarget) {
        
        for (email in emails) {
          MailApp.sendEmail(emails[email], emailSubject, '', {
            htmlBody: emailBody
          });
        }
        
      }
    }
   else {
    function createTriggerCheckAgain() {
      ScriptApp.newTrigger('myFunction')
      .timeBased()
      .after(60 * 60 * 24 * 1000)
      .create();
    }
  } 
  }
  
}

任何 tips/help 将不胜感激。


顺便说一句,原始文章的评论者建议了一条不同的路线:

// Named range could be get spreadsheet-wise
var notified_range = spread_sheet.getRangeByName('Notified?');
var notified_values = notified_range.getValues();
...
// We don't want to spam ourselves
var isNotified = notified_values[+stock+1][0];
...
if ( stockSymbol === '<' ) {
if ( stockCurrent < stockTarget && isNotified != true) {
for (email in emails) {
MailApp.sendEmail(emails[email], emailSubject, '', {
htmlBody: emailBody
});
}
// GetCell index starts from 1, not 0. So we +2
notified_range.getCell(stock+2, 1).setValue(true);
}
}

我已经尝试在原始代码中实现这些。也没有成功。

您的代码中存在括号不匹配以及我尝试优化的一些冗余部分。

你能检查一下这是否符合你的要求吗?

代码:

function myFunction() {
  var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
  var values = spreadSheet.getDataRange().getValues();
  values.shift();

  var stocks = [];
  var emails = ['test@gmail.com'];
  var emailSubject = 'PRICE ALERT';
  var emailBody = 'The following tickers reached its target price:';

  values.forEach(function (stockArray, index) {
    // ticker, CURRENT price, comparison symbol, TARGET price
    var [stockTicker, stockCurrent, stockSymbol, stockTarget, dates] = stockArray;
    
    var dateToday = convertDate(new Date());
    // Make string into array and removes all blank elements
    var dateArray = dates.toString().split(",").filter(date => date);
    // Properly format dates 
    dateArray.forEach(function (date, index){
      dateArray[index] = convertDate(new Date(date));
    });
    
    // If current price is <stockSymbol> than target and dateToday isn't in column E
    if (eval(stockCurrent + " " + stockSymbol + " " + stockTarget) && !dateArray.includes(dateToday)) {
      // Take note of the tickers so we can send in 1 email
      stocks.push(stockTicker);
      // Add dateToday to dateArray if date is new
      dateArray.push(dateToday);
      // Set dateArray as string as value to column E 
      spreadSheet.getRange("E" + (index + 2)).setValue(dateArray.join(","));
    }
  });

  // Only proceed to mail if we had a new valid ticker that satisfies the condition
  if(stocks.length > 0) {
    // Join all stocks in one email per user, send as 1 email
    emailBody = emailBody + "<br> - " + stocks.join("<br> - ");
    emails.forEach(function (email) {
      MailApp.sendEmail(email, emailSubject, '', {
        htmlBody: emailBody
      });
    });
  }

  // Create trigger after every run (will trigger every minute)
  createTrigger();
}

function convertDate(date){
  // Convert date to proper timezone
  return Utilities.formatDate(date, SpreadsheetApp.getActive().getSpreadsheetTimeZone(), "MM/dd/YYYY");
}

function createTrigger(){
  // Delete all existing triggers before creating one
  // Ensuring none will exist before creating trigger.
  var triggers = ScriptApp.getProjectTriggers();
  triggers.forEach(function (trigger){
    ScriptApp.deleteTrigger(trigger);
  });

  // Create trigger after every run which is per minute
  ScriptApp.newTrigger('myFunction')
    .timeBased()
    .after(60 * 1000)
    .create();
}

示例输出:

注:

如果我没记错的话,这些代码的价格可以随时更改,但每天只能更改一次。所以上面代码上面的代码只会在当天第一次满足条件时给你发邮件。[​​=15=]

这里的触发器不会达到限制,因为我总是在添加一个之前删除所有触发器,所以我们确信它不会因为达到触发器的限制而失败。

要测试,只需 运行 函数一次,然后让触发器完成它的工作。

另外,您可能 运行 由于配额问题。如需更多 information/details,请阅读以下资源:

Resource/s: