From 10c23846ac9c8c0208e701c4c91b81aec4707593 Mon Sep 17 00:00:00 2001 From: Travis Frisinger Date: Sat, 6 Jan 2018 22:39:13 +0200 Subject: [PATCH] Added --buy_max_amt option to allow for multiple bots on same exchange (#1051) * Found where I need to adjust stuff in the trade bot! * Added issue link, seems simple enough, set a max amount and push it below * Added in buy_amt_max to cap the % that can be traded * renamed buy_amt_max to buy_max_amt * Added buy_max_amt tothe README.md options * Added engine.test.js * Added test for my --buy_max_amt option * Removed all ; from test file, seems as though the standard is no ; * Refactored test to be better structured * Fixed README.md formating * Fixed trade.js formating issue * fixed some more formating issues * Fixed some more formating issues * Update engine.js --- README.md | 1 + commands/trade.js | 1 + lib/engine.js | 14 +++- test/lib/engine.test.js | 173 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 test/lib/engine.test.js diff --git a/README.md b/README.md index dea56488c8..1a9bce9f8b 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,7 @@ zenbot trade --help --asset_capital for paper trading, amount of start capital in asset --avg_slippage_pct avg. amount of slippage to apply to paper trades --buy_pct buy with this % of currency balance + --buy_max_amt buy with up to this amount of currency balance --sell_pct sell with this % of asset balance --markdown_buy_pct % to mark down buy price (previously the --markup_pct property) --markup_sell_pct % to mark up sell price (previously the --markup_pct property) diff --git a/commands/trade.js b/commands/trade.js index 242fa432c7..88501a83b4 100644 --- a/commands/trade.js +++ b/commands/trade.js @@ -28,6 +28,7 @@ module.exports = function container (get, set, clear) { .option('--asset_capital ', 'for paper trading, amount of start capital in asset', Number, c.asset_capital) .option('--avg_slippage_pct ', 'avg. amount of slippage to apply to paper trades', Number, c.avg_slippage_pct) .option('--buy_pct ', 'buy with this % of currency balance', Number, c.buy_pct) + .option('--buy_max_amt ', 'buy with up to this amount of currency balance', Number, c.buy_max_amt) .option('--sell_pct ', 'sell with this % of asset balance', Number, c.sell_pct) .option('--markdown_buy_pct ', '% to mark down buy price', Number, c.markdown_buy_pct) .option('--markup_sell_pct ', '% to mark up sell price', Number, c.markup_sell_pct) diff --git a/lib/engine.js b/lib/engine.js index e11f254a02..e8c826f490 100644 --- a/lib/engine.js +++ b/lib/engine.js @@ -188,6 +188,7 @@ module.exports = function container (get, set, clear) { if (so.mode !== 'live') { return cb() } + s.exchange.getBalance({currency: s.currency, asset: s.asset}, function (err, balance) { if (err) return cb(err) s.balance = balance @@ -433,12 +434,19 @@ module.exports = function container (get, set, clear) { price = n(quote.bid).subtract(n(quote.bid).multiply(so.markdown_buy_pct / 100)).format(s.product.increment, Math.floor) if (!size) { if (so.mode === 'live') { + var buy_pct = so.buy_pct + if(so.buy_max_amt){ + var buy_max_as_pct = n(so.buy_max_amt).divide(s.balance.currency).multiply(100) + if(buy_max_as_pct < buy_pct){ + buy_pct = buy_max_as_pct + } + } if (so.order_type === 'maker') { - size = n(s.balance.currency).multiply(so.buy_pct).divide(100).multiply(s.exchange.makerFee / 100).format('0.00000000'); + size = n(s.balance.currency).multiply(buy_pct).divide(100).multiply(s.exchange.makerFee / 100).format('0.00000000') } else { - size = n(s.balance.currency).multiply(so.buy_pct).divide(100).multiply(s.exchange.takerFee / 100).format('0.00000000'); + size = n(s.balance.currency).multiply(buy_pct).divide(100).multiply(s.exchange.takerFee / 100).format('0.00000000') } - size = n(s.balance.currency).multiply(so.buy_pct).divide(100).subtract(size).divide(price).format('0.00000000') + size = n(s.balance.currency).multiply(buy_pct).divide(100).subtract(size).divide(price).format('0.00000000') } else { size = n(s.balance.currency).multiply(so.buy_pct).divide(100).divide(price).format('0.00000000') } diff --git a/test/lib/engine.test.js b/test/lib/engine.test.js new file mode 100644 index 0000000000..2b44bb56c6 --- /dev/null +++ b/test/lib/engine.test.js @@ -0,0 +1,173 @@ +describe("Engine", function() { + describe("executeSignal", function() { + describe("when maker", function(){ + it("with buy_max_amt less than buy_pct amount should use buy_max_amt", function(){ + // arrange + var signal_type = "buy" + var currency_amount = 1 + var buy_pct = 50 + var buy_max_amt = 0.25 + var order_type = "maker" + var buy_spy = jasmine.createSpy() + var sut = createEngine(currency_amount, buy_pct, buy_max_amt, order_type, buy_spy) + // act + sut.executeSignal(signal_type) + // assert + var expected = "2.77500000" + var buyArgs = buy_spy.calls.mostRecent().args[0] + expect(buyArgs.size).toBe(expected) + }) + + it("with buy_max_amt more than buy_pct amount should use buy_pct", function(){ + // arrange + var signal_type = "buy" + var currency_amount = 1 + var buy_pct = 50 + var buy_max_amt = 0.75 + var order_type = "maker" + var buy_spy = jasmine.createSpy() + var sut = createEngine(currency_amount, buy_pct, buy_max_amt, order_type, buy_spy) + // act + sut.executeSignal(signal_type) + // assert + var expected = "5.55000000" + var buyArgs = buy_spy.calls.mostRecent().args[0] + expect(buyArgs.size).toBe(expected) + }) + + it("with buy_max_amt equals buy_pct amount should use buy_pct", function(){ + // arrange + var signal_type = "buy" + var currency_amount = 1 + var buy_pct = 50 + var buy_max_amt = 0.50 + var order_type = "maker" + var buy_spy = jasmine.createSpy() + var sut = createEngine(currency_amount, buy_pct, buy_max_amt, order_type, buy_spy) + // act + sut.executeSignal(signal_type) + // assert + var expected = "5.55000000" + var buyArgs = buy_spy.calls.mostRecent().args[0] + expect(buyArgs.size).toBe(expected) + }) + }) + + describe("when taker", function(){ + it("with buy_max_amt less than buy_pct amount should use buy_max_amt", function(){ + // arrange + var signal_type = "buy" + var currency_amount = 1 + var buy_pct = 50 + var buy_max_amt = 0.25 + var order_type = "taker" + var buy_spy = jasmine.createSpy() + var sut = createEngine(currency_amount, buy_pct, buy_max_amt, order_type, buy_spy) + // act + sut.executeSignal(signal_type) + // assert + var expected = "2.77222222" + var buyArgs = buy_spy.calls.mostRecent().args[0] + expect(buyArgs.size).toBe(expected) + }) + + it("with buy_max_amt more than buy_pct amount should use buy_pct", function(){ + // arrange + var signal_type = "buy" + var currency_amount = 1 + var buy_pct = 50 + var buy_max_amt = 0.75 + var order_type = "taker" + var buy_spy = jasmine.createSpy() + var sut = createEngine(currency_amount, buy_pct, buy_max_amt, order_type, buy_spy) + // act + sut.executeSignal(signal_type) + // assert + var expected = "5.54444444" + var buyArgs = buy_spy.calls.mostRecent().args[0] + expect(buyArgs.size).toBe(expected) + }) + + it("with buy_max_amt equals buy_pct amount should use buy_pct", function(){ + // arrange + var signal_type = "buy" + var currency_amount = 1 + var buy_pct = 50 + var buy_max_amt = 0.50 + var order_type = "taker" + var buy_spy = jasmine.createSpy() + var sut = createEngine(currency_amount, buy_pct, buy_max_amt, order_type, buy_spy) + // act + sut.executeSignal(signal_type) + // assert + var expected = "5.54444444" + var buyArgs = buy_spy.calls.mostRecent().args[0] + expect(buyArgs.size).toBe(expected) + }) + }) + }) +}) + +function createEngine(currency_amount, buy_pct, buy_max_amt, order_type, buy_spy){ + var fake_asset = "test_asset" + var fake_currency = "BTC" + var fake_exchange = "test_exchange" + var fake_project = "test_product" + var fake_bid = 0.10 + var fake_ask = 0.11 + var fake_balance = { currency: currency_amount, asset:0} + + var fakes = { + get: function() { }, + set: function() { }, + clear: function() { } + } + + var fake_product = { + "asset": fake_asset, + "currency": fake_currency, + "min_total": "0.1", + "max_size": null, + "increment": "0.01", + "label": "Test TST/BTC" + } + + var fake_return = { + "conf": {}, + "exchanges.test_exchange" : { + getProducts: function() { return [fake_product] }, + getQuote: function(product, callback){ callback(null, { bid: fake_bid, ask: fake_ask}) }, + getBalance: function(args, callback){ return callback(null, fake_balance)}, + buy: buy_spy, + name: fake_exchange, + makerFee: 0.1, + takerFee: 0.2 + }, + "lib.notify": { + pushMessage: function(title, message){ } + } + } + + spyOn(fakes,"get").and.callFake(function(param){ + return fake_return[param] + }) + + var engine = require("../../lib/engine")(fakes.get, fakes.set, fakes.clear) + var input = { + options: { + selector: { + exchange_id:fake_exchange, + product_id:fake_project, + asset:fake_asset, + currency: fake_currency + }, + period: "30m", + markdown_buy_pct : 2, + mode:"live", + order_type: order_type, + buy_pct:buy_pct, + buy_max_amt:buy_max_amt + } + } + return engine(input) +} \ No newline at end of file