From 60f7c7131108e23290478a9733b62542147081c7 Mon Sep 17 00:00:00 2001 From: Chase Sillevis Date: Tue, 6 Jun 2017 13:29:21 +0200 Subject: [PATCH] Order type: allow consumer to specify type of order to use --- 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 ea8013c0f1..b4a5ed1e30 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 ee780b891a..5c3141ea1e 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 2cc283e65c..90153d829e 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 474e20b2f3..45ed20f1a0 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 = {} @@ -73,6 +74,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 48e804cfad..3981c2f511 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') @@ -129,6 +130,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 = { @@ -150,6 +156,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 081ffeb4da..0ce2732a19 100644 --- a/extensions/exchanges/kraken/exchange.js +++ b/extensions/exchanges/kraken/exchange.js @@ -50,6 +50,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, @@ -181,12 +182,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 61b4899361..d16babca26 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) {