Skip to content

Instantly share code, notes, and snippets.

@jannes-io
Created February 14, 2021 11:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jannes-io/a31f02e2100c7a2c786347d00820e974 to your computer and use it in GitHub Desktop.
Save jannes-io/a31f02e2100c7a2c786347d00820e974 to your computer and use it in GitHub Desktop.
Stock alert thing I wrote when intoxicated
import './env';
import 'reflect-metadata';
import { launch, Page } from 'puppeteer';
import { Connection } from 'typeorm';
import * as AWS from 'aws-sdk';
import { SendEmailRequest } from 'aws-sdk/clients/ses';
import { establishDbConnection } from './services';
import { Alert, AlertType } from './entities';
AWS.config.update({
region: 'eu-west-1',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_ACCESS_KEY_SECRET,
},
});
const SESClient = new AWS.SES({ apiVersion: '2010-12-01' });
const sendMail = async (alert: Alert) => {
let action = '';
switch (alert.type) {
case AlertType.STOP_LOSS:
case AlertType.LIMIT_SELL:
action = `SELL AT ${alert.price}`;
break;
case AlertType.LIMIT_BUY:
action = `BUY AT ${alert.price}`;
break;
default:
}
const htmlBody = `
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<p>STOCK ALERT TRIGGERED</p>
<p>type: ${alert.type}</p>
<p>stock: ${alert.exchange} - ${alert.symbol}</p>
<p>action: ${action}</p>
</body>
</html>
`;
const params: SendEmailRequest = {
Destination: { ToAddresses: [process.env.SES_MAIL_TO] },
Source: `stock alerts <${process.env.SES_MAIL_FROM}>`,
Message: {
Subject: {
Charset: 'UTF-8',
Data: `Stock alert triggered: ${alert.symbol} - ${action}`,
},
Body: {
Html: {
Charset: 'UTF-8',
Data: htmlBody,
},
},
},
};
try {
await SESClient.sendEmail(params).promise();
} catch (e) {
console.error('Failed to send alert', e);
}
};
const handleAlerts = async (page: Page, stock: string, alerts: Alert[]) => {
const url = `https://www.tradingview.com/symbols/${stock}`;
await page.goto(url);
const selector = '.tv-symbol-price-quote__value span';
const currentPriceStr = await page.$$eval(selector, (nodes) => nodes[0].innerHTML);
const currentPrice = parseFloat(currentPriceStr);
await Promise.all(alerts.map(async (alert) => {
switch (alert.type) {
case AlertType.LIMIT_SELL:
if (currentPrice > alert.price) {
await sendMail(alert);
}
break;
case AlertType.LIMIT_BUY:
case AlertType.STOP_LOSS:
if (currentPrice < alert.price) {
await sendMail(alert);
}
break;
default:
}
}));
};
const watchStocks = async (db: Connection, page: Page) => {
const allAlerts = await db.getRepository(Alert).find();
const alertMap = allAlerts.reduce((acc: Record<string, Alert[]>, alert) => {
const stock = `${alert.exchange}-${alert.symbol}`;
if (acc[stock] === undefined) {
acc[stock] = [];
}
acc[stock].push(alert);
return acc;
}, {});
const alertPromises = Object
.entries(alertMap)
.map(([stock, alerts]) => handleAlerts(page, stock, alerts));
await Promise.allSettled(alertPromises);
// setTimeout(() => watchStocks(db), 5 * 60 * 1000);
};
const run = async () => {
const browser = await launch();
const page = await browser.newPage();
const db = await establishDbConnection();
await watchStocks(db, page);
return browser;
};
run()
.then((browser) => browser.close())
.catch(console.error);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment