first commit
This commit is contained in:
commit
aa7869eb33
386 files changed
+69375
No files matched your search
@@ -0,0 +1,311 @@
|
||||
/**
|
||||
* Created by rocketman1337345 on 8/14/16.
|
||||
* Extended by RJPGriffin (Gryphon) on 30/5/18
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
Required Config:
|
||||
|
||||
config.pushbullet = {
|
||||
// sends pushbullets if true
|
||||
enabled: true,
|
||||
// Send 'Gekko starting' message if true
|
||||
sendMessageOnStart: true,
|
||||
// Send Message for advice? Recommend Flase for paper, true for live
|
||||
sendOnAdvice: true,
|
||||
// Send Message on Trade Completion?
|
||||
sendOnTrade: true,
|
||||
// For Overall P/L calc. Pass in old balance if desired, else leave '0'
|
||||
startingBalance: '0',
|
||||
// your pushbullet API key
|
||||
key: '',
|
||||
// your email
|
||||
email: '[email protected]',
|
||||
// Messages will start with this tag
|
||||
tag: '[GEKKO]'
|
||||
};
|
||||
|
||||
|
||||
**/
|
||||
|
||||
var pushbullet = require("pushbullet");
|
||||
var _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
const request = require('request');
|
||||
var log = require('../core/log.js');
|
||||
var util = require('../core/util.js');
|
||||
var config = util.getConfig();
|
||||
var pbConf = config.pushbullet;
|
||||
|
||||
var Pushbullet = function(done) {
|
||||
_.bindAll(this);
|
||||
|
||||
this.pusher;
|
||||
this.price = 'N/A';
|
||||
this.startingBalance = pbConf.startingBalance === undefined ? 0 : Number(pbConf.startingBalance);
|
||||
this.advicePrice = 0;
|
||||
this.adviceTime = moment();
|
||||
this.lastBuyTime = moment();
|
||||
this.lastBuyBalance = 0;
|
||||
this.hasBought = 0;
|
||||
|
||||
this.done = done;
|
||||
this.setup();
|
||||
};
|
||||
|
||||
Pushbullet.prototype.setup = function(done) {
|
||||
|
||||
var setupPushBullet = function(err, result) {
|
||||
if (pbConf.sendMessageOnStart) {
|
||||
var title = pbConf.tag;
|
||||
var exchange = config.watch.exchange;
|
||||
var currency = config.watch.currency;
|
||||
var asset = config.watch.asset;
|
||||
let tradeType = 'watching';
|
||||
if (config.trader.enabled) {
|
||||
tradeType = "Live Trading";
|
||||
}
|
||||
if (config.paperTrader.enabled) {
|
||||
tradeType = "Paper Trading";
|
||||
}
|
||||
|
||||
var body = `Gekko has started ${tradeType} ${asset}/${currency} on ${exchange}.`;
|
||||
|
||||
//If trading Advisor is enabled, add strategy and candle size information
|
||||
if (config.tradingAdvisor.enabled) {
|
||||
body += `\n\nUsing ${config.tradingAdvisor.method} strategy on M${config.tradingAdvisor.candleSize} candles.`
|
||||
}
|
||||
|
||||
this.mail(title, body);
|
||||
} else {
|
||||
log.debug('Skipping Send message on startup')
|
||||
}
|
||||
};
|
||||
setupPushBullet.call(this)
|
||||
};
|
||||
|
||||
Pushbullet.prototype.processCandle = function(candle, done) {
|
||||
this.price = candle.close;
|
||||
|
||||
done();
|
||||
};
|
||||
|
||||
|
||||
Pushbullet.prototype.processAdvice = function(advice) {
|
||||
|
||||
this.advicePrice = this.price;
|
||||
this.adviceTime = advice.date;
|
||||
|
||||
if (pbConf.sendOnAdvice) {
|
||||
|
||||
var text = [
|
||||
'Gekko has new advice for ',
|
||||
capF(config.watch.exchange),
|
||||
', advice is to go ',
|
||||
capF(advice.recommendation),
|
||||
'.\n\nThe current ',
|
||||
config.watch.asset,
|
||||
' price is ',
|
||||
this.advicePrice
|
||||
].join('');
|
||||
|
||||
var subject = pbConf.tag + ' New advice: go ' + advice.recommendation;
|
||||
|
||||
this.mail(subject, text);
|
||||
}
|
||||
};
|
||||
|
||||
Pushbullet.prototype.processTradeCompleted = function(trade) {
|
||||
|
||||
//Check Starting balance is initialized - if 0, initialize it
|
||||
this.startingBalance = this.startingBalance ? this.startingBalance : trade.balance;
|
||||
|
||||
// If config variable doesn't exist (old config) defaults to send
|
||||
let sendOnTrade = pbConf.sendOnTrade === undefined ? 1 : pbConf.sendOnTrade;
|
||||
|
||||
if (sendOnTrade) {
|
||||
|
||||
// Calculate exposure Time
|
||||
let exposureTimeStr = '';
|
||||
let balanceChangeStr = '\n';
|
||||
let totBalanceChangeStr = '';
|
||||
let subject = `${pbConf.tag} ${capF(trade.action)} complete`;
|
||||
|
||||
if (trade.action === 'buy') {
|
||||
this.hasBought = 1; // Flag to ensure that the following variables have been filled
|
||||
this.lastBuyTime = trade.date;
|
||||
this.lastBuyBalance = trade.balance;
|
||||
} else if (this.hasBought) { //if sell and we have previous buy data
|
||||
exposureTimeStr = `\nExposure Time: ${moment.duration(trade.date.diff(this.lastBuyTime)).humanize()}`;
|
||||
|
||||
//Calculate balance change
|
||||
let oBal = this.lastBuyBalance; // Old Balance
|
||||
let nBal = trade.balance; // New Balance
|
||||
let diffBal = Math.abs(nBal - oBal); // Balance Difference
|
||||
let percDiffBal = (diffBal / oBal) * 100; // Percentage difference
|
||||
|
||||
|
||||
if (nBal >= oBal) { // profit!
|
||||
balanceChangeStr = `\n\nRound trip profit of: \n${getNumStr(diffBal)}${config.watch.currency} \n${getNumStr(percDiffBal,2)}%\n`
|
||||
subject = `${subject}: +${getNumStr(percDiffBal,2)}%`
|
||||
} else if (nBal < oBal) { // Loss :(
|
||||
balanceChangeStr = `\n\nRound trip loss of: \n-${getNumStr(diffBal)}${config.watch.currency} \n-${getNumStr(percDiffBal,2)}%\n`
|
||||
subject = `${subject}: -${getNumStr(percDiffBal,2)}%`
|
||||
}
|
||||
|
||||
//Calculate overall P/l
|
||||
let sBal = this.startingBalance;
|
||||
let tDiffBal = Math.abs(nBal - sBal);
|
||||
let percDiffTotBal = (tDiffBal / sBal) * 100;
|
||||
if (nBal >= sBal) { // profit!
|
||||
totBalanceChangeStr = `\nOverall gain of: \n${getNumStr(tDiffBal)}${config.watch.currency} \n${getNumStr(percDiffTotBal,2)}%\n`
|
||||
} else if (nBal < sBal) { // Loss :(
|
||||
totBalanceChangeStr = `\nOverall loss of \n-${getNumStr(tDiffBal)}${config.watch.currency} \n-${getNumStr(percDiffTotBal,2)}%\n`
|
||||
|
||||
} else if (trade.action === 'sell' && !this.hasBought) {
|
||||
balanceChangeStr = `\n\nNot enough data for exposure time, round trip or overall performance yet. This will appear after bot has completed first round trip.`
|
||||
}
|
||||
}
|
||||
|
||||
let costOfTradeStr = `\nCost of Trade: ${getNumStr(trade.cost)}${config.watch.currency}, ${getNumStr(((trade.cost / (trade.amount*trade.price)) * 100), 2)}%`;
|
||||
|
||||
//build strings that are only sent for Live trading, not paperTrader
|
||||
let orderFillTimeStr = '';
|
||||
let slippageStr = '';
|
||||
|
||||
if (!config.paperTrader.enabled) {
|
||||
let timeToComplete = moment.duration(trade.date.diff(this.adviceTime)).humanize();
|
||||
orderFillTimeStr = `\nOrder fill Time: ${timeToComplete}`;
|
||||
|
||||
var slip;
|
||||
//Slip direction is opposite for buy and sell
|
||||
if (trade.price === this.advicePrice) {
|
||||
slip = 0;
|
||||
} else if (trade.action === 'buy') {
|
||||
slip = 100 * ((trade.price - this.advicePrice) / this.advicePrice);
|
||||
} else if (trade.action === 'sell') {
|
||||
slip = 100 * ((this.advicePrice - trade.price) / this.advicePrice);
|
||||
}
|
||||
slippageStr = `\nSlipped ${getNumStr(slip,2)}% from advice @ ${getNumStr(this.advicePrice)}`;
|
||||
|
||||
|
||||
}
|
||||
|
||||
var text = [
|
||||
capF(config.watch.exchange), ' ', config.watch.asset, '/', config.watch.currency,
|
||||
`\n\n${config.watch.asset} Trade Price: ${getNumStr(trade.price)}`,
|
||||
`\n${getPastTense(trade.action)} ${getNumStr(trade.amount)} ${config.watch.asset}`,
|
||||
orderFillTimeStr,
|
||||
slippageStr,
|
||||
costOfTradeStr,
|
||||
exposureTimeStr,
|
||||
balanceChangeStr,
|
||||
totBalanceChangeStr,
|
||||
'\nBalance: ', getNumStr(trade.balance), config.watch.currency,
|
||||
].join('');
|
||||
|
||||
this.mail(subject, text);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// A long winded function to make sure numbers aren't displayed with too many decimal places
|
||||
// and are a little humanized
|
||||
function getNumStr(num, fixed = 4) {
|
||||
let numStr = '';
|
||||
|
||||
if (typeof num != "number") {
|
||||
num = Number(num);
|
||||
if (isNaN(num)) {
|
||||
// console.log("Pushbullet Plugin: Number Conversion Failed");
|
||||
return "Conversion Failure";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (Number.isInteger(num)) {
|
||||
numStr = num.toString();
|
||||
} else {
|
||||
|
||||
//Create modNum Max - Must be a better way...
|
||||
let modNumMax = '1';
|
||||
for (let i = 1; i < fixed; i++) {
|
||||
modNumMax = modNumMax + '0';
|
||||
}
|
||||
modNumMax = Number(modNumMax);
|
||||
|
||||
let i = 0;
|
||||
if (num < 1) {
|
||||
let modNum = num - Math.floor(num);
|
||||
while (modNum < modNumMax && i < 8) {
|
||||
modNum *= 10;
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
i = fixed;
|
||||
}
|
||||
numStr = num.toFixed(i);
|
||||
//Remove any excess zeros
|
||||
while (numStr.charAt(numStr.length - 1) === '0') {
|
||||
numStr = numStr.substring(0, numStr.length - 1);
|
||||
}
|
||||
|
||||
//If last char remaining is a decimal point, remove it
|
||||
if (numStr.charAt(numStr.length - 1) === '.') {
|
||||
numStr = numStr.substring(0, numStr.length - 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Add commas for thousands etc
|
||||
let dp = numStr.indexOf('.'); //find deciaml point
|
||||
if (dp < 0) { //no dp found
|
||||
dp = numStr.length;
|
||||
}
|
||||
|
||||
let insPos = dp - 3;
|
||||
insCount = 0;
|
||||
while (insPos > 0) {
|
||||
insCount++;
|
||||
numStr = numStr.slice(0, insPos) + ',' + numStr.slice(insPos);
|
||||
insPos -= 3;
|
||||
}
|
||||
|
||||
|
||||
return (numStr);
|
||||
}
|
||||
|
||||
function capF(inWord) { //Capitalise first letter of string
|
||||
return (inWord.charAt(0).toUpperCase() + inWord.slice(1));
|
||||
}
|
||||
|
||||
function getPastTense(action) {
|
||||
let ret = '';
|
||||
if (action === 'buy') {
|
||||
ret = 'Bought'
|
||||
} else if (action === 'sell') {
|
||||
ret = 'Sold'
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Pushbullet.prototype.mail = function(subject, content, done) {
|
||||
var pusher = new pushbullet(pbConf.key);
|
||||
pusher.note(pbConf.email, subject, content, function(error, response) {
|
||||
if (error || !response) {
|
||||
log.error('Pushbullet ERROR:', error)
|
||||
} else if (response && response.active) {
|
||||
log.info('Pushbullet Message Sent')
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Pushbullet.prototype.checkResults = function(err) {
|
||||
if (err)
|
||||
log.warn('error sending pushbullet message', err);
|
||||
else
|
||||
log.info('Send advice via pushbullet message.');
|
||||
};
|
||||
|
||||
module.exports = Pushbullet;
|
||||
Reference in new issue
Block a user