Verified Commit e5822e2e authored by Melroy van den Berg's avatar Melroy van den Berg
Browse files

Add time date to logging. Remove obsolete exchange setting. Update readme. Fix...

Add time date to logging. Remove obsolete exchange setting. Update readme. Fix package.json. Add verbose debug logging to debug strange issue.
parent 347bfc05
Pipeline #3571 passed with stage
in 11 seconds
# Crypto Alert Bot
`crypto-bot` is an open-source bot that track some popular crypto tokens/coins (BTC, ETH, ADA, LINK, SOL, DOT, MATIC, ALGO and AVAX). The bot gives you a notification when the trend is changing (up/down trend).
`crypto-bot` is an open-source bot that track some popular crypto tokens/coins (BTC, ETH, ADA, LINK, SOL, DOT, MATIC, ALGO, AVAX and VET). The bot gives you a notification when the trend is changing (up/down trend).
This bot will be informed you via the [Telegram channel](https://t.me/crypto_exchange_updates) automatically, whenever there is an up- or downtrend in one of those crypto coins. Technical analysis is applied to determine the up- or downtrends (MACD/PPO).
This bot will is already *running live* in the [following Telegram channel](https://t.me/crypto_exchange_updates), whenever there is an up- or downtrend you automatically get notified of each of the previously mentioned crypto coins. Technical analysis is applied to determine the up- or downtrends (based on MACD crosses of PPO indicator).
## Usage
......@@ -48,6 +48,8 @@ Follow the steps:
3. Install depedencies via: `npm install` (once needed)
4. Start the bot using: `npm start`
During development you could use: `npm run start-fake`. Which will start the app, but **not** connect to the TwelveData API.
**Advice:** Run the bot 24/7 on some dedicated hardware. `cron_time` within the configuration will take care of the triggers when to look-up for data.
**Hidden feature:** Set `DEBUG` to `true` value in the [dataProcessor.js](src/dataProcessor.js) source file to dump the market data to a comma-seperated values (CSV) file. Useful for off-line verification/calculations.
......
......@@ -9,8 +9,8 @@ telegram_settings:
tickers:
params:
crypto_symbols_pairs: [ "BTC/USD", "ETH/USD", "ADA/USD", "LINK/USD", "SOL/USD", "pDOTn/USD", "MATIC/USD", "ALGO/USD", "AVAX/USD" ]
exchange: "Binance" # Default timezone for Binance data is UTC (Coordinated Universal Time)
# Optionally you could provide the crypto exchange like so: BTC/USD:Huobi
crypto_symbols_pairs: [ "BTC/USD", "ETH/USD", "ADA/USD", "LINK/USD", "SOL/USD", "pDOTn/USD", "MATIC/USD", "ALGO/USD", "AVAX/USD", "VET/USD" ]
interval: "1week"
outputsize: 100
cron_time: "0 15 * * * *" # Notation: Second-Minute-Hour-Day-Month-WeekDay. So, every hour at hh:15 hour, since crypto can be volatile. Keep the time between cron triggers above 10 mins to avoid API rate limit issues.
......
This diff is collapsed.
{
"name": "crypto-bot",
"version": "2.5.1",
"description": "Crypto tokens bot notifier",
"description": "Crypto Bot coins/tokens Telegram notifier",
"main": "src/index.js",
"scripts": {
"start": "node .",
......@@ -39,7 +39,7 @@
"yaml": "^1.10.2"
},
"devDependencies": {
"eslint": "^8.3.0",
"eslint": "^7.32.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-node": "^11.1.0",
......
......@@ -34,9 +34,11 @@ class Communicate {
if (fs.existsSync(tempFilePath)) {
const data = this.readContent(tempFilePath)
// Check if the current MACD cross time is later than the previous stored time.
// If this is true, we found a new MACD cross on the PPO indicator! So let's send an update message.
sendMessage = (currentTime > data.time)
} else {
console.warn('WARN: Missing crypto temp file on disk (' + tempFilename + '). First run/trigger?')
console.warn(Util.getCurrentDateTime() + ' - WARN: Missing crypto temp file on disk (' + tempFilename + '). First run/trigger?')
sendMessage = true // Always send a message the first time, if file does not yet exists.
}
......@@ -63,7 +65,7 @@ class Communicate {
break
}
message += `\n\nHistogram: ${histogram}% (before: ${prevHistogram}%). High: ${high}. Low: ${low}. Close: ${close}. MACD cross date: ${dateString}.`
message += '\n\n[Open ' + symbolPair + ' chart (TradingView)](https://www.tradingview.com/chart?symbol=FTX:' + symbolURITradingView + ')'
message += '\n\n[Open ' + symbolPair + ' chart (TradingView)](https://www.tradingview.com/chart?symbol=Binance:' + symbolURITradingView + ')'
this.sendTelegramMessage(message)
messageSend = true // Only used for debug console message
......@@ -74,7 +76,7 @@ class Communicate {
}
}
if (messageSend === false) {
console.log('DEBUG: No new MACD crosses detected for ' + symbolPair + '. Do not send a message update.')
console.debug(Util.getCurrentDateTime() + ' - No new MACD crosses detected for ' + symbolPair + '. Don\'t send update.')
}
}
......@@ -82,7 +84,7 @@ class Communicate {
* Helper method for sending the message to Telegram bot
*/
sendTelegramMessage (message) {
console.log('INFO: Try sending the following message to Telegram channel: ' + message)
console.info(Util.getCurrentDateTime() + ' - Sending message send to Telegram: ' + message)
this.bot.sendMessage(this.botChatID, message, this.sendMessageOptions).catch(error => {
console.error('ERROR: Could not send Telegram message: "' + message + '", due to error: ' + error.message)
......
......@@ -36,11 +36,11 @@ class DataProcessor {
let nrOfDataPoints = this.warmupPeriod + this.dataPeriod
let firstIndexUsed = (values.length - 1) - (this.dataPeriod - 1)
if (values.length < nrOfDataPoints) {
console.error('ERROR: Not enough data received from API for symbol: ' + data.symbol + '. Expected: ' + nrOfDataPoints + ' (' + this.warmupPeriod + '+' + this.dataPeriod + ') - Actual: ' + values.length)
console.error(Util.getCurrentDateTime() + ' - ERROR: Not enough data received from API for symbol: ' + data.symbol + '. Expected: ' + nrOfDataPoints + ' (' + this.warmupPeriod + '+' + this.dataPeriod + ') - Actual: ' + values.length)
nrOfDataPoints = values.length
}
if (firstIndexUsed < 0) {
console.error('ERROR: Index of first used data point out-of-range for symbol: ' + data.symbol + '.')
console.error(Util.getCurrentDateTime() + ' - ERROR: Index of first used data point out-of-range for symbol: ' + data.symbol + '.')
firstIndexUsed = 0
}
const lastDataPoints = values.slice(values.length - nrOfDataPoints, values.length)
......@@ -50,6 +50,7 @@ class DataProcessor {
// We could create a buffer of history of PPO,
// or just save what we need for now: previous PPO histogram
let previousPPO = null
console.log('VERBOSE: Symbol: ' + data.symbol + ' Start time: ' + startTimestamp + '(I:' + firstIndexUsed + ')')
for (const tick of lastDataPoints) {
// Update indicator based on close price
ppo.update(tick.close)
......@@ -66,6 +67,7 @@ class DataProcessor {
}
// Only check data after warming-up period
console.log('VERBOSE: Tick time: ' + tick.time)
if (tick.time > startTimestamp) {
// Check for MACD crosses
if (previousPPO !== null) {
......@@ -73,6 +75,7 @@ class DataProcessor {
const bullish = Math.sign(previousPPO.hist) === -1 &&
(Math.sign(currentPPO.hist) === 1 || Math.sign(currentPPO.hist) === 0)
if (bullish) {
console.log('VERBOSE: Bull cross found!')
crosses.push({
type: 'bullish',
close: tick.close,
......@@ -88,6 +91,7 @@ class DataProcessor {
const bearish = (Math.sign(previousPPO.hist) === 0 || Math.sign(previousPPO.hist) === 1) &&
(Math.sign(currentPPO.hist) === -1)
if (bearish) {
console.log('VERBOSE: Bear cross found!')
crosses.push({
type: 'bearish',
close: tick.close,
......
......@@ -17,6 +17,7 @@ const TEST_API_SECRET_HASH = crypto.randomBytes(40).toString('hex')
const TelegramBot = require('node-telegram-bot-api')
const express = require('express')
const Communicate = require('./communicate')
const Util = require('./util')
const { version } = require('../package.json')
const cfg = YAML.parse(fs.readFileSync('config.yml', 'utf8').toString())
......@@ -24,12 +25,12 @@ if (!cfg) {
throw new Error('Please create a config.yml file.')
}
if (cfg.exchange_settings.use_cache) {
console.log('WARN: Cached market data files will be used (if available).')
console.warn(Util.getCurrentDateTime() + ' - WARN: Cached market data files will be used (if available).')
}
console.log('INFO: Using Telegram channel chat ID: ' + cfg.telegram_settings.chat_id)
console.log('INFO: Current test API hash: ' + TEST_API_SECRET_HASH)
console.log('INFO: Current crypto coins will be tracked: ' + cfg.tickers.params.crypto_symbols_pairs)
console.info(Util.getCurrentDateTime() + ' - Using Telegram channel chat ID: ' + cfg.telegram_settings.chat_id)
console.info(Util.getCurrentDateTime() + ' - Current test API hash: ' + TEST_API_SECRET_HASH)
console.info(Util.getCurrentDateTime() + ' - Current crypto coins will be tracked: ' + cfg.tickers.params.crypto_symbols_pairs)
// Setup Telegram bot
const bot = (isFake) ? {} : new TelegramBot(cfg.telegram_settings.bot_token)
......@@ -39,7 +40,7 @@ if (isFake) {
bot.onText = (regexp, callback) => {}
bot.sendMessage = (chatId, text, form = {}) => {
return new Promise(function (resolve, reject) {
console.log('Send Messaged (just a drill)! Chat ID: ' + chatId + ' with message: ' + text)
console.log(Util.getCurrentDateTime() + ' - Send messaged (just a drill)! Chat ID: ' + chatId + ' with message: ' + text)
resolve()
})
}
......@@ -84,13 +85,13 @@ app.get('/health', (req, res) => {
// Simple ping command
bot.onText(/\/ping/, () => {
bot.sendMessage(cfg.telegram_settings.chat_id, 'Pong').catch(error => {
console.log('ERROR: Could not send pong message, due to error: ' + error.message)
console.error(Util.getCurrentDateTime() + ' - ERROR: Could not send pong message, due to error: ' + error.message)
})
})
// Start Express Server
app.listen(port, host, () => {
console.log(`INFO: Crypto Exchange Bot v${version} is now running on ${host} on port ${port}.`)
console.info(`${Util.getCurrentDateTime()} - Crypto Exchange Bot v${version} is now running on ${host} on port ${port}.`)
})
/**
......@@ -108,7 +109,7 @@ function fetchData (symbolPairs) {
}
})
.catch(error => {
console.error('Error: Something went wrong during getting or processing the stock market data. With message: ' + error.message + '. Stack:\n')
console.error(Util.getCurrentDateTime() + ' - Error: Something went wrong during getting or processing the stock market data. With message: ' + error.message + '. Stack:\n')
console.error(error.stack)
})
}
......@@ -147,4 +148,4 @@ function sliceIntoChunks (arr, chunkSize) {
// Cron job for onTickCryptoExchange()
const job = new CronJob(cfg.tickers.cron_time, onTickCryptoExchange, null, false, cfg.tickers.cron_timezone)
job.start()
console.log('INFO: Cron triggers scheduled for (upcoming 10 shown):\n - ' + job.nextDates(10).join('\n - '))
console.info(Util.getCurrentDateTime() + ' - Cron triggers scheduled for (upcoming 10 shown):\n - ' + job.nextDates(10).join('\n - '))
......@@ -22,6 +22,14 @@ class Util {
}
return n
}
/**
* Return current date + time
* @returns Current date time string
*/
static getCurrentDateTime () {
return new Date().toLocaleString('nl-NL')
}
}
module.exports = Util
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment