kraken-grid 1.0.3 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +5 -5
  2. package/index.js +132 -118
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -49,14 +49,14 @@ This simply prints out a list of all the open orders the code last retrieved (it
49
49
  * `Pair` is symbol (see `buy`) with the 'USD' suffix.
50
50
  * `Price` is the price for this trade.
51
51
  * The corresponding bracketed items will be missing for an order with no leverage or without a conditional close.
52
- * `userref` is a user-reference number derived from the UNIX TimeStamp when the order was placed. Extending the grid to higher sells uses a userref 10,000,000 less than the current highest sell's userref, and extending it to lower-priced buys uses a userref 1,000,000 less than the current lowest buy's userref. The last six digits of all userrefs are assumed to be different for every combination of price and symbol.
52
+ * `userref` is a user-reference number created when you use the `buy` or `sell` command. It starts with 1 for buys and 2 for sells, followed by two digits that identify the cryptocurrency pair, and then the price without the decimal point and with leading zeroes. Note that this causes collisions in very rare cases like a price of $35.01 and another price for the same crypto of $350.10. I expect this to be too rare to fix at this time.
53
53
 
54
- If you enter anything for [SEARCH], the list will only display lines that contain what you entered, except in one case, `C`. If it's just the `C`, it will retrieve the last 50 orders that are no longer open (Filled, Cancelled, or Expired), but only list those that actually executed (Filled). If you add the userref after `C`, then it will fetch only orders with that userref, which means the buys and sells at one grid point. See `set` for a list of them userrefs for the grid points. Such orders also include the time at which the order filled completely.
54
+ If you enter anything for [SEARCH], the list will only display lines that contain what you entered, except in one case, `C`. If it's just the `C`, it will retrieve the last 50 orders that are no longer open (Filled, Cancelled, or Expired), but only list those that actually executed (Filled). If you add the userref after `C`, then it will fetch only orders with that userref, which means the buys and sells between one set of prices. Use `set` for a list of the userrefs for the grid points. Such orders also include the time at which the order filled completely.
55
55
 
56
56
  ### set
57
57
  This lists the `userref`s and prices at which buys and sells have been (and will be) placed.
58
58
  `set R S P`
59
- R _must be_ a userref, S _must be_ either `buy` or `sell`, and P is the price you want to add for that grid point. If the bot fails to determine either the buy price or the sell price, it displays a ?, and this will prevent the creation of a new order as described under `refnum`. This command allows you to fix that so that Step 2.1 under `report` will work properly as described under `refnum`.
59
+ R _must be_ a userref, S _must be_ either `buy` or `sell`, and P is the price you want to add or replace) for that grid point. If the bot fails to determine either the buy price or the sell price, it displays a ?, and this will prevent the creation of a new order as described under `refnum`. This command allows you to fix that so that Step 2.1 under `report` will work properly as described under `refnum`.
60
60
 
61
61
  ### reset
62
62
  This erases the list of `userref`s and prices at which buys and sells will be placed, but that list gets immediately rebuilt because it performs the second step in `report`.
@@ -69,7 +69,7 @@ This automatically and repeatedly executes the second step of `report` and then
69
69
  This stops the automatic calling of `report`. The bot will do nothing until you give it a new command.
70
70
 
71
71
  ### margin
72
- The bot will try to use leverage when there isn't enough USD or crypto. Whether or not it succeeds, it will still be able to report how much you are long or short for each supported crypto. Reporting that is all this command does.
72
+ Whether or not you use this command, the bot will try to use leverage when there isn't enough USD or crypto. Whether or not it succeeds, it will still be able to report how much you are long or short for each supported crypto. Reporting that is all this command does.
73
73
 
74
74
  ### kill
75
75
  `kill X`
@@ -91,7 +91,7 @@ C _must be_ a `Counter` as shown by executing `list`, and it must be an order th
91
91
  There is a little bit of logic in the code to spit out a lot more information when verbose is on. It's off by default and this command just toggles it.
92
92
 
93
93
  ### safe
94
- When the bot starts, it is in "safe" mode, which means that it will not __actually__ add or cancel any orders. The idea is that it won't do anything, but instead just show you what it would do if __safe__ were off. Your have to enter `safe` to turn this off so that the bot will actually do things. It allows for startup with a lot less rish with a possible buggy bot.
94
+ When the bot starts, it is in "safe" mode, which means that it will not __actually__ add or cancel any orders. The idea is that it won't do anything, but instead just show you what it would do if __safe__ were off. Your have to enter `safe` to turn this off so that the bot will actually do things. It allows for startup with a lot less risk with a possibly buggy bot.
95
95
 
96
96
  ### ws - EXPERIMENTAL
97
97
  This connects to Kraken's WebSockets, which, I have to warn you, send you something about every second, and sometimes silently disconnects.
package/index.js CHANGED
@@ -3,8 +3,8 @@ const fs = require('fs');
3
3
  const prompt = require('prompt-sync')({sigint: true});
4
4
 
5
5
  let homeDir = process.env.APPDATA
6
- || (process.platform == 'darwin'
7
- ? process.env.HOME + '/Library/Preferences'
6
+ || (process.platform == 'darwin'
7
+ ? process.env.HOME + '/Library/Preferences'
8
8
  : process.env.HOME + "/.local/share"),
9
9
  keyFile = homeDir+'/keys.txt';
10
10
 
@@ -16,7 +16,7 @@ if(!fs.existsSync(keyFile)) {
16
16
 
17
17
  const myKeys = fs.readFileSync(keyFile,{encoding:'utf8', flag:'r'});
18
18
  const [key,secret] = myKeys.split(' ');
19
- const KrakenClient = require('kraken-api');
19
+ const KrakenClient = require('kraka-djs');
20
20
  const kraken = new KrakenClient(key, secret);
21
21
  function sleep(ms) {
22
22
  return new Promise(resolve => setTimeout(resolve, ms));
@@ -33,10 +33,12 @@ async function kapi(arg,sd=5)
33
33
  ret = await kraken.api(arg);
34
34
  }
35
35
  } catch(err) {
36
- if((!/AddOrder/.test(arg[0])&&/ETIMEDOUT|EAI_AGAIN/.test(err.code))
36
+ if((!/AddOrder/.test(arg[0])&&/ETIMEDOUT|EAI_AGAIN/.test(err.code))
37
37
  || /nonce/.test(err.message)
38
+ || /Response code 50/.test(err.message)
38
39
  || (risky && /Internal error/.test(err.message))
39
- || /Response code 50/.test(err.message)) {
40
+ || /Unavailable/.test(err.message)
41
+ || /Rate limit|Throttled/.test(err.message)) {
40
42
  console.log(22,err.message+", so trying again in "+sd+"s...("+(new Date)+"):");
41
43
  if(Array.isArray(arg)) {
42
44
  delete arg[1].nonce;
@@ -52,25 +54,28 @@ async function kapi(arg,sd=5)
52
54
  } else {
53
55
  catcher(26,err);
54
56
  ret = { result: { descr: err }};
55
- }
57
+ }
56
58
  }
57
59
  if(verbose) console.log(ret);
58
60
  return ret;
59
61
  }
60
62
 
61
- async function order(buysell, xmrbtc, price, amt, lev='none', uref=0, closeO=0) {
63
+ async function order(buysell, xmrbtc, price, amt, lev='none', uref=0, closeO=null) {
62
64
  let cO = Number(closeO),
63
65
  p = Number(price),
64
66
  a = Number(amt),
65
67
  ret = '';
68
+
66
69
  if( cO == price ) cO = 0;
70
+ if(uref==0) uref = makeUserRef(buysell, xmrbtc, price);
71
+
67
72
  console.log(27,(safeMode ? '(Safe mode, so NOT) ' : '')
68
73
  +buysell+"ing "+a+" "+xmrbtc+" at "+p+" with leverage "+lev
69
- +(cO>0 ? "" : " to close at "+cO));
70
- // let ordered;
71
- if(!safeMode) {
72
- let response = await kapi(['AddOrder',
73
- { pair: xmrbtc+'USD', // Just call it 'pair'! #USD Refactor
74
+ +(cO==0 ? "" : " to close at "+(isNaN(cO)?closeO+' is NaN!':cO)) +" as "+uref);
75
+ if( cO>0 && (buysell == 'buy' ? cO <= price : cO >= price) )
76
+ throw 'Close price, '+cO+' is on the wrong side of '+buysell+' at '+price+'!';
77
+ ret = ['AddOrder',
78
+ { pair: xmrbtc+'USD', // Just call it 'pair' so it already has the USD at the end! #USD Refactor
74
79
  userref: uref,
75
80
  type: buysell,
76
81
  ordertype: 'limit',
@@ -78,8 +83,10 @@ async function order(buysell, xmrbtc, price, amt, lev='none', uref=0, closeO=0)
78
83
  volume: a,
79
84
  leverage: lev,
80
85
  close: (cO>0 ? {ordertype:'limit',price:cO} : null)
81
- }]);
82
- console.log(40,response ? ((d = response.result)
86
+ }];
87
+ if(!safeMode) {
88
+ let response = await kapi(ret);
89
+ console.log(40,response ? ((d = response.result)
83
90
  ? (ret = d.txid,d.descr) : 'No result.descr from kapi') : "No kapi response.");
84
91
  console.log(42,"Cooling it for a second...");
85
92
  await sleep(1000);
@@ -89,6 +96,14 @@ async function order(buysell, xmrbtc, price, amt, lev='none', uref=0, closeO=0)
89
96
 
90
97
  function gpToStr(gp) { return gp.userref+':'+gp.buy+'-'+gp.sell+' '+gp.bought+'/'+gp.sold; }
91
98
 
99
+ function makeUserRef(buysell, xmrbtc, price) {
100
+ let ret = Number((buysell=='buy'?'1':'2')
101
+ + ('00'+USDPairs.indexOf(xmrbtc+'USD')).slice(-2)
102
+ + String('000000'+price).replace('.','').slice(-7));
103
+ if(verbose) console.log("Created userref ",ret);
104
+ return ret;
105
+ }
106
+
92
107
  async function listOpens(portfolio = null, isFresh=false) {
93
108
  let response = await kapi('OpenOrders'),
94
109
  opens = response.result.open;
@@ -128,7 +143,7 @@ async function listOpens(portfolio = null, isFresh=false) {
128
143
  }
129
144
  }
130
145
  }
131
-
146
+
132
147
  // Save the old order array so we can see the diff
133
148
  // -----------------------------------------------
134
149
  let oldRefs = [];
@@ -141,7 +156,7 @@ async function listOpens(portfolio = null, isFresh=false) {
141
156
  op = od.price;
142
157
  rv = oo.vol-oo.vol_exec;
143
158
  ur = oo.userref;
144
-
159
+
145
160
  if(ur > 0) {
146
161
  // BothSides record for userref
147
162
  // ----------------------------
@@ -185,7 +200,7 @@ async function listOpens(portfolio = null, isFresh=false) {
185
200
  // order of the closing order into which we combine.
186
201
  // -------------------------------------------------
187
202
  if(od.close && ur>0) { // Externally added orders have userref=0
188
- cp = /[0-9.]+$/.exec(od.close)[0];
203
+ cp = /[0-9.]+$/.exec(od.close)[0];
189
204
  gp = gPrices.find(gprice => gprice.userref==ur);
190
205
  if(!gp) {
191
206
  gp = {userref:ur,buy:'?',sell:'?', bought: 0, sold: 0};
@@ -209,7 +224,7 @@ async function listOpens(portfolio = null, isFresh=false) {
209
224
  volume: Number(oo.vol),
210
225
  type: od.type,
211
226
  sym: /USD/.test(od.pair) ? /^([A-Z]+)USD/.exec(od.pair)[1] : od.pair,
212
- // Just call it 'pair'! #USD Refactor
227
+ // Just call it 'pair' instad of sym and use od.pair! #USD Refactor
213
228
  ctype: ct,
214
229
  lev: od.leverage,
215
230
  ids: [o],
@@ -218,7 +233,7 @@ async function listOpens(portfolio = null, isFresh=false) {
218
233
  price: od.price,
219
234
  hasClose: Boolean(od.close)
220
235
  };
221
- } else {
236
+ } else {
222
237
  comps[ci].total+=rv; // Volume for combined order.
223
238
  comps[ci].ids.push(o);
224
239
  comps[ci].volume += Number(oo.vol); // Volume for extended order.
@@ -240,11 +255,11 @@ async function listOpens(portfolio = null, isFresh=false) {
240
255
  console.log(159, "New: ",o,opensA.length, od.order, oo.userref, cp);
241
256
  if(verbose) console.log(160,oo);
242
257
  }
243
-
258
+
244
259
  if(portfolio && isFresh && od.leverage == "none") {
245
260
  if(od.type == "buy") {
246
261
  if(/USD$/.test(od.pair)) { // Deplete our cash
247
- portfolio['ZUSD'][2] -= od.price*opens[o].vol;
262
+ portfolio['ZUSD'][2] -= od.price*opens[o].vol; // #USD Refactor and basePair()
248
263
  } else if(/XBT$/.test(od.pair)) { // Deplete our BTC
249
264
  portfolio['XBT'][0] -= od.price*opens[o].vol;
250
265
  }
@@ -261,7 +276,7 @@ async function listOpens(portfolio = null, isFresh=false) {
261
276
 
262
277
  let nexes = 0, // Orders not requiring extension
263
278
  dontask = false;
264
- for( comp in comps ) if(/USD/.test(comp)) { // non-USD pairs break regex below... #USD Refactor
279
+ for( comp in comps ) if(/USD/.test(comp)) { // non-USD pairs break regex below... #USD Refactor
265
280
  let c = comps[comp],
266
281
  gp = gPrices.find(gprice => gprice.userref==c.userref);
267
282
  bs = bSides.find(b => b.userref==c.userref);
@@ -272,8 +287,8 @@ async function listOpens(portfolio = null, isFresh=false) {
272
287
  }
273
288
  gp[c.ctype] = c.open;
274
289
  gp[c.type] = c.price;
275
- [,sym,price] = /([A-Z]+)USD([0-9.]+)/.exec(comp); //#USD Refactor
276
- if(verbose) console.log("Checking: " + c.type + ' '
290
+ [,sym,price] = /([A-Z]+)USD([0-9.]+)/.exec(comp); //remove USD #USD Refactor
291
+ if(verbose) console.log("Checking: " + c.type + ' '
277
292
  + sym + ' ' + price + ' ' + Math.round(c.total*10000)/10000
278
293
  + (c.open ? ' to '+c.ctype+'-close @'+c.open : '') +' (' + c.userref + "):");
279
294
  if(!isNaN(c.open)) {
@@ -287,7 +302,7 @@ async function listOpens(portfolio = null, isFresh=false) {
287
302
  // ----------------------
288
303
  let traded = c.type=='buy' ? 'sold' : 'bought';
289
304
  gp[traded]+=c.total;
290
- }
305
+ }
291
306
  // Do we need to extend the grid?
292
307
  // If we don't have a buy and a sell, then yes.
293
308
  // --------------------------------------------
@@ -306,8 +321,8 @@ async function listOpens(portfolio = null, isFresh=false) {
306
321
  if(bs.buy) { // Missing the sell
307
322
  do {
308
323
  sp = Math.round(decimals*gp.sell*gp.sell/gp.buy)/decimals;
309
- c.userref -= 10000000;
310
- // We may already have this grid price but the order
324
+ c.userref = makeUserRef('sell', c.sym, sp);
325
+ // We may already have this grid price but the order
311
326
  // was deleted, so search for it first.
312
327
  ngp = gPrices.find(n => n.userref==c.userref);
313
328
  if(!ngp) {
@@ -325,12 +340,12 @@ async function listOpens(portfolio = null, isFresh=false) {
325
340
  getLev(portfolio,'sell',sp,c.volume,c.sym,false),c.userref,
326
341
  gp.sell);
327
342
  gp = ngp;
328
- } while(sp <= 1*portfolio[c.sym][1]);
343
+ } while(sp <= 1*portfolio[c.sym][1]);
329
344
  } else {
330
345
  do {
331
346
  bp = Math.round(decimals*gp.buy*gp.buy/gp.sell)/decimals;
332
- c.userref -= 1000000;
333
- // We may already have this grid price but the order
347
+ c.userref = makeUserRef('buy', c.sym, bp);
348
+ // We may already have this grid price but the order
334
349
  // was deleted, so search for it first.
335
350
  ngp = gPrices.find(n => n.userref==c.userref);
336
351
  if(!ngp) {
@@ -354,7 +369,7 @@ async function listOpens(portfolio = null, isFresh=false) {
354
369
  }
355
370
  }
356
371
  // console.log(gPrices);
357
- console.log(nexes,"orders didn't require extension.");
372
+ console.log(nexes,"orders did NOT require extension.");
358
373
  // console.log(comps);
359
374
  if(portfolio){
360
375
  portfolio['O'] = opensA;
@@ -371,10 +386,10 @@ function getLev(portfolio,buysell,price,amt,xmrbtc,posP) {
371
386
  if(1*price > 1*portfolio[xmrbtc][1] && posP)
372
387
  return "Buying "+xmrbtc+" @ "+price+" isn't a limit order.";
373
388
  if(price*amt > 1*portfolio['ZUSD'][2]) { // #USD Refactor - This doesn't support leverage
374
- lev = '2'; // on non-USD pairs.
389
+ lev = '2'; // on non-USD pairs. Hunt ZUSD and add basePair(pair) to get base.
375
390
  } else {
376
- portfolio['ZUSD'][2] -= price*amt;
377
- }
391
+ portfolio['ZUSD'][2] -= price*amt; // #USD Refactor and basePair()
392
+ }
378
393
  } else {
379
394
  if(price*1 < 1*portfolio[xmrbtc][1] && posP) return "Selling "+xmrbtc+" @ "+price+" isn't a limit order.";
380
395
  //console.log("We have "+portfolio[xmrbtc][2]+" "+xmrbtc);
@@ -395,7 +410,7 @@ async function kill(o,oa) {
395
410
  if(/^y/i.test(killAll)) {
396
411
  let killed = await kapi('CancelAll');
397
412
  console.log(314,killed);
398
- } else { console.log("Maybe be more careful."); }
413
+ } else { console.log("Maybe be more careful."); }
399
414
  return;
400
415
  } else if(safeMode) {
401
416
  console.log("In Safemode, so NOT killing "+o);
@@ -432,16 +447,16 @@ async function handleArgs(portfolio, args, uref = 0) {
432
447
  // Do we need leverage?
433
448
  // --------------------
434
449
  let lev = getLev(portfolio,buysell,price,amt,xmrbtc,posP);
435
- let cPrice = portfolio['G'][uref] ? portfolio['G'][uref][buysell=='buy'?'sell':'buy'] : 0;
450
+ let cPrice = !isNaN(portfolio['G'][uref]) ? portfolio['G'][uref][buysell=='buy'?'sell':'buy'] : 0;
436
451
  // Without a record of a closing price, use the last one we found.
437
452
  // ---------------------------------------------------------------
438
453
  if(!cPrice) cPrice = portfolio[xmrbtc][1];
439
- let closeO = posP ? cPrice : 0;
454
+ let closeO = posP ? cPrice : null;
440
455
  let ret = await order(buysell,xmrbtc,price,amt,lev,uref,closeO);
441
456
  console.log("New order: "+ret);
442
457
  return;
443
458
  } else if(args[0] == 'set') {
444
- set(portfolio, args[1], args[2], args[3]);
459
+ await set(portfolio, args[1], args[2], args[3]);
445
460
  } else if(args[0] == 'reset') {
446
461
  portfolio['G'] = [];
447
462
  await listOpens(portfolio);
@@ -461,7 +476,7 @@ async function handleArgs(portfolio, args, uref = 0) {
461
476
  orders = [];
462
477
  for( o in response.result.closed) {
463
478
  let oo = response.result.closed[o];
464
- if(oo.status=='closed') orders.push([o,response.result.closed[o]]);
479
+ if(oo.status=='closed') orders.push([o,response.result.closed[o]]);
465
480
  }
466
481
  args.pop();
467
482
  }
@@ -482,7 +497,7 @@ async function handleArgs(portfolio, args, uref = 0) {
482
497
  } else {
483
498
  a = a[1][args[1]];
484
499
  b = b[1][args[1]];
485
- }
500
+ }
486
501
  return isNaN(a)
487
502
  ? a.localeCompare(b)
488
503
  : a - b;
@@ -492,7 +507,7 @@ async function handleArgs(portfolio, args, uref = 0) {
492
507
  console.log(i+1, x[1].descr[args[1]]
493
508
  ? x[1].descr[args[1]] : x[1][args[1]],
494
509
  ldo,x[1].userref,x[1].descr.close);
495
- });
510
+ });
496
511
  };
497
512
  } else if(args[0] == 'test') {
498
513
  // Put some test code here if you want
@@ -528,7 +543,7 @@ async function refnum(opensA,oid,newRef) {
528
543
  await order(bs,sym,p,amt,lev,newRef);
529
544
  } else {
530
545
  console.log(oRef+" already has userref "+o.userref);
531
- }
546
+ }
532
547
  }
533
548
 
534
549
  async function deleverage(opensA,oid,undo=false) {
@@ -551,7 +566,7 @@ async function deleverage(opensA,oid,undo=false) {
551
566
  await order(o.descr.type,/^([A-Z]+)USD/.exec(o.descr.pair)[1],
552
567
  o.descr.price,Math.round(10000*(Number(o.vol) - Number(o.vol_exec)))/10000,
553
568
  (undo ? '2' : 'none'),o.userref);
554
- } else {
569
+ } else {
555
570
  await order(o.descr.type,/^([A-Z]+)USD/.exec(o.descr.pair)[1],
556
571
  o.descr.price,Math.round(10000*(Number(o.vol) - Number(o.vol_exec)))/10000,
557
572
  (undo ? '2' : 'none'),o.userref,
@@ -563,12 +578,12 @@ async function deleverage(opensA,oid,undo=false) {
563
578
 
564
579
  function set(p,ur,type,price) {
565
580
  if(ur) {
566
- let gp = p['G'].find(g => g.userref==ur);
581
+ let gp = p['G'].find(g => g.userref==ur);
567
582
  if(!gp) {
568
583
  gp = {userref:Number(ur),buy:'?',sell:'?'};
569
584
  p['G'].push(gp);
570
585
  }
571
- console.log(405,gp);
586
+ console.log(405,gp);
572
587
  gp[type] = price;
573
588
  }
574
589
  p['G'].sort((a,b) => a.userref-b.userref);
@@ -576,33 +591,33 @@ function set(p,ur,type,price) {
576
591
  p['G'].forEach(x => {
577
592
  let f = toDec(((x.sell-x.buy)*Math.min(x.bought,x.sold)),2);
578
593
  console.log(x.userref+': '+x.buy+'-'+x.sell
579
- + ((x.bought+x.sold)>0
594
+ + ((x.bought+x.sold)>0
580
595
  ? (", bought "+toDec(x.bought,2)+" and sold "+toDec(x.sold,2)+' for ' + f)
581
596
  : '' ));
582
597
  if(!isNaN(f)) profits += f;
583
598
  });
584
- console.log("That's "+toDec(profits,2)+" since "+new Date(ts150*1000));
599
+ console.log("That's "+toDec(profits,2)+" since "+new Date(ts150*1000));
585
600
  }
586
601
 
587
602
  function toDec(n,places) {
588
603
  let f = 10**places;
589
604
  return Math.round(n*f)/f;
590
605
  }
591
- async function report(portfolio,showBalance=true) {
606
+ async function report(portfolio,showBalance=true) {
592
607
  let dataPromise = [
593
608
  'Balance',
594
- ['Ticker',{ pair : 'XBTUSD,XMRUSD,BCHUSD,DASHUSD,EOSUSD,ETHUSD,LTCUSD,USDTUSD,USTUSD,LUNAUSD' }],
609
+ ['Ticker',{ pair : USDPairs.join() }],
595
610
  'TradeBalance'
596
611
  ];
597
612
  try {
598
613
  dataPromise[0] = await kapi(dataPromise[0]);
599
614
  dataPromise[1] = await kapi(dataPromise[1]);
600
615
  dataPromise[2] = await kapi(dataPromise[2]);
601
- } catch(err) {
616
+ } catch(err) {
602
617
  catcher(411,err);
603
- console.log(423,"Waiting a minute...");
618
+ console.log(423,"Waiting a minute...");
604
619
  await sleep(60000);
605
- return;
620
+ return;
606
621
  }
607
622
  let [bal,tik,trb] = dataPromise;
608
623
  let mar = await marginReport(false);
@@ -613,15 +628,15 @@ async function report(portfolio,showBalance=true) {
613
628
 
614
629
  let price;
615
630
  for( const p in bal.result) {
616
- let ts = p+'USD',
617
- tsz = p+'ZUSD',
631
+ let ts = p+'USD', // #USD Refactor and basePair()
632
+ tsz = p+'ZUSD', // #USD Refactor and basePair()
618
633
  sym = /^X/.test(p) ? p.substr(1) : p,
619
634
  amt = toDec(bal.result[p],4);
620
635
  if(ts in tik.result) price = tik.result[ts].c[0];
621
636
  else if(tsz in tik.result) price = tik.result[tsz].c[0];
622
637
  price = toDec(price,(sym=='EOS'?4:2));
623
638
  portfolio[sym]=[amt,price,amt]; // holdings w/reserves, price, holdings w/o reserves
624
- if(mar[sym]) portfolio[sym][0] = toDec(portfolio[sym][0]+mar[sym].open,4);
639
+ if(mar[sym]) portfolio[sym][0] = toDec(portfolio[sym][0]+mar[sym].open,4);
625
640
  if(showBalance) console.log(p+"\t"+w(portfolio[sym][0],16)+price);
626
641
  }
627
642
  if(showBalance) {
@@ -636,8 +651,8 @@ async function report(portfolio,showBalance=true) {
636
651
  }
637
652
  }
638
653
  }
639
- //console.log(portfolio);
640
- console.log(new Date);
654
+ //console.log(portfolio);
655
+ console.log(new Date,' ',(auto>0?'A':'.')+(risky?'R':'.')+(safeMode?'S':'.'));
641
656
  await listOpens(portfolio,true);
642
657
  process.stdout.write("\033[A".repeat(cli.apl));
643
658
  cli.apl = 2;
@@ -665,7 +680,6 @@ async function marginReport(show = true) {
665
680
 
666
681
  let stopNow = false,
667
682
  portfolio = [],
668
- histi = Math.floor(Date.now() / 1000),
669
683
  ts150 = 0,
670
684
  delay = 60,
671
685
  auto = 0,
@@ -673,75 +687,74 @@ let stopNow = false,
673
687
  risky = false,
674
688
  cmdList = [],
675
689
  safeMode = true,
690
+ USDPairs = 'XBTUSD,XMRUSD,BCHUSD,DASHUSD,EOSUSD,ETHUSD,LTCUSD,USDTUSD,USTUSD,LUNAUSD'.split(',');
691
+ auto_on_hold = false;
676
692
  cli = {'apl': 0};
677
693
  async function runOnce(cmdList) {
678
- //while(!stopNow) {
679
- // if(cmd==null) cmd = prompt((auto>0 ? '('+delay+' min.)' : 'manual')+'>');
680
- let cmds = cmdList.map((x) => { return x.trim(); }),
681
- cdx = 0;
682
-
683
- console.log("Got "+(cmds.length)+" commands...");
684
- while(cdx < cmds.length) {
685
- let args = cmds[cdx++].split(' ').map((x) => { return x.trim(); });
686
- console.log("...("+cdx+")> "+args.join(' '));
687
- try {
688
- if(args[0] == 'kill') await kill(args[1],portfolio['O']);
689
- else if(args[0] == "ws") {
690
- if(kwsCheck) console.log("Kraken WebSocket heartbeat at "+kwsCheck);
691
- if(!kwsCheck || (new Date()).valueOf() > 10000+kwsCheck.valueOf()) {
692
- openSocket();
694
+ let cmds = cmdList.map((x) => { return x.trim(); }),
695
+ cdx = 0;
696
+ auto_on_hold = auto>0;
697
+
698
+ console.log("Got "+(cmds.length)+" commands...");
699
+ while(cdx < cmds.length) {
700
+ let args = cmds[cdx++].split(' ').map((x) => { return x.trim(); });
701
+ console.log("...("+cdx+")> "+args.join(' '));
702
+ try {
703
+ if(args[0] == 'kill') await kill(args[1],portfolio['O']);
704
+ else if(args[0] == "ws") {
705
+ if(kwsCheck) console.log("Kraken WebSocket heartbeat at "+kwsCheck);
706
+ if(!kwsCheck || (new Date()).valueOf() > 10000+kwsCheck.valueOf()) {
707
+ openSocket();
708
+ }
709
+ } else if(args[0] == "report" || args[0] == "") await report(portfolio);
710
+ else if(/^(manual)$/.test(args[0])) {
711
+ clearInterval(auto);
712
+ auto = 0;
713
+ } else if(args[0] == "auto") {
714
+ clearInterval(auto);
715
+ if(args[1]&&!isNaN(args[1])) delay = args[1];
716
+ let counter = delay;
717
+ auto = setInterval(async function() {
718
+ if(0 == --counter) {
719
+ if(!auto_on_hold) await report(portfolio,false);
720
+ counter = delay;
693
721
  }
694
- } else if(args[0] == "report" || args[0] == "") await report(portfolio);
695
- else if(/^(manual)$/.test(args[0])) {
696
- clearInterval(auto);
697
- auto = 0;
698
- } else if(args[0] == "auto") {
699
- clearInterval(auto);
700
- if(args[1]&&!isNaN(args[1])) delay = args[1];
701
- let counter = delay;
702
- auto = setInterval(async function() {
703
- if(0 == --counter) {
704
- await report(portfolio,false);
705
- counter = delay;
706
- }
707
- },1000);
708
- await report(portfolio);
709
- } else if(args[0] == "risky") {
710
- risky = !risky;
711
- console.log("Risky Mode is "+(risky
712
- ? 'on - Experimental additions will be tried' : 'off'));
713
- } else if(args[0] == "safe") {
714
- safeMode = !safeMode;
715
- console.log("Safe Mode is "+(safeMode
716
- ? 'on - Orders will be displayed butnot placed' : 'off'));
717
- } else if(args[0] == "verbose") {
718
- verbose = !verbose;
719
- console.log("Verbose is "+(verbose ? 'on' : 'off'));
720
- } else if(args[0] == 'margin') {
721
- await marginReport();
722
- } else await handleArgs(portfolio, args, ++histi).then(console.log);
723
- } catch(err) {
724
- catcher(468,err);
725
- }
726
- // Wait a sec for the nonce.
727
- // -------------------------
728
- await sleep(1000);
722
+ },1000);
723
+ await report(portfolio);
724
+ } else if(args[0] == "risky") {
725
+ risky = !risky;
726
+ console.log("Risky Mode is "+(risky
727
+ ? 'on - Experimental additions will be tried' : 'off'));
728
+ } else if(args[0] == "safe") {
729
+ safeMode = !safeMode;
730
+ console.log("Safe Mode is "+(safeMode
731
+ ? 'on - Orders will be displayed butnot placed' : 'off'));
732
+ } else if(args[0] == "verbose") {
733
+ verbose = !verbose;
734
+ console.log("Verbose is "+(verbose ? 'on' : 'off'));
735
+ } else if(args[0] == 'margin') {
736
+ await marginReport();
737
+ } else await handleArgs(portfolio, args, 0).then(console.log);
738
+ } catch(err) {
739
+ catcher(468,err);
729
740
  }
730
- //console.log("Try CRTL-C while I sleep for a minute...");
731
- //await sleep(1000);
732
- cmd = null;
733
- //}
741
+ // Wait a sec for the nonce.
742
+ // -------------------------
743
+ await sleep(1000);
744
+ }
745
+ //console.log("Try CRTL-C while I sleep for a minute...");
746
+ //await sleep(1000);
747
+ cmd = null;
748
+ auto_on_hold = false;
734
749
  }
735
750
 
736
- process.stdin.on('readable', async () => {
751
+ process.stdin.on('readable', () => {
737
752
  // clearInterval(auto);
738
753
  let cmd = '',
739
754
  waiter = 0;
740
755
  data = '';
741
756
  while(null != (data = process.stdin.read())) cmd += data;
742
757
  if(/^quit/.test(cmd)) {
743
- console.log("Userref collisions possible with restart before "
744
- + new Date(histi * 1000));
745
758
  process.exit(0);
746
759
  } else {
747
760
  clearTimeout(waiter);
@@ -749,7 +762,7 @@ process.stdin.on('readable', async () => {
749
762
  // Do we need to stop this listener from listening while runOnce runs?
750
763
  if(cmdList.length > 0) runOnce(cmdList).catch((err) => { catcher(496,err); });
751
764
  cmdList = [];
752
- },100);
765
+ },100);
753
766
  cmdList.push(cmd);
754
767
  }
755
768
  });
@@ -758,6 +771,7 @@ function catcher(line,err) {
758
771
  if(/ETIMEDOUT/.test(err.code)) return; // We can ignore timeout errors.
759
772
  console.log("Line "+line+";\n",err);
760
773
  clearInterval(auto);
774
+ auto = 0;
761
775
  }
762
776
 
763
777
  process.on('uncaughtException', function (err) {
@@ -765,13 +779,13 @@ process.on('uncaughtException', function (err) {
765
779
  });
766
780
 
767
781
  let kwsCheck;
768
- function krakenSaid(obj) {
782
+ async function krakenSaid(obj) {
769
783
  if(obj.event=='heartbeat') {
770
784
  kwsCheck = new Date();
771
785
  } else {
772
786
  console.log(557,obj);
773
787
  if(Array.isArray(obj)) {
774
- runOnce(['report']).catch((err) => { catcher(543,err); });
788
+ await runOnce(['report']).catch((err) => { catcher(543,err); });
775
789
  }
776
790
  }
777
791
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kraken-grid",
3
- "version": "1.0.3",
3
+ "version": "1.0.6",
4
4
  "description": "Bot repeatedly buys & sells on kraken from a conditional close order.",
5
5
  "main": "index.js",
6
6
  "bin": "./index.js",
@@ -24,6 +24,7 @@
24
24
  },
25
25
  "homepage": "https://github.com/dscotese/kraken-grid#readme",
26
26
  "dependencies": {
27
+ "kraka-djs": "^1.0.2",
27
28
  "kraken-api": "^1.0.1",
28
29
  "prompt-sync": "^4.2.0",
29
30
  "ws": "^8.4.0"