Advertisement
Guest User

Untitled

a guest
Dec 14th, 2017
111
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 22.61 KB | None | 0 0
  1. "use strict";
  2. exports.__esModule = true;
  3. var renderchart_1 = require("./renderchart");
  4. var request = require('request');
  5. var candlestick_1 = require("./candlestick");
  6. var telegram = require('telegram-bot-api');
  7. //const player = require('play-sound')();
  8. var fs = require('fs');
  9. var slack_1 = require("./slack");
  10. var masterCurrencies = ['USD', 'USDT', 'EUR', 'BTC', 'ETH']; //'XMR'
  11. var slackCurrencyGroup = { 'BTC': 'BTC', 'ETH': 'ETH', 'USD': 'Fiat', 'EUR': 'Fiat', 'USDT': 'Fiat' };
  12. var slackThreadCurrency = { 'BTC': 'BTC', 'ETH': 'ETH', 'USD': 'USD', 'EUR': 'EUR', 'USDT': 'USD' };
  13. var CurrencyPair = /** @class */ (function () {
  14. function CurrencyPair(primary_curr, base_curr, exchange) {
  15. this.primary_curr = primary_curr;
  16. this.base_curr = base_curr;
  17. this.exchange = exchange;
  18. // Array of minute candlesticks for last two hours
  19. this.candles_1m = new Array();
  20. // Array of hour candlesticks for last two months
  21. this.candles_1h = new Array();
  22. // Remember if last ticker triggered an alert with the same base time, prevent duplicate alerts
  23. this.lastDropState = false;
  24. this.lastLowerTime = 0;
  25. this.lastBaseCandle = null;
  26. // Time when a base was last found. Ignore newer bases for <ignorenewer> hours.
  27. this.lastBaseFoundTime = 0;
  28. // Holds master and secondary currencies: { curr1: master, curr2: secondary }
  29. this.masterCurrency = null;
  30. // Holds the name of the slack group
  31. this.slackGroup = null;
  32. // Slack thread id
  33. this.threadId = null;
  34. // Ignore this currency pair, use for delisted pairs etc.
  35. this.ignore = false;
  36. this.lastCurrentLow = 0;
  37. // Check if an least one of the currencies is a "master currency"
  38. this.masterCurrency = this.FindMasterCurrency(primary_curr, base_curr);
  39. // If the currency pair contains a master currency, find the correct slack group and read the settings
  40. if (this.masterCurrency) {
  41. this.slackGroup = ('scanner_alerts_' + slackCurrencyGroup[this.masterCurrency.curr1]).toLowerCase();
  42. this.ReadSettings();
  43. }
  44. }
  45. // Find out if the currency pair contains at least one master currency, and order the currencies so the strongest one comes first
  46. CurrencyPair.prototype.FindMasterCurrency = function (curr1, curr2) {
  47. var curr1index = masterCurrencies.indexOf(curr1);
  48. var curr2index = masterCurrencies.indexOf(curr2);
  49. if (curr1index < 0)
  50. curr1index = 999; // Not a master currency
  51. if (curr2index < 0)
  52. curr2index = 999; // Not a master currency
  53. if (curr1index == 999 && curr2index == 999) {
  54. console.log(curr1 + '/' + curr2 + ' No master currency');
  55. return null;
  56. }
  57. else if (curr1index < curr2index) {
  58. return { curr1: curr1, curr2: curr2 };
  59. }
  60. else {
  61. return { curr1: curr2, curr2: curr1 };
  62. }
  63. };
  64. // Read settings for this currency pair. The settings are the slack thread id and whether to ignore the currency pair (in case of delisting etc)
  65. CurrencyPair.prototype.ReadSettings = function () {
  66. var settingsFile = './data/' + slackThreadCurrency[this.masterCurrency.curr1] + '_' + this.masterCurrency.curr2 + '.settings.json';
  67. if (this.FileExists(settingsFile)) {
  68. var settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
  69. if (settings.threadId)
  70. this.threadId = settings.threadId;
  71. if (settings.ignore)
  72. this.ignore = settings.ignore;
  73. }
  74. };
  75. // Select if the currency pair should be ignored (never alert)
  76. CurrencyPair.prototype.SetIgnore = function (value) {
  77. this.ignore = value;
  78. this.WriteSettings();
  79. if (value)
  80. console.log('Ignoring ' + this.exchange.exch_code + ' ' + this.base_curr + ' ' + this.primary_curr);
  81. else
  82. console.log('No longer ignoring ' + this.exchange.exch_code + ' ' + this.base_curr + ' ' + this.primary_curr);
  83. };
  84. // Write settings to file
  85. CurrencyPair.prototype.WriteSettings = function () {
  86. var settingsFile = './data/' + slackThreadCurrency[this.masterCurrency.curr1] + '_' + this.masterCurrency.curr2 + '.settings.json';
  87. var settings = { threadId: this.threadId, ignore: this.ignore };
  88. fs.writeFileSync(settingsFile, JSON.stringify(settings), 'utf8');
  89. };
  90. // Check if a file exists
  91. CurrencyPair.prototype.FileExists = function (filePath) {
  92. try {
  93. fs.statSync(filePath);
  94. }
  95. catch (err) {
  96. if (err.code == 'ENOENT')
  97. return false;
  98. }
  99. return true;
  100. };
  101. // Get history from Coinigy
  102. CurrencyPair.prototype.GetHistory = function (callback) {
  103. var _this = this;
  104. var historyfile = './data/' + this.exchange.exch_code + '_' + this.base_curr + '_' + this.primary_curr + '.json';
  105. // Calculate current unix timestamp
  106. var unixtime = Math.round(+new Date() / 1000);
  107. // Unix timestamp one hour ago. Will get minute candles for last hour.
  108. var fromtime_1m = unixtime - 3600;
  109. // Unix timestamp 60 days ago. Will get hour candlesticks for last two months.
  110. var fromtime_1h = unixtime - 3600 * CurrencyPair.max1hcandles;
  111. // Url for minute candles for desired time range
  112. var url1m = "https://www.coinigy.com/getjson/chart_feed/" + this.exchange.exch_code + "/" + this.primary_curr + "/" + this.base_curr + "/1/" + fromtime_1m + "/" + unixtime;
  113. // Get minute candles from coinigy
  114. request(url1m, function (err, res, body) {
  115. if (!err && res.statusCode == 200) {
  116. JSON.parse(body).forEach(function (x) {
  117. var c = new candlestick_1.CandleStick();
  118. c.start_time = x[0];
  119. c.open = x[1];
  120. c.high = x[2];
  121. c.low = x[3];
  122. c.close = x[4];
  123. _this.candles_1m.push(c);
  124. });
  125. // Read history from JSON file, if exists
  126. if (_this.FileExists(historyfile)) {
  127. var history_1 = JSON.parse(fs.readFileSync(historyfile, 'utf8')).filter(function (x) { return x.start_time > fromtime_1h; });
  128. history_1.forEach(function (x) {
  129. var c = new candlestick_1.CandleStick();
  130. c.start_time = x.start_time;
  131. c.close = x.close;
  132. c.high = x.high;
  133. c.low = x.low;
  134. c.open = x.open;
  135. _this.candles_1h.push(c);
  136. });
  137. if (_this.candles_1h.length > 0)
  138. _this.candles_1h.shift();
  139. if (_this.candles_1h.length > 0)
  140. fromtime_1h = _this.candles_1h[0].start_time + 3600;
  141. }
  142. // Url for 1 hour candles
  143. var url1h = "https://www.coinigy.com/getjson/chart_feed/" + _this.exchange.exch_code + "/" + _this.primary_curr + "/" + _this.base_curr + "/60/" + fromtime_1h + "/" + unixtime;
  144. // Get (newer) hour candles from coinigy
  145. request(url1h, function (err, res, body) {
  146. if (!err && res.statusCode == 200) {
  147. var newCandles_1 = new Array();
  148. JSON.parse(body).forEach(function (x) {
  149. var c = new candlestick_1.CandleStick();
  150. c.start_time = x[0];
  151. c.open = x[1];
  152. c.high = x[2];
  153. c.low = x[3];
  154. c.close = x[4];
  155. newCandles_1.push(c);
  156. });
  157. _this.candles_1h = newCandles_1.concat(_this.candles_1h);
  158. // Write history to JSON file
  159. fs.writeFileSync(historyfile, JSON.stringify(_this.candles_1h), 'utf8');
  160. // Finished getting history for this coin
  161. callback();
  162. }
  163. else {
  164. // Error getting 1 hour candles
  165. console.log(err);
  166. }
  167. });
  168. }
  169. else {
  170. // Error getting 1 minute candles
  171. console.log(err);
  172. }
  173. });
  174. };
  175. // Get Coinigy url for this currency pair
  176. CurrencyPair.prototype.GetCoinigyUrl = function () {
  177. return "https://www.coinigy.com/main/markets/" + this.exchange.exch_code + "/" + this.primary_curr + "/" + this.base_curr;
  178. };
  179. // Find the first base, don't look for bases for the last <ignoreHours> hours
  180. CurrencyPair.prototype.FindBase = function (value, ignoreHours) {
  181. // First candle found with low value lower than <maxbottomdistance> above <value>
  182. var firstCandle = null;
  183. // Lowest candle body found
  184. var lowestCandle = null;
  185. // Current candle number, 0 is newest
  186. var i = 0;
  187. // Discovery state. 0: Looking for candle with low value < maxbottomdistance. 1: Looking for bottom of base
  188. var state = 0;
  189. // Number of consecutive candles with body low > lowest candle body low
  190. var skippedCandles = 0;
  191. // Iterate through the hourly candles back in time
  192. for (var _i = 0, _a = this.candles_1h; _i < _a.length; _i++) {
  193. var x = _a[_i];
  194. // For the first candles, only check if they are higher than the current price, not if they are bases. If a candle is lower that the current price, stop looking.
  195. if (i < ignoreHours) {
  196. if (value > x.low) {
  197. lowestCandle = x;
  198. break;
  199. }
  200. } // State 0 - look for a candle that is < maxbottomdistance above current price.
  201. else if (state == 0 && x.bodylow() <= (value * (1 + CurrencyPair.maxbottomdistance / 100)) && i >= ignoreHours) {
  202. firstCandle = x;
  203. lowestCandle = firstCandle;
  204. state = 1;
  205. }
  206. else if (state == 1) {
  207. if (x.bodylow() <= firstCandle.bodylow()) {
  208. if (x.bodylow() < lowestCandle.bodylow()) {
  209. lowestCandle = x;
  210. }
  211. skippedCandles = 0;
  212. }
  213. else {
  214. skippedCandles++;
  215. if (skippedCandles > CurrencyPair.maxskipcandles) {
  216. break;
  217. }
  218. }
  219. }
  220. i++;
  221. }
  222. return lowestCandle;
  223. };
  224. // Find the lowest price in a given period, looks a little buggy
  225. CurrencyPair.prototype.GetLow = function (start_time, end_time) {
  226. var low = -1;
  227. var last_candle_start = Math.floor(end_time / 3600) * 3600;
  228. this.candles_1h.filter(function (x) { return x.start_time >= start_time && x.start_time < last_candle_start; }).forEach(function (y) {
  229. if (low > 0 || y.low < low)
  230. low = y.low;
  231. });
  232. var lastCandle = this.GetCandleForTime(last_candle_start, end_time); // Calculate the last 1h candle from 1m candles. Only applicable if end_time is within the last hour.
  233. if (lastCandle && (lastCandle.low < low || low < 0))
  234. low = lastCandle.low;
  235. return low;
  236. };
  237. // Calculate a candle between to timestamps from 1m candles
  238. CurrencyPair.prototype.GetCandleForTime = function (start_time, end_time) {
  239. if (this.candles_1m.length == 0)
  240. return null;
  241. var cs1h = new candlestick_1.CandleStick();
  242. cs1h.close = this.candles_1m[0].close;
  243. cs1h.high = this.candles_1m[0].high;
  244. cs1h.low = this.candles_1m[0].low;
  245. cs1h.open = this.candles_1m[0].open;
  246. cs1h.start_time = start_time;
  247. var c_1m = this.candles_1m.filter(function (x) { return x.start_time >= cs1h.start_time && x.start_time < end_time; });
  248. c_1m.forEach(function (y) {
  249. cs1h.open = y.open;
  250. cs1h.high = Math.max(cs1h.high, y.high);
  251. cs1h.low = Math.min(cs1h.low, y.low);
  252. });
  253. return cs1h;
  254. };
  255. // New tick received from exchange, perform calculations
  256. CurrencyPair.prototype.NewTick = function (ticker) {
  257. // Cancel if currency pair is set to be ignored.
  258. if (this.ignore)
  259. return;
  260. var unix = ticker.tick_time;
  261. this.base_volume = ticker.base_volume_24;
  262. this.primary_volume = ticker.primary_volume_24;
  263. // Add a new candle to the 1h array if a new hour has started or this is the first tick received
  264. if (!this.last_tick_time || Math.floor(unix / 3600) != Math.floor(this.last_tick_time / 3600)) {
  265. var cs = this.GetCandleForTime(Math.floor(unix / 3600) * 3600, unix);
  266. if (!cs)
  267. cs = new candlestick_1.CandleStick();
  268. cs.open = ticker.last_price;
  269. cs.high = ticker.last_price;
  270. cs.low = ticker.last_price;
  271. cs.start_time = Math.floor(unix / 3600) * 3600; // Calculate exact start time of this hour
  272. this.candles_1h.unshift(cs);
  273. this.candles_1h = this.candles_1h.filter(function (x) { return x.start_time > unix - 3600 * 24 * 60; }); // Remove candles more than 60 days old
  274. }
  275. // Add a new candle to the 1m array if a new minute has started or this is the first tick received
  276. if (!this.last_tick_time || Math.floor(unix / 60) != Math.floor(this.last_tick_time / 60)) {
  277. var cs = new candlestick_1.CandleStick();
  278. cs.open = ticker.last_price;
  279. cs.high = ticker.last_price;
  280. cs.low = ticker.last_price;
  281. cs.start_time = Math.floor(unix / 60) * 60; // Calculate exact start time of this minute
  282. this.candles_1m.unshift(cs);
  283. this.candles_1m = this.candles_1m.filter(function (x) { return x.start_time > unix - 3600; }); // Remove candles more than 1h old
  284. this.lastCurrentLow = 0;
  285. }
  286. // If the 1h array contains at least one candle, update high, low and close-prices
  287. if (this.candles_1h.length > 0 && this.candles_1h[0]) {
  288. this.candles_1h[0].high = Math.max(this.candles_1h[0].high, ticker.last_price);
  289. this.candles_1h[0].low = Math.min(this.candles_1h[0].low, ticker.last_price);
  290. this.candles_1h[0].close = ticker.last_price;
  291. }
  292. else {
  293. console.log("1h candle missing in " + this.exchange + ": " + this.base_curr + "-" + this.primary_curr);
  294. }
  295. // If the 1m array contains at least one candle, update high, low and close-prices
  296. if (this.candles_1m.length > 0 && this.candles_1m[0]) {
  297. this.candles_1m[0].high = Math.max(this.candles_1m[0].high, ticker.last_price);
  298. this.candles_1m[0].low = Math.min(this.candles_1m[0].low, ticker.last_price);
  299. this.candles_1m[0].close = ticker.last_price;
  300. }
  301. else {
  302. console.log("1m candle missing in " + this.exchange + ": " + this.base_curr + "-" + this.primary_curr);
  303. }
  304. this.last_tick_time = unix;
  305. // Check if price has dropped below a base
  306. this.CheckDrop();
  307. };
  308. ;
  309. // Used when running the scanner locally, to give a sound when an alert is issued
  310. CurrencyPair.prototype.PlaySound = function () {
  311. /* player.play('./ting.mp3', (err) => {
  312. if (err) console.log(`Could not play sound: ${err}`);
  313. });*/
  314. };
  315. // Create a graph using techan charts
  316. CurrencyPair.prototype.CreateGraphic = function (fromCandle, baseCandle, base, title, message, value) {
  317. var _this = this;
  318. var r = new renderchart_1.RenderChart();
  319. var basestart = Math.min(this.candles_1h.length - 1, baseCandle);
  320. var baseend = 0;
  321. // Create a unique filename for the chart
  322. var filename = this.exchange.exch_code + " " + this.base_curr + "_" + this.primary_curr + " " + +new Date() + ".png";
  323. console.log('Rendering...');
  324. r.render(this.candles_1h.slice(0, fromCandle), value, { FromTime: new Date(this.candles_1h[baseend].start_time * 1000), ToTime: new Date(this.candles_1h[basestart].start_time * 1000), Level: base }, filename, this.exchange.exch_code + " " + this.base_curr + "_" + this.primary_curr, function (file) {
  325. console.log('Render complete');
  326. var url = 'http://quickfingers.trade/alerts/' + file;
  327. console.log(message);
  328. });
  329. // If slack is enabled and there is a group for this pair, send an alert
  330. /*if (CurrencyPair.useSlack && this.slackGroup) {
  331. console.log('Rendering...');
  332. r.render(this.candles_1h.slice(0, fromCandle), value, { FromTime: new Date(this.candles_1h[baseend].start_time * 1000), ToTime: new Date(this.candles_1h[basestart].start_time * 1000), Level: base }, filename, this.exchange.exch_code + " " + this.base_curr + "_" + this.primary_curr, function (file) {
  333. console.log('Render complete, sending slack message');
  334. var url = 'http://quickfingers.trade/alerts/' + file;
  335. CurrencyPair.slack.send(_this.slackGroup, url, title, message, _this.threadId, function (ts) {
  336. // If a new thread was created, save the thread id
  337. if (_this.threadId != ts) {
  338. _this.threadId = ts;
  339. _this.WriteSettings();
  340. }
  341. });
  342. });
  343. }*/
  344. };
  345. // Check if the price has dropped below the base
  346. CurrencyPair.prototype.CheckDrop = function () {
  347. // Only check if there is at least one 1m candle, should always be true
  348. if (this.candles_1m.length > 0) {
  349. // Find the low value of the current candle, only check if the price is different from last time
  350. var currentLow = this.candles_1m[0].low;
  351. if (currentLow != this.lastCurrentLow) {
  352. this.lastCurrentLow = currentLow;
  353. var unix = Math.round(+new Date() / 1000);
  354. // Find the closest base
  355. var baseCandle = this.FindBase(currentLow, CurrencyPair.ignorehours);
  356. if (baseCandle) {
  357. // Continue if there wasn't found a base candle last time, if this base is older than the last one found or if it's more than <ignoreNewer> hours since the last base was found
  358. if (!this.lastBaseCandle || baseCandle.start_time < this.lastBaseCandle.start_time || (unix - this.lastBaseFoundTime) > CurrencyPair.ignoreNewer * 3600) {
  359. var timeSinceLastLow = Math.floor((unix - baseCandle.start_time) / 3600);
  360. // Alert if the base is at least <minddrop> % above current price, at least <minhours> hours old and isn't the same base that was alerted last time
  361. if ((baseCandle.bodylow() > currentLow * (1 + CurrencyPair.mindrop / 100)) && timeSinceLastLow > CurrencyPair.minhours && baseCandle.start_time != this.lastLowerTime) {
  362. var title = "(" + this.exchange.exch_code + ") " + this.primary_curr + "/" + this.base_curr;
  363. var message = "Last base: " + timeSinceLastLow + "h ago\nVolume: " + Math.round(this.base_volume * 100) / 100 + " " + this.base_curr + " / " + Math.round(this.primary_volume * 100) / 100 + " " + this.primary_curr + "\nPrice: " + this.candles_1m[0].close + "\n" + this.GetCoinigyUrl() + "\n" + this.exchange.GetUrl(this.primary_curr, this.base_curr);
  364. this.lastLowerTime = baseCandle.start_time;
  365. this.lastBaseCandle = baseCandle;
  366. this.lastBaseFoundTime = unix;
  367. this.PlaySound();
  368. // Create the graph and send to slack
  369. this.CreateGraphic(this.candles_1h.length - 24, this.candles_1h.indexOf(baseCandle), baseCandle.bodylow(), title, message, currentLow);
  370. }
  371. }
  372. }
  373. else if (this.candles_1h.length > CurrencyPair.minhours) {
  374. if (this.lastLowerTime != -999) {
  375. var title = "(" + this.exchange.exch_code + ") " + this.primary_curr + "/" + this.base_curr;
  376. var message = "Lowest value in > " + this.candles_1h.length + "h\n" +
  377. "Volume: " + Math.round(this.base_volume * 100) / 100 + " " + this.base_curr + " / " + Math.round(this.primary_volume * 100) / 100 + " " + this.primary_curr + "\nPrice: " + this.candles_1m[0].close +
  378. '\n' + this.GetCoinigyUrl() + '\n' + this.exchange.GetUrl(this.primary_curr, this.base_curr);
  379. this.lastLowerTime = -999;
  380. this.PlaySound();
  381. // Create the graph and send to slack
  382. this.CreateGraphic(this.candles_1h.length - 24, 0, -1, title, message, currentLow);
  383. }
  384. } // Alert once if there are no 1h candles
  385. else if (this.candles_1h.length == 0) {
  386. if (this.lastLowerTime != -998)
  387. console.log("No 1h candles for: (" + this.exchange.exch_code + ") " + this.primary_curr + "/" + this.base_curr);
  388. this.lastLowerTime = -998;
  389. }
  390. }
  391. }
  392. };
  393. CurrencyPair.slack = new slack_1.Slack();
  394. // Maximum number of 1h candles to keep
  395. CurrencyPair.max1hcandles = 24 * 60;
  396. // Settings
  397. // Minimum drop from base before alerting
  398. CurrencyPair.mindrop = 5;
  399. // Maximum distance to candle when searching for bases
  400. CurrencyPair.maxbottomdistance = 8;
  401. // Minimum time since last base
  402. CurrencyPair.minhours = 72;
  403. // Ignore last x hours when searching for a base
  404. CurrencyPair.ignorehours = 12;
  405. // Maximum candles with higher body than the first candle found on the current base. Used when searching for bottom of base
  406. CurrencyPair.maxskipcandles = 8;
  407. // Send to telegram channel
  408. CurrencyPair.useSlack = false;
  409. // Number of hours to ignore newer bases than the last one that was found
  410. CurrencyPair.ignoreNewer = 2;
  411. return CurrencyPair;
  412. }());
  413. exports.CurrencyPair = CurrencyPair;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement