Last active
February 9, 2023 19:51
-
-
Save alobato/ab778f4ee9bd1799824f12169d5ff391 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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