Skip to content

Instantly share code, notes, and snippets.

@kazuhito-m
Last active January 25, 2024 11:21
Show Gist options
  • Save kazuhito-m/354501a372a796812fdbf8bfb9a9ec5e to your computer and use it in GitHub Desktop.
Save kazuhito-m/354501a372a796812fdbf8bfb9a9ec5e to your computer and use it in GitHub Desktop.
AlertStockQuotations.gs
/**
* 設定した前週比の下落幅パーセントを下回った場合、チャットにアラートするGASスクリプト。
*
* 決まった形式のGoogleSpreadSheetの情報を元に、指定銘柄の前週比(スケジューリングで一週間ごとに実行)を記録、
* 指定した比率以下に下落した場合、Discordのチャンネルに通知する。
*
* 環境変数(スクリプトプロパティに設定する)
* - SPREAD_SHEET_URL: データ&設定を仕込んだGoogleSpreadSheetのURL
* - DISCORD_ENDPOINT: Discordの特定チャンネルへのWebHook URL
*/
function main() {
const spreadSheetUrl = getPropertyOf('SPREAD_SHEET_URL');
const spreadSheet = SpreadsheetApp.openByUrl(spreadSheetUrl);
for (let sheet of spreadSheet.getSheets()) {
recordNowStockPrice(sheet);
}
for (let sheet of spreadSheet.getSheets()) {
alertDownStockPrice(sheet);
}
}
function recordNowStockPrice(sheet) {
const code = sheet.getRange('B2').getValue();
const stockPrice = getGoogleFinanceNowPriceOf(code);
const row = findNextDataRow(sheet);
sheet.getRange('A' + row).setValue(new Date());
sheet.getRange('B' + row).setValue(stockPrice);
}
function getGoogleFinanceNowPriceOf(googleFinanceCode) {
const url = `https://www.google.com/finance/quote/${googleFinanceCode}`;
const html = UrlFetchApp.fetch(url).getContentText();
const stockPrice = Parser.data(html)
.from('<div class="YMlKec fxKbKc">')
.to('</div>')
.build();
return stockPrice;
}
function alertDownStockPrice(sheet) {
const alertDownParcentages = sheet.getRange('F2').getValue()
.toString().split(',');
const lastDataRow = findNextDataRow(sheet) - 1;
const profitAndLossRatio = sheet.getRange('D' + lastDataRow).getValue();
for (let parcent of alertDownParcentages) {
const alertDownRatio = parcent / -100;
if (profitAndLossRatio < alertDownRatio) {
sendAlert(sheet.getName(), parcent, profitAndLossRatio * 100);
break;
}
}
}
function sendAlert(name, downBaseParcent, profitAndLossParcent) {
console.log(name + ':' + downBaseParcent + '% , ' + profitAndLossParcent + '%');
sendAlertDiscord(name, downBaseParcent, profitAndLossParcent);
}
function sendAlertDiscord(name, downBaseParcent, profitAndLossParcent) {
const lossParcent = Math.round(profitAndLossParcent * 100) / 100;
const body = `{
"embeds": [
{
"color": 16711680,
"title": "株価暴落アラート",
"description": "\`${name}\` に、前週比 \`${downBaseParcent}%\` 以上の下落が検知されました。",
"fields": [
{
"name": "銘柄",
"value": "\`${name}\`",
"inline": false
},
{
"name": "実際の下落率",
"value": "\`${lossParcent}\` %",
"inline": false
}
]
}
]
}`;
sendDiscordMessage(body);
}
function sendDiscordMessage(bodyJson) {
const endPont = getPropertyOf('DISCORD_ENDPOINT');
const headers = {
'Content-type': 'application/json',
};
const options = {
'method': 'POST',
'headers': headers,
'payload': bodyJson
};
UrlFetchApp.fetch(endPont, options);
}
function findNextDataRow(sheet) {
const endRow = sheet.getLastRow();
for (let i = 5; i <= endRow; i++) {
const text = sheet.getRange('A' + i).getValue().toString();
if (text.trim() === '') return i;
}
return endRow;
}
function getPropertyOf(id) {
return PropertiesService.getScriptProperties().getProperty(id);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment