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.
- package/README.md +5 -5
- package/index.js +132 -118
- 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
|
|
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
|
|
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
|
-
|
|
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
|
|
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('
|
|
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
|
-
|| /
|
|
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=
|
|
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
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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);
|
|
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
|
|
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
|
|
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
|
|
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 :
|
|
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 :
|
|
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
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
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
|
-
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
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
|
-
//
|
|
731
|
-
//
|
|
732
|
-
|
|
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',
|
|
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
|
+
"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"
|