Skip to content

Instantly share code, notes, and snippets.

@alobato
Last active February 9, 2023 19:51
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 alobato/ab778f4ee9bd1799824f12169d5ff391 to your computer and use it in GitHub Desktop.
Save alobato/ab778f4ee9bd1799824f12169d5ff391 to your computer and use it in GitHub Desktop.
#!/usr/bin/env node
import { Command } from 'commander'
import got from 'got'
import fs from 'fs'
const fsp = fs.promises
const LOOP_DELAY = 100
async function read(tempFile) {
try {
const data = await fsp.readFile(tempFile)
return JSON.parse(data)
} catch (err) {
return null
}
}
async function write(data, tempFile) {
try {
await fsp.writeFile(tempFile, JSON.stringify(data))
} catch(err) {}
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
function calculateMA(values, period) {
let sum = 0
for (let i = 0; i < period; i++) {
sum += values[i]
}
const movingAverages = [sum / period]
for (let i = period; i < values.length; i++) {
sum -= values[i - period]
sum += values[i]
movingAverages.push(sum / period)
}
return movingAverages
}
function calculateSTD(values, period, movingAverage) {
let sum = 0
for (let i = 0; i < period; i++) {
sum += Math.pow(values[i] - movingAverage, 2)
}
const standardDeviations = [Math.sqrt(sum / period)]
for (let i = period; i < values.length; i++) {
sum -= Math.pow(values[i - period] - movingAverage, 2)
sum += Math.pow(values[i] - movingAverage, 2)
standardDeviations.push(Math.sqrt(sum / period))
}
return standardDeviations
}
function calculateBollingerBands(closes, period, multiplier) {
const movingAverage = calculateMA(closes, period)
const standardDeviation = calculateSTD(closes, period, movingAverage[period - 1])
const upperBand = []
const lowerBand = []
for (let i = period - 1; i < closes.length; i++) {
upperBand.push(movingAverage[i] + (multiplier * standardDeviation[i - period + 1]))
lowerBand.push(movingAverage[i] - (multiplier * standardDeviation[i - period + 1]))
}
return { upperBand, lowerBand }
}
function getGainLoss(closes) {
const gains = []
const losses = []
for (let i = 1; i < closes.length; i++) {
if (closes[i] > closes[i - 1]) {
gains.push(closes[i] - closes[i - 1])
} else {
losses.push(closes[i - 1] - closes[i])
}
}
return { gains, losses }
}
function calculateRSI(closes, period) {
const { gains, losses } = getGainLoss(closes)
const avgGain = gains.reduce((a, b) => a + b, 0) / period
const avgLoss = losses.reduce((a, b) => a + b, 0) / period
const rs = avgGain / avgLoss
return 100 - (100 / (1 + rs))
}
const program = new Command()
program
.option('-t, --tg-token <token>', 'Telegram Token')
.option('-c, --tg-chat-id <chatId>', 'Telegram chat id')
.option('-f, --temp-file <tempFile>', 'Temp file')
.option('-s, --symbols <symbols>', 'Symbols list')
.option('-m, --min <min>', 'Min RSI')
.option('-x, --max <max>', 'Max RSI');
program.parse(process.argv)
const options = program.opts()
;(async () => {
const symbols = options.symbols.split(',')
const lastSentSymbols = []
for (const symbol of symbols) {
await sleep(LOOP_DELAY)
const data = await got.get(`https://www.binance.com/api/v3/klines?symbol=${symbol.replace('_', '')}&interval=15m`).json();
const closes = data.map((d) => parseFloat(d[4]))
const rsiPeriod = 14
const rsi = calculateRSI(closes, rsiPeriod)
console.log('rsi', rsi)
calculateBollingerBands(closes, 20, 2)
const bollingerBandsPeriod = 20
const bollingerBandsPerioMultiplier = 2
const { upperBand, lowerBand } = calculateBollingerBands(closes, bollingerBandsPeriod, bollingerBandsPerioMultiplier)
console.log('upperBand', upperBand)
console.log('lowerBand', lowerBand)
// const lastUpperBand = upperBand[upperBand.length - 1]
// const lastLowerBand = lowerBand[lowerBand.length - 1]
// console.log('lastUpperBand', lastUpperBand)
// console.log('lastLowerBand', lastLowerBand)
if (rsi > Number(options.min) || rsi < Number(options.max)) {
let lastSymbols
const data = await read(options.tempFile)
if (data) {
lastSymbols = data.lastSentSymbols
}
if (!lastSymbols || lastSymbols.length === 0 || !lastSymbols.includes(symbol)) {
try { await got.post(`https://api.telegram.org/bot${options.tgToken}/sendMessage`, { json: { disable_web_page_preview: true, parse_mode: 'html', chat_id: options.tgChatId, text: `<a href="https://www.binance.com/pt-BR/trade/${symbol}">${symbol} ${rsi.toFixed(2)}</a>` } }).json() } catch (err) { console.error(err) }
lastSentSymbols.push(symbol)
}
}
}
if (lastSentSymbols.length > 0) {
await write({ lastSentSymbols }, options.tempFile)
}
process.exit()
})().catch((error) => {
console.error(error)
process.exit(1)
})
{
"name": "indicators",
"version": "1.0.0",
"description": "indicators",
"type": "module",
"bin": "./index.mjs",
"dependencies": {
"commander": "10.0.0",
"got": "12.5.3"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment