Created
April 21, 2020 23:02
-
-
Save kroitor/56e454223d4ee82e1b45d66e744825b3 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
"use strict"; | |
/* ------------------------------------------------------------------------ */ | |
const blessed = require ('blessed') | |
, ccxt = require ('ccxt') | |
, log = require ('ololog') | |
, ansi = require ('ansicolor').nice | |
, StackTracey = require ('stacktracey') | |
, asciichart = require ('asciichart') | |
, asTable = require ('as-table').configure ({ | |
delimiter: ' | '.lightGray.dim, | |
right: true, | |
title: x => String (x).lightGray, | |
dash: '-'.lightGray.dim | |
}) | |
/* ------------------------------------------------------------------------ */ | |
const printErr = err => { | |
if (('actual' in err) && ('expected' in err)) { | |
log.bright.red.error ('[AssertionError] ' + err.message, '\n') | |
log.red.error.indent (1) ('actual: ', err.actual, '\n') | |
log.green.error.indent (1) ('expected:', err.expected, '\n') | |
log.bright.red.error.indent (1) (new StackTracey (err).pretty) | |
} else { | |
log.bright.red.error ('\n', err) | |
} | |
process.exit (1) | |
} | |
/* ------------------------------------------------------------------------ */ | |
const enableRateLimit = true | |
const exchanges = { | |
kraken: new ccxt.kraken ({ enableRateLimit }), | |
gdax: new ccxt.gdax ({ enableRateLimit }), | |
} | |
/* ------------------------------------------------------------------------ */ | |
const id = 'kraken' | |
const balances = {} | |
const renderDelay = 1000 | |
const cursorUp = '\u001b[1A' | |
const retryDelay = 5000 | |
const symbol = 'BTC/USD' | |
const timeframe = '1m' | |
let output = undefined | |
let lastPrice = undefined | |
let firstPrice = undefined | |
const exchange = new ccxt[id] ({ enableRateLimit }) | |
/* ------------------------------------------------------------------------ */ | |
// Create a screen object. | |
const screen = blessed.screen () | |
screen.title = [ 'CCXT', exchanges[id].name, symbol, timeframe ].join (' ') | |
var chart = blessed.box ({ | |
top: 'center', | |
left: 'center', | |
width: 150, | |
height: 34, | |
// content: 'Hello {bold}world{/bold}!', | |
// tags: true, | |
border: { | |
type: 'line' | |
}, | |
style: { | |
fg: 'white', | |
bg: 'black', | |
border: { | |
fg: '#224422' | |
}, | |
hover: { | |
bg: 'blue' | |
} | |
} | |
}) | |
screen.append (chart); | |
chart.setLine (1, 'Loading...'.dim) | |
// // If our box is clicked, change the content. | |
// box.on ('click', function (data) { | |
// box.setContent ('{center}Some different {red-fg}content{/red-fg}.{/center}') | |
// screen.render () | |
// }) | |
// // If box is focused, handle `enter`/`return` and give us some more content. | |
// box.key ('enter', function (ch, key) { | |
// box.setContent ('{right}Even different {black-fg}content{/black-fg}.{/right}\n') | |
// box.setLine (1, 'bar') | |
// box.insertLine (1, 'foo') | |
// screen.render () | |
// }) | |
// chart.key ('f1', function (ch, key) { | |
// chart.style.bg = 'blue' | |
// // screen.render () | |
// }) | |
// Quit on Escape, q, or Control-C. | |
screen.key (['escape', 'q', 'C-c'], function (ch, key) { | |
return process.exit (0) | |
}); | |
// Focus our element. | |
chart.focus () | |
/* ------------------------------------------------------------------------ */ | |
;(async () => { | |
/* ..................................................................... */ | |
const poll = new Promise (async (resolve, reject) => { | |
await exchange.loadMarkets () | |
if (!exchange.symbols.includes (symbol)) | |
throw new Error (id + ' does not support ' + symbol) | |
while (true) { | |
try { | |
const closingPrice = 4 // [ timestamp, open, high, low, close, volume ] | |
const ohlcv = await exchange.fetchOHLCV (symbol, timeframe) | |
firstPrice = ohlcv[0][closingPrice] | |
lastPrice = ohlcv[ohlcv.length - 1][closingPrice] | |
const series = ohlcv.slice (-120).map (x => x[closingPrice]) | |
output = asciichart.plot (series, { height: 30, padding: ' ' }).split ('\n') | |
// dirty highlight hack, as formatting is not supported by asciichart (for now) | |
output = output.map (line => { | |
const openSymbolIndex = line.indexOf ('┼') | |
const delimiterIndex = Math.max (openSymbolIndex, line.indexOf ('┤')) | |
const openSymbolFound = openSymbolIndex >= 0 | |
line = line.slice (0, -1) | |
const price = line.slice (0, 7) | |
let lastCharacter = line.slice (-1) | |
if (openSymbolFound) | |
line = firstPrice.toFixed (2).white + line.slice (7).white | |
else | |
line = (price + line.slice (7, 9)).green + line.slice (9) | |
let currentPriceLine = false | |
if (lastCharacter == '╰' || lastCharacter == '╭' || lastCharacter == '─') { | |
currentPriceLine = true | |
} | |
line = line + ((currentPriceLine) ? ('┼ '.white + lastPrice.toFixed (2).white) : ('├ '.green + price.green)) | |
return ' ' + line | |
// return openSymbolFound ? : line.slice | |
}) | |
} catch (e) { | |
// log.error (e) | |
await ccxt.sleep (retryDelay) | |
} | |
} | |
}) | |
//*/ | |
/* ..................................................................... */ | |
const render = new Promise (async (resolve, reject) => { | |
while (true) { | |
await ccxt.sleep (renderDelay) | |
// for (let exchange of exchanges) { | |
// } | |
chart.setLine (0, [ | |
'', | |
exchange.name.bright.cyan, | |
symbol.bright.cyan, | |
timeframe.cyan, | |
' '.repeat (79), | |
String (new Date ()).cyan, | |
].join (' ')) | |
if (!output) { | |
chart.setLine (1, ' Loading' + '.'.red.repeat (parseInt (Date.now () / renderDelay) % 10)) | |
// screen.debug (new Date (), output.length) | |
// screen.render () | |
// log (chart) | |
// process.exit () | |
} else { | |
output.forEach ((line, i) => { | |
chart.setLine (i + 1, line) | |
}) | |
} | |
screen.render () | |
} | |
}) | |
/* ..................................................................... */ | |
await Promise.all ([ poll, render ]) | |
}) () |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment