From c095652db51b6d5a9c139ef0f51dabc5a2d57827 Mon Sep 17 00:00:00 2001 From: Siim Haugas Date: Fri, 6 Mar 2015 14:28:12 +0200 Subject: [PATCH 1/5] Fix #266 bitstamp trader fails to buy; Add some mocha tests --- README.md | 11 ++- core/portfolioManager.js | 24 +++--- exchanges/bitstamp.js | 5 +- mocha_test/portfolioManager.js | 139 +++++++++++++++++++++++++++++++++ package.json | 7 +- 5 files changed, 169 insertions(+), 17 deletions(-) create mode 100644 mocha_test/portfolioManager.js diff --git a/README.md b/README.md index a05ae787a..a0be09974 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ You are looking at the brand new and completetly different version of Gekko. We' -Gordon Gekko -Gekko is a Bitcoin trading bot and backtesting platform that connects to popular Bitcoin exchanges. It is written in javascript and runs on [nodejs](http://nodejs.org). +Gekko is a Bitcoin trading bot and backtesting platform that connects to popular Bitcoin exchanges. It is written in javascript and runs on [nodejs](http://nodejs.org). This is the open source do-it-yourself version, we are planning on running hosted Gekkos in the cloud which does not require you to download and install anything, configure any textfiles or deal with the commandline. If you are looking for such a solution, sign up at [Wizbit](http://wizb.it) and we'll let you know once it's out! @@ -81,8 +81,8 @@ You need to download Gekko's dependencies, which can easily be done with [npm](h ## Configuring Gekko -> Configuring Gekko consists of three parts: -> +> Configuring Gekko consists of three parts: +> > - Watching a realtime market > - Automate trading advice > - Enabling plugins @@ -120,6 +120,11 @@ If you want to contribute or are interested in how Gekko works: * More exchanges (bitfinex, btcchina) * More indicators * Webbased interface +* Write more tests + +## Running mocha tests + npm install mocha -g + mocha mocha_test ## Credits diff --git a/core/portfolioManager.js b/core/portfolioManager.js index b2242959b..4fbb6ed58 100644 --- a/core/portfolioManager.js +++ b/core/portfolioManager.js @@ -67,13 +67,13 @@ Manager.prototype.init = function(callback) { // Because on cex.io your asset grows refresh and // display portfolio stats every 5 minutes if(this.exchange.name === 'cex.io') - setInterval(this.recheckPortfolio, util.minToMs(5)); + setInterval(this.recheckPortfolio, util.minToMs(5)); } Manager.prototype.setPortfolio = function(callback) { var set = function(err, portfolio) { this.portfolio = portfolio; - + if(_.isFunction(callback)) callback(); }; @@ -83,7 +83,7 @@ Manager.prototype.setPortfolio = function(callback) { Manager.prototype.setFee = function(callback) { var set = function(err, fee) { this.fee = fee; - + if(_.isFunction(callback)) callback(); }; @@ -93,7 +93,7 @@ Manager.prototype.setFee = function(callback) { Manager.prototype.setTicker = function(callback) { var set = function(err, ticker) { this.ticker = ticker; - + if(_.isFunction(callback)) callback(); } @@ -110,15 +110,15 @@ Manager.prototype.getBalance = function(fund) { // This function makes sure order get to the exchange // and initiates follow up to make sure the orders will -// get executed. This is the backbone of the portfolio +// get executed. This is the backbone of the portfolio // manager. -// +// // How this is done depends on a couple of things: -// +// // is this a directExchange? (does it support MKT orders) // is this a infinityOrderExchange (does it support order // requests bigger then the current balance?) -Manager.prototype.trade = function(what) { +Manager.prototype.trade = function(what, callback) { if(what !== 'BUY' && what !== 'SELL') return; @@ -135,7 +135,7 @@ Manager.prototype.trade = function(what) { if(this.infinityOrderExchange) amount = 10000; else - amount = this.getBalance(this.currency) / (this.ticker.ask+(this.ticker.ask*this.fee)); + amount = this.getBalance(this.currency) / (parseFloat(this.ticker.ask) + (parseFloat(this.ticker.ask)*this.fee)); // can we just create a MKT order? if(this.directExchange) @@ -168,9 +168,13 @@ Manager.prototype.trade = function(what) { log.debug('Trade Percent: adjusting amount', amount, 'by ', this.tradePercent, '%'); amount = amount * this.tradePercent / 100; } - + this.sell(amount, price); } + + if(callback) { + callback(); + } }; async.series([ this.setTicker, diff --git a/exchanges/bitstamp.js b/exchanges/bitstamp.js index b73125bfd..0abc2177a 100644 --- a/exchanges/bitstamp.js +++ b/exchanges/bitstamp.js @@ -65,6 +65,7 @@ Trader.prototype.getFee = function(callback) { callback(err); callback(false, data.fee / 100); + console.log('data.fee: ', data.fee) } this.bitstamp.balance(_.bind(set, this)); } @@ -78,7 +79,7 @@ Trader.prototype.buy = function(amount, price, callback) { }; // TODO: fees are hardcoded here? - amount *= 0.995; // remove fees + // amount *= 0.995; // remove fees // prevent: Ensure that there are no more than 8 digits in total. amount *= 100000000; amount = Math.floor(amount); @@ -128,4 +129,4 @@ Trader.prototype.getTrades = function(since, callback, descending) { } -module.exports = Trader; \ No newline at end of file +module.exports = Trader; diff --git a/mocha_test/portfolioManager.js b/mocha_test/portfolioManager.js new file mode 100644 index 000000000..00cfc3f74 --- /dev/null +++ b/mocha_test/portfolioManager.js @@ -0,0 +1,139 @@ +'use strict'; +var Manager = require('../core/PortfolioManager.js'); +var util = require('../core/util.js'); +var _ = require('lodash'); +var chai = chai || require('chai'); +var sinon = sinon || require('sinon'); +var should = chai.should(); + +describe('Portfolio Manager', function(){ + + var config = util.getConfig(); + var manager; + + config.watch = { + enabled: true, + exchange: 'bitstamp', // @link https://github.com/askmike/gekko#supported-exchanges + key: 'LIVzgHi4vS1YKyuae9TUGNR4k2C55BYF', + secret: 'itUNBJMLGKrjQI1UGIqJT7o9pqDAFPkk', + currency: 'USD', + asset: 'BTC' + } + + config.trader = { + enabled: true, + tradePercent: 10, + username: '100000' // your username, only fill in when using bitstamp or cexio + } + + + manager = new Manager(_.extend(config.trader, config.watch)); + + var stubInit = sinon.stub(manager, "init", function(callback) { + // setportfolio + // setfee + this.portfolio = [ { name: 'BTC', amount: 0.99 }, { name: 'USD', amount: 242.2 } ]; + this.fee = 0.0020; + callback(); + }); + + var setPortfolioStub = sinon.stub(manager, "setPortfolio", function(callback) { + this.portfolio = [ { name: 'BTC', amount: 0.99 }, { name: 'USD', amount: 242.2 } ]; + callback(); + }.bind(manager)); + + + it('should fail to create an instance without config', function() { + (function(){ + new Manager({}); + }).should.throw(); + }); + + + it('should not fail to create an instance with config', function() { + (function(){ + new Manager(_.extend(config.trader, config.watch)); + }).should.not.throw(); + }); + + + describe('#init', function(){ + + it('should successfully initialize', function(done) { + manager.init(done); + }); + + it('should have fee', function(){ + manager.fee.should.equal(0.0020); + }); + + it('should have fee type that is number', function(){ + manager.fee.should.be.a('number'); + }); + + it('should have a portfolio', function(){ + manager.portfolio.should.be.an('array'); + }); + + }); + + + describe('#trade', function(){ + it('should buy if necessary balance exists', function(done){ + manager.trade('BUY', done); + }); + + it('should buy if necessary balance exists', function(done){ + manager.trade('BUY', done); + }); + }); + + + describe('#getFund', function(){ + + }); + + + describe('#getBalance', function(){ + + }); + + + describe('#getMinimum', function(){ + + }); + + + describe('#buy', function(){ + + }); + + + describe('#sell', function(){ + + }); + + + describe('#noteOrder', function(){ + + }); + + + describe('#checkOrder', function(){ + + }); + + + describe('#logPortfolio', function(){ + + }); + + + describe('#recheckPortfolio', function(){ + + }); + + describe('#enforcePosition', function(){ + + }); +}); diff --git a/package.json b/package.json index 0eac1a1b4..f87f88af9 100644 --- a/package.json +++ b/package.json @@ -26,10 +26,13 @@ "semver": "2.2.1", "kraken-api": "0.1.x", "lakebtc_nodejs": "0.1.x", - "pushbullet": "1.4.3" + "pushbullet": "1.4.3" }, "devDependencies": { - "nodeunit": "0.8.2" + "nodeunit": "0.8.2", + "mocha": "latest", + "sinon": "latest", + "chai": "latest" }, "engines": { "node": ">=0.10.x" }, "repository": { From 26e1a3204537b710460dc2edeb150b391fa1cdfe Mon Sep 17 00:00:00 2001 From: Siim Haugas Date: Fri, 6 Mar 2015 14:28:52 +0200 Subject: [PATCH 2/5] Remove log message --- exchanges/bitstamp.js | 1 - 1 file changed, 1 deletion(-) diff --git a/exchanges/bitstamp.js b/exchanges/bitstamp.js index 0abc2177a..854b0451b 100644 --- a/exchanges/bitstamp.js +++ b/exchanges/bitstamp.js @@ -65,7 +65,6 @@ Trader.prototype.getFee = function(callback) { callback(err); callback(false, data.fee / 100); - console.log('data.fee: ', data.fee) } this.bitstamp.balance(_.bind(set, this)); } From 0805eac3330aa8db000a84d7d7b7779bca9143dd Mon Sep 17 00:00:00 2001 From: Siim Haugas Date: Sun, 8 Mar 2015 16:45:30 +0200 Subject: [PATCH 3/5] Fix #240 Unable to buy on bitstamp --- core/portfolioManager.js | 2 +- exchanges/bitstamp.js | 2 -- mocha_test/portfolioManager.js | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/core/portfolioManager.js b/core/portfolioManager.js index 4fbb6ed58..2a7d6014d 100644 --- a/core/portfolioManager.js +++ b/core/portfolioManager.js @@ -135,7 +135,7 @@ Manager.prototype.trade = function(what, callback) { if(this.infinityOrderExchange) amount = 10000; else - amount = this.getBalance(this.currency) / (parseFloat(this.ticker.ask) + (parseFloat(this.ticker.ask)*this.fee)); + amount = parseFloat((this.getBalance(this.currency) / (parseFloat(this.ticker.ask) + (parseFloat(this.ticker.ask)*this.fee))).toFixed(4); // can we just create a MKT order? if(this.directExchange) diff --git a/exchanges/bitstamp.js b/exchanges/bitstamp.js index 854b0451b..e9647be82 100644 --- a/exchanges/bitstamp.js +++ b/exchanges/bitstamp.js @@ -77,8 +77,6 @@ Trader.prototype.buy = function(amount, price, callback) { callback(null, result.id); }; - // TODO: fees are hardcoded here? - // amount *= 0.995; // remove fees // prevent: Ensure that there are no more than 8 digits in total. amount *= 100000000; amount = Math.floor(amount); diff --git a/mocha_test/portfolioManager.js b/mocha_test/portfolioManager.js index 00cfc3f74..60c995410 100644 --- a/mocha_test/portfolioManager.js +++ b/mocha_test/portfolioManager.js @@ -33,7 +33,7 @@ describe('Portfolio Manager', function(){ // setportfolio // setfee this.portfolio = [ { name: 'BTC', amount: 0.99 }, { name: 'USD', amount: 242.2 } ]; - this.fee = 0.0020; + this.fee = 0.0025; callback(); }); @@ -64,7 +64,7 @@ describe('Portfolio Manager', function(){ }); it('should have fee', function(){ - manager.fee.should.equal(0.0020); + manager.fee.should.equal(0.0025); }); it('should have fee type that is number', function(){ From b3614e8beac023a7a0d2f03f7448276ca32fa047 Mon Sep 17 00:00:00 2001 From: Siim Haugas Date: Thu, 19 Mar 2015 07:25:08 -0400 Subject: [PATCH 4/5] Fix SELL and parenthesis --- core/portfolioManager.js | 4 ++-- exchanges/bitstamp.js | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/core/portfolioManager.js b/core/portfolioManager.js index 2a7d6014d..5d4f256eb 100644 --- a/core/portfolioManager.js +++ b/core/portfolioManager.js @@ -135,7 +135,7 @@ Manager.prototype.trade = function(what, callback) { if(this.infinityOrderExchange) amount = 10000; else - amount = parseFloat((this.getBalance(this.currency) / (parseFloat(this.ticker.ask) + (parseFloat(this.ticker.ask)*this.fee))).toFixed(4); + amount = parseFloat((this.getBalance(this.currency) / (parseFloat(this.ticker.ask) + (parseFloat(this.ticker.ask)*this.fee))).toFixed(4)); // can we just create a MKT order? if(this.directExchange) @@ -156,7 +156,7 @@ Manager.prototype.trade = function(what, callback) { if(this.infinityOrderExchange) amount = 10000; else - amount = this.getBalance(this.asset); + amount = parseFloat(this.getBalance(this.asset) - this.getBalance(this.asset) * this.fee).toFixed(4); // can we just create a MKT order? if(this.directExchange) diff --git a/exchanges/bitstamp.js b/exchanges/bitstamp.js index e9647be82..a1dc9d84b 100644 --- a/exchanges/bitstamp.js +++ b/exchanges/bitstamp.js @@ -77,6 +77,8 @@ Trader.prototype.buy = function(amount, price, callback) { callback(null, result.id); }; + // TODO: fees are hardcoded here? + // amount *= 0.995; // remove fees // prevent: Ensure that there are no more than 8 digits in total. amount *= 100000000; amount = Math.floor(amount); @@ -92,6 +94,11 @@ Trader.prototype.sell = function(amount, price, callback) { callback(null, result.id); }; + + amount *= 10000000; + amount = Math.floor(amount); + amount /= 10000000; + this.bitstamp.sell(amount, price, _.bind(set, this)); } From 502543d3a56d6c80ea2cd3d52f29caceff9393d9 Mon Sep 17 00:00:00 2001 From: Siim Haugas Date: Thu, 19 Mar 2015 14:10:57 +0200 Subject: [PATCH 5/5] Fix whitespace differences --- README.md | 6 +++--- core/portfolioManager.js | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a0be09974..31599fcaa 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ You are looking at the brand new and completetly different version of Gekko. We' -Gordon Gekko -Gekko is a Bitcoin trading bot and backtesting platform that connects to popular Bitcoin exchanges. It is written in javascript and runs on [nodejs](http://nodejs.org). +Gekko is a Bitcoin trading bot and backtesting platform that connects to popular Bitcoin exchanges. It is written in javascript and runs on [nodejs](http://nodejs.org). This is the open source do-it-yourself version, we are planning on running hosted Gekkos in the cloud which does not require you to download and install anything, configure any textfiles or deal with the commandline. If you are looking for such a solution, sign up at [Wizbit](http://wizb.it) and we'll let you know once it's out! @@ -81,8 +81,8 @@ You need to download Gekko's dependencies, which can easily be done with [npm](h ## Configuring Gekko -> Configuring Gekko consists of three parts: -> +> Configuring Gekko consists of three parts: +> > - Watching a realtime market > - Automate trading advice > - Enabling plugins diff --git a/core/portfolioManager.js b/core/portfolioManager.js index 5d4f256eb..d63134fd2 100644 --- a/core/portfolioManager.js +++ b/core/portfolioManager.js @@ -67,13 +67,13 @@ Manager.prototype.init = function(callback) { // Because on cex.io your asset grows refresh and // display portfolio stats every 5 minutes if(this.exchange.name === 'cex.io') - setInterval(this.recheckPortfolio, util.minToMs(5)); + setInterval(this.recheckPortfolio, util.minToMs(5)); } Manager.prototype.setPortfolio = function(callback) { var set = function(err, portfolio) { this.portfolio = portfolio; - + if(_.isFunction(callback)) callback(); }; @@ -83,7 +83,7 @@ Manager.prototype.setPortfolio = function(callback) { Manager.prototype.setFee = function(callback) { var set = function(err, fee) { this.fee = fee; - + if(_.isFunction(callback)) callback(); }; @@ -93,7 +93,7 @@ Manager.prototype.setFee = function(callback) { Manager.prototype.setTicker = function(callback) { var set = function(err, ticker) { this.ticker = ticker; - + if(_.isFunction(callback)) callback(); } @@ -110,11 +110,11 @@ Manager.prototype.getBalance = function(fund) { // This function makes sure order get to the exchange // and initiates follow up to make sure the orders will -// get executed. This is the backbone of the portfolio +// get executed. This is the backbone of the portfolio // manager. -// +// // How this is done depends on a couple of things: -// +// // is this a directExchange? (does it support MKT orders) // is this a infinityOrderExchange (does it support order // requests bigger then the current balance?) @@ -168,7 +168,7 @@ Manager.prototype.trade = function(what, callback) { log.debug('Trade Percent: adjusting amount', amount, 'by ', this.tradePercent, '%'); amount = amount * this.tradePercent / 100; } - + this.sell(amount, price); }