From 6534aad3776da0096a3887e7fddf4494327b6229 Mon Sep 17 00:00:00 2001 From: Chase Date: Fri, 9 Jun 2017 11:34:35 +0200 Subject: [PATCH] Order type: allow consumer to specify type of order to use (#233) --- README.md | 2 ++ commands/sim.js | 1 + commands/trade.js | 6 ++++ conf-sample.js | 3 ++ extensions/exchanges/bittrex/exchange.js | 8 +++--- extensions/exchanges/gdax/exchange.js | 11 +++++++ extensions/exchanges/kraken/exchange.js | 7 +++-- extensions/exchanges/poloniex/exchange.js | 1 + lib/engine.js | 35 +++++++++++++++++------ 9 files changed, 60 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 218d4991dc..2a6c389735 100644 --- a/README.md +++ b/README.md @@ -206,6 +206,7 @@ zenbot trade --help -h, --help output usage information --conf path to optional conf overrides file --strategy strategy to use + --order_type order type to use (maker/taker) --paper use paper trading mode (no real trades will take place) --currency_capital for paper trading, amount of start capital in currency --asset_capital for paper trading, amount of start capital in asset @@ -325,6 +326,7 @@ The moving average convergence divergence calculation is a lagging indicator, us - `--oversold_rsi=` will try to buy when the price dives. This is one of the ways to get profit above buy/hold, but setting it too high might result in a loss of the price continues to fall. - In a market with predictable price surges and corrections, `--profit_stop_enable_pct=10` will try to sell when the last buy hits 10% profit and then drops to 9% (the drop % is set with `--profit_stop_pct`). However in strong, long uptrends this option may end up causing a sell too early. - As of v4.0.5, the `--neutral_rate=auto` filter is disabled, which is currently producing better results with the new default 10m period. Some coins may benefit from `--neutral_rate=auto` though, try simulating with and without it. +- For Kraken and GDAX you may wish to use `--order_type="taker"`, this uses market orders instead of limit orders. You usually pay a higher fee, but you can be sure that your order is filled instantly. This means that the sim will more closely match your live trading. Please note that GDAX does not charge maker fees (limit orders), so you will need to choose between not paying fees and running the risk orders do not get filled on time, or paying somewhat high % of fees and making sure your orders are always filled on time. ## Manual trade tools diff --git a/commands/sim.js b/commands/sim.js index 80f2983952..1c963f254d 100644 --- a/commands/sim.js +++ b/commands/sim.js @@ -15,6 +15,7 @@ module.exports = function container (get, set, clear) { .description('run a simulation on backfilled data') .option('--conf ', 'path to optional conf overrides file') .option('--strategy ', 'strategy to use', String, c.strategy) + .option('--order_type ', 'order type to use (maker/taker)', /^(maker|taker)$/i, c.order_type) .option('--filename ', 'filename for the result output (ex: result.html)', String, c.filename) .option('--start ', 'start at timestamp') .option('--end ', 'end at timestamp') diff --git a/commands/trade.js b/commands/trade.js index f2f92c0817..b4d05272f6 100644 --- a/commands/trade.js +++ b/commands/trade.js @@ -16,6 +16,7 @@ module.exports = function container (get, set, clear) { .description('run trading bot against live market data') .option('--conf ', 'path to optional conf overrides file') .option('--strategy ', 'strategy to use', String, c.strategy) + .option('--order_type ', 'order type to use (maker/taker)', /^(maker|taker)$/i, c.order_type) .option('--paper', 'use paper trading mode (no real trades will take place)', Boolean, false) .option('--currency_capital ', 'for paper trading, amount of start capital in currency', Number, c.currency_capital) .option('--asset_capital ', 'for paper trading, amount of start capital in asset', Number, c.asset_capital) @@ -63,6 +64,11 @@ module.exports = function container (get, set, clear) { } var engine = get('lib.engine')(s) + var order_types = ['maker', 'taker'] + if (!so.order_type in order_types) { + so.order_type = 'maker' + } + var db_cursor, trade_cursor var query_start = tb().resize(so.period).subtract(so.min_periods * 2).toMilliseconds() var days = Math.ceil((new Date().getTime() - query_start) / 86400000) diff --git a/conf-sample.js b/conf-sample.js index c9d83e2fe5..6c8a2b2250 100644 --- a/conf-sample.js +++ b/conf-sample.js @@ -27,6 +27,7 @@ c.gdax.passphrase = 'YOUR-PASSPHRASE' c.poloniex = {} c.poloniex.key = 'YOUR-API-KEY' c.poloniex.secret = 'YOUR-SECRET' +// please note: poloniex does not support market orders via the API // to enable Kraken trading, enter your API credentials: c.kraken = {} @@ -79,6 +80,8 @@ c.order_poll_time = 5000 c.wait_for_settlement = 5000 // % to mark up or down price for orders c.markup_pct = 0 +// become a market taker (high fees) or a market maker (low fees) +c.order_type = 'maker' // Misc options: diff --git a/extensions/exchanges/bittrex/exchange.js b/extensions/exchanges/bittrex/exchange.js index 9aa8952b89..83db8f6de8 100644 --- a/extensions/exchanges/bittrex/exchange.js +++ b/extensions/exchanges/bittrex/exchange.js @@ -217,18 +217,18 @@ module.exports = function container(get, set, clear) { } if (type === 'buy') { - if (opts.order_type === 'limit') { + if (opts.order_type === 'maker') { bittrex_authed.buylimit(params, fn) } - if (opts.order_type === 'market') { + if (opts.order_type === 'taker') { bittrex_authed.buymarket(params, fn) } } if (type === 'sell') { - if (opts.order_type === 'limit') { + if (opts.order_type === 'maker') { bittrex_authed.selllimit(params, fn) } - if (opts.order_type === 'market') { + if (opts.order_type === 'taker') { bittrex_authed.sellmarket(params, fn) } } diff --git a/extensions/exchanges/gdax/exchange.js b/extensions/exchanges/gdax/exchange.js index 8565a79d1c..419eb7ccd2 100644 --- a/extensions/exchanges/gdax/exchange.js +++ b/extensions/exchanges/gdax/exchange.js @@ -49,6 +49,7 @@ module.exports = function container (get, set, clear) { name: 'gdax', historyScan: 'backward', makerFee: 0, + takerFee: 0.3, getProducts: function () { return require('./products.json') @@ -130,6 +131,11 @@ module.exports = function container (get, set, clear) { if (typeof opts.post_only === 'undefined') { opts.post_only = true } + if (opts.order_type === 'taker') { + delete opts.price + opts.type = 'market' + } + delete opts.order_type client.buy(opts, function (err, resp, body) { if (body && body.message === 'Insufficient funds') { var order = { @@ -151,6 +157,11 @@ module.exports = function container (get, set, clear) { if (typeof opts.post_only === 'undefined') { opts.post_only = true } + if (opts.order_type === 'taker') { + delete opts.price + opts.type = 'market' + } + delete opts.order_type client.sell(opts, function (err, resp, body) { if (body && body.message === 'Insufficient funds') { var order = { diff --git a/extensions/exchanges/kraken/exchange.js b/extensions/exchanges/kraken/exchange.js index b49c036b64..0691efd874 100644 --- a/extensions/exchanges/kraken/exchange.js +++ b/extensions/exchanges/kraken/exchange.js @@ -53,6 +53,7 @@ module.exports = function container(get, set, clear) { name: 'kraken', historyScan: 'forward', makerFee: 0.16, + takerFee: 0.26, // The limit for the public API is not documented, 1750 ms between getTrades in backfilling seems to do the trick to omit warning messages. backfillRateLimit: 1750, @@ -184,12 +185,14 @@ module.exports = function container(get, set, clear) { var params = { pair: joinProduct(opts.product_id), type: type, - ordertype: 'limit', - price: opts.price, + ordertype: (opts.order_type === 'maker' ? 'limit' : 'market'), volume: opts.size, trading_agreement: c.kraken.tosagree, oflags: opts.post_only === true ? 'post' : undefined } + if ('price' in opts) { + params.price = opts.price + } client.api('AddOrder', params, function (error, data) { if (error && error.message.match(recoverableErrors)) { return retry('trade', args, error) diff --git a/extensions/exchanges/poloniex/exchange.js b/extensions/exchanges/poloniex/exchange.js index d963c4393d..61a68ba278 100644 --- a/extensions/exchanges/poloniex/exchange.js +++ b/extensions/exchanges/poloniex/exchange.js @@ -43,6 +43,7 @@ module.exports = function container (get, set, clear) { name: 'poloniex', historyScan: 'backward', makerFee: 0.15, + takerFee: 0.25, getProducts: function () { return require('./products.json') diff --git a/lib/engine.js b/lib/engine.js index ce42095f28..e397ee3964 100644 --- a/lib/engine.js +++ b/lib/engine.js @@ -514,9 +514,17 @@ module.exports = function container (get, set, clear) { s.balance.asset = n(s.balance.asset).add(s.buy_order.size).format('0.00000000') var total = n(price).multiply(s.buy_order.size) s.balance.currency = n(s.balance.currency).subtract(total).format('0.00000000') - if (s.exchange.makerFee) { - fee = n(s.buy_order.size).multiply(s.exchange.makerFee / 100).value() - s.balance.asset = n(s.balance.asset).subtract(fee).format('0.00000000') + if (so.order_type === 'maker') { + if (s.exchange.makerFee) { + fee = n(s.buy_order.size).multiply(s.exchange.makerFee / 100).value() + s.balance.asset = n(s.balance.asset).subtract(fee).format('0.00000000') + } + } + if (so.order_type === 'taker') { + if (s.exchange.takerFee) { + fee = n(s.buy_order.size).multiply(s.exchange.takerFee / 100).value() + s.balance.asset = n(s.balance.asset).subtract(fee).format('0.00000000') + } } } s.action = 'bought' @@ -528,8 +536,9 @@ module.exports = function container (get, set, clear) { slippage: n(price).subtract(s.buy_order.orig_price).divide(s.buy_order.orig_price).value(), type: 'buy', size: s.buy_order.orig_size, + fee: fee, price: price, - fee: fee + order_type: so.order_type } s.my_trades.push(my_trade) if (so.stats) { @@ -553,9 +562,18 @@ module.exports = function container (get, set, clear) { s.balance.asset = n(s.balance.asset).subtract(s.sell_order.size).value() var total = n(price).multiply(s.sell_order.size) s.balance.currency = n(s.balance.currency).add(total).value() - if (s.exchange.makerFee) { - fee = n(s.sell_order.size).multiply(s.exchange.makerFee / 100).multiply(price).value() - s.balance.currency = n(s.balance.currency).subtract(fee).format('0.00000000') + + if (so.order_type === 'maker') { + if (s.exchange.makerFee) { + fee = n(s.sell_order.size).multiply(s.exchange.makerFee / 100).multiply(price).value() + s.balance.currency = n(s.balance.currency).subtract(fee).format('0.00000000') + } + } + if (so.order_type === 'taker') { + if (s.exchange.takerFee) { + fee = n(s.sell_order.size).multiply(s.exchange.takerFee / 100).multiply(price).value() + s.balance.currency = n(s.balance.currency).subtract(fee).format('0.00000000') + } } } s.action = 'sold' @@ -567,8 +585,9 @@ module.exports = function container (get, set, clear) { slippage: n(s.sell_order.orig_price).subtract(price).divide(price).value(), type: 'sell', size: s.sell_order.orig_size, + fee: fee, price: price, - fee: fee + order_type: so.order_type } s.my_trades.push(my_trade) if (so.stats) {