具有 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:
我在 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,请阅读以下资源: